/src/extensions/paginator/backgrid-paginator.js
JavaScript | 339 lines | 160 code | 44 blank | 135 comment | 37 complexity | 05c0659c9e757fa62cc4f084f5a03eb1 MD5 | raw file
Possible License(s): MIT
- /*
- backgrid-paginator
- http://github.com/wyuenho/backgrid
- Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
- Licensed under the MIT @license.
- */
- (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 rewind, back, forward and fast forward
- control handles. The individual control handles can be turned off.
- @class Backgrid.Extension.Paginator
- */
- Backgrid.Extension.Paginator = Backbone.View.extend({
- /** @property */
- className: "backgrid-paginator",
- /** @property */
- windowSize: 10,
- /**
- @property {Object.<string, Object.<string, string>>} controls You can
- disable specific control handles by omitting certain keys.
- */
- controls: {
- rewind: {
- label: "《",
- title: "First"
- },
- back: {
- label: "〈",
- title: "Previous"
- },
- forward: {
- label: "〉",
- title: "Next"
- },
- fastForward: {
- label: "》",
- title: "Last"
- }
- },
- /**
- @property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle
- class to use for rendering individual handles
- */
- pageHandle: PageHandle,
- /** @property */
- goBackFirstOnSort: true,
- /**
- Initializer.
- @param {Object} options
- @param {Backbone.Collection} options.collection
- @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;
- 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();
- });
- }
- },
- _calculateWindow: function () {
- var collection = this.collection;
- var state = collection.state;
- // convert all indices to 0-based here
- var firstPage = state.firstPage;
- var lastPage = +state.lastPage;
- lastPage = Math.max(0, firstPage ? lastPage - 1 : lastPage);
- var currentPage = Math.max(state.currentPage, state.firstPage);
- 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];
- },
- /**
- 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 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);
- }
- });
- return handles;
- },
- /**
- Render the paginator handles inside an unordered list.
- */
- render: function () {
- this.$el.empty();
- if (this.handles) {
- for (var i = 0, l = this.handles.length; i < l; i++) {
- this.handles[i].remove();
- }
- }
- 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;
- }
- });
- }(_, Backbone, Backgrid));