mirror of https://github.com/Sonarr/Sonarr
updated backgrid
This commit is contained in:
parent
467c88f711
commit
8fd7def9dd
|
@ -6,10 +6,12 @@
|
|||
Licensed under the MIT @license.
|
||||
*/
|
||||
|
||||
(function ($, _, Backbone, Backgrid, lunr) {
|
||||
(function (root) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var Backbone = root.Backbone, Backgrid = root.Backgrid, lunr = root.lunr;
|
||||
|
||||
/**
|
||||
ServerSideFilter is a search form widget that submits a query to the server
|
||||
for filtering the current collection.
|
||||
|
@ -36,14 +38,17 @@
|
|||
/** @property {string} [name='q'] Query key */
|
||||
name: "q",
|
||||
|
||||
/** @property The HTML5 placeholder to appear beneath the search box. */
|
||||
/**
|
||||
@property {string} [placeholder] The HTML5 placeholder to appear beneath
|
||||
the search box.
|
||||
*/
|
||||
placeholder: null,
|
||||
|
||||
/**
|
||||
@param {Object} options
|
||||
@param {Backbone.Collection} options.collection
|
||||
@param {String} [options.name]
|
||||
@param {String} [options.placeholder]
|
||||
@param {string} [options.name]
|
||||
@param {string} [options.placeholder]
|
||||
*/
|
||||
initialize: function (options) {
|
||||
Backgrid.requireOptions(options, ["collection"]);
|
||||
|
@ -51,16 +56,21 @@
|
|||
this.name = options.name || this.name;
|
||||
this.placeholder = options.placeholder || this.placeholder;
|
||||
|
||||
// Persist the query on pagination
|
||||
var collection = this.collection, self = this;
|
||||
if (Backbone.PageableCollection &&
|
||||
collection instanceof Backbone.PageableCollection &&
|
||||
collection.mode == "server") {
|
||||
collection.queryParams[this.name] = function () {
|
||||
return self.$el.find("input[type=text]").val();
|
||||
return self.searchBox().val() || null;
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
searchBox: function () {
|
||||
return this.$el.find("input[type=text]");
|
||||
},
|
||||
|
||||
/**
|
||||
Upon search form submission, this event handler constructs a query
|
||||
parameter object and pass it to Collection#fetch for server-side
|
||||
|
@ -68,9 +78,22 @@
|
|||
*/
|
||||
search: function (e) {
|
||||
if (e) e.preventDefault();
|
||||
|
||||
var data = {};
|
||||
data[this.name] = this.$el.find("input[type=text]").val();
|
||||
this.collection.fetch({data: data});
|
||||
|
||||
// go back to the first page on search
|
||||
var collection = this.collection;
|
||||
if (Backbone.PageableCollection &&
|
||||
collection instanceof Backbone.PageableCollection &&
|
||||
collection.mode == "server") {
|
||||
collection.state.currentPage = 1;
|
||||
}
|
||||
else {
|
||||
var query = this.searchBox().val();
|
||||
if (query) data[this.name] = query;
|
||||
}
|
||||
|
||||
collection.fetch({data: data, reset: true});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -79,8 +102,8 @@
|
|||
*/
|
||||
clear: function (e) {
|
||||
if (e) e.preventDefault();
|
||||
this.$("input[type=text]").val(null);
|
||||
this.collection.fetch();
|
||||
this.searchBox().val(null);
|
||||
this.collection.fetch({reset: true});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -115,8 +138,7 @@
|
|||
e.preventDefault();
|
||||
this.clear();
|
||||
},
|
||||
"change input[type=text]": "search",
|
||||
"keyup input[type=text]": "search",
|
||||
"keydown input[type=text]": "search",
|
||||
"submit": function (e) {
|
||||
e.preventDefault();
|
||||
this.search();
|
||||
|
@ -124,14 +146,14 @@
|
|||
},
|
||||
|
||||
/**
|
||||
@property {?Array.<string>} A list of model field names to search
|
||||
for matches. If null, all of the fields will be searched.
|
||||
@property {?Array.<string>} [fields] A list of model field names to
|
||||
search for matches. If null, all of the fields will be searched.
|
||||
*/
|
||||
fields: null,
|
||||
|
||||
/**
|
||||
@property wait The time in milliseconds to wait since for since the last
|
||||
change to the search box's value before searching. This value can be
|
||||
@property [wait=149] The time in milliseconds to wait since for since the
|
||||
last change to the search box's value before searching. This value can be
|
||||
adjusted depending on how often the search box is used and how large the
|
||||
search index is.
|
||||
*/
|
||||
|
@ -143,9 +165,9 @@
|
|||
|
||||
@param {Object} options
|
||||
@param {Backbone.Collection} options.collection
|
||||
@param {String} [options.placeholder]
|
||||
@param {String} [options.fields]
|
||||
@param {String} [options.wait=149]
|
||||
@param {string} [options.placeholder]
|
||||
@param {string} [options.fields]
|
||||
@param {string} [options.wait=149]
|
||||
*/
|
||||
initialize: function (options) {
|
||||
ServerSideFilter.prototype.initialize.apply(this, arguments);
|
||||
|
@ -155,11 +177,8 @@
|
|||
|
||||
this._debounceMethods(["search", "clear"]);
|
||||
|
||||
var collection = this.collection;
|
||||
var collection = this.collection = this.collection.fullCollection || this.collection;
|
||||
var shadowCollection = this.shadowCollection = collection.clone();
|
||||
shadowCollection.url = collection.url;
|
||||
shadowCollection.sync = collection.sync;
|
||||
shadowCollection.parse = collection.parse;
|
||||
|
||||
this.listenTo(collection, "add", function (model, collection, options) {
|
||||
shadowCollection.add(model, options);
|
||||
|
@ -167,9 +186,15 @@
|
|||
this.listenTo(collection, "remove", function (model, collection, options) {
|
||||
shadowCollection.remove(model, options);
|
||||
});
|
||||
this.listenTo(collection, "sort reset", function (collection, options) {
|
||||
this.listenTo(collection, "sort", function (col) {
|
||||
if (!this.searchBox().val()) shadowCollection.reset(col.models);
|
||||
});
|
||||
this.listenTo(collection, "reset", function (col, options) {
|
||||
options = _.extend({reindex: true}, options || {});
|
||||
if (options.reindex) shadowCollection.reset(collection.models);
|
||||
if (options.reindex && col === collection &&
|
||||
options.from == null && options.to == null) {
|
||||
shadowCollection.reset(col.models);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -218,7 +243,9 @@
|
|||
when all the matches have been found.
|
||||
*/
|
||||
search: function () {
|
||||
var matcher = _.bind(this.makeMatcher(this.$("input[type=text]").val()), this);
|
||||
var matcher = _.bind(this.makeMatcher(this.searchBox().val()), this);
|
||||
var col = this.collection;
|
||||
if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true});
|
||||
this.collection.reset(this.shadowCollection.filter(matcher), {reindex: false});
|
||||
},
|
||||
|
||||
|
@ -226,7 +253,7 @@
|
|||
Clears the search box and reset the collection to its original.
|
||||
*/
|
||||
clear: function () {
|
||||
this.$("input[type=text]").val(null);
|
||||
this.searchBox().val(null);
|
||||
this.collection.reset(this.shadowCollection.models, {reindex: false});
|
||||
}
|
||||
|
||||
|
@ -262,7 +289,7 @@
|
|||
|
||||
@param {Object} options
|
||||
@param {Backbone.Collection} options.collection
|
||||
@param {String} [options.placeholder]
|
||||
@param {string} [options.placeholder]
|
||||
@param {string} [options.ref] `lunrjs` document reference attribute name.
|
||||
@param {Object} [options.fields] A hash of `lunrjs` index field names and
|
||||
boost value.
|
||||
|
@ -273,7 +300,7 @@
|
|||
|
||||
this.ref = options.ref || this.ref;
|
||||
|
||||
var collection = this.collection;
|
||||
var collection = this.collection = this.collection.fullCollection || this.collection;
|
||||
this.listenTo(collection, "add", this.addToIndex);
|
||||
this.listenTo(collection, "remove", this.removeFromIndex);
|
||||
this.listenTo(collection, "reset", this.resetIndex);
|
||||
|
@ -351,15 +378,17 @@
|
|||
query answer.
|
||||
*/
|
||||
search: function () {
|
||||
var searchResults = this.index.search(this.$("input[type=text]").val());
|
||||
var searchResults = this.index.search(this.searchBox().val());
|
||||
var models = [];
|
||||
for (var i = 0; i < searchResults.length; i++) {
|
||||
var result = searchResults[i];
|
||||
models.push(this.shadowCollection.get(result.ref));
|
||||
}
|
||||
this.collection.reset(models, {reindex: false});
|
||||
var col = this.collection;
|
||||
if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true});
|
||||
col.reset(models, {reindex: false});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}(jQuery, _, Backbone, Backgrid, lunr));
|
||||
}(this));
|
||||
|
|
|
@ -41,10 +41,6 @@ if (!String.prototype.trim || ws.trim()) {
|
|||
};
|
||||
}
|
||||
|
||||
function capitalize(s) {
|
||||
return String.fromCharCode(s.charCodeAt(0) - 32) + s.slice(1);
|
||||
}
|
||||
|
||||
function lpad(str, length, padstr) {
|
||||
var paddingLen = length - (str + '').length;
|
||||
paddingLen = paddingLen < 0 ? 0 : paddingLen;
|
||||
|
@ -72,7 +68,9 @@ var Backgrid = root.Backgrid = {
|
|||
|
||||
resolveNameToClass: function (name, suffix) {
|
||||
if (_.isString(name)) {
|
||||
var key = _.map(name.split('-'), function (e) { return capitalize(e); }).join('') + suffix;
|
||||
var key = _.map(name.split('-'), function (e) {
|
||||
return e.slice(0, 1).toUpperCase() + e.slice(1);
|
||||
}).join('') + suffix;
|
||||
var klass = Backgrid[key] || Backgrid.Extension[key];
|
||||
if (_.isUndefined(klass)) {
|
||||
throw new ReferenceError("Class '" + key + "' not found");
|
||||
|
@ -81,7 +79,17 @@ var Backgrid = root.Backgrid = {
|
|||
}
|
||||
|
||||
return name;
|
||||
},
|
||||
|
||||
callByNeed: function () {
|
||||
var value = arguments[0];
|
||||
if (!_.isFunction(value)) return value;
|
||||
|
||||
var context = arguments[1];
|
||||
var args = [].slice.call(arguments, 2);
|
||||
return value.apply(context, !!(args + '') ? args : void 0);
|
||||
}
|
||||
|
||||
};
|
||||
_.extend(Backgrid, Backbone.Events);
|
||||
|
||||
|
@ -99,7 +107,7 @@ _.extend(Backgrid, Backbone.Events);
|
|||
var Command = Backgrid.Command = function (evt) {
|
||||
_.extend(this, {
|
||||
altKey: !!evt.altKey,
|
||||
char: evt.char,
|
||||
"char": evt["char"],
|
||||
charCode: evt.charCode,
|
||||
ctrlKey: !!evt.ctrlKey,
|
||||
key: evt.key,
|
||||
|
@ -737,12 +745,33 @@ var Cell = Backgrid.Cell = Backbone.View.extend({
|
|||
if (!(this.column instanceof Column)) {
|
||||
this.column = new Column(this.column);
|
||||
}
|
||||
this.formatter = Backgrid.resolveNameToClass(this.column.get("formatter") || this.formatter, "Formatter");
|
||||
|
||||
var column = this.column, model = this.model, $el = this.$el;
|
||||
|
||||
this.formatter = Backgrid.resolveNameToClass(column.get("formatter") ||
|
||||
this.formatter, "Formatter");
|
||||
|
||||
this.editor = Backgrid.resolveNameToClass(this.editor, "CellEditor");
|
||||
this.listenTo(this.model, "change:" + this.column.get("name"), function () {
|
||||
if (!this.$el.hasClass("editor")) this.render();
|
||||
|
||||
this.listenTo(model, "change:" + column.get("name"), function () {
|
||||
if (!$el.hasClass("editor")) this.render();
|
||||
});
|
||||
this.listenTo(this.model, "backgrid:error", this.renderError);
|
||||
|
||||
this.listenTo(model, "backgrid:error", this.renderError);
|
||||
|
||||
this.listenTo(column, "change:editable change:sortable change:renderable",
|
||||
function (column) {
|
||||
var changed = column.changedAttributes();
|
||||
for (var key in changed) {
|
||||
if (changed.hasOwnProperty(key)) {
|
||||
$el.toggleClass(key, changed[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (column.get("editable")) $el.addClass("editable");
|
||||
if (column.get("sortable")) $el.addClass("sortable");
|
||||
if (column.get("renderable")) $el.addClass("renderable");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -779,7 +808,8 @@ var Cell = Backgrid.Cell = Backbone.View.extend({
|
|||
var model = this.model;
|
||||
var column = this.column;
|
||||
|
||||
if (column.get("editable")) {
|
||||
var editable = Backgrid.callByNeed(column.get("editable"), column, model);
|
||||
if (editable) {
|
||||
|
||||
this.currentEditor = new this.editor({
|
||||
column: this.column,
|
||||
|
@ -828,7 +858,7 @@ var Cell = Backgrid.Cell = Backbone.View.extend({
|
|||
*/
|
||||
remove: function () {
|
||||
if (this.currentEditor) {
|
||||
this.currentEditor.remove.apply(this, arguments);
|
||||
this.currentEditor.remove.apply(this.currentEditor, arguments);
|
||||
delete this.currentEditor;
|
||||
}
|
||||
return Backbone.View.prototype.remove.apply(this, arguments);
|
||||
|
@ -1483,6 +1513,7 @@ var Column = Backgrid.Column = Backbone.Model.extend({
|
|||
editable: true,
|
||||
renderable: true,
|
||||
formatter: undefined,
|
||||
sortValue: undefined,
|
||||
cell: undefined,
|
||||
headerCell: undefined
|
||||
},
|
||||
|
@ -1491,22 +1522,36 @@ var Column = Backgrid.Column = Backbone.Model.extend({
|
|||
Initializes this Column instance.
|
||||
|
||||
@param {Object} attrs Column attributes.
|
||||
|
||||
@param {string} attrs.name The name of the model attribute.
|
||||
|
||||
@param {string|Backgrid.Cell} attrs.cell The cell type.
|
||||
If this is a string, the capitalized form will be used to look up a
|
||||
cell class in Backbone, i.e.: string => StringCell. If a Cell subclass
|
||||
is supplied, it is initialized with a hash of parameters. If a Cell
|
||||
instance is supplied, it is used directly.
|
||||
|
||||
@param {string|Backgrid.HeaderCell} [attrs.headerCell] The header cell type.
|
||||
|
||||
@param {string} [attrs.label] The label to show in the header.
|
||||
@param {boolean} [attrs.sortable=true]
|
||||
@param {boolean} [attrs.editable=true]
|
||||
@param {boolean} [attrs.renderable=true]
|
||||
@param {Backgrid.CellFormatter|Object|string} [attrs.formatter] The
|
||||
|
||||
@param {boolean|string} [attrs.sortable=true]
|
||||
|
||||
@param {boolean|string} [attrs.editable=true]
|
||||
|
||||
@param {boolean|string} [attrs.renderable=true]
|
||||
|
||||
@param {Backgrid.CellFormatter | Object | string} [attrs.formatter] The
|
||||
formatter to use to convert between raw model values and user input.
|
||||
|
||||
@param {(function(Backbone.Model, string): Object) | string} [sortValue] The
|
||||
function to use to extract a value from the model for comparison during
|
||||
sorting. If this value is a string, a method with the same name will be
|
||||
looked up from the column instance.
|
||||
|
||||
@throws {TypeError} If attrs.cell or attrs.options are not supplied.
|
||||
@throws {ReferenceError} If attrs.cell is a string but a cell class of
|
||||
|
||||
@throws {ReferenceError} If formatter is a string but a formatter class of
|
||||
said name cannot be found in the Backgrid module.
|
||||
|
||||
See:
|
||||
|
@ -1522,8 +1567,32 @@ var Column = Backgrid.Column = Backbone.Model.extend({
|
|||
}
|
||||
|
||||
var headerCell = Backgrid.resolveNameToClass(this.get("headerCell"), "HeaderCell");
|
||||
|
||||
var cell = Backgrid.resolveNameToClass(this.get("cell"), "Cell");
|
||||
this.set({ cell: cell, headerCell: headerCell }, { silent: true });
|
||||
|
||||
var sortValue = this.get("sortValue");
|
||||
if (sortValue == null) sortValue = function (model, colName) {
|
||||
return model.get(colName);
|
||||
};
|
||||
else if (_.isString(sortValue)) sortValue = this[sortValue];
|
||||
|
||||
var sortable = this.get("sortable");
|
||||
if (_.isString(sortable)) sortable = this[sortable];
|
||||
|
||||
var editable = this.get("editable");
|
||||
if (_.isString(editable)) editable = this[editable];
|
||||
|
||||
var renderable = this.get("renderable");
|
||||
if (_.isString(renderable)) renderable = this[renderable];
|
||||
|
||||
this.set({
|
||||
cell: cell,
|
||||
headerCell: headerCell,
|
||||
sortable: sortable,
|
||||
editable: editable,
|
||||
renderable: renderable,
|
||||
sortValue: sortValue
|
||||
}, { silent: true });
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -1587,22 +1656,11 @@ var Row = Backgrid.Row = Backbone.View.extend({
|
|||
cells.push(this.makeCell(columns.at(i), options));
|
||||
}
|
||||
|
||||
this.listenTo(columns, "change:renderable", function (column, renderable) {
|
||||
for (var i = 0; i < cells.length; i++) {
|
||||
var cell = cells[i];
|
||||
if (cell.column.get("name") == column.get("name")) {
|
||||
if (renderable) cell.$el.show(); else cell.$el.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.listenTo(columns, "add", function (column, columns) {
|
||||
var i = columns.indexOf(column);
|
||||
var cell = this.makeCell(column, options);
|
||||
cells.splice(i, 0, cell);
|
||||
|
||||
if (!cell.column.get("renderable")) cell.$el.hide();
|
||||
|
||||
var $el = this.$el;
|
||||
if (i === 0) {
|
||||
$el.prepend(cell.render().$el);
|
||||
|
@ -1646,11 +1704,8 @@ var Row = Backgrid.Row = Backbone.View.extend({
|
|||
this.$el.empty();
|
||||
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
for (var i = 0; i < this.cells.length; i++) {
|
||||
var cell = this.cells[i];
|
||||
fragment.appendChild(cell.render().el);
|
||||
if (!cell.column.get("renderable")) cell.$el.hide();
|
||||
fragment.appendChild(this.cells[i].render().el);
|
||||
}
|
||||
|
||||
this.el.appendChild(fragment);
|
||||
|
@ -1766,7 +1821,24 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
|||
if (!(this.column instanceof Column)) {
|
||||
this.column = new Column(this.column);
|
||||
}
|
||||
|
||||
this.listenTo(this.collection, "backgrid:sort", this._resetCellDirection);
|
||||
|
||||
var column = this.column, $el = this.$el;
|
||||
|
||||
this.listenTo(column, "change:editable change:sortable change:renderable",
|
||||
function (column) {
|
||||
var changed = column.changedAttributes();
|
||||
for (var key in changed) {
|
||||
if (changed.hasOwnProperty(key)) {
|
||||
$el.toggleClass(key, changed[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (column.get("editable")) $el.addClass("editable");
|
||||
if (column.get("sortable")) $el.addClass("sortable");
|
||||
if (column.get("renderable")) $el.addClass("renderable");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1793,9 +1865,9 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
|||
|
||||
@private
|
||||
*/
|
||||
_resetCellDirection: function (sortByColName, direction, comparator, collection) {
|
||||
_resetCellDirection: function (columnToSort, direction, comparator, collection) {
|
||||
if (collection == this.collection) {
|
||||
if (sortByColName !== this.column.get("name")) this.direction(null);
|
||||
if (columnToSort !== this.column) this.direction(null);
|
||||
else this.direction(direction);
|
||||
}
|
||||
},
|
||||
|
@ -1808,34 +1880,12 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
|||
onClick: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var columnName = this.column.get("name");
|
||||
|
||||
if (this.column.get("sortable")) {
|
||||
if (this.direction() === "ascending") {
|
||||
this.sort(columnName, "descending", function (left, right) {
|
||||
var leftVal = left.get(columnName);
|
||||
var rightVal = right.get(columnName);
|
||||
if (leftVal === rightVal) {
|
||||
return 0;
|
||||
}
|
||||
else if (leftVal > rightVal) { return -1; }
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
else if (this.direction() === "descending") {
|
||||
this.sort(columnName, null);
|
||||
}
|
||||
else {
|
||||
this.sort(columnName, "ascending", function (left, right) {
|
||||
var leftVal = left.get(columnName);
|
||||
var rightVal = right.get(columnName);
|
||||
if (leftVal === rightVal) {
|
||||
return 0;
|
||||
}
|
||||
else if (leftVal < rightVal) { return -1; }
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
var column = this.column;
|
||||
var sortable = Backgrid.callByNeed(column.get("sortable"), column, this.model);
|
||||
if (sortable) {
|
||||
if (this.direction() === "ascending") this.sort(column, "descending");
|
||||
else if (this.direction() === "descending") this.sort(column, null);
|
||||
else this.sort(column, "ascending");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1852,31 +1902,37 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
|||
and the current page will then be returned.
|
||||
|
||||
Triggers a Backbone `backgrid:sort` event from the collection when done
|
||||
with the column name, direction, comparator and a reference to the
|
||||
collection.
|
||||
with the column, direction, comparator and a reference to the collection.
|
||||
|
||||
@param {string} columnName
|
||||
@param {Backgrid.Column} column
|
||||
@param {null|"ascending"|"descending"} direction
|
||||
@param {function(*, *): number} [comparator]
|
||||
|
||||
See [Backbone.Collection#comparator](http://backbonejs.org/#Collection-comparator)
|
||||
*/
|
||||
sort: function (columnName, direction, comparator) {
|
||||
|
||||
comparator = comparator || this._cidComparator;
|
||||
sort: function (column, direction) {
|
||||
|
||||
var collection = this.collection;
|
||||
|
||||
if (Backbone.PageableCollection && collection instanceof Backbone.PageableCollection) {
|
||||
var order;
|
||||
if (direction === "ascending") order = -1;
|
||||
else if (direction === "descending") order = 1;
|
||||
else order = null;
|
||||
var order;
|
||||
if (direction === "ascending") order = -1;
|
||||
else if (direction === "descending") order = 1;
|
||||
else order = null;
|
||||
|
||||
collection.setSorting(order ? columnName : null, order);
|
||||
var comparator = this.makeComparator(column.get("name"), order,
|
||||
order ?
|
||||
column.get("sortValue") :
|
||||
function (model) {
|
||||
return model.cid;
|
||||
});
|
||||
|
||||
if (Backbone.PageableCollection &&
|
||||
collection instanceof Backbone.PageableCollection) {
|
||||
|
||||
collection.setSorting(order && column.get("name"), order,
|
||||
{sortValue: column.get("sortValue")});
|
||||
|
||||
if (collection.mode == "client") {
|
||||
if (!collection.fullCollection.comparator) {
|
||||
if (collection.fullCollection.comparator == null) {
|
||||
collection.fullCollection.comparator = comparator;
|
||||
}
|
||||
collection.fullCollection.sort();
|
||||
|
@ -1888,26 +1944,24 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
|||
collection.sort();
|
||||
}
|
||||
|
||||
this.collection.trigger("backgrid:sort", columnName, direction, comparator, this.collection);
|
||||
this.collection.trigger("backgrid:sort", column, direction, comparator,
|
||||
this.collection);
|
||||
},
|
||||
|
||||
/**
|
||||
Default comparator for Backbone.Collections. Sorts cids in ascending
|
||||
order. The cids of the models are assumed to be in insertion order.
|
||||
makeComparator: function (attr, order, func) {
|
||||
|
||||
@private
|
||||
@param {*} left
|
||||
@param {*} right
|
||||
*/
|
||||
_cidComparator: function (left, right) {
|
||||
var lcid = left.cid, rcid = right.cid;
|
||||
if (!_.isUndefined(lcid) && !_.isUndefined(rcid)) {
|
||||
lcid = lcid.slice(1) * 1, rcid = rcid.slice(1) * 1;
|
||||
if (lcid < rcid) return -1;
|
||||
else if (lcid > rcid) return 1;
|
||||
}
|
||||
return function (left, right) {
|
||||
// extract the values from the models
|
||||
var l = func(left, attr), r = func(right, attr), t;
|
||||
|
||||
return 0;
|
||||
// if descending order, swap left and right
|
||||
if (order === 1) t = l, l = r, r = t;
|
||||
|
||||
// compare as usual
|
||||
if (l === r) return 0;
|
||||
else if (l < r) return -1;
|
||||
return 1;
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1915,7 +1969,9 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
|||
*/
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
var $label = $("<a>").text(this.column.get("label")).append("<b class='sort-caret'></b>");
|
||||
var $label = $("<a>").text(this.column.get("label"));
|
||||
var sortable = Backgrid.callByNeed(this.column.get("sortable"), this.column, this.model);
|
||||
if (sortable) $label.append("<b class='sort-caret'></b>");
|
||||
this.$el.append($label);
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
|
@ -2259,6 +2315,9 @@ var Body = Backgrid.Body = Backbone.View.extend({
|
|||
moveToNextCell: function (model, column, command) {
|
||||
var i = this.collection.indexOf(model);
|
||||
var j = this.columns.indexOf(column);
|
||||
var cell, renderable, editable;
|
||||
|
||||
this.rows[i].cells[j].exitEditMode();
|
||||
|
||||
if (command.moveUp() || command.moveDown() || command.moveLeft() ||
|
||||
command.moveRight() || command.save()) {
|
||||
|
@ -2267,7 +2326,12 @@ var Body = Backgrid.Body = Backbone.View.extend({
|
|||
|
||||
if (command.moveUp() || command.moveDown()) {
|
||||
var row = this.rows[i + (command.moveUp() ? -1 : 1)];
|
||||
if (row) row.cells[j].enterEditMode();
|
||||
if (row) {
|
||||
cell = row.cells[j];
|
||||
if (Backgrid.callByNeed(cell.column.get("editable"), cell.column, model)) {
|
||||
cell.enterEditMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (command.moveLeft() || command.moveRight()) {
|
||||
var right = command.moveRight();
|
||||
|
@ -2276,16 +2340,16 @@ var Body = Backgrid.Body = Backbone.View.extend({
|
|||
right ? offset++ : offset--) {
|
||||
var m = ~~(offset / l);
|
||||
var n = offset - m * l;
|
||||
var cell = this.rows[m].cells[n];
|
||||
if (cell.column.get("renderable") && cell.column.get("editable")) {
|
||||
cell = this.rows[m].cells[n];
|
||||
renderable = Backgrid.callByNeed(cell.column.get("renderable"), cell.column, cell.model);
|
||||
editable = Backgrid.callByNeed(cell.column.get("editable"), cell.column, model);
|
||||
if (renderable && editable) {
|
||||
cell.enterEditMode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.rows[i].cells[j].exitEditMode();
|
||||
}
|
||||
});
|
||||
/*
|
||||
|
|
|
@ -6,18 +6,186 @@
|
|||
Licensed under the MIT @license.
|
||||
*/
|
||||
|
||||
(function ($, _, Backbone, Backgrid) {
|
||||
(function (_, Backbone, Backgrid) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
PageHandle is a class that renders the actual page handles and reacts to
|
||||
click events for pagination.
|
||||
|
||||
This class acts in two modes - control or discrete page handle modes. If
|
||||
one of the `is*` flags is `true`, an instance of this class is under
|
||||
control page handle mode. Setting a `pageIndex` to an instance of this
|
||||
class under control mode has no effect and the correct page index will
|
||||
always be inferred from the `is*` flag. Only one of the `is*` flags should
|
||||
be set to `true` at a time. For example, an instance of this class cannot
|
||||
simultaneously be a rewind control and a fast forward control. A `label`
|
||||
and a `title` template or a string are required to be passed to the
|
||||
constuctor under this mode. If a `title` template is provided, it __MUST__
|
||||
accept a parameter `label`. When the `label` is provided to the `title`
|
||||
template function, its result will be used to render the generated anchor's
|
||||
title attribute.
|
||||
|
||||
If all of the `is*` flags is set to `false`, which is the default, an
|
||||
instance of this class will be in discrete page handle mode. An instance
|
||||
under this mode requires the `pageIndex` to be passed from the constructor
|
||||
as an option and it __MUST__ be a 0-based index of the list of page numbers
|
||||
to render. The constuctor will normalize the base to the same base the
|
||||
underlying PageableCollection collection instance uses. A `label` is not
|
||||
required under this mode, which will default to the equivalent 1-based page
|
||||
index calculated from `pageIndex` and the underlying PageableCollection
|
||||
instance. A provided `label` will still be honored however. The `title`
|
||||
parameter is also not required under this mode, in which case the default
|
||||
`title` template will be used. You are encouraged to provide your own
|
||||
`title` template however if you wish to localize the title strings.
|
||||
|
||||
If this page handle represents the current page, an `active` class will be
|
||||
placed on the root list element.
|
||||
|
||||
if this page handle is at the border of the list of pages, a `disabled`
|
||||
class will be placed on the root list element.
|
||||
|
||||
Only page handles that are neither `active` nor `disabled` will respond to
|
||||
click events and triggers pagination.
|
||||
|
||||
@class Backgrid.Extension.PageHandle
|
||||
*/
|
||||
var PageHandle = Backgrid.Extension.PageHandle = Backbone.View.extend({
|
||||
|
||||
/** @property */
|
||||
tagName: "li",
|
||||
|
||||
/** @property */
|
||||
events: {
|
||||
"click a": "changePage"
|
||||
},
|
||||
|
||||
/**
|
||||
@property {string|function(Object.<string, string>): string} title
|
||||
The title to use for the `title` attribute of the generated page handle
|
||||
anchor elements. It can be a string or an Underscore template function
|
||||
that takes a mandatory `label` parameter.
|
||||
*/
|
||||
title: _.template('Page <%- label %>'),
|
||||
|
||||
/**
|
||||
@property {boolean} isRewind Whether this handle represents a rewind
|
||||
control
|
||||
*/
|
||||
isRewind: false,
|
||||
|
||||
/**
|
||||
@property {boolean} isBack Whether this handle represents a back
|
||||
control
|
||||
*/
|
||||
isBack: false,
|
||||
|
||||
/**
|
||||
@property {boolean} isForward Whether this handle represents a forward
|
||||
control
|
||||
*/
|
||||
isForward: false,
|
||||
|
||||
/**
|
||||
@property {boolean} isFastForward Whether this handle represents a fast
|
||||
forward control
|
||||
*/
|
||||
isFastForward: false,
|
||||
|
||||
/**
|
||||
Initializer.
|
||||
|
||||
@param {Object} options
|
||||
@param {Backbone.Collection} options.collection
|
||||
@param {number} pageIndex 0-based index of the page number this handle
|
||||
handles. This parameter will be normalized to the base the underlying
|
||||
PageableCollection uses.
|
||||
@param {string} [options.label] If provided it is used to render the
|
||||
anchor text, otherwise the normalized pageIndex will be used
|
||||
instead. Required if any of the `is*` flags is set to `true`.
|
||||
@param {string} [options.title]
|
||||
@param {boolean} [options.isRewind=false]
|
||||
@param {boolean} [options.isBack=false]
|
||||
@param {boolean} [options.isForward=false]
|
||||
@param {boolean} [options.isFastForward=false]
|
||||
*/
|
||||
initialize: function (options) {
|
||||
Backbone.View.prototype.initialize.apply(this, arguments);
|
||||
|
||||
var collection = this.collection;
|
||||
var state = collection.state;
|
||||
var currentPage = state.currentPage;
|
||||
var firstPage = state.firstPage;
|
||||
var lastPage = state.lastPage;
|
||||
|
||||
_.extend(this, _.pick(options,
|
||||
["isRewind", "isBack", "isForward", "isFastForward"]));
|
||||
|
||||
var pageIndex;
|
||||
if (this.isRewind) pageIndex = firstPage;
|
||||
else if (this.isBack) pageIndex = Math.max(firstPage, currentPage - 1);
|
||||
else if (this.isForward) pageIndex = Math.min(lastPage, currentPage + 1);
|
||||
else if (this.isFastForward) pageIndex = lastPage;
|
||||
else {
|
||||
pageIndex = +options.pageIndex;
|
||||
pageIndex = (firstPage ? pageIndex + 1 : pageIndex);
|
||||
}
|
||||
this.pageIndex = pageIndex;
|
||||
|
||||
if (((this.isRewind || this.isBack) && currentPage == firstPage) ||
|
||||
((this.isForward || this.isFastForward) && currentPage == lastPage)) {
|
||||
this.$el.addClass("disabled");
|
||||
}
|
||||
else if (!(this.isRewind ||
|
||||
this.isBack ||
|
||||
this.isForward ||
|
||||
this.isFastForward) &&
|
||||
currentPage == pageIndex) {
|
||||
this.$el.addClass("active");
|
||||
}
|
||||
|
||||
this.label = (options.label || (firstPage ? pageIndex : pageIndex + 1)) + '';
|
||||
var title = options.title || this.title;
|
||||
this.title = _.isFunction(title) ? title({label: this.label}) : title;
|
||||
},
|
||||
|
||||
/**
|
||||
Renders a clickable anchor element under a list item.
|
||||
*/
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
var anchor = document.createElement("a");
|
||||
anchor.href = '#';
|
||||
if (this.title) anchor.title = this.title;
|
||||
anchor.innerHTML = this.label;
|
||||
this.el.appendChild(anchor);
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
jQuery click event handler. Goes to the page this PageHandle instance
|
||||
represents. No-op if this page handle is currently active or disabled.
|
||||
*/
|
||||
changePage: function (e) {
|
||||
e.preventDefault();
|
||||
var $el = this.$el;
|
||||
if (!$el.hasClass("active") && !$el.hasClass("disabled")) {
|
||||
this.collection.getPage(this.pageIndex);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
Paginator is a Backgrid extension that renders a series of configurable
|
||||
pagination handles. This extension is best used for splitting a large data
|
||||
set across multiple pages. If the number of pages is larger then a
|
||||
threshold, which is set to 10 by default, the page handles are rendered
|
||||
within a sliding window, plus the fast forward, fast backward, previous and
|
||||
next page handles. The fast forward, fast backward, previous and next page
|
||||
handles can be turned off.
|
||||
within a sliding window, plus the rewind, back, forward and fast forward
|
||||
control handles. The individual control handles can be turned off.
|
||||
|
||||
@class Backgrid.Extension.Paginator
|
||||
*/
|
||||
|
@ -30,97 +198,65 @@
|
|||
windowSize: 10,
|
||||
|
||||
/**
|
||||
@property {Object} fastForwardHandleLabels You can disable specific
|
||||
handles by setting its value to `null`.
|
||||
@property {Object.<string, Object.<string, string>>} controls You can
|
||||
disable specific control handles by omitting certain keys.
|
||||
*/
|
||||
fastForwardHandleLabels: {
|
||||
first: "《",
|
||||
prev: "〈",
|
||||
next: "〉",
|
||||
last: "》"
|
||||
controls: {
|
||||
rewind: {
|
||||
label: "《",
|
||||
title: "First"
|
||||
},
|
||||
back: {
|
||||
label: "〈",
|
||||
title: "Previous"
|
||||
},
|
||||
forward: {
|
||||
label: "〉",
|
||||
title: "Next"
|
||||
},
|
||||
fastForward: {
|
||||
label: "》",
|
||||
title: "Last"
|
||||
}
|
||||
},
|
||||
|
||||
/** @property */
|
||||
template: _.template('<ul><% _.each(handles, function (handle) { %><li <% if (handle.className) { %>class="<%= handle.className %>"<% } %>><a href="#" <% if (handle.title) {%> title="<%= handle.title %>"<% } %>><%= handle.label %></a></li><% }); %></ul>'),
|
||||
/**
|
||||
@property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle
|
||||
class to use for rendering individual handles
|
||||
*/
|
||||
pageHandle: PageHandle,
|
||||
|
||||
/** @property */
|
||||
events: {
|
||||
"click a": "changePage"
|
||||
},
|
||||
goBackFirstOnSort: true,
|
||||
|
||||
/**
|
||||
Initializer.
|
||||
|
||||
@param {Object} options
|
||||
@param {Backbone.Collection} options.collection
|
||||
@param {boolean} [options.fastForwardHandleLabels] Whether to render fast forward buttons.
|
||||
@param {boolean} [options.controls]
|
||||
@param {boolean} [options.pageHandle=Backgrid.Extension.PageHandle]
|
||||
@param {boolean} [options.goBackFirstOnSort=true]
|
||||
*/
|
||||
initialize: function (options) {
|
||||
Backgrid.requireOptions(options, ["collection"]);
|
||||
|
||||
this.controls = options.controls || this.controls;
|
||||
this.pageHandle = options.pageHandle || this.pageHandle;
|
||||
|
||||
var collection = this.collection;
|
||||
var fullCollection = collection.fullCollection;
|
||||
if (fullCollection) {
|
||||
this.listenTo(fullCollection, "add", this.render);
|
||||
this.listenTo(fullCollection, "remove", this.render);
|
||||
this.listenTo(fullCollection, "reset", this.render);
|
||||
}
|
||||
else {
|
||||
this.listenTo(collection, "add", this.render);
|
||||
this.listenTo(collection, "remove", this.render);
|
||||
this.listenTo(collection, "reset", this.render);
|
||||
this.listenTo(collection, "add", this.render);
|
||||
this.listenTo(collection, "remove", this.render);
|
||||
this.listenTo(collection, "reset", this.render);
|
||||
if ((options.goBackFirstOnSort || this.goBackFirstOnSort) &&
|
||||
collection.fullCollection) {
|
||||
this.listenTo(collection.fullCollection, "sort", function () {
|
||||
collection.getFirstPage();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
jQuery event handler for the page handlers. Goes to the right page upon
|
||||
clicking.
|
||||
|
||||
@param {Event} e
|
||||
*/
|
||||
changePage: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $li = $(e.target).parent();
|
||||
if (!$li.hasClass("active") && !$li.hasClass("disabled")) {
|
||||
|
||||
var label = $(e.target).text();
|
||||
var ffLabels = this.fastForwardHandleLabels;
|
||||
|
||||
var collection = this.collection;
|
||||
|
||||
if (ffLabels) {
|
||||
switch (label) {
|
||||
case ffLabels.first:
|
||||
collection.getFirstPage();
|
||||
return;
|
||||
case ffLabels.prev:
|
||||
collection.getPreviousPage();
|
||||
return;
|
||||
case ffLabels.next:
|
||||
collection.getNextPage();
|
||||
return;
|
||||
case ffLabels.last:
|
||||
collection.getLastPage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var state = collection.state;
|
||||
var pageIndex = +label;
|
||||
collection.getPage(state.firstPage === 0 ? pageIndex - 1 : pageIndex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
Internal method to create a list of page handle objects for the template
|
||||
to render them.
|
||||
|
||||
@return {Array.<Object>} an array of page handle objects hashes
|
||||
*/
|
||||
makeHandles: function () {
|
||||
|
||||
var handles = [];
|
||||
_calculateWindow: function () {
|
||||
var collection = this.collection;
|
||||
var state = collection.state;
|
||||
|
||||
|
@ -132,48 +268,44 @@
|
|||
currentPage = firstPage ? currentPage - 1 : currentPage;
|
||||
var windowStart = Math.floor(currentPage / this.windowSize) * this.windowSize;
|
||||
var windowEnd = Math.min(lastPage + 1, windowStart + this.windowSize);
|
||||
return [windowStart, windowEnd];
|
||||
},
|
||||
|
||||
if (collection.mode !== "infinite") {
|
||||
for (var i = windowStart; i < windowEnd; i++) {
|
||||
handles.push({
|
||||
label: i + 1,
|
||||
title: "No. " + (i + 1),
|
||||
className: currentPage === i ? "active" : undefined
|
||||
});
|
||||
}
|
||||
/**
|
||||
Creates a list of page handle objects for rendering.
|
||||
|
||||
@return {Array.<Object>} an array of page handle objects hashes
|
||||
*/
|
||||
makeHandles: function () {
|
||||
|
||||
var handles = [];
|
||||
var collection = this.collection;
|
||||
|
||||
var window = this._calculateWindow();
|
||||
var winStart = window[0], winEnd = window[1];
|
||||
|
||||
for (var i = winStart; i < winEnd; i++) {
|
||||
handles.push(new PageHandle({
|
||||
collection: collection,
|
||||
pageIndex: i
|
||||
}));
|
||||
}
|
||||
|
||||
var ffLabels = this.fastForwardHandleLabels;
|
||||
if (ffLabels) {
|
||||
|
||||
if (ffLabels.prev) {
|
||||
handles.unshift({
|
||||
label: ffLabels.prev,
|
||||
className: collection.hasPrevious() ? void 0 : "disabled"
|
||||
});
|
||||
var controls = this.controls;
|
||||
_.each(["back", "rewind", "forward", "fastForward"], function (key) {
|
||||
var value = controls[key];
|
||||
if (value) {
|
||||
var handleCtorOpts = {
|
||||
collection: collection,
|
||||
title: value.title,
|
||||
label: value.label
|
||||
};
|
||||
handleCtorOpts["is" + key.slice(0, 1).toUpperCase() + key.slice(1)] = true;
|
||||
var handle = new PageHandle(handleCtorOpts);
|
||||
if (key == "rewind" || key == "back") handles.unshift(handle);
|
||||
else handles.push(handle);
|
||||
}
|
||||
|
||||
if (ffLabels.first) {
|
||||
handles.unshift({
|
||||
label: ffLabels.first,
|
||||
className: collection.hasPrevious() ? void 0 : "disabled"
|
||||
});
|
||||
}
|
||||
|
||||
if (ffLabels.next) {
|
||||
handles.push({
|
||||
label: ffLabels.next,
|
||||
className: collection.hasNext() ? void 0 : "disabled"
|
||||
});
|
||||
}
|
||||
|
||||
if (ffLabels.last) {
|
||||
handles.push({
|
||||
label: ffLabels.last,
|
||||
className: collection.hasNext() ? void 0 : "disabled"
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return handles;
|
||||
},
|
||||
|
@ -184,15 +316,24 @@
|
|||
render: function () {
|
||||
this.$el.empty();
|
||||
|
||||
this.$el.append(this.template({
|
||||
handles: this.makeHandles()
|
||||
}));
|
||||
if (this.handles) {
|
||||
for (var i = 0, l = this.handles.length; i < l; i++) {
|
||||
this.handles[i].remove();
|
||||
}
|
||||
}
|
||||
|
||||
this.delegateEvents();
|
||||
var handles = this.handles = this.makeHandles();
|
||||
|
||||
var ul = document.createElement("ul");
|
||||
for (var i = 0; i < handles.length; i++) {
|
||||
ul.appendChild(handles[i].render().el);
|
||||
}
|
||||
|
||||
this.el.appendChild(ul);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}(jQuery, _, Backbone, Backgrid));
|
||||
}(_, Backbone, Backgrid));
|
||||
|
|
|
@ -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.<Object>} 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.<Object>} 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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue