Skip to content

Instantly share code, notes, and snippets.

@cowwoc
Created February 9, 2015 04:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cowwoc/b752cf667c049af05cec to your computer and use it in GitHub Desktop.
Save cowwoc/b752cf667c049af05cec to your computer and use it in GitHub Desktop.
/******/ (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("&nbsp;");
$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