diff --git a/UI/JsLibraries/backbone.pageable.js b/UI/JsLibraries/backbone.pageable.js index f63288064..23f0b3e45 100644 --- a/UI/JsLibraries/backbone.pageable.js +++ b/UI/JsLibraries/backbone.pageable.js @@ -1,5 +1,5 @@ /* - backbone-pageable 1.3.0 + backbone-pageable 1.3.1 http://github.com/wyuenho/backbone-pageable Copyright (c) 2013 Jimmy Yuen Ho Wong @@ -574,6 +574,17 @@ /** Change the page size of this collection. + Under most if not all circumstances, you should call this method to + change the page size of a pageable collection because it will keep the + pagination state sane. By default, the method will recalculate the + current page number to one that will retain the current page's models + when increasing the page size. When decreasing the page size, this method + will retain the last models to the current page that will fit into the + smaller page size. + + If `options.first` is true, changing the page size will also reset the + current page back to the first page instead of trying to be smart. + For server mode operations, changing the page size will trigger a #fetch and subsequently a `reset` event. @@ -586,6 +597,8 @@ @param {number} pageSize The new page size to set to #state. @param {Object} [options] {@link #fetch} options. + @param {boolean} [options.first=false] Reset the current page number to + the first page if `true`. @param {boolean} [options.fetch] If `true`, force a fetch in client mode. @throws {TypeError} If `pageSize` is not a finite integer. @@ -598,14 +611,24 @@ setPageSize: function (pageSize, options) { pageSize = finiteInt(pageSize, "pageSize"); - options = options || {}; + options = options || {first: false}; - this.state = this._checkState(_extend({}, this.state, { + var state = this.state; + var totalPages = ceil(state.totalRecords / pageSize); + var currentPage = max(state.firstPage, + floor(totalPages * + (state.firstPage ? + state.currentPage : + state.currentPage + 1) / + state.totalPages)); + + state = this.state = this._checkState(_extend({}, state, { pageSize: pageSize, - totalPages: ceil(this.state.totalRecords / pageSize) + currentPage: options.first ? state.firstPage : currentPage, + totalPages: totalPages })); - return this.getPage(this.state.currentPage, options); + return this.getPage(state.currentPage, _omit(options, ["first"])); }, /** @@ -992,13 +1015,14 @@ encouraged to override #parseState and #parseRecords instead. @param {Object} resp The deserialized response data from the server. + @param {Object} the options for the ajax request @return {Array.} An array of model objects */ - parse: function (resp) { - var newState = this.parseState(resp, _clone(this.queryParams), _clone(this.state)); + parse: function (resp, options) { + var newState = this.parseState(resp, _clone(this.queryParams), _clone(this.state), options); if (newState) this.state = this._checkState(_extend({}, this.state, newState)); - return this.parseRecords(resp); + return this.parseRecords(resp, options); }, /** @@ -1016,10 +1040,16 @@ `totalRecords` value is enough to trigger a full pagination state recalculation. - parseState: function (resp, queryParams, state) { + parseState: function (resp, queryParams, state, options) { return {totalRecords: resp.total_entries}; } + If you want to use header fields use: + + parseState: function (resp, queryParams, state, options) { + return {totalRecords: options.xhr.getResponseHeader("X-total")}; + } + This method __MUST__ return a new state object instead of directly modifying the #state object. The behavior of directly modifying #state is undefined. @@ -1027,10 +1057,12 @@ @param {Object} resp The deserialized response data from the server. @param {Object} queryParams A copy of #queryParams. @param {Object} state A copy of #state. + @param {Object} [options] The options passed through from + `parse`. (backbone >= 0.9.10 only) @return {Object} A new (partial) state object. */ - parseState: function (resp, queryParams, state) { + parseState: function (resp, queryParams, state, options) { if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) { var newState = _clone(state); @@ -1059,10 +1091,12 @@ response is returned directly. @param {Object} resp The deserialized response data from the server. + @param {Object} [options] The options passed through from the + `parse`. (backbone >= 0.9.10 only) @return {Array.} An array of model objects */ - parseRecords: function (resp) { + parseRecords: function (resp, options) { if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) { return resp[1]; } @@ -1138,7 +1172,7 @@ kvp = extraKvps[i]; v = kvp[1]; v = _isFunction(v) ? v.call(thisCopy) : v; - data[kvp[0]] = v; + if (v != null) data[kvp[0]] = v; } var fullCol = this.fullCollection, links = this.links; @@ -1212,11 +1246,11 @@ @param {string} [sortKey=this.state.sortKey] See `state.sortKey`. @param {number} [order=this.state.order] See `state.order`. + @param {(function(Backbone.Model, string): Object) | string} [sortValue] See #setSorting. See [Backbone.Collection.comparator](http://backbonejs.org/#Collection-comparator). */ - _makeComparator: function (sortKey, order) { - + _makeComparator: function (sortKey, order, sortValue) { var state = this.state; sortKey = sortKey || state.sortKey; @@ -1224,8 +1258,12 @@ if (!sortKey || !order) return; + if (!sortValue) sortValue = function (model, attr) { + return model.get(attr); + }; + return function (left, right) { - var l = left.get(sortKey), r = right.get(sortKey), t; + var l = sortValue(left, sortKey), r = sortValue(right, sortKey), t; if (order === 1) t = l, l = r, r = t; if (l === r) return 0; else if (l < r) return -1; @@ -1244,6 +1282,11 @@ `sortKey` to `null` removes the comparator from both the current page and the full collection. + If a `sortValue` function is given, it will be passed the `(model, + sortKey)` arguments and is used to extract a value from the model during + comparison sorts. If `sortValue` is not given, `model.get(sortKey)` is + used for sorting. + @chainable @param {string} sortKey See `state.sortKey`. @@ -1252,6 +1295,7 @@ @param {"server"|"client"} [options.side] By default, `"client"` if `mode` is `"client"`, `"server"` otherwise. @param {boolean} [options.full=true] + @param {(function(Backbone.Model, string): Object) | string} [options.sortValue] */ setSorting: function (sortKey, order, options) { @@ -1270,7 +1314,7 @@ options = _extend({side: mode == "client" ? mode : "server", full: true}, options); - var comparator = this._makeComparator(sortKey, order); + var comparator = this._makeComparator(sortKey, order, options.sortValue); var full = options.full, side = options.side; diff --git a/UI/JsLibraries/lodash.underscore.js b/UI/JsLibraries/lodash.underscore.js index e2df3e4f0..1320c9dd0 100644 --- a/UI/JsLibraries/lodash.underscore.js +++ b/UI/JsLibraries/lodash.underscore.js @@ -18,6 +18,9 @@ /** Used internally to indicate various things */ var indicatorObject = {}; + /** Used to avoid reference errors in `createIterator` */ + var iteratorObject = {}; + /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */ var keyPrefix = +new Date + ''; @@ -561,7 +564,7 @@ * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); * // => ['one', 'two', 'three'] (order is not guaranteed) */ - var keys = !nativeKeys ? shimKeys : function(object) { + var keys = iteratorObject.keys = !nativeKeys ? shimKeys : function(object) { if (!isObject(object)) { return []; } @@ -4445,7 +4448,6 @@ /*--------------------------------------------------------------------------*/ - // expose Lo-Dash // some AMD build optimizers, like r.js, check for specific condition patterns like the following: if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { // Expose Lo-Dash to the global object even when an AMD loader is present in diff --git a/UI/JsLibraries/messenger.js b/UI/JsLibraries/messenger.js index 5d8108d05..07e690b6e 100644 --- a/UI/JsLibraries/messenger.js +++ b/UI/JsLibraries/messenger.js @@ -1,4 +1,4 @@ -/*! messenger 1.3.3 */ +/*! messenger 1.3.5 */ /* * This file begins the output concatenated into messenger.js * @@ -993,23 +993,22 @@ window.Messenger.Events = (function() { } handlers = {}; _.each(['error', 'success'], function(type) { + var originalHandler; + originalHandler = opts[type]; return handlers[type] = function() { - var data, defaultOpts, handlerResp, msgOpts, reason, resp, responseOpts, xhr, _base, _ref10, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9; + var data, defaultOpts, handlerResp, msgOpts, reason, resp, responseOpts, xhr, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9; resp = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - if ((_ref3 = opts[type]) != null ? _ref3._originalHandler : void 0) { - opts[type] = opts[type]._originalHandler; - } - _ref4 = _this._normalizeResponse.apply(_this, resp), reason = _ref4[0], data = _ref4[1], xhr = _ref4[2]; + _ref3 = _this._normalizeResponse.apply(_this, resp), reason = _ref3[0], data = _ref3[1], xhr = _ref3[2]; if (type === 'success' && !(msg.errorCount != null) && m_opts.showSuccessWithoutError === false) { m_opts['successMessage'] = null; } if (type === 'error') { - if ((_ref5 = m_opts.errorCount) == null) { + if ((_ref4 = m_opts.errorCount) == null) { m_opts.errorCount = 0; } m_opts.errorCount += 1; } - handlerResp = m_opts.returnsPromise ? resp[0] : typeof (_base = opts[type])._originalHandler === "function" ? _base._originalHandler.apply(_base, resp) : void 0; + handlerResp = m_opts.returnsPromise ? resp[0] : typeof originalHandler === "function" ? originalHandler.apply(null, resp) : void 0; responseOpts = _this._getHandlerResponse(handlerResp); if (_.isString(responseOpts)) { responseOpts = { @@ -1020,21 +1019,21 @@ window.Messenger.Events = (function() { msg.hide(); return; } - if (type === 'error' && ((m_opts.ignoredErrorCodes != null) && (_ref6 = xhr != null ? xhr.status : void 0, __indexOf.call(m_opts.ignoredErrorCodes, _ref6) >= 0))) { + if (type === 'error' && ((m_opts.ignoredErrorCodes != null) && (_ref5 = xhr != null ? xhr.status : void 0, __indexOf.call(m_opts.ignoredErrorCodes, _ref5) >= 0))) { msg.hide(); return; } defaultOpts = { message: getMessageText(type, xhr), type: type, - events: (_ref7 = events[type]) != null ? _ref7 : {}, + events: (_ref6 = events[type]) != null ? _ref6 : {}, hideOnNavigate: type === 'success' }; msgOpts = $.extend({}, m_opts, defaultOpts, responseOpts); - if (typeof ((_ref8 = msgOpts.retry) != null ? _ref8.allow : void 0) === 'number') { + if (typeof ((_ref7 = msgOpts.retry) != null ? _ref7.allow : void 0) === 'number') { msgOpts.retry.allow--; } - if (type === 'error' && (xhr != null ? xhr.status : void 0) >= 500 && ((_ref9 = msgOpts.retry) != null ? _ref9.allow : void 0)) { + if (type === 'error' && (xhr != null ? xhr.status : void 0) >= 500 && ((_ref8 = msgOpts.retry) != null ? _ref8.allow : void 0)) { if (msgOpts.retry.delay == null) { if (msgOpts.errorCount < 4) { msgOpts.retry.delay = 10; @@ -1043,7 +1042,7 @@ window.Messenger.Events = (function() { } } if (msgOpts.hideAfter) { - if ((_ref10 = msgOpts._hideAfter) == null) { + if ((_ref9 = msgOpts._hideAfter) == null) { msgOpts._hideAfter = msgOpts.hideAfter; } msgOpts.hideAfter = msgOpts._hideAfter + msgOpts.retry.delay; @@ -1087,7 +1086,6 @@ window.Messenger.Events = (function() { handler = handlers[type]; old = opts[type]; opts[type] = handler; - opts[type]._originalHandler = old; } } msg._actionInstance = m_opts.action.apply(m_opts, [opts].concat(__slice.call(args))); diff --git a/UI/JsLibraries/sugar.js b/UI/JsLibraries/sugar.js index 0e9dd46bd..b6a10f7c4 100644 --- a/UI/JsLibraries/sugar.js +++ b/UI/JsLibraries/sugar.js @@ -322,7 +322,7 @@ function escapeRegExp(str) { if(!isString(str)) str = string(str); - return str.replace(/([\\/'*+?|()\[\]{}.^$])/g,'\\$1'); + return str.replace(/([\\/\'*+?|()\[\]{}.^$])/g,'\\$1'); } @@ -4876,7 +4876,7 @@ * ***/ - number.extend({ + extend(number, true, false, { /*** * @method upto(, [fn], [step] = 1) @@ -4897,7 +4897,7 @@ }, /*** - * @method clamp([start], [end]) + * @method clamp([start] = Infinity, [end] = Infinity) * @returns Number * @short Constrains the number so that it is between [start] and [end]. * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method. @@ -4905,6 +4905,17 @@ ***/ 'clamp': function(start, end) { return new Range(start, end).clamp(this); + }, + + /*** + * @method cap([max] = Infinity) + * @returns Number + * @short Constrains the number so that it is between [start] and [end]. + * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method. + * + ***/ + 'cap': function(max) { + return this.clamp(Undefined, max); } }); @@ -5638,7 +5649,7 @@ } function selectFromObject(obj, args, select) { - var result = {}, match; + var match, result = obj instanceof Hash ? new Hash : {}; iterateOverObject(obj, function(key, value) { match = false; flattenedArgs(args, function(arg) { @@ -5816,17 +5827,18 @@ * ***/ 'merge': function(target, source, deep, resolve) { - var key, val; + var key, val, goDeep; // Strings cannot be reliably merged thanks to // their properties not being enumerable in < IE8. - if(target && typeof source != 'string') { + if(target && typeof source !== 'string') { for(key in source) { if(!hasOwnProperty(source, key) || !target) continue; - val = source[key]; + val = source[key]; + goDeep = deep && isObjectPrimitive(val); // Conflict! if(isDefined(target[key])) { // Do not merge. - if(resolve === false) { + if(resolve === false && !goDeep) { continue; } // Use the result of the callback as the result. @@ -5835,7 +5847,7 @@ } } // Deep merging. - if(deep === true && val && isObjectPrimitive(val)) { + if(goDeep) { if(isDate(val)) { val = new date(val.getTime()); } else if(isRegExp(val)) { @@ -6032,7 +6044,6 @@ buildObjectExtend(); buildObjectInstanceMethods(ObjectHashMethods, Hash); - /*** * @package RegExp * @dependency core @@ -6979,7 +6990,7 @@ ***/ 'capitalize': function(all) { var lastResponded; - return this.toLowerCase().replace(all ? /[\s\S]/g : /^\S/, function(lower) { + return this.toLowerCase().replace(all ? /[^']/g : /^\S/, function(lower) { var upper = lower.toUpperCase(), result; result = lastResponded ? lower : upper; lastResponded = upper !== lower; @@ -6990,8 +7001,8 @@ /*** * @method assign(, , ...) * @returns String - * @short Assigns variables to tokens in a string. - * @extra If an object is passed, it's properties can be assigned using the object's keys. If a non-object (string, number, etc.) is passed it can be accessed by the argument number beginning with 1 (as with regex tokens). Multiple objects can be passed and will be merged together (original objects are unaffected). + * @short Assigns variables to tokens in a string, demarcated with `{}`. + * @extra If an object is passed, it's properties can be assigned using the object's keys (i.e. {name}). If a non-object (string, number, etc.) is passed it can be accessed by the argument number beginning with {1} (as with regex tokens). Multiple objects can be passed and will be merged together (original objects are unaffected). * @example * * 'Welcome, Mr. {name}.'.assign({ name: 'Franklin' }) -> 'Welcome, Mr. Franklin.'