PageSourceSearch

https://sharetheride.com/js/libs/dirPagination.js

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

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.