Skip to content

Instantly share code, notes, and snippets.

@allex
Created November 15, 2016 07:48
Show Gist options
  • Select an option

  • Save allex/4b15fe7c6bcea3f8b30f865cfd928939 to your computer and use it in GitHub Desktop.

Select an option

Save allex/4b15fe7c6bcea3f8b30f865cfd928939 to your computer and use it in GitHub Desktop.
/**
* A simple pagination.
*
* @author Allex Wang ([email protected])
*
* @example
*
* ```js
* var pagination = new Pagination('#pager', {
* pageSize: 10,
* totalCount: 100,
* displayInfo: true,
*
* // Handle on page index changed
* onPage: function(pageNum, e) {}
*
* // Implements a async paging api method.
* // @type {Promise} promise duck like
* async: function(pageOpts) {
* var deferred = $.Deferred();
* setTimeout(function() {
* deferred.resolve({ totalCount: random(100, 500) });
* }, 100);
* return deferred.promise();
* }
* });
*
* // APIs
*
* pagination.selectPage(2);
* pagination.setPageSize(20);
* pagination.setTotalCount(100);
* pagination.disable();
* pagination.enable();
* pagination.destroy();
* pagination.get('totalCount') // totalCount, pageCount, currentPage
*
* ```
*
* GistID: 4b15fe7c6bcea3f8b30f865cfd928939
* GistURL: https://gist.github.com/4b15fe7c6bcea3f8b30f865cfd928939
*/
(function( root, factory ) {
if ( typeof define === 'function' && define.amd ) {
define( ['jquery'], factory );
} else {
// Browser globals (root is window)
root.returnExports = factory(jQuery);
}
}(this, function( $ ) {
'use strict';
var PAGINATION_DATA_KEY = 'pagination';
var Pagination = function(element, options) {
var $el = $(element);
if ($el.data(PAGINATION_DATA_KEY)) {
throw 'Please destroy the old pagination instance first.';
}
this.pager = $el;
this.init(options);
};
Pagination.prototype = {
constructor: Pagination,
init: function(options) {
var o = $.extend({
totalCount: 1,
pageSize: 1,
pageCount: 0,
displayedPages: 3,
edges: 2,
currentPage: 1, // start with 1
hrefTextPrefix: '#p-',
hrefTextSuffix: '',
prevText: '上一页',
nextText: '下一页',
ellipseText: '…',
className: '',
labelMap: [],
displayInfo: false, // Show the extra paging info with quick pagination, defaults to false.
selectOnClick: true,
async: false, // (Optional) a function that returns a deferred's promise object for async paging.
onPage: function(pageNum, e) {
// Callback triggered when a page is clicked
// Page number is given as an optional parameter
},
onInit: function() {
// Callback triggered immediately after initialization
}
}, options || {});
var self = this, pager = self.pager;
// batch fix optional callbacks scope.
$.each(o, function(i, n) {
var f = o[n];
if (typeof f === 'function') {
o[n] = function() { return f.apply(self, arguments); };
}
});
o.pageCount = o.pageCount ? o.pageCount : Math.ceil(o.totalCount / o.pageSize) ? Math.ceil(o.totalCount / o.pageSize) : 1;
o.currentPage = o.currentPage - 1;
o.halfDisplayed = o.displayedPages / 2;
pager.addClass([o.className, 'ui-pager'].join(' ')).data(PAGINATION_DATA_KEY, o);
self._draw();
self._initEvents();
o.onInit();
},
_initEvents: function() {
var self = this, pager = self.pager, o = self.data();
var readPageFromInput = function() {
var input = pager.find('.page-no'), v, pageNum;
if (input.length) {
v = input.val();
pageNum = parseInt(v, 10);
if (!v || isNaN(pageNum)) {
input.val(o.currentPage + 1);
return false;
}
return pageNum;
}
return false;
};
var selectInputPage = function(e) {
if (o.disabled) {
return;
}
var pageNum = readPageFromInput();
if (pageNum > 0 && pageNum <= o.pageCount && pageNum - 1 !== o.currentPage) {
self._selectPage(pageNum - 1, e);
}
};
// event delegates
pager.delegate('.page-link', 'click', function(e) {
e.preventDefault();
var index = +$(this).data('page-index');
if (!isNaN(index)) {
self._selectPage(index, e);
}
});
if (o.displayInfo) {
pager.delegate('.page-no', 'click', function(e) {
this.select();
})
.delegate('.page-no', 'keydown', function(e) {
if (e.keyCode === 13) { // Enter
selectInputPage();
}
})
.delegate('.page-info .ui-button', 'click', function(e) {
e.preventDefault();
selectInputPage();
});
}
// destroy pagination
pager.one('pagination.destroy', function() {
pager
.undelegate('.page-link', 'click')
.undelegate('.page-no', 'click keydown')
.undelegate('.page-info .ui-button', 'click')
.removeData(PAGINATION_DATA_KEY);
o = null;
pager = null;
self.pager = null;
});
},
/**
* Provide priviliged data getter/setter.
*
* @method data
*/
data: function(k, v) {
var d = this.pager.data(PAGINATION_DATA_KEY);
if (k) {
return v === undefined ? d[k] : (d[k] = v);
} else {
return d;
}
},
/**
* Common api for retrieve pagination meta data by a specific key.
* eg. currentPage, pageCount, totalCount etc,.
*
* @param {String} key The meta data key name.
*/
get: function(key) {
var v = !key ? null : this.data(key);
if (key && key === 'currentPage') { // fix `currentPage` start with 0
return v + 1;
}
return v;
},
getCurrentPage: function() {
return this.data().currentPage + 1;
},
selectPage: function(page) {
return this._selectPage(page - 1);
},
prevPage: function() {
var o = this.data();
if (o.currentPage > 0) {
this._selectPage(o.currentPage - 1);
}
return this;
},
nextPage: function() {
var o = this.data();
if (o.currentPage < o.pageCount - 1) {
this._selectPage(o.currentPage + 1);
}
return this;
},
destroy: function() {
this.pager.empty().trigger('pagination.destroy');
},
drawPage: function(page) {
var o = this.data();
o.currentPage = page - 1;
this._draw();
return this;
},
redraw: function() {
this._draw();
},
disable: function() {
var o = this.data();
o.disabled = true;
this._draw();
return this;
},
enable: function() {
var o = this.data();
o.disabled = false;
this._draw();
return this;
},
setTotalCount: function(totalCount, silent) {
var o = this.data();
o.totalCount = totalCount;
o.pageCount = this._getPages(o);
if (!silent) {
this._draw();
}
},
setPageSize: function(pageSize) {
var o = this.data();
o.pageSize = pageSize;
o.pageCount = this._getPages(o);
this._selectPage(0);
return this;
},
_wraper: function(create) {
var pager = this.pager, ul = pager[0].tagName.toUpperCase() === 'UL' ? pager : pager.find('ul');
if (!ul.length && create) {
return $('<ul></ul>').appendTo(pager);
}
return ul;
},
_draw: function() {
var o = this.data(),
interval = this._getInterval(o),
i,
pager = this.pager;
// cleanup first
pager.empty();
var $panel = this._wraper(true);
if (o.disabled) {
$panel.addClass('disabled');
}
// Generate Prev link
if (o.prevText) {
this._appendItem(o.currentPage - 1, {text: o.prevText, classes: 'prev'});
}
// Generate start edges
if (interval.start > 0 && o.edges > 0) {
var end = Math.min(o.edges, interval.start);
for (i = 0; i < end; i++) {
this._appendItem(i);
}
if (o.edges < interval.start && (interval.start - o.edges != 1)) {
$panel.append('<li class="disabled"><span class="ellipse">' + o.ellipseText + '</span></li>');
} else if (interval.start - o.edges == 1) {
this._appendItem(o.edges);
}
}
// Generate interval links
for (i = interval.start; i < interval.end; i++) {
this._appendItem(i);
}
// Generate end edges
if (interval.end < o.pageCount && o.edges > 0) {
if (o.pageCount - o.edges > interval.end && (o.pageCount - o.edges - interval.end != 1)) {
$panel.append('<li class="disabled"><span class="ellipse">' + o.ellipseText + '</span></li>');
} else if (o.pageCount - o.edges - interval.end == 1) {
this._appendItem(interval.end++);
}
var begin = Math.max(o.pageCount - o.edges, interval.end);
for (i = begin; i < o.pageCount; i++) {
this._appendItem(i);
}
}
// Generate Next link
if (o.nextText) {
this._appendItem(o.currentPage + 1, {text: o.nextText, classes: 'next'});
}
// Pager extra info
if (o.displayInfo) {
this._renderPI();
}
},
_getPages: function(o) {
var pageCount = Math.ceil(o.totalCount / o.pageSize);
return pageCount || 1;
},
_getInterval: function(o) {
return {
start: Math.ceil(o.currentPage > o.halfDisplayed ? Math.max(Math.min(o.currentPage - o.halfDisplayed, (o.pageCount - o.displayedPages)), 0) : 0),
end: Math.ceil(o.currentPage > o.halfDisplayed ? Math.min(o.currentPage + o.halfDisplayed, o.pageCount) : Math.min(o.displayedPages, o.pageCount))
};
},
_appendItem: function(pageIndex, opts) {
var self = this,
pager = self.pager,
options,
$link,
o = self.data(),
$linkWrapper = $('<li></li>'),
$ul = self._wraper();
pageIndex = pageIndex < 0 ? 0 : (pageIndex < o.pageCount ? pageIndex : o.pageCount - 1);
options = {
text: pageIndex + 1,
classes: ''
};
if (o.labelMap.length && o.labelMap[pageIndex]) {
options.text = o.labelMap[pageIndex];
}
options = $.extend(options, opts || {});
if (pageIndex === o.currentPage || o.disabled) {
if (o.disabled) {
$linkWrapper.addClass('disabled');
} else {
$linkWrapper.addClass('active');
}
$link = $('<span class="current">' + (options.text) + '</span>');
} else {
$link = $('<a href="' + o.hrefTextPrefix + (pageIndex + 1) + o.hrefTextSuffix + '" data-page-index="' + pageIndex + '" class="page-link">' + (options.text) + '</a>');
}
if (options.classes) {
$link.addClass(options.classes);
}
$linkWrapper.append($link);
$ul.append($linkWrapper);
},
_selectPage: function(pageIndex, e) {
var self = this, o = self.data();
var selectPage = function(pageIndex, e) {
o.currentPage = pageIndex;
if (o.selectOnClick) {
self._draw();
}
o.onPage(pageIndex + 1, e);
};
if (typeof o.async === 'function') {
var deferred = o.async({pageSize: o.pageSize, currentPage: pageIndex + 1});
if (deferred && deferred.then) {
if (o.lock) {
return self;
}
o.lock = 1;
return deferred.then(function(data) {
var totalCount = (data || 0).totalCount;
if (totalCount !== undefined) {
self.setTotalCount(totalCount, true);
}
selectPage(pageIndex, e);
return self;
}).always(function() {
o.lock = 0;
o = undefined;
selectPage = undefined;
});
} else {
throw 'Error: Please make sure that the `async` returns is a Deferred\'s promise';
}
}
selectPage(pageIndex);
return self;
},
/**
* Render page extra info with quick pagination fields.
* @private
*/
_renderPI: function() {
var self = this,
pager = self.pager,
options,
$link,
o = self.data(),
pageNum = o.currentPage + 1,
pageCount = o.pageCount,
sInfo,
$infoWrapper = $('<li class="page-info"></li>'),
$ul = self._wraper();
pageNum = pageNum < 1 ? 1 : Math.min(pageNum, pageCount);
sInfo = '共' + o.pageCount + '页,跳转到第<input type="text" class="page-no" autocomplete="off" value="' + pageNum + '">页<a class="ui-button" href="javascript:;">跳转</a>';
if (o.disabled) {
$infoWrapper.addClass('disabled');
}
$infoWrapper.html(sInfo);
$ul.append($infoWrapper);
}
};
return Pagination;
}));
// vim: set fdm=marker ts=4 sw=4 sts=4 tw=85 et :
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment