mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
239 lines
7.7 KiB
JavaScript
239 lines
7.7 KiB
JavaScript
import $ from "jquery";
|
|
|
|
/** Builds (twitter bootstrap styled) pagination controls.
|
|
* If the totalDataSize is not null, a horizontal list of page buttons is displayed.
|
|
* If totalDataSize is null, two links ('Prev' and 'Next) are displayed.
|
|
* When pages are changed, a 'pagination.page-change' event is fired
|
|
* sending the event and the (0-based) page requested.
|
|
*/
|
|
|
|
function Pagination(element, options) {
|
|
/** the total number of pages */
|
|
this.numPages = null;
|
|
/** the current, active page */
|
|
this.currPage = 0;
|
|
return this.init(element, options);
|
|
}
|
|
|
|
/** data key under which this object will be stored in the element */
|
|
Pagination.prototype.DATA_KEY = "pagination";
|
|
/** default options */
|
|
Pagination.prototype.defaults = {
|
|
/** which page to begin at */
|
|
startingPage: 0,
|
|
/** number of data per page */
|
|
perPage: 20,
|
|
/** the total number of data (null == unknown) */
|
|
totalDataSize: null,
|
|
/** size of current data on current page */
|
|
currDataSize: null,
|
|
};
|
|
|
|
/** init the control, calc numPages if possible, and render
|
|
* @param {jQuery} the element that will contain the pagination control
|
|
* @param {Object} options a map containing overrides to the pagination default options
|
|
*/
|
|
Pagination.prototype.init = function _init($element, options) {
|
|
options = options || {};
|
|
this.$element = $element;
|
|
this.options = $.extend(true, {}, this.defaults, options);
|
|
|
|
this.currPage = this.options.startingPage;
|
|
if (this.options.totalDataSize !== null) {
|
|
this.numPages = Math.ceil(this.options.totalDataSize / this.options.perPage);
|
|
// limit currPage by numPages
|
|
if (this.currPage >= this.numPages) {
|
|
this.currPage = this.numPages - 1;
|
|
}
|
|
}
|
|
//console.debug( 'Pagination.prototype.init:', this.$element, this.currPage );
|
|
//console.debug( JSON.stringify( this.options ) );
|
|
|
|
// bind to data of element
|
|
this.$element.data(Pagination.prototype.DATA_KEY, this);
|
|
|
|
this._render();
|
|
return this;
|
|
};
|
|
|
|
/** helper to create a simple li + a combo */
|
|
function _make$Li(contents) {
|
|
return $(['<li><a href="javascript:void(0);">', contents, "</a></li>"].join(""));
|
|
}
|
|
|
|
/** render previous and next pagination buttons */
|
|
Pagination.prototype._render = function __render() {
|
|
// no data - no pagination
|
|
if (this.options.totalDataSize === 0) {
|
|
return this;
|
|
}
|
|
// only one page
|
|
if (this.numPages === 1) {
|
|
return this;
|
|
}
|
|
|
|
// when the number of pages are known, render each page as a link
|
|
if (this.numPages > 0) {
|
|
this._renderPages();
|
|
this._scrollToActivePage();
|
|
|
|
// when the number of pages is not known, render previous or next
|
|
} else {
|
|
this._renderPrevNext();
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/** render previous and next pagination buttons */
|
|
Pagination.prototype._renderPrevNext = function __renderPrevNext() {
|
|
var pagination = this;
|
|
var $prev = _make$Li("Prev");
|
|
var $next = _make$Li("Next");
|
|
|
|
var $paginationContainer = $("<ul/>").addClass("pagination pagination-prev-next");
|
|
|
|
// disable if it either end
|
|
if (this.currPage === 0) {
|
|
$prev.addClass("disabled");
|
|
} else {
|
|
$prev.click(() => {
|
|
pagination.prevPage();
|
|
});
|
|
}
|
|
if (
|
|
(this.numPages && this.currPage === this.numPages - 1) ||
|
|
(this.options.currDataSize && this.options.currDataSize < this.options.perPage)
|
|
) {
|
|
$next.addClass("disabled");
|
|
} else {
|
|
$next.click(() => {
|
|
pagination.nextPage();
|
|
});
|
|
}
|
|
|
|
this.$element.html($paginationContainer.append([$prev, $next]));
|
|
//console.debug( this.$element, this.$element.html() );
|
|
return this.$element;
|
|
};
|
|
|
|
/** render page links for each possible page (if we can) */
|
|
Pagination.prototype._renderPages = function __renderPages() {
|
|
// it's better to scroll the control and let the user see all pages
|
|
// than to force her/him to change pages in order to find the one they want (as traditional << >> does)
|
|
var pagination = this;
|
|
|
|
var $scrollingContainer = $("<div>").addClass("pagination-scroll-container");
|
|
|
|
var $paginationContainer = $("<ul/>").addClass("pagination pagination-page-list");
|
|
|
|
var page$LiClick = function (ev) {
|
|
pagination.goToPage($(this).data("page"));
|
|
};
|
|
|
|
for (var i = 0; i < this.numPages; i += 1) {
|
|
// add html5 data tag 'page' for later click event handler use
|
|
var $pageLi = _make$Li(i + 1)
|
|
.attr("data-page", i)
|
|
.click(page$LiClick);
|
|
// highlight the current page
|
|
if (i === this.currPage) {
|
|
$pageLi.addClass("active");
|
|
}
|
|
//console.debug( '\t', $pageLi );
|
|
$paginationContainer.append($pageLi);
|
|
}
|
|
return this.$element.html($scrollingContainer.html($paginationContainer));
|
|
};
|
|
|
|
/** scroll scroll-container (if any) to show the active page */
|
|
Pagination.prototype._scrollToActivePage = function __scrollToActivePage() {
|
|
// scroll to show active page in center of scrollable area
|
|
var $container = this.$element.find(".pagination-scroll-container");
|
|
// no scroll container : don't scroll
|
|
if (!$container.length) {
|
|
return this;
|
|
}
|
|
|
|
var $activePage = this.$element.find("li.active");
|
|
var midpoint = $container.width() / 2;
|
|
//console.debug( $container, $activePage, midpoint );
|
|
$container.scrollLeft($container.scrollLeft() + $activePage.position().left - midpoint);
|
|
return this;
|
|
};
|
|
|
|
/** go to a certain page */
|
|
Pagination.prototype.goToPage = function goToPage(page) {
|
|
if (page <= 0) {
|
|
page = 0;
|
|
}
|
|
if (this.numPages && page >= this.numPages) {
|
|
page = this.numPages - 1;
|
|
}
|
|
if (page === this.currPage) {
|
|
return this;
|
|
}
|
|
|
|
//console.debug( '\t going to page ' + page )
|
|
this.currPage = page;
|
|
this.$element.trigger("pagination.page-change", this.currPage);
|
|
//console.info( 'pagination:page-change', this.currPage );
|
|
this._render();
|
|
return this;
|
|
};
|
|
|
|
/** go to the previous page */
|
|
Pagination.prototype.prevPage = function prevPage() {
|
|
return this.goToPage(this.currPage - 1);
|
|
};
|
|
|
|
/** go to the next page */
|
|
Pagination.prototype.nextPage = function nextPage() {
|
|
return this.goToPage(this.currPage + 1);
|
|
};
|
|
|
|
/** return the current page */
|
|
Pagination.prototype.page = function page() {
|
|
return this.currPage;
|
|
};
|
|
|
|
// alternate constructor invocation
|
|
Pagination.create = function _create($element, options) {
|
|
return new Pagination($element, options);
|
|
};
|
|
|
|
// as jq plugin
|
|
$.fn.extend({
|
|
pagination: function $pagination(options) {
|
|
var nonOptionsArgs = $.makeArray(arguments).slice(1);
|
|
|
|
// if passed an object - use that as an options map to create pagination for each selected
|
|
if ($.type(options) === "object") {
|
|
return this.map(function () {
|
|
Pagination.create($(this), options);
|
|
return this;
|
|
});
|
|
}
|
|
|
|
// (other invocations only work on the first element in selected)
|
|
var $firstElement = $(this[0]);
|
|
|
|
var previousControl = $firstElement.data(Pagination.prototype.DATA_KEY);
|
|
// if a pagination control was found for this element, either...
|
|
if (previousControl) {
|
|
// invoke a function on the pagination object if passed a string (the function name)
|
|
if ($.type(options) === "string") {
|
|
var fn = previousControl[options];
|
|
if ($.type(fn) === "function") {
|
|
return fn.apply(previousControl, nonOptionsArgs);
|
|
}
|
|
|
|
// if passed nothing, return the previously set control
|
|
} else {
|
|
return previousControl;
|
|
}
|
|
}
|
|
// if there is no control already set, return undefined
|
|
return undefined;
|
|
},
|
|
});
|