table_view.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. var filters = require('views/common/filter_view');
  20. App.TableView = Em.View.extend(App.UserPref, {
  21. init: function() {
  22. this.set('filterConditions', []);
  23. this._super();
  24. },
  25. /**
  26. * Defines to show pagination or show all records
  27. * @type {Boolean}
  28. */
  29. pagination: true,
  30. /**
  31. * Shows if all data is loaded and filtered
  32. * @type {Boolean}
  33. */
  34. filteringComplete: false,
  35. /**
  36. * intermediary for filteringComplete
  37. * @type {Boolean}
  38. */
  39. tableFilteringComplete: false,
  40. /**
  41. * The number of rows to show on every page
  42. * The value should be a number converted into string type in order to support select element API
  43. * Example: "10", "25"
  44. * @type {String}
  45. */
  46. displayLength: null,
  47. /**
  48. * default value of display length
  49. * The value should be a number converted into string type in order to support select element API
  50. * Example: "10", "25"
  51. */
  52. defaultDisplayLength: "10",
  53. /**
  54. * number of items in table after applying filters
  55. */
  56. filteredCount: function () {
  57. return this.get('filteredContent.length');
  58. }.property('filteredContent.length'),
  59. /**
  60. * total number of items in table before applying filters
  61. */
  62. totalCount: function () {
  63. return this.get('content.length');
  64. }.property('content.length'),
  65. /**
  66. * Do filtering, using saved in the local storage filter conditions
  67. */
  68. willInsertElement:function () {
  69. var self = this;
  70. var name = this.get('controller.name');
  71. if (!this.get('displayLength')) {
  72. if (App.db.getDisplayLength(name)) {
  73. this.set('displayLength', App.db.getDisplayLength(name));
  74. } else {
  75. this.getUserPref(this.displayLengthKey()).complete(function(){
  76. self.initFilters();
  77. });
  78. return;
  79. }
  80. }
  81. this.initFilters();
  82. },
  83. /**
  84. * initialize filters
  85. * restore values from local DB
  86. * or clear filters in case there is no filters to restore
  87. */
  88. initFilters: function () {
  89. var name = this.get('controller.name');
  90. var self = this;
  91. var filterConditions = App.db.getFilterConditions(name);
  92. if (filterConditions) {
  93. this.set('filterConditions', filterConditions);
  94. var childViews = this.get('childViews');
  95. filterConditions.forEach(function (condition, index, filteredConditions) {
  96. var view = !Em.isNone(condition.iColumn) && childViews.findProperty('column', condition.iColumn);
  97. if (view) {
  98. if (view.get('emptyValue')) {
  99. view.clearFilter();
  100. self.saveFilterConditions(condition.iColumn, view.get('appliedEmptyValue'), condition.type, false);
  101. } else {
  102. view.set('value', condition.value);
  103. }
  104. Em.run.next(function () {
  105. view.showClearFilter();
  106. // check if it is the last iteration
  107. if (filteredConditions.length - index - 1 === 0) {
  108. self.set('tableFilteringComplete', true);
  109. }
  110. });
  111. }
  112. });
  113. } else {
  114. this.set('tableFilteringComplete', true);
  115. }
  116. },
  117. /**
  118. * Persist-key of current table displayLength property
  119. * @param {String} loginName current user login name
  120. * @returns {String}
  121. */
  122. displayLengthKey: function (loginName) {
  123. if (App.get('testMode')) {
  124. return 'pagination_displayLength';
  125. }
  126. loginName = loginName ? loginName : App.router.get('loginName');
  127. return this.get('controller.name') + '-pagination-displayLength-' + loginName;
  128. },
  129. /**
  130. * Set received from server value to <code>displayLengthOnLoad</code>
  131. * @param {Number} response
  132. * @param {Object} request
  133. * @param {Object} data
  134. * @returns {*}
  135. */
  136. getUserPrefSuccessCallback: function (response, request, data) {
  137. console.log('Got DisplayLength value from server with key ' + data.key + '. Value is: ' + response);
  138. this.set('displayLength', response);
  139. return response;
  140. },
  141. /**
  142. * Set default value to <code>displayLength</code> (and send it on server) if value wasn't found on server
  143. * @returns {Number}
  144. */
  145. getUserPrefErrorCallback: function () {
  146. // this user is first time login
  147. console.log('Persist did NOT find the key');
  148. var displayLengthDefault = this.get('defaultDisplayLength');
  149. this.set('displayLength', displayLengthDefault);
  150. if (App.isAccessible('upgrade_ADMIN')) {
  151. this.saveDisplayLength();
  152. }
  153. this.filter();
  154. return displayLengthDefault;
  155. },
  156. /**
  157. * Return pagination information displayed on the page
  158. * @type {String}
  159. */
  160. paginationInfo: function () {
  161. return this.t('tableView.filters.paginationInfo').format(this.get('startIndex'), this.get('endIndex'), this.get('filteredCount'));
  162. }.property('filteredCount', 'endIndex'),
  163. paginationLeft: Ember.View.extend({
  164. tagName: 'a',
  165. template: Ember.Handlebars.compile('<i class="icon-arrow-left"></i>'),
  166. classNameBindings: ['class'],
  167. class: function () {
  168. if (this.get("parentView.startIndex") > 1) {
  169. return "paginate_previous";
  170. }
  171. return "paginate_disabled_previous";
  172. }.property("parentView.startIndex", 'parentView.filteredCount'),
  173. click: function () {
  174. if (this.get('class') === "paginate_previous") {
  175. this.get('parentView').previousPage();
  176. }
  177. }
  178. }),
  179. paginationRight: Ember.View.extend({
  180. tagName: 'a',
  181. template: Ember.Handlebars.compile('<i class="icon-arrow-right"></i>'),
  182. classNameBindings: ['class'],
  183. class: function () {
  184. if ((this.get("parentView.endIndex")) < this.get("parentView.filteredCount")) {
  185. return "paginate_next";
  186. }
  187. return "paginate_disabled_next";
  188. }.property("parentView.endIndex", 'parentView.filteredCount'),
  189. click: function () {
  190. if (this.get('class') === "paginate_next") {
  191. this.get('parentView').nextPage();
  192. }
  193. }
  194. }),
  195. paginationFirst: Ember.View.extend({
  196. tagName: 'a',
  197. template: Ember.Handlebars.compile('<i class="icon-step-backward"></i>'),
  198. classNameBindings: ['class'],
  199. class: function () {
  200. if ((this.get("parentView.endIndex")) > parseInt(this.get("parentView.displayLength"))) {
  201. return "paginate_previous";
  202. }
  203. return "paginate_disabled_previous";
  204. }.property("parentView.endIndex", 'parentView.filteredCount'),
  205. click: function () {
  206. if (this.get('class') === "paginate_previous") {
  207. this.get('parentView').firstPage();
  208. }
  209. }
  210. }),
  211. paginationLast: Ember.View.extend({
  212. tagName: 'a',
  213. template: Ember.Handlebars.compile('<i class="icon-step-forward"></i>'),
  214. classNameBindings: ['class'],
  215. class: function () {
  216. if (this.get("parentView.endIndex") !== this.get("parentView.filteredCount")) {
  217. return "paginate_next";
  218. }
  219. return "paginate_disabled_next";
  220. }.property("parentView.endIndex", 'parentView.filteredCount'),
  221. click: function () {
  222. if (this.get('class') === "paginate_next") {
  223. this.get('parentView').lastPage();
  224. }
  225. }
  226. }),
  227. /**
  228. * Select View with list of "rows-per-page" options
  229. * @type {Ember.View}
  230. */
  231. rowsPerPageSelectView: Em.Select.extend({
  232. content: ['10', '25', '50', '100'],
  233. change: function () {
  234. this.get('parentView').saveDisplayLength();
  235. }
  236. }),
  237. /**
  238. * Start index for displayed content on the page
  239. */
  240. startIndex: 1,
  241. /**
  242. * Calculate end index for displayed content on the page
  243. */
  244. endIndex: function () {
  245. if (this.get('pagination') && this.get('displayLength')) {
  246. return Math.min(this.get('filteredCount'), this.get('startIndex') + parseInt(this.get('displayLength')) - 1);
  247. } else {
  248. return this.get('filteredCount') || 0;
  249. }
  250. }.property('startIndex', 'displayLength', 'filteredCount'),
  251. /**
  252. * Onclick handler for previous page button on the page
  253. */
  254. previousPage: function () {
  255. var result = this.get('startIndex') - parseInt(this.get('displayLength'));
  256. this.set('startIndex', (result < 2) ? 1 : result);
  257. },
  258. /**
  259. * Onclick handler for next page button on the page
  260. */
  261. nextPage: function () {
  262. var result = this.get('startIndex') + parseInt(this.get('displayLength'));
  263. if (result - 1 < this.get('filteredCount')) {
  264. this.set('startIndex', result);
  265. }
  266. },
  267. /**
  268. * Onclick handler for first page button on the page
  269. */
  270. firstPage: function () {
  271. this.set('startIndex', 1);
  272. },
  273. /**
  274. * Onclick handler for last page button on the page
  275. */
  276. lastPage: function () {
  277. var pagesCount = this.get('filteredCount') / parseInt(this.get('displayLength'));
  278. var startIndex = (this.get('filteredCount') % parseInt(this.get('displayLength')) === 0) ?
  279. (pagesCount - 1) * parseInt(this.get('displayLength')) :
  280. Math.floor(pagesCount) * parseInt(this.get('displayLength'));
  281. this.set('startIndex', ++startIndex);
  282. },
  283. /**
  284. * Calculates default value for startIndex property after applying filter or changing displayLength
  285. */
  286. updatePaging: function (controller, property) {
  287. var displayLength = this.get('displayLength');
  288. var filteredContentLength = this.get('filteredCount');
  289. if (property == 'displayLength') {
  290. this.set('startIndex', Math.min(1, filteredContentLength));
  291. } else if (!filteredContentLength) {
  292. this.set('startIndex', 0);
  293. } else if (this.get('startIndex') > filteredContentLength) {
  294. this.set('startIndex', Math.floor((filteredContentLength - 1) / displayLength) * displayLength + 1);
  295. } else if (!this.get('startIndex')) {
  296. this.set('startIndex', 1);
  297. }
  298. }.observes('displayLength', 'filteredCount'),
  299. /**
  300. * Apply each filter to each row
  301. *
  302. * @param {Number} iColumn number of column by which filter
  303. * @param {Object} value
  304. * @param {String} type
  305. */
  306. updateFilter: function (iColumn, value, type) {
  307. this.saveFilterConditions(iColumn, value, type, false);
  308. this.filtersUsedCalc();
  309. this.filter();
  310. },
  311. /**
  312. * save filter conditions to local storage
  313. * @param iColumn {Number}
  314. * @param value {String|Array}
  315. * @param type {String}
  316. * @param skipFilter {Boolean}
  317. */
  318. saveFilterConditions: function(iColumn, value, type, skipFilter) {
  319. var filterCondition = this.get('filterConditions').findProperty('iColumn', iColumn);
  320. if (filterCondition) {
  321. filterCondition.value = value;
  322. filterCondition.skipFilter = skipFilter;
  323. } else {
  324. filterCondition = {
  325. skipFilter: skipFilter,
  326. iColumn: iColumn,
  327. value: value,
  328. type: type
  329. };
  330. this.get('filterConditions').push(filterCondition);
  331. }
  332. App.db.setFilterConditions(this.get('controller.name'), this.get('filterConditions'));
  333. },
  334. saveDisplayLength: function() {
  335. var self = this;
  336. Em.run.next(function() {
  337. App.db.setDisplayLength(self.get('controller.name'), self.get('displayLength'));
  338. if (!App.get('testMode')) {
  339. if (App.isAccessible('upgrade_ADMIN')) {
  340. self.postUserPref(self.displayLengthKey(), self.get('displayLength'));
  341. }
  342. }
  343. });
  344. },
  345. clearFilterCondition: function() {
  346. App.db.setFilterConditions(this.get('controller.name'), null);
  347. },
  348. /**
  349. * Contain filter conditions for each column
  350. * @type {Array}
  351. */
  352. filterConditions: [],
  353. /**
  354. * Contains content after implementing filters
  355. * @type {Array}
  356. */
  357. filteredContent: [],
  358. /**
  359. * Determine if <code>filteredContent</code> is empty or not
  360. * @type {Boolean}
  361. */
  362. hasFilteredItems: function() {
  363. return !!this.get('filteredCount');
  364. }.property('filteredCount'),
  365. /**
  366. * Contains content to show on the current page of data page view
  367. * @type {Array}
  368. */
  369. pageContent: function () {
  370. return this.get('filteredContent').slice(this.get('startIndex') - 1, this.get('endIndex'));
  371. }.property('filteredCount', 'startIndex', 'endIndex'),
  372. /**
  373. * Filter table by filterConditions
  374. */
  375. filter: function () {
  376. var content = this.get('content');
  377. var filterConditions = this.get('filterConditions').filterProperty('value');
  378. var result;
  379. var assoc = this.get('colPropAssoc');
  380. if (filterConditions.length) {
  381. result = content.filter(function (item) {
  382. var match = true;
  383. filterConditions.forEach(function (condition) {
  384. var filterFunc = filters.getFilterByType(condition.type, false);
  385. if (match) {
  386. match = filterFunc(item.get(assoc[condition.iColumn]), condition.value);
  387. }
  388. });
  389. return match;
  390. });
  391. this.set('filteredContent', result);
  392. } else {
  393. this.set('filteredContent', content.toArray());
  394. }
  395. }.observes('content.length'),
  396. /**
  397. * sort content by active sort column
  398. */
  399. sortContent: function() {
  400. var activeSort = App.db.getSortingStatuses(this.get('controller.name')).find(function (sort) {
  401. return (sort.status === 'sorting_asc' || sort.status === 'sorting_desc');
  402. });
  403. var sortIndexes = {
  404. 'sorting_asc': 1,
  405. 'sorting_desc': -1
  406. };
  407. this.get('content').sort(function (a, b) {
  408. if (a.get(activeSort.name) > b.get(activeSort.name)) return sortIndexes[activeSort.status];
  409. if (a.get(activeSort.name) < b.get(activeSort.name)) return -(sortIndexes[activeSort.status]);
  410. return 0;
  411. });
  412. this.filter();
  413. },
  414. /**
  415. * Does any filter is used on the page
  416. * @type {Boolean}
  417. */
  418. filtersUsed: false,
  419. /**
  420. * Determine if some filters are used on the page
  421. * Set <code>filtersUsed</code> value
  422. */
  423. filtersUsedCalc: function() {
  424. var filterConditions = this.get('filterConditions');
  425. if (!filterConditions.length) {
  426. this.set('filtersUsed', false);
  427. return;
  428. }
  429. var filtersUsed = false;
  430. filterConditions.forEach(function(filterCondition) {
  431. if (filterCondition.value.toString() !== '') {
  432. filtersUsed = true;
  433. }
  434. });
  435. this.set('filtersUsed', filtersUsed);
  436. },
  437. /**
  438. * Run <code>clearFilter</code> in the each child filterView
  439. */
  440. clearFilters: function() {
  441. this.set('filterConditions', []);
  442. this.get('_childViews').forEach(function(childView) {
  443. if (childView['clearFilter']) {
  444. childView.clearFilter();
  445. }
  446. });
  447. }
  448. });