123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /**
- * dirPagination - AngularJS module for paginating (almost) anything.
- *
- *
- * Credits
- * =======
- *
- * Daniel Tabuenca: https://groups.google.com/d/msg/angular/an9QpzqIYiM/r8v-3W1X5vcJ
- * for the idea on how to dynamically invoke the ng-repeat directive.
- *
- * I borrowed a couple of lines and a few attribute names from the AngularUI Bootstrap project:
- * https://github.com/angular-ui/bootstrap/blob/master/src/pagination/pagination.js
- *
- * Copyright 2014 Michael Bromley <michael@michaelbromley.co.uk>
- */
- (function() {
- /**
- * Config
- */
- var moduleName = 'angularUtils.directives.dirPagination';
- var DEFAULT_ID = '__default';
- /**
- * Module
- */
- var module;
- try {
- module = angular.module(moduleName);
- } catch(err) {
- // named module does not exist, so create one
- module = angular.module(moduleName, []);
- }
- module
- .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective])
- .directive('dirPaginateNoCompile', noCompileDirective)
- .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective])
- .filter('itemsPerPage', ['paginationService', itemsPerPageFilter])
- .service('paginationService', paginationService)
- .provider('paginationTemplate', paginationTemplateProvider)
- .run(['$templateCache',dirPaginationControlsTemplateInstaller]);
- function dirPaginateDirective($compile, $parse, paginationService) {
- return {
- terminal: true,
- multiElement: true,
- compile: dirPaginationCompileFn
- };
- function dirPaginationCompileFn(tElement, tAttrs){
- var expression = tAttrs.dirPaginate;
- // regex taken directly from https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L211
- var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
- var filterPattern = /\|\s*itemsPerPage\s*:[^|]*/;
- if (match[2].match(filterPattern) === null) {
- throw 'pagination directive: the \'itemsPerPage\' filter must be set.';
- }
- var itemsPerPageFilterRemoved = match[2].replace(filterPattern, '');
- var collectionGetter = $parse(itemsPerPageFilterRemoved);
- addNoCompileAttributes(tElement);
- // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
- // dir-pagination-controls directives that may be looking for this ID.
- var rawId = tAttrs.paginationId || DEFAULT_ID;
- paginationService.registerInstance(rawId);
- return function dirPaginationLinkFn(scope, element, attrs){
- // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
- // potentially register a new ID if it evaluates to a different value than the rawId.
- var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
- paginationService.registerInstance(paginationId);
- var repeatExpression = getRepeatExpression(expression, paginationId);
- addNgRepeatToElement(element, attrs, repeatExpression);
- removeTemporaryAttributes(element);
- var compiled = $compile(element);
- var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId);
- paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);
- if (typeof attrs.totalItems !== 'undefined') {
- paginationService.setAsyncModeTrue(paginationId);
- scope.$watch(function() {
- return $parse(attrs.totalItems)(scope);
- }, function (result) {
- if (0 <= result) {
- paginationService.setCollectionLength(paginationId, result);
- }
- });
- } else {
- scope.$watchCollection(function() {
- return collectionGetter(scope);
- }, function(collection) {
- if (collection) {
- paginationService.setCollectionLength(paginationId, collection.length);
- }
- });
- }
- // Delegate to the link function returned by the new compilation of the ng-repeat
- compiled(scope);
- };
- }
- /**
- * If a pagination id has been specified, we need to check that it is present as the second argument passed to
- * the itemsPerPage filter. If it is not there, we add it and return the modified expression.
- *
- * @param expression
- * @param paginationId
- * @returns {*}
- */
- function getRepeatExpression(expression, paginationId) {
- var repeatExpression,
- idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);
- if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
- repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:[^|]*)/, "$1 : '" + paginationId + "'");
- } else {
- repeatExpression = expression;
- }
- return repeatExpression;
- }
- /**
- * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the
- * appropriate multi-element ng-repeat to the first and last element in the range.
- * @param element
- * @param attrs
- * @param repeatExpression
- */
- function addNgRepeatToElement(element, attrs, repeatExpression) {
- if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) {
- // using multiElement mode (dir-paginate-start, dir-paginate-end)
- attrs.$set('ngRepeatStart', repeatExpression);
- element.eq(element.length - 1).attr('ng-repeat-end', true);
- } else {
- attrs.$set('ngRepeat', repeatExpression);
- }
- }
- /**
- * Adds the dir-paginate-no-compile directive to each element in the tElement range.
- * @param tElement
- */
- function addNoCompileAttributes(tElement) {
- angular.forEach(tElement, function(el) {
- if (el.nodeType === Node.ELEMENT_NODE) {
- angular.element(el).attr('dir-paginate-no-compile', true);
- }
- });
- }
- /**
- * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives.
- * @param element
- */
- function removeTemporaryAttributes(element) {
- angular.forEach(element, function(el) {
- if (el.nodeType === Node.ELEMENT_NODE) {
- angular.element(el).removeAttr('dir-paginate-no-compile');
- }
- });
- element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate');
- element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end');
- }
- /**
- * Creates a getter function for the current-page attribute, using the expression provided or a default value if
- * no current-page expression was specified.
- *
- * @param scope
- * @param attrs
- * @param paginationId
- * @returns {*}
- */
- function makeCurrentPageGetterFn(scope, attrs, paginationId) {
- var currentPageGetter;
- if (attrs.currentPage) {
- currentPageGetter = $parse(attrs.currentPage);
- } else {
- // if the current-page attribute was not set, we'll make our own
- var defaultCurrentPage = paginationId + '__currentPage';
- scope[defaultCurrentPage] = 1;
- currentPageGetter = $parse(defaultCurrentPage);
- }
- return currentPageGetter;
- }
- }
- /**
- * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end).
- * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of
- * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled.
- */
- function noCompileDirective() {
- return {
- priority: 5000,
- terminal: true
- };
- }
- function dirPaginationControlsTemplateInstaller($templateCache) {
- //往里面 拼入html页 分页
- $templateCache.put('angularUtils.directives.dirPagination.template', '<ul class="pagination" ng-if="1 < pages.length"><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }"><a ng-click="setCurrent(1)">首页</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a ng-click="setCurrent(pagination.current - 1)"><上一页</a></li><li ng-repeat="pageNumber in pages track by $index" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' }"><a ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a ng-click="setCurrent(pagination.current + 1)">下一页></a></li><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a ng-click="setCurrent(pagination.last)">尾页</a></li></ul>');
- //$templateCache.put('angularUtils.directives.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)">首页</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(pagination.current - 1)"><上一页</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)">下一页></a></li><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)">尾页</a></li></ul>');
- //$templateCache.put('angularUtils.directives.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)">首页</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(pagination.current - 1)"><上一页</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)">下一页></a></li><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)">尾页</a></li><li><select><option value="10">10</option><option value="12">12</option><option value="15">15</option><option value="18">18</option><option value="20">20</option></select></li><li><label>共{{ctl.total_count}}条数据</label></li></ul>');
- }
- function dirPaginationControlsDirective(paginationService, paginationTemplate) {
- var numberRegex = /^\d+$/;
- return {
- restrict: 'AE',
- templateUrl: function(elem, attrs) {
- return attrs.templateUrl || paginationTemplate.getPath();
- },
- scope: {
- maxSize: '=?',
- onPageChange: '&?',
- paginationId: '=?'
- },
- link: dirPaginationControlsLinkFn
- };
- function dirPaginationControlsLinkFn(scope, element, attrs) {
- // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
- // 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
- // no corresponding dir-paginate directive and wrongly throwing an exception.
- var rawId = attrs.paginationId || DEFAULT_ID;
- var paginationId = scope.paginationId || attrs.paginationId || DEFAULT_ID;
- if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
- var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' ';
- throw 'pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive.';
- }
- if (!scope.maxSize) { scope.maxSize = 9; }
- scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
- scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;
- var paginationRange = Math.max(scope.maxSize, 5);
- scope.pages = [];
- scope.pagination = {
- last: 1,
- current: 1
- };
- scope.range = {
- lower: 1,
- upper: 1,
- total: 1
- };
- scope.$watch(function() {
- return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
- }, function(length) {
- if (0 < length) {
- generatePagination();
- }
- });
- scope.$watch(function() {
- return (paginationService.getItemsPerPage(paginationId));
- }, function(current, previous) {
- if (current != previous && typeof previous !== 'undefined') {
- goToPage(scope.pagination.current);
- }
- });
- scope.$watch(function() {
- return paginationService.getCurrentPage(paginationId);
- }, function(currentPage, previousPage) {
- if (currentPage != previousPage) {
- goToPage(currentPage);
- }
- });
- scope.setCurrent = function (num) {
- if (isValidPageNumber(num)) {
- num = parseInt(num, 10);
- paginationService.setCurrentPage(paginationId, num);
- }
- };
- function goToPage(num) {
- if (isValidPageNumber(num)) {
- scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
- scope.pagination.current = num;
- updateRangeValues();
- // if a callback has been set, then call it with the page number as an argument
- if (scope.onPageChange) {
- scope.onPageChange({ newPageNumber : num });
- }
- }
- }
- function generatePagination() {
- var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;
- scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
- scope.pagination.current = page;
- scope.pagination.last = scope.pages[scope.pages.length - 1];
- if (scope.pagination.last < scope.pagination.current) {
- scope.setCurrent(scope.pagination.last);
- } else {
- updateRangeValues();
- }
- }
- /**
- * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
- * template to display the current page range, e.g. "showing 21 - 40 of 144 results";
- */
- function updateRangeValues() {
- var currentPage = paginationService.getCurrentPage(paginationId),
- itemsPerPage = paginationService.getItemsPerPage(paginationId),
- totalItems = paginationService.getCollectionLength(paginationId);
- scope.range.lower = (currentPage - 1) * itemsPerPage + 1;
- scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
- scope.range.total = totalItems;
- }
- function isValidPageNumber(num) {
- return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
- }
- }
- /**
- * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
- * links used in pagination
- *
- * @param currentPage
- * @param rowsPerPage
- * @param paginationRange
- * @param collectionLength
- * @returns {Array}
- */
- function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
- var pages = [];
- var totalPages = Math.ceil(collectionLength / rowsPerPage);
- var halfWay = Math.ceil(paginationRange / 2);
- var position;
- if (currentPage <= halfWay) {
- position = 'start';
- } else if (totalPages - halfWay < currentPage) {
- position = 'end';
- } else {
- position = 'middle';
- }
- var ellipsesNeeded = paginationRange < totalPages;
- var i = 1;
- while (i <= totalPages && i <= paginationRange) {
- var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);
- var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end'));
- var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start'));
- if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
- pages.push('...');
- } else {
- pages.push(pageNumber);
- }
- i ++;
- }
- return pages;
- }
- /**
- * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
- *
- * @param i
- * @param currentPage
- * @param paginationRange
- * @param totalPages
- * @returns {*}
- */
- function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
- var halfWay = Math.ceil(paginationRange/2);
- if (i === paginationRange) {
- return totalPages;
- } else if (i === 1) {
- return i;
- } else if (paginationRange < totalPages) {
- if (totalPages - halfWay < currentPage) {
- return totalPages - paginationRange + i;
- } else if (halfWay < currentPage) {
- return currentPage - halfWay + i;
- } else {
- return i;
- }
- } else {
- return i;
- }
- }
- }
- /**
- * This filter slices the collection into pages based on the current page number and number of items per page.
- * @param paginationService
- * @returns {Function}
- */
- function itemsPerPageFilter(paginationService) {
- return function(collection, itemsPerPage, paginationId) {
- if (typeof (paginationId) === 'undefined') {
- paginationId = DEFAULT_ID;
- }
- if (!paginationService.isRegistered(paginationId)) {
- throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.';
- }
- var end;
- var start;
- if (collection instanceof Array) {
- itemsPerPage = parseInt(itemsPerPage) || 9999999999;
- if (paginationService.isAsyncMode(paginationId)) {
- start = 0;
- } else {
- start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage;
- }
- end = start + itemsPerPage;
- paginationService.setItemsPerPage(paginationId, itemsPerPage);
- return collection.slice(start, end);
- } else {
- return collection;
- }
- };
- }
- /**
- * This service allows the various parts of the module to communicate and stay in sync.
- */
- function paginationService() {
- var instances = {};
- var lastRegisteredInstance;
- this.registerInstance = function(instanceId) {
- if (typeof instances[instanceId] === 'undefined') {
- instances[instanceId] = {
- asyncMode: false
- };
- lastRegisteredInstance = instanceId;
- }
- };
- this.isRegistered = function(instanceId) {
- return (typeof instances[instanceId] !== 'undefined');
- };
- this.getLastInstanceId = function() {
- return lastRegisteredInstance;
- };
- this.setCurrentPageParser = function(instanceId, val, scope) {
- instances[instanceId].currentPageParser = val;
- instances[instanceId].context = scope;
- };
- this.setCurrentPage = function(instanceId, val) {
- instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
- };
- this.getCurrentPage = function(instanceId) {
- var parser = instances[instanceId].currentPageParser;
- return parser ? parser(instances[instanceId].context) : 1;
- };
- this.setItemsPerPage = function(instanceId, val) {
- instances[instanceId].itemsPerPage = val;
- };
- this.getItemsPerPage = function(instanceId) {
- return instances[instanceId].itemsPerPage;
- };
- this.setCollectionLength = function(instanceId, val) {
- instances[instanceId].collectionLength = val;
- };
- this.getCollectionLength = function(instanceId) {
- return instances[instanceId].collectionLength;
- };
- this.setAsyncModeTrue = function(instanceId) {
- instances[instanceId].asyncMode = true;
- };
- this.isAsyncMode = function(instanceId) {
- return instances[instanceId].asyncMode;
- };
- }
- /**
- * This provider allows global configuration of the template path used by the dir-pagination-controls directive.
- */
- function paginationTemplateProvider() {
- var templatePath = 'angularUtils.directives.dirPagination.template';
- this.setPath = function(path) {
- templatePath = path;
- };
- this.$get = function() {
- return {
- getPath: function() {
- return templatePath;
- }
- };
- };
- }
- })();
|