mirror of https://github.com/Sonarr/Sonarr
Revert "updated backgrid" Also fixed his naive capitalization...
This reverts commit 8fd7def9dd
.
This commit is contained in:
parent
dca7274f35
commit
b81b39550f
|
@ -6,12 +6,10 @@
|
||||||
Licensed under the MIT @license.
|
Licensed under the MIT @license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function (root) {
|
(function ($, _, Backbone, Backgrid, lunr) {
|
||||||
|
|
||||||
"use strict";
|
"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
|
ServerSideFilter is a search form widget that submits a query to the server
|
||||||
for filtering the current collection.
|
for filtering the current collection.
|
||||||
|
@ -38,17 +36,14 @@
|
||||||
/** @property {string} [name='q'] Query key */
|
/** @property {string} [name='q'] Query key */
|
||||||
name: "q",
|
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,
|
placeholder: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@param {Object} options
|
@param {Object} options
|
||||||
@param {Backbone.Collection} options.collection
|
@param {Backbone.Collection} options.collection
|
||||||
@param {string} [options.name]
|
@param {String} [options.name]
|
||||||
@param {string} [options.placeholder]
|
@param {String} [options.placeholder]
|
||||||
*/
|
*/
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
Backgrid.requireOptions(options, ["collection"]);
|
Backgrid.requireOptions(options, ["collection"]);
|
||||||
|
@ -56,21 +51,16 @@
|
||||||
this.name = options.name || this.name;
|
this.name = options.name || this.name;
|
||||||
this.placeholder = options.placeholder || this.placeholder;
|
this.placeholder = options.placeholder || this.placeholder;
|
||||||
|
|
||||||
// Persist the query on pagination
|
|
||||||
var collection = this.collection, self = this;
|
var collection = this.collection, self = this;
|
||||||
if (Backbone.PageableCollection &&
|
if (Backbone.PageableCollection &&
|
||||||
collection instanceof Backbone.PageableCollection &&
|
collection instanceof Backbone.PageableCollection &&
|
||||||
collection.mode == "server") {
|
collection.mode == "server") {
|
||||||
collection.queryParams[this.name] = function () {
|
collection.queryParams[this.name] = function () {
|
||||||
return self.searchBox().val() || null;
|
return self.$el.find("input[type=text]").val();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
searchBox: function () {
|
|
||||||
return this.$el.find("input[type=text]");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Upon search form submission, this event handler constructs a query
|
Upon search form submission, this event handler constructs a query
|
||||||
parameter object and pass it to Collection#fetch for server-side
|
parameter object and pass it to Collection#fetch for server-side
|
||||||
|
@ -78,22 +68,9 @@
|
||||||
*/
|
*/
|
||||||
search: function (e) {
|
search: function (e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
|
|
||||||
var data = {};
|
var data = {};
|
||||||
|
data[this.name] = this.$el.find("input[type=text]").val();
|
||||||
// go back to the first page on search
|
this.collection.fetch({data: data});
|
||||||
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});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,8 +79,8 @@
|
||||||
*/
|
*/
|
||||||
clear: function (e) {
|
clear: function (e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
this.searchBox().val(null);
|
this.$("input[type=text]").val(null);
|
||||||
this.collection.fetch({reset: true});
|
this.collection.fetch();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,7 +115,8 @@
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.clear();
|
this.clear();
|
||||||
},
|
},
|
||||||
"keydown input[type=text]": "search",
|
"change input[type=text]": "search",
|
||||||
|
"keyup input[type=text]": "search",
|
||||||
"submit": function (e) {
|
"submit": function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.search();
|
this.search();
|
||||||
|
@ -146,14 +124,14 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@property {?Array.<string>} [fields] A list of model field names to
|
@property {?Array.<string>} A list of model field names to search
|
||||||
search for matches. If null, all of the fields will be searched.
|
for matches. If null, all of the fields will be searched.
|
||||||
*/
|
*/
|
||||||
fields: null,
|
fields: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@property [wait=149] The time in milliseconds to wait since for since the
|
@property wait The time in milliseconds to wait since for since the last
|
||||||
last change to the search box's value before searching. This value can be
|
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
|
adjusted depending on how often the search box is used and how large the
|
||||||
search index is.
|
search index is.
|
||||||
*/
|
*/
|
||||||
|
@ -165,9 +143,9 @@
|
||||||
|
|
||||||
@param {Object} options
|
@param {Object} options
|
||||||
@param {Backbone.Collection} options.collection
|
@param {Backbone.Collection} options.collection
|
||||||
@param {string} [options.placeholder]
|
@param {String} [options.placeholder]
|
||||||
@param {string} [options.fields]
|
@param {String} [options.fields]
|
||||||
@param {string} [options.wait=149]
|
@param {String} [options.wait=149]
|
||||||
*/
|
*/
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
ServerSideFilter.prototype.initialize.apply(this, arguments);
|
ServerSideFilter.prototype.initialize.apply(this, arguments);
|
||||||
|
@ -177,8 +155,11 @@
|
||||||
|
|
||||||
this._debounceMethods(["search", "clear"]);
|
this._debounceMethods(["search", "clear"]);
|
||||||
|
|
||||||
var collection = this.collection = this.collection.fullCollection || this.collection;
|
var collection = this.collection;
|
||||||
var shadowCollection = this.shadowCollection = collection.clone();
|
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) {
|
this.listenTo(collection, "add", function (model, collection, options) {
|
||||||
shadowCollection.add(model, options);
|
shadowCollection.add(model, options);
|
||||||
|
@ -186,15 +167,9 @@
|
||||||
this.listenTo(collection, "remove", function (model, collection, options) {
|
this.listenTo(collection, "remove", function (model, collection, options) {
|
||||||
shadowCollection.remove(model, options);
|
shadowCollection.remove(model, options);
|
||||||
});
|
});
|
||||||
this.listenTo(collection, "sort", function (col) {
|
this.listenTo(collection, "sort reset", function (collection, options) {
|
||||||
if (!this.searchBox().val()) shadowCollection.reset(col.models);
|
|
||||||
});
|
|
||||||
this.listenTo(collection, "reset", function (col, options) {
|
|
||||||
options = _.extend({reindex: true}, options || {});
|
options = _.extend({reindex: true}, options || {});
|
||||||
if (options.reindex && col === collection &&
|
if (options.reindex) shadowCollection.reset(collection.models);
|
||||||
options.from == null && options.to == null) {
|
|
||||||
shadowCollection.reset(col.models);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -243,9 +218,7 @@
|
||||||
when all the matches have been found.
|
when all the matches have been found.
|
||||||
*/
|
*/
|
||||||
search: function () {
|
search: function () {
|
||||||
var matcher = _.bind(this.makeMatcher(this.searchBox().val()), this);
|
var matcher = _.bind(this.makeMatcher(this.$("input[type=text]").val()), this);
|
||||||
var col = this.collection;
|
|
||||||
if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true});
|
|
||||||
this.collection.reset(this.shadowCollection.filter(matcher), {reindex: false});
|
this.collection.reset(this.shadowCollection.filter(matcher), {reindex: false});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -253,7 +226,7 @@
|
||||||
Clears the search box and reset the collection to its original.
|
Clears the search box and reset the collection to its original.
|
||||||
*/
|
*/
|
||||||
clear: function () {
|
clear: function () {
|
||||||
this.searchBox().val(null);
|
this.$("input[type=text]").val(null);
|
||||||
this.collection.reset(this.shadowCollection.models, {reindex: false});
|
this.collection.reset(this.shadowCollection.models, {reindex: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +262,7 @@
|
||||||
|
|
||||||
@param {Object} options
|
@param {Object} options
|
||||||
@param {Backbone.Collection} options.collection
|
@param {Backbone.Collection} options.collection
|
||||||
@param {string} [options.placeholder]
|
@param {String} [options.placeholder]
|
||||||
@param {string} [options.ref] `lunrjs` document reference attribute name.
|
@param {string} [options.ref] `lunrjs` document reference attribute name.
|
||||||
@param {Object} [options.fields] A hash of `lunrjs` index field names and
|
@param {Object} [options.fields] A hash of `lunrjs` index field names and
|
||||||
boost value.
|
boost value.
|
||||||
|
@ -300,7 +273,7 @@
|
||||||
|
|
||||||
this.ref = options.ref || this.ref;
|
this.ref = options.ref || this.ref;
|
||||||
|
|
||||||
var collection = this.collection = this.collection.fullCollection || this.collection;
|
var collection = this.collection;
|
||||||
this.listenTo(collection, "add", this.addToIndex);
|
this.listenTo(collection, "add", this.addToIndex);
|
||||||
this.listenTo(collection, "remove", this.removeFromIndex);
|
this.listenTo(collection, "remove", this.removeFromIndex);
|
||||||
this.listenTo(collection, "reset", this.resetIndex);
|
this.listenTo(collection, "reset", this.resetIndex);
|
||||||
|
@ -378,17 +351,15 @@
|
||||||
query answer.
|
query answer.
|
||||||
*/
|
*/
|
||||||
search: function () {
|
search: function () {
|
||||||
var searchResults = this.index.search(this.searchBox().val());
|
var searchResults = this.index.search(this.$("input[type=text]").val());
|
||||||
var models = [];
|
var models = [];
|
||||||
for (var i = 0; i < searchResults.length; i++) {
|
for (var i = 0; i < searchResults.length; i++) {
|
||||||
var result = searchResults[i];
|
var result = searchResults[i];
|
||||||
models.push(this.shadowCollection.get(result.ref));
|
models.push(this.shadowCollection.get(result.ref));
|
||||||
}
|
}
|
||||||
var col = this.collection;
|
this.collection.reset(models, {reindex: false});
|
||||||
if (col.pageableCollection) col.pageableCollection.getFirstPage({silent: true});
|
|
||||||
col.reset(models, {reindex: false});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}(this));
|
}(jQuery, _, Backbone, Backgrid, lunr));
|
||||||
|
|
|
@ -41,6 +41,12 @@ if (!String.prototype.trim || ws.trim()) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function capitalize(s) {
|
||||||
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||||
|
|
||||||
|
// return String.fromCharCode(s.charCodeAt(0) - 32) + s.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
function lpad(str, length, padstr) {
|
function lpad(str, length, padstr) {
|
||||||
var paddingLen = length - (str + '').length;
|
var paddingLen = length - (str + '').length;
|
||||||
paddingLen = paddingLen < 0 ? 0 : paddingLen;
|
paddingLen = paddingLen < 0 ? 0 : paddingLen;
|
||||||
|
@ -68,9 +74,7 @@ var Backgrid = root.Backgrid = {
|
||||||
|
|
||||||
resolveNameToClass: function (name, suffix) {
|
resolveNameToClass: function (name, suffix) {
|
||||||
if (_.isString(name)) {
|
if (_.isString(name)) {
|
||||||
var key = _.map(name.split('-'), function (e) {
|
var key = _.map(name.split('-'), function (e) { return capitalize(e); }).join('') + suffix;
|
||||||
return e.slice(0, 1).toUpperCase() + e.slice(1);
|
|
||||||
}).join('') + suffix;
|
|
||||||
var klass = Backgrid[key] || Backgrid.Extension[key];
|
var klass = Backgrid[key] || Backgrid.Extension[key];
|
||||||
if (_.isUndefined(klass)) {
|
if (_.isUndefined(klass)) {
|
||||||
throw new ReferenceError("Class '" + key + "' not found");
|
throw new ReferenceError("Class '" + key + "' not found");
|
||||||
|
@ -79,17 +83,7 @@ var Backgrid = root.Backgrid = {
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
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);
|
_.extend(Backgrid, Backbone.Events);
|
||||||
|
|
||||||
|
@ -107,7 +101,7 @@ _.extend(Backgrid, Backbone.Events);
|
||||||
var Command = Backgrid.Command = function (evt) {
|
var Command = Backgrid.Command = function (evt) {
|
||||||
_.extend(this, {
|
_.extend(this, {
|
||||||
altKey: !!evt.altKey,
|
altKey: !!evt.altKey,
|
||||||
"char": evt["char"],
|
char: evt.char,
|
||||||
charCode: evt.charCode,
|
charCode: evt.charCode,
|
||||||
ctrlKey: !!evt.ctrlKey,
|
ctrlKey: !!evt.ctrlKey,
|
||||||
key: evt.key,
|
key: evt.key,
|
||||||
|
@ -745,33 +739,12 @@ var Cell = Backgrid.Cell = Backbone.View.extend({
|
||||||
if (!(this.column instanceof Column)) {
|
if (!(this.column instanceof Column)) {
|
||||||
this.column = new Column(this.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.editor = Backgrid.resolveNameToClass(this.editor, "CellEditor");
|
||||||
|
this.listenTo(this.model, "change:" + this.column.get("name"), function () {
|
||||||
this.listenTo(model, "change:" + column.get("name"), function () {
|
if (!this.$el.hasClass("editor")) this.render();
|
||||||
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");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -808,8 +781,7 @@ var Cell = Backgrid.Cell = Backbone.View.extend({
|
||||||
var model = this.model;
|
var model = this.model;
|
||||||
var column = this.column;
|
var column = this.column;
|
||||||
|
|
||||||
var editable = Backgrid.callByNeed(column.get("editable"), column, model);
|
if (column.get("editable")) {
|
||||||
if (editable) {
|
|
||||||
|
|
||||||
this.currentEditor = new this.editor({
|
this.currentEditor = new this.editor({
|
||||||
column: this.column,
|
column: this.column,
|
||||||
|
@ -858,7 +830,7 @@ var Cell = Backgrid.Cell = Backbone.View.extend({
|
||||||
*/
|
*/
|
||||||
remove: function () {
|
remove: function () {
|
||||||
if (this.currentEditor) {
|
if (this.currentEditor) {
|
||||||
this.currentEditor.remove.apply(this.currentEditor, arguments);
|
this.currentEditor.remove.apply(this, arguments);
|
||||||
delete this.currentEditor;
|
delete this.currentEditor;
|
||||||
}
|
}
|
||||||
return Backbone.View.prototype.remove.apply(this, arguments);
|
return Backbone.View.prototype.remove.apply(this, arguments);
|
||||||
|
@ -1513,7 +1485,6 @@ var Column = Backgrid.Column = Backbone.Model.extend({
|
||||||
editable: true,
|
editable: true,
|
||||||
renderable: true,
|
renderable: true,
|
||||||
formatter: undefined,
|
formatter: undefined,
|
||||||
sortValue: undefined,
|
|
||||||
cell: undefined,
|
cell: undefined,
|
||||||
headerCell: undefined
|
headerCell: undefined
|
||||||
},
|
},
|
||||||
|
@ -1522,36 +1493,22 @@ var Column = Backgrid.Column = Backbone.Model.extend({
|
||||||
Initializes this Column instance.
|
Initializes this Column instance.
|
||||||
|
|
||||||
@param {Object} attrs Column attributes.
|
@param {Object} attrs Column attributes.
|
||||||
|
|
||||||
@param {string} attrs.name The name of the model attribute.
|
@param {string} attrs.name The name of the model attribute.
|
||||||
|
|
||||||
@param {string|Backgrid.Cell} attrs.cell The cell type.
|
@param {string|Backgrid.Cell} attrs.cell The cell type.
|
||||||
If this is a string, the capitalized form will be used to look up a
|
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
|
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
|
is supplied, it is initialized with a hash of parameters. If a Cell
|
||||||
instance is supplied, it is used directly.
|
instance is supplied, it is used directly.
|
||||||
|
|
||||||
@param {string|Backgrid.HeaderCell} [attrs.headerCell] The header cell type.
|
@param {string|Backgrid.HeaderCell} [attrs.headerCell] The header cell type.
|
||||||
|
|
||||||
@param {string} [attrs.label] The label to show in the header.
|
@param {string} [attrs.label] The label to show in the header.
|
||||||
|
@param {boolean} [attrs.sortable=true]
|
||||||
@param {boolean|string} [attrs.sortable=true]
|
@param {boolean} [attrs.editable=true]
|
||||||
|
@param {boolean} [attrs.renderable=true]
|
||||||
@param {boolean|string} [attrs.editable=true]
|
@param {Backgrid.CellFormatter|Object|string} [attrs.formatter] The
|
||||||
|
|
||||||
@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.
|
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 {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.
|
said name cannot be found in the Backgrid module.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
|
@ -1567,32 +1524,8 @@ var Column = Backgrid.Column = Backbone.Model.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
var headerCell = Backgrid.resolveNameToClass(this.get("headerCell"), "HeaderCell");
|
var headerCell = Backgrid.resolveNameToClass(this.get("headerCell"), "HeaderCell");
|
||||||
|
|
||||||
var cell = Backgrid.resolveNameToClass(this.get("cell"), "Cell");
|
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 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1656,11 +1589,22 @@ var Row = Backgrid.Row = Backbone.View.extend({
|
||||||
cells.push(this.makeCell(columns.at(i), options));
|
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) {
|
this.listenTo(columns, "add", function (column, columns) {
|
||||||
var i = columns.indexOf(column);
|
var i = columns.indexOf(column);
|
||||||
var cell = this.makeCell(column, options);
|
var cell = this.makeCell(column, options);
|
||||||
cells.splice(i, 0, cell);
|
cells.splice(i, 0, cell);
|
||||||
|
|
||||||
|
if (!cell.column.get("renderable")) cell.$el.hide();
|
||||||
|
|
||||||
var $el = this.$el;
|
var $el = this.$el;
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
$el.prepend(cell.render().$el);
|
$el.prepend(cell.render().$el);
|
||||||
|
@ -1704,8 +1648,11 @@ var Row = Backgrid.Row = Backbone.View.extend({
|
||||||
this.$el.empty();
|
this.$el.empty();
|
||||||
|
|
||||||
var fragment = document.createDocumentFragment();
|
var fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
for (var i = 0; i < this.cells.length; i++) {
|
for (var i = 0; i < this.cells.length; i++) {
|
||||||
fragment.appendChild(this.cells[i].render().el);
|
var cell = this.cells[i];
|
||||||
|
fragment.appendChild(cell.render().el);
|
||||||
|
if (!cell.column.get("renderable")) cell.$el.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.el.appendChild(fragment);
|
this.el.appendChild(fragment);
|
||||||
|
@ -1821,24 +1768,7 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
||||||
if (!(this.column instanceof Column)) {
|
if (!(this.column instanceof Column)) {
|
||||||
this.column = new Column(this.column);
|
this.column = new Column(this.column);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listenTo(this.collection, "backgrid:sort", this._resetCellDirection);
|
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");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1865,9 +1795,9 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
||||||
|
|
||||||
@private
|
@private
|
||||||
*/
|
*/
|
||||||
_resetCellDirection: function (columnToSort, direction, comparator, collection) {
|
_resetCellDirection: function (sortByColName, direction, comparator, collection) {
|
||||||
if (collection == this.collection) {
|
if (collection == this.collection) {
|
||||||
if (columnToSort !== this.column) this.direction(null);
|
if (sortByColName !== this.column.get("name")) this.direction(null);
|
||||||
else this.direction(direction);
|
else this.direction(direction);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1880,12 +1810,34 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
||||||
onClick: function (e) {
|
onClick: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var column = this.column;
|
var columnName = this.column.get("name");
|
||||||
var sortable = Backgrid.callByNeed(column.get("sortable"), column, this.model);
|
|
||||||
if (sortable) {
|
if (this.column.get("sortable")) {
|
||||||
if (this.direction() === "ascending") this.sort(column, "descending");
|
if (this.direction() === "ascending") {
|
||||||
else if (this.direction() === "descending") this.sort(column, null);
|
this.sort(columnName, "descending", function (left, right) {
|
||||||
else this.sort(column, "ascending");
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1902,37 +1854,31 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
||||||
and the current page will then be returned.
|
and the current page will then be returned.
|
||||||
|
|
||||||
Triggers a Backbone `backgrid:sort` event from the collection when done
|
Triggers a Backbone `backgrid:sort` event from the collection when done
|
||||||
with the column, direction, comparator and a reference to the collection.
|
with the column name, direction, comparator and a reference to the
|
||||||
|
collection.
|
||||||
|
|
||||||
@param {Backgrid.Column} column
|
@param {string} columnName
|
||||||
@param {null|"ascending"|"descending"} direction
|
@param {null|"ascending"|"descending"} direction
|
||||||
|
@param {function(*, *): number} [comparator]
|
||||||
|
|
||||||
See [Backbone.Collection#comparator](http://backbonejs.org/#Collection-comparator)
|
See [Backbone.Collection#comparator](http://backbonejs.org/#Collection-comparator)
|
||||||
*/
|
*/
|
||||||
sort: function (column, direction) {
|
sort: function (columnName, direction, comparator) {
|
||||||
|
|
||||||
|
comparator = comparator || this._cidComparator;
|
||||||
|
|
||||||
var collection = this.collection;
|
var collection = this.collection;
|
||||||
|
|
||||||
var order;
|
if (Backbone.PageableCollection && collection instanceof Backbone.PageableCollection) {
|
||||||
if (direction === "ascending") order = -1;
|
var order;
|
||||||
else if (direction === "descending") order = 1;
|
if (direction === "ascending") order = -1;
|
||||||
else order = null;
|
else if (direction === "descending") order = 1;
|
||||||
|
else order = null;
|
||||||
|
|
||||||
var comparator = this.makeComparator(column.get("name"), order,
|
collection.setSorting(order ? columnName : null, 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.mode == "client") {
|
||||||
if (collection.fullCollection.comparator == null) {
|
if (!collection.fullCollection.comparator) {
|
||||||
collection.fullCollection.comparator = comparator;
|
collection.fullCollection.comparator = comparator;
|
||||||
}
|
}
|
||||||
collection.fullCollection.sort();
|
collection.fullCollection.sort();
|
||||||
|
@ -1944,24 +1890,26 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
||||||
collection.sort();
|
collection.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.collection.trigger("backgrid:sort", column, direction, comparator,
|
this.collection.trigger("backgrid:sort", columnName, direction, comparator, this.collection);
|
||||||
this.collection);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
makeComparator: function (attr, order, func) {
|
/**
|
||||||
|
Default comparator for Backbone.Collections. Sorts cids in ascending
|
||||||
|
order. The cids of the models are assumed to be in insertion order.
|
||||||
|
|
||||||
return function (left, right) {
|
@private
|
||||||
// extract the values from the models
|
@param {*} left
|
||||||
var l = func(left, attr), r = func(right, attr), t;
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
// if descending order, swap left and right
|
return 0;
|
||||||
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;
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1969,9 +1917,7 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
|
||||||
*/
|
*/
|
||||||
render: function () {
|
render: function () {
|
||||||
this.$el.empty();
|
this.$el.empty();
|
||||||
var $label = $("<a>").text(this.column.get("label"));
|
var $label = $("<a>").text(this.column.get("label")).append("<b class='sort-caret'></b>");
|
||||||
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.$el.append($label);
|
||||||
this.delegateEvents();
|
this.delegateEvents();
|
||||||
return this;
|
return this;
|
||||||
|
@ -2315,9 +2261,6 @@ var Body = Backgrid.Body = Backbone.View.extend({
|
||||||
moveToNextCell: function (model, column, command) {
|
moveToNextCell: function (model, column, command) {
|
||||||
var i = this.collection.indexOf(model);
|
var i = this.collection.indexOf(model);
|
||||||
var j = this.columns.indexOf(column);
|
var j = this.columns.indexOf(column);
|
||||||
var cell, renderable, editable;
|
|
||||||
|
|
||||||
this.rows[i].cells[j].exitEditMode();
|
|
||||||
|
|
||||||
if (command.moveUp() || command.moveDown() || command.moveLeft() ||
|
if (command.moveUp() || command.moveDown() || command.moveLeft() ||
|
||||||
command.moveRight() || command.save()) {
|
command.moveRight() || command.save()) {
|
||||||
|
@ -2326,12 +2269,7 @@ var Body = Backgrid.Body = Backbone.View.extend({
|
||||||
|
|
||||||
if (command.moveUp() || command.moveDown()) {
|
if (command.moveUp() || command.moveDown()) {
|
||||||
var row = this.rows[i + (command.moveUp() ? -1 : 1)];
|
var row = this.rows[i + (command.moveUp() ? -1 : 1)];
|
||||||
if (row) {
|
if (row) row.cells[j].enterEditMode();
|
||||||
cell = row.cells[j];
|
|
||||||
if (Backgrid.callByNeed(cell.column.get("editable"), cell.column, model)) {
|
|
||||||
cell.enterEditMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (command.moveLeft() || command.moveRight()) {
|
else if (command.moveLeft() || command.moveRight()) {
|
||||||
var right = command.moveRight();
|
var right = command.moveRight();
|
||||||
|
@ -2340,16 +2278,16 @@ var Body = Backgrid.Body = Backbone.View.extend({
|
||||||
right ? offset++ : offset--) {
|
right ? offset++ : offset--) {
|
||||||
var m = ~~(offset / l);
|
var m = ~~(offset / l);
|
||||||
var n = offset - m * l;
|
var n = offset - m * l;
|
||||||
cell = this.rows[m].cells[n];
|
var cell = this.rows[m].cells[n];
|
||||||
renderable = Backgrid.callByNeed(cell.column.get("renderable"), cell.column, cell.model);
|
if (cell.column.get("renderable") && cell.column.get("editable")) {
|
||||||
editable = Backgrid.callByNeed(cell.column.get("editable"), cell.column, model);
|
|
||||||
if (renderable && editable) {
|
|
||||||
cell.enterEditMode();
|
cell.enterEditMode();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.rows[i].cells[j].exitEditMode();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -6,186 +6,18 @@
|
||||||
Licensed under the MIT @license.
|
Licensed under the MIT @license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function (_, Backbone, Backgrid) {
|
(function ($, _, Backbone, Backgrid) {
|
||||||
|
|
||||||
"use strict";
|
"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
|
Paginator is a Backgrid extension that renders a series of configurable
|
||||||
pagination handles. This extension is best used for splitting a large data
|
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
|
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
|
threshold, which is set to 10 by default, the page handles are rendered
|
||||||
within a sliding window, plus the rewind, back, forward and fast forward
|
within a sliding window, plus the fast forward, fast backward, previous and
|
||||||
control handles. The individual control handles can be turned off.
|
next page handles. The fast forward, fast backward, previous and next page
|
||||||
|
handles can be turned off.
|
||||||
|
|
||||||
@class Backgrid.Extension.Paginator
|
@class Backgrid.Extension.Paginator
|
||||||
*/
|
*/
|
||||||
|
@ -198,65 +30,97 @@
|
||||||
windowSize: 10,
|
windowSize: 10,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@property {Object.<string, Object.<string, string>>} controls You can
|
@property {Object} fastForwardHandleLabels You can disable specific
|
||||||
disable specific control handles by omitting certain keys.
|
handles by setting its value to `null`.
|
||||||
*/
|
*/
|
||||||
controls: {
|
fastForwardHandleLabels: {
|
||||||
rewind: {
|
first: "《",
|
||||||
label: "《",
|
prev: "〈",
|
||||||
title: "First"
|
next: "〉",
|
||||||
},
|
last: "》"
|
||||||
back: {
|
|
||||||
label: "〈",
|
|
||||||
title: "Previous"
|
|
||||||
},
|
|
||||||
forward: {
|
|
||||||
label: "〉",
|
|
||||||
title: "Next"
|
|
||||||
},
|
|
||||||
fastForward: {
|
|
||||||
label: "》",
|
|
||||||
title: "Last"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/** @property */
|
||||||
@property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle
|
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>'),
|
||||||
class to use for rendering individual handles
|
|
||||||
*/
|
|
||||||
pageHandle: PageHandle,
|
|
||||||
|
|
||||||
/** @property */
|
/** @property */
|
||||||
goBackFirstOnSort: true,
|
events: {
|
||||||
|
"click a": "changePage"
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initializer.
|
Initializer.
|
||||||
|
|
||||||
@param {Object} options
|
@param {Object} options
|
||||||
@param {Backbone.Collection} options.collection
|
@param {Backbone.Collection} options.collection
|
||||||
@param {boolean} [options.controls]
|
@param {boolean} [options.fastForwardHandleLabels] Whether to render fast forward buttons.
|
||||||
@param {boolean} [options.pageHandle=Backgrid.Extension.PageHandle]
|
|
||||||
@param {boolean} [options.goBackFirstOnSort=true]
|
|
||||||
*/
|
*/
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
Backgrid.requireOptions(options, ["collection"]);
|
Backgrid.requireOptions(options, ["collection"]);
|
||||||
|
|
||||||
this.controls = options.controls || this.controls;
|
|
||||||
this.pageHandle = options.pageHandle || this.pageHandle;
|
|
||||||
|
|
||||||
var collection = this.collection;
|
var collection = this.collection;
|
||||||
this.listenTo(collection, "add", this.render);
|
var fullCollection = collection.fullCollection;
|
||||||
this.listenTo(collection, "remove", this.render);
|
if (fullCollection) {
|
||||||
this.listenTo(collection, "reset", this.render);
|
this.listenTo(fullCollection, "add", this.render);
|
||||||
if ((options.goBackFirstOnSort || this.goBackFirstOnSort) &&
|
this.listenTo(fullCollection, "remove", this.render);
|
||||||
collection.fullCollection) {
|
this.listenTo(fullCollection, "reset", this.render);
|
||||||
this.listenTo(collection.fullCollection, "sort", function () {
|
}
|
||||||
collection.getFirstPage();
|
else {
|
||||||
});
|
this.listenTo(collection, "add", this.render);
|
||||||
|
this.listenTo(collection, "remove", this.render);
|
||||||
|
this.listenTo(collection, "reset", this.render);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_calculateWindow: function () {
|
/**
|
||||||
|
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 = [];
|
||||||
var collection = this.collection;
|
var collection = this.collection;
|
||||||
var state = collection.state;
|
var state = collection.state;
|
||||||
|
|
||||||
|
@ -268,44 +132,48 @@
|
||||||
currentPage = firstPage ? currentPage - 1 : currentPage;
|
currentPage = firstPage ? currentPage - 1 : currentPage;
|
||||||
var windowStart = Math.floor(currentPage / this.windowSize) * this.windowSize;
|
var windowStart = Math.floor(currentPage / this.windowSize) * this.windowSize;
|
||||||
var windowEnd = Math.min(lastPage + 1, windowStart + this.windowSize);
|
var windowEnd = Math.min(lastPage + 1, windowStart + this.windowSize);
|
||||||
return [windowStart, windowEnd];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
if (collection.mode !== "infinite") {
|
||||||
Creates a list of page handle objects for rendering.
|
for (var i = windowStart; i < windowEnd; i++) {
|
||||||
|
handles.push({
|
||||||
@return {Array.<Object>} an array of page handle objects hashes
|
label: i + 1,
|
||||||
*/
|
title: "No. " + (i + 1),
|
||||||
makeHandles: function () {
|
className: currentPage === i ? "active" : undefined
|
||||||
|
});
|
||||||
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 controls = this.controls;
|
var ffLabels = this.fastForwardHandleLabels;
|
||||||
_.each(["back", "rewind", "forward", "fastForward"], function (key) {
|
if (ffLabels) {
|
||||||
var value = controls[key];
|
|
||||||
if (value) {
|
if (ffLabels.prev) {
|
||||||
var handleCtorOpts = {
|
handles.unshift({
|
||||||
collection: collection,
|
label: ffLabels.prev,
|
||||||
title: value.title,
|
className: collection.hasPrevious() ? void 0 : "disabled"
|
||||||
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;
|
return handles;
|
||||||
},
|
},
|
||||||
|
@ -316,24 +184,15 @@
|
||||||
render: function () {
|
render: function () {
|
||||||
this.$el.empty();
|
this.$el.empty();
|
||||||
|
|
||||||
if (this.handles) {
|
this.$el.append(this.template({
|
||||||
for (var i = 0, l = this.handles.length; i < l; i++) {
|
handles: this.makeHandles()
|
||||||
this.handles[i].remove();
|
}));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var handles = this.handles = this.makeHandles();
|
this.delegateEvents();
|
||||||
|
|
||||||
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}(_, Backbone, Backgrid));
|
}(jQuery, _, Backbone, Backgrid));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
backbone-pageable 1.3.1
|
backbone-pageable 1.3.0
|
||||||
http://github.com/wyuenho/backbone-pageable
|
http://github.com/wyuenho/backbone-pageable
|
||||||
|
|
||||||
Copyright (c) 2013 Jimmy Yuen Ho Wong
|
Copyright (c) 2013 Jimmy Yuen Ho Wong
|
||||||
|
@ -574,17 +574,6 @@
|
||||||
/**
|
/**
|
||||||
Change the page size of this collection.
|
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
|
For server mode operations, changing the page size will trigger a #fetch
|
||||||
and subsequently a `reset` event.
|
and subsequently a `reset` event.
|
||||||
|
|
||||||
|
@ -597,8 +586,6 @@
|
||||||
|
|
||||||
@param {number} pageSize The new page size to set to #state.
|
@param {number} pageSize The new page size to set to #state.
|
||||||
@param {Object} [options] {@link #fetch} options.
|
@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.
|
@param {boolean} [options.fetch] If `true`, force a fetch in client mode.
|
||||||
|
|
||||||
@throws {TypeError} If `pageSize` is not a finite integer.
|
@throws {TypeError} If `pageSize` is not a finite integer.
|
||||||
|
@ -611,24 +598,14 @@
|
||||||
setPageSize: function (pageSize, options) {
|
setPageSize: function (pageSize, options) {
|
||||||
pageSize = finiteInt(pageSize, "pageSize");
|
pageSize = finiteInt(pageSize, "pageSize");
|
||||||
|
|
||||||
options = options || {first: false};
|
options = options || {};
|
||||||
|
|
||||||
var state = this.state;
|
this.state = this._checkState(_extend({}, 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,
|
pageSize: pageSize,
|
||||||
currentPage: options.first ? state.firstPage : currentPage,
|
totalPages: ceil(this.state.totalRecords / pageSize)
|
||||||
totalPages: totalPages
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return this.getPage(state.currentPage, _omit(options, ["first"]));
|
return this.getPage(this.state.currentPage, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1015,14 +992,13 @@
|
||||||
encouraged to override #parseState and #parseRecords instead.
|
encouraged to override #parseState and #parseRecords instead.
|
||||||
|
|
||||||
@param {Object} resp The deserialized response data from the server.
|
@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
|
@return {Array.<Object>} An array of model objects
|
||||||
*/
|
*/
|
||||||
parse: function (resp, options) {
|
parse: function (resp) {
|
||||||
var newState = this.parseState(resp, _clone(this.queryParams), _clone(this.state), options);
|
var newState = this.parseState(resp, _clone(this.queryParams), _clone(this.state));
|
||||||
if (newState) this.state = this._checkState(_extend({}, this.state, newState));
|
if (newState) this.state = this._checkState(_extend({}, this.state, newState));
|
||||||
return this.parseRecords(resp, options);
|
return this.parseRecords(resp);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1040,16 +1016,10 @@
|
||||||
`totalRecords` value is enough to trigger a full pagination state
|
`totalRecords` value is enough to trigger a full pagination state
|
||||||
recalculation.
|
recalculation.
|
||||||
|
|
||||||
parseState: function (resp, queryParams, state, options) {
|
parseState: function (resp, queryParams, state) {
|
||||||
return {totalRecords: resp.total_entries};
|
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
|
This method __MUST__ return a new state object instead of directly
|
||||||
modifying the #state object. The behavior of directly modifying #state is
|
modifying the #state object. The behavior of directly modifying #state is
|
||||||
undefined.
|
undefined.
|
||||||
|
@ -1057,12 +1027,10 @@
|
||||||
@param {Object} resp The deserialized response data from the server.
|
@param {Object} resp The deserialized response data from the server.
|
||||||
@param {Object} queryParams A copy of #queryParams.
|
@param {Object} queryParams A copy of #queryParams.
|
||||||
@param {Object} state A copy of #state.
|
@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.
|
@return {Object} A new (partial) state object.
|
||||||
*/
|
*/
|
||||||
parseState: function (resp, queryParams, state, options) {
|
parseState: function (resp, queryParams, state) {
|
||||||
if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) {
|
if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) {
|
||||||
|
|
||||||
var newState = _clone(state);
|
var newState = _clone(state);
|
||||||
|
@ -1091,12 +1059,10 @@
|
||||||
response is returned directly.
|
response is returned directly.
|
||||||
|
|
||||||
@param {Object} resp The deserialized response data from the server.
|
@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
|
@return {Array.<Object>} An array of model objects
|
||||||
*/
|
*/
|
||||||
parseRecords: function (resp, options) {
|
parseRecords: function (resp) {
|
||||||
if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) {
|
if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) {
|
||||||
return resp[1];
|
return resp[1];
|
||||||
}
|
}
|
||||||
|
@ -1172,7 +1138,7 @@
|
||||||
kvp = extraKvps[i];
|
kvp = extraKvps[i];
|
||||||
v = kvp[1];
|
v = kvp[1];
|
||||||
v = _isFunction(v) ? v.call(thisCopy) : v;
|
v = _isFunction(v) ? v.call(thisCopy) : v;
|
||||||
if (v != null) data[kvp[0]] = v;
|
data[kvp[0]] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullCol = this.fullCollection, links = this.links;
|
var fullCol = this.fullCollection, links = this.links;
|
||||||
|
@ -1246,11 +1212,11 @@
|
||||||
|
|
||||||
@param {string} [sortKey=this.state.sortKey] See `state.sortKey`.
|
@param {string} [sortKey=this.state.sortKey] See `state.sortKey`.
|
||||||
@param {number} [order=this.state.order] See `state.order`.
|
@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).
|
See [Backbone.Collection.comparator](http://backbonejs.org/#Collection-comparator).
|
||||||
*/
|
*/
|
||||||
_makeComparator: function (sortKey, order, sortValue) {
|
_makeComparator: function (sortKey, order) {
|
||||||
|
|
||||||
var state = this.state;
|
var state = this.state;
|
||||||
|
|
||||||
sortKey = sortKey || state.sortKey;
|
sortKey = sortKey || state.sortKey;
|
||||||
|
@ -1258,12 +1224,8 @@
|
||||||
|
|
||||||
if (!sortKey || !order) return;
|
if (!sortKey || !order) return;
|
||||||
|
|
||||||
if (!sortValue) sortValue = function (model, attr) {
|
|
||||||
return model.get(attr);
|
|
||||||
};
|
|
||||||
|
|
||||||
return function (left, right) {
|
return function (left, right) {
|
||||||
var l = sortValue(left, sortKey), r = sortValue(right, sortKey), t;
|
var l = left.get(sortKey), r = right.get(sortKey), t;
|
||||||
if (order === 1) t = l, l = r, r = t;
|
if (order === 1) t = l, l = r, r = t;
|
||||||
if (l === r) return 0;
|
if (l === r) return 0;
|
||||||
else if (l < r) return -1;
|
else if (l < r) return -1;
|
||||||
|
@ -1282,11 +1244,6 @@
|
||||||
`sortKey` to `null` removes the comparator from both the current page and
|
`sortKey` to `null` removes the comparator from both the current page and
|
||||||
the full collection.
|
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
|
@chainable
|
||||||
|
|
||||||
@param {string} sortKey See `state.sortKey`.
|
@param {string} sortKey See `state.sortKey`.
|
||||||
|
@ -1295,7 +1252,6 @@
|
||||||
@param {"server"|"client"} [options.side] By default, `"client"` if
|
@param {"server"|"client"} [options.side] By default, `"client"` if
|
||||||
`mode` is `"client"`, `"server"` otherwise.
|
`mode` is `"client"`, `"server"` otherwise.
|
||||||
@param {boolean} [options.full=true]
|
@param {boolean} [options.full=true]
|
||||||
@param {(function(Backbone.Model, string): Object) | string} [options.sortValue]
|
|
||||||
*/
|
*/
|
||||||
setSorting: function (sortKey, order, options) {
|
setSorting: function (sortKey, order, options) {
|
||||||
|
|
||||||
|
@ -1314,7 +1270,7 @@
|
||||||
options = _extend({side: mode == "client" ? mode : "server", full: true},
|
options = _extend({side: mode == "client" ? mode : "server", full: true},
|
||||||
options);
|
options);
|
||||||
|
|
||||||
var comparator = this._makeComparator(sortKey, order, options.sortValue);
|
var comparator = this._makeComparator(sortKey, order);
|
||||||
|
|
||||||
var full = options.full, side = options.side;
|
var full = options.full, side = options.side;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue