PageSourceSearch

https://academy-of-visual-arts.com/wordpress/wp-content/themes/ava…/build/js/mixitup-pagination.js?ver=7.1.3

js academy-of-visual-arts.com collected 2026-10-09 22:39:46 UTC 72,911 bytes, 1,974 lines download raw bytes

1/**!
2 * MixItUp Pagination v3.3.0
3 * Client-side pagination for filtered and sorted content
4 * Build 875b7d31-63d1-4040-ac6f-b1c814027891
5 *
6 * Requires mixitup.js >= v^3.1.8
7 *
8 * @copyright Copyright 2014-2017 KunkaLabs Limited.
9 * @author    KunkaLabs Limited.
10 * @link      https://www.kunkalabs.com/mixitup-pagination/
11 *
12 * @license   Commercial use requires a commercial license.
13 *            https://www.kunkalabs.com/mixitup-pagination/licenses/
14 *
15 *            Non-commercial use permitted under same terms as  license.
16 *            http://creativecommons.org/licenses/by-nc/3.0/
vendor: 71,787 bytes, lines 16-1961
16
17 */
18
19(function(window) {
20    'use strict';
21
22    var mixitupPagination = function(mixitup) {
23        var h = mixitup.h;
24
25        if (
26            !mixitup.CORE_VERSION ||
27            !h.compareVersions(mixitupPagination.REQUIRE_CORE_VERSION, mixitup.CORE_VERSION)
28        ) {
29            throw new Error(
30                '[MixItUp Pagination] MixItUp Pagination ' +
31                mixitupPagination.EXTENSION_VERSION +
32                ' requires at least MixItUp ' +
33                mixitupPagination.REQUIRE_CORE_VERSION
34            );
35        }
36
37        /**
38         * A group of optional callback functions to be invoked at various
39         * points within the lifecycle of a mixer operation.
40         *
41         * @constructor
42         * @memberof    mixitup.Config
43         * @name        callbacks
44         * @namespace
45         * @public
46         * @since       2.0.0
47         */
48
49        mixitup.ConfigCallbacks.registerAction('afterConstruct', 'pagination', function() {
50            /**
51             * A callback function invoked whenever a pagination operation starts.
52             *
53             * This function is equivalent to `onMixStart`, and is invoked immediately
54             * after it with the same arguments. Unlike `onMixStart` however, it will
55             * not be invoked for filter or sort operations.
56             *
57             *
58             * @name        onPaginateStart
59             * @memberof    mixitup.Config.callbacks
60             * @instance
61             * @type        {function}
62             * @default     null
63             */
64
65            this.onPaginateStart = null;
66
67            /**
68             * A callback function invoked whenever a pagination operation ends.
69             *
70             * This function is equivalent to `onMixEnd`, and is invoked immediately
71             * after it with the same arguments. Unlike `onMixEnd` however, it will
72             * not be invoked for filter or sort operations.
73             *
74             * @name        onPaginateStart
75             * @memberof    mixitup.Config.callbacks
76             * @instance
77             * @type        {function}
78             * @default     null
79             */
80
81            this.onPaginateEnd = null;
82        });
83
84        /**
85         * A group of properties defining the output and structure of class names programmatically
86         * added to controls and containers to reflect the state of the mixer.
87         *
88         * @constructor
89         * @memberof    mixitup.Config
90         * @name        classNames
91         * @namespace
92         * @public
93         * @since       3.0.0
94         */
95
96        mixitup.ConfigClassNames.registerAction('afterConstruct', 'pagination', function() {
97
98            /**
99             * The "element" portion of the class name added to pager controls.
100             *
101             * @example <caption>Example: changing the `config.classNames.elementPager` value</caption>
102             *
103             * // Change from the default value of 'control' to 'pager'
104             *
105             * var mixer = mixitup(containerEl, {
106             *     classNames: {
107             *         elementPager: 'pager'
108             *     }
109             * });
110             *
111             * // Base pager output: "mixitup-pager"
112             *
113             * @name        elementPager
114             * @memberof    mixitup.Config.classNames
115             * @instance
116             * @type        {string}
117             * @default     'control'
118             */
119
120            this.elementPager = 'control';
121
122            /**
123             * The "element" portion of the class name added to the page list element, when it is
124             * in its disabled state.
125             *
126             * The page list element is the containing element in which pagers are rendered.
127             *
128             * @example <caption>Example: changing the `config.classNames.elementPageList` value</caption>
129             *
130             * // Change from the default value of 'page-list' to 'pagination-links'
131             *
132             * var mixer = mixitup(containerEl, {
133             *     classNames: {
134             *         elementPageList: 'pagination-links'
135             *     }
136             * });
137             *
138             * // Disabled page-list output: "mixitup-pagination-links-disabled"
139             *
140             * @name        elementPageList
141             * @memberof    mixitup.Config.classNames
142             * @instance
143             * @type        {string}
144             * @default     'page-list'
145             */
146
147            this.elementPageList = 'page-list';
148
149            /**
150             * The "element" portion of the class name added to the page stats element, when it is
151             * in its disabled state.
152             *
153             * The page stats element is the containing element in which information about the
154             * current page and total number of pages is rendered.
155             *
156             * @example <caption>Example: changing the `config.classNames.elementPageStats` value</caption>
157             *
158             * // Change from the default value of 'page-stats' to 'pagination-info'
159             *
160             * var mixer = mixitup(containerEl, {
161             *     classNames: {
162             *         elementPageList: 'pagination-info'
163             *     }
164             * });
165             *
166             * // Disabled page-list output: "mixitup-pagination-info-disabled"
167             *
168             * @name        elementPageStats
169             * @memberof    mixitup.Config.classNames
170             * @instance
171             * @type        {string}
172             * @default     'page-stats'
173             */
174
175            this.elementPageStats = 'page-stats';
176
177            /**
178             * The "modifier" portion of the class name added to the first pager in the list of pager controls.
179             *
180             * @name        modifierFirst
181             * @memberof    mixitup.Config.classNames
182             * @instance
183             * @type        {string}
184             * @default     'first'
185             */
186
187            this.modifierFirst = 'first';
188
189            /**
190             * The "modifier" portion of the class name added to the last pager in the list of pager controls.
191             *
192             * @name        modifierLast
193             * @memberof    mixitup.Config.classNames
194             * @instance
195             * @type        {string}
196             * @default     'last'
197             */
198
199            this.modifierLast = 'last';
200
201            /**
202             * The "modifier" portion of the class name added to the previous pager in the list of pager controls.
203             *
204             * @name        modifierLast
205             * @memberof    mixitup.Config.classNames
206             * @instance
207             * @type        {string}
208             * @default     'prev'
209             */
210
211            this.modifierPrev = 'prev';
212
213            /**
214             * The "modifier" portion of the class name added to the next pager in the list of pager controls.
215             *
216             * @name        modifierNext
217             * @memberof    mixitup.Config.classNames
218             * @instance
219             * @type        {string}
220             * @default     'next'
221             */
222
223            this.modifierNext = 'next';
224
225            /**
226             * The "modifier" portion of the class name added to truncation markers in the list of pager controls.
227             *
228             * @name        modifierTruncationMarker
229             * @memberof    mixitup.Config.classNames
230             * @instance
231             * @type        {string}
232             * @default     'truncation-marker'
233             */
234
235            this.modifierTruncationMarker = 'truncation-marker';
236        });
237
238        /**
239         * A group of properties defining the initial state of the mixer on load (instantiation).
240         *
241         * @constructor
242         * @memberof    mixitup.Config
243         * @name        load
244         * @namespace
245         * @public
246         * @since       2.0.0
247         */
248
249        mixitup.ConfigLoad.registerAction('afterConstruct', 'pagination', function() {
250            /**
251             * An integer defining the starting page on load, if a page limit is active.
252             *
253             * @example <caption>Example: Defining a start page other than 1 to be applied on load</caption>
254             *
255             * // The mixer will show page 3 on load, with 8 items per page.
256             *
257             * var mixer = mixitup(containerEl, {
258             *     pagination: {
259             *         limit: 8
260             *     },
261             *     load: {
262             *         page: 3
263             *     }
264             * });
265             *
266             * @name        page
267             * @memberof    mixitup.Config.load
268             * @instance
269             * @type        {number}
270             * @default     1
271             */
272
273            this.page = 1;
274        });
275
276        /**
277         * A group of properties defining the mixer's pagination behavior.
278         *
279         * @constructor
280         * @memberof    mixitup.Config
281         * @name        pagination
282         * @namespace
283         * @public
284         * @since       2.0.0
285         */
286
287        mixitup.ConfigPagination = function() {
288
289            /**
290             * A boolean dictating whether or not MixItUp should render a list of pager controls.
291             *
292             * If you wish to control pagination functionality via the API, or your own UI, this can be set to `false`.
293             *
294             * In order for this functionality to work, you must provide MixItUp with a `pageList`
295             * element matching the selector defined in `selectors.pageList`. Pager controls will be
296             * rendered inside this element as per the templates defined for the `templates.pager`
297             * and related configuration options, or if set, a custom render
298             * function supplied to the `render.pager` configuration option.
299             *
300             * @example <caption>Example: Disabling the rendering of the built-in "page list" UI</caption>
301             *
302             * var mixer = mixitup(containerEl, {
303             *     pagination: {
304             *         limit: 8,
305             *         generatePageList: false
306             *     }
307             * });
308             *
309             * @name        generatePageList
310             * @memberof    mixitup.Config.pagination
311             * @instance
312             * @type        {boolean}
313             * @default     true
314             */
315
316            this.generatePageList = true;
317
318            /**
319             * A boolean dictating whether or not MixItUp should render a stats about the
320             * current page (e.g. "1 to 4 of 16").
321             *
322             * In order for this functionality to work, you must provide MixItUp with a `pageStats`
323             * element matching the selector defined in `selectors.pageStats`. Page stats content will
324             * be rendered inside this element as per the templates defined for the `templates.pageStats`
325             * and `templates.pageStatsSingle` configuration options, or if set, a custom render
326             * function supplied to the `render.pageStats` configuration option.
327             *
328             * @example <caption>Example: Disabling the rendering of the built-in "page stats" UI</caption>
329             *
330             * var mixer = mixitup(containerEl, {
331             *     pagination: {
332             *         limit: 8,
333             *         generatePageStats: false
334             *     }
335             * });
336             *
337             * @name        generatePageStats
338             * @memberof    mixitup.Config.pagination
339             * @instance
340             * @type        {boolean}
341             * @default     true
342             */
343
344            this.generatePageStats = true;
345
346            /**
347             * A boolean dictating whether or not to maintain the active page when switching
348             * from filter to filter.
349             *
350             * By default, MixItUp will attempt to maintain the active page or its highest
351             * equivalent in the new collection of matching targets (e.g. page 3 would become
352             * page 2 if there are not enough targets in the new collection), but by setting
353             * this option to `false`, changing the active filter will always cause the mixer
354             * to revert to page one of the new collection.
355             *
356             * @example <caption>Example: Ensuring that the mixer reverts to page one when filtered</caption>
357             *
358             * var mixer = mixitup(containerEl, {
359             *     pagination: {
360             *         limit: 8,
361             *         maintainActivePage: false
362             *     }
363             * });
364             *
365             * @name        maintainActivePage
366             * @memberof    mixitup.Config.pagination
367             * @instance
368             * @type        {boolean}
369             * @default     true
370             */
371
372            this.maintainActivePage = true;
373
374            /**
375             * A boolean dictating whether or not to allow "looping" of the built-in previous
376             * and next pagination controls.
377             *
378             * By default, when on the first page, the "previous" button will be disabled,
379             * and when on the last page, the "next" button will be disabled. By setting
380             * this option to `true`, the user may loop from the first to last page and
381             * vice-versa.
382             *
383             * @example <caption>Example: Allowing prev/next controls to "loop" through pages</caption>
384             *
385             * var mixer = mixitup(containerEl, {
386             *     pagination: {
387             *         limit: 8,
388             *         loop: true
389             *     }
390             * });
391             *
392             * @name        loop
393             * @memberof    mixitup.Config.pagination
394             * @instance
395             * @type        {boolean}
396             * @default     false
397             */
398
399            this.loop = false;
400
401            /**
402             * A boolean dictating whether or not to prevent rendering of the built-in
403             * "page list" UI if the matching collection of targets has only enough content
404             * for one page.
405             *
406             * @example <caption>Example: Hiding the page list UI if only one page</caption>
407             *
408             * var mixer = mixitup(containerEl, {
409             *     pagination: {
410             *         limit: 8,
411             *         hidePageListIfSinglePage: true
412             *     }
413             * });
414             *
415             * @name        hidePageListIfSinglePage
416             * @memberof    mixitup.Config.pagination
417             * @instance
418             * @type        {boolean}
419             * @default     false
420             */
421
422            this.hidePageListIfSinglePage = false;
423
424            /**
425             * A boolean dictating whether or not to prevent rendering of the built-in
426             * "page stats" UI if the matching collection of targets has only enough content
427             * for one page.
428             *
429             * @example <caption>Example: Hiding the page stats UI if only one page</caption>
430             *
431             * var mixer = mixitup(containerEl, {
432             *     pagination: {
433             *         limit: 8,
434             *         hidePageStatsIfSinglePage: true
435             *     }
436             * });
437             *
438             * @name        hidePageStatsIfSinglePage
439             * @memberof    mixitup.Config.pagination
440             * @instance
441             * @type        {boolean}
442             * @default     false
443             */
444
445            this.hidePageStatsIfSinglePage = false;
446
447            /**
448             * A number defining the maximum number of items per page.
449             *
450             * By default, this is set to `-1` and pagination is effectively
451             * disabled. By setting this to any number greater than 0, pagination
452             * will be applied to the mixers targets, effectively activating the
453             * extension.
454             *
455             * @example <caption>Example: Activating the pagination extension by defining a valid limit</caption>
456             *
457             * var mixer = mixitup(containerEl, {
458             *     pagination: {
459             *         limit: 8
460             *     }
461             * });
462             *
463             * @name        limit
464             * @memberof    mixitup.Config.pagination
465             * @instance
466             * @type        {number}
467             * @default     -1
468             */
469
470            this.limit = -1;
471
472            /**
473             * A number dictating the maximum number of individual pager controls to render before
474             * truncating the list (e.g. adding an ellipses between non-consecutive pagers).
475             *
476             * The minimum value permitted for this option is 5, which ensures
477             * there will always be at least a first, last, and two padding pagers, in addition
478             * to the pager representing the currently active page.
479             *
480             * @name        maxPagers
481             * @memberof    mixitup.Config.pagination
482             * @instance
483             * @type        {number}
484             * @default     5
485             */
486
487            this.maxPagers = 5;
488
489            h.seal(this);
490        };
491
492        /**
493         * A group of optional render functions for creating and updating elements.
494         *
495         * @constructor
496         * @memberof    mixitup.Config
497         * @name        render
498         * @namespace
499         * @public
500         * @since       3.0.0
501         */
502
503        mixitup.ConfigRender.registerAction('afterConstruct', 'pagination', function() {
504            /**
505             * A function returning an HTML string representing a single pager control element.
506             *
507             * By default, MixItUp will render pager controls using its own internal renderer
508             * and templates (see `templates.pager`), but you may override this functionality by
509             * providing your own render function here instead. All pager elements must have a
510             * data-page element indicating the action of the control.
511             *
512             * The function receives an object containing all neccessary information
513             * about the pager as its first parameter.
514             *
515             * @name        pager
516             * @memberof    mixitup.Config.render
517             * @instance
518             * @type        {function}
519             * @default     'null'
520             */
521
522            this.pager = null;
523
524            /**
525             * A function returning an HTML string forming the contents of the "page stats" element.
526             *
527             * By default, MixItUp will render page stats using its own internal renderer
528             * and templates (see `templates.pageStats`), but you may override this functionality by
529             * providing your own render function here instead.
530             *
531             * The function receives an object containing all neccessary information
532             * about the current page and total pages as its first parameter.
533             *
534             * @name        pageStats
535             * @memberof    mixitup.Config.render
536             * @instance
537             * @type        {function}
538             * @default     'null'
539             */
540
541            this.pageStats = null;
542        });
543
544        /**
545         * A group of properties defining the selectors used to query elements within a mixitup container.
546         *
547         * @constructor
548         * @memberof    mixitup.Config
549         * @name        selectors
550         * @namespace
551         * @public
552         * @since       2.0.0
553         */
554
555        mixitup.ConfigSelectors.registerAction('afterConstruct', 'pagination', function() {
556            /**
557             * A selector string used to query the page list element.
558             *
559             * Depending on the value of `controls.scope`, MixItUp will either query the
560             * entire document for the page list element, or just the container.
561             *
562             * @name        pageList
563             * @memberof    mixitup.Config.selectors
564             * @instance
565             * @type        {string}
566             * @default     '.mixitup-page-list'
567             */
568
569            this.pageList  = '.mixitup-page-list';
570
571            /**
572             * A selector string used to query the page stats element.
573             *
574             * Depending on the value of `controls.scope`, MixItUp will either query the
575             * entire document for the page stats element, or just the container.
576             *
577             * @name        pageStats
578             * @memberof    mixitup.Config.selectors
579             * @instance
580             * @type        {string}
581             * @default     '.mixitup-page-stats'
582             */
583
584            this.pageStats = '.mixitup-page-stats';
585        });
586
587        /**
588         * A group of template strings used to render pager controls and page stats elements.
589         *
590         * @constructor
591         * @memberof    mixitup.Config
592         * @name        templates
593         * @namespace
594         * @public
595         * @since       3.0.0
596         */
597
598        mixitup.ConfigTemplates.registerAction('afterConstruct', 'pagination', function() {
599            /**
600             * @name        pager
601             * @memberof    mixitup.Config.templates
602             * @instance
603             * @type        {string}
604             * @default     '<button type="button" class="${classNames}" data-page="${pageNumber}">${pageNumber}</button>'
605             */
606
607            this.pager = '<button type="button" class="${classNames}" data-page="${pageNumber}">${pageNumber}</button>';
608
609            /**
610             * @name        pagerPrev
611             * @memberof    mixitup.Config.templates
612             * @instance
613             * @type        {string}
614             * @default     '<button type="button" class="${classNames}" data-page="prev">&laquo;</button>'
615             */
616
617            this.pagerPrev = '<button type="button" class="${classNames}" data-page="prev">&laquo;</button>';
618
619            /**
620             * @name        pagerNext
621             * @memberof    mixitup.Config.templates
622             * @instance
623             * @type        {string}
624             * @default     '<button type="button" class="${classNames}" data-page="next">&raquo;</button>'
625             */
626
627            this.pagerNext = '<button type="button" class="${classNames}" data-page="next">&raquo;</button>';
628
629            /**
630             * @name        pagerTruncationMarker
631             * @memberof    mixitup.Config.templates
632             * @instance
633             * @type        {string}
634             * @default     '<span class="${classNames}">&hellip;</span>'
635             */
636
637            this.pagerTruncationMarker = '<span class="${classNames}">&hellip;</span>';
638
639            /**
640             * @name        pageStats
641             * @memberof    mixitup.Config.templates
642             * @instance
643             * @type        {string}
644             * @default     '${startPageAt} to ${endPageAt} of ${totalTargets}'
645             */
646
647            this.pageStats = '${startPageAt} to ${endPageAt} of ${totalTargets}';
648
649            /**
650             * @name        pageStatsSingle
651             * @memberof    mixitup.Config.templates
652             * @instance
653             * @type        {string}
654             * @default     '${startPageAt} of ${totalTargets}'
655             */
656
657            this.pageStatsSingle = '${startPageAt} of ${totalTargets}';
658
659            /**
660             * @name        pageStatsFail
661             * @memberof    mixitup.Config.templates
662             * @instance
663             * @type        {string}
664             * @default     'None found'
665             */
666
667            this.pageStatsFail = 'None found';
668        });
669
670        /**
671         * The MixItUp configuration object is extended with the following properties
672         * relating to the Pagination extension.
673         *
674         * For the full list of configuration options, please refer to the MixItUp
675         * core documentation.
676         *
677         * @constructor
678         * @memberof    mixitup
679         * @name        Config
680         * @namespace
681         * @public
682         * @since       2.0.0
683         */
684
685        mixitup.Config.registerAction('beforeConstruct', 'pagination', function() {
686            this.pagination = new mixitup.ConfigPagination();
687        });
688
689        mixitup.ModelPager = function() {
690            this.pageNumber         = -1;
691            this.classNames         = '';
692            this.classList          = [];
693            this.isDisabled         = false;
694            this.isPrev             = false;
695            this.isNext             = false;
696            this.isPageLink         = false;
697            this.isTruncationMarker = false;
698
699            h.seal(this);
700        };
701
702        mixitup.ModelPageStats = function() {
703            this.startPageAt    = -1;
704            this.endPageAt      = -1;
705            this.totalTargets   = -1;
706
707            h.seal(this);
708        };
709
710        mixitup.UiClassNames.registerAction('afterConstruct', 'pagination', function() {
711            this.first              = '';
712            this.last               = '';
713            this.prev               = '';
714            this.next               = '';
715            this.first              = '';
716            this.last               = '';
717            this.truncated          = '';
718            this.truncationMarker   = '';
719        });
720
721        mixitup.controlDefinitions.push(new mixitup.ControlDefinition('pager', '[data-page]', true, 'pageListEls'));
722
723        /**
724         * @param   {mixitup.MultimixCommand[]} commands
725         * @param   {ClickEvent}                e
726         * @return  {object|null}
727         */
728
729        mixitup.Control.registerFilter('commandsHandleClick', 'pagination', function(commands, e) {
730            var self            = this,
731                command         = {},
732                page            = '',
733                pageNumber      = -1,
734                mixer           = null,
735                button          = null,
736                i               = -1;
737
738            if (!self.selector || self.selector !== '[data-page]') {
739                // Static control or non-pager live control
740
741                return commands;
742            }
743
744            button = h.closestParent(e.target, self.selector, true, self.bound[0].dom.document);
745
746            for (i = 0; mixer = self.bound[i]; i++) {
747                command = commands[i];
748
749                if (!mixer.config.pagination || mixer.config.pagination.limit < 0 || mixer.config.pagination.limit === Infinity) {
750                    // Pagination is disabled for this instance. Do not handle.
751
752                    commands[i] = null;
753
754                    continue;
755                }
756
757                if (!button || h.hasClass(button, mixer.classNamesPager.active) || h.hasClass(button, mixer.classNamesPager.disabled)) {
758                    // No button was clicked or button is already active. Do not handle.
759
760                    commands[i] = null;
761
762                    continue;
763                }
764
765                page = button.getAttribute('data-page');
766
767                if (page === 'prev') {
768                    command.paginate = 'prev';
769                } else if (page === 'next') {
770                    command.paginate = 'next';
771                } else if (pageNumber) {
772                    command.paginate = parseInt(page);
773                }
774
775                if (mixer.lastClicked) {
776                    mixer.lastClicked = button;
777                }
778            }
779
780            return commands;
781        });
782
783        mixitup.CommandMultimix.registerAction('afterConstruct', 'pagination', function() {
784            this.paginate = null;
785        });
786
787        /**
788         * @constructor
789         * @memberof    mixitup
790         * @private
791         * @since       3.0.0
792         */
793
794        mixitup.CommandPaginate = function() {
795            this.page   = -1;
796            this.limit  = -1;
797            this.action = ''; // enum: ['prev', 'next']
798            this.anchor = null;
799
800            h.seal(this);
801        };
802
803        mixitup.Events.registerAction('afterConstruct', 'pagination', function() {
804            /**
805             * A custom event triggered whenever a pagination operation starts.
806             *
807             * @name        paginateStart
808             * @memberof    mixitup.Events
809             * @static
810             * @type        {CustomEvent}
811             */
812
813            this.paginateStart = null;
814
815            /**
816             * A custom event triggered whenever a pagination operation ends.
817             *
818             * @name        paginateEnd
819             * @memberof    mixitup.Events
820             * @static
821             * @type        {CustomEvent}
822             */
823
824            this.paginateEnd = null;
825        });
826
827        mixitup.events = new mixitup.Events();
828
829        mixitup.Operation.registerAction('afterConstruct', 'pagination', function() {
830            this.startPagination    = null;
831            this.newPagination      = null;
832            this.startTotalPages    = -1;
833            this.newTotalPages      = -1;
834        });
835
836        /**
837         * `mixitup.State` objects expose various pieces of data detailing the state of
838         * a MixItUp instance. They are provided at the start and end of any operation via
839         * callbacks and events, with the most recent state stored between operations
840         * for retrieval at any time via the API.
841         *
842         * @constructor
843         * @namespace
844         * @name        State
845         * @memberof    mixitup
846         * @public
847         * @since       3.0.0
848         */
849
850        mixitup.State.registerAction('afterConstruct', 'pagination', function() {
851
852            /**
853             * The currently active pagination command as set by a control click or API call.
854             *
855             * @name        activePagination
856             * @memberof    mixitup.State
857             * @instance
858             * @type        {mixitup.CommandPagination}
859             * @default     null
860             */
861
862            this.activePagination = null;
863
864            /**
865             * The total number of pages produced as a combination of the current page
866             * limit and active filter.
867             *
868             * @name        totalPages
869             * @memberof    mixitup.State
870             * @instance
871             * @type        {number}
872             * @default     -1
873             */
874
875            this.totalPages = -1;
876        });
877
878        mixitup.MixerDom.registerAction('afterConstruct', 'pagination', function() {
879            this.pageListEls  = [];
880            this.pageStatsEls = [];
881        });
882
883        /**
884         * The mixitup.Mixer class is extended with the following methods relating to
885         * the Pagination extension.
886         *
887         * For the full list of methods, please refer to the MixItUp core documentation.
888         *
889         * @constructor
890         * @namespace
891         * @name        Mixer
892         * @memberof    mixitup
893         * @public
894         * @since       3.0.0
895         */
896
897        mixitup.Mixer.registerAction('afterConstruct', 'pagination', function() {
898            this.classNamesPager        = new mixitup.UiClassNames();
899            this.classNamesPageList     = new mixitup.UiClassNames();
900            this.classNamesPageStats    = new mixitup.UiClassNames();
901        });
902
903        /**
904         * @private
905         * @return  {void}
906         */
907
908        mixitup.Mixer.registerAction('afterAttach', 'pagination', function() {
909            var self = this,
910                el   = null,
911                i    = -1;
912
913            if (self.config.pagination.limit < 0) return;
914
915            // Map pagination ui classNames
916
917            // jscs:disable
918            self.classNamesPager.base               = h.getClassname(self.config.classNames, 'pager');
919            self.classNamesPager.active             = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierActive);
920            self.classNamesPager.disabled           = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierDisabled);
921            self.classNamesPager.first              = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierFirst);
922            self.classNamesPager.last               = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierLast);
923            self.classNamesPager.prev               = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierPrev);
924            self.classNamesPager.next               = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierNext);
925            self.classNamesPager.truncationMarker   = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierTruncationMarker);
926
927            self.classNamesPageList.base            = h.getClassname(self.config.classNames, 'page-list');
928            self.classNamesPageList.disabled        = h.getClassname(self.config.classNames, 'page-list', self.config.classNames.modifierDisabled);
929
930            self.classNamesPageStats.base           = h.getClassname(self.config.classNames, 'page-stats');
931            self.classNamesPageStats.disabled       = h.getClassname(self.config.classNames, 'page-stats', self.config.classNames.modifierDisabled);
932            // jscs:enable
933
934            if (self.config.pagination.generatePageList && self.dom.pageListEls.length > 0) {
935                for (i = 0; (el = self.dom.pageListEls[i]); i++) {
936                    self.renderPageListEl(el, self.lastOperation);
937                }
938            }
939
940            if (self.config.pagination.generatePageStats && self.dom.pageStatsEls.length > 0) {
941                for (i = 0; (el = self.dom.pageStatsEls[i]); i++) {
942                    self.renderPageStatsEl(el, self.lastOperation);
943                }
944            }
945        });
946
947        mixitup.Mixer.registerAction('afterSanitizeConfig', 'pagination', function() {
948            var self            = this,
949                onMixStart      = self.config.callbacks.onMixStart,
950                onMixEnd        = self.config.callbacks.onMixEnd,
951                onPaginateStart = self.config.callbacks.onPaginateStart,
952                onPaginateEnd   = self.config.callbacks.onPaginateEnd,
953                didPaginate     = false;
954
955            if (self.config.pagination.limit < 0) return;
956
957            self.classNamesPager        = new mixitup.UiClassNames();
958            self.classNamesPageList     = new mixitup.UiClassNames();
959            self.classNamesPageStats    = new mixitup.UiClassNames();
960
961            self.config.callbacks.onMixStart = function(prevState, nextState) {
962                if (
963                    prevState.activePagination.limit !== nextState.activePagination.limit ||
964                    prevState.activePagination.page !== nextState.activePagination.page
965                ) {
966                    didPaginate = true;
967                }
968
969                if (typeof onMixStart === 'function') onMixStart.apply(self.dom.container, arguments);
970
971                if (!didPaginate) return;
972
973                mixitup.events.fire('paginateStart', self.dom.container, {
974                    state: prevState,
975                    futureState: nextState,
976                    instance: self
977                }, self.dom.document);
978
979                if (typeof onPaginateStart === 'function') onPaginateStart.apply(self.dom.container, arguments);
980            };
981
982            self.config.callbacks.onMixEnd = function(state) {
983                if (typeof onMixEnd === 'function') onMixEnd.apply(self.dom.container, arguments);
984
985                if (!didPaginate) return;
986
987                didPaginate = false;
988
989                mixitup.events.fire('paginateEnd', self.dom.container, {
990                    state: state,
991                    instance: self
992                }, self.dom.document);
993
994                if (typeof onPaginateEnd === 'function') onPaginateEnd.apply(self.dom.container, arguments);
995            };
996        });
997
998        /**
999         * @private
1000         * @param   {mixitup.Operation} operation
1001         * @param   {mixitup.State}     state
1002         * @return  {mixitup.Operation}
1003         */
1004
1005        mixitup.Mixer.registerFilter('operationGetInitialState', 'pagination', function(operation, state) {
1006            var self = this;
1007
1008            if (self.config.pagination.limit < 0) return operation;
1009
1010            operation.newPagination = state.activePagination;
1011
1012            return operation;
1013        });
1014
1015        /**
1016         * @private
1017         * @param   {mixitup.State} state
1018         * @return  {mixitup.State}
1019         */
1020
1021        mixitup.Mixer.registerFilter('stateGetInitialState', 'pagination', function(state) {
1022            var self = this;
1023
1024            if (self.config.pagination.limit < 0) return state;
1025
1026            state.activePagination = new mixitup.CommandPaginate();
1027
1028            state.activePagination.page     = self.config.load.page;
1029            state.activePagination.limit    = self.config.pagination.limit;
1030
1031            return state;
1032        });
1033
1034        /**
1035         * @private
1036         * @return  {void}
1037         */
1038
1039        mixitup.Mixer.registerAction('afterGetFinalMixData', 'pagination', function() {
1040            var self = this;
1041
1042            if (self.config.pagination.limit < 0) return;
1043
1044            if (typeof self.config.pagination.maxPagers === 'number') {
1045                // Restrict max pagers to a minimum of 5. There must always
1046                // be a first, last, and one on either side of the active pager.
1047                // e.g. « 1 ... 4 5 6 ... 10 »
1048
1049                self.config.pagination.maxPagers = Math.max(5, self.config.pagination.maxPagers);
1050            }
1051        });
1052
1053        /**
1054         * @private
1055         * @return  {void}
1056         */
1057
1058        mixitup.Mixer.registerAction('afterCacheDom', 'pagination', function() {
1059            var self    = this,
1060                parent  = null;
1061
1062            if (self.config.pagination.limit < 0) return;
1063
1064            if (!self.config.pagination.generatePageList) return;
1065
1066            switch (self.config.controls.scope) {
1067                case 'local':
1068                    parent = self.dom.container;
1069
1070                    break;
1071                case 'global':
1072                    parent = self.dom.document;
1073
1074                    break;
1075                default:
1076                    throw new Error(mixitup.messages.ERROR_CONFIG_INVALID_CONTROLS_SCOPE);
1077            }
1078
1079            self.dom.pageListEls  = parent.querySelectorAll(self.config.selectors.pageList);
1080            self.dom.pageStatsEls = parent.querySelectorAll(self.config.selectors.pageStats);
1081        });
1082
1083        /**
1084         * @private
1085         * @param   {mixitup.State}     state
1086         * @param   {mixitup.Operation} operation
1087         * @return  {mixitup.State}
1088         */
1089
1090        mixitup.Mixer.registerFilter('stateBuildState', 'pagination', function(state, operation) {
1091            var self        = this;
1092
1093            if (self.config.pagination.limit < 0) return state;
1094
1095            // Map pagination-specific properties into state
1096
1097            state.activePagination  = operation.newPagination;
1098            state.totalPages        = operation.newTotalPages;
1099
1100            return state;
1101        });
1102
1103        /**
1104         * @private
1105         * @param   {mixitup.UserInstruction}     instruction
1106         * @return  {mixitup.UserInstruction}
1107         */
1108
1109        mixitup.Mixer.registerFilter('instructionParseMultimixArgs', 'pagination', function(instruction) {
1110            var self = this;
1111
1112            if (self.config.pagination.limit < 0) return instruction;
1113
1114            if (instruction.command.paginate && !(instruction.command.paginate instanceof mixitup.CommandPaginate)) {
1115                instruction.command.paginate = self.parsePaginateArgs([instruction.command.paginate]).command;
1116            }
1117
1118            return instruction;
1119        });
1120
1121        /**
1122         * @private
1123         * @param   {mixitup.Operation} operation
1124         * @return  {void}
1125         */
1126
1127        mixitup.Mixer.registerAction('afterFilterOperation', 'pagination', function(operation) {
1128            var self        = this,
1129                startPageAt = -1,
1130                endPageAt   = -1,
1131                inPage      = [],
1132                notInPage   = [],
1133                target      = null,
1134                index       = -1,
1135                i           = -1;
1136
1137            if (self.config.pagination.limit < 0) return;
1138
1139            // Calculate the new total pages as a matter of course (i.e. a change in filter)
1140
1141            // New matching array has already been set at this point
1142
1143            operation.newTotalPages = operation.newPagination.limit ?
1144                Math.max(Math.ceil(operation.matching.length / operation.newPagination.limit), 1) :
1145                1;
1146
1147            if (self.config.pagination.maintainActivePage) {
1148                operation.newPagination.page = (operation.newPagination.page > operation.newTotalPages) ?
1149                    operation.newTotalPages :
1150                    operation.newPagination.page;
1151            }
1152
1153            // Keep config in sync with latest limit
1154
1155            self.config.pagination.limit = operation.newPagination.limit;
1156
1157            if (operation.newPagination.anchor) {
1158                // Start page at an anchor element
1159
1160                for (i = 0; target = operation.matching[i]; i++) {
1161                    if (target.dom.el === operation.newPagination.anchor) break;
1162                }
1163
1164                startPageAt = i;
1165                endPageAt   = i + operation.newPagination.limit - 1;
1166            } else {
1167                // Start page based on limit and page index
1168
1169                startPageAt = operation.newPagination.limit * (operation.newPagination.page - 1);
1170                endPageAt   = (operation.newPagination.limit * operation.newPagination.page) - 1;
1171
1172                if (isNaN(startPageAt)) {
1173                    startPageAt = 0;
1174                }
1175            }
1176
1177            if (operation.newPagination.limit < 0) return;
1178
1179            for (i = 0; target = operation.show[i]; i++) {
1180                // For each target in `show`, include in page, only if within the range
1181
1182                if (i >= startPageAt && i <= endPageAt) {
1183                    inPage.push(target);
1184                } else {
1185                    // Else move to `notInPage`
1186
1187                    notInPage.push(target);
1188                }
1189            }
1190
1191            // override the operation's `show` array with the newly constructed `inPage` array
1192
1193            operation.show = inPage;
1194
1195            // For anything not in the page, make sure it is correctly assigned:
1196
1197            for (i = 0; target = operation.toHide[i]; i++) {
1198                // For example, if a target would normally be included in `toHide`, but is
1199                // now already hidden as not in the page, make sure it is removed from `toHide`
1200                // so it is not included in the operation.
1201
1202                if (!target.isShown) {
1203                    operation.toHide.splice(i, 1);
1204
1205                    target.isShown = false;
1206
1207                    i--;
1208                }
1209            }
1210
1211            for (i = 0; target = notInPage[i]; i++) {
1212                // For each target not in page, move into `hide`
1213
1214                operation.hide.push(target);
1215
1216                if ((index = operation.toShow.indexOf(target)) > -1) {
1217                    // Any targets due to be shown will no longer be shown
1218
1219                    operation.toShow.splice(index, 1);
1220                }
1221
1222                if (target.isShown) {
1223                    // If currently shown, move to `toHide`
1224
1225                    operation.toHide.push(target);
1226                }
1227            }
1228        });
1229
1230        /**
1231         * @private
1232         * @param   {mixitup.Operation}         operation
1233         * @param   {mixitup.CommandMultimix}   command
1234         * @return  {mixitup.Operation}
1235         */
1236
1237        mixitup.Mixer.registerFilter('operationUnmappedGetOperation', 'pagination', function(operation, command) {
1238            var self = this;
1239
1240            if (self.config.pagination.limit < 0) return operation;
1241
1242            operation.startState      = self.state;
1243            operation.startPagination = self.state.activePagination;
1244            operation.startTotalPages = self.state.totalPages;
1245
1246            operation.newPagination = new mixitup.CommandPaginate();
1247
1248            operation.newPagination.limit = operation.startPagination.limit;
1249            operation.newPagination.page  = operation.startPagination.page;
1250
1251            if (command.paginate) {
1252                self.parsePaginateCommand(command.paginate, operation);
1253            } else if (command.filter || command.sort) {
1254                h.extend(operation.newPagination, operation.startPagination);
1255
1256                // Reset to 1, or maintain active:
1257
1258                if (!self.config.pagination.maintainActivePage) {
1259                    operation.newPagination.page = 1;
1260                } else {
1261                    operation.newPagination.page = self.state.activePagination.page;
1262                }
1263            }
1264
1265            return operation;
1266        });
1267
1268        /**
1269         * @private
1270         * @param   {mixitup.Operation} operation
1271         * @param   {object}            command
1272         * @param   {boolean}           [isPreFetch=false]
1273         * @return  {mixitup.Operation}
1274         */
1275
1276        mixitup.Mixer.registerFilter('operationMappedGetOperation', 'pagination', function(operation, command, isPreFetch) {
1277            var self = this,
1278                el   = null,
1279                i    = -1;
1280
1281            if (self.config.pagination.limit < 0) return operation;
1282
1283            if (isPreFetch) {
1284                // The operation is being pre-fetched, so don't update the pagers or stats yet.
1285
1286                return operation;
1287            }
1288
1289            if (self.config.pagination.generatePageList && self.dom.pageListEls.length > 0) {
1290                for (i = 0; (el = self.dom.pageListEls[i]); i++) {
1291                    self.renderPageListEl(el, operation);
1292                }
1293            }
1294
1295            if (self.config.pagination.generatePageStats && self.dom.pageStatsEls.length > 0) {
1296                for (i = 0; (el = self.dom.pageStatsEls[i]); i++) {
1297                    self.renderPageStatsEl(el, operation);
1298                }
1299            }
1300
1301            return operation;
1302        });
1303
1304        mixitup.Mixer.extend(
1305        /** @lends mixitup.Mixer */
1306        {
1307            /**
1308             * @private
1309             * @param   {mixitup.CommandPaginate}   command
1310             * @param   {mixitup.Operation}         operation
1311             * @return  {void}
1312             */
1313
1314            parsePaginateCommand: function(command, operation) {
1315                var self = this;
1316
1317                // e.g. mixer.paginate({page: 3, limit: 2});
1318                // e.g. mixer.paginate({action: 'next'});
1319                // e.g. mixer.paginate({anchor: anchorTarget, limit: 5});
1320
1321                if (command.page > -1) {
1322                    if (command.page === 0) throw new Error(mixitup.messages.ERROR_PAGINATE_INDEX_RANGE);
1323
1324                    // TODO: replace Infinity with the highest possible page index
1325
1326                    operation.newPagination.page = Math.max(1, Math.min(Infinity, command.page));
1327                } else if (command.action === 'next') {
1328                    operation.newPagination.page = self.getNextPage();
1329                } else if (command.action === 'prev') {
1330                    operation.newPagination.page = self.getPrevPage();
1331                } else if (command.anchor) {
1332                    operation.newPagination.anchor = command.anchor;
1333                }
1334
1335                if (command.limit > -1) {
1336                    operation.newPagination.limit = command.limit;
1337                }
1338
1339                if (operation.newPagination.limit !== operation.startPagination.limit) {
1340                    // A new limit has been sent via the API, calculate total pages
1341
1342                    operation.newTotalPages = operation.newPagination.limit ?
1343                        Math.max(Math.ceil(operation.startState.matching.length / operation.newPagination.limit), 1) :
1344                        1;
1345                }
1346
1347                if (operation.newPagination.limit <= 0 || operation.newPagination.limit === Infinity) {
1348                    operation.newPagination.page = 1;
1349                }
1350            },
1351
1352            /**
1353             * @private
1354             * @return  {number}    page
1355             */
1356
1357            getNextPage: function() {
1358                var self = this,
1359                    page = -1;
1360
1361                page = self.state.activePagination.page + 1;
1362
1363                if (page > self.state.totalPages) {
1364                    page = self.config.pagination.loop ? 1 : self.state.activePagination.page;
1365                }
1366
1367                return page;
1368            },
1369
1370            /**
1371             * @private
1372             * @return  {Number}    page
1373             */
1374
1375            getPrevPage: function() {
1376                var self = this,
1377                    page = -1;
1378
1379                page = self.state.activePagination.page - 1;
1380
1381                if (page < 1) {
1382                    page = self.config.pagination.loop ? self.state.totalPages : self.state.activePagination.page;
1383                }
1384
1385                return page;
1386            },
1387
1388            /**
1389             * @private
1390             * @param   {HTMLElement}        pageListEl
1391             * @param   {mixitup.Operation}  operation
1392             * @return  {void}
1393             */
1394
1395            renderPageListEl: function(pageListEl, operation) {
1396                var self                = this,
1397                    activeIndex         = -1,
1398                    pagerHtml           = '',
1399                    buttonList          = [],
1400                    model               = null,
1401                    renderer            = null,
1402                    allowedIndices      = [],
1403                    truncatedBefore     = false,
1404                    truncatedAfter      = false,
1405                    disabled            = null,
1406                    el                  = null,
1407                    html                = '',
1408                    i                   = -1;
1409
1410                if (
1411                    operation.newPagination.limit < 0 ||
1412                    operation.newPagination.limit === Infinity ||
1413                    (operation.newTotalPages < 2 && self.config.pagination.hidePageListIfSinglePage)
1414                ) {
1415                    // Empty the pager list, and add disabled class
1416
1417                    pageListEl.innerHTML = '';
1418
1419                    h.addClass(pageListEl, self.classNamesPageList.disabled);
1420
1421                    return;
1422                }
1423
1424                activeIndex = operation.newPagination.page - 1;
1425
1426                renderer = typeof (renderer = self.config.render.pager) === 'function' ?  renderer : null;
1427
1428                if (self.config.pagination.maxPagers < Infinity && operation.newTotalPages > self.config.pagination.maxPagers) {
1429                    allowedIndices = self.getAllowedIndices(operation);
1430                }
1431
1432                // Render prev button
1433
1434                model = new mixitup.ModelPager();
1435
1436                model.isPrev = true;
1437                model.classList.push(self.classNamesPager.base, self.classNamesPager.prev);
1438
1439                // If first and not looping, disable the prev button
1440
1441                if (operation.newPagination.page === 1 && !self.config.pagination.loop) {
1442                    model.classList.push(self.classNamesPager.disabled);
1443
1444                    model.isDisabled = true;
1445                }
1446
1447                model.classNames = model.classList.join(' ');
1448
1449                if (renderer) {
1450                    pagerHtml = renderer(model);
1451                } else {
1452                    pagerHtml = h.template(self.config.templates.pagerPrev)(model);
1453                }
1454
1455                buttonList.push(pagerHtml);
1456
1457                // Render per-page pagers
1458
1459                for (i = 0; i < operation.newTotalPages; i++) {
1460                    pagerHtml = self.renderPager(i, operation, allowedIndices);
1461
1462                    if (pagerHtml || (i < activeIndex && truncatedBefore) || i > activeIndex && truncatedAfter) {
1463                        if (pagerHtml) {
1464                            buttonList.push(pagerHtml);
1465                        }
1466
1467                        continue;
1468                    }
1469
1470                    // Replace gaps between pagers with a truncation maker, but only once
1471
1472                    model = new mixitup.ModelPager();
1473
1474                    model.isTruncationMarker = true;
1475
1476                    model.classList.push(self.classNamesPager.base, self.classNamesPager.truncationMarker);
1477                    model.classNames = model.classList.join(' ');
1478
1479                    if (renderer) {
1480                        pagerHtml = renderer(model);
1481                    } else {
1482                        pagerHtml = h.template(self.config.templates.pagerTruncationMarker)(model);
1483                    }
1484
1485                    buttonList.push(pagerHtml);
1486
1487                    // Prevent multiple truncation markers
1488
1489                    if (i < activeIndex) {
1490                        truncatedBefore = true;
1491                    }
1492
1493                    if (i > activeIndex) {
1494                        truncatedAfter = true;
1495                    }
1496                }
1497
1498                // Render next button
1499
1500                model = new mixitup.ModelPager();
1501
1502                model.isNext = true;
1503                model.classList.push(self.classNamesPager.base, self.classNamesPager.next);
1504
1505                // If last page and not looping, disable the next button
1506
1507                if (operation.newPagination.page === operation.newTotalPages && !self.config.pagination.loop) {
1508                    model.classList.push(self.classNamesPager.disabled);
1509                }
1510
1511                model.classNames = model.classList.join(' ');
1512
1513                if (renderer) {
1514                    pagerHtml = renderer(model);
1515                } else {
1516                    pagerHtml = h.template(self.config.templates.pagerNext)(model);
1517                }
1518
1519                buttonList.push(pagerHtml);
1520
1521                // Replace markup
1522
1523                html = buttonList.join(' ');
1524
1525                pageListEl.innerHTML = html;
1526
1527                // Add disabled attribute to disabled buttons
1528
1529                disabled = pageListEl.querySelectorAll('.' + self.classNamesPager.disabled);
1530
1531                for (i = 0; el = disabled[i]; i++) {
1532                    if (typeof el.disabled === 'boolean') {
1533                        el.disabled = true;
1534                    }
1535                }
1536
1537                if (truncatedBefore || truncatedAfter) {
1538                    h.addClass(pageListEl, self.classNamesPageList.truncated);
1539                } else {
1540                    h.removeClass(pageListEl, self.classNamesPageList.truncated);
1541                }
1542
1543                if (operation.newTotalPages > 1) {
1544                    h.removeClass(pageListEl, self.classNamesPageList.disabled);
1545                } else {
1546                    h.addClass(pageListEl, self.classNamesPageList.disabled);
1547                }
1548            },
1549
1550            /**
1551             * An algorithm defining which pagers should be rendered based on their index
1552             * and the current active page, when a `pagination.maxPagers` value is applied.
1553             *
1554             * @private
1555             * @param   {mixitup.Operation} operation
1556             * @return  {number[]}
1557             */
1558
1559            getAllowedIndices: function(operation) {
1560                var self                = this,
1561                    activeIndex         = operation.newPagination.page - 1,
1562                    lastIndex           = operation.newTotalPages - 1,
1563                    indices             = [],
1564                    paddingRange        = -1,
1565                    paddingBack         = -1,
1566                    paddingFront        = -1,
1567                    paddingRangeStart   = -1,
1568                    paddingRangeEnd     = -1,
1569                    paddingRangeOffset  = -1,
1570                    i                   = -1;
1571
1572                // Examples:
1573
1574                // « 1 2 *3* 4 5 »                  maxPagers = 5
1575                // « 1 ... 4 *5* 6 ... 10 »         maxPagers = 5
1576
1577                // « 1 ... 6 7 *8* 9 10 »           maxPagers = 6
1578                // « 1 ... 3 4 *5* 6 ... 10 »       maxPagers = 6
1579
1580                // « 1 ... 3 4 *5* 6 7 ... 10 »     maxPagers = 7
1581                // « *1* 2 3 4 5 6 ... 10 »         maxPagers = 7
1582
1583                // This algorithm ensures that at any time, the active pager
1584                // should be surrounded by as many "padding" pagers as possible to equal the
1585                // value of `pagination.maxPagers`, accounting for the fact the first and last
1586                // pager should also always be rendered.
1587
1588                // Push in index 0 to represent the first pager
1589
1590                indices.push(0);
1591
1592                // Calculate the "padding range" by subtracting 2 from `pagination.maxPagers`
1593
1594                paddingRange  = self.config.pagination.maxPagers - 2;
1595
1596                // Distribute the padding equally behind and in front of the active pager.
1597                // If the padding range is an even number, we allow an extra pager behind the active pager.
1598
1599                paddingBack   = Math.ceil((paddingRange - 1) / 2);
1600                paddingFront  = Math.floor((paddingRange - 1) / 2);
1601
1602                // Calculate where the range should start and finish based on the active index
1603
1604                paddingRangeStart   = activeIndex - paddingBack;
1605                paddingRangeEnd     = activeIndex + paddingFront;
1606
1607                // Set the offset to 0
1608
1609                paddingRangeOffset  = 0;
1610
1611                // If the start of the range has collided with the first pager, positively offset as needed
1612
1613                if (paddingRangeStart < 1) {
1614                    paddingRangeOffset = 1 - paddingRangeStart;
1615                }
1616
1617                // If the end of the range has collided with the last pager, negatively offset as needed
1618
1619                if (paddingRangeEnd > lastIndex - 1) {
1620                    paddingRangeOffset = (lastIndex - 1) - paddingRangeEnd;
1621                }
1622
1623                // Calcuate the first index of the range taking into account any offset
1624
1625                i = paddingRangeStart + paddingRangeOffset;
1626
1627                // Iteratate through the range, adding the respective indices:
1628
1629                while (paddingRange) {
1630                    indices.push(i);
1631
1632                    i++;
1633                    paddingRange--;
1634                }
1635
1636                indices.push(lastIndex);
1637
1638                return indices;
1639            },
1640
1641            /**
1642             * Renderes individual, per-page pagers.
1643             *
1644             * @private
1645             * @param   {number}              i
1646             * @param   {mixitup.Operation}   operation
1647             * @param   {number[]}            [allowedIndices]
1648             * @return  {string}
1649             */
1650
1651            renderPager: function(i, operation, allowedIndices) {
1652                var self        = this,
1653                    renderer    = null,
1654                    activePage  = operation.newPagination.page - 1,
1655                    model       = new mixitup.ModelPager(),
1656                    output      = '';
1657
1658                if (
1659                    self.config.pagination.maxPagers < Infinity &&
1660                    allowedIndices.length &&
1661                    allowedIndices.indexOf(i) < 0
1662                ) {
1663                    // maxPagers is set, and this pager is not in the allowed range
1664
1665                    return '';
1666                }
1667
1668                renderer = typeof (renderer = self.config.render.pager) === 'function' ?  renderer : null;
1669
1670                model.isPageLink = true;
1671
1672                model.classList.push(self.classNamesPager.base);
1673
1674                if (i === 0) {
1675                    model.classList.push(self.classNamesPager.first);
1676                }
1677
1678                if (i === operation.newTotalPages - 1) {
1679                    model.classList.push(self.classNamesPager.last);
1680                }
1681
1682                if (i === activePage) {
1683                    model.classList.push(self.classNamesPager.active);
1684                }
1685
1686                model.classNames = model.classList.join(' ');
1687                model.pageNumber = i + 1;
1688
1689                if (renderer) {
1690                    output = renderer(model);
1691                } else {
1692                    output = h.template(self.config.templates.pager)(model);
1693                }
1694
1695                return output;
1696            },
1697
1698            /**
1699             * @private
1700             * @param   {HTMLElement}       pageStatsEl
1701             * @param   {mixitup.Operation} operation
1702             * @return  {void}
1703             */
1704
1705            renderPageStatsEl: function(pageStatsEl, operation) {
1706                var self            = this,
1707                    model           = new mixitup.ModelPageStats(),
1708                    renderer        = null,
1709                    output          = '',
1710                    template        = '';
1711
1712                if (
1713                    operation.newPagination.limit < 0 ||
1714                    operation.newPagination.limit === Infinity ||
1715                    (operation.newTotalPages < 2 && self.config.pagination.hidePageStatsIfSinglePage)
1716                ) {
1717                    // Empty the pager list, and add disabled class
1718
1719                    pageStatsEl.innerHTML = '';
1720
1721                    h.addClass(pageStatsEl, self.classNamesPageStats.disabled);
1722
1723                    return;
1724                }
1725
1726                renderer = typeof (renderer = self.config.render.pageStats) === 'function' ?  renderer : null;
1727
1728                model.totalTargets = operation.matching.length;
1729
1730                if (model.totalTargets) {
1731                    template = operation.newPagination.limit === 1 ?
1732                        self.config.templates.pageStatsSingle :
1733                        self.config.templates.pageStats;
1734                } else {
1735                    template = self.config.templates.pageStatsFail;
1736                }
1737
1738                if (model.totalTargets && operation.newPagination.limit > 0) {
1739                    model.startPageAt = ((operation.newPagination.page - 1) * operation.newPagination.limit) + 1;
1740                    model.endPageAt   = Math.min(model.startPageAt + operation.newPagination.limit - 1, model.totalTargets);
1741                } else {
1742                    model.startPageAt = model.endPageAt = 0;
1743                }
1744
1745                if (renderer) {
1746                    output = renderer(model);
1747                } else {
1748                    output = h.template(template)(model);
1749                }
1750
1751                pageStatsEl.innerHTML = output;
1752
1753                if (model.totalTargets) {
1754                    h.removeClass(pageStatsEl, self.classNamesPageStats.disabled);
1755                } else {
1756                    h.addClass(pageStatsEl, self.classNamesPageStats.disabled);
1757                }
1758            },
1759
1760            /**
1761             * @private
1762             * @param   {Array<*>}                  args
1763             * @return  {mixitup.UserInstruction}   instruction
1764             */
1765
1766            parsePaginateArgs: function(args) {
1767                var self        = this,
1768                    instruction = new mixitup.UserInstruction(),
1769                    arg         = null,
1770                    i           = -1;
1771
1772                instruction.animate = self.config.animation.enable;
1773                instruction.command = new mixitup.CommandPaginate();
1774
1775                for (i = 0; i < args.length; i++) {
1776                    arg = args[i];
1777
1778                    if (arg === null) continue;
1779
1780                    if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) {
1781                        instruction.command.anchor = arg;
1782                    } else if (arg instanceof mixitup.CommandPaginate || typeof arg === 'object') {
1783                        h.extend(instruction.command, arg);
1784                    } else if (typeof arg === 'number') {
1785                        instruction.command.page = arg;
1786                    } else if (typeof arg === 'string' && !isNaN(parseInt(arg))) {
1787                        // e.g. "4"
1788
1789                        instruction.command.page = parseInt(arg);
1790                    } else if (typeof arg === 'string') {
1791                        instruction.command.action = arg;
1792                    } else if (typeof arg === 'boolean') {
1793                        instruction.animate = arg;
1794                    } else if (typeof arg === 'function') {
1795                        instruction.callback = arg;
1796                    }
1797                }
1798
1799                h.freeze(instruction);
1800
1801                // NB: Don't freeze command as may need to be sanitized later by other methods
1802
1803                return instruction;
1804            },
1805
1806            /**
1807             * Changes the current page and/or the current page limit.
1808             *
1809             * @example
1810             *
1811             * .paginate(page [, animate] [, callback])
1812             *
1813             * @example <caption>Example 1: Changing the active page</caption>
1814             *
1815             * console.log(mixer.getState().activePagination.page); // 1
1816             *
1817             * mixer.paginate(2)
1818             *     .then(function(state) {
1819             *         console.log(mixer.getState().activePagination.page === 2); // true
1820             *     });
1821             *
1822             * @example <caption>Example 2: Progressing to the next page</caption>
1823             *
1824             * console.log(mixer.getState().activePagination.page); // 1
1825             *
1826             * mixer.paginate('next')
1827             *     .then(function(state) {
1828             *         console.log(mixer.getState().activePagination.page === 2); // true
1829             *     });
1830             *
1831             * @example <caption>Example 3: Starting a page from an abitrary "anchor" element</caption>
1832             *
1833             * var anchorEl = mixer.getState().show[3];
1834             *
1835             * mixer.paginate(anchorEl)
1836             *     .then(function(state) {
1837             *         console.log(mixer.getState().activePagination.anchor === anchorEl); // true
1838             *         console.log(mixer.getState().show[0] === anchorEl); // true
1839             *     });
1840             *
1841             * @example <caption>Example 4: Changing the page limit</caption>
1842             *
1843             * var anchorEl = mixer.getState().show[3];
1844             *
1845             * console.log(mixer.getState().activePagination.limit); // 8
1846             *
1847             * mixer.paginate({
1848             *    limit: 4
1849             * })
1850             *     .then(function(state) {
1851             *         console.log(mixer.getState().activePagination.limit === 4); // true
1852             *     });
1853             *
1854             * @example <caption>Example 5: Changing the active page and page limit</caption>
1855             *
1856             * mixer.paginate({
1857             *    limit: 4,
1858             *    page: 2
1859             * })
1860             *     .then(function(state) {
1861             *         console.log(mixer.getState().activePagination.page === 2); // true
1862             *         console.log(mixer.getState().activePagination.limit === 4); // true
1863             *     });
1864             *
1865             * @public
1866             * @instance
1867             * @param       {(number|string|object|HTMLElement)}    page
1868             *     A page number, string (`'next'`, `'prev'`), HTML element reference, or command object.
1869             * @param       {boolean}   [animate=true]
1870             *      An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default.
1871             * @param       {function}  [callback=null]
1872             *      An optional callback function to be invoked after the operation has completed.
1873             * @return      {Promise.<mixitup.State>}
1874             *     A promise resolving with the current state object.
1875             */
1876
1877            paginate: function() {
1878                var self        = this,
1879                    instruction = self.parsePaginateArgs(arguments);
1880
1881                return self.multimix({
1882                    paginate: instruction.command
1883                }, instruction.animate, instruction.callback);
1884            },
1885
1886            /**
1887             * A shorthand for `.paginate('next')`. Moves to the next page.
1888             *
1889             * @example
1890             *
1891             * .nextPage()
1892             *
1893             * @example <caption>Example: Moving to the next page</caption>
1894             *
1895             * console.log(mixer.getState().activePagination.page); // 1
1896             *
1897             * mixer.nextPage()
1898             *     .then(function(state) {
1899             *         console.log(mixer.getState().activePagination.page === 2); // true
1900             *     });
1901             *
1902             * @public
1903             * @instance
1904             * @return      {Promise.<mixitup.State>}
1905             *     A promise resolving with the current state object.
1906             */
1907
1908            nextPage: function() {
1909                var self        = this,
1910                    instruction = self.parsePaginateArgs(arguments);
1911
1912                return self.multimix({
1913                    paginate: {
1914                        action: 'next'
1915                    }
1916                }, instruction.animate, instruction.callback);
1917            },
1918
1919            /**
1920             * A shorthand for `.paginate('prev')`. Moves to the previous page.
1921             *
1922             * @example
1923             *
1924             * .prevPage()
1925             *
1926             * @example <caption>Example: Moving to the previous page</caption>
1927             *
1928             * console.log(mixer.getState().activePagination.page); // 5
1929             *
1930             * mixer.prevPage()
1931             *     .then(function(state) {
1932             *         console.log(mixer.getState().activePagination.page === 4); // true
1933             *     });
1934             *
1935             * @public
1936             * @instance
1937             * @return      {Promise.<mixitup.State>}
1938             *     A promise resolving with the current state object.
1939             */
1940
1941            prevPage: function() {
1942                var self = this,
1943                    instruction = self.parsePaginateArgs(arguments);
1944
1945                return self.multimix({
1946                    paginate: {
1947                        action: 'prev'
1948                    }
1949                }, instruction.animate, instruction.callback);
1950            }
1951        });
1952
1953        mixitup.Facade.registerAction('afterConstruct', 'pagination', function(mixer) {
1954            this.paginate = mixer.paginate.bind(mixer);
1955            this.nextPage = mixer.nextPage.bind(mixer);
1956            this.prevPage = mixer.prevPage.bind(mixer);
1957        });    };
1958
1959    mixitupPagination.TYPE                    = 'mixitup-extension';
1960    mixitupPagination.NAME                    = 'mixitup-pagination';
1961    mixitupPagination.EXTENSION_VERSION       = '3.3.
19610';
1962    mixitupPagination.REQUIRE_CORE_VERSION    = '^3.1.8';
1963
1964    if (typeof exports === 'object' && typeof module === 'object') {
1965        module.exports = mixitupPagination;
1966    } else if (typeof define === 'function' && define.amd) {
1967        define(function() {
1968            return mixitupPagination;
1969        });
1970    } else if (window.mixitup && typeof window.mixitup === 'function') {
1971        mixitupPagination(window.mixitup);
1972    } else {
1973        throw new Error('[MixItUp Pagination] MixItUp core not found');
1974    }})(window);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.