Created
February 9, 2015 04:31
-
-
Save cowwoc/b752cf667c049af05cec to your computer and use it in GitHub Desktop.
Common.js for https://github.com/webpack/webpack/issues/770
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******/ (function(modules) { // webpackBootstrap | |
/******/ // install a JSONP callback for chunk loading | |
/******/ var parentJsonpFunction = window["webpackJsonp"]; | |
/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) { | |
/******/ // add "moreModules" to the modules object, | |
/******/ // then flag all "chunkIds" as loaded and fire callback | |
/******/ var moduleId, chunkId, i = 0, callbacks = []; | |
/******/ for(;i < chunkIds.length; i++) { | |
/******/ chunkId = chunkIds[i]; | |
/******/ if(installedChunks[chunkId]) | |
/******/ callbacks.push.apply(callbacks, installedChunks[chunkId]); | |
/******/ installedChunks[chunkId] = 0; | |
/******/ } | |
/******/ for(moduleId in moreModules) { | |
/******/ modules[moduleId] = moreModules[moduleId]; | |
/******/ } | |
/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules); | |
/******/ while(callbacks.length) | |
/******/ callbacks.shift().call(null, __webpack_require__); | |
/******/ if(moreModules[0]) { | |
/******/ installedModules[0] = 0; | |
/******/ return __webpack_require__(0); | |
/******/ } | |
/******/ }; | |
/******/ | |
/******/ // The module cache | |
/******/ var installedModules = {}; | |
/******/ | |
/******/ // object to store loaded and loading chunks | |
/******/ // "0" means "already loaded" | |
/******/ // Array means "loading", array contains callbacks | |
/******/ var installedChunks = { | |
/******/ 5:0 | |
/******/ }; | |
/******/ | |
/******/ // The require function | |
/******/ function __webpack_require__(moduleId) { | |
/******/ | |
/******/ // Check if module is in cache | |
/******/ if(installedModules[moduleId]) | |
/******/ return installedModules[moduleId].exports; | |
/******/ | |
/******/ // Create a new module (and put it into the cache) | |
/******/ var module = installedModules[moduleId] = { | |
/******/ exports: {}, | |
/******/ id: moduleId, | |
/******/ loaded: false | |
/******/ }; | |
/******/ | |
/******/ // Execute the module function | |
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | |
/******/ | |
/******/ // Flag the module as loaded | |
/******/ module.loaded = true; | |
/******/ | |
/******/ // Return the exports of the module | |
/******/ return module.exports; | |
/******/ } | |
/******/ | |
/******/ // This file contains only the entry chunk. | |
/******/ // The chunk loading function for additional chunks | |
/******/ __webpack_require__.e = function requireEnsure(chunkId, callback) { | |
/******/ // "0" is the signal for "already loaded" | |
/******/ if(installedChunks[chunkId] === 0) | |
/******/ return callback.call(null, __webpack_require__); | |
/******/ | |
/******/ // an array means "currently loading". | |
/******/ if(installedChunks[chunkId] !== undefined) { | |
/******/ installedChunks[chunkId].push(callback); | |
/******/ } else { | |
/******/ // start chunk loading | |
/******/ installedChunks[chunkId] = [callback]; | |
/******/ var head = document.getElementsByTagName('head')[0]; | |
/******/ var script = document.createElement('script'); | |
/******/ script.type = 'text/javascript'; | |
/******/ script.charset = 'utf-8'; | |
/******/ script.async = true; | |
/******/ script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"Users","1":"Properties","2":"Index","3":"Cards","4":"Authentications"}[chunkId]||chunkId) + ".js"; | |
/******/ head.appendChild(script); | |
/******/ } | |
/******/ }; | |
/******/ | |
/******/ // expose the modules object (__webpack_modules__) | |
/******/ __webpack_require__.m = modules; | |
/******/ | |
/******/ // expose the module cache | |
/******/ __webpack_require__.c = installedModules; | |
/******/ | |
/******/ // __webpack_public_path__ | |
/******/ __webpack_require__.p = ""; | |
/******/ }) | |
/************************************************************************/ | |
/******/ ([ | |
/* 0 */, | |
/* 1 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = log4javascript; | |
/***/ }, | |
/* 2 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = URI; | |
/***/ }, | |
/* 3 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = $; | |
/***/ }, | |
/* 4 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = Q; | |
/***/ }, | |
/* 5 */, | |
/* 6 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = {}; | |
var Utilities = __webpack_require__(36); | |
var ObjectPreconditions = __webpack_require__(37); | |
/** | |
* Create a new precondition. | |
* | |
* @function | |
* @param {Object} parameter the parameter value | |
* @param {String} name the parameter name | |
* @return {ObjectPreconditions} the precondition | |
*/ | |
Preconditions.requireThat = function(parameter, name) | |
{ | |
"use strict"; | |
if (!Utilities.instanceOf(name, String)) | |
{ | |
if (name === undefined) | |
throw new TypeError("name was undefined instead of being of type String"); | |
if (name === null) | |
throw new TypeError("name was null instead of being of type String"); | |
throw new TypeError("name was of type " + Utilities.getClassName(name) + " instead of String: " + | |
name.toString()); | |
} | |
return new ObjectPreconditions(parameter, name); | |
}; | |
module.exports = Preconditions; | |
/***/ }, | |
/* 7 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Utilities = __webpack_require__(13); | |
var ResourceNotFoundException = __webpack_require__(38); | |
var AjaxWithRetry = __webpack_require__(39); | |
var RequestScope = __webpack_require__(12); | |
var Preconditions = __webpack_require__(6); | |
var Q = __webpack_require__(4); | |
var URI = __webpack_require__(2); | |
var ajax = new AjaxWithRetry(); | |
/** | |
* An identifiable user of the system. Unless otherwise stated, all URIs are absolute. | |
* | |
* @class | |
* @property {URI.<user>} uri the resource URI | |
* @property {String} email the user's email address | |
* @property {String} oldPassword the user's old password (null if not set). If non-null, | |
* {@code newPassword} must be set as well. | |
* @property {String} newPassword the user's new password (null if not set). Usually when | |
* newPassword is set oldPassword must be set as well; however, administrators have | |
* permission to change the password without setting oldPassword. | |
* @property {String} name the user's name | |
* | |
* @param {URI.<user>} uri the user's URI | |
* @param {String} email the user's email address | |
* @param {String} name the user's name | |
* @return {User} the user | |
*/ | |
function User(uri, email, name) | |
{ | |
"use strict"; | |
if (!(this instanceof User)) | |
{ | |
throw new Error("User constructor may not be invoked directly. You must use the \"new\" " + | |
"operator."); | |
} | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI).isAbsolute(); | |
Preconditions.requireThat(email, "email").isInstanceOf(String).trim().isNotEmpty(); | |
Preconditions.requireThat(name, "name").isInstanceOf(String).trim().isNotEmpty(); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "email", | |
{ | |
value: email, | |
writable: true, | |
enumerable: true | |
}); | |
var oldPassword = null; | |
Object.defineProperty(this, "oldPassword", | |
{ | |
/** | |
* @return {String} the old password | |
*/ | |
get: function() | |
{ | |
return oldPassword; | |
}, | |
/** | |
* @param {String} value the old password | |
*/ | |
set: function(value) | |
{ | |
Preconditions.requireThat(value, "oldPassword").isDefined(); | |
oldPassword = value; | |
}, | |
enumerable: true | |
}); | |
var newPassword = null; | |
Object.defineProperty(this, "newPassword", | |
{ | |
/** | |
* @return {String} the new password | |
*/ | |
get: function() | |
{ | |
return newPassword; | |
}, | |
/** | |
* @param {String} value the new password | |
*/ | |
set: function(value) | |
{ | |
Preconditions.requireThat(value, "newPassword").isDefined(); | |
newPassword = value; | |
}, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "name", | |
{ | |
value: name, | |
writable: true, | |
enumerable: true | |
}); | |
return this; | |
} | |
/** | |
* Convert a JSON object to a User object | |
* | |
* @param {string} json the JSON object with the user values. | |
* @return {User} the converted User object from the JSON parameter. | |
*/ | |
User.fromJson = function(json) | |
{ | |
"use strict"; | |
var jsonObject = JSON.parse(json); | |
var uri = Utilities.stringToUri(jsonObject.uri, "uri"); | |
var email = jsonObject.email; | |
var name = jsonObject.name; | |
return new User(uri, email, name); | |
}; | |
/** | |
* Convert the User object to a JSON object | |
* | |
* @return {string} the converted JSON object. | |
*/ | |
User.prototype.toJson = function() | |
{ | |
"use strict"; | |
return Utilities.toJson(this); | |
}; | |
/** | |
* @return {String} the String representation of the Object | |
*/ | |
User.prototype.toString = function() | |
{ | |
"use strict"; | |
return this.toJson(); | |
}; | |
/** | |
* Saves the user. | |
* | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
User.prototype.save = function() | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(this, "this").isInstanceOf(User); | |
var settings = | |
{ | |
type: "PUT", | |
data: | |
{ | |
email: this.email, | |
name: this.name | |
}, | |
contentType: "application/vnd.com.realestate.User+json; version=1; charset=utf-8", | |
headers: {} | |
}; | |
var csrfToken = RequestScope.csrfToken; | |
Preconditions.requireThat(csrfToken, "csrfToken").isNotNull(); | |
settings.headers[Utilities.csrfToken] = csrfToken; | |
if (this.newPassword !== null) | |
{ | |
if (this.oldPassword !== null) | |
settings.data.oldPassword = this.oldPassword; | |
settings.data.newPassword = this.newPassword; | |
} | |
else if (this.oldPassword !== null) | |
throw new TypeError("newPassword must be set because oldPassword is set"); | |
settings.data = JSON.stringify(settings.data); | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(this.uri, settings). | |
then(Utilities.handleCommonResponses). | |
then(function(response) | |
{ | |
Preconditions.requireThat(this, "this").isInstanceOf(User); | |
var settings = response.settings; | |
var xhr = response.xhr; | |
switch (xhr.status) | |
{ | |
case 200: | |
case 204: | |
break; | |
case 301: | |
{ | |
try | |
{ | |
this.uri = xhr.getResponseHeader("location"); | |
} | |
catch (e) | |
{ | |
throw ajax.onError(e, settings, xhr); | |
} | |
break; | |
} | |
default: | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
} | |
}.bind(this)); | |
}.bind(this)); | |
}; | |
/** | |
* Deletes the user. | |
* | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
User.prototype.delete = function() | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(this, "this").isInstanceOf(User); | |
var settings = | |
{ | |
type: "DELETE", | |
contentType: "application/vnd.com.realestate.User+json; version=1; charset=utf-8", | |
headers: {} | |
}; | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(this.uri, settings). | |
then(Utilities.handleCommonResponses). | |
then(function(response) | |
{ | |
var settings = response.settings; | |
var xhr = response.xhr; | |
switch (xhr.status) | |
{ | |
case 204: | |
break; | |
default: | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
} | |
}). | |
catch(function(error) | |
{ | |
if (error instanceof ResourceNotFoundException) | |
return; | |
throw error; | |
}); | |
}.bind(this)); | |
}; | |
/** | |
* Deletes a user by its email address. | |
* | |
* @param {URI} baseUri the server's base URI | |
* @param {String} email the user's email address | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
User.deleteByEmail = function(baseUri, email) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
return User.getByEmail(baseUri, email). | |
then(function(user) | |
{ | |
return user.delete(); | |
}, | |
function(error) | |
{ | |
if (!(error instanceof ResourceNotFoundException)) | |
throw error; | |
}); | |
}); | |
}; | |
/** | |
* Looks up a user by its URI. | |
* | |
* @param {URI.<user>} uri the user's URI | |
* @return {Q} a Promise that returns {@link User} on success and {@code Error} on failure | |
*/ | |
User.getByUri = function(uri) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI).isAbsolute(); | |
var settings = | |
{ | |
type: "GET", | |
headers: | |
{ | |
accept: "application/vnd.com.realestate.User+json; version=1; charset=utf-8" | |
} | |
}; | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(uri, settings).then(User.gotUser); | |
}); | |
}; | |
/** | |
* Invoked when an operation returns a User's state. | |
* | |
* @param {jqXHR} response the server response | |
* @return {User} the user | |
*/ | |
User.gotUser = function(response) | |
{ | |
"use strict"; | |
return Utilities.handleCommonResponses(response). | |
then(function(response) | |
{ | |
var settings = response.settings; | |
var xhr = response.xhr; | |
if (xhr.status !== 200) | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
var data = xhr.responseJSON; | |
var canonicalUri = xhr.getResponseHeader("Content-Location"); | |
if (!canonicalUri || typeof (canonicalUri) !== "string") | |
throw ajax.onError("Content-Location header is missing", settings, xhr); | |
try | |
{ | |
var absoluteUrl = new URI(canonicalUri).absoluteTo(settings.url); | |
if (!data || typeof (data) !== "object") | |
throw new TypeError("Invalid data: " + data); | |
return new User(absoluteUrl, data.email, data.name); | |
} | |
catch (e) | |
{ | |
throw ajax.onError(e, settings, xhr); | |
} | |
}); | |
}; | |
/** | |
* Looks up a user by its email address. | |
* | |
* @param {URI} baseUri the server's base URI | |
* @param {String} email the user's email address | |
* @return {Q} a Promise that returns {@link User} on success and {@code Error} on failure | |
*/ | |
User.getByEmail = function(baseUri, email) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(baseUri, "baseUri").isInstanceOf(URI).isAbsolute(); | |
Preconditions.requireThat(email, "email").isInstanceOf(String).trim().isNotEmpty(); | |
var settings = | |
{ | |
type: "GET", | |
headers: | |
{ | |
accept: "application/vnd.com.realestate.User+json; version=1; charset=utf-8" | |
} | |
}; | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(new URI("users;email=" + email).absoluteTo(baseUri), settings).then(User.gotUser); | |
}); | |
}; | |
/** | |
* Inserts a new User. | |
* | |
* @param {URI} baseUri the server's base URI | |
* @param {String} email the user's email address | |
* @param {String} password the user's password | |
* @param {String} name the user's name | |
* @return {Q} a Promise that returns {@code URI.<user>} on success and {@code Error} on | |
* failure | |
*/ | |
User.insert = function(baseUri, email, password, name) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(baseUri, "baseUri").isInstanceOf(URI).isAbsolute(); | |
Preconditions.requireThat(email, "email").isInstanceOf(String).trim().isNotEmpty(); | |
Preconditions.requireThat(password, "password").isInstanceOf(String).trim().isNotEmpty(); | |
var settings = | |
{ | |
type: "POST", | |
data: JSON.stringify( | |
{ | |
email: email, | |
password: password, | |
name: name | |
}), | |
contentType: "application/vnd.com.realestate.User+json; version=1; charset=utf-8", | |
headers: {} | |
}; | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(new URI("users").absoluteTo(baseUri), settings). | |
then(Utilities.handleCommonResponses). | |
then(function(response) | |
{ | |
var settings = response.settings; | |
var xhr = response.xhr; | |
switch (xhr.status) | |
{ | |
case 201: | |
{ | |
var location; | |
try | |
{ | |
location = xhr.getResponseHeader("location"); | |
} | |
catch (e) | |
{ | |
throw ajax.onError(e, settings, xhr); | |
} | |
return location; | |
} | |
default: | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
} | |
}); | |
}); | |
}; | |
/** | |
* Inserts and loads a new User. | |
* | |
* @param {URI} baseUri the server's base URI | |
* @param {String} email the user's email address | |
* @param {String} password the user's password | |
* @param {String} name the user's name | |
* @return {Q} a Promise that returns {@link User} on success and {@code Error} on failure | |
* @see User#insert | |
* @see User#reload | |
*/ | |
User.insertAndLoad = function(baseUri, email, password, name) | |
{ | |
"use strict"; | |
return User.insert(baseUri, email, password, name).then(function(user) | |
{ | |
return User.getByUri(user); | |
}); | |
}; | |
/** | |
* @typedef {Object} UserUris | |
* | |
* @property {String} eTag the version of the list | |
* @property {Date} lastModified the last time the list was modified | |
* @property {Array.<URI.<user>>} users the list of users | |
*/ | |
/** | |
* Lists all users. | |
* | |
* @param {URI} baseUri the server's base URI | |
* @param {UserUris} oldValue (optional) the previous value of the list | |
* @return {Q} a Promise that returns {@link UserUris} on success and {@code Error} on | |
* failure | |
*/ | |
User.list = function(baseUri, oldValue) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(baseUri, "baseUri").isInstanceOf(URI).isAbsolute(); | |
var settings = | |
{ | |
type: "GET", | |
headers: | |
{ | |
accept: "application/vnd.com.realestate.Users+json; version=1; charset=utf-8" | |
} | |
}; | |
Utilities.setRequestCachingHeaders(oldValue, settings); | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(new URI("users").absoluteTo(baseUri), settings). | |
then(Utilities.handleCommonResponses). | |
then(function(response) | |
{ | |
var settings = response.settings; | |
var xhr = response.xhr; | |
switch (xhr.status) | |
{ | |
case 200: | |
break; | |
case 304: | |
return oldValue; | |
default: | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
} | |
var data = xhr.responseJSON; | |
try | |
{ | |
data = Utilities.stringToUriArray(data, "data"); | |
Preconditions.requireThat(data, "data").isInstanceOf(Array).elements().isInstanceOf(URI).isAbsolute(); | |
} | |
catch (e) | |
{ | |
throw ajax.onError(e.message, settings, xhr); | |
} | |
return Utilities.setResponseCachingHeaders(response, | |
{ | |
users: data | |
}); | |
}); | |
}); | |
}; | |
/** | |
* Converts a list of user URIs to a list of User objects. | |
* | |
* @param {UserUris} userUris a list of user URIs | |
* @return {Q} a Promise that returns {@code Users} on success and {@code Error} | |
* on failure | |
*/ | |
User.listByUris = function(userUris) | |
{ | |
"use strict"; | |
var promise = new Q(); | |
var result = []; | |
Preconditions.requireThat(userUris, "userUris").isSet(); | |
Preconditions.requireThat(userUris.users, "userUris.users").isInstanceOf(Array). | |
elements().isInstanceOf(URI).isAbsolute(); | |
userUris.users.forEach(function(userUri) | |
{ | |
promise = promise.then(function() | |
{ | |
return User.getByUri(userUri); | |
}).then(function(user) | |
{ | |
result.push(user); | |
}); | |
}); | |
return promise.then(function() | |
{ | |
return { | |
users: result, | |
eTag: userUris.eTag, | |
lastModified: userUris.lastModified | |
}; | |
}); | |
}; | |
module.exports = User; | |
/***/ }, | |
/* 8 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var AjaxWithRetry = __webpack_require__(39); | |
var ResourceNotFoundException = __webpack_require__(38); | |
var Utilities = __webpack_require__(13); | |
var RequestScope = __webpack_require__(12); | |
var SessionScope = __webpack_require__(41); | |
var Preconditions = __webpack_require__(6); | |
var Q = __webpack_require__(4); | |
var URI = __webpack_require__(2); | |
var ajax = new AjaxWithRetry(); | |
/** | |
* A token that proves that a user has authenticated successfully. | |
* | |
* @class | |
* @property {URI.<authentication>} uri the resource URI | |
* @property {URI.<user>} user the user's URI | |
* | |
* @param {URI.<authentication>} uri the resource URI | |
* @param {URI.<user>} user the user's URI | |
* @return {Authentication} the authentication token | |
*/ | |
function Authentication(uri, user) | |
{ | |
"use strict"; | |
if (!(this instanceof Authentication)) | |
{ | |
throw new Error("User constructor may not be invoked directly. You must" + | |
" use the \"new\" operator."); | |
} | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI).isAbsolute(); | |
Preconditions.requireThat(user, "user").isInstanceOf(URI).isAbsolute(); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "user", | |
{ | |
value: user, | |
enumerable: true | |
}); | |
return this; | |
} | |
/** | |
* @return {String} the string representation of the object | |
*/ | |
Authentication.prototype.toString = function() | |
{ | |
"use strict"; | |
return Utilities.toJson(this); | |
}; | |
/** | |
* Deletes an authentication token by its URI. | |
* | |
* @param {URI.<authentication>} uri the resource URI | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
Authentication.deleteByUri = function(uri) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI).isAbsolute(); | |
var settings = | |
{ | |
type: "DELETE", | |
headers: {} | |
}; | |
Utilities.addCommonHeaders(settings); | |
return ajax.request(uri, settings). | |
then(Utilities.handleCommonResponses). | |
then(function(response) | |
{ | |
var settings = response.settings; | |
var xhr = response.xhr; | |
switch (xhr.status) | |
{ | |
case 204: | |
break; | |
default: | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
} | |
}). | |
catch(function(error) | |
{ | |
if (error instanceof ResourceNotFoundException) | |
return; | |
throw error; | |
}); | |
}.bind(this)); | |
}; | |
/** | |
* Inserts a new Authentication token. | |
* | |
* @param {URI} baseUri the server's base URI | |
* @param {String} email the user's email address | |
* @param {String} password the user's password | |
* @return {Q} a Promise that returns {@code URI.<authentication>} on success and | |
* {@code Error} on failure | |
*/ | |
Authentication.insert = function(baseUri, email, password) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
Preconditions.requireThat(baseUri, "baseUri").isInstanceOf(URI).isAbsolute(); | |
Preconditions.requireThat(email, "email").isInstanceOf(String).trim().isNotEmpty(); | |
Preconditions.requireThat(password, "password").isInstanceOf(String).trim().isNotEmpty(); | |
var settings = | |
{ | |
type: "POST", | |
data: JSON.stringify( | |
{ | |
email: email, | |
password: password | |
}), | |
contentType: "application/vnd.com.realestate.Authentication+json; version=1; charset=utf-8", | |
headers: {} | |
}; | |
var csrfToken = RequestScope.csrfToken; | |
Preconditions.requireThat(csrfToken, "csrfToken").isNotNull(); | |
settings.headers[Utilities.csrfToken] = csrfToken; | |
var authenticationToken = SessionScope.authenticationToken; | |
if (authenticationToken) | |
settings.headers[Utilities.authenticationToken] = authenticationToken; | |
return ajax.request(new URI("authentications").absoluteTo(baseUri), settings). | |
then(Utilities.handleCommonResponses). | |
then(function(response) | |
{ | |
var settings = response.settings; | |
var xhr = response.xhr; | |
switch (xhr.status) | |
{ | |
case 201: | |
{ | |
try | |
{ | |
return new URI(xhr.getResponseHeader("location")).absoluteTo(settings.url); | |
} | |
catch (e) | |
{ | |
throw ajax.onError(e, settings, xhr); | |
} | |
// WORKAROUND: https://github.com/jshint/jshint/issues/1961 | |
break; | |
} | |
default: | |
throw ajax.onError("Unexpected response code", settings, xhr); | |
} | |
}); | |
}); | |
}; | |
module.exports = Authentication; | |
/***/ }, | |
/* 9 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var Authentication = __webpack_require__(8); | |
var Utilities = __webpack_require__(13); | |
var Database = __webpack_require__(40); | |
var SessionScope = __webpack_require__(41); | |
var Html = __webpack_require__(10); | |
var Q = __webpack_require__(4); | |
var URI = __webpack_require__(2); | |
var $ = __webpack_require__(3); | |
var Authentications = {}; | |
var MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24; | |
var TOKEN_NAME_IN_URI = "sessionId"; | |
var TOKEN_NAME_IN_STORAGE = "SESSION_ID"; | |
/** | |
* Creates a new token name. | |
* | |
* @return {Q} a Promise that returns the token name on success and {@code Error} on failure | |
*/ | |
function createTokenName() | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
var result = Q.defer(); | |
Database.open(function(database) | |
{ | |
/** | |
* Checks whether a token name is available. If it is, returns it through the Promise. | |
*/ | |
function getTokenName() | |
{ | |
var tokenName = Math.floor(Math.random() * 1000000).toString(16); | |
var request = connection.get(tokenName); | |
request.onerror = Database.onError(result); | |
/** | |
* Invoked after retrieving {@code tokenName}. | |
* | |
* @param {Event} event describes the event | |
*/ | |
request.onsuccess = function(event) | |
{ | |
var existingValue = event.target.result; | |
if (existingValue !== undefined) | |
{ | |
// Name is taken. Try again. | |
getTokenName(); | |
return; | |
} | |
var request = connection.put(true, tokenName); | |
request.onerror = Database.onError(result); | |
/** | |
* Invoked after inserting {@code tokenName = true}. | |
*/ | |
request.onsuccess = function() | |
{ | |
Database.close(database); | |
result.resolve(tokenName); | |
}; | |
}; | |
} | |
Preconditions.requireThat(database, "database").isNotNull(); | |
var transaction = database.transaction([Database.authenticationMap], "readwrite"); | |
var connection = transaction.objectStore(Database.authenticationMap); | |
getTokenName(); | |
}); | |
return result.promise; | |
}); | |
} | |
/** | |
* A function that should be invoked on page load or after the authentication token changes. If the page is not | |
* authenticated, it is redirected to the authentication page (unless it is already there). | |
* | |
* @return {Q} a Promise that returns a {@code Boolean} on success and {@code Error} on failure. The | |
* {@code Boolean} is {@code true} if the page is authenticated, {@code false} if it is not. | |
*/ | |
Authentications.reload = function() | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
var tokenName = Authentications.getTokenName(); | |
var tokenValue = Utilities.getCookieValue(tokenName); | |
if (!tokenValue) | |
{ | |
var current = new URI(window.location.href); | |
var baseUri = Html.getBaseUri(); | |
var authenticationsPage = baseUri.clone().segment("authentications"); | |
if (current.clone().query("").equals(authenticationsPage)) | |
{ | |
// Prevent endless loop | |
return false; | |
} | |
var redirect = baseUri.clone().removeQuery(TOKEN_NAME_IN_URI).segment("authentications"). | |
setQuery("referer", current.clone().removeQuery(TOKEN_NAME_IN_URI)); | |
window.location.href = redirect; | |
return false; | |
} | |
SessionScope.authenticationToken = tokenValue.toString(); | |
Authentications.addTokenToAllLinks(); | |
return true; | |
}); | |
}; | |
/** | |
* @typedef TokenNameAndValue | |
* @property {String} name the name of the cookie holding the authentication token | |
* @property {URI.<authentication>} value the authentication token | |
*/ | |
/** | |
* Stores the token in local storage and {@code document.cookie}. | |
* | |
* @param {URI.<authentication>} token the token to save | |
* @return {Q} a Promise that returns {@code TokenNameAndValue} on success and {@code Error} on failure | |
*/ | |
Authentications.saveToken = function(token) | |
{ | |
"use strict"; | |
return createTokenName(token).then(function(sessionId) | |
{ | |
// Store the token in session storage | |
sessionStorage.setItem(TOKEN_NAME_IN_STORAGE, sessionId); | |
sessionStorage.setItem(sessionId, token.toString()); | |
// If a user opens a new tab and navigates to a resource that requires authentication, use the last created | |
// token. | |
localStorage.setItem(TOKEN_NAME_IN_STORAGE, sessionId); | |
localStorage.setItem(sessionId, token.toString()); | |
// Update Cookie for this site | |
Utilities.setCookieValue(sessionId, token.toString(), MILLISECONDS_PER_DAY); | |
// Update SessionScope | |
SessionScope.authenticationToken = token.toString(); | |
return { | |
name: sessionId, | |
value: token | |
}; | |
}); | |
}; | |
/** | |
* Returns token name found in the URL, sessionStorage or localStorage. | |
* | |
* @return {(String|null)} null if the token name is not set | |
*/ | |
Authentications.getTokenName = function() | |
{ | |
"use strict"; | |
var uriParameters = new URI(window.location.href).search(true); | |
var result = uriParameters[TOKEN_NAME_IN_URI]; | |
if (result) | |
return result; | |
result = sessionStorage.getItem(TOKEN_NAME_IN_STORAGE); | |
if (result) | |
return result; | |
return localStorage.getItem(TOKEN_NAME_IN_STORAGE); | |
}; | |
/** | |
* Adds the token name to all anchor elements on the page, so when they are followed, the correct authentication | |
* token is used. | |
*/ | |
Authentications.addTokenToAllLinks = function() | |
{ | |
"use strict"; | |
var tokenName = Authentications.getTokenName(); | |
$("a").each(function(index, element) | |
{ | |
var el = $(element); | |
var href = el.attr("href"); | |
if (!href) | |
return; | |
href = new URI(href).setQuery(TOKEN_NAME_IN_URI, tokenName).toString(); | |
el.attr("href", href); | |
}); | |
}; | |
/** | |
* Adds or updates the token name in a URI. | |
* | |
* @param {URI} uri a URI to update | |
* @return {String} the updated URI | |
*/ | |
Authentications.addToUri = function(uri) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
var tokenName = Authentications.getTokenName(); | |
if (tokenName === null) | |
return uri.toString(); | |
return uri.setQuery(TOKEN_NAME_IN_URI, tokenName).toString(); | |
}; | |
/** | |
* Logs the current tab out of the system. | |
*/ | |
Authentications.logout = function() | |
{ | |
"use strict"; | |
var tokenName = Authentications.getTokenName(); | |
var tokenValue = Utilities.getCookieValue(tokenName); | |
Q.try(function() | |
{ | |
if (tokenValue !== null) | |
return Authentication.deleteByUri(new URI(tokenValue)); | |
}).then(function() | |
{ | |
if (tokenName !== null) | |
{ | |
sessionStorage.removeItem(tokenName); | |
localStorage.removeItem(tokenName); | |
Utilities.deleteCookie(tokenName); | |
} | |
localStorage.removeItem(TOKEN_NAME_IN_STORAGE); | |
sessionStorage.removeItem(TOKEN_NAME_IN_STORAGE); | |
var location = new URI(window.location.href).removeQuery(TOKEN_NAME_IN_URI).toString(); | |
window.location.replace(location); | |
return Authentications.reload(); | |
}).done(); | |
}; | |
module.exports = Authentications; | |
/***/ }, | |
/* 10 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var URI = __webpack_require__(2); | |
var Html = {}; | |
/** | |
* @return {URI} the base URI of the current HTML document | |
* @see http://stackoverflow.com/a/13832757/14731 | |
*/ | |
Html.getBaseUri = function() | |
{ | |
"use strict"; | |
var bases = document.getElementsByTagName("base"); | |
if (bases.length > 0) | |
return new URI(bases[0].href); | |
else | |
return new URI(window.location.origin); | |
}; | |
module.exports = Html; | |
/***/ }, | |
/* 11 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when an operation fails due to the existence of a resource. | |
* | |
* @class | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* @param {URI} conflict the URI of the conflicting resource | |
* | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
* @property {URI} conflict the URI of the conflicting resource | |
*/ | |
function ConflictingResourceException(method, uri, message, conflict) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
Preconditions.requireThat(conflict, "conflict").isInstanceOf(URI); | |
this.name = "ConflictingResourceException"; | |
this.conflict = conflict; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else | |
message = method + " " + uri.toString() + " conflicted with an existing resource: " + conflict.toString(); | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "conflict", | |
{ | |
value: conflict, | |
enumerable: true | |
}); | |
} | |
ConflictingResourceException.prototype = Object.create(Error.prototype); | |
module.exports = ConflictingResourceException; | |
/***/ }, | |
/* 12 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
// This singleton holds request-scoped variables, like the CSRF token. | |
var RequestScope = {}; | |
var csrfToken = null; | |
Object.defineProperty(RequestScope, "csrfToken", | |
{ | |
/** | |
* @return {String} the CSRF token | |
*/ | |
get: function() | |
{ | |
"use strict"; | |
return csrfToken; | |
}, | |
/** | |
* @param {String} value the new CSRF token | |
*/ | |
set: function(value) | |
{ | |
"use strict"; | |
Preconditions.requireThat(value, "value").isInstanceOf(String).trim().isNotEmpty(); | |
csrfToken = value; | |
}, | |
enumerable: true | |
}); | |
module.exports = RequestScope; | |
/***/ }, | |
/* 13 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var AjaxWithRetry = __webpack_require__(39); | |
var UnauthorizedException = __webpack_require__(42); | |
var RequestScope = __webpack_require__(12); | |
var SessionScope = __webpack_require__(41); | |
var ReferenceNotFoundException = __webpack_require__(43); | |
var ForbiddenException = __webpack_require__(16); | |
var PreconditionFailedException = __webpack_require__(44); | |
var ConflictingResourceException = __webpack_require__(11); | |
var BadRequestException = __webpack_require__(45); | |
var ResourceNotFoundException = __webpack_require__(38); | |
var ServiceUnavailableException = __webpack_require__(23); | |
var AssertionException = __webpack_require__(18); | |
var Preconditions = __webpack_require__(6); | |
var ResourceNotFoundException = __webpack_require__(38); | |
var Q = __webpack_require__(4); | |
var URI = __webpack_require__(2); | |
var printStackTrace = __webpack_require__(24); | |
var $ = __webpack_require__(3); | |
/** | |
* @property {String} csrfToken the name of the CSRF token | |
* @property {String} authenticationToken the name of the authentication header | |
*/ | |
var Utilities = {}; | |
Utilities.csrfToken = "com.realestate.CsrfToken"; | |
Utilities.authenticationToken = "com.realestate.Authentication"; | |
Q.longStackSupport = true; | |
/** | |
* Throws an unhandled error to the default uncaught exception handler. | |
* | |
* @param {Error} error the error | |
*/ | |
function printUncaughtError(error) | |
{ | |
"use strict"; | |
// Q.longStackSupport() modifies error.stack, but Chrome ignores it. | |
// | |
// WORKAROUND: https://code.google.com/p/chromium/issues/detail?id=249575 | |
var message = error.message + "\n" + printStackTrace( | |
{ | |
e: error, | |
guess: true | |
}).join("\n"); | |
console.error(message); | |
alert("An unexpected error has occurred. See the Javascript console for more information."); | |
} | |
/** | |
* Invoked for uncaught exceptions thrown by a Promise. | |
* | |
* @param {Object} error the error object | |
*/ | |
Q.onerror = function(error) | |
{ | |
"use strict"; | |
printUncaughtError(error); | |
}; | |
/** | |
* See http://stackoverflow.com/a/20972210/14731. | |
* | |
* @param {String} message the error message | |
* @param {String} filename the file that triggered the error | |
* @param {String} lineNumber the line number that triggered the error | |
* @param {String} columnNumber the column number that triggered the error | |
* @param {(Error|undefined|null)} error the exception | |
*/ | |
window.onerror = function(message, filename, lineNumber, columnNumber, error) | |
{ | |
"use strict"; | |
if (!error) | |
return false; | |
printUncaughtError(error); | |
}; | |
/** | |
* Returns the JSON representation of an object. | |
* | |
* @param {string} object the object | |
* @param {number} objectMaxDepth for objects, the maximum number of times to recurse into | |
* descendants | |
* @param {number} arrayMaxLength for arrays, the maximum number of elements to enumerate | |
* @param {String} indent the string to use for indentation | |
* @return {String} the JSON representation | |
*/ | |
Utilities.toJson = function(object, objectMaxDepth, arrayMaxLength, indent) | |
{ | |
"use strict"; | |
/** | |
* Escapes control characters, quote characters, backslash characters and quotes the string. | |
* | |
* @param {String} string the string to quote | |
* @return {String} the quoted string | |
*/ | |
function quote(string) | |
{ | |
escapable.lastIndex = 0; | |
var escaped; | |
if (escapable.test(string)) | |
{ | |
escaped = string.replace(escapable, function(a) | |
{ | |
var replacement = replacements[a]; | |
if (typeof (replacement) === "string") | |
return replacement; | |
// Pad the unicode representation with leading zeros, up to 4 characters. | |
return "\\u" + a.charCodeAt(0).toString(16).padLeft(4, "0"); | |
}); | |
} | |
else | |
escaped = string; | |
return "\"" + escaped + "\""; | |
} | |
/** | |
* Returns the String representation of an object. | |
* | |
* Based on <a href="https://github.com/Canop/JSON.prune/blob/master/JSON.prune.js">https://github.com/Canop/JSON.prune/blob/master/JSON.prune.js</a> | |
* | |
* @param {String} path the fully-qualified path of value in the JSON object | |
* @param {type} value the value of the property | |
* @param {String} cumulativeIndent the indentation to apply at this level | |
* @param {number} depth the current recursion depth | |
* @return {String} the JSON representation of the object, or "null" for values that aren't | |
* valid in JSON (e.g. infinite numbers). | |
*/ | |
function toString(path, value, cumulativeIndent, depth) | |
{ | |
switch (typeof (value)) | |
{ | |
case "string": | |
return quote(value); | |
case "number": | |
{ | |
// JSON numbers must be finite | |
if (isFinite(value)) | |
return String(value); | |
return "null"; | |
} | |
case "boolean": | |
return String(value); | |
case "object": | |
{ | |
if (!value) | |
return "null"; | |
else if (value.constructor.name === "URI") | |
return quote(value.toString()); | |
else if (value.constructor.name === "Duration") | |
return quote(value.toIsoString()); | |
var valueIndex = values.indexOf(value); | |
if (valueIndex !== -1) | |
return "Reference => " + paths[valueIndex]; | |
values.push(value); | |
paths.push(path); | |
if (depth > objectMaxDepth) | |
return "..."; | |
// Make an array to hold the partial results of stringifying this object value. | |
var partial = []; | |
// Is the value an array? | |
var i; | |
if (Object.prototype.toString.apply(value) === "[object Array]") | |
{ | |
// The value is an array. Stringify every element | |
var length = Math.min(value.length, arrayMaxLength); | |
// Whether a property has one or multiple values, they should be treated as the same | |
// object depth. As such, we do not increment the object depth when recursing into an | |
// array. | |
for (i = 0; i < length; ++i) | |
{ | |
partial[i] = toString(path + "." + i, value[i], cumulativeIndent + indent, depth, | |
arrayMaxLength); | |
} | |
if (i < value.length) | |
{ | |
// arrayMaxLength reached | |
partial[i] = "..."; | |
} | |
return "\n" + cumulativeIndent + "[" + partial.join(", ") + "\n" + cumulativeIndent + | |
"]"; | |
} | |
// Otherwise, iterate through all of the keys in the object. | |
for (var subKey in value) | |
{ | |
if (Object.prototype.hasOwnProperty.call(value, subKey)) | |
{ | |
var subValue; | |
try | |
{ | |
subValue = toString(path + "." + subKey, value[subKey], cumulativeIndent + indent, | |
depth + 1); | |
partial.push(quote(subKey) + ": " + subValue); | |
} | |
catch (e) | |
{ | |
// this try/catch due to forbidden accessors on some objects | |
if (e.message) | |
subKey = e.message; | |
else | |
subKey = "access denied"; | |
} | |
} | |
} | |
var result = "\n" + cumulativeIndent + "{\n"; | |
for (i = 0; i < partial.length; ++i) | |
result += cumulativeIndent + indent + partial[i] + ",\n"; | |
if (partial.length > 0) | |
{ | |
// Remove trailing comma | |
result = result.slice(0, result.length - 2) + "\n"; | |
} | |
result += cumulativeIndent + "}"; | |
return result; | |
} | |
default: | |
return "null"; | |
} | |
} | |
if (indent === undefined) | |
indent = " "; | |
if (objectMaxDepth === undefined) | |
objectMaxDepth = 0; | |
if (arrayMaxLength === undefined) | |
arrayMaxLength = 50; | |
// Matches characters that must be escaped | |
var escapable = new RegExp("[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5" + | |
"\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]", "g"); | |
// The replacement characters | |
var replacements = { | |
"\b": "\\b", | |
"\t": "\\t", | |
"\n": "\\n", | |
"\f": "\\f", | |
"\r": "\\r", | |
"\"": "\\\"", | |
"\\": "\\\\" | |
}; | |
// A list of all the objects that were seen (used to avoid recursion) | |
var values = []; | |
// The path of an object in the JSON object, with indexes corresponding to entries in the | |
// "values" variable. | |
var paths = []; | |
return toString("root", object, "", 0); | |
}; | |
/** | |
* Throws AssertionException if an expression is false. | |
* | |
* @param {boolean} expression the expression to evaluate | |
* @param {String} message explains the failed assertion | |
* @throws {AssertionException} if the expression is false | |
*/ | |
Utilities.assert = function(expression, message) | |
{ | |
"use strict"; | |
if (!expression) | |
throw new AssertionException(message); | |
}; | |
/** | |
* @param {Element} element a DOM element | |
* @return {String} the path of the DOM element | |
* @see http://www.codeproject.com/Tips/801855/Highlighting-the-Elements-in-Web-Browser-Control-a | |
*/ | |
Utilities.getElementPath = function(element) | |
{ | |
"use strict"; | |
return "//" + $(element).parents().andSelf().map(function() | |
{ | |
var $this = $(this); | |
var tagName = this.nodeName; | |
if ($this.siblings(tagName).length > 0) | |
tagName += "[" + $this.prevAll(tagName).length + "]"; | |
return tagName; | |
}).get().join("/").toUpperCase(); | |
}; | |
/** | |
* Notifies a Promise of a WebRTC success. | |
* | |
* @param {Q} result the {@code Promise} to notify of the success | |
* @return {Function} a WebRTC success callback | |
*/ | |
Utilities.rtcToAjaxSuccessAdapter = function(result) | |
{ | |
"use strict"; | |
return function() | |
{ | |
result.resolve.apply(undefined, arguments); | |
}; | |
}; | |
/** | |
* @callback WebRtcFailure | |
* @param {String} reason the reason the operation failed | |
*/ | |
/** | |
* Notifies a Promise of a WebRTC failure. | |
* | |
* @param {Q} result the {@code Promise} to notify of the failure | |
* @param {Error} context indicates who invoked the WebRTC function | |
* @return {WebRtcFailure} a WebRTC failure callback | |
*/ | |
Utilities.rtcToAjaxFailureAdapter = function(result, context) | |
{ | |
"use strict"; | |
return function(rtcError) | |
{ | |
if (typeof (rtcError) === "string") | |
{ | |
// WORKAROUND: https://code.google.com/p/webrtc/issues/detail?id=2316 | |
context.message = rtcError; | |
rtcError = context; | |
} | |
else | |
{ | |
// See http://www.w3.org/TR/webrtc/#rtcsdperror | |
rtcError.message += ". Line number: " + rtcError.sdpLineNumber; | |
} | |
result.reject(rtcError); | |
}; | |
}; | |
/** | |
* Throws BadRequestException if a request would leave the resource in an incomplete or | |
* inconsistent state. | |
* Throws ReferenceNotFoundException if a request failed because it referenced a non-existent | |
* resource. | |
* Throws UnauthorizedException if a request failed due to missing or invalid authentication | |
* tokens. | |
* Throws ForbiddenException if the server refuses to carry out an operation. | |
* Throws ResourceNotFoundException if a request failed due to a missing or removed resource. | |
* Throws ConflictingResourceException if the result of the operation would have conflicted | |
* with another resource. | |
* Throws PreconditionFailedException if one of the operation's preconditions was false | |
* (typically this indicates that a resource was modified since we last retrieved its value). | |
* | |
* @param {AjaxWithRetry.AjaxResult} response the server response | |
* @return {Q} a Promise that returns {@code AjaxWithRetry.AjaxResult} on success and | |
* {@code Error} on failure | |
*/ | |
Utilities.handleCommonResponses = function(response) | |
{ | |
"use strict"; | |
return Q.try(function() | |
{ | |
var settings = response.settings; | |
var method = settings.type; | |
var uri = new URI(settings.url); | |
var xhr = response.xhr; | |
var location; | |
switch (xhr.status) | |
{ | |
case 400: | |
{ | |
location = xhr.getResponseHeader("location"); | |
if (location === null) | |
throw new BadRequestException(method, uri, xhr.responseText); | |
var absoluteUrl; | |
try | |
{ | |
absoluteUrl = new URI(location).absoluteTo(uri); | |
} | |
catch (e) | |
{ | |
var reason = e; | |
if (reason instanceof Error) | |
reason = printStackTrace( | |
{ | |
e: reason, | |
guess: true | |
}).join("\n"); | |
throw new Error(reason + ". " + method + " " + uri.toString() + " returned HTTP " + xhr.status + | |
": " + xhr.responseText); | |
} | |
throw new ReferenceNotFoundException(method, uri, xhr.responseText, absoluteUrl); | |
} | |
case 401: | |
throw UnauthorizedException.parse(response); | |
case 403: | |
throw new ForbiddenException(method, uri, xhr.responseText); | |
case 404: | |
throw new ResourceNotFoundException(method, uri, xhr.responseText, false); | |
case 409: | |
{ | |
location = new URI(xhr.getResponseHeader("location")); | |
throw new ConflictingResourceException(settings.type, uri, xhr.responseText, location); | |
} | |
case 410: | |
throw new ResourceNotFoundException(method, uri, xhr.responseText, true); | |
case 412: | |
throw new PreconditionFailedException(method, uri, xhr.responseText); | |
case 0: | |
case 503: | |
throw new ServiceUnavailableException(method, uri, xhr.responseText); | |
} | |
return response; | |
}); | |
}; | |
/** | |
* Adds caching headers to an AJAX request. | |
* | |
* @param {Object} oldValue (optional) the old value of the list | |
* @param {object} settings the key/value pairs used to configure the ajax request */ | |
Utilities.setRequestCachingHeaders = function(oldValue, settings) | |
{ | |
"use strict"; | |
if (!oldValue) | |
return; | |
Preconditions.requireThat(settings, "settings").isSet(); | |
if (oldValue.eTag && typeof (oldValue.eTag) !== "string") | |
throw new TypeError("Invalid oldValue.eTag: " + Utilities.toJson(oldValue.eTag)); | |
if (oldValue.lastModified && typeof (oldValue.lastModified) !== "string") | |
{ | |
throw new TypeError("Invalid oldValue.lastModified: " + | |
Utilities.toJson(oldValue.lastModified)); | |
} | |
if (settings.headers === undefined) | |
settings.headers = {}; | |
settings.headers["If-None-Match"] = oldValue.eTag; | |
settings.headers["If-Modified-Since"] = oldValue.lastModified; | |
}; | |
/** | |
* Adds CSRF and authentication headers for an upcoming request. | |
* | |
* @param {object} settings the key/value pairs used to configure the ajax request | |
*/ | |
Utilities.addCommonHeaders = function(settings) | |
{ | |
"use strict"; | |
if (settings.headers === undefined) | |
settings.headers = {}; | |
switch (settings.type) | |
{ | |
case "GET": | |
case "HEAD": | |
case "OPTIONS": | |
break; | |
default: | |
{ | |
// CSRF protection is only relevant for unsafe methods | |
var csrfToken = RequestScope.csrfToken; | |
Preconditions.requireThat(csrfToken, "csrfToken").isInstanceOf(String); | |
settings.headers[Utilities.csrfToken] = csrfToken; | |
break; | |
} | |
} | |
var authenticationToken = SessionScope.authenticationToken; | |
Preconditions.requireThat(authenticationToken, "authenticationToken").isInstanceOf(String); | |
settings.headers[Utilities.authenticationToken] = authenticationToken; | |
}; | |
/** | |
* Converts an a String to a URI. | |
* | |
* @param {String} string the String to convert | |
* @param {String} name the name of the variable being converted | |
* @return {URI} a URI | |
* @throws {TypeError} if the String could not be converted to a URI | |
*/ | |
Utilities.stringToUri = function(string, name) | |
{ | |
"use strict"; | |
Preconditions.requireThat(name, "name").isInstanceOf(String); | |
Preconditions.requireThat(string, name).isInstanceOf(String); | |
try | |
{ | |
return new URI(string); | |
} | |
catch (e) | |
{ | |
throw new TypeError(name + " is not a valid URI. Value: " + string + ". Cause: " + e.message); | |
} | |
}; | |
/** | |
* Converts an array of Strings to an array of URIs. | |
* | |
* @param {Array.<String>} array the array of Strings to convert | |
* @param {String} name the name of the variable being converted | |
* @return {Array.<URI>} an array of URIs | |
* @throws {TypeError} if an element could not be converted to a URI, or if array is not an array of Strings | |
*/ | |
Utilities.stringToUriArray = function(array, name) | |
{ | |
"use strict"; | |
Preconditions.requireThat(name, "name").isInstanceOf(String); | |
Preconditions.requireThat(array, name).isInstanceOf(Array).elements().isInstanceOf(String); | |
var result = []; | |
for (var i = 0; i < array.length; ++i) | |
{ | |
try | |
{ | |
result.push(new URI(array[i])); | |
} | |
catch (e) | |
{ | |
throw new TypeError(name + " contained a non-URI at index: " + i + ". Value: " + array[i] + ". Cause: " + | |
e.message); | |
} | |
} | |
return result; | |
}; | |
/** | |
* Converts an array of URIs to an array of String. | |
* | |
* @param {Array.<URI>} array the array of URI to convert | |
* @param {String} name the name of the variable being converted | |
* @return {Array.<String>} an array of Strings | |
* @throws {TypeError} if an element could not be converted to a String, or if array is not an array of URI | |
*/ | |
Utilities.uriToStringArray = function(array, name) | |
{ | |
"use strict"; | |
Preconditions.requireThat(name, "name").isInstanceOf(String); | |
Preconditions.requireThat(array, name).isInstanceOf(Array).elements().isInstanceOf(URI); | |
var result = []; | |
for (var i = 0; i < array.length; ++i) | |
result.push(array[i].toString()); | |
return result; | |
}; | |
/** | |
* Adds caching headers from an AJAX response. | |
* | |
* @param {AjaxWithRetry.AjaxResult} response the server response | |
* @param {Object} result the value returned to the user | |
* @return {Object} the updated response object | |
*/ | |
Utilities.setResponseCachingHeaders = function(response, result) | |
{ | |
"use strict"; | |
if (response && typeof (response) !== "object") | |
throw new TypeError("Invalid xhr: " + Utilities.toJson(response)); | |
var settings = response.settings; | |
var xhr = response.xhr; | |
var eTag = xhr.getResponseHeader("ETag"); | |
if (!eTag) | |
{ | |
throw AjaxWithRetry.onError("Unexpected ETag header: " + Utilities.toJson(eTag), | |
settings, xhr); | |
} | |
var lastModified = xhr.getResponseHeader("Last-Modified"); | |
if (!lastModified) | |
{ | |
throw AjaxWithRetry.onError("Unexpected Last-Modified header: " + | |
Utilities.toJson(lastModified), settings, xhr); | |
} | |
result.eTag = eTag; | |
result.lastModified = lastModified; | |
return result; | |
}; | |
/** | |
* Parses the cookie with the given name and returns it's value. | |
* Original impl.: http://www.w3schools.com/js/js_cookies.asp | |
* | |
* @param {String} name the name of the cookie | |
* @return {(String|null)} the cookie value, or null if not found | |
*/ | |
Utilities.getCookieValue = function(name) | |
{ | |
"use strict"; | |
name = (name + "=").toLowerCase(); | |
var ca = document.cookie.split(";"); | |
for (var i = 0; i < ca.length; i++) | |
{ | |
var c = ca[i]; | |
while (c.charAt(0) === " ") | |
c = c.substring(1); | |
if (c.toLowerCase().indexOf(name) !== -1) | |
return c.substring(name.length, c.length); | |
} | |
return null; | |
}; | |
/** | |
* Create the cookie with the given name and value. | |
* Original impl.: http://www.w3schools.com/js/js_cookies.asp | |
* | |
* @param {String} name the name of the cookie | |
* @param {String} value the value of the cookie | |
* @param {number} expiresMilliseconds the maximum number of milliseconds that may elapse before the cookie is | |
* removed | |
*/ | |
Utilities.setCookieValue = function(name, value, expiresMilliseconds) | |
{ | |
"use strict"; | |
var date = new Date(); | |
date.setTime(date.getTime() + expiresMilliseconds); | |
var expires = "expires=" + date.toUTCString(); | |
document.cookie = name + "=" + value + "; Path=/; " + expires; | |
}; | |
/** | |
* Delete the cookie with the given name. | |
* | |
* @param {String} name the cookie name | |
* @see http://www.w3schools.com/js/js_cookies.asp | |
*/ | |
Utilities.deleteCookie = function(name) | |
{ | |
"use strict"; | |
document.cookie = name + "=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; | |
}; | |
/** | |
* Converts a String to a Boolean value. | |
* | |
* @param {string} value the string value | |
* @return {Boolean} the boolean representation | |
*/ | |
Utilities.toBoolean = function(value) | |
{ | |
"use strict"; | |
if (typeof (value) !== "string") | |
throw new TypeError("value must be a string. Was: " + this.toJson(value)); | |
return value.toLowerCase() === "true"; | |
}; | |
module.exports = Utilities; | |
/***/ }, | |
/* 14 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var Utilities = __webpack_require__(13); | |
var BadRequestException = __webpack_require__(45); | |
var ConflictingResourceException = __webpack_require__(11); | |
var UnauthorizedException = __webpack_require__(42); | |
var ForbiddenException = __webpack_require__(16); | |
var Q = __webpack_require__(4); | |
var printStackTrace = __webpack_require__(24); | |
var $ = __webpack_require__(3); | |
// WORKAROUND: https://github.com/webpack/webpack/issues/754 | |
//require("webjars/semantic-ui.js"); | |
/** | |
* @function OnSubmit | |
* | |
* @this Form | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
/** | |
* Creates a new Form. | |
* | |
* @class | |
* @param {Object} $form the form | |
* @param {Object} rules the form validation rules | |
* @param {OnSubmit} onSubmit submits the form | |
* @param {Function} afterStateChanged invoked after the form's state changes | |
* | |
* @property {Object} $form the form | |
* @property {Function} onSubmit submits the form | |
* @property {Function} afterStateChanged invoked after the form's state changes | |
*/ | |
function Form($form, rules, onSubmit, afterStateChanged) | |
{ | |
"use strict"; | |
if (!(this instanceof Form)) | |
{ | |
throw new Error("Form constructor may not be invoked directly. You must use the " + | |
"\"new\" operator."); | |
} | |
Preconditions.requireThat($form, "$form").isSet(); | |
if ($form.length !== 1) | |
{ | |
throw new TypeError("Expecting $form to have a length of 1. It had a length of " + | |
$form.length); | |
} | |
Preconditions.requireThat(rules, "rules").isSet(); | |
Preconditions.requireThat(onSubmit, "onSubmit").isInstanceOf(Function); | |
Preconditions.requireThat(afterStateChanged, "afterStateChanged").isInstanceOf(Function); | |
Object.defineProperty(this, "$form", | |
{ | |
value: $form, | |
enumerable: true | |
}); | |
Utilities.assert(this.getId() !== undefined, "$form must have an id attribute"); | |
Object.defineProperty(this, "onSubmit", | |
{ | |
value: onSubmit, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "afterStateChanged", | |
{ | |
value: afterStateChanged, | |
enumerable: true | |
}); | |
var settings = | |
{ | |
inline: true, | |
on: "blur", | |
onSuccess: function() | |
{ | |
// User attempting to submit the form, and all of its fields are valid | |
this.submit().then(this.afterSubmitSuccess.bind(this)). | |
catch(this.afterSubmitError.bind(this)); | |
}.bind(this), | |
onFailure: function() | |
{ | |
// User attempting to submit the form, and at least one of its fields is invalid. | |
// Hide the error dialog because the form already displays individual error prompts per | |
// field. | |
this.hideError(); | |
}.bind(this) | |
}; | |
this.$form.form(rules, settings); | |
this.$form.find("input").on("blur", this.afterStateChanged.bind(this)); | |
this.$form.on("submit", function() | |
{ | |
// Prevents the form from being submitted, use AJAX | |
return false; | |
}.bind(this)); | |
} | |
/** | |
* Invoked after the form's state changes. | |
*/ | |
Form.prototype.afterStateChanged = function() | |
{ | |
"use strict"; | |
// WARNING: Chrome does not yet support form autofill for forms submitted using AJAX and | |
// pushState(): https://code.google.com/p/chromium/issues/detail?id=43219 | |
history.replaceState(this.getState(), window.title, window.location.href); | |
}; | |
/** | |
* @return {String} the DOM id associated with the component | |
*/ | |
Form.prototype.getId = function() | |
{ | |
"use strict"; | |
return this.$form.attr("id"); | |
}; | |
/** | |
* @typedef {Object} FormState | |
* | |
* @property {String} id the DOM id of the component that generated the state | |
* @property {Object} state an object whose keys map to field names and values map to field | |
* values | |
*/ | |
/** | |
* @return {FormState} the form's state | |
*/ | |
Form.prototype.getState = function() | |
{ | |
"use strict"; | |
return { | |
id: this.getId(), | |
state: this.$form.find(":input").serializeArray() | |
}; | |
}; | |
/** | |
* Sets the form's state. | |
* | |
* @param {FormState} data the form's state | |
*/ | |
Form.prototype.setState = function(data) | |
{ | |
"use strict"; | |
Preconditions.requireThat(data, "data").isSet(); | |
Preconditions.requireThat(data.id, "data.id").isSet(); | |
Preconditions.requireThat(data.state, "data.state").isSet(); | |
this.$form.find(":input").each(function(index, element) | |
{ | |
element.val(data.state.form[index].value); | |
}); | |
}; | |
/** | |
* Displays an error message. | |
* | |
* @param {String} message the error message | |
*/ | |
Form.prototype.showError = function(message) | |
{ | |
"use strict"; | |
this.$form.form("add errors", [message]); | |
var className = this.$form.form("setting", "className"); | |
this.$form.removeClass(className.success).addClass(className.error); | |
}; | |
/** | |
* Hides the error dialog. | |
*/ | |
Form.prototype.hideError = function() | |
{ | |
"use strict"; | |
var className = this.$form.form("setting", "className"); | |
this.$form.form("add errors", []); | |
this.$form.removeClass(className.error); | |
}; | |
/** | |
* Resets the form. | |
*/ | |
Form.prototype.reset = function() | |
{ | |
"use strict"; | |
this.hideError(); | |
var $form = this.$form; | |
var $actions = $form.find(".actions"); | |
var $loader = $form.find(".ui.loader").parent(); | |
$loader.dimmer("hide"); | |
// Process all descendants | |
$actions.find().removeAttr("disabled").show(); | |
// Clear any error prompts that resulted from the user clicking the Cancel button | |
// WORKAROUND: https://github.com/Semantic-Org/Semantic-UI/issues/906 | |
var $fieldsWithErrors = $form.find(".field.error"); | |
var $errorPrompts = $form.form("setting", "selector").prompt; | |
$fieldsWithErrors.removeClass("error").find($errorPrompts).remove(); | |
this.$form[0].reset(); | |
}; | |
/** | |
* Submits the form. | |
* | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
Form.prototype.submit = function() | |
{ | |
"use strict"; | |
var $form = this.$form; | |
var $actions = $form.find(".actions"); | |
var $loader = $form.find(".ui.loader").parent(); | |
return Q.try(function() | |
{ | |
$actions.find().attr("disabled", "disabled"); | |
$actions.hide(); | |
$loader.dimmer("hide"); | |
return this.onSubmit.call(this); | |
}.bind(this)).finally(function() | |
{ | |
$loader.dimmer("show"); | |
$actions.find().attr("disabled", "enabled"); | |
$actions.show(); | |
}); | |
}; | |
/** | |
* Invoked after a failure to submit the form. The default implementation handles TypeError, | |
* BadRequestException, ConflictingResourceException and UnauthorizedException. | |
* | |
* @param {Error} error the reason the operation failed | |
*/ | |
Form.prototype.afterSubmitError = function(error) | |
{ | |
"use strict"; | |
var stack; | |
if (error instanceof TypeError || error instanceof BadRequestException || | |
error instanceof ConflictingResourceException) | |
{ | |
this.showError(error.message); | |
stack = printStackTrace( | |
{ | |
e: error, | |
guess: true | |
}).join("\n"); | |
var message = error.message + "\n" + stack; | |
console.error(message); | |
return; | |
} | |
else if (error instanceof ForbiddenException) | |
{ | |
this.showError(error.message); | |
return; | |
} | |
else if (error instanceof UnauthorizedException) | |
{ | |
if (error.uri.path() === "/authentications") | |
{ | |
// If submitting to authentications page, display "Wrong password" instead of redirecting back to the same | |
// page. | |
this.showError(error.message); | |
return; | |
} | |
else | |
{ | |
window.location.href = error.location; | |
throw error; | |
} | |
} | |
stack = printStackTrace( | |
{ | |
e: error, | |
guess: true | |
}).join("\n"); | |
this.showError(stack.replace(/\r|\r\n|\n/g, "<br/>")); | |
throw error; | |
}; | |
/** | |
* Invoked after successfully submitting the form. | |
*/ | |
Form.prototype.afterSubmitSuccess = function() | |
{ | |
"use strict"; | |
}; | |
/** | |
* Improves the look of the error dialog in case there is only one error. | |
*/ | |
Form.fixTemplate = function() | |
{ | |
"use strict"; | |
$.fn.form.settings.templates.error = Form.errorTemplate; | |
}; | |
/** | |
* @param {String[]} errors a list of errors to display | |
* @return {Object} the HTML to append to the error dialog | |
*/ | |
Form.errorTemplate = function(errors) | |
{ | |
"use strict"; | |
var html; | |
switch (errors.length) | |
{ | |
case 0: | |
break; | |
case 1: | |
{ | |
// Semantic-UI wants a jQuery object so it can append to it: http://stackoverflow.com/a/24259221/14731 | |
// Cause jQuery to parse text as HTML: http://stackoverflow.com/a/24259221/14731 | |
html = "<span>" + errors[0] + "</span>"; | |
break; | |
} | |
default: | |
{ | |
html = "<ul class=\"list\">"; | |
$.each(errors, function(index, value) | |
{ | |
html += "<li>" + value + "</li>"; | |
}); | |
html += "</ul>"; | |
break; | |
} | |
} | |
return $(html); | |
}; | |
Form.fixTemplate(); | |
module.exports = Form; | |
/***/ }, | |
/* 15 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Authentications = __webpack_require__(9); | |
var $ = __webpack_require__(3); | |
var LoginPanel = {}; | |
/** | |
* Initializes the LoginPanel. | |
*/ | |
LoginPanel.configure = function() | |
{ | |
"use strict"; | |
$("#logoutButton").click(function(event) | |
{ | |
var loggedIn = $(event.delegateTarget).parent().hasClass("logout"); | |
if (loggedIn) | |
Authentications.logout(); | |
else | |
Authentications.reload(); | |
}); | |
}; | |
module.exports = LoginPanel; | |
/***/ }, | |
/* 16 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when the server refuses to carry out an operation. | |
* | |
* @class | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
*/ | |
function ForbiddenException(method, uri, message) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
this.name = "ForbiddenException"; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else | |
message = method + " " + uri.toString() + " failed due to insufficient permissions"; | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
ForbiddenException.prototype = Object.create(Error.prototype); | |
module.exports = ForbiddenException; | |
/***/ }, | |
/* 17 */, | |
/* 18 */ | |
/***/ function(module, exports, __webpack_require__) { | |
/** | |
* Creates a new AssertionException. | |
* | |
* @param {String} message explains the failed assertion | |
*/ | |
function AssertionException(message) | |
{ | |
"use strict"; | |
this.name = "AssertionException"; | |
this.message = message; | |
} | |
AssertionException.prototype = Object.create(Error.prototype); | |
AssertionException.prototype.constructor = AssertionException; | |
module.exports = AssertionException; | |
/***/ }, | |
/* 19 */, | |
/* 20 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var ResourceTable = __webpack_require__(21); | |
var Modal = __webpack_require__(22); | |
// WORKAROUND: https://github.com/webpack/webpack/issues/754 | |
//require("semantic-ui/dist/semantic.js"); | |
//require("semantic-ui/dist/semantic.css"); | |
var $ = __webpack_require__(3); | |
var Sidebar = {}; | |
/** | |
* Creates a new Sidebar. | |
* | |
* @class | |
* @param {ResourceTable} table the table of resources on the page | |
* @param {Modal} editModal the modal used to edit a resource, null if the resource cannot be | |
* edited | |
* | |
* @property {Object} $sidebar the sidebar | |
*/ | |
Sidebar.configure = function(table, editModal) | |
{ | |
"use strict"; | |
Preconditions.requireThat(table, "table").isInstanceOf(ResourceTable); | |
if (editModal !== null) | |
Preconditions.requireThat(editModal, "editModal").isInstanceOf(Modal); | |
var $sidebar = $(".ui.sidebar"); | |
Object.defineProperty(Sidebar, "$sidebar", | |
{ | |
value: $sidebar, | |
enumerable: true | |
}); | |
var $tbody = table.$tbody; | |
$sidebar.sidebar( | |
{ | |
transition: "overlay", | |
closable: false, | |
dimPage: false | |
}); | |
if (editModal !== null) | |
{ | |
$tbody.find("tr:not(.template) input:checkbox").on("change", function(event) | |
{ | |
// Hide actions that can only operate on a single row | |
var $selected = $tbody.find("input:checked"); | |
$sidebar.find(".edit").each(function(index, action) | |
{ | |
if ($selected.length > 1) | |
$(action).hide(); | |
else | |
$(action).show(); | |
}); | |
}); | |
$sidebar.find(".edit").click(function() | |
{ | |
var selectedRow = $tbody.find("tr:not(.template) input:checked").parents("tr")[0]; | |
// -2 because rowIndex is 1-based, and it includes the template row | |
var index = selectedRow.rowIndex - 2; | |
var resource = table.indexToResource[index]; | |
table.selectedIndex = index; | |
var oldAfterHidden = editModal.afterHidden; | |
editModal.afterHidden = function() | |
{ | |
oldAfterHidden.call(editModal); | |
delete table.selectedIndex; | |
editModal.afterHidden = oldAfterHidden; | |
}.bind(this); | |
editModal.form.reset(); | |
editModal.form.setState( | |
{ | |
id: editModal.form.getId(), | |
state: resource | |
}); | |
editModal.show(); | |
}.bind(this)); | |
} | |
}; | |
module.exports = Sidebar; | |
/***/ }, | |
/* 21 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var ResourceNotFoundException = __webpack_require__(38); | |
var Utilities = __webpack_require__(13); | |
var ForbiddenException = __webpack_require__(16); | |
var UnauthorizedException = __webpack_require__(42); | |
var Authentications = __webpack_require__(9); | |
var Preconditions = __webpack_require__(6); | |
var Q = __webpack_require__(4); | |
var URI = __webpack_require__(2); | |
var $ = __webpack_require__(3); | |
__webpack_require__(29); | |
/** | |
* Loads the resources associated with the table. | |
* | |
* @callback LoadResources | |
* @return {Q} a Promise that returns {@code Array} of resources on success and | |
* {@code Error} on failure | |
*/ | |
/** | |
* Populates a row from a resource. | |
* | |
* @callback PopulateRow | |
* @this ResourceTable | |
* @param {Object} resource a resource | |
* @param {Object} $row the row to populate | |
* @param {Number} index the row index | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
/** | |
* Convert the json into the correct Resource | |
* | |
* @callback ResourceFromJson | |
* @this ResourceTable | |
* @param {string} json the json object | |
* @return {object} the correct resource instance | |
*/ | |
/** | |
* Creates a new ResourceTable. | |
* | |
* @class | |
* @param {String} rowType the type of rows stored in the table (e.g. "users") | |
* @param {LoadResources} loadResources loads the resources associated with the table | |
* @param {PopulateRow} populateRow populates a row from a resource | |
* @param {ResourceFromJson} resourceFromJson get the resource value from a json object | |
* | |
* @property {String} rowType the type of rows stored in the table (e.g. "users") | |
* @property {LoadResources} loadResources loads the resources associated with the table | |
* @property {PopulateRow} populateRow populates a row from a resource | |
* @property {Array} resources the resources associated with the table | |
* @property {Object} indexToResource maps an index to the table resource at that index | |
* @property {Object} $sidebar the action sidebar | |
* @property {Object} $loader the loader to show while updating the table | |
* @property {Object} $template a template of a table row | |
* @property {Object} $table the table | |
* @property {Object} $thead the table head | |
* @property {Object} $tbody the table body | |
* @property {Number} selectedIndex the index of the row being edited, {@code undefined} | |
* while not editing | |
*/ | |
function ResourceTable(rowType, loadResources, populateRow, resourceFromJson) | |
{ | |
"use strict"; | |
if (!(this instanceof ResourceTable)) | |
{ | |
throw new Error("ResourceTable constructor may not be invoked directly. You must" + | |
" use the \"new\" operator."); | |
} | |
Preconditions.requireThat(rowType, "rowType").isInstanceOf(String).trim().isNotEmpty(); | |
Preconditions.requireThat(loadResources, "loadResources").isInstanceOf(Function); | |
Preconditions.requireThat(populateRow, "populateRow").isInstanceOf(Function); | |
Preconditions.requireThat(resourceFromJson, "resourceFromJson").isInstanceOf(Function); | |
Object.defineProperty(this, "rowType", | |
{ | |
value: rowType, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "loadResources", | |
{ | |
value: loadResources, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "populateRow", | |
{ | |
value: populateRow, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "resourceFromJson", | |
{ | |
value: resourceFromJson, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "resources", | |
{ | |
value: [], | |
enumerable: true, | |
configurable: true | |
}); | |
Object.defineProperty(this, "$sidebar", | |
{ | |
value: $(".ui.sidebar"), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "$table", | |
{ | |
value: $("table"), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "$thead", | |
{ | |
value: this.$table.find("thead"), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "$tbody", | |
{ | |
value: this.$table.find("tbody"), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "$loader", | |
{ | |
value: this.$table.parent().find(".ui.dimmer"), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "$template", | |
{ | |
value: $("tbody .template"), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "indexToResource", | |
{ | |
value: {}, | |
writable: true, | |
enumerable: true | |
}); | |
Utilities.assert(this.getId() !== undefined, "$table must have an id attribute"); | |
this.$sidebar.find(".remove").on("click", function() | |
{ | |
this.onRemoveRows().done(); | |
}.bind(this)); | |
var $headerCheckbox = this.$thead.find("th input:checkbox"); | |
$headerCheckbox.on("change", function(event) | |
{ | |
var headerChecked = $(event.delegateTarget).prop("checked"); | |
var $dataCheckbox = this.$tbody.find("tr:not(.template) input:checkbox"); | |
$dataCheckbox.prop("checked", headerChecked).trigger("change"); | |
}.bind(this)); | |
$headerCheckbox.closest("th").css("user-select", "none"); | |
// CSS has no parent selector: http://stackoverflow.com/a/2000614 | |
var dataCheckbox = this.$tbody.find("tr:not(.template) input:checkbox"); | |
dataCheckbox.closest("td").css("user-select", "none"); | |
} | |
/** | |
* @return {String} the DOM id associated with the component | |
*/ | |
ResourceTable.prototype.getId = function() | |
{ | |
"use strict"; | |
return this.$table.attr("id"); | |
}; | |
/** | |
* @typedef {Object} ResourceTableState | |
* | |
* @property {String} id the DOM id of the component that generated the state | |
* @property {Object} state the table's state | |
*/ | |
/** | |
* @return {ResourceTableState} the table's state | |
*/ | |
ResourceTable.prototype.getState = function() | |
{ | |
"use strict"; | |
var jsonResources = []; | |
for (var i = 0; i < this.resources.length; i++) | |
{ | |
jsonResources.push(this.resources[i].toJson()); | |
} | |
return { | |
id: this.getId(), | |
state: | |
{ | |
indexToResource: this.indexToResource, | |
resources: jsonResources | |
} | |
}; | |
}; | |
/** | |
* Updates the table rows. | |
* | |
* @param {Array} resources the resources to read values from | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
function setRows(resources) | |
{ | |
"use strict"; | |
// jshint validthis:true | |
Preconditions.requireThat(this, "this").isInstanceOf(ResourceTable); | |
Preconditions.requireThat(resources, "resources").isInstanceOf(Array); | |
/** | |
* Causes clicking on a row to select it. | |
* | |
* @param {Event} event the jQuery event object | |
*/ | |
function selectRow(event) | |
{ | |
if (event.target.tagName === "A") | |
{ | |
// Clicking on links should not select the row | |
return; | |
} | |
if (event.target.tagName === "LABEL" || event.target.tagName === "INPUT") | |
{ | |
// The checkbox already has a "click" handler (typically set by subclasses overriding afterLoadingElements()) | |
return; | |
} | |
// Clicking on the row's background should toggle its selection state | |
var $checkbox = $(event.delegateTarget).find("input:checkbox"); | |
$checkbox.prop("checked", !$checkbox.prop("checked")).trigger("change"); | |
} | |
var newIndexToResource = {}; | |
var result = new Q(); | |
var newRows = []; | |
resources.forEach(function(resource, index) | |
{ | |
result = result.then(function() | |
{ | |
Preconditions.requireThat(this, "this").isInstanceOf(ResourceTable); | |
var instance = this.$template.clone(); | |
instance.removeClass("template"); | |
return this.populateRow.call(this, resource, instance, index).then(function() | |
{ | |
newIndexToResource[index] = resource; | |
newRows.push(instance); | |
}); | |
}.bind(this)); | |
}, this); | |
return result.then(function() | |
{ | |
// data-version indicates how many times the rows have been reloaded (used by UI tests) | |
var version = this.$table.attr("data-version"); | |
++version; | |
this.$table.attr("data-version", version); | |
// Detach the template | |
var $template = this.$template.parentsUntil("tbody").add(this.$template).first().detach(); | |
// Remove all rows except for the template | |
this.$tbody.empty().append($template, newRows); | |
// Add a click listener to each row | |
newRows.forEach(function(row) | |
{ | |
$(row).on("click", selectRow.bind(this)); | |
}.bind(this)); | |
this.indexToResource = newIndexToResource; | |
}.bind(this)); | |
} | |
/** | |
* Sets the table's state. | |
* | |
* @param {ResourceTableState} data the table's state | |
*/ | |
ResourceTable.prototype.setState = function(data) | |
{ | |
"use strict"; | |
Preconditions.requireThat(data, "data").isSet(); | |
Preconditions.requireThat(data.id, "data.id").isSet(); | |
Preconditions.requireThat(data.state, "data.state").isSet(); | |
Preconditions.requireThat(data.state.indexToResource, "data.state.indexToResource").isSet(); | |
Preconditions.requireThat(data.state.resources, "data.state.resources").isSet(); | |
var resources = data.state.resources; | |
for (var i = 0; i < resources.length; ++i) | |
resources[i] = this.resourceFromJson(resources[i]); | |
this.indexToResource = data.state.indexToResource; | |
setRows.call(this, data.state.resources).done(); | |
}; | |
/** | |
* Reloads the rows. | |
* | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
ResourceTable.prototype.reloadRows = function() | |
{ | |
"use strict"; | |
this.$loader.dimmer("show"); | |
return this.loadResources().then(function(resources) | |
{ | |
Object.defineProperty(this, "resources", | |
{ | |
value: resources, | |
enumerable: true, | |
configurable: true | |
}); | |
return setRows.call(this, resources); | |
}.bind(this)).then(this.afterReloadingRows.bind(this)); | |
}; | |
/** | |
* Invoked after reloading the rows. | |
*/ | |
ResourceTable.prototype.afterReloadingRows = function() | |
{ | |
"use strict"; | |
this.$tbody.find("tr:not(.template) input:checkbox").on("change", function(event) | |
{ | |
// Show the sidebar if at least one row is selected | |
var $selected = $("tbody input:checked"); | |
if ($selected.length > 0) | |
this.$sidebar.sidebar("show"); | |
else | |
{ | |
var $headerCheckbox = this.$thead.find("th input:checkbox"); | |
$headerCheckbox.prop("checked", false); | |
this.$sidebar.sidebar("hide"); | |
} | |
event.stopPropagation(); | |
}.bind(this)); | |
this.$loader.dimmer("hide"); | |
this.$sidebar.sidebar("hide"); | |
}; | |
/** | |
* Removes rows. | |
* | |
* @return {Q} a Promise that returns {@code undefined} on success and {@code Error} on | |
* failure | |
*/ | |
ResourceTable.prototype.onRemoveRows = function() | |
{ | |
"use strict"; | |
var checkboxes = $("tbody tr:not(.template) input:checked"); | |
return Q.try(function() | |
{ | |
var prompt = "Are you sure you wish to remove the selected " + this.rowType + "?"; | |
var headerCheckbox = $("th input:checkbox"); | |
headerCheckbox.prop("checked", false); | |
if (checkboxes.length <= 0 || !confirm(prompt)) | |
return; | |
return Q.try(function() | |
{ | |
var result = new Q(); | |
checkboxes.get().forEach(function(checkbox, index) | |
{ | |
result = result.then(function() | |
{ | |
var index = checkbox.id.replace("checkbox-", ""); | |
var row = this.indexToResource[index]; | |
return row.delete(); | |
}.bind(this)).catch(function(error) | |
{ | |
if (error instanceof ForbiddenException) | |
alert(error.message); | |
else if (error instanceof UnauthorizedException) | |
{ | |
window.location.href = error.location; | |
return; | |
} | |
else if (!(error instanceof ResourceNotFoundException && error.removed)) | |
Utilities.throwUnhandledError(error); | |
}); | |
}, this); | |
return result.done(this.reloadRows.bind(this)); | |
}.bind(this)); | |
}.bind(this)); | |
}; | |
/** | |
* Adds a link. | |
* | |
* @param {Element} $container the element to append the link to | |
* @param {String} text the text of the link | |
* @param {URI} location the location the address to link to | |
*/ | |
ResourceTable.prototype.addLink = function($container, text, location) | |
{ | |
"use strict"; | |
Preconditions.requireThat(location, "location").isInstanceOf(URI); | |
if ($container.length > 0) | |
$container.append(" "); | |
$container.append($("<a>").attr("class", "mini ui button").attr("href", Authentications.addToUri(location)). | |
text(text)); | |
}; | |
/** | |
* @return {String} the DOM id associated with the component | |
*/ | |
ResourceTable.prototype.getId = function() | |
{ | |
"use strict"; | |
return this.$table.attr("id"); | |
}; | |
module.exports = ResourceTable; | |
/***/ }, | |
/* 22 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Utilities = __webpack_require__(13); | |
var Form = __webpack_require__(14); | |
var Preconditions = __webpack_require__(6); | |
/** | |
* Creates a new Modal. | |
* | |
* @class | |
* @param {Object} $modal the modal dialog (must have a unique id) | |
* @param {Object} formRules the form validation rules | |
* @param {OnSubmit} onSubmit submits the form | |
* @param {Function} afterStateChanged invoked after the modal's state changes | |
* | |
* @property {Object} $modal the modal dialog | |
* @property {String} id the modal's id attribute | |
* @property {Object} form the embedded form | |
* @property {Function} afterStateChanged invoked after the modal's state changes | |
*/ | |
function Modal($modal, formRules, onSubmit, afterStateChanged) | |
{ | |
"use strict"; | |
if (!(this instanceof Modal)) | |
{ | |
throw new Error("Modal constructor may not be invoked directly. You must use the " + | |
"\"new\" operator."); | |
} | |
Preconditions.requireThat($modal, "$modal").isSet(); | |
if ($modal.length !== 1) | |
{ | |
throw new TypeError("$modal must have a length of 1. It had a length of " + | |
$modal.length); | |
} | |
Preconditions.requireThat(formRules, "formRules").isSet(); | |
Preconditions.requireThat(afterStateChanged, "afterStateChanged").isInstanceOf(Function); | |
Object.defineProperty(this, "$modal", | |
{ | |
value: $modal, | |
enumerable: true | |
}); | |
Utilities.assert(this.getId() !== undefined, "$modal must have an id attribute"); | |
Object.defineProperty(this, "form", | |
{ | |
value: new Form($modal.find("form"), formRules, onSubmit, afterStateChanged), | |
enumerable: true | |
}); | |
Object.defineProperty(this, "afterStateChanged", | |
{ | |
value: afterStateChanged, | |
enumerable: true | |
}); | |
// The component state must remain separate from its view, otherwise when the user hits the | |
// back/forward buttons in mid-animation the wrong state gets saved | |
var state = | |
{ | |
visible: false | |
}; | |
Object.defineProperty(this, "state", | |
{ | |
value: state, | |
enumerable: true | |
}); | |
var oldAfterSubmitSuccess = this.form.afterSubmitSuccess; | |
this.form.afterSubmitSuccess = function() | |
{ | |
oldAfterSubmitSuccess.call(this.form); | |
this.$modal.modal("hide"); | |
}.bind(this); | |
this.$modal.modal( | |
{ | |
/** | |
* Invoked after the submit button is clicked. | |
* @return {Boolean} false if the default form submission process should be aborted | |
*/ | |
onApprove: function() | |
{ | |
// Force the form to undergo validation before submitting | |
return false; | |
}, | |
onVisible: function() | |
{ | |
this.afterVisible(); | |
}.bind(this), | |
onHidden: function() | |
{ | |
this.afterHidden(); | |
}.bind(this), | |
// WORKAROUND: https://github.com/Semantic-Org/Semantic-UI/issues/1432 | |
duration: 0 | |
}); | |
} | |
/** | |
* @return {String} the DOM id associated with the component | |
*/ | |
Modal.prototype.getId = function() | |
{ | |
"use strict"; | |
return this.$modal.attr("id"); | |
}; | |
/** | |
* @typedef {Object} ModalState | |
* | |
* @property {String} id the DOM id of the component that generated the state | |
* @property {Object} state the modal's state | |
*/ | |
/** | |
* @return {ModalState} the modal's state | |
*/ | |
Modal.prototype.getState = function() | |
{ | |
"use strict"; | |
return { | |
id: this.getId(), | |
state: | |
{ | |
visible: this.state.visible, | |
form: this.form.getState() | |
} | |
}; | |
}; | |
/** | |
* Sets the modal's state. | |
* | |
* @param {ModalState} data the modal's state | |
*/ | |
Modal.prototype.setState = function(data) | |
{ | |
"use strict"; | |
Preconditions.requireThat(data, "data").isSet(); | |
Preconditions.requireThat(data.id, "data.id").isSet(); | |
Preconditions.requireThat(data.state, "data.state").isSet(); | |
Preconditions.requireThat(data.state.visible, "data.state.visible").isInstanceOf(Boolean); | |
Preconditions.requireThat(data.state.form, "data.state.form").isSet(); | |
if (data.state.visible !== this.$modal.is(":visible")) | |
{ | |
if (data.state.visible) | |
this.$modal.modal("show"); | |
else | |
this.$modal.modal("hide"); | |
this.state.visible = data.state.visible; | |
} | |
this.form.setState(data.state.form); | |
}; | |
/** | |
* Shows the modal dialog. | |
*/ | |
Modal.prototype.show = function() | |
{ | |
"use strict"; | |
// Transition to the new (empty) state before populating it. Cannot wait for animation to | |
// complete because hitting the back button shouldn't push a new state. | |
history.pushState(null, window.title, window.location.href); | |
this.state.visible = true; | |
this.afterStateChanged(); | |
this.$modal.modal("show"); | |
}; | |
/** | |
* Hides the modal dialog. | |
*/ | |
Modal.prototype.hide = function() | |
{ | |
"use strict"; | |
// Transition to the new (empty) state before populating it. Cannot wait for animation to | |
// complete because hitting the back button shouldn't push a new state. | |
history.pushState(null, window.title, window.location.href); | |
this.state.visible = false; | |
this.afterStateChanged(); | |
this.$modal.modal("hide"); | |
}; | |
/** | |
* Invoked after the modal becomes visible. | |
*/ | |
Modal.prototype.afterVisible = function() | |
{ | |
"use strict"; | |
}; | |
/** | |
* Invoked after the modal becomes hidden. | |
*/ | |
Modal.prototype.afterHidden = function() | |
{ | |
"use strict"; | |
}; | |
module.exports = Modal; | |
/***/ }, | |
/* 23 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when the server is too busy to carry out an operation. | |
* | |
* @class | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
*/ | |
function ServiceUnavailableException(method, uri, message) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
this.name = "ServiceUnavailableException"; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else | |
message = method + " " + uri.toString() + " failed because the server is unavailable at the moment."; | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
ServiceUnavailableException.prototype = Object.create(Error.prototype); | |
module.exports = ServiceUnavailableException; | |
/***/ }, | |
/* 24 */ | |
/***/ function(module, exports, __webpack_require__) { | |
module.exports = printStackTrace; | |
/***/ }, | |
/* 25 */, | |
/* 26 */, | |
/* 27 */, | |
/* 28 */, | |
/* 29 */ | |
/***/ function(module, exports, __webpack_require__) { | |
// removed by extract-text-webpack-plugin | |
/***/ }, | |
/* 30 */, | |
/* 31 */, | |
/* 32 */, | |
/* 33 */, | |
/* 34 */, | |
/* 35 */, | |
/* 36 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Utilities = {}; | |
/** | |
* @param {Object} value a value | |
* @param {Object} type a type (e.g. String not "string") | |
* @return {Boolean} true if {@code value} is of type {@code type} | |
*/ | |
Utilities.instanceOf = function(value, type) | |
{ | |
"use strict"; | |
if ((value === undefined || value === null || type === undefined || type === null) && | |
value !== type) | |
{ | |
return false; | |
} | |
// instanceof works for most objects, constructor works for literals | |
// http://stackoverflow.com/a/1185835/14731 | |
return value instanceof type || value.constructor === type || | |
value.constructor instanceof type; | |
}; | |
/** | |
* Returns an object's class name. | |
* | |
* @param {object} object an object | |
* @return {String} the name of the object's class | |
* @see http://stackoverflow.com/a/332429/14731 | |
*/ | |
Utilities.getClassName = function(object) | |
{ | |
"use strict"; | |
// We cannot modify Object.prototype directly because libraries like JQuery blow up: | |
// http://stackoverflow.com/q/14941657/14731 | |
// See also http://sugarjs.com/native#enumerable_properties. | |
if (object === undefined) | |
return "undefined"; | |
if (object === null) | |
return "null"; | |
var results = object.constructor.toString().match(/^function ([^(]+)\(/); | |
if (results && results.length > 1) | |
{ | |
if (object === Array) | |
return "Array"; | |
var constructor = results[1]; | |
if (constructor === "Function") | |
{ | |
// Built-in types (e.g. String) | |
return object.toString().match(/^function ([^(]+)\(/)[1]; | |
} | |
// User-defined types | |
return constructor; | |
} | |
// Instances of built-in types (e.g. "abc") | |
return Object.prototype.toString.call(object).match(/^\[object (.*)\]$/)[1]; | |
}; | |
module.exports = Utilities; | |
/***/ }, | |
/* 37 */ | |
/***/ function(module, exports, __webpack_require__) { | |
// Gatekeeper for circular dependencies: http://stackoverflow.com/a/26809254/14731 | |
var Utilities = __webpack_require__(36); | |
var ObjectPreconditions = __webpack_require__(48); | |
var StringPreconditions = __webpack_require__(49); | |
var UriPreconditions = __webpack_require__(50); | |
var ArrayPreconditions = __webpack_require__(51); | |
var URI = __webpack_require__(2); | |
/** | |
* Ensures that parameter is of the specified type. | |
* | |
* @param {Object} type a type | |
* @return {Preconditions} this | |
* @throws {TypeError} if the parameter is not of the specified type | |
*/ | |
ObjectPreconditions.prototype.isInstanceOf = function(type) | |
{ | |
"use strict"; | |
if (!Utilities.instanceOf(this.parameter, type)) | |
{ | |
if (this.parameter === undefined) | |
throw new TypeError(this.name + " was undefined instead of being of type " + Utilities.getClassName(type)); | |
if (this.parameter === null) | |
throw new TypeError(this.name + " was null instead of being of type " + Utilities.getClassName(type)); | |
throw new TypeError(this.name + " was of type " + | |
Utilities.getClassName(this.parameter) + " instead of " + | |
Utilities.getClassName(type) + ": " + this.parameter.toString()); | |
} | |
if (type === String) | |
return new StringPreconditions(this.parameter, this.name); | |
if (type === URI) | |
return new UriPreconditions(this.parameter, this.name); | |
if (Array.isArray(this.parameter)) | |
return new ArrayPreconditions(this.parameter, this.name); | |
return this; | |
}; | |
module.exports = ObjectPreconditions; | |
/***/ }, | |
/* 38 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when a referenced resource cannot be found. | |
* | |
* @class | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
* @property {boolean} removed true if the resource used to exist, false if unknown | |
* | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* @param {boolean} removed true if the resource used to exist, false if unknown | |
*/ | |
function ResourceNotFoundException(method, uri, message, removed) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
this.name = "ResourceNotFoundException"; | |
this.removed = removed; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else if (removed) | |
message = method + " " + uri.toString() + " failed because the resource was removed"; | |
else | |
message = method + " " + uri.toString() + " failed because the resource was not found"; | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "removed", | |
{ | |
value: removed, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
ResourceNotFoundException.prototype = Object.create(Error.prototype); | |
module.exports = ResourceNotFoundException; | |
/***/ }, | |
/* 39 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var Q = __webpack_require__(4); | |
var printStackTrace = __webpack_require__(24); | |
var $ = __webpack_require__(3); | |
var safeRetries = 2; | |
/** | |
* @function RetryDelayFunction | |
* Returns the amount of time to wait after a failure. | |
* | |
* @param {Number} retries the number of times the operation has been retried | |
* @return {Number} the number of milliseconds to wait before retrying the operation | |
*/ | |
/** | |
* @class | |
* @property {RetryDelayFunction} defaultDelayFunction the default delay function | |
* @property {Array} errorCodes the HTTP response codes that should trigger a retry (default: | |
* {@code [502, 503, 504]}) | |
* @property {(Number|undefined)} retries the number of times to retry the request before allowing the | |
* exception to bubble up (default: {@code undefined}). {@code undefined} implies {@code 2} for | |
* {@code HTTP HEAD, GET, PUT, DELETE}, otherwise {@code 0}). | |
* @property {RetryDelayFunction} delayFunction a function that indicates how long to wait before | |
* retrying the operation (default: {@code defaultDelayFunction}) | |
*/ | |
function AjaxWithRetry() | |
{ | |
"use strict"; | |
var delayInitialIntervalMs = 250; | |
var delayIntervalMultiplier = 1.5; | |
var delayRandomizationFactor = 0.5; | |
/** | |
* @function RetryDelayFunction | |
*/ | |
this.defaultDelayFunction = function(retries) | |
{ | |
var retryInterval = delayInitialIntervalMs * Math.pow(delayIntervalMultiplier, retries); | |
var delta = retryInterval * delayRandomizationFactor; | |
var min = retryInterval - delta; | |
var max = retryInterval + delta; | |
return (Math.random() * (max - min + 1)) + min; | |
}; | |
Object.defineProperty(this, "errorCodes", | |
{ | |
value: [502, 503, 504], | |
writable: true, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "delayFunction", | |
{ | |
value: this.defaultDelayFunction, | |
writable: true, | |
enumerable: true | |
}); | |
return this; | |
} | |
/** | |
* @typedef {Object} AjaxResult | |
* | |
* @property {object} settings the key/value pairs used to configure the ajax request | |
* @property {XMLHttpRequest} xhr the XMLHttpRequest associated with the request | |
*/ | |
/** | |
* Invokes an AJAX request, retrying using an exponential back-off in case of HTTP response codes | |
* contained in {@code errorCodes}. Based on | |
* http://javadoc.google-http-java-client.googlecode.com/hg/1.15.0-rc/com/google/api/client/util/ExponentialBackOff.html | |
* | |
* @param {URI} url the request URL | |
* @param {Object} settings configures the AJAX request. See http://api.jquery.com/jQuery.ajax/ | |
* @return {Q} a Promise that returns {@code AjaxResult} on success and {@code Error} on failure | |
*/ | |
AjaxWithRetry.prototype.request = function(url, settings) | |
{ | |
"use strict"; | |
var urlAsString = url.toString(); | |
// Check if user overrode "retries" value | |
if (typeof (settings.retries) === "number") | |
this.retries = settings.retries; | |
else | |
{ | |
switch (settings.type) | |
{ | |
case "HEAD": | |
case "GET": | |
case "PUT": | |
case "DELETE": | |
{ | |
this.retries = safeRetries; | |
break; | |
} | |
default: | |
{ | |
this.retries = 0; | |
break; | |
} | |
} | |
} | |
/** | |
* Invokes an operation, retrying after a delay if it fails. | |
* | |
* @this AjaxWithRetry | |
* @param {Number} runs the number of times the operation has run | |
* @return {Q} a Promise that returns {@code AjaxResult} on success and {@code Error} on | |
* failure | |
*/ | |
function sendRequest(runs) | |
{ | |
// jshint validthis:true | |
Preconditions.requireThat(this, "this").isInstanceOf(AjaxWithRetry); | |
return Q.promise(function(resolve) | |
{ | |
return $.ajax(urlAsString, settings).then(function(data, textStatus, jqXHR) | |
{ | |
delete jqXHR.then; // treat xhr as a non-promise | |
resolve( | |
{ | |
settings: this, | |
xhr: jqXHR | |
}); | |
}, function(jqXHR, textStatus, errorThrown) | |
{ | |
if (errorThrown && typeof (errorThrown) !== "string") | |
throw errorThrown; | |
delete jqXHR.then; // treat xhr as a non-promise | |
resolve( | |
{ | |
settings: this, | |
xhr: jqXHR | |
}); | |
}); | |
}).then(function(response) | |
{ | |
Preconditions.requireThat(this, "this").isInstanceOf(AjaxWithRetry); | |
++runs; | |
var xhr = response.xhr; | |
if (this.errorCodes.indexOf(xhr.status) < 0 || runs > this.retries) | |
{ | |
// Valid response code or too many retries | |
return response; | |
} | |
var minimumDelay; | |
if (xhr.status === 503) | |
{ | |
minimumDelay = xhr.getResponseHeader("Retry-After"); | |
// Convert seconds to milliseconds | |
if (minimumDelay !== null && typeof (minimumDelay) === "number") | |
minimumDelay *= 1000; | |
else | |
minimumDelay = 0; | |
} | |
var delayMs = Math.max(minimumDelay, this.delayFunction(runs)); | |
return Q.delay(delayMs).then(function() | |
{ | |
Preconditions.requireThat(this, "this").isInstanceOf(AjaxWithRetry); | |
return sendRequest.call(this, runs); | |
}.bind(this)); | |
}.bind(this)); | |
} | |
Preconditions.requireThat(this, "this").isInstanceOf(AjaxWithRetry); | |
return sendRequest.call(this, 0); | |
}; | |
/** | |
* @param {String} reason the reason the operation failed | |
* @param {object} settings the key/value pairs used to configure the ajax request | |
* @param {XMLHttpRequest} xhr the XMLHttpRequest associated with the request | |
* @return {Error} an Error that describes an AJAX error | |
*/ | |
AjaxWithRetry.prototype.onError = function(reason, settings, xhr) | |
{ | |
"use strict"; | |
return AjaxWithRetry.onError(reason, settings, xhr); | |
}; | |
/** | |
* @param {String} reason the reason the operation failed | |
* @param {object} settings the key/value pairs used to configure the ajax request | |
* @param {XMLHttpRequest} xhr the XMLHttpRequest associated with the request | |
* @return {Error} an Error that describes an AJAX error | |
*/ | |
AjaxWithRetry.onError = function(reason, settings, xhr) | |
{ | |
"use strict"; | |
if (reason instanceof Error) | |
{ | |
var stack = printStackTrace( | |
{ | |
e: reason, | |
guess: true | |
}).join("\n"); | |
reason = reason.message + "\n" + stack; | |
} | |
var headers = xhr.getAllResponseHeaders(); | |
return new Error(reason + ".\n" + settings.type + " " + settings.url + | |
" returned HTTP " + xhr.status + ".\n" + | |
"Headers: " + headers + | |
"Entity: " + xhr.responseText); | |
}; | |
module.exports = AjaxWithRetry; | |
/***/ }, | |
/* 40 */ | |
/***/ function(module, exports, __webpack_require__) { | |
/* global indexedDB:false */ | |
var Preconditions = __webpack_require__(6); | |
var Q = __webpack_require__(4); | |
/** | |
* @property {String} name the database name | |
* @property {Number} version the database version | |
* @property {String} authenticationTable the name of the authentication table | |
*/ | |
var Database = {}; | |
Database.name = "com.realestate.Database"; | |
Database.version = 7; | |
Database.authenticationMap = "com.realestate.authentication"; | |
/** | |
* Create a database store object if not exists | |
* | |
* @param {IDBDatabase} database the database | |
* @param {String} tableName the name of the table to create | |
* @param {Object} storeOptions the store configuration (see {@code optionalParameters} at | |
* https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase.createObjectStore#Parameters) | |
* @returns {ObjectStore} created object store, or null if store with the same name already exists | |
*/ | |
function createTable(database, tableName, storeOptions) | |
{ | |
"use strict"; | |
if (!database.objectStoreNames.contains(tableName)) | |
return database.createObjectStore(tableName, storeOptions); | |
return null; | |
} | |
/** | |
* Handles a database upgrade. | |
* | |
* @param {IDBVersionChangeEvent} event describes the event | |
*/ | |
function databaseUpgrade(event) | |
{ | |
"use strict"; | |
var database = event.target.result; | |
createTable(database, Database.authenticationMap); | |
} | |
/** | |
* Opens the database. | |
* | |
* @param {Function} onSuccess a callback that is invoked with the {@code IDBDatabase} on success | |
* @throws {Error} on failure | |
*/ | |
Database.open = function(onSuccess) | |
{ | |
"use strict"; | |
if (!indexedDB) | |
throw "Browser does not support IndexedDB"; | |
var request = indexedDB.open(Database.name, Database.version); | |
request.onupgradeneeded = databaseUpgrade; | |
/** | |
* Invoked if the database cannot be opened. | |
* | |
* @param {Event} event describes the event | |
* @throws {String} describes the error | |
*/ | |
request.onerror = function(event) | |
{ | |
throw "Database error: " + event.target.errorCode; | |
}; | |
/** | |
* Invoked after opening the database. | |
* | |
* @param {Event} event describes the event | |
*/ | |
request.onsuccess = function(event) | |
{ | |
onSuccess(event.target.result); | |
}; | |
}; | |
/** | |
* Handles a database error. | |
* | |
* @param {Q} promise a Promise to reject with an {@code Error} describing what went wrong | |
*/ | |
Database.onError = function(promise) | |
{ | |
"use strict"; | |
return function(event) | |
{ | |
promise.reject("Database error: " + event.target.errorCode); | |
}; | |
}; | |
/** | |
* Closes the database. | |
* | |
* @param {IDBDatabase} database the database | |
*/ | |
Database.close = function(database) | |
{ | |
"use strict"; | |
database.close(); | |
}; | |
/** | |
* Delete the database. | |
*/ | |
Database.delete = function() | |
{ | |
"use strict"; | |
var request = indexedDB.deleteDatabase(Database.name); | |
request.onerror = function(event) | |
{ | |
throw "Database error: " + event.target.errorCode; | |
}; | |
request.onsuccess = function(event) | |
{ | |
console.log("Deleted database successfully"); | |
}; | |
}; | |
/** | |
* Sets a record in the database. If an existing record has the same id, it will be overwriten. | |
* @param {String} table the table that will contain the record | |
* @param {String} id a unique ID that identifies the data | |
* @param {Any} data the data to insert | |
*/ | |
Database.put = function(table, id, data) | |
{ | |
"use strict"; | |
Preconditions.requireThat(table, "table").isInstanceOf(String); | |
Preconditions.requireThat(id, "id").isInstanceOf(String); | |
Preconditions.requireThat(data, "data").isDefined(); | |
return Q.try(function() | |
{ | |
var result = Q.defer(); | |
Database.open(function(database) | |
{ | |
var transaction = database.transaction([table], "readwrite"); | |
var store = transaction.objectStore(table); | |
// Records are uniquely identified by an id | |
var record = | |
{ | |
id: id, | |
data: data | |
}; | |
var request = store.put(record); | |
request.onerror = function(e) | |
{ | |
Database.close(database); | |
result.reject(e.target); | |
}; | |
request.onsuccess = function() | |
{ | |
Database.close(database); | |
result.resolve(record); | |
}; | |
}); | |
return result.promise; | |
}); | |
}; | |
/** | |
* Looks up a record by its id. | |
* | |
* @param {String} table the table containing the record | |
* @param {String} id the data id | |
* @returns {Q} a Promise that returns the record on success and {@code undefined} on failure | |
*/ | |
Database.getById = function(table, id) | |
{ | |
"use strict"; | |
Preconditions.requireThat(table, "table").isInstanceOf(String); | |
Preconditions.requireThat(id, "id").isInstanceOf(String); | |
return Q.try(function() | |
{ | |
var result = Q.defer(); | |
Database.open(function(database) | |
{ | |
var transaction = database.transaction([table]); | |
var store = transaction.objectStore(table); | |
var request = store.get(id); | |
request.onerror = function(e) | |
{ | |
Database.close(database); | |
result.reject(e.target); | |
}; | |
request.onsuccess = function(e) | |
{ | |
Database.close(database); | |
result.resolve(e.target.result); | |
}; | |
}); | |
return result.promise; | |
}); | |
}; | |
/** | |
* @function QueryFilter | |
* @param {Object} record a database record | |
* @return {Boolean} true if the record should be included in the result set, false if it should be excluded | |
*/ | |
/** | |
* Looks up database records. | |
* @param {String} table the table to search in | |
* @param {QueryFilter} filter a function that determines which records are returned. If {@code undefined} will | |
* return all data records stored in table | |
* <p> | |
* Example - Get all records named "Jane": | |
* <code> | |
* Database.query("table", function(data){ | |
* return data.name === "Jane"; | |
* } | |
* </code> | |
* @returns {Q} a Promise that will return an {@code Array} of database records | |
*/ | |
Database.query = function(table, filter) | |
{ | |
"use strict"; | |
Preconditions.requireThat(table, "table").isInstanceOf(String); | |
return Q.try(function() | |
{ | |
var result = Q.defer(); | |
Database.open(function(database) | |
{ | |
var transaction = database.transaction([table]); | |
var store = transaction.objectStore(table); | |
var recordSet = []; | |
store.openCursor().onsuccess = function(event) | |
{ | |
var cursor = event.target.result; | |
if (cursor) | |
{ | |
if (!filter || filter(cursor.value.data)) | |
recordSet.push(cursor.value); | |
cursor.continue(); | |
} | |
else | |
result.resolve(recordSet); | |
}; | |
}); | |
return result.promise; | |
}); | |
}; | |
/** | |
* Removes a record from a table. | |
* | |
* @param {String} table the table containing the record | |
* @param {String} id the record ID | |
* @returns {Q} a Promise that returns {@code true} if the record is removed, {@code false} otherwise. | |
*/ | |
Database.remove = function(table, id) | |
{ | |
"use strict"; | |
Preconditions.requireThat(table, "table").isInstanceOf(String); | |
Preconditions.requireThat(id, "id").isInstanceOf(String); | |
return Q.try(function() | |
{ | |
var result = Q.defer(); | |
Database.open(function(database) | |
{ | |
var transaction = database.transaction([table], "readwrite"); | |
var store = transaction.objectStore(table); | |
var request = store.delete(id); | |
request.onerror = function(e) | |
{ | |
Database.close(database); | |
result.reject(e.target); | |
}; | |
request.onsuccess = function(e) | |
{ | |
Database.close(database); | |
result.resolve(e.target.result); | |
}; | |
}); | |
return result.promise; | |
}); | |
}; | |
module.exports = Database; | |
/***/ }, | |
/* 41 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
// This singleton holds session-scoped variables, like the authentication token. | |
var SessionScope = {}; | |
var authenticationToken = null; | |
Object.defineProperty(SessionScope, "authenticationToken", | |
{ | |
/** | |
* @return {String} the authentication token | |
*/ | |
get: function() | |
{ | |
"use strict"; | |
return authenticationToken; | |
}, | |
/** | |
* @param {String} value the new authentication token | |
*/ | |
set: function(value) | |
{ | |
"use strict"; | |
Preconditions.requireThat(value, "value").isInstanceOf(String).trim().isNotEmpty(); | |
authenticationToken = value; | |
}, | |
enumerable: true | |
}); | |
module.exports = SessionScope; | |
/***/ }, | |
/* 42 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var AjaxWithRetry = __webpack_require__(39); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when a request is redirected to another URL. | |
* | |
* @class | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
* @property {URI} location the uri of new URL | |
* | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* @param {URI} location the URL of an HTML login page | |
*/ | |
function UnauthorizedException(method, uri, message, location) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
Preconditions.requireThat(location, "location").isInstanceOf(URI); | |
this.name = "UnauthorizedException"; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else | |
{ | |
message = method + " " + uri.toString() + " was unauthorized. Clients may authenticate at: " + | |
location; | |
} | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "location", | |
{ | |
value: location, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
UnauthorizedException.prototype = Object.create(Error.prototype); | |
/** | |
* Parses a UnauthorizedException. | |
* | |
* @param {AjaxWithRetry.AjaxResult} response the server response | |
* @return {UnauthorizedException} a new {@code UnauthorizedException} | |
* @throws {SyntaxError} if the response could not be parsed | |
*/ | |
UnauthorizedException.parse = function(response) | |
{ | |
"use strict"; | |
var settings = response.settings; | |
var method = settings.type; | |
var uri = new URI(settings.url); | |
var xhr = response.xhr; | |
var challenge = xhr.getResponseHeader("WWW-Authenticate"); | |
if (challenge === null) | |
throw AjaxWithRetry.onError("Missing WWW-Authenticate header", settings, xhr); | |
challenge = challenge.trim(); | |
var index = challenge.indexOf(" "); | |
var challengeType = challenge.substring(0, index); | |
if (challengeType !== "com.realestate.Authentication") | |
throw new SyntaxError("Unexpected challenge: " + challengeType); | |
var parameters = challenge.substring(index, challenge.length).split(","); | |
for (var i = 0; i < parameters.length; ++i) | |
{ | |
var keyValue = []; | |
if (parameters[i].indexOf("=") !== -1) | |
{ | |
keyValue.push(parameters[i].substring(0, parameters[i].indexOf("="))); | |
keyValue.push(parameters[i].substring(parameters[i].indexOf("=") + 1, | |
parameters[i].length)); | |
} | |
if (keyValue.length !== 2) | |
throw new SyntaxError("Unexpected key-value pair: " + keyValue); | |
var key = keyValue[0].trim(); | |
var value = keyValue[1].trim(); | |
if (key !== "Location") | |
throw new SyntaxError("Unexpected key: " + key); | |
var location; | |
try | |
{ | |
location = new URI(value).absoluteTo(uri); | |
} | |
catch (e) | |
{ | |
throw AjaxWithRetry.onError(e, settings, xhr); | |
} | |
return new UnauthorizedException(method, uri, xhr.responseText, location); | |
} | |
throw new SyntaxError("Missing Location parameter: " + challenge); | |
}; | |
module.exports = UnauthorizedException; | |
/***/ }, | |
/* 43 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when a request references a non-existent resource. | |
* | |
* @class | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
* @property {URI} reference the uri of the non-existent resource | |
* | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* @param {URI} reference the uri of the non-existent resource | |
*/ | |
function ReferenceNotFoundException(method, uri, message, reference) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
Preconditions.requireThat(reference, "reference").isInstanceOf(URI); | |
this.name = "ReferenceNotFoundException"; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri + " returned: " + message; | |
else | |
message = method + " " + uri + " referenced a non-existent resource: " + reference; | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "reference", | |
{ | |
value: reference, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
ReferenceNotFoundException.prototype = Object.create(Error.prototype); | |
module.exports = ReferenceNotFoundException; | |
/***/ }, | |
/* 44 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when an operation fails because one of the preconditions the client specified was false | |
* (e.g. one client attempted to modify a resource that was already modified by another client). | |
* | |
* @class | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
*/ | |
function PreconditionFailedException(method, uri, message) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
this.name = "PreconditionFailedException"; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else | |
message = method + " " + uri.toString() + " failed because a request precondition was not met"; | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
PreconditionFailedException.prototype = Object.create(Error.prototype); | |
module.exports = PreconditionFailedException; | |
/***/ }, | |
/* 45 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var URI = __webpack_require__(2); | |
/** | |
* Thrown when an operation would leave the resource in an incomplete or inconsistent state. | |
* | |
* @class | |
* @param {String} method the HTTP method of the request | |
* @param {URI} uri the URI of the request | |
* @param {String} message the message returned by the server | |
* | |
* @property {String} method the HTTP method of the request | |
* @property {URI} uri the URI of the request | |
* @property {String} message the message returned by the server | |
*/ | |
function BadRequestException(method, uri, message) | |
{ | |
"use strict"; | |
Preconditions.requireThat(uri, "uri").isInstanceOf(URI); | |
this.name = "BadRequestException"; | |
if (message && message.trim().length > 0) | |
message = method + " " + uri.toString() + " returned: " + message; | |
else | |
{ | |
message = method + " " + uri.toString() + " would have left the resource in an incomplete or inconsistent " + | |
"state"; | |
} | |
Object.defineProperty(this, "method", | |
{ | |
value: method, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "uri", | |
{ | |
value: uri, | |
enumerable: true | |
}); | |
Object.defineProperty(this, "message", | |
{ | |
value: message, | |
enumerable: true | |
}); | |
} | |
BadRequestException.prototype = Object.create(Error.prototype); | |
module.exports = BadRequestException; | |
/***/ }, | |
/* 46 */ | |
/***/ function(module, exports, __webpack_require__) { | |
/* alphanum.js (C) Brian Huisman | |
* Based on the Alphanum Algorithm by David Koelle | |
* The Alphanum Algorithm is discussed at http://www.DaveKoelle.com | |
* | |
* Distributed under same license as original | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2.1 of the License, or any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
*/ | |
/* ******************************************************************** | |
* Alphanum Array prototype version | |
* - Much faster than the sort() function version | |
* - Ability to specify case sensitivity at runtime is a bonus | |
* | |
*/ | |
Array.prototype.alphanumSort = function(caseInsensitive) { | |
for (var z = 0, t; t = this[z]; z++) { | |
this[z] = new Array(); | |
var x = 0, y = -1, n = 0, i, j; | |
while (i = (j = t.charAt(x++)).charCodeAt(0)) { | |
var m = (i == 46 || (i >=48 && i <= 57)); | |
if (m !== n) { | |
this[z][++y] = ""; | |
n = m; | |
} | |
this[z][y] += j; | |
} | |
} | |
this.sort(function(a, b) { | |
for (var x = 0, aa, bb; (aa = a[x]) && (bb = b[x]); x++) { | |
if (caseInsensitive) { | |
aa = aa.toLowerCase(); | |
bb = bb.toLowerCase(); | |
} | |
if (aa !== bb) { | |
var c = Number(aa), d = Number(bb); | |
if (c == aa && d == bb) { | |
return c - d; | |
} else return (aa > bb) ? 1 : -1; | |
} | |
} | |
return a.length - b.length; | |
}); | |
for (var z = 0; z < this.length; z++) | |
this[z] = this[z].join(""); | |
} | |
/* ******************************************************************** | |
* Alphanum sort() function version - case sensitive | |
* - Slower, but easier to modify for arrays of objects which contain | |
* string properties | |
* | |
*/ | |
function alphanum(a, b) { | |
function chunkify(t) { | |
var tz = new Array(); | |
var x = 0, y = -1, n = 0, i, j; | |
while (i = (j = t.charAt(x++)).charCodeAt(0)) { | |
var m = (i == 46 || (i >=48 && i <= 57)); | |
if (m !== n) { | |
tz[++y] = ""; | |
n = m; | |
} | |
tz[y] += j; | |
} | |
return tz; | |
} | |
var aa = chunkify(a); | |
var bb = chunkify(b); | |
for (x = 0; aa[x] && bb[x]; x++) { | |
if (aa[x] !== bb[x]) { | |
var c = Number(aa[x]), d = Number(bb[x]); | |
if (c == aa[x] && d == bb[x]) { | |
return c - d; | |
} else return (aa[x] > bb[x]) ? 1 : -1; | |
} | |
} | |
return aa.length - bb.length; | |
} | |
/* ******************************************************************** | |
* Alphanum sort() function version - case insensitive | |
* - Slower, but easier to modify for arrays of objects which contain | |
* string properties | |
* | |
*/ | |
function alphanumCase(a, b) { | |
function chunkify(t) { | |
var tz = new Array(); | |
var x = 0, y = -1, n = 0, i, j; | |
while (i = (j = t.charAt(x++)).charCodeAt(0)) { | |
var m = (i == 46 || (i >=48 && i <= 57)); | |
if (m !== n) { | |
tz[++y] = ""; | |
n = m; | |
} | |
tz[y] += j; | |
} | |
return tz; | |
} | |
var aa = chunkify(a.toLowerCase()); | |
var bb = chunkify(b.toLowerCase()); | |
for (x = 0; aa[x] && bb[x]; x++) { | |
if (aa[x] !== bb[x]) { | |
var c = Number(aa[x]), d = Number(bb[x]); | |
if (c == aa[x] && d == bb[x]) { | |
return c - d; | |
} else return (aa[x] > bb[x]) ? 1 : -1; | |
} | |
} | |
return aa.length - bb.length; | |
} | |
/***/ }, | |
/* 47 */, | |
/* 48 */ | |
/***/ function(module, exports, __webpack_require__) { | |
/** | |
* Creates a new ObjectPreconditions. | |
* | |
* @class | |
* @param {Object} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {Object} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function ObjectPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
/** | |
* Ensures that parameter is defined and non-null. | |
* | |
* @return {ObjectPreconditions} this | |
* @throws {TypeError} if the parameter is undefined or null | |
*/ | |
ObjectPreconditions.prototype.isSet = function() | |
{ | |
"use strict"; | |
if (this.parameter === undefined) | |
throw new TypeError(this.name + " must be defined"); | |
if (this.parameter === null) | |
throw new TypeError(this.name + " may not be null"); | |
return this; | |
}; | |
/** | |
* Ensures that parameter is defined. | |
* | |
* @return {ObjectPreconditions} this | |
* @throws {TypeError} if the parameter is undefined | |
*/ | |
ObjectPreconditions.prototype.isDefined = function() | |
{ | |
"use strict"; | |
if (this.parameter === undefined) | |
throw new TypeError(this.name + " may not be undefined"); | |
return this; | |
}; | |
/** | |
* Ensures that parameter is non-null. | |
* | |
* @return {ObjectPreconditions} this | |
* @throws {TypeError} if the parameter is undefined | |
*/ | |
ObjectPreconditions.prototype.isNotNull = function() | |
{ | |
"use strict"; | |
if (this.parameter === null) | |
throw new TypeError(this.name + " may not be null"); | |
return this; | |
}; | |
module.exports = ObjectPreconditions; | |
/***/ }, | |
/* 49 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Preconditions = __webpack_require__(6); | |
var AbstractObjectPreconditions = __webpack_require__(48); | |
/** | |
* Creates a new StringPreconditions. | |
* | |
* @class | |
* @extends AbstractObjectPreconditions | |
* @param {String} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {String} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function StringPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
StringPreconditions.prototype = Object.create(AbstractObjectPreconditions.prototype); | |
StringPreconditions.prototype.constructor = AbstractObjectPreconditions; | |
/** | |
* Trims the string. | |
* | |
* @return {StringPreconditions} this | |
*/ | |
StringPreconditions.prototype.trim = function() | |
{ | |
"use strict"; | |
this.parameter = this.parameter.trim(); | |
return this; | |
}; | |
/** | |
* Ensures that the parameter is an empty string. | |
* | |
* @return {StringPreconditions} this | |
* @throws {TypeError} if the parameter is not an empty string | |
*/ | |
StringPreconditions.prototype.isEmpty = function() | |
{ | |
"use strict"; | |
if (this.parameter.length > 0) | |
throw new TypeError(this.name + " must be empty"); | |
return this; | |
}; | |
/** | |
* Ensures that the parameter is not an empty string. | |
* | |
* @return {StringPreconditions} this | |
* @throws {TypeError} if the parameter is an empty string | |
*/ | |
StringPreconditions.prototype.isNotEmpty = function() | |
{ | |
"use strict"; | |
if (this.parameter.length <= 0) | |
throw new TypeError(this.name + " may not be empty"); | |
return this; | |
}; | |
/** | |
* Ensures that the parameter's length is within range. | |
* | |
* @param {Number} minimum the minimum acceptable length | |
* @param {Number} maximum the maximum acceptable length | |
* @return {StringPreconditions} this | |
* @throws {TypeError} if the parameter is an empty string | |
*/ | |
StringPreconditions.prototype.lengthIn = function(minimum, maximum) | |
{ | |
"use strict"; | |
Preconditions.requireThat(minimum, "minimum").isInstanceOf(Number); | |
Preconditions.requireThat(maximum, "maximum").isInstanceOf(Number); | |
if (this.parameter.length < minimum || this.parameter.length > maximum) | |
{ | |
throw new TypeError(this.name + "'s length must be in the range [" + minimum + ", " + maximum + "], was " + | |
this.parameter.length() + ". " + this.name + ": \"" + this.parameter.toString() + "\""); | |
} | |
return this; | |
}; | |
module.exports = StringPreconditions; | |
/***/ }, | |
/* 50 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var AbstractObjectPreconditions = __webpack_require__(48); | |
/** | |
* Creates a new UriPreconditions. | |
* | |
* @class | |
* @extends ObjectPreconditions | |
* @param {URI} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {URI} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function UriPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
UriPreconditions.prototype = Object.create(AbstractObjectPreconditions.prototype); | |
UriPreconditions.prototype.constructor = AbstractObjectPreconditions; | |
/** | |
* Ensures that the URI is absolute. | |
* | |
* @return {StringPreconditions} this | |
* @throws {TypeError} if the parameter path is not absolute | |
*/ | |
UriPreconditions.prototype.isAbsolute = function() | |
{ | |
"use strict"; | |
if (!this.parameter.is("absolute")) | |
throw new TypeError(this.name + " must be absolute: " + this.parameter.toString()); | |
return this; | |
}; | |
/** | |
* Ensures that the URI is relative. | |
* | |
* @return {StringPreconditions} this | |
* @throws {TypeError} if the parameter path is not a relative | |
*/ | |
UriPreconditions.prototype.isRelative = function() | |
{ | |
"use strict"; | |
if (!this.parameter.is("relative")) | |
throw new TypeError(this.name + " must be relative: " + this.parameter.toString()); | |
return this; | |
}; | |
module.exports = UriPreconditions; | |
/***/ }, | |
/* 51 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var AbstractObjectPreconditions = __webpack_require__(48); | |
var ObjectArrayPreconditions = __webpack_require__(53); | |
/** | |
* Creates a new ArrayPreconditions. | |
* | |
* @class | |
* @extends ObjectPreconditions | |
* @param {Array} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {Array} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function ArrayPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
ArrayPreconditions.prototype = Object.create(AbstractObjectPreconditions.prototype); | |
ArrayPreconditions.prototype.constructor = AbstractObjectPreconditions; | |
/** | |
* Returns preconditions for the array elements. | |
* | |
* @return {ObjectArrayPreconditions} this | |
*/ | |
ArrayPreconditions.prototype.elements = function() | |
{ | |
"use strict"; | |
return new ObjectArrayPreconditions(this.parameter, this.name); | |
}; | |
module.exports = ArrayPreconditions; | |
/***/ }, | |
/* 52 */, | |
/* 53 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Utilities = __webpack_require__(36); | |
var AbstractObjectPreconditions = __webpack_require__(48); | |
var StringArrayPreconditions = __webpack_require__(54); | |
var UriArrayPreconditions = __webpack_require__(55); | |
var URI = __webpack_require__(2); | |
/** | |
* Creates a new ObjectArrayPreconditions. | |
* | |
* @class | |
* @extends ObjectPreconditions | |
* @param {Array} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {Array} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function ObjectArrayPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
ObjectArrayPreconditions.prototype = Object.create(AbstractObjectPreconditions.prototype); | |
ObjectArrayPreconditions.prototype.constructor = AbstractObjectPreconditions; | |
/** | |
* Ensures that array elements are of the specified type. | |
* | |
* @param {Object} type a type (e.g. String not "string") | |
* @return {ObjectArrayPreconditions} this | |
* @throws {TypeError} if an element is not of the specified type | |
*/ | |
ObjectArrayPreconditions.prototype.isInstanceOf = function(type) | |
{ | |
"use strict"; | |
for (var i = 0; i < this.parameter.length; ++i) | |
{ | |
if (!Utilities.instanceOf(this.parameter[i], type)) | |
{ | |
throw new TypeError(this.name + " index " + i + " contained an element of type " + | |
Utilities.getClassName(this.parameter[i]) + " instead of " + | |
Utilities.getClassName(type) + ": " + this.parameter.toString()); | |
} | |
} | |
if (type === String) | |
return new StringArrayPreconditions(this.parameter, this.name); | |
if (type === URI) | |
return new UriArrayPreconditions(this.parameter, this.name); | |
return this; | |
}; | |
module.exports = ObjectArrayPreconditions; | |
/***/ }, | |
/* 54 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Utilities = __webpack_require__(36); | |
var AbstractObjectPreconditions = __webpack_require__(48); | |
// WORKAROUND: https://github.com/webpack/webpack/issues/754 | |
//require("sugar"); | |
/** | |
* Creates a new StringArrayPreconditions. | |
* | |
* @class | |
* @extends AbstractObjectPreconditions | |
* @param {Array} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {Array} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function StringArrayPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
StringArrayPreconditions.prototype = Object.create(AbstractObjectPreconditions.prototype); | |
StringArrayPreconditions.prototype.constructor = AbstractObjectPreconditions; | |
/** | |
* Ensures that array elements are of the specified type. | |
* | |
* @param {Object} type a type (e.g. String not "string") | |
* @return {StringArrayPreconditions} this | |
* @throws {TypeError} if an element is not of the specified type | |
*/ | |
StringArrayPreconditions.prototype.isInstanceOf = function(type) | |
{ | |
"use strict"; | |
if (type !== String) | |
{ | |
throw new TypeError(this.name + " index 0 contained an element of type String instead of " + | |
Utilities.getClassName(type) + ": " + this.parameter.toString()); | |
} | |
return this; | |
}; | |
/** | |
* Trims the strings in all array elements. | |
* | |
* @return {StringArrayPreconditions} this | |
*/ | |
StringArrayPreconditions.prototype.trim = function() | |
{ | |
"use strict"; | |
this.parameter = this.parameter.clone(); | |
for (var i = 0; i < this.parameter.length; ++i) | |
this.parameter[i] = this.parameter[i].trim(); | |
return this; | |
}; | |
/** | |
* Ensures that all elements are an empty string. | |
* | |
* @return {StringArrayPreconditions} this | |
* @throws {TypeError} if the array contains a non-empty string | |
*/ | |
StringArrayPreconditions.prototype.isEmpty = function() | |
{ | |
"use strict"; | |
for (var i = 0; i < this.parameter.length; ++i) | |
{ | |
if (this.parameter[i].length > 0) | |
{ | |
throw new TypeError(this.name + " contained a non-empty string at index: " + i + ". Value: " + | |
this.parameter[i].toString()); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Ensures that all elements are not an empty string. | |
* | |
* @return {StringArrayPreconditions} this | |
* @throws {TypeError} if the array contains an empty string | |
*/ | |
StringArrayPreconditions.prototype.isNotEmpty = function() | |
{ | |
"use strict"; | |
for (var i = 0; i < this.parameter.length; ++i) | |
{ | |
if (this.parameter[i].length <= 0) | |
throw new TypeError(this.name + " contained empty string at index: " + i); | |
} | |
return this; | |
}; | |
module.exports = StringArrayPreconditions; | |
/***/ }, | |
/* 55 */ | |
/***/ function(module, exports, __webpack_require__) { | |
var Utilities = __webpack_require__(36); | |
var AbstractObjectPreconditions = __webpack_require__(48); | |
var URI = __webpack_require__(2); | |
// WORKAROUND: https://github.com/webpack/webpack/issues/754 | |
//require("webjars/sugar.js"); | |
/** | |
* Creates a new UriArrayPreconditions. | |
* | |
* @class | |
* @extends ObjectPreconditions | |
* @param {Array} parameter the value of the parameter | |
* @param {String} name the name of the parameter | |
* | |
* @property {Array} parameter the parameter value | |
* @property {String} name the parameter name | |
*/ | |
function UriArrayPreconditions(parameter, name) | |
{ | |
"use strict"; | |
this.parameter = parameter; | |
this.name = name; | |
} | |
UriArrayPreconditions.prototype = Object.create(AbstractObjectPreconditions.prototype); | |
UriArrayPreconditions.prototype.constructor = AbstractObjectPreconditions; | |
/** | |
* Ensures that array elements are of the specified type. | |
* | |
* @param {Object} type a type (e.g. String not "string") | |
* @return {UriArrayPreconditions} this | |
* @throws {TypeError} if an element is not of the specified type | |
*/ | |
UriArrayPreconditions.prototype.isInstanceOf = function(type) | |
{ | |
"use strict"; | |
if (type !== URI) | |
{ | |
throw new TypeError(this.name + " index 0 contained an element of type URI instead of " + | |
Utilities.getClassName(type) + ": " + this.parameter.toString()); | |
} | |
return this; | |
}; | |
/** | |
* Ensures that all array elements are absolute. | |
* | |
* @return {UriArrayPreconditions} this | |
* @throws {TypeError} if an array element is not absolute | |
*/ | |
UriArrayPreconditions.prototype.isAbsolute = function() | |
{ | |
"use strict"; | |
for (var i = 0; i < this.parameter.length; ++i) | |
{ | |
if (!this.parameter[i].is("absolute")) | |
{ | |
throw new TypeError(this.name + " contained a non-absolute URI at index: " + i + ". Value: " + | |
this.parameter[i].toString()); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Ensures that all array elements are relative. | |
* | |
* @return {StringArrayPreconditions} this | |
* @throws {TypeError} if the parameter path is not relative | |
*/ | |
UriArrayPreconditions.prototype.isRelative = function() | |
{ | |
"use strict"; | |
for (var i = 0; i < this.parameter.length; ++i) | |
{ | |
if (!this.parameter[i].is("relative")) | |
{ | |
throw new TypeError(this.name + " contained a non-relative URI at index: " + i + ". Value: " + | |
this.parameter[i].toString()); | |
} | |
} | |
return this; | |
}; | |
module.exports = UriArrayPreconditions; | |
/***/ } | |
/******/ ]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment