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">«</button>' 615 */ 616 617 this.pagerPrev = '<button type="button" class="${classNames}" data-page="prev">«</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">»</button>' 625 */ 626 627 this.pagerNext = '<button type="button" class="${classNames}" data-page="next">»</button>'; 628 629 /** 630 * @name pagerTruncationMarker 631 * @memberof mixitup.Config.templates 632 * @instance 633 * @type {string} 634 * @default '<span class="${classNames}">…</span>' 635 */ 636 637 this.pagerTruncationMarker = '<span class="${classNames}">…</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.