PageSourceSearch

https://www.1220kldc.com/wp-content/plugins/entity-closing-lite/li…/dirPagination.js?ver=1.0.0

js 1220kldc.com collected 2026-10-01 06:56:03 UTC 27,701 bytes, 639 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    /**
20     * Config
21     */
22    var moduleName = 'angularUtils.directives.dirPagination';
23    var DEFAULT_ID = '__default';
24
25    /**
26     * Module
27     */
28    angular.module(moduleName, [])
29        .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective])
30        .directive('dirPaginateNoCompile', noCompileDirective)
31        .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective])
32        .filter('itemsPerPage', ['paginationService', itemsPerPageFilter])
33        .service('paginationService', paginationService)
34        .provider('paginationTemplate', paginationTemplateProvider)
35        .run(['$templateCache',dirPaginationControlsTemplateInstaller]);
36
37    function dirPaginateDirective($compile, $parse, paginationService) {
38
39        return  {
40            terminal: true,
41            multiElement: true,
42            priority: 100,
43            compile: dirPaginationCompileFn
44        };
45
46        function dirPaginationCompileFn(tElement, tAttrs){
47
48            var expression = tAttrs.dirPaginate;
49            // regex taken directly from https://github.com/angular/angular.js/blob/v1.4.x/src/ng/directive/ngRepeat.js#L339
50            var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
51
52            var filterPattern = /\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/;
53            if (match[2].match(filterPattern) === null) {
54                throw 'pagination directive: the \'itemsPerPage\' filter must be set.';
55            }
56            var itemsPerPageFilterRemoved = match[2].replace(filterPattern, '');
57            var collectionGetter = $parse(itemsPerPageFilterRemoved);
58
59            addNoCompileAttributes(tElement);
60
61            // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
62            // dir-pagination-controls directives that may be looking for this ID.
63            var rawId = tAttrs.paginationId || DEFAULT_ID;
64            paginationService.registerInstance(rawId);
65
66            return function dirPaginationLinkFn(scope, element, attrs){
67
68                // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
69                // potentially register a new ID if it evaluates to a different value than the rawId.
70                var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
71                
72                // (TODO: this seems sound, but I'm reverting as many bug reports followed it's introduction in 0.11.
vendor: 24,470 bytes, lines 72-639
720.
73                // Needs more investigation.)
74                // In case rawId != paginationId we deregister using rawId for the sake of general cleanliness
75                // before registering using paginationId
76                // paginationService.deregisterInstance(rawId);
77                paginationService.registerInstance(paginationId);
78
79                var repeatExpression = getRepeatExpression(expression, paginationId);
80                addNgRepeatToElement(element, attrs, repeatExpression);
81
82                removeTemporaryAttributes(element);
83                var compiled =  $compile(element);
84
85                var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId);
86                paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);
87
88                if (typeof attrs.totalItems !== 'undefined') {
89                    paginationService.setAsyncModeTrue(paginationId);
90                    scope.$watch(function() {
91                        return $parse(attrs.totalItems)(scope);
92                    }, function (result) {
93                        if (0 <= result) {
94                            paginationService.setCollectionLength(paginationId, result);
95                        }
96                    });
97                } else {
98                    paginationService.setAsyncModeFalse(paginationId);
99                    scope.$watchCollection(function() {
100                        return collectionGetter(scope);
101                    }, function(collection) {
102                        if (collection) {
103                            var collectionLength = (collection instanceof Array) ? collection.length : Object.keys(collection).length;
104                            paginationService.setCollectionLength(paginationId, collectionLength);
105                        }
106                    });
107                }
108
109                // Delegate to the link function returned by the new compilation of the ng-repeat
110                compiled(scope);
111                 
112                // (TODO: Reverting this due to many bug reports in v 0.11.0. Needs investigation as the
113                // principle is sound)
114                // When the scope is destroyed, we make sure to remove the reference to it in paginationService
115                // so that it can be properly garbage collected
116                // scope.$on('$destroy', function destroyDirPagination() {
117                //     paginationService.deregisterInstance(paginationId);
118                // });
119            };
120        }
121
122        /**
123         * If a pagination id has been specified, we need to check that it is present as the second argument passed to
124         * the itemsPerPage filter. If it is not there, we add it and return the modified expression.
125         *
126         * @param expression
127         * @param paginationId
128         * @returns {*}
129         */
130        function getRepeatExpression(expression, paginationId) {
131            var repeatExpression,
132                idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);
133
134            if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
135                repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/, "$1 : '" + paginationId + "'");
136            } else {
137                repeatExpression = expression;
138            }
139
140            return repeatExpression;
141        }
142
143        /**
144         * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the
145         * appropriate multi-element ng-repeat to the first and last element in the range.
146         * @param element
147         * @param attrs
148         * @param repeatExpression
149         */
150        function addNgRepeatToElement(element, attrs, repeatExpression) {
151            if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) {
152                // using multiElement mode (dir-paginate-start, dir-paginate-end)
153                attrs.$set('ngRepeatStart', repeatExpression);
154                element.eq(element.length - 1).attr('ng-repeat-end', true);
155            } else {
156                attrs.$set('ngRepeat', repeatExpression);
157            }
158        }
159
160        /**
161         * Adds the dir-paginate-no-compile directive to each element in the tElement range.
162         * @param tElement
163         */
164        function addNoCompileAttributes(tElement) {
165            angular.forEach(tElement, function(el) {
166                if (el.nodeType === 1) {
167                    angular.element(el).attr('dir-paginate-no-compile', true);
168                }
169            });
170        }
171
172        /**
173         * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives.
174         * @param element
175         */
176        function removeTemporaryAttributes(element) {
177            angular.forEach(element, function(el) {
178                if (el.nodeType === 1) {
179                    angular.element(el).removeAttr('dir-paginate-no-compile');
180                }
181            });
182            element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate');
183            element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end');
184        }
185
186        /**
187         * Creates a getter function for the current-page attribute, using the expression provided or a default value if
188         * no current-page expression was specified.
189         *
190         * @param scope
191         * @param attrs
192         * @param paginationId
193         * @returns {*}
194         */
195        function makeCurrentPageGetterFn(scope, attrs, paginationId) {
196            var currentPageGetter;
197            if (attrs.currentPage) {
198                currentPageGetter = $parse(attrs.currentPage);
199            } else {
200                // If the current-page attribute was not set, we'll make our own.
201                // Replace any non-alphanumeric characters which might confuse
202                // the $parse service and give unexpected results.
203                // See https://github.com/michaelbromley/angularUtils/issues/233
204                var defaultCurrentPage = (paginationId + '__currentPage').replace(/\W/g, '_');
205                scope[defaultCurrentPage] = 1;
206                currentPageGetter = $parse(defaultCurrentPage);
207            }
208            return currentPageGetter;
209        }
210    }
211
212    /**
213     * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end).
214     * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of
215     * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled.
216     */
217    function noCompileDirective() {
218        return {
219            priority: 5000,
220            terminal: true
221        };
222    }
223
224    function dirPaginationControlsTemplateInstaller($templateCache) {
225        $templateCache.put('angularUtils.directives.dirPagination.template', '<ul class="pagination" ng-if="1 < pages.length || !autoHide"><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 tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' || ( ! autoHide && pages.length === 1 ) }"><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>');
226    }
227
228    function dirPaginationControlsDirective(paginationService, paginationTemplate) {
229
230        var numberRegex = /^\d+$/;
231
232        var DDO = {
233            restrict: 'AE',
234            scope: {
235                maxSize: '=?',
236                onPageChange: '&?',
237                paginationId: '=?',
238                autoHide: '=?'
239            },
240            link: dirPaginationControlsLinkFn
241        };
242
243        // We need to check the paginationTemplate service to see whether a template path or
244        // string has been specified, and add the `template` or `templateUrl` property to
245        // the DDO as appropriate. The order of priority to decide which template to use is
246        // (highest priority first):
247        // 1. paginationTemplate.getString()
248        // 2. attrs.templateUrl
249        // 3. paginationTemplate.getPath()
250        var templateString = paginationTemplate.getString();
251        if (templateString !== undefined) {
252            DDO.template = templateString;
253        } else {
254            DDO.templateUrl = function(elem, attrs) {
255                return attrs.templateUrl || paginationTemplate.getPath();
256            };
257        }
258        return DDO;
259
260        function dirPaginationControlsLinkFn(scope, element, attrs) {
261
262            // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
263            // 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
264            // no corresponding dir-paginate directive and wrongly throwing an exception.
265            var rawId = attrs.paginationId ||  DEFAULT_ID;
266            var paginationId = scope.paginationId || attrs.paginationId ||  DEFAULT_ID;
267
268            if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
269                var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' ';
270                if (window.console) {
271                    console.warn('Pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive, which was not found at link time.');
272                }
273            }
274
275            if (!scope.maxSize) { scope.maxSize = 9; }
276            scope.autoHide = scope.autoHide === undefined ? true : scope.autoHide;
277            scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
278            scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;
279
280            var paginationRange = Math.max(scope.maxSize, 5);
281            scope.pages = [];
282            scope.pagination = {
283                last: 1,
284                current: 1
285            };
286            scope.range = {
287                lower: 1,
288                upper: 1,
289                total: 1
290            };
291
292            scope.$watch('maxSize', function(val) {
293                if (val) {
294                    paginationRange = Math.max(scope.maxSize, 5);
295                    generatePagination();
296                }
297            });
298
299            scope.$watch(function() {
300                if (paginationService.isRegistered(paginationId)) {
301                    return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
302                }
303            }, function(length) {
304                if (0 < length) {
305                    generatePagination();
306                }
307            });
308
309            scope.$watch(function() {
310                if (paginationService.isRegistered(paginationId)) {
311                    return (paginationService.getItemsPerPage(paginationId));
312                }
313            }, function(current, previous) {
314                if (current != previous && typeof previous !== 'undefined') {
315                    goToPage(scope.pagination.current);
316                }
317            });
318
319            scope.$watch(function() {
320                if (paginationService.isRegistered(paginationId)) {
321                    return paginationService.getCurrentPage(paginationId);
322                }
323            }, function(currentPage, previousPage) {
324                if (currentPage != previousPage) {
325                    goToPage(currentPage);
326                }
327            });
328
329            scope.setCurrent = function(num) {
330                if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) {
331                    num = parseInt(num, 10);
332                    paginationService.setCurrentPage(paginationId, num);
333                }
334            };
335
336            /**
337             * Custom "track by" function which allows for duplicate "..." entries on long lists,
338             * yet fixes the problem of wrongly-highlighted links which happens when using
339             * "track by $index" - see https://github.com/michaelbromley/angularUtils/issues/153
340             * @param id
341             * @param index
342             * @returns {string}
343             */
344            scope.tracker = function(id, index) {
345                return id + '_' + index;
346            };
347
348            function goToPage(num) {
349                if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) {
350                    var oldPageNumber = scope.pagination.current;
351
352                    scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
353                    scope.pagination.current = num;
354                    updateRangeValues();
355
356                    // if a callback has been set, then call it with the page number as the first argument
357                    // and the previous page number as a second argument
358                    if (scope.onPageChange) {
359                        scope.onPageChange({
360                            newPageNumber : num,
361                            oldPageNumber : oldPageNumber
362                        });
363                    }
364                }
365            }
366
367            function generatePagination() {
368                if (paginationService.isRegistered(paginationId)) {
369                    var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;
370                    scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
371                    scope.pagination.current = page;
372                    scope.pagination.last = scope.pages[scope.pages.length - 1];
373                    if (scope.pagination.last < scope.pagination.current) {
374                        scope.setCurrent(scope.pagination.last);
375                    } else {
376                        updateRangeValues();
377                    }
378                }
379            }
380
381            /**
382             * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
383             * template to display the current page range, e.g. "showing 21 - 40 of 144 results";
384             */
385            function updateRangeValues() {
386                if (paginationService.isRegistered(paginationId)) {
387                    var currentPage = paginationService.getCurrentPage(paginationId),
388                        itemsPerPage = paginationService.getItemsPerPage(paginationId),
389                        totalItems = paginationService.getCollectionLength(paginationId);
390
391                    scope.range.lower = (currentPage - 1) * itemsPerPage + 1;
392                    scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
393                    scope.range.total = totalItems;
394                }
395            }
396            function isValidPageNumber(num) {
397                return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
398            }
399        }
400
401        /**
402         * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
403         * links used in pagination
404         *
405         * @param currentPage
406         * @param rowsPerPage
407         * @param paginationRange
408         * @param collectionLength
409         * @returns {Array}
410         */
411        function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
412            var pages = [];
413            var totalPages = Math.ceil(collectionLength / rowsPerPage);
414            var halfWay = Math.ceil(paginationRange / 2);
415            var position;
416
417            if (currentPage <= halfWay) {
418                position = 'start';
419            } else if (totalPages - halfWay < currentPage) {
420                position = 'end';
421            } else {
422                position = 'middle';
423            }
424
425            var ellipsesNeeded = paginationRange < totalPages;
426            var i = 1;
427            while (i <= totalPages && i <= paginationRange) {
428                var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);
429
430                var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end'));
431                var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start'));
432                if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
433                    pages.push('...');
434                } else {
435                    pages.push(pageNumber);
436                }
437                i ++;
438            }
439            return pages;
440        }
441
442        /**
443         * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
444         *
445         * @param i
446         * @param currentPage
447         * @param paginationRange
448         * @param totalPages
449         * @returns {*}
450         */
451        function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
452            var halfWay = Math.ceil(paginationRange/2);
453            if (i === paginationRange) {
454                return totalPages;
455            } else if (i === 1) {
456                return i;
457            } else if (paginationRange < totalPages) {
458                if (totalPages - halfWay < currentPage) {
459                    return totalPages - paginationRange + i;
460                } else if (halfWay < currentPage) {
461                    return currentPage - halfWay + i;
462                } else {
463                    return i;
464                }
465            } else {
466                return i;
467            }
468        }
469    }
470
471    /**
472     * This filter slices the collection into pages based on the current page number and number of items per page.
473     * @param paginationService
474     * @returns {Function}
475     */
476    function itemsPerPageFilter(paginationService) {
477
478        return function(collection, itemsPerPage, paginationId) {
479            if (typeof (paginationId) === 'undefined') {
480                paginationId = DEFAULT_ID;
481            }
482            if (!paginationService.isRegistered(paginationId)) {
483                throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.';
484            }
485            var end;
486            var start;
487            if (angular.isObject(collection)) {
488                itemsPerPage = parseInt(itemsPerPage) || 9999999999;
489                if (paginationService.isAsyncMode(paginationId)) {
490                    start = 0;
491                } else {
492                    start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage;
493                }
494                end = start + itemsPerPage;
495                paginationService.setItemsPerPage(paginationId, itemsPerPage);
496
497                if (collection instanceof Array) {
498                    // the array just needs to be sliced
499                    return collection.slice(start, end);
500                } else {
501                    // in the case of an object, we need to get an array of keys, slice that, then map back to
502                    // the original object.
503                    var slicedObject = {};
504                    angular.forEach(keys(collection).slice(start, end), function(key) {
505                        slicedObject[key] = collection[key];
506                    });
507                    return slicedObject;
508                }
509            } else {
510                return collection;
511            }
512        };
513    }
514
515    /**
516     * Shim for the Object.keys() method which does not exist in IE < 9
517     * @param obj
518     * @returns {Array}
519     */
520    function keys(obj) {
521        if (!Object.keys) {
522            var objKeys = [];
523            for (var i in obj) {
524                if (obj.hasOwnProperty(i)) {
525                    objKeys.push(i);
526                }
527            }
528            return objKeys;
529        } else {
530            return Object.keys(obj);
531        }
532    }
533
534    /**
535     * This service allows the various parts of the module to communicate and stay in sync.
536     */
537    function paginationService() {
538
539        var instances = {};
540        var lastRegisteredInstance;
541
542        this.registerInstance = function(instanceId) {
543            if (typeof instances[instanceId] === 'undefined') {
544                instances[instanceId] = {
545                    asyncMode: false
546                };
547                lastRegisteredInstance = instanceId;
548            }
549        };
550
551        this.deregisterInstance = function(instanceId) {
552            delete instances[instanceId];
553        };
554        
555        this.isRegistered = function(instanceId) {
556            return (typeof instances[instanceId] !== 'undefined');
557        };
558
559        this.getLastInstanceId = function() {
560            return lastRegisteredInstance;
561        };
562
563        this.setCurrentPageParser = function(instanceId, val, scope) {
564            instances[instanceId].currentPageParser = val;
565            instances[instanceId].context = scope;
566        };
567        this.setCurrentPage = function(instanceId, val) {
568            instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
569        };
570        this.getCurrentPage = function(instanceId) {
571            var parser = instances[instanceId].currentPageParser;
572            return parser ? parser(instances[instanceId].context) : 1;
573        };
574
575        this.setItemsPerPage = function(instanceId, val) {
576            instances[instanceId].itemsPerPage = val;
577        };
578        this.getItemsPerPage = function(instanceId) {
579            return instances[instanceId].itemsPerPage;
580        };
581
582        this.setCollectionLength = function(instanceId, val) {
583            instances[instanceId].collectionLength = val;
584        };
585        this.getCollectionLength = function(instanceId) {
586            return instances[instanceId].collectionLength;
587        };
588
589        this.setAsyncModeTrue = function(instanceId) {
590            instances[instanceId].asyncMode = true;
591        };
592
593        this.setAsyncModeFalse = function(instanceId) {
594            instances[instanceId].asyncMode = false;
595        };
596
597        this.isAsyncMode = function(instanceId) {
598            return instances[instanceId].asyncMode;
599        };
600    }
601
602    /**
603     * This provider allows global configuration of the template path used by the dir-pagination-controls directive.
604     */
605    function paginationTemplateProvider() {
606
607        var templatePath = 'angularUtils.directives.dirPagination.template';
608        var templateString;
609
610        /**
611         * Set a templateUrl to be used by all instances of <dir-pagination-controls>
612         * @param {String} path
613         */
614        this.setPath = function(path) {
615            templatePath = path;
616        };
617
618        /**
619         * Set a string of HTML to be used as a template by all instances
620         * of <dir-pagination-controls>. If both a path *and* a string have been set,
621         * the string takes precedence.
622         * @param {String} str
623         */
624        this.setString = function(str) {
625            templateString = str;
626        };
627
628        this.$get = function() {
629            return {
630                getPath: function() {
631                    return templatePath;
632                },
633                getString: function() {
634                    return templateString;
635                }
636            };
637        };
638    }
639})();

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.