//################################################################################################
/** @file Pagination-based mixin for ish.js
* @mixin ish.type.arr.pagination
* @author Nick Campbell
* @license MIT
* @copyright 2014-2023, Nick Campbell
* @ignore
*/ //############################################################################################
/*global module, define */ //# Enable Node globals for JSHint
/*jshint maxcomplexity:9 */ //# Enable max complexity warnings for JSHint
(function () {
'use strict'; //<MIXIN>
function init(core) {
//################################################################################################
/** Collection of Pagination-based functionality.
* @namespace ish.type.arr.pagination
* @ignore
*/ //############################################################################################
core.oop.partial(core.type.arr, function (/*oProtected*/) {
//#
function newPage(sLabel, iPage) {
iPage = core.type.int.mk(iPage, 0);
return {
page: iPage,
label: sLabel,
isCurrent: false,
clickable: (iPage !== 0)
};
} //# newPage
//#
function setCurrentPage(iPage, fnReturnVal) {
var i, iLowerPage, iUpperPage,
bTrailingEllipse = false,
iMaxPagesToShow = fnReturnVal.maxPagesToShow,
iHalfMaxPages = Math.ceil(iMaxPagesToShow / 2),
iPageCount = Math.ceil(fnReturnVal.data.length / fnReturnVal.pageSize),
a_iPages = []
;
//# Ensure the passed iPage .is.int
iPage = core.type.int.mk(iPage, 1);
//#
switch (iPage) {
case -1: {
iPage = fnReturnVal.current - 1;
break;
}
case -2: {
iPage = fnReturnVal.current + 1;
break;
}
}
fnReturnVal.current = (iPage < 1 ? 1 : iPage);
fnReturnVal.current = (fnReturnVal.current > iPageCount ? iPageCount : fnReturnVal.current);
//# If we are supposed to .showPrevNext
if (fnReturnVal.showPrevNext && fnReturnVal.current > 1) {
//# .push the First page linky
a_iPages.push(newPage("<", -1));
}
//# Setup then fix the iLowerPage and iUpperPage
iLowerPage = (fnReturnVal.current - iHalfMaxPages);
iUpperPage = (fnReturnVal.current + iHalfMaxPages);
if (iLowerPage < 1) {
iUpperPage -= iLowerPage; //# 1 - (-1) = 2
iLowerPage = 1;
}
//#
if (iUpperPage > iPageCount) {
iLowerPage += (iPageCount - iUpperPage);
iLowerPage = (iLowerPage < 1 ? 1 : iLowerPage);
iUpperPage = iPageCount;
}
//#
if ((iUpperPage - iLowerPage + 1) > iMaxPagesToShow) {
iUpperPage--;
}
//# If we need a leading ellipse, adjust the counters and .push it in now
if (iLowerPage > 1) {
a_iPages.push(newPage("...", iLowerPage));
iLowerPage++;
}
//# If we need a bTrailingEllipse, adjust the counters and flip bTrailingEllipse
if (iUpperPage < iPageCount) {
iUpperPage--;
bTrailingEllipse = true;
}
//# Traverse the iMaxPagesToShow, .push'ing each as we go
for (i = iLowerPage; i <= iUpperPage; i++) {
a_iPages.push(newPage(i, i));
//# If we are on the .isCurrent iPage, flip it
if (i === iPage) {
a_iPages[a_iPages.length - 1].isCurrent = true;
}
}
//# If we need a bTrailingEllipse, .push it in now
if (bTrailingEllipse) {
a_iPages.push(newPage("...", iUpperPage + 1));
}
//# If we are supposed to .showPrevNext, .push the Last page linky
if (fnReturnVal.showPrevNext && fnReturnVal.current < iPageCount) {
a_iPages.push(newPage(">", -2));
}
fnReturnVal.pages = a_iPages;
return iPage;
} //# setCurrentPage
return {
//#########
/** Creates an interface that represents the pagination information.
* @function ish.type.arr.pagination
* @param {variant[]} a_vData Value representing the array of data.
* @param {object} [oOptions] Value representing the following options:
* @param {integer} [oOptions.current=1] Value representing the current page.
* @param {integer} [oOptions.pageSize=10] Value representing the page size.
* @param {integer} [oOptions.maxPagesToShow=10] Value representing the max pages to show.
* @param {boolean} [oOptions.showPrevNext=false] Value representing if the previous/next controls will be shown.
* @returns {object} =interface Value representing the following properties:
* @returns {object[]} =interface.pages Value representing the pagination's page descriptions; <code>{ clickable: boolean, isCurrent: boolean, label: string, page: integer }</code>.
* @returns {variant[]} =interface.data Value representing the pagination's data.
* @returns {integer} =interface.current Value representing the pagination's current page.
* @returns {integer} =interface.pageSize Value representing the pagination's page size.
* @returns {integer} =interface.maxPagesToShow Value representing the pagination's max number of pages to show.
* @returns {integer} =interface.showPrevNext Value representing the pagination's max number of pages to show.
* @returns {function} =interface.slice Determines the current page's records; <code>slice()</code>.
* @returns {function} =interface.set Sets the current page; <code>set(iPage)</code>:
* <table class="params">
* <tr><td class="name"><code>iPage</code><td><td class="type param-type">integer<td><td class="description last">Value representing the page to set as current.</td></tr>
* </table>
*/ //#####
pagination: function (a_vData, oOptions) {
var fnReturnVal = function (a_vData, oOptions) {
//#
a_vData = core.type.arr.mk(a_vData);
//# Ensure the passed oOptions .is .obj then setup the local vars and their related .properties
oOptions = core.type.obj.mk(oOptions);
core.extend(fnReturnVal, {
pages: null,
data: a_vData,
current: core.type.int.mk(oOptions.current, 1),
pageSize: core.type.int.mk(oOptions.pageSize, 10),
maxPagesToShow: core.type.int.mk(oOptions.maxPagesToShow, 10),
showPrevNext: core.type.bool.mk(oOptions.showPrevNext, false),
//showMiddlePages: core.type.bool.mk(oOptions.showMiddlePages, false),
//template: newPage,
//#########
/** Determines the current page's records.
* @function ish.type.arr.pagination:*:slice
* @returns {variant[]} Value representing the current page's records.
* @ignore
*/ //#####
slice: function () {
var iIndex = ((fnReturnVal.current - 1) * fnReturnVal.pageSize);
return a_vData.slice(iIndex, iIndex + fnReturnVal.pageSize);
}, //# ish.type.arr.pagination.slice
//#########
/** Sets the current page.
* @function ish.type.arr.pagination:*:set
* @param {integer} iPage Value representing the page to set as current.
* @returns {integer} Value representing the current page.
* @ignore
*/ //#####
set: function (iPage) {
return setCurrentPage(iPage, fnReturnVal);
} //# ish.type.arr.pagination.set
});
//# .setCurrentPage to .current
setCurrentPage(fnReturnVal.current, fnReturnVal);
};
//# Init fnReturnVal new reference with the passed a_vData/oOptions, then return it to the caller
fnReturnVal(a_vData, oOptions);
return fnReturnVal;
}
};
});
//# .fire the plugin's loaded event
core.io.event.fire("ish.type.arr.pagination");
//# Return core to allow for chaining
return core;
} //# init
//# If we are running server-side
//# NOTE: Compliant with UMD, see: https://github.com/umdjs/umd/blob/master/templates/returnExports.js
//# NOTE: Does not work with strict CommonJS, but only CommonJS-like environments that support module.exports, like Node.
if (typeof module === 'object' && module.exports) { //if (typeof module !== 'undefined' && this.module !== module && module.exports) {
module.exports = init;
}
//# Else if we are running in an .amd environment, register as an anonymous module
else if (typeof define === 'function' && define.amd) {
define([], init);
}
//# Else we are running in the browser, so we need to setup the _document-based features
else {
return init(window.head.ish || document.querySelector("SCRIPT[ish]").ish);
}
//</MIXIN>
}());