ng-table.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. (function(angular, factory) {
  2. 'use strict';
  3. if (typeof define === 'function' && define.amd) {
  4. define(['angular'], function(angular) {
  5. return factory(angular);
  6. });
  7. } else {
  8. return factory(angular);
  9. }
  10. }(angular || null, function(angular) {
  11. 'use strict';
  12. /**
  13. * ngTable: Table + Angular JS
  14. *
  15. * @author Vitalii Savchuk <esvit666@gmail.com>
  16. * @url https://github.com/esvit/ng-table/
  17. * @license New BSD License <http://creativecommons.org/licenses/BSD/>
  18. */
  19. /**
  20. * @ngdoc module
  21. * @name ngTable
  22. * @description ngTable: Table + Angular JS
  23. * @example
  24. <doc:example>
  25. <doc:source>
  26. <script>
  27. var app = angular.module('myApp', ['ngTable']);
  28. app.controller('MyCtrl', function($scope) {
  29. $scope.users = [
  30. {name: "Moroni", age: 50},
  31. {name: "Tiancum", age: 43},
  32. {name: "Jacob", age: 27},
  33. {name: "Nephi", age: 29},
  34. {name: "Enos", age: 34}
  35. ];
  36. });
  37. </script>
  38. <table ng-table class="table">
  39. <tr ng-repeat="user in users">
  40. <td data-title="'Name'">{{user.name}}</td>
  41. <td data-title="'Age'">{{user.age}}</td>
  42. </tr>
  43. </table>
  44. </doc:source>
  45. </doc:example>
  46. */
  47. var app = angular.module('ngTable', []);
  48. /**
  49. * ngTable: Table + Angular JS
  50. *
  51. * @author Vitalii Savchuk <esvit666@gmail.com>
  52. * @url https://github.com/esvit/ng-table/
  53. * @license New BSD License <http://creativecommons.org/licenses/BSD/>
  54. */
  55. /**
  56. * @ngdoc value
  57. * @name ngTable.value:ngTableDefaultParams
  58. * @description Default Parameters for ngTable
  59. */
  60. app.value('ngTableDefaults', {
  61. params: {},
  62. settings: {}
  63. });
  64. /**
  65. * @ngdoc service
  66. * @name ngTable.factory:NgTableParams
  67. * @description Parameters manager for ngTable
  68. */
  69. app.factory('NgTableParams', ['$q', '$log', 'ngTableDefaults', function($q, $log, ngTableDefaults) {
  70. var isNumber = function(n) {
  71. return !isNaN(parseFloat(n)) && isFinite(n);
  72. };
  73. var NgTableParams = function(baseParameters, baseSettings) {
  74. var self = this,
  75. log = function() {
  76. if (settings.debugMode && $log.debug) {
  77. $log.debug.apply(this, arguments);
  78. }
  79. };
  80. this.data = [];
  81. /**
  82. * @ngdoc method
  83. * @name ngTable.factory:NgTableParams#parameters
  84. * @methodOf ngTable.factory:NgTableParams
  85. * @description Set new parameters or get current parameters
  86. *
  87. * @param {string} newParameters New parameters
  88. * @param {string} parseParamsFromUrl Flag if parse parameters like in url
  89. * @returns {Object} Current parameters or `this`
  90. */
  91. this.parameters = function(newParameters, parseParamsFromUrl) {
  92. parseParamsFromUrl = parseParamsFromUrl || false;
  93. if (angular.isDefined(newParameters)) {
  94. for (var key in newParameters) {
  95. var value = newParameters[key];
  96. if (parseParamsFromUrl && key.indexOf('[') >= 0) {
  97. var keys = key.split(/\[(.*)\]/).reverse()
  98. var lastKey = '';
  99. for (var i = 0, len = keys.length; i < len; i++) {
  100. var name = keys[i];
  101. if (name !== '') {
  102. var v = value;
  103. value = {};
  104. value[lastKey = name] = (isNumber(v) ? parseFloat(v) : v);
  105. }
  106. }
  107. if (lastKey === 'sorting') {
  108. params[lastKey] = {};
  109. }
  110. params[lastKey] = angular.extend(params[lastKey] || {}, value[lastKey]);
  111. } else {
  112. params[key] = (isNumber(newParameters[key]) ? parseFloat(newParameters[key]) : newParameters[key]);
  113. }
  114. }
  115. log('ngTable: set parameters', params);
  116. return this;
  117. }
  118. return params;
  119. };
  120. /**
  121. * @ngdoc method
  122. * @name ngTable.factory:NgTableParams#settings
  123. * @methodOf ngTable.factory:NgTableParams
  124. * @description Set new settings for table
  125. *
  126. * @param {string} newSettings New settings or undefined
  127. * @returns {Object} Current settings or `this`
  128. */
  129. this.settings = function(newSettings) {
  130. if (angular.isDefined(newSettings)) {
  131. if (angular.isArray(newSettings.data)) {
  132. //auto-set the total from passed in data
  133. newSettings.total = newSettings.data.length;
  134. }
  135. settings = angular.extend(settings, newSettings);
  136. log('ngTable: set settings', settings);
  137. return this;
  138. }
  139. return settings;
  140. };
  141. /**
  142. * @ngdoc method
  143. * @name ngTable.factory:NgTableParams#page
  144. * @methodOf ngTable.factory:NgTableParams
  145. * @description If parameter page not set return current page else set current page
  146. *
  147. * @param {string} page Page number
  148. * @returns {Object|Number} Current page or `this`
  149. */
  150. this.page = function(page) {
  151. return angular.isDefined(page) ? this.parameters({
  152. 'page': page
  153. }) : params.page;
  154. };
  155. /**
  156. * @ngdoc method
  157. * @name ngTable.factory:NgTableParams#total
  158. * @methodOf ngTable.factory:NgTableParams
  159. * @description If parameter total not set return current quantity else set quantity
  160. *
  161. * @param {string} total Total quantity of items
  162. * @returns {Object|Number} Current page or `this`
  163. */
  164. this.total = function(total) {
  165. return angular.isDefined(total) ? this.settings({
  166. 'total': total
  167. }) : settings.total;
  168. };
  169. /**
  170. * @ngdoc method
  171. * @name ngTable.factory:NgTableParams#count
  172. * @methodOf ngTable.factory:NgTableParams
  173. * @description If parameter count not set return current count per page else set count per page
  174. *
  175. * @param {string} count Count per number
  176. * @returns {Object|Number} Count per page or `this`
  177. */
  178. this.count = function(count) {
  179. // reset to first page because can be blank page
  180. return angular.isDefined(count) ? this.parameters({
  181. 'count': count,
  182. 'page': 1
  183. }) : params.count;
  184. };
  185. /**
  186. * @ngdoc method
  187. * @name ngTable.factory:NgTableParams#filter
  188. * @methodOf ngTable.factory:NgTableParams
  189. * @description If parameter page not set return current filter else set current filter
  190. *
  191. * @param {string} filter New filter
  192. * @returns {Object} Current filter or `this`
  193. */
  194. this.filter = function(filter) {
  195. return angular.isDefined(filter) ? this.parameters({
  196. 'filter': filter,
  197. 'page': 1
  198. }) : params.filter;
  199. };
  200. /**
  201. * @ngdoc method
  202. * @name ngTable.factory:NgTableParams#sorting
  203. * @methodOf ngTable.factory:NgTableParams
  204. * @description If 'sorting' parameter is not set, return current sorting. Otherwise set current sorting.
  205. *
  206. * @param {string} sorting New sorting
  207. * @returns {Object} Current sorting or `this`
  208. */
  209. this.sorting = function(sorting) {
  210. if (arguments.length == 2) {
  211. var sortArray = {};
  212. sortArray[sorting] = arguments[1];
  213. this.parameters({
  214. 'sorting': sortArray
  215. });
  216. return this;
  217. }
  218. return angular.isDefined(sorting) ? this.parameters({
  219. 'sorting': sorting
  220. }) : params.sorting;
  221. };
  222. /**
  223. * @ngdoc method
  224. * @name ngTable.factory:NgTableParams#isSortBy
  225. * @methodOf ngTable.factory:NgTableParams
  226. * @description Checks sort field
  227. *
  228. * @param {string} field Field name
  229. * @param {string} direction Direction of sorting 'asc' or 'desc'
  230. * @returns {Array} Return true if field sorted by direction
  231. */
  232. this.isSortBy = function(field, direction) {
  233. return angular.isDefined(params.sorting[field]) && angular.equals(params.sorting[field], direction);
  234. };
  235. /**
  236. * @ngdoc method
  237. * @name ngTable.factory:NgTableParams#orderBy
  238. * @methodOf ngTable.factory:NgTableParams
  239. * @description Return object of sorting parameters for angular filter
  240. *
  241. * @returns {Array} Array like: [ '-name', '+age' ]
  242. */
  243. this.orderBy = function() {
  244. var sorting = [];
  245. for (var column in params.sorting) {
  246. sorting.push((params.sorting[column] === "asc" ? "+" : "-") + column);
  247. }
  248. return sorting;
  249. };
  250. /**
  251. * @ngdoc method
  252. * @name ngTable.factory:NgTableParams#getData
  253. * @methodOf ngTable.factory:NgTableParams
  254. * @description Called when updated some of parameters for get new data
  255. *
  256. * @param {Object} $defer promise object
  257. * @param {Object} params New parameters
  258. */
  259. this.getData = function($defer, params) {
  260. if (angular.isArray(this.data) && angular.isObject(params)) {
  261. $defer.resolve(this.data.slice((params.page() - 1) * params.count(), params.page() * params.count()));
  262. } else {
  263. $defer.resolve([]);
  264. }
  265. return $defer.promise;
  266. };
  267. /**
  268. * @ngdoc method
  269. * @name ngTable.factory:NgTableParams#getGroups
  270. * @methodOf ngTable.factory:NgTableParams
  271. * @description Return groups for table grouping
  272. */
  273. this.getGroups = function($defer, column) {
  274. var defer = $q.defer();
  275. defer.promise.then(function(data) {
  276. var groups = {};
  277. angular.forEach(data, function(item) {
  278. var groupName = angular.isFunction(column) ? column(item) : item[column];
  279. groups[groupName] = groups[groupName] || {
  280. data: []
  281. };
  282. groups[groupName]['value'] = groupName;
  283. groups[groupName].data.push(item);
  284. });
  285. var result = [];
  286. for (var i in groups) {
  287. result.push(groups[i]);
  288. }
  289. log('ngTable: refresh groups', result);
  290. $defer.resolve(result);
  291. });
  292. return this.getData(defer, self);
  293. };
  294. /**
  295. * @ngdoc method
  296. * @name ngTable.factory:NgTableParams#generatePagesArray
  297. * @methodOf ngTable.factory:NgTableParams
  298. * @description Generate array of pages
  299. *
  300. * @param {boolean} currentPage which page must be active
  301. * @param {boolean} totalItems Total quantity of items
  302. * @param {boolean} pageSize Quantity of items on page
  303. * @returns {Array} Array of pages
  304. */
  305. this.generatePagesArray = function(currentPage, totalItems, pageSize) {
  306. var maxBlocks, maxPage, maxPivotPages, minPage, numPages, pages;
  307. maxBlocks = 11;
  308. pages = [];
  309. numPages = Math.ceil(totalItems / pageSize);
  310. if (numPages > 1) {
  311. pages.push({
  312. type: 'prev',
  313. number: Math.max(1, currentPage - 1),
  314. active: currentPage > 1
  315. });
  316. pages.push({
  317. type: 'first',
  318. number: 1,
  319. active: currentPage > 1,
  320. current: currentPage === 1
  321. });
  322. maxPivotPages = Math.round((maxBlocks - 5) / 2);
  323. minPage = Math.max(2, currentPage - maxPivotPages);
  324. maxPage = Math.min(numPages - 1, currentPage + maxPivotPages * 2 - (currentPage - minPage));
  325. minPage = Math.max(2, minPage - (maxPivotPages * 2 - (maxPage - minPage)));
  326. var i = minPage;
  327. while (i <= maxPage) {
  328. if ((i === minPage && i !== 2) || (i === maxPage && i !== numPages - 1)) {
  329. pages.push({
  330. type: 'more',
  331. active: false
  332. });
  333. } else {
  334. pages.push({
  335. type: 'page',
  336. number: i,
  337. active: currentPage !== i,
  338. current: currentPage === i
  339. });
  340. }
  341. i++;
  342. }
  343. pages.push({
  344. type: 'last',
  345. number: numPages,
  346. active: currentPage !== numPages,
  347. current: currentPage === numPages
  348. });
  349. pages.push({
  350. type: 'next',
  351. number: Math.min(numPages, currentPage + 1),
  352. active: currentPage < numPages
  353. });
  354. }
  355. return pages;
  356. };
  357. /**
  358. * @ngdoc method
  359. * @name ngTable.factory:NgTableParams#url
  360. * @methodOf ngTable.factory:NgTableParams
  361. * @description Return groups for table grouping
  362. *
  363. * @param {boolean} asString flag indicates return array of string or object
  364. * @returns {Array} If asString = true will be return array of url string parameters else key-value object
  365. */
  366. this.url = function(asString) {
  367. asString = asString || false;
  368. var pairs = (asString ? [] : {});
  369. for (var key in params) {
  370. if (params.hasOwnProperty(key)) {
  371. var item = params[key],
  372. name = encodeURIComponent(key);
  373. if (typeof item === "object") {
  374. for (var subkey in item) {
  375. if (!angular.isUndefined(item[subkey]) && item[subkey] !== "") {
  376. var pname = name + "[" + encodeURIComponent(subkey) + "]";
  377. if (asString) {
  378. pairs.push(pname + "=" + item[subkey]);
  379. } else {
  380. pairs[pname] = item[subkey];
  381. }
  382. }
  383. }
  384. } else if (!angular.isFunction(item) && !angular.isUndefined(item) && item !== "") {
  385. if (asString) {
  386. pairs.push(name + "=" + encodeURIComponent(item));
  387. } else {
  388. pairs[name] = encodeURIComponent(item);
  389. }
  390. }
  391. }
  392. }
  393. return pairs;
  394. };
  395. /**
  396. * @ngdoc method
  397. * @name ngTable.factory:NgTableParams#reload
  398. * @methodOf ngTable.factory:NgTableParams
  399. * @description Reload table data
  400. */
  401. this.reload = function() {
  402. var $defer = $q.defer(),
  403. self = this,
  404. pData = null;
  405. if (!settings.$scope) {
  406. return;
  407. }
  408. settings.$loading = true;
  409. if (settings.groupBy) {
  410. pData = settings.getGroups($defer, settings.groupBy, this);
  411. } else {
  412. pData = settings.getData($defer, this);
  413. }
  414. log('ngTable: reload data');
  415. if (!pData) {
  416. // If getData resolved the $defer, and didn't promise us data,
  417. // create a promise from the $defer. We need to return a promise.
  418. pData = $defer.promise;
  419. }
  420. return pData.then(function(data) {
  421. settings.$loading = false;
  422. log('ngTable: current scope', settings.$scope);
  423. if (settings.groupBy) {
  424. self.data = data;
  425. if (settings.$scope) settings.$scope.$groups = data;
  426. } else {
  427. self.data = data;
  428. if (settings.$scope) settings.$scope.$data = data;
  429. }
  430. if (settings.$scope) settings.$scope.pages = self.generatePagesArray(self.page(), self.total(), self.count());
  431. settings.$scope.$emit('ngTableAfterReloadData');
  432. return data;
  433. });
  434. };
  435. this.reloadPages = function() {
  436. var self = this;
  437. settings.$scope.pages = self.generatePagesArray(self.page(), self.total(), self.count());
  438. };
  439. var params = this.$params = {
  440. page: 1,
  441. count: 1,
  442. filter: {},
  443. sorting: {},
  444. group: {},
  445. groupBy: null
  446. };
  447. angular.extend(params, ngTableDefaults.params);
  448. var settings = {
  449. $scope: null, // set by ngTable controller
  450. $loading: false,
  451. data: null, //allows data to be set when table is initialized
  452. total: 0,
  453. defaultSort: 'desc',
  454. filterDelay: 750,
  455. counts: [10, 25, 50, 100],
  456. sortingIndicator: 'span',
  457. getGroups: this.getGroups,
  458. getData: this.getData
  459. };
  460. angular.extend(settings, ngTableDefaults.settings);
  461. this.settings(baseSettings);
  462. this.parameters(baseParameters, true);
  463. return this;
  464. };
  465. return NgTableParams;
  466. }]);
  467. /**
  468. * @ngdoc service
  469. * @name ngTable.factory:ngTableParams
  470. * @description Backwards compatible shim for lowercase 'n' in NgTableParams
  471. */
  472. app.factory('ngTableParams', ['NgTableParams', function(NgTableParams) {
  473. return NgTableParams;
  474. }]);
  475. /**
  476. * ngTable: Table + Angular JS
  477. *
  478. * @author Vitalii Savchuk <esvit666@gmail.com>
  479. * @url https://github.com/esvit/ng-table/
  480. * @license New BSD License <http://creativecommons.org/licenses/BSD/>
  481. */
  482. /**
  483. * @ngdoc object
  484. * @name ngTable.directive:ngTable.ngTableController
  485. *
  486. * @description
  487. * Each {@link ngTable.directive:ngTable ngTable} directive creates an instance of `ngTableController`
  488. */
  489. app.controller('ngTableController', ['$scope', 'NgTableParams', '$timeout', '$parse', '$compile', '$attrs', '$element',
  490. 'ngTableColumn',
  491. function($scope, NgTableParams, $timeout, $parse, $compile, $attrs, $element, ngTableColumn) {
  492. var isFirstTimeLoad = true;
  493. $scope.$filterRow = {};
  494. $scope.$loading = false;
  495. // until such times as the directive uses an isolated scope, we need to ensure that the check for
  496. // the params field only consults the "own properties" of the $scope. This is to avoid seeing the params
  497. // field on a $scope higher up in the prototype chain
  498. if (!$scope.hasOwnProperty("params")) {
  499. $scope.params = new NgTableParams();
  500. $scope.params.isNullInstance = true;
  501. }
  502. $scope.params.settings().$scope = $scope;
  503. var delayFilter = (function() {
  504. var timer = 0;
  505. return function(callback, ms) {
  506. $timeout.cancel(timer);
  507. timer = $timeout(callback, ms);
  508. };
  509. })();
  510. function resetPage() {
  511. $scope.params.$params.page = 1;
  512. }
  513. $scope.$watch('params.$params', function(newParams, oldParams) {
  514. if (newParams === oldParams) {
  515. return;
  516. }
  517. $scope.params.settings().$scope = $scope;
  518. if (!angular.equals(newParams.filter, oldParams.filter)) {
  519. var maybeResetPage = isFirstTimeLoad ? angular.noop : resetPage;
  520. delayFilter(function() {
  521. maybeResetPage();
  522. $scope.params.reload();
  523. }, $scope.params.settings().filterDelay);
  524. } else {
  525. $scope.params.reload();
  526. }
  527. if (!$scope.params.isNullInstance) {
  528. isFirstTimeLoad = false;
  529. }
  530. }, true);
  531. this.compileDirectiveTemplates = function() {
  532. if (!$element.hasClass('ng-table')) {
  533. $scope.templates = {
  534. header: ($attrs.templateHeader ? $attrs.templateHeader : 'ng-table/header.html'),
  535. pagination: ($attrs.templatePagination ? $attrs.templatePagination : 'ng-table/pager.html')
  536. };
  537. $element.addClass('ng-table');
  538. var headerTemplate = null;
  539. if ($element.find('> thead').length === 0) {
  540. headerTemplate = angular.element(document.createElement('thead')).attr('ng-include', 'templates.header');
  541. $element.prepend(headerTemplate);
  542. }
  543. var paginationTemplate = angular.element(document.createElement('div')).attr({
  544. 'ng-table-pagination': 'params',
  545. 'template-url': 'templates.pagination'
  546. });
  547. $element.before(paginationTemplate);
  548. if (headerTemplate) {
  549. $compile(headerTemplate)($scope);
  550. }
  551. $compile(paginationTemplate)($scope);
  552. }
  553. };
  554. this.loadFilterData = function($columns) {
  555. angular.forEach($columns, function($column) {
  556. var def;
  557. def = $column.filterData($scope, {
  558. $column: $column
  559. });
  560. if (!def) {
  561. delete $column.filterData;
  562. return;
  563. }
  564. // if we're working with a deferred object, let's wait for the promise
  565. if ((angular.isObject(def) && angular.isObject(def.promise))) {
  566. delete $column.filterData;
  567. return def.promise.then(function(data) {
  568. // our deferred can eventually return arrays, functions and objects
  569. if (!angular.isArray(data) && !angular.isFunction(data) && !angular.isObject(data)) {
  570. // if none of the above was found - we just want an empty array
  571. data = [];
  572. } else if (angular.isArray(data)) {
  573. data.unshift({
  574. title: '-',
  575. id: ''
  576. });
  577. }
  578. $column.data = data;
  579. });
  580. }
  581. // otherwise, we just return what the user gave us. It could be a function, array, object, whatever
  582. else {
  583. return $column.data = def;
  584. }
  585. });
  586. };
  587. this.buildColumns = function(columns) {
  588. return columns.map(function(col) {
  589. return ngTableColumn.buildColumn(col, $scope)
  590. })
  591. };
  592. this.setupBindingsToInternalScope = function(tableParamsExpr) {
  593. // note: this we're setting up watches to simulate angular's isolated scope bindings
  594. // note: is REALLY important to watch for a change to the ngTableParams *reference* rather than
  595. // $watch for value equivalence. This is because ngTableParams references the current page of data as
  596. // a field and it's important not to watch this
  597. var tableParamsGetter = $parse(tableParamsExpr);
  598. $scope.$watch(tableParamsGetter, (function(params) {
  599. if (angular.isUndefined(params)) {
  600. return;
  601. }
  602. $scope.paramsModel = tableParamsGetter;
  603. $scope.params = params;
  604. }), false);
  605. if ($attrs.showFilter) {
  606. console.log($attrs)
  607. $scope.$parent.$watch($attrs.showFilter, function(value) {
  608. $scope.show_filter = value;
  609. });
  610. }
  611. if ($attrs.disableFilter) {
  612. $scope.$parent.$watch($attrs.disableFilter, function(value) {
  613. $scope.$filterRow.disabled = value;
  614. });
  615. }
  616. };
  617. $scope.sortBy = function($column, event) {
  618. var parsedSortable = $column.sortable && $column.sortable();
  619. if (!parsedSortable) {
  620. return;
  621. }
  622. var defaultSort = $scope.params.settings().defaultSort;
  623. var inverseSort = (defaultSort === 'asc' ? 'desc' : 'asc');
  624. var sorting = $scope.params.sorting() && $scope.params.sorting()[parsedSortable] && ($scope.params.sorting()[parsedSortable] === defaultSort);
  625. var sortingParams = (event.ctrlKey || event.metaKey) ? $scope.params.sorting() : {};
  626. sortingParams[parsedSortable] = (sorting ? inverseSort : defaultSort);
  627. $scope.params.parameters({
  628. sorting: sortingParams
  629. });
  630. };
  631. }
  632. ]);
  633. /**
  634. * @ngdoc service
  635. * @name ngTable.factory:ngTableColumn
  636. *
  637. * @description
  638. * Service to construct a $column definition used by {@link ngTable.directive:ngTable ngTable} directive
  639. */
  640. app.factory('ngTableColumn', [function() {
  641. var defaults = {
  642. 'class': function() { return ''; },
  643. filter: function() { return false; },
  644. filterData: angular.noop,
  645. headerTemplateURL: function() { return false; },
  646. headerTitle: function() { return ' '; },
  647. sortable: function() { return false; },
  648. show: function() { return true; },
  649. title: function() { return ' '; },
  650. titleAlt: function() { return ''; }
  651. };
  652. /**
  653. * @ngdoc method
  654. * @name ngTable.factory:ngTableColumn#buildColumn
  655. * @methodOf ngTable.factory:ngTableColumn
  656. * @description Creates a $column for use within a header template
  657. *
  658. * @param {Object} column an existing $column or simple column data object
  659. * @param {Scope} defaultScope the $scope to supply to the $column getter methods when not supplied by caller
  660. * @returns {Object} a $column object
  661. */
  662. function buildColumn(column, defaultScope) {
  663. // note: we're not modifying the original column object. This helps to avoid unintended side affects
  664. var extendedCol = Object.create(column);
  665. for (var prop in defaults) {
  666. if (extendedCol[prop] === undefined) {
  667. extendedCol[prop] = defaults[prop];
  668. }
  669. if (!angular.isFunction(extendedCol[prop])) {
  670. // wrap raw field values with "getter" functions
  671. // - this is to ensure consistency with how ngTable.compile builds columns
  672. // - note that the original column object is being "proxied"; this is important
  673. // as it ensure that any changes to the original object will be returned by the "getter"
  674. (function(prop1) {
  675. extendedCol[prop1] = function() {
  676. return column[prop1];
  677. };
  678. })(prop);
  679. }
  680. (function(prop1) {
  681. // satisfy the arguments expected by the function returned by parsedAttribute in the ngTable directive
  682. var getterFn = extendedCol[prop1];
  683. extendedCol[prop1] = function() {
  684. if (arguments.length === 0) {
  685. return getterFn.call(column, defaultScope);
  686. } else {
  687. return getterFn.apply(column, arguments);
  688. }
  689. };
  690. })(prop);
  691. }
  692. return extendedCol;
  693. }
  694. return {
  695. buildColumn: buildColumn
  696. };
  697. }]);
  698. /**
  699. * ngTable: Table + Angular JS
  700. *
  701. * @author Vitalii Savchuk <esvit666@gmail.com>
  702. * @url https://github.com/esvit/ng-table/
  703. * @license New BSD License <http://creativecommons.org/licenses/BSD/>
  704. */
  705. /**
  706. * @ngdoc directive
  707. * @name ngTable.directive:ngTable
  708. * @restrict A
  709. *
  710. * @description
  711. * Directive that instantiates {@link ngTable.directive:ngTable.ngTableController ngTableController}.
  712. */
  713. app.directive('ngTable', ['$q', '$parse',
  714. function($q, $parse) {
  715. 'use strict';
  716. return {
  717. restrict: 'A',
  718. priority: 1001,
  719. scope: true,
  720. controller: 'ngTableController',
  721. compile: function(element) {
  722. var columns = [],
  723. i = 0,
  724. row = null;
  725. // IE 8 fix :not(.ng-table-group) selector
  726. angular.forEach(angular.element(element.find('tr')), function(tr) {
  727. tr = angular.element(tr);
  728. if (!tr.hasClass('ng-table-group') && !row) {
  729. row = tr;
  730. }
  731. });
  732. if (!row) {
  733. return;
  734. }
  735. angular.forEach(row.find('td'), function(item) {
  736. var el = angular.element(item);
  737. if (el.attr('ignore-cell') && 'true' === el.attr('ignore-cell')) {
  738. return;
  739. }
  740. var getAttrValue = function(attr) {
  741. return el.attr('x-data-' + attr) || el.attr('data-' + attr) || el.attr(attr);
  742. };
  743. var parsedAttribute = function(attr) {
  744. var expr = getAttrValue(attr);
  745. if (!expr) {
  746. return undefined;
  747. }
  748. return function(scope, locals) {
  749. return $parse(expr)(scope, angular.extend(locals || {}, {
  750. $columns: columns
  751. }));
  752. };
  753. };
  754. var titleExpr = getAttrValue('title-alt') || getAttrValue('title');
  755. if (titleExpr) {
  756. el.attr('data-title-text', '{{' + titleExpr + '}}'); // this used in responsive table
  757. }
  758. // NOTE TO MAINTAINERS: if you add extra fields to a $column be sure to extend ngTableColumn with
  759. // a corresponding "safe" default
  760. columns.push({
  761. id: i++,
  762. title: parsedAttribute('title'),
  763. titleAlt: parsedAttribute('title-alt'),
  764. headerTitle: parsedAttribute('header-title'),
  765. sortable: parsedAttribute('sortable'),
  766. 'class': parsedAttribute('header-class'),
  767. filter: parsedAttribute('filter'),
  768. headerTemplateURL: parsedAttribute('header'),
  769. filterData: parsedAttribute('filter-data'),
  770. show: (el.attr("ng-show") ? function(scope) {
  771. return $parse(el.attr("ng-show"))(scope);
  772. } : undefined)
  773. });
  774. });
  775. return function(scope, element, attrs, controller) {
  776. scope.$columns = columns = controller.buildColumns(columns);
  777. controller.setupBindingsToInternalScope(attrs.ngTable);
  778. controller.loadFilterData(columns);
  779. controller.compileDirectiveTemplates();
  780. };
  781. }
  782. }
  783. }
  784. ]);
  785. /**
  786. * @ngdoc directive
  787. * @name ngTable.directive:ngTableDynamic
  788. * @restrict A
  789. *
  790. * @description
  791. * A dynamic version of the {@link ngTable.directive:ngTable ngTable} directive that accepts a dynamic list of columns
  792. * definitions to render
  793. */
  794. app.directive('ngTableDynamic', ['$parse', function($parse) {
  795. function parseDirectiveExpression(attr) {
  796. if (!attr || attr.indexOf(" with ") > -1) {
  797. var parts = attr.split(/\s+with\s+/);
  798. return {
  799. tableParams: parts[0],
  800. columns: parts[1]
  801. };
  802. } else {
  803. throw new Error('Parse error (expected example: ng-table-dynamic=\'tableParams with cols\')');
  804. }
  805. }
  806. return {
  807. restrict: 'A',
  808. priority: 1001,
  809. scope: true,
  810. controller: 'ngTableController',
  811. compile: function(tElement) {
  812. var row;
  813. // IE 8 fix :not(.ng-table-group) selector
  814. angular.forEach(angular.element(tElement.find('tr')), function(tr) {
  815. tr = angular.element(tr);
  816. if (!tr.hasClass('ng-table-group') && !row) {
  817. row = tr;
  818. }
  819. });
  820. if (!row) {
  821. return;
  822. }
  823. angular.forEach(row.find('td'), function(item) {
  824. var el = angular.element(item);
  825. var getAttrValue = function(attr) {
  826. return el.attr('x-data-' + attr) || el.attr('data-' + attr) || el.attr(attr);
  827. };
  828. // this used in responsive table
  829. var titleExpr = getAttrValue('title');
  830. if (!titleExpr) {
  831. el.attr('data-title-text', '{{$columns[$index].titleAlt(this) || $columns[$index].title(this)}}');
  832. }
  833. var showExpr = el.attr('ng-show');
  834. if (!showExpr) {
  835. el.attr('ng-show', '$columns[$index].show(this)');
  836. }
  837. });
  838. return function(scope, element, attrs, controller) {
  839. var expr = parseDirectiveExpression(attrs.ngTableDynamic);
  840. var columns = $parse(expr.columns)(scope) || [];
  841. scope.$columns = controller.buildColumns(columns);
  842. controller.setupBindingsToInternalScope(expr.tableParams);
  843. controller.loadFilterData(scope.$columns);
  844. controller.compileDirectiveTemplates();
  845. };
  846. }
  847. };
  848. }]);
  849. /**
  850. * ngTable: Table + Angular JS
  851. *
  852. * @author Vitalii Savchuk <esvit666@gmail.com>
  853. * @url https://github.com/esvit/ng-table/
  854. * @license New BSD License <http://creativecommons.org/licenses/BSD/>
  855. */
  856. /**
  857. * @ngdoc directive
  858. * @name ngTable.directive:ngTablePagination
  859. * @restrict A
  860. */
  861. app.directive('ngTablePagination', ['$compile',
  862. function($compile) {
  863. 'use strict';
  864. return {
  865. restrict: 'A',
  866. scope: {
  867. 'params': '=ngTablePagination',
  868. 'templateUrl': '='
  869. },
  870. replace: false,
  871. link: function(scope, element, attrs) {
  872. scope.params.settings().$scope.$on('ngTableAfterReloadData', function() {
  873. scope.pages = scope.params.generatePagesArray(scope.params.page(), scope.params.total(), scope.params.count());
  874. }, true);
  875. scope.$watch('templateUrl', function(templateUrl) {
  876. if (angular.isUndefined(templateUrl)) {
  877. return;
  878. }
  879. var template = angular.element(document.createElement('div'))
  880. template.attr({
  881. 'ng-include': 'templateUrl'
  882. });
  883. element.append(template);
  884. $compile(template)(scope);
  885. });
  886. }
  887. };
  888. }
  889. ]);
  890. angular.module('ngTable').run(['$templateCache', function($templateCache) {
  891. $templateCache.put('ng-table/pager.html', '<div class="ng-cloak ng-table-pager" ng-if="params.data.length"> <div ng-if="params.settings().counts.length" class="ng-table-counts btn-group pull-right"> <button ng-repeat="count in params.settings().counts" type="button" ng-class="{\'active\':params.count()==count}" ng-click="params.count(count)" class="btn btn-default"> <span ng-bind="count"></span> </button> </div> <ul class="pagination ng-table-pagination"> <li ng-class="{\'disabled\': !page.active && !page.current, \'active\': page.current}" ng-repeat="page in pages" ng-switch="page.type"> <a ng-switch-when="prev" ng-click="params.page(page.number)" href="">&laquo;</a> <a ng-switch-when="first" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></a> <a ng-switch-when="page" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></a> <a ng-switch-when="more" ng-click="params.page(page.number)" href="">&#8230;</a> <a ng-switch-when="last" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></a> <a ng-switch-when="next" ng-click="params.page(page.number)" href="">&raquo;</a> </li> </ul> </div> ');
  892. $templateCache.put('ng-table/filters/select-multiple.html', '<select ng-options="data.id as data.title for data in $column.data" ng-disabled="$filterRow.disabled" multiple ng-multiple="true" ng-model="params.filter()[name]" ng-show="filter==\'select-multiple\'" class="filter filter-select-multiple form-control" name="{{name}}"> </select>');
  893. $templateCache.put('ng-table/filters/select.html', '<select ng-options="data.id as data.title for data in $column.data" ng-disabled="$filterRow.disabled" ng-model="params.filter()[name]" ng-show="filter==\'select\'" class="filter filter-select form-control" name="{{name}}"> </select>');
  894. $templateCache.put('ng-table/filters/text.html', '<input type="text" name="{{name}}" ng-disabled="$filterRow.disabled" ng-model="params.filter()[name]" ng-if="filter==\'text\'" class="input-filter form-control"/>');
  895. $templateCache.put('ng-table/header.html', '<tr> <th title="{{$column.headerTitle(this)}}" ng-repeat="$column in $columns" ng-class="{ \'sortable\': $column.sortable(this), \'sort-asc\': params.sorting()[$column.sortable(this)]==\'asc\', \'sort-desc\': params.sorting()[$column.sortable(this)]==\'desc\' }" ng-click="sortBy($column, $event)" ng-show="$column.show(this)" ng-init="template=$column.headerTemplateURL(this)" class="header {{$column.class(this)}}"> <div ng-if="!template" ng-show="!template" class="ng-table-header" ng-class="{\'sort-indicator\': params.settings().sortingIndicator==\'div\'}"> <span ng-bind="$column.title(this)" ng-class="{\'sort-indicator\': params.settings().sortingIndicator==\'span\'}"></span> </div> <div ng-if="template" ng-show="template" ng-include="template"></div> </th> </tr> <tr ng-show="show_filter" class="ng-table-filters"> <th data-title-text="{{$column.titleAlt(this) || $column.title(this)}}" ng-repeat="$column in $columns" ng-show="$column.show(this)" class="filter"> <div ng-repeat="(name, filter) in $column.filter(this)"> <div ng-if="filter.indexOf(\'/\') !==-1" ng-include="filter"></div> <div ng-if="filter.indexOf(\'/\')===-1" ng-include="\'ng-table/filters/\' + filter + \'.html\'"></div> </div> </th> </tr> ');
  896. }]);
  897. return app;
  898. }));