(function(angular, factory) { 'use strict'; if (typeof define === 'function' && define.amd) { define(['angular'], function(angular) { return factory(angular); }); } else { return factory(angular); } }(angular || null, function(angular) { 'use strict'; /** * ngTable: Table + Angular JS * * @author Vitalii Savchuk * @url https://github.com/esvit/ng-table/ * @license New BSD License */ /** * @ngdoc module * @name ngTable * @description ngTable: Table + Angular JS * @example
{{user.name}} {{user.age}}
*/ var app = angular.module('ngTable', []); /** * ngTable: Table + Angular JS * * @author Vitalii Savchuk * @url https://github.com/esvit/ng-table/ * @license New BSD License */ /** * @ngdoc value * @name ngTable.value:ngTableDefaultParams * @description Default Parameters for ngTable */ app.value('ngTableDefaults', { params: {}, settings: {} }); /** * @ngdoc service * @name ngTable.factory:ngTableParams * @description Parameters manager for ngTable */ app.factory('ngTableParams', ['$q', '$log', 'ngTableDefaults', function($q, $log, ngTableDefaults) { var isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n); }; var ngTableParams = function(baseParameters, baseSettings) { var self = this, log = function() { if (settings.debugMode && $log.debug) { $log.debug.apply(this, arguments); } }; this.data = []; /** * @ngdoc method * @name ngTable.factory:ngTableParams#parameters * @methodOf ngTable.factory:ngTableParams * @description Set new parameters or get current parameters * * @param {string} newParameters New parameters * @param {string} parseParamsFromUrl Flag if parse parameters like in url * @returns {Object} Current parameters or `this` */ this.parameters = function(newParameters, parseParamsFromUrl) { parseParamsFromUrl = parseParamsFromUrl || false; if (angular.isDefined(newParameters)) { for (var key in newParameters) { var value = newParameters[key]; if (parseParamsFromUrl && key.indexOf('[') >= 0) { var keys = key.split(/\[(.*)\]/).reverse() var lastKey = ''; for (var i = 0, len = keys.length; i < len; i++) { var name = keys[i]; if (name !== '') { var v = value; value = {}; value[lastKey = name] = (isNumber(v) ? parseFloat(v) : v); } } if (lastKey === 'sorting') { params[lastKey] = {}; } params[lastKey] = angular.extend(params[lastKey] || {}, value[lastKey]); } else { params[key] = (isNumber(newParameters[key]) ? parseFloat(newParameters[key]) : newParameters[key]); } } log('ngTable: set parameters', params); return this; } return params; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#settings * @methodOf ngTable.factory:ngTableParams * @description Set new settings for table * * @param {string} newSettings New settings or undefined * @returns {Object} Current settings or `this` */ this.settings = function(newSettings) { if (angular.isDefined(newSettings)) { if (angular.isArray(newSettings.data)) { //auto-set the total from passed in data newSettings.total = newSettings.data.length; } settings = angular.extend(settings, newSettings); log('ngTable: set settings', settings); return this; } return settings; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#page * @methodOf ngTable.factory:ngTableParams * @description If parameter page not set return current page else set current page * * @param {string} page Page number * @returns {Object|Number} Current page or `this` */ this.page = function(page) { return angular.isDefined(page) ? this.parameters({ 'page': page }) : params.page; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#total * @methodOf ngTable.factory:ngTableParams * @description If parameter total not set return current quantity else set quantity * * @param {string} total Total quantity of items * @returns {Object|Number} Current page or `this` */ this.total = function(total) { return angular.isDefined(total) ? this.settings({ 'total': total }) : settings.total; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#count * @methodOf ngTable.factory:ngTableParams * @description If parameter count not set return current count per page else set count per page * * @param {string} count Count per number * @returns {Object|Number} Count per page or `this` */ this.count = function(count) { // reset to first page because can be blank page return angular.isDefined(count) ? this.parameters({ 'count': count, 'page': 1 }) : params.count; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#filter * @methodOf ngTable.factory:ngTableParams * @description If parameter page not set return current filter else set current filter * * @param {string} filter New filter * @returns {Object} Current filter or `this` */ this.filter = function(filter) { return angular.isDefined(filter) ? this.parameters({ 'filter': filter, 'page': 1 }) : params.filter; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#sorting * @methodOf ngTable.factory:ngTableParams * @description If 'sorting' parameter is not set, return current sorting. Otherwise set current sorting. * * @param {string} sorting New sorting * @returns {Object} Current sorting or `this` */ this.sorting = function(sorting) { if (arguments.length == 2) { var sortArray = {}; sortArray[sorting] = arguments[1]; this.parameters({ 'sorting': sortArray }); return this; } return angular.isDefined(sorting) ? this.parameters({ 'sorting': sorting }) : params.sorting; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#isSortBy * @methodOf ngTable.factory:ngTableParams * @description Checks sort field * * @param {string} field Field name * @param {string} direction Direction of sorting 'asc' or 'desc' * @returns {Array} Return true if field sorted by direction */ this.isSortBy = function(field, direction) { return angular.isDefined(params.sorting[field]) && angular.equals(params.sorting[field], direction); }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#orderBy * @methodOf ngTable.factory:ngTableParams * @description Return object of sorting parameters for angular filter * * @returns {Array} Array like: [ '-name', '+age' ] */ this.orderBy = function() { var sorting = []; for (var column in params.sorting) { sorting.push((params.sorting[column] === "asc" ? "+" : "-") + column); } return sorting; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#getData * @methodOf ngTable.factory:ngTableParams * @description Called when updated some of parameters for get new data * * @param {Object} $defer promise object * @param {Object} params New parameters */ this.getData = function($defer, params) { if (angular.isArray(this.data) && angular.isObject(params)) { $defer.resolve(this.data.slice((params.page() - 1) * params.count(), params.page() * params.count())); } else { $defer.resolve([]); } return $defer.promise; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#getGroups * @methodOf ngTable.factory:ngTableParams * @description Return groups for table grouping */ this.getGroups = function($defer, column) { var defer = $q.defer(); defer.promise.then(function(data) { var groups = {}; angular.forEach(data, function(item) { var groupName = angular.isFunction(column) ? column(item) : item[column]; groups[groupName] = groups[groupName] || { data: [] }; groups[groupName]['value'] = groupName; groups[groupName].data.push(item); }); var result = []; for (var i in groups) { result.push(groups[i]); } log('ngTable: refresh groups', result); $defer.resolve(result); }); return this.getData(defer, self); }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#generatePagesArray * @methodOf ngTable.factory:ngTableParams * @description Generate array of pages * * @param {boolean} currentPage which page must be active * @param {boolean} totalItems Total quantity of items * @param {boolean} pageSize Quantity of items on page * @returns {Array} Array of pages */ this.generatePagesArray = function(currentPage, totalItems, pageSize) { var maxBlocks, maxPage, maxPivotPages, minPage, numPages, pages; maxBlocks = 11; pages = []; numPages = Math.ceil(totalItems / pageSize); if (numPages > 1) { pages.push({ type: 'prev', number: Math.max(1, currentPage - 1), active: currentPage > 1 }); pages.push({ type: 'first', number: 1, active: currentPage > 1, current: currentPage === 1 }); maxPivotPages = Math.round((maxBlocks - 5) / 2); minPage = Math.max(2, currentPage - maxPivotPages); maxPage = Math.min(numPages - 1, currentPage + maxPivotPages * 2 - (currentPage - minPage)); minPage = Math.max(2, minPage - (maxPivotPages * 2 - (maxPage - minPage))); var i = minPage; while (i <= maxPage) { if ((i === minPage && i !== 2) || (i === maxPage && i !== numPages - 1)) { pages.push({ type: 'more', active: false }); } else { pages.push({ type: 'page', number: i, active: currentPage !== i, current: currentPage === i }); } i++; } pages.push({ type: 'last', number: numPages, active: currentPage !== numPages, current: currentPage === numPages }); pages.push({ type: 'next', number: Math.min(numPages, currentPage + 1), active: currentPage < numPages }); } return pages; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#url * @methodOf ngTable.factory:ngTableParams * @description Return groups for table grouping * * @param {boolean} asString flag indicates return array of string or object * @returns {Array} If asString = true will be return array of url string parameters else key-value object */ this.url = function(asString) { asString = asString || false; var pairs = (asString ? [] : {}); for (var key in params) { if (params.hasOwnProperty(key)) { var item = params[key], name = encodeURIComponent(key); if (typeof item === "object") { for (var subkey in item) { if (!angular.isUndefined(item[subkey]) && item[subkey] !== "") { var pname = name + "[" + encodeURIComponent(subkey) + "]"; if (asString) { pairs.push(pname + "=" + item[subkey]); } else { pairs[pname] = item[subkey]; } } } } else if (!angular.isFunction(item) && !angular.isUndefined(item) && item !== "") { if (asString) { pairs.push(name + "=" + encodeURIComponent(item)); } else { pairs[name] = encodeURIComponent(item); } } } } return pairs; }; /** * @ngdoc method * @name ngTable.factory:ngTableParams#reload * @methodOf ngTable.factory:ngTableParams * @description Reload table data */ this.reload = function() { var $defer = $q.defer(), self = this, pData = null; if (!settings.$scope) { return; } settings.$loading = true; if (settings.groupBy) { pData = settings.getGroups($defer, settings.groupBy, this); } else { pData = settings.getData($defer, this); } log('ngTable: reload data'); if (!pData) { // If getData resolved the $defer, and didn't promise us data, // create a promise from the $defer. We need to return a promise. pData = $defer.promise; } return pData.then(function(data) { settings.$loading = false; log('ngTable: current scope', settings.$scope); if (settings.groupBy) { self.data = data; if (settings.$scope) settings.$scope.$groups = data; } else { self.data = data; if (settings.$scope) settings.$scope.$data = data; } if (settings.$scope) settings.$scope.pages = self.generatePagesArray(self.page(), self.total(), self.count()); settings.$scope.$emit('ngTableAfterReloadData'); return data; }); }; this.reloadPages = function() { var self = this; settings.$scope.pages = self.generatePagesArray(self.page(), self.total(), self.count()); }; var params = this.$params = { page: 1, count: 1, filter: {}, sorting: {}, group: {}, groupBy: null }; angular.extend(params, ngTableDefaults.params); var settings = { $scope: null, // set by ngTable controller $loading: false, data: null, //allows data to be set when table is initialized total: 0, defaultSort: 'desc', filterDelay: 750, counts: [10, 25, 50, 100], getGroups: this.getGroups, getData: this.getData }; angular.extend(settings, ngTableDefaults.settings); this.settings(baseSettings); this.parameters(baseParameters, true); return this; }; return ngTableParams; }]); /** * ngTable: Table + Angular JS * * @author Vitalii Savchuk * @url https://github.com/esvit/ng-table/ * @license New BSD License */ /** * @ngdoc object * @name ngTable.directive:ngTable.ngTableController * * @description * Each {@link ngTable.directive:ngTable ngTable} directive creates an instance of `ngTableController` */ var ngTableController = ['$scope', 'ngTableParams', '$timeout', function($scope, ngTableParams, $timeout) { var isFirstTimeLoad = true; $scope.$loading = false; if (!$scope.hasOwnProperty("params")) { $scope.params = new ngTableParams(); $scope.params.isNullInstance = true; } $scope.params.settings().$scope = $scope; var delayFilter = (function() { var timer = 0; return function(callback, ms) { $timeout.cancel(timer); timer = $timeout(callback, ms); }; })(); function resetPage() { $scope.params.$params.page = 1; } $scope.$watch('params.$params', function(newParams, oldParams) { if (newParams === oldParams) { return; } $scope.params.settings().$scope = $scope; if (!angular.equals(newParams.filter, oldParams.filter)) { var maybeResetPage = isFirstTimeLoad ? angular.noop : resetPage; delayFilter(function() { maybeResetPage(); $scope.params.reload(); }, $scope.params.settings().filterDelay); } else { $scope.params.reload(); } if (!$scope.params.isNullInstance) { isFirstTimeLoad = false; } }, true); $scope.sortBy = function(column, event) { var parsedSortable = $scope.parse(column.sortable); if (!parsedSortable) { return; } var defaultSort = $scope.params.settings().defaultSort; var inverseSort = (defaultSort === 'asc' ? 'desc' : 'asc'); var sorting = $scope.params.sorting() && $scope.params.sorting()[parsedSortable] && ($scope.params.sorting()[parsedSortable] === defaultSort); var sortingParams = (event.ctrlKey || event.metaKey) ? $scope.params.sorting() : {}; sortingParams[parsedSortable] = (sorting ? inverseSort : defaultSort); $scope.params.parameters({ sorting: sortingParams }); }; }]; /** * ngTable: Table + Angular JS * * @author Vitalii Savchuk * @url https://github.com/esvit/ng-table/ * @license New BSD License */ /** * @ngdoc directive * @name ngTable.directive:ngTable * @restrict A * * @description * Directive that instantiates {@link ngTable.directive:ngTable.ngTableController ngTableController}. */ app.directive('ngTable', ['$compile', '$q', '$parse', function($compile, $q, $parse) { 'use strict'; return { restrict: 'A', priority: 1001, scope: true, controller: ngTableController, compile: function(element) { var columns = [], i = 0, row = null; // custom header var thead = element.find('> thead'); // IE 8 fix :not(.ng-table-group) selector angular.forEach(angular.element(element.find('tr')), function(tr) { tr = angular.element(tr); if (!tr.hasClass('ng-table-group') && !row) { row = tr; } }); if (!row) { return; } angular.forEach(row.find('td'), function(item) { var el = angular.element(item); if (el.attr('ignore-cell') && 'true' === el.attr('ignore-cell')) { return; } var parsedAttribute = function(attr, defaultValue) { return function(scope) { return $parse(el.attr('x-data-' + attr) || el.attr('data-' + attr) || el.attr(attr))(scope, { $columns: columns }) || defaultValue; }; }; var parsedTitle = parsedAttribute('title', ' '), headerTemplateURL = parsedAttribute('header', false), filter = parsedAttribute('filter', false)(), filterTemplateURL = false, filterName = false; if (filter && filter.$$name) { filterName = filter.$$name; delete filter.$$name; } if (filter && filter.templateURL) { filterTemplateURL = filter.templateURL; delete filter.templateURL; } el.attr('data-title-text', parsedTitle()); // this used in responsive table columns.push({ id: i++, title: parsedTitle, sortable: parsedAttribute('sortable', false), 'class': el.attr('x-data-header-class') || el.attr('data-header-class') || el.attr('header-class'), filter: filter, filterTemplateURL: filterTemplateURL, filterName: filterName, headerTemplateURL: headerTemplateURL, filterData: (el.attr("filter-data") ? el.attr("filter-data") : null), show: (el.attr("ng-show") ? function(scope) { return $parse(el.attr("ng-show"))(scope); } : function() { return true; }) }); }); return function(scope, element, attrs) { scope.$loading = false; scope.$columns = columns; scope.$filterRow = {}; scope.$watch(attrs.ngTable, (function(params) { if (angular.isUndefined(params)) { return; } scope.paramsModel = $parse(attrs.ngTable); scope.params = params; }), true); scope.parse = function(text) { return angular.isDefined(text) ? text(scope) : ''; }; if (attrs.showFilter) { scope.$parent.$watch(attrs.showFilter, function(value) { scope.show_filter = value; }); } if (attrs.disableFilter) { scope.$parent.$watch(attrs.disableFilter, function(value) { scope.$filterRow.disabled = value; }); } angular.forEach(columns, function(column) { var def; if (!column.filterData) { return; } def = $parse(column.filterData)(scope, { $column: column }); // if we're working with a deferred object, let's wait for the promise if ((angular.isObject(def) && angular.isObject(def.promise))) { delete column.filterData; return def.promise.then(function(data) { // our deferred can eventually return arrays, functions and objects if (!angular.isArray(data) && !angular.isFunction(data) && !angular.isObject(data)) { // if none of the above was found - we just want an empty array data = []; } else if (angular.isArray(data)) { data.unshift({ title: '-', id: '' }); } column.data = data; }); } // otherwise, we just return what the user gave us. It could be a function, array, object, whatever else { return column.data = def; } }); if (!element.hasClass('ng-table')) { scope.templates = { header: (attrs.templateHeader ? attrs.templateHeader : 'ng-table/header.html'), pagination: (attrs.templatePagination ? attrs.templatePagination : 'ng-table/pager.html') }; var headerTemplate = thead.length > 0 ? thead : angular.element(document.createElement('thead')).attr('ng-include', 'templates.header'); var paginationTemplate = angular.element(document.createElement('div')).attr({ 'ng-table-pagination': 'params', 'template-url': 'templates.pagination' }); element.find('> thead').remove(); element.addClass('ng-table') .prepend(headerTemplate) .after(paginationTemplate); $compile(headerTemplate)(scope); $compile(paginationTemplate)(scope); } }; } } } ]); /** * ngTable: Table + Angular JS * * @author Vitalii Savchuk * @url https://github.com/esvit/ng-table/ * @license New BSD License */ /** * @ngdoc directive * @name ngTable.directive:ngTablePagination * @restrict A */ app.directive('ngTablePagination', ['$compile', function($compile) { 'use strict'; return { restrict: 'A', scope: { 'params': '=ngTablePagination', 'templateUrl': '=' }, replace: false, link: function(scope, element, attrs) { scope.params.settings().$scope.$on('ngTableAfterReloadData', function() { scope.pages = scope.params.generatePagesArray(scope.params.page(), scope.params.total(), scope.params.count()); }, true); scope.$watch('templateUrl', function(templateUrl) { if (angular.isUndefined(templateUrl)) { return; } var template = angular.element(document.createElement('div')) template.attr({ 'ng-include': 'templateUrl' }); element.append(template); $compile(template)(scope); }); } }; } ]); angular.module('ngTable').run(['$templateCache', function ($templateCache) { $templateCache.put('ng-table/filters/select-multiple.html', ''); $templateCache.put('ng-table/filters/select.html', ''); $templateCache.put('ng-table/filters/text.html', ''); $templateCache.put('ng-table/header.html', '
'); $templateCache.put('ng-table/pager.html', ' '); }]); return app; }));