PageSourceSearch

https://customer.vitelglobal.com/plugin/js/dirPagination.js

js vitelglobal.com collected 2026-09-24 10:19:47 UTC 22,435 bytes, 514 lines download raw bytes

1/**
2 * dirPagination - AngularJS module for paginating (almost) anything.
3 *
4 *
5 * Credits
6 * =======
7 *
8 * Daniel Tabuenca: https://groups.google.com/d/msg/angular/an9QpzqIYiM/r8v-3W1X5vcJ
9 * for the idea on how to dynamically invoke the ng-repeat directive.
10 *
11 * I borrowed a couple of lines and a few attribute names from the AngularUI Bootstrap project:
12 * https://github.com/angular-ui/bootstrap/blob/master/src/pagination/pagination.js
13 *
14 * Copyright 2014 Michael Bromley <[email protected]>
15 */
16
17(function() { 
18		  
19    var moduleName = 'dirPagination';
20    var DEFAULT_ID = '__default';
21
22    /** Module */
23    var module;
24    try {
25        module = angular.module(moduleName);
26    } catch(err) {
27        module = angular.module(moduleName, []);  // named module does not exist, so create one
28    }
29
30    module
31        .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective])
32        .directive('dirPaginateNoCompile', noCompileDirective)
33        .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective])
34        .filter('itemsPerPage', ['paginationService', itemsPerPageFilter])
35        .service('paginationService', paginationService)
36        .provider('paginationTemplate', paginationTemplateProvider)
37        .run(['$templateCache',dirPaginationControlsTemplateInstaller]);
38
39    function dirPaginateDirective($compile, $parse, paginationService) {
40
41        return  {
42            terminal: true,
43            multiElement: true,
44            compile: dirPaginationCompileFn
45        };
46
47        function dirPaginationCompileFn(tElement, tAttrs){
48
49            var expression = tAttrs.dirPaginate;
50            // regex taken directly from https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L211
51            var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
52
53            var filterPattern = /\|\s*itemsPerPage\s*:[^|]*/;
54            if (match[2].match(filterPattern) === null) {
55                throw 'pagination directive: the \'itemsPerPage\' filter must be set.';
56            }
57            var itemsPerPageFilterRemoved = match[2].replace(filterPattern, '');
58            var collectionGetter = $parse(itemsPerPageFilterRemoved);
59
60            addNoCompileAttributes(tElement);
61
62            // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
63            // dir-pagination-controls directives that may be looking for this ID.
64            var rawId = tAttrs.paginationId || DEFAULT_ID;
65            paginationService.registerInstance(rawId);
66
67            return function dirPaginationLinkFn(scope, element, attrs){
68
69                // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
70                // potentially register a new ID if it evaluates to a different value than the rawId.
71                var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
72                paginationService.registerInstance(paginationId);
73
74                var repeatExpression = getRepeatExpression(expression, paginationId);
75                addNgRepeatToElement(element, attrs, repeatExpression);
76
77                removeTemporaryAttributes(element);
78                var compiled =  $compile(element);
79
80                var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId);
81                paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);
82
83                if (typeof attrs.totalItems !== 'undefined') {
84                    paginationService.setAsyncModeTrue(paginationId);
85                    scope.$watch(function() {
86                        return $parse(attrs.totalItems)(scope);
87                    }, function (result) {
88                        if (0 <= result) {
89                            paginationService.setCollectionLength(paginationId, result);
90                        }
91                    });
92                } else {
93                    scope.$watchCollection(function() {
94                        return collectionGetter(scope);
95                    }, function(collection) {
96                        if (collection) {
97                            paginationService.setCollectionLength(paginationId, collection.length);
98                        }
99                    });
100                }
101
102                // Delegate to the link function returned by the new compilation of the ng-repeat
103                compiled(scope);
104            };
105        }
106
107        /**
108         * If a pagination id has been specified, we need to check that it is present as the second argument passed to
109         * the itemsPerPage filter. If it is not there, we add it and return the modified expression.
110         *
111         * @param expression
112         * @param paginationId
113         * @returns {*}
114         */
115        function getRepeatExpression(expression, paginationId) {
116            var repeatExpression,
117                idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);
118
119            if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
120                repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:[^|]*)/, "$1 : '" + paginationId + "'");
121            } else {
122                repeatExpression = expression;
123            }
124
125            return repeatExpression;
126        }
127
128        /**
129         * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the
130         * appropriate multi-element ng-repeat to the first and last element in the range.
131         * @param element
132         * @param attrs
133         * @param repeatExpression
134         */
135        function addNgRepeatToElement(element, attrs, repeatExpression) {
136            if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) {
137                // using multiElement mode (dir-paginate-start, dir-paginate-end)
138                attrs.$set('ngRepeatStart', repeatExpression);
139                element.eq(element.length - 1).attr('ng-repeat-end', true);
140            } else {
141                attrs.$set('ngRepeat', repeatExpression);
142            }
143        }
144
145        /**
146         * Adds the dir-paginate-no-compile directive to each element in the tElement range.
147         * @param tElement
148         */
149        function addNoCompileAttributes(tElement) {
150            angular.forEach(tElement, function(el) {
151                if (el.nodeType === Node.ELEMENT_NODE) {
152                    angular.element(el).attr('dir-paginate-no-compile', true);
153                }
154            });
155        }
156
157        /**
158         * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives.
159         * @param element
160         */
161        function removeTemporaryAttributes(element) {
162            angular.forEach(element, function(el) {
163                if (el.nodeType === Node.ELEMENT_NODE) {
164                    angular.element(el).removeAttr('dir-paginate-no-compile');
165                }
166            });
167            element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate');
168            element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end');
169        }
170
171        /**
172         * Creates a getter function for the current-page attribute, using the expression provided or a default value if
173         * no current-page expression was specified.
174         *
175         * @param scope
176         * @param attrs
177         * @param paginationId
178         * @returns {*}
179         */
180        function makeCurrentPageGetterFn(scope, attrs, paginationId) {
181            var currentPageGetter;
182            if (attrs.currentPage) {
183                currentPageGetter = $parse(attrs.currentPage);
184            } else {
185                // if the current-page attribute was not set, we'll make our own
186                var defaultCurrentPage = paginationId + '__currentPage';
187                scope[defaultCurrentPage] = 1;
188                currentPageGetter = $parse(defaultCurrentPage);
189            }
190            return currentPageGetter;
191        }
192    }
193
194    /**
195     * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end).
196     * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of
197     * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled.
198     */
199    function noCompileDirective() {
200        return {
201            priority: 5000,
202            terminal: true
203        };
204    }
205
206    function dirPaginationControlsTemplateInstaller($templateCache) {
207        $templateCache.put('dirPagination.template', '<ul class="pagination" ng-if="1 < pages.length"><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(1)">&laquo;</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(pagination.current - 1)">&lsaquo;</a></li><li ng-repeat="pageNumber in pages track by $index" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' }"><a href="" ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.current + 1)">&rsaquo;</a></li><li ng-if="boundaryLinks"  ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)">&raquo;</a></li></ul>');
208    }
209
210    function dirPaginationControlsDirective(paginationService, paginationTemplate) {
211
212        var numberRegex = /^\d+$/;
213
214        return {
215            restrict: 'AE',
216            templateUrl: function(elem, attrs) {
217                return attrs.templateUrl || paginationTemplate.getPath();
218            },
219            scope: {
220                maxSize: '=?',
221                onPageChange: '&?',
222                paginationId: '=?'
223            },
224            link: dirPaginationControlsLinkFn
225        };
226
227        function dirPaginationControlsLinkFn(scope, element, attrs) {
228
229            // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
230            // not yet been linked (e.g. if it is inside an ng-if block), and in that case it prevents this controls directive from assuming that there is
231            // no corresponding dir-paginate directive and wrongly throwing an exception.
232            var rawId = attrs.paginationId ||  DEFAULT_ID;
233            var paginationId = scope.paginationId || attrs.paginationId ||  DEFAULT_ID;
234
235            if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
236                var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' ';
237                throw 'pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive.';
238            }
239
240            if (!scope.maxSize) { scope.maxSize = 9; }
241            scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
242            scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;
243
244            var paginationRange = Math.max(scope.maxSize, 5);
245            scope.pages = [];
246            scope.pagination = {
247                last: 1,
248                current: 1
249            };
250            scope.range = {
251                lower: 1,
252                upper: 1,
253                total: 1
254            };
255
256            scope.$watch(function() {
257                return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
258            }, function(length) {
259                if (0 < length) {
260                    generatePagination();
261                }
262            });
263
264            scope.$watch(function() {
265                return (paginationService.getItemsPerPage(paginationId));
266            }, function(current, previous) {
267                if (current != previous && typeof previous !== 'undefined') {
268                    goToPage(scope.pagination.current);
269                }
270            });
271
272            scope.$watch(function() {
273                return paginationService.getCurrentPage(paginationId);
274            }, function(currentPage, previousPage) {
275                if (currentPage != previousPage) {
276                    goToPage(currentPage);
277                }
278            });
279
280            scope.setCurrent = function(num) {
281                if (isValidPageNumber(num)) {
282                    num = parseInt(num, 10);
283                    paginationService.setCurrentPage(paginationId, num);
284                }
285            };
286
287            function goToPage(num) {
288                if (isValidPageNumber(num)) {
289                    scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
290                    scope.pagination.current = num;
291                    updateRangeValues();
292
293                    // if a callback has been set, then call it with the page number as an argument
294                    if (scope.onPageChange) {
295                        scope.onPageChange({ newPageNumber : num });
296                    }
297                }
298            }
299
300            function generatePagination() {
301                var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;
302
303                scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
304                scope.pagination.current = page;
305                scope.pagination.last = scope.pages[scope.pages.length - 1];
306                if (scope.pagination.last < scope.pagination.current) {
307                    scope.setCurrent(scope.pagination.last);
308                } else {
309                    updateRangeValues();
310                }
311            }
312
313            /**
314             * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
315             * template to display the current page range, e.g. "showing 21 - 40 of 144 results";
316             */
317            function updateRangeValues() {
318                var currentPage = paginationService.getCurrentPage(paginationId),
319                    itemsPerPage = paginationService.getItemsPerPage(paginationId),
320                    totalItems = paginationService.getCollectionLength(paginationId);
321
322                scope.range.lower = (currentPage - 1) * itemsPerPage + 1;
323                scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
324                scope.range.total = totalItems;
325            }
326
327            function isValidPageNumber(num) {
328                return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
329            }
330        }
331
332        /**
333         * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
334         * links used in pagination
335         *
336         * @param currentPage
337         * @param rowsPerPage
338         * @param paginationRange
339         * @param collectionLength
340         * @returns {Array}
341         */
342        function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
343            var pages = [];
344            var totalPages = Math.ceil(collectionLength / rowsPerPage);
345            var halfWay = Math.ceil(paginationRange / 2);
346            var position;
347
348            if (currentPage <= halfWay) {
349                position = 'start';
350            } else if (totalPages - halfWay < currentPage) {
351                position = 'end';
352            } else {
353                position = 'middle';
354            }
355
356            var ellipsesNeeded = paginationRange < totalPages;
357            var i = 1;
358            while (i <= totalPages && i <= paginationRange) {
359                var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);
360
361                var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end'));
362                var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start'));
363                if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
364                    pages.push('...');
365                } else {
366                    pages.push(pageNumber);
367                }
368                i ++;
369            }
370            return pages;
371        }
372
373        /**
374         * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
375         *
376         * @param i
377         * @param currentPage
378         * @param paginationRange
379         * @param totalPages
380         * @returns {*}
381         */
382        function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
383            var halfWay = Math.ceil(paginationRange/2);
384            if (i === paginationRange) {
385                return totalPages;
386            } else if (i === 1) {
387                return i;
388            } else if (paginationRange < totalPages) {
389                if (totalPages - halfWay < currentPage) {
390                    return totalPages - paginationRange + i;
391                } else if (halfWay < currentPage) {
392                    return currentPage - halfWay + i;
393                } else {
394                    return i;
395                }
396            } else {
397                return i;
398            }
399        }
400    }
401
402    /**
403     * This filter slices the collection into pages based on the current page number and number of items per page.
404     * @param paginationService
405     * @returns {Function}
406     */
407    function itemsPerPageFilter(paginationService) {
408
409        return function(collection, itemsPerPage, paginationId) {
410            if (typeof (paginationId) === 'undefined') {
411                paginationId = DEFAULT_ID;
412            }
413            if (!paginationService.isRegistered(paginationId)) {
414                throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.';
415            }
416            var end;
417            var start;
418            if (collection instanceof Array) {
419                itemsPerPage = parseInt(itemsPerPage) || 9999999999;
420                if (paginationService.isAsyncMode(paginationId)) {
421                    start = 0;
422                } else {
423                    start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage;
424                }
425                end = start + itemsPerPage;
426                paginationService.setItemsPerPage(paginationId, itemsPerPage);
427
428                return collection.slice(start, end);
429            } else {
430                return collection;
431            }
432        };
433    }
434
435    /**
436     * This service allows the various parts of the module to communicate and stay in sync.
437     */
438    function paginationService() {
439
440        var instances = {};
441        var lastRegisteredInstance;
442
443        this.registerInstance = function(instanceId) {
444            if (typeof instances[instanceId] === 'undefined') {
445                instances[instanceId] = {
446                    asyncMode: false
447                };
448                lastRegisteredInstance = instanceId;
449            }
450        };
451
452        this.isRegistered = function(instanceId) {
453            return (typeof instances[instanceId] !== 'undefined');
454        };
455
456        this.getLastInstanceId = function() {
457            return lastRegisteredInstance;
458        };
459
460        this.setCurrentPageParser = function(instanceId, val, scope) {
461            instances[instanceId].currentPageParser = val;
462            instances[instanceId].context = scope;
463        };
464        this.setCurrentPage = function(instanceId, val) {
465            instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
466        };
467        this.getCurrentPage = function(instanceId) {
468            var parser = instances[instanceId].currentPageParser;
469            return parser ? parser(instances[instanceId].context) : 1;
470        };
471
472        this.setItemsPerPage = function(instanceId, val) {
473            instances[instanceId].itemsPerPage = val;
474        };
475        this.getItemsPerPage = function(instanceId) {
476            return instances[instanceId].itemsPerPage;
477        };
478
479        this.setCollectionLength = function(instanceId, val) {
480            instances[instanceId].collectionLength = val;
481        };
482        this.getCollectionLength = function(instanceId) {
483            return instances[instanceId].collectionLength;
484        };
485
486        this.setAsyncModeTrue = function(instanceId) {
487            instances[instanceId].asyncMode = true;
488        };
489
490        this.isAsyncMode = function(instanceId) {
491            return instances[instanceId].asyncMode;
492        };
493    }
494
495    /**
496     * This provider allows global configuration of the template path used by the dir-pagination-controls directive.
497     */
498    function paginationTemplateProvider() {
499
500        var templatePath = 'dirPagination.template';
501
502        this.setPath = function(path) {
503            templatePath = path;
504        };
505
506        this.$get = function() {
507            return {
508                getPath: function() {
509                    return templatePath;
510                }
511            };
512        };
513    }
514})();

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.