ezframe 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +11 -4
  4. data/app_template/asset/image/favicon.ico +0 -0
  5. data/app_template/asset/js/ezframe.js +288 -0
  6. data/app_template/config/generic.yml +3 -0
  7. data/app_template/config/materialize.yml +5 -0
  8. data/{example/chat → app_template}/config.ru +2 -10
  9. data/app_template/pages/basic.rb +5 -0
  10. data/exe/create_table.rb +1 -0
  11. data/exe/setup.rb +15 -0
  12. data/ezframe.gemspec +3 -4
  13. data/lib/ezframe/auth.rb +15 -12
  14. data/lib/ezframe/column_set.rb +68 -28
  15. data/lib/ezframe/column_type.rb +231 -68
  16. data/lib/ezframe/config.rb +4 -0
  17. data/lib/ezframe/controller.rb +20 -10
  18. data/lib/ezframe/database.rb +10 -3
  19. data/lib/ezframe/ht.rb +167 -0
  20. data/lib/ezframe/html.rb +28 -4
  21. data/lib/ezframe/japanese_utils.rb +40 -0
  22. data/lib/ezframe/{pages.rb → loader.rb} +3 -0
  23. data/lib/ezframe/materialize.rb +55 -20
  24. data/lib/ezframe/model.rb +0 -2
  25. data/lib/ezframe/page_base.rb +18 -12
  26. data/lib/ezframe/page_kit.rb +12 -33
  27. data/lib/ezframe/template.rb +20 -15
  28. data/lib/ezframe/util.rb +5 -0
  29. data/lib/ezframe/version.rb +1 -1
  30. data/lib/ezframe.rb +5 -4
  31. metadata +27 -70
  32. data/example/auth/Gemfile +0 -8
  33. data/example/auth/asset/css/materialize.min.css +0 -13
  34. data/example/auth/asset/js/common.js +0 -200
  35. data/example/auth/asset/js/htmlgen.js +0 -79
  36. data/example/auth/columns/user.yml +0 -12
  37. data/example/auth/config/view_conf.yml +0 -3
  38. data/example/auth/config.ru +0 -26
  39. data/example/auth/pages/app.rb +0 -61
  40. data/example/auth/template/base.html +0 -12
  41. data/example/chat/Gemfile +0 -9
  42. data/example/chat/asset/css/materialize.min.css +0 -13
  43. data/example/chat/asset/js/common.js +0 -200
  44. data/example/chat/asset/js/htmlgen.js +0 -79
  45. data/example/chat/columns/belong.yml +0 -6
  46. data/example/chat/columns/channel.yml +0 -3
  47. data/example/chat/columns/talk.yml +0 -6
  48. data/example/chat/columns/user.yml +0 -12
  49. data/example/chat/config/view_conf.yml +0 -3
  50. data/example/chat/pages/app.rb +0 -59
  51. data/example/chat/template/base.html +0 -12
  52. data/example/todo/Gemfile +0 -8
  53. data/example/todo/asset/css/datatable.css +0 -54
  54. data/example/todo/asset/css/materialize.min.css +0 -13
  55. data/example/todo/asset/js/common.js +0 -135
  56. data/example/todo/asset/js/datatable.js +0 -1814
  57. data/example/todo/asset/js/htmlgen.js +0 -79
  58. data/example/todo/asset/js/init.js +0 -3
  59. data/example/todo/asset/js/materialize.min.js +0 -6
  60. data/example/todo/asset/js/mydatatable.js +0 -9
  61. data/example/todo/asset/js/mymaterialize.js +0 -22
  62. data/example/todo/columns/todo.yml +0 -9
  63. data/example/todo/config/view_conf.yml +0 -3
  64. data/example/todo/config.ru +0 -15
  65. data/example/todo/pages/app.rb +0 -93
  66. data/example/todo/template/base.html +0 -12
  67. data/exe/myrackup +0 -5
  68. data/lib/ezframe/hthash.rb +0 -116
@@ -1,1814 +0,0 @@
1
- "use strict";
2
-
3
- var DataTable = function (table, opts) {
4
-
5
- this.options = {};
6
- this.table = table;
7
- this.currentPage = 0;
8
- this.currentStart = 0; // different from currentPage * pageSize because there is a filter
9
- this.filterIndex = [];
10
-
11
- for (var k in DataTable.defaultOptions) {
12
- if (DataTable.defaultOptions.hasOwnProperty(k)) {
13
- if (opts.hasOwnProperty(k)) {
14
- this.options[k] = opts[k] ;
15
- }
16
- else {
17
- this.options[k] = DataTable.defaultOptions[k] ;
18
- }
19
- }
20
- }
21
-
22
- if (opts.hasOwnProperty('data')) {
23
- this.options.data = opts.data ;
24
- }
25
-
26
- /* If nb columns not specified, count the number of column from thead. */
27
- if (this.options.nbColumns < 0) {
28
- this.options.nbColumns = this.table.tHead.rows[0].cells.length;
29
- }
30
-
31
- /* Create the base for pagination. */
32
- this.pagingDivs = document.querySelectorAll(this.options.pagingDivSelector);
33
- for (var i = 0; i < this.pagingDivs.length; ++i) {
34
- var div = this.pagingDivs[i];
35
- this.addClass(div, 'pagination-datatables');
36
- this.addClass(div, this.options.pagingDivClass);
37
- var ul = document.createElement('ul');
38
- this.addClass(ul, this.options.pagingListClass);
39
- div.appendChild(ul);
40
- }
41
- this.pagingLists = document.querySelectorAll(this.options.pagingDivSelector + ' ul');
42
- this.counterDivs = document.querySelectorAll(this.options.counterDivSelector);
43
- this.loadingDiv = document.querySelector(this.options.loadingDivSelector);
44
-
45
- /* DATA ! */
46
-
47
- var dataTable = this;
48
-
49
- if (!this.table.tHead) {
50
- this.table.tHead = document.createElement('thead');
51
- this.table.appendChild(this.table.rows[0]);
52
- }
53
-
54
- /* Compatibility issue (forceStrings => No defined data types). */
55
- if (this.options.forceStrings) {
56
- this.options.dataTypes = false ;
57
- }
58
-
59
- var ths = this.table.tHead.rows[0].cells ;
60
-
61
- if (!this.table.tBodies[0]) {
62
- this.table.tBodies[0] = document.createElement('tbody');
63
- }
64
-
65
- if (this.options.data instanceof Array) {
66
- this.data = this.options.data;
67
- }
68
- else if (this.options.data instanceof Object) {
69
- var ajaxOptions = DataTable.defaultAjaxOptions;
70
- for (var k in this.options.data) {
71
- ajaxOptions[k] = this.options.data[k];
72
- }
73
- this.options.data = ajaxOptions;
74
- var fetchData = true;
75
- if (fetchData) {
76
- if (this.table.dataset.size !== undefined) {
77
- this.options.data.size = parseInt(this.table.dataset.size, 10);
78
- }
79
- this.data = [];
80
- if (this.options.data.size !== undefined) {
81
- this.loadingDiv.innerHTML = '<div class="progress datatable-load-bar">'
82
- + '<div class="progress-bar progress-bar-striped active" style="width: 0%;">'
83
- + '</div></div>';
84
- if (this.options.data.allInOne) {
85
- this.getAjaxDataAsync(true);
86
- }
87
- else {
88
- for (var i = 0; i < this.options.data.size;
89
- i += this.options.pageSize * this.options.pagingNumberOfPages) {
90
- this.getAjaxDataAsync(i);
91
- }
92
- }
93
- }
94
- else {
95
- this.loadingDiv.innerHTML = '<div class="progress datatable-load-bar">'
96
- + '<div class="progress-bar progress-bar-striped active" style="width: 0%;">'
97
- + '</div></div>';
98
- this.getAjaxDataAsync(0, true);
99
- }
100
- }
101
- }
102
- else if (this.table.tBodies[0].rows.length > 0) {
103
- this.data = [];
104
- var rows = this.table.tBodies[0].rows;
105
- var nCols = rows[0].cells.length ;
106
- for (var i = 0; i < rows.length; ++i) {
107
- this.data.push ([]) ;
108
- }
109
- for (var j = 0 ; j < nCols ; ++j) {
110
- var dt = function (x) { return x ; } ;
111
- if (this.options.dataTypes instanceof Array) {
112
- switch (this.options.dataTypes[j]) {
113
- case 'int':
114
- dt = parseInt ;
115
- break ;
116
- case 'float':
117
- case 'double':
118
- dt = parseFloat ;
119
- break ;
120
- case 'date':
121
- case 'datetime':
122
- dt = function (x) { return new Date(x) ; } ;
123
- break ;
124
- case false:
125
- case true:
126
- case 'string':
127
- case 'str':
128
- dt = function (x) { return x ; } ;
129
- break ;
130
- default:
131
- dt = this.options.dataTypes[j] ;
132
- }
133
- }
134
- for (var i = 0; i < rows.length; ++i) {
135
- this.data[i].push(dt(rows[i].cells[j].innerHTML.trim())) ;
136
- }
137
- }
138
- if (this.options.dataTypes === true) {
139
- for (var c = 0; c < this.data[0].length; ++c) {
140
- var isNumeric = true;
141
- for (var i = 0; i < this.data.length; ++i) {
142
- if (this.data[i][c] !== ""
143
- && !((this.data[i][c] - parseFloat(this.data[i][c]) + 1) >= 0)) {
144
- isNumeric = false;
145
- }
146
- }
147
- if (isNumeric) {
148
- for (var i = 0; i < this.data.length; ++i) {
149
- if (this.data[i][c] !== "") {
150
- this.data[i][c] = parseFloat(this.data[i][c]);
151
- }
152
- }
153
- }
154
- }
155
- }
156
- }
157
-
158
- /* Add sorting class to all th and add callback. */
159
- this.createSort();
160
-
161
- /* Add filter where it's needed. */
162
- this.createFilter();
163
-
164
- this.triggerSort();
165
- this.filter();
166
-
167
- };
168
-
169
- DataTable.prototype = {
170
-
171
- constructor: DataTable,
172
-
173
- /**
174
- *
175
- * Add the specified class(es) to the specified DOM Element.
176
- *
177
- * @param node The DOM Element
178
- * @param classes (Array or String)
179
- *
180
- **/
181
- addClass: function (node, classes) {
182
- if (typeof classes === "string") {
183
- classes = classes.split(' ');
184
- }
185
- classes.forEach(function (c) {
186
- if (!c) { return; }
187
- node.classList.add(c);
188
- });
189
- },
190
-
191
- /**
192
- *
193
- * Remove the specified node from the DOM.
194
- *
195
- * @param node The node to removes
196
- *
197
- **/
198
- removeNode: function (node) {
199
- if (node) {
200
- node.parentNode.removeChild(node);
201
- }
202
- },
203
-
204
- /**
205
- *
206
- * Clear size option and set timeout (if specified) for refresh.
207
- *
208
- * Note: This function should be call when a ajax loading is finished.
209
- *
210
- * @update refreshTimeOut The new timeout
211
- *
212
- **/
213
- setRefreshTimeout: function () {
214
- if (this.options.data.refresh) {
215
- clearTimeout(this.refreshTimeOut);
216
- this.refreshTimeOut = setTimeout((function (datatable) {
217
- return function () { datatable.getAjaxDataAsync(0, true); };
218
- })(this), this.options.data.refresh);
219
- }
220
- },
221
-
222
- /**
223
- *
224
- * Hide the loading divs.
225
- *
226
- **/
227
- hideLoadingDivs: function () {
228
- this.removeNode(this.loadingDiv);
229
- },
230
-
231
-
232
- /**
233
- *
234
- * Update the loading divs with the current % of data load (according
235
- * to this.options.data.size).
236
- *
237
- * Note: Call setRefreshTimeout & hideLoadingDivs if all the data have been loaded.
238
- *
239
- **/
240
- updateLoadingDivs: function () {
241
- if (this.data.length >= this.options.data.size) {
242
- this.setRefreshTimeout();
243
- this.hideLoadingDivs();
244
- }
245
- else {
246
- this.loadingDiv.querySelector('div.progress .progress-bar').style.width =
247
- parseInt(100 * this.data.length / this.options.data.size, 10) + '%';
248
- }
249
- },
250
-
251
- /**
252
- *
253
- * Get data according to this.options.data, asynchronously, using recursivity.
254
- *
255
- * @param start The first offset to send to the server
256
- *
257
- * @update data Concat data received from server to old data
258
- *
259
- * Note: Each call increment start by pageSize * pagingNumberOfPages.
260
- *
261
- **/
262
- getAjaxDataAsync: function (start, recursive) {
263
- if (typeof recursive === "undefined") { recursive = false; }
264
- if (recursive && typeof this.syncData === "undefined") {
265
- this.syncData = {
266
- data: [],
267
- toAdd: [],
268
- toUpdate: {},
269
- toDelete: []
270
- };
271
- }
272
- var xhr = new XMLHttpRequest();
273
- xhr.timeout = this.options.data.timeout;
274
- xhr.onreadystatechange = function (datatable, start, recursive) {
275
- return function () {
276
- if (this.readyState == 4) {
277
- switch (this.status) {
278
- case 200:
279
- if (recursive) {
280
- if (this.response.length > 0) {
281
- datatable.syncData.data =
282
- datatable.syncData.data.concat(this.response);
283
- datatable.getAjaxDataAsync(start + datatable.options.pageSize * datatable.options.pagingNumberOfPages, true);
284
- }
285
- else {
286
- var syncData = datatable.syncData;
287
- delete datatable.syncData;
288
- datatable.data = syncData.data;
289
- datatable.addRows(syncData.toAdd);
290
- syncData.toDelete.forEach(function (e) {
291
- if (e instanceof Function) {
292
- datatable.deleteAll(e);
293
- }
294
- else {
295
- datatable.deleteRow(e);
296
- }
297
- });
298
- for (var id in syncData.toUpdate) {
299
- datatable.updateRow(id, syncData.toUpdate[id]);
300
- }
301
-
302
- datatable.sort(true);
303
- datatable.setRefreshTimeout();
304
- }
305
- }
306
- else {
307
- datatable.data = datatable.data.concat(this.response);
308
- datatable.updateLoadingDivs();
309
- datatable.sort(true);
310
- }
311
- break;
312
- case 404:
313
- case 500:
314
- console.log("ERROR: " + this.status + " - " + this.statusText);
315
- console.log(xhr);
316
- break;
317
- default:
318
- datatable.getAjaxDataAsync(start, recursive);
319
- break;
320
- }
321
- }
322
- }
323
- } (this, start, recursive);
324
- var url = this.options.data.url ;
325
- var limit = this.options.pageSize * this.options.pagingNumberOfPages ;
326
- var formdata = new FormData();
327
- if (start !== true) {
328
- if (this.options.data.type.toUpperCase() == 'GET') {
329
- url += '?start=' + start + '&limit=' + limit ;
330
- }
331
- else {
332
- formdata.append('offset', start);
333
- formdata.append('limit', limit);
334
- }
335
- }
336
- xhr.open(this.options.data.type, url, true);
337
- xhr.responseType = 'json';
338
- xhr.send(formdata);
339
- },
340
-
341
- /**
342
- *
343
- * @return The last page number according to options.pageSize and
344
- * current number of filtered elements.
345
- *
346
- **/
347
- getLastPageNumber: function () {
348
- return parseInt(Math.ceil(this.filterIndex.length / this.options.pageSize), 10);
349
- },
350
-
351
- createPagingLink: function (content, page, disabled) {
352
- var link = document.createElement('a');
353
-
354
- // Check options...
355
- if (this.options.pagingLinkClass) {
356
- link.classList.add(this.options.pagingLinkClass);
357
- }
358
-
359
- if (this.options.pagingLinkHref) {
360
- link.href = this.options.pagingLinkHref;
361
- }
362
-
363
- if (this.options.pagingLinkDisabledTabIndex !== false && disabled) {
364
- link.tabIndex = this.options.pagingLinkDisabledTabIndex;
365
- }
366
-
367
- link.dataset.page = page;
368
- link.innerHTML = content;
369
- return link;
370
- },
371
-
372
- /**
373
- *
374
- * Update the paging divs.
375
- *
376
- **/
377
- updatePaging: function () {
378
-
379
- /* Be carefull if you change something here, all this part calculate the first
380
- and last page to display. I choose to center the current page, it's more beautiful... */
381
-
382
- var nbPages = this.options.pagingNumberOfPages;
383
- var dataTable = this;
384
- var cp = parseInt(this.currentStart / this.options.pageSize, 10) + 1;
385
- var lp = this.getLastPageNumber();
386
- var start;
387
- var end;
388
- var first = this.filterIndex.length ? this.currentStart + 1 : 0;
389
- var last = (this.currentStart + this.options.pageSize) > this.filterIndex.length ?
390
- this.filterIndex.length : this.currentStart + this.options.pageSize;
391
-
392
- if (cp < nbPages / 2) {
393
- start = 1;
394
- }
395
- else if (cp >= lp - nbPages / 2) {
396
- start = lp - nbPages + 1;
397
- if (start < 1) {
398
- start = 1;
399
- }
400
- }
401
- else {
402
- start = parseInt(cp - nbPages / 2 + 1, 10);
403
- }
404
-
405
- if (start + nbPages < lp + 1) {
406
- end = start + nbPages - 1;
407
- }
408
- else {
409
- end = lp;
410
- }
411
-
412
- /* Juste iterate over each paging list and append li to ul. */
413
-
414
- for (var i = 0; i < this.pagingLists.length; ++i) {
415
- var childs = [];
416
- if (dataTable.options.firstPage) {
417
- var li = document.createElement('li');
418
- li.appendChild(dataTable.createPagingLink(
419
- dataTable.options.firstPage, "first", cp === 1
420
- ));
421
- if (cp === 1) { li.classList.add('active'); }
422
- childs.push(li);
423
- }
424
- if (dataTable.options.prevPage) {
425
- var li = document.createElement('li');
426
- li.appendChild(dataTable.createPagingLink(
427
- dataTable.options.prevPage, "prev", cp === 1
428
- ));
429
- if (cp === 1) { li.classList.add('active'); }
430
- childs.push(li);
431
- }
432
- if (dataTable.options.pagingPages) {
433
- var _childs = this.options.pagingPages.call(this.table, start,
434
- end, cp, first, last);
435
- if (_childs instanceof Array) {
436
- childs = childs.concat(_childs);
437
- }
438
- else {
439
- childs.push(_childs);
440
- }
441
- }
442
- else {
443
- for (var k = start; k <= end; k++) {
444
- var li = document.createElement('li');
445
- li.appendChild(dataTable.createPagingLink(
446
- k, k, cp === k
447
- ));
448
- if (k === cp) { li.classList.add('active'); }
449
- childs.push(li);
450
- }
451
- }
452
- if (dataTable.options.nextPage) {
453
- var li = document.createElement('li');
454
- li.appendChild(dataTable.createPagingLink(
455
- dataTable.options.nextPage, "next", cp === lp || lp === 0
456
- ));
457
- if (cp === lp || lp === 0) { li.classList.add('active'); }
458
- childs.push(li);
459
- }
460
- if (dataTable.options.lastPage) {
461
- var li = document.createElement('li');
462
- li.appendChild(dataTable.createPagingLink(
463
- dataTable.options.lastPage, "last", cp === lp || lp === 0
464
- ));
465
- if (cp === lp || lp === 0) { li.classList.add('active'); }
466
- childs.push(li);
467
- }
468
- this.pagingLists[i].innerHTML = '';
469
- childs.forEach(function (e) {
470
- if (dataTable.options.pagingItemClass) {
471
- e.classList.add(dataTable.options.pagingItemClass);
472
- }
473
- if (e.childNodes.length > 0) {
474
- e.childNodes[0].addEventListener('click', function (event) {
475
- event.preventDefault();
476
- if (this.parentNode.classList.contains('active') ||
477
- typeof this.dataset.page === 'undefined') {
478
- return;
479
- }
480
- switch (this.dataset.page) {
481
- case 'first':
482
- dataTable.loadPage(1);
483
- break;
484
- case 'prev':
485
- dataTable.loadPage(cp - 1);
486
- break;
487
- case 'next':
488
- dataTable.loadPage(cp + 1);
489
- break;
490
- case 'last':
491
- dataTable.loadPage(lp);
492
- break;
493
- default:
494
- dataTable.loadPage(parseInt(parseInt(this.dataset.page), 10));
495
- }
496
- }, false);
497
- }
498
- this.pagingLists[i].appendChild(e);
499
- }, this);
500
- }
501
-
502
- },
503
-
504
- /**
505
- *
506
- * Update the counter divs.
507
- *
508
- **/
509
- updateCounter: function () {
510
- var cp = this.filterIndex.length ?
511
- parseInt(this.currentStart / this.options.pageSize, 10) + 1 : 0;
512
- var lp = parseInt(Math.ceil(this.filterIndex.length / this.options.pageSize), 10);
513
- var first = this.filterIndex.length ? this.currentStart + 1 : 0;
514
- var last = (this.currentStart + this.options.pageSize) > this.filterIndex.length ?
515
- this.filterIndex.length : this.currentStart + this.options.pageSize;
516
- for (var i = 0; i < this.counterDivs.length; ++i) {
517
- this.counterDivs[i].innerHTML = this.options.counterText.call(this.table, cp, lp,
518
- first, last,
519
- this.filterIndex.length,
520
- this.data.length);
521
- }
522
- },
523
-
524
- /**
525
- *
526
- * @return The sort function according to options.sort, options.sortKey & options.sortDir.
527
- *
528
- * Note: This function could return false if no sort function can be generated.
529
- *
530
- **/
531
- getSortFunction: function () {
532
- if (this.options.sort === false) {
533
- return false;
534
- }
535
- if (this.options.sort instanceof Function) {
536
- return this.options.sort;
537
- }
538
- if (this.data.length === 0 || !(this.options.sortKey in this.data[0])) {
539
- return false;
540
- }
541
- var key = this.options.sortKey;
542
- var asc = this.options.sortDir === 'asc';
543
- if (this.options.sort[key] instanceof Function) {
544
- return function (s) {
545
- return function (a, b) {
546
- var vala = a[key], valb = b[key];
547
- return asc ? s(vala, valb) : -s(vala, valb);
548
- };
549
- } (this.options.sort[key]);
550
- }
551
- return function (a, b) {
552
- var vala = a[key], valb = b[key];
553
- if (vala > valb) { return asc ? 1 : -1; }
554
- if (vala < valb) { return asc ? -1 : 1; }
555
- return 0;
556
- };
557
- },
558
-
559
- /**
560
- *
561
- * Destroy the filters (remove the filter line).
562
- *
563
- **/
564
- destroyFilter: function () {
565
- this.removeNode(this.table.querySelector('.datatable-filter-line'));
566
- },
567
-
568
- /**
569
- *
570
- * Change the text input filter placeholder according to this.options.filterText.
571
- *
572
- **/
573
- changePlaceHolder: function () {
574
- var placeholder = this.options.filterText ? this.options.filterText : '';
575
- var inputTexts = this.table.querySelectorAll('.datatable-filter-line input[type="text"]');
576
- for (var i = 0; i < inputTexts.length; ++i) {
577
- inputTexts[i].placeholder = placeholder;
578
- }
579
- },
580
-
581
- /**
582
- *
583
- * Create a text filter for the specified field.
584
- *
585
- * @param field The field corresponding to the filter
586
- *
587
- * @update filters Add the new filter to the list of filter (calling addFilter)
588
- *
589
- * @return The input filter
590
- *
591
- **/
592
- createTextFilter: function (field) {
593
- var opt = this.options.filters[field];
594
- var input = opt instanceof HTMLInputElement ? opt : document.createElement('input');
595
- input.type = 'text';
596
- if (this.options.filterText) {
597
- input.placeholder = this.options.filterText;
598
- }
599
- this.addClass(input, 'datatable-filter datatable-input-text');
600
- input.dataset.filter = field;
601
- this.filterVals[field] = '';
602
- var typewatch = (function () {
603
- var timer = 0;
604
- return function (callback, ms) {
605
- clearTimeout(timer);
606
- timer = setTimeout(callback, ms);
607
- };
608
- })();
609
- input.onkeyup = function (datatable) {
610
- return function () {
611
- var val = this.value.toUpperCase();
612
- var field = this.dataset.filter;
613
- typewatch(function () {
614
- datatable.filterVals[field] = val;
615
- datatable.filter();
616
- }, 300);
617
- };
618
- } (this);
619
- input.onkeydown = input.onkeyup;
620
- var regexp = opt === 'regexp' || input.dataset.regexp;
621
- if (opt instanceof Function) {
622
- this.addFilter(field, opt);
623
- }
624
- else if (regexp) {
625
- this.addFilter(field, function (data, val) {
626
- return new RegExp(val).test(String(data));
627
- });
628
- }
629
- else {
630
- this.addFilter(field, function (data, val) {
631
- return String(data).toUpperCase().indexOf(val) !== -1;
632
- });
633
- }
634
- this.addClass(input, this.options.filterInputClass);
635
- return input;
636
- },
637
-
638
- /**
639
- * Check if the specified value is in the specified array, without strict type checking.
640
- *
641
- * @param val The val to search
642
- * @param arr The array
643
- *
644
- * @return true if the value was found in the array
645
- **/
646
- _isIn: function (val, arr) {
647
- var found = false;
648
- for (var i = 0; i < arr.length && !found; ++i) {
649
- found = arr[i] == val;
650
- }
651
- return found;
652
- },
653
-
654
- /**
655
- * Return the index of the specified element in the object.
656
- *
657
- * @param v
658
- * @param a
659
- *
660
- * @return The index, or -1
661
- **/
662
- _index: function (v, a) {
663
- if (a === undefined || a === null) {
664
- return -1;
665
- }
666
- var index = -1;
667
- for (var i = 0; i < a.length && index == -1; ++i) {
668
- if (a[i] === v) index = i;
669
- }
670
- return index;
671
- },
672
-
673
- /**
674
- * Return the keys of the specified object.
675
- *
676
- * @param obj
677
- *
678
- * @return The keys of the specified object.
679
- **/
680
- _keys: function (obj) {
681
- if (obj === undefined || obj === null) {
682
- return undefined;
683
- }
684
- var keys = [];
685
- for (var k in obj) {
686
- if (obj.hasOwnProperty(k)) keys.push(k);
687
- }
688
- return keys;
689
- },
690
-
691
- /**
692
- *
693
- * Create a select filter for the specified field.
694
- *
695
- * @param field The field corresponding to the filter
696
- *
697
- * @update filters Add the new filter to the list of filter (calling addFilter)
698
- *
699
- * @return The select filter.
700
- *
701
- **/
702
- createSelectFilter: function (field) {
703
- var opt = this.options.filters[field];
704
- var values = {}, selected = [], multiple = false,
705
- empty = true, emptyValue = this.options.filterEmptySelect;
706
- var tag = false;
707
- if (opt instanceof HTMLSelectElement) {
708
- tag = opt;
709
- }
710
- else if (opt instanceof Object && 'element' in opt && opt.element) {
711
- tag = opt.element;
712
- }
713
- if (opt instanceof HTMLSelectElement || opt === 'select') {
714
- values = this.getFilterOptions(field);
715
- }
716
- else {
717
- multiple = ('multiple' in opt) && (opt.multiple === true);
718
- empty = ('empty' in opt) && opt.empty;
719
- emptyValue = (('empty' in opt) && (typeof opt.empty === 'string')) ?
720
- opt.empty : this.options.filterEmptySelect;
721
- if ('values' in opt) {
722
- if (opt.values === 'auto') {
723
- values = this.getFilterOptions(field);
724
- }
725
- else {
726
- values = opt.values;
727
- }
728
- if ('default' in opt) {
729
- selected = opt['default'];
730
- }
731
- else if (multiple) {
732
- selected = [];
733
- for (var k in values) {
734
- if (values[k] instanceof Object) {
735
- selected = selected.concat(this._keys(values[k]));
736
- }
737
- else {
738
- selected.push(k);
739
- }
740
- }
741
- }
742
- else {
743
- selected = [];
744
- }
745
- if (!(selected instanceof Array)) {
746
- selected = [selected];
747
- }
748
- }
749
- else {
750
- values = opt;
751
- selected = multiple ? this._keys(values) : [];
752
- }
753
- }
754
- var select = tag ? tag : document.createElement('select');
755
- if (multiple) {
756
- select.multiple = true;
757
- }
758
- if (opt['default']) {
759
- select.dataset['default'] = opt['default'];
760
- }
761
- this.addClass(select, 'datatable-filter datatable-select');
762
- select.dataset.filter = field;
763
- if (empty) {
764
- var option = document.createElement('option');
765
- option.dataset.empty = true;
766
- option.value = "";
767
- option.innerHTML = emptyValue;
768
- select.appendChild(option);
769
- }
770
- var allKeys = [];
771
- for (var key in values) {
772
- if (values[key] instanceof Object) {
773
- var optgroup = document.createElement('optgroup');
774
- optgroup.label = key;
775
- for (var skey in values[key]) {
776
- if (values[key].hasOwnProperty(skey)) {
777
- allKeys.push(skey);
778
- var option = document.createElement('option');
779
- option.value = skey;
780
- option.selected = this._isIn(skey, selected);
781
- option.innerHTML = values[key][skey];
782
- optgroup.appendChild(option);
783
- }
784
- }
785
- select.appendChild(optgroup);
786
- }
787
- else {
788
- allKeys.push(key);
789
- var option = document.createElement('option');
790
- option.value = key;
791
- option.selected = this._isIn(key, selected);
792
- option.innerHTML = values[key];
793
- select.appendChild(option);
794
- }
795
- }
796
- var val = select.value;
797
- if (multiple) {
798
- val = [];
799
- for (var i = 0; i < select.options.length; ++i) {
800
- if (select.options[i].selected) { val.push(select.options[i].value); }
801
- }
802
- }
803
- this.filterVals[field] = multiple ? val : ((empty && !val) ? allKeys : [val]);
804
- select.onchange = function (allKeys, multiple, empty, datatable) {
805
- return function () {
806
- var val = this.value;
807
- if (multiple) {
808
- val = [];
809
- for (var i = 0; i < this.options.length; ++i) {
810
- if (this.options[i].selected) { val.push(this.options[i].value); }
811
- }
812
- }
813
- var field = this.dataset.filter;
814
- datatable.filterVals[field] = multiple ? val : ((empty && !val) ? allKeys : [val]);
815
- datatable.filter();
816
- };
817
- } (allKeys, multiple, empty, this);
818
- if (opt instanceof Object && opt.fn instanceof Function) {
819
- this.addFilter(field, opt.fn);
820
- select.dataset.filterType = 'function';
821
- }
822
- else {
823
- this.addFilter(field, function (aKeys, datatable) {
824
- return function (data, val) {
825
- if (!val) { return false; }
826
- if (val == aKeys && !data) { return true; }
827
- return datatable._isIn(data, val);
828
- };
829
- } (allKeys, this));
830
- select.dataset.filterType = 'default';
831
- }
832
- this.addClass(select, this.options.filterSelectClass);
833
- return select;
834
- },
835
-
836
- /**
837
- *
838
- * Create the filter line according to options.filters.
839
- *
840
- **/
841
- createFilter: function () {
842
- this.filters = [];
843
- this.filterTags = [];
844
- this.filterVals = [];
845
- // Speical options if '*'
846
- if (this.options.filters === '*') {
847
- var nThs = this.table.tHead.rows[0].cells.length;
848
- this.options.filters = [];
849
- while (nThs--) {
850
- this.options.filters.push(true);
851
- }
852
- }
853
- if (this.options.filters) {
854
- var filterLine = false;
855
- var tr = document.createElement('tr');
856
- tr.classList.add('datatable-filter-line');
857
- for (var field in this.options.filters) {
858
- if (this.options.filters.hasOwnProperty(field)) {
859
- var td = document.createElement('td');
860
- if (this.options.filters[field] !== false) {
861
- var opt = this.options.filters[field];
862
- var input = opt === true || opt === 'regexp' || opt === 'input' || opt instanceof Function || opt instanceof HTMLInputElement;
863
- var filter = input ? this.createTextFilter(field) : this.createSelectFilter(field);
864
- this.filterTags[field] = filter;
865
- if (!document.body.contains(filter)) {
866
- td.classList.add('datatable-filter-cell');
867
- td.appendChild(filter);
868
- }
869
- }
870
- if (!(this.options.filters[field] instanceof Object) || !this.options.filters[field].noColumn) {
871
- tr.appendChild(td);
872
- }
873
- }
874
- }
875
- if (tr.querySelectorAll('td.datatable-filter-cell').length > 0) {
876
- this.table.tHead.appendChild(tr);
877
- }
878
- }
879
- },
880
-
881
- /**
882
- *
883
- * Filter data and refresh.
884
- *
885
- * @param keepCurrentPage true if the current page should not be changed (on refresh
886
- * for example), if not specified or false, the current page will be set to 0.
887
- *
888
- * @update filterIndex Will contain the new filtered indexes
889
- * @update currentStart The new starting point
890
- *
891
- **/
892
- filter: function (keepCurrentPage) {
893
- if (typeof keepCurrentPage === 'undefined') {
894
- keepCurrentPage = false;
895
- }
896
- var oldCurrentStart = this.currentStart;
897
- this.currentStart = 0;
898
- this.filterIndex = [];
899
- for (var i = 0; i < this.data.length; i++) {
900
- if (this.checkFilter(this.data[i])) { this.filterIndex.push(i); }
901
- }
902
- if (keepCurrentPage) {
903
- this.currentStart = oldCurrentStart;
904
- while (this.currentStart >= this.filterIndex.length) {
905
- this.currentStart -= this.options.pageSize;
906
- }
907
- if (this.currentStart < 0) {
908
- this.currentStart = 0;
909
- }
910
- }
911
- if (this.options.filterSelectOptions && this.filterIndex.length > 0) {
912
- var dtable = this;
913
- var allKeys = [];
914
- for (var j = 0; j < this.data[0].length; ++j) {
915
- allKeys.push({});
916
- }
917
- for (var i = 0; i < this.filterIndex.length; ++i) {
918
- var row = this.data[this.filterIndex[i]];
919
- for (var j = 0; j < row.length; ++j) {
920
- allKeys[j][row[j]] = true;
921
- }
922
- }
923
- for (var k = 0; k < allKeys.length; ++k) {
924
- var keys = this._keys(allKeys[k]);
925
- if (this.filterTags[k]
926
- && this.filterTags[k] instanceof HTMLSelectElement
927
- && this.filterTags[k].dataset.filterType == 'default') {
928
- var options = this.filterTags[k].childNodes;
929
- for (var i = 0; i < options.length; ++i) {
930
- if (!options[i].dataset.empty) {
931
- options[i].style.display = dtable._isIn(options[i].value, keys)
932
- ? 'block' : 'none';
933
- }
934
- }
935
- }
936
- }
937
- }
938
- this.refresh();
939
- },
940
-
941
-
942
- /**
943
- *
944
- * Reset all filters.
945
- *
946
- **/
947
- resetFilters: function () {
948
- var dtable = this;
949
- this.filterTags.forEach(function (e) {
950
- var field = e.dataset.filter;
951
- if (e instanceof HTMLInputElement) {
952
- e.value = '';
953
- dtable.filterVals[field] = '';
954
- }
955
- else {
956
- if (e.multiple) {
957
- var allKeys = [];
958
- for (var i = 0; i < e.childNodes.length; ++i) {
959
- e.childNodes[i].selected = true;
960
- allKeys.push(e.childNodes[i].value);
961
- }
962
- dtable.filterVals[field] = allKeys;
963
- }
964
- else if (e.dataset['default']
965
- && e.querySelector('option[value="' + e.dataset['default'] + '"]').length > 0) {
966
- for (var i = 0; i < e.childNodes.length; ++i) {
967
- e.childNodes[i].selected = e.childNodes[i].value == e.dataset['default'];
968
- }
969
- dtable.filterVals[field] = [e.dataset['default']];
970
- }
971
- else if (e.childNodes.length > 0) {
972
- e.childNodes[0].selected = true;
973
- for (var i = 1; i < e.childNodes.length; ++i) {
974
- e.childNodes[i].selected = false;
975
- }
976
- if (e.childNodes[0].dataset.empty) {
977
- var allKeys = [];
978
- for (var i = 1; i < e.childNodes.length; ++i) {
979
- allKeys.push(e.childNodes[i].value);
980
- }
981
- dtable.filterVals[field] = allKeys;
982
- }
983
- else {
984
- dtable.filterVals[field] = [e.childNodes[0].value];
985
- }
986
- }
987
- }
988
- });
989
- this.filter();
990
- },
991
-
992
- /**
993
- * Strip HTML tags for the specified string.
994
- *
995
- * @param str The string from which tags must be stripped.
996
- *
997
- * @return The string with HTML tags removed.
998
- *
999
- **/
1000
- stripTags: function (str) {
1001
- var e = document.createElement('div');
1002
- e.innerHTML = str;
1003
- return e.textContent || e.innerText;
1004
- },
1005
-
1006
- /**
1007
- *
1008
- * Check if the specified data match the filters according to this.filters
1009
- * and this.filterVals.
1010
- *
1011
- * @param data The data to check
1012
- *
1013
- * @return true if the data match the filters, false otherwise
1014
- *
1015
- **/
1016
- checkFilter: function (data) {
1017
- var ok = true;
1018
- for (var fk in this.filters) {
1019
- var currentData = fk[0] === '_' ? data : data[fk];
1020
- if (typeof currentData === "string") {
1021
- currentData = this.stripTags(currentData);
1022
- }
1023
- if (!this.filters[fk](currentData, this.filterVals[fk])) {
1024
- ok = false;
1025
- break;
1026
- }
1027
- }
1028
- return ok;
1029
- },
1030
-
1031
- /**
1032
- *
1033
- * Add a new filter.
1034
- *
1035
- * @update filters
1036
- *
1037
- **/
1038
- addFilter: function (field, filter) {
1039
- this.filters[field] = filter;
1040
- },
1041
-
1042
- /**
1043
- *
1044
- * Get the filter select options for a specified field according
1045
- * to this.data.
1046
- *
1047
- * @return The options found.
1048
- *
1049
- **/
1050
- getFilterOptions: function (field) {
1051
- var options = {}, values = [];
1052
- for (var key in this.data) {
1053
- if (this.data[key][field] !== '') {
1054
- values.push(this.data[key][field]);
1055
- }
1056
- }
1057
- values.sort();
1058
- for (var i in values) {
1059
- if (values.hasOwnProperty(i)) {
1060
- var txt = this.stripTags(values[i]);
1061
- options[txt] = txt;
1062
- }
1063
- }
1064
- return options;
1065
- },
1066
-
1067
- /**
1068
- *
1069
- * Remove class, data and event on sort headers.
1070
- *
1071
- **/
1072
- destroySort: function () {
1073
- $('thead th').removeClass('sorting sorting-asc sorting-desc')
1074
- .unbind('click.datatable')
1075
- .removeData('sort');
1076
- },
1077
-
1078
- /**
1079
- *
1080
- * Add class, event & data to headers according to this.options.sort or data-sort attribute
1081
- * of headers.
1082
- *
1083
- * @update options.sort Will be set to true if not already and a data-sort attribute is found.
1084
- *
1085
- **/
1086
- createSort: function () {
1087
- var dataTable = this;
1088
- if (!(this.options.sort instanceof Function)) {
1089
-
1090
- var countTH = 0;
1091
- var ths = this.table.tHead.rows[0].cells;
1092
- for (var i = 0; i < ths.length; ++i) {
1093
-
1094
- if (ths[i].dataset.sort) {
1095
- dataTable.options.sort = true;
1096
- }
1097
- else if (dataTable.options.sort === '*') {
1098
- ths[i].dataset.sort = countTH;
1099
- }
1100
- else {
1101
- var key;
1102
- if (dataTable.options.sort instanceof Array) {
1103
- key = countTH;
1104
- }
1105
- else if (dataTable.options.sort instanceof Object) {
1106
- key = dataTable._keys(dataTable.options.sort)[countTH];
1107
- }
1108
- if (key !== undefined && dataTable.options.sort[key]) {
1109
- ths[i].dataset.sort = key;
1110
- }
1111
- }
1112
-
1113
- if (ths[i].dataset.sort !== undefined) {
1114
- ths[i].classList.add('sorting');
1115
- }
1116
-
1117
- countTH++;
1118
-
1119
- ths[i].addEventListener('click', function () {
1120
- if (this.dataset.sort) {
1121
- if (this.classList.contains('sorting-asc')) {
1122
- dataTable.options.sortDir = 'desc';
1123
- this.classList.remove('sorting-asc')
1124
- this.classList.add('sorting-desc');
1125
- }
1126
- else if (this.classList.contains('sorting-desc')) {
1127
- dataTable.options.sortDir = 'asc';
1128
- this.classList.remove('sorting-desc');
1129
- this.classList.add('sorting-asc');
1130
- }
1131
- else {
1132
- var oths = this.parentNode.cells;
1133
- for (var j = 0; j < oths.length; j++) {
1134
- oths[j].classList.remove('sorting-desc');
1135
- oths[j].classList.remove('sorting-asc');
1136
- }
1137
- dataTable.options.sortDir = 'asc';
1138
- dataTable.options.sortKey = this.dataset.sort;
1139
- this.classList.add('sorting-asc');
1140
- }
1141
- dataTable.sort();
1142
- dataTable.refresh();
1143
- }
1144
- }, false);
1145
-
1146
- }
1147
-
1148
- }
1149
- },
1150
-
1151
- /**
1152
- *
1153
- * Trigger sort event on the table: If options.sort is a function,
1154
- * sort the table, otherwize trigger click on the column specifid by options.sortKey.
1155
- *
1156
- **/
1157
- triggerSort: function () {
1158
- if (this.options.sort instanceof Function) {
1159
- this.sort();
1160
- this.refresh();
1161
- }
1162
- else if (this.options.sortKey !== false) {
1163
- var ths = this.table.tHead.rows[0].cells;
1164
- var th;
1165
- for (var j = 0; j < ths.length; j++) {
1166
- ths[j].classList.remove('sorting-desc');
1167
- ths[j].classList.remove('sorting-asc');
1168
- if (ths[j].dataset.sort === this.options.sortKey) {
1169
- th = ths[j];
1170
- }
1171
- }
1172
- if (th !== undefined) {
1173
- th.classList.add('sorting-' + this.options.sortDir);
1174
- this.sort();
1175
- this.refresh();
1176
- }
1177
- }
1178
- },
1179
-
1180
- /**
1181
- *
1182
- * Sort the data.
1183
- *
1184
- * @update data
1185
- *
1186
- **/
1187
- sort: function (keepCurrentPage) {
1188
- var fnSort = this.getSortFunction();
1189
- if (fnSort !== false) {
1190
- this.data.sort(fnSort);
1191
- }
1192
- this.filter(keepCurrentPage);
1193
- },
1194
-
1195
- /**
1196
- *
1197
- * Try to identify the specified data with the specify identifier according
1198
- * to this.options.identify.
1199
- *
1200
- * @return true if the data match, false otherwize
1201
- *
1202
- **/
1203
- identify: function (id, data) {
1204
- if (this.options.identify === false) {
1205
- return false;
1206
- }
1207
- if (this.options.identify instanceof Function) {
1208
- return this.options.identify(id, data);
1209
- }
1210
- return data[this.options.identify] == id;
1211
- },
1212
-
1213
- /**
1214
- *
1215
- * Find the index of the first element matching id in the data array.
1216
- *
1217
- * @param The id to find (will be match according to this.options.identify)
1218
- *
1219
- * @return The index of the first element found, or -1 if no element is found
1220
- *
1221
- **/
1222
- indexOf: function (id) {
1223
- var index = -1;
1224
- for (var i = 0; i < this.data.length && index === -1; i++) {
1225
- if (this.identify(id, this.data[i])) {
1226
- index = i;
1227
- }
1228
- }
1229
- return index;
1230
- },
1231
-
1232
- /**
1233
- *
1234
- * Get an elements from the data array.
1235
- *
1236
- * @param id An identifier for the element (see this.options.identify)
1237
- *
1238
- **/
1239
- row: function (id) {
1240
- if (this.options.identify === true) {
1241
- return this.data[id];
1242
- }
1243
- return this.data[this.indexOf(id)];
1244
- },
1245
-
1246
- /**
1247
- *
1248
- * Retrieve all data.
1249
- *
1250
- *
1251
- **/
1252
- all: function (filter) {
1253
- if (typeof filter === "undefined"
1254
- || filter === true) {
1255
- return this.data;
1256
- }
1257
- var returnData = [];
1258
- for (var i = 0; i < this.data.length; ++i) {
1259
- if (filter(this.data[i])) {
1260
- returnData.push(this.data[i]);
1261
- }
1262
- }
1263
- return returnData;
1264
- },
1265
-
1266
- /**
1267
- *
1268
- * Add an element to the data array.
1269
- *
1270
- * @param data The element to add
1271
- *
1272
- * @update data
1273
- *
1274
- **/
1275
- addRow: function (data) {
1276
- this.data.push(data);
1277
- if (typeof this.syncData !== "undefined") {
1278
- this.syncData.toAdd.push(data);
1279
- }
1280
- this.sort();
1281
- this.filter();
1282
- this.currentStart = parseInt(this._index(this._index(data, this.data),
1283
- this.filterIndex)
1284
- / this.options.pageSize, 10) * this.options.pageSize;
1285
- this.refresh();
1286
- },
1287
-
1288
- /**
1289
- *
1290
- * Add elements to the data array.
1291
- *
1292
- * @param data Array of elements to add
1293
- *
1294
- * @update data
1295
- *
1296
- **/
1297
- addRows: function (data) {
1298
- this.data = this.data.concat(data);
1299
- if (typeof this.syncData !== "undefined") {
1300
- this.syncData.toAdd = this.syncData.toAdd.concat(data);
1301
- }
1302
- this.sort();
1303
- this.filter();
1304
- this.currentStart = parseInt(this._index(this._index(data, this.data),
1305
- this.filterIndex)
1306
- / this.options.pageSize, 10) * this.options.pageSize;
1307
- this.refresh();
1308
- },
1309
-
1310
- /**
1311
- *
1312
- * Remove an element from the data array.
1313
- *
1314
- * @param id An identifier for the element (see this.options.identify)
1315
- *
1316
- **/
1317
- deleteRow: function (id) {
1318
- var oldCurrentStart = this.currentStart;
1319
- var index = this.indexOf(id);
1320
- if (index === -1) {
1321
- console.log('No data found with id: ' + id);
1322
- return;
1323
- }
1324
- this.data.splice(index, 1);
1325
- if (typeof this.syncData !== "undefined") {
1326
- this.syncData.toDelete.push(id);
1327
- }
1328
- this.filter();
1329
- if (oldCurrentStart < this.filterIndex.length) {
1330
- this.currentStart = oldCurrentStart;
1331
- }
1332
- else {
1333
- this.currentStart = oldCurrentStart - this.options.pageSize;
1334
- if (this.currentStart < 0) { this.currentStart = 0; }
1335
- }
1336
- this.refresh();
1337
- },
1338
-
1339
- /**
1340
- *
1341
- * Delete all elements matching the filter arg.
1342
- *
1343
- **/
1344
- deleteAll: function (filter) {
1345
- var oldCurrentStart = this.currentStart
1346
- var newData = [];
1347
- if (typeof this.syncData !== "undefined") {
1348
- this.syncData.toDelete.push(filter);
1349
- }
1350
- for (var i = 0; i < this.data.length; ++i) {
1351
- if (!filter(this.data[i])) {
1352
- newData.push(this.data[i]);
1353
- }
1354
- }
1355
- this.data = newData;
1356
- this.filter();
1357
- if (oldCurrentStart < this.filterIndex.length) {
1358
- this.currentStart = oldCurrentStart;
1359
- }
1360
- else {
1361
- this.currentStart = oldCurrentStart - this.options.pageSize;
1362
- if (this.currentStart < 0) { this.currentStart = 0; }
1363
- }
1364
- this.refresh();
1365
- },
1366
-
1367
- /**
1368
- *
1369
- * Update an element in the data array. Will add the element if it is not found.
1370
- *
1371
- * @param id An identifier for the element (see this.options.identify)
1372
- * @param data The new data (identifier value will be set to id)
1373
- *
1374
- **/
1375
- updateRow: function (id, data) {
1376
- var index = this.indexOf(id);
1377
- if (typeof this.syncData !== "undefined") {
1378
- this.syncData.toUpdate[id] = data;
1379
- }
1380
- if (index !== -1) {
1381
- if (id in data) {
1382
- delete data[id];
1383
- }
1384
- for (var key in this.data[index]) {
1385
- if (key in data) {
1386
- this.data[index][key] = data[key];
1387
- }
1388
- }
1389
- this.sort();
1390
- this.filter();
1391
- this.currentStart = parseInt(this._index(this.indexOf(id),
1392
- this.filterIndex)
1393
- / this.options.pageSize, 10) * this.options.pageSize;
1394
- this.refresh();
1395
- }
1396
- },
1397
-
1398
- /**
1399
- *
1400
- * Change the current page and refresh.
1401
- *
1402
- * @param page The number of the page to load
1403
- *
1404
- * @update currentStart
1405
- *
1406
- **/
1407
- loadPage: function (page) {
1408
- var oldPage = this.currentStart / this.options.pageSize;
1409
- if (page < 1) {
1410
- page = 1;
1411
- }
1412
- else if (page > this.getLastPageNumber()) {
1413
- page = this.getLastPageNumber();
1414
- }
1415
- this.currentStart = (page - 1) * this.options.pageSize;
1416
- this.refresh();
1417
- this.options.onChange.call(this.table, oldPage + 1, page);
1418
- },
1419
-
1420
- /**
1421
- *
1422
- * @return The current page
1423
- *
1424
- **/
1425
- getCurrentPage: function () {
1426
- return this.currentStart / this.options.pageSize + 1;
1427
- },
1428
-
1429
- /**
1430
- *
1431
- * Refresh the page according to current page (DO NOT SORT).
1432
- * This function call options.lineFormat.
1433
- *
1434
- **/
1435
- refresh: function () {
1436
- this.options.beforeRefresh.call(this.table);
1437
- this.updatePaging();
1438
- this.updateCounter();
1439
- this.table.tBodies[0].innerHTML = "";
1440
- if (this.currentStart >= this.currentDataLength) {
1441
- this.table.tBodies[0].innerHTML = '<tr><td colspan="' + this.options.nbColumns + '">'
1442
- + '<div class="progress progress-striped active">'
1443
- + '<div class="bar" style="width: 100%;"></div>'
1444
- + '</div></div></tr>';
1445
- return;
1446
- }
1447
- for (var i = 0;
1448
- i < this.options.pageSize && i + this.currentStart < this.filterIndex.length;
1449
- i++) {
1450
- var index = this.filterIndex[this.currentStart + i];
1451
- var data = this.data[index];
1452
- this.table.tBodies[0].appendChild(this.options.lineFormat.call(this.table,
1453
- index, data));
1454
- }
1455
- this.options.afterRefresh.call(this.table);
1456
- },
1457
-
1458
- /**
1459
- *
1460
- * Set a option and refresh the table if necessary.
1461
- *
1462
- * @param key The name of the option to change
1463
- * @param val The new option value
1464
- *
1465
- * @update options
1466
- *
1467
- **/
1468
- setOption: function (key, val) {
1469
- if (key in this.options) {
1470
- this.options[key] = val;
1471
- if (key === 'sort') {
1472
- this.destroySort();
1473
- this.createSort();
1474
- this.triggerSort();
1475
- }
1476
- if (key === 'sortKey' || key === 'sortDir') {
1477
- this.sort();
1478
- }
1479
- if (key === 'filters') {
1480
- this.destroyFilter();
1481
- this.createFilter();
1482
- }
1483
- if (key === 'filterText') {
1484
- this.changePlaceHolder();
1485
- }
1486
- this.filter();
1487
- }
1488
- },
1489
-
1490
- /**
1491
- *
1492
- * Set a list of options and refresh the table if necessary.
1493
- *
1494
- * @param options A list of options to set (plain object)
1495
- *
1496
- * @update options
1497
- *
1498
- **/
1499
- setOptions: function (options) {
1500
- for (var key in options) {
1501
- if (key in this.options) {
1502
- this.options[key] = options[key];
1503
- }
1504
- }
1505
- if ('sort' in options) {
1506
- this.destroySort();
1507
- this.createSort();
1508
- this.triggerSort();
1509
- }
1510
- else if ('sortKey' in options || 'sortDir' in options) {
1511
- this.sort();
1512
- }
1513
- if ('filters' in options) {
1514
- this.destroyFilter();
1515
- this.createFilter();
1516
- }
1517
- if ('filterText' in options) {
1518
- this.changePlaceHolder();
1519
- }
1520
- this.filter();
1521
- },
1522
-
1523
- /**
1524
- *
1525
- * Remove all the elements added by the datatable.
1526
- *
1527
- **/
1528
- destroy: function () {
1529
- if (this.refreshTimeOut !== undefined) {
1530
- clearTimeout(this.refreshTimeOut);
1531
- }
1532
- this.destroySort();
1533
- for (var i = 0; i < this.pagingDivs.length; ++i) {
1534
- this.pagingDivs[i].classList.remove('pagination-datatable');
1535
- this.pagingDivs[i].classList.remove(this.options.pagingDivClass);
1536
- this.pagingDivs[i].innerHTML = '';
1537
- }
1538
- this.destroyFilter();
1539
- this.table.classList.remove(this.options.tableClass);
1540
- this.removeNode(this.table.tBodies[0]);
1541
- this.table.appendChild(document.createElement('tbody'));
1542
- for (var i = 0; i < this.data.length; i++) {
1543
- var index = this.filterIndex[this.currentStart + i];
1544
- var data = this.data[index];
1545
- this.table.tBodies[0].appendChild(this.options.lineFormat.call(this.table,
1546
- index, data));
1547
- }
1548
-
1549
- }
1550
- };
1551
-
1552
- /**
1553
- * Default option for DataTable.
1554
- *
1555
- */
1556
- DataTable.defaultOptions = {
1557
-
1558
- /**
1559
- * Specify whether the type of the column should be deduced or not. If this option
1560
- * is true, the type is not deduced (mainly here for backward compatibility).
1561
- *
1562
- * @see dataTypes
1563
- */
1564
- forceStrings: false,
1565
-
1566
- /**
1567
- * Specify the class of the table.
1568
- *
1569
- */
1570
- tableClass: 'datatable',
1571
-
1572
- /**
1573
- * Specify the selector for the paging div element.
1574
- *
1575
- */
1576
- pagingDivSelector: '.paging',
1577
-
1578
- /**
1579
- * Specify the class for the paging div element.
1580
- *
1581
- */
1582
- pagingDivClass: 'text-center',
1583
-
1584
- /**
1585
- * Specify the class for the paging list element.
1586
- *
1587
- */
1588
- pagingListClass: 'pagination',
1589
-
1590
- /**
1591
- * Specify the class for the paging list item elements.
1592
- *
1593
- */
1594
- pagingItemClass: '',
1595
-
1596
- /**
1597
- * Specify the class for the paging list link elements.
1598
- *
1599
- */
1600
- pagingLinkClass: '',
1601
-
1602
- /**
1603
- * Specify the href attribute for the paging list link elements.
1604
- *
1605
- */
1606
- pagingLinkHref: '',
1607
-
1608
- /**
1609
- * Specify the tabindex attribute for the paging list link elements when
1610
- * disabled.
1611
- *
1612
- */
1613
- pagingLinkDisabledTabIndex: false,
1614
-
1615
- /**
1616
- * Specify the selector for the counter div element.
1617
- *
1618
- * @see counterText
1619
- */
1620
- counterDivSelector: '.counter',
1621
-
1622
- /**
1623
- * Specify the selector the loading div element.
1624
- *
1625
- * @see data
1626
- */
1627
- loadingDivSelector: '.loading',
1628
-
1629
- /**
1630
- * Sepcify the sort options.
1631
- *
1632
- * @type boolean|string|Array|Object
1633
- */
1634
- sort: false,
1635
-
1636
- /**
1637
- * Specify the default sort key.
1638
- *
1639
- * @type boolean|int|string.
1640
- */
1641
- sortKey: false,
1642
-
1643
- /**
1644
- * Specify the default sort directions, 'asc' or 'desc'.
1645
- *
1646
- */
1647
- sortDir: 'asc',
1648
-
1649
- /**
1650
- * Specify the number of columns, a value of -1 (default) specify
1651
- * the the number of columns should be retrieved for the <thead>
1652
- * elements of the table.
1653
- *
1654
- */
1655
- nbColumns: -1,
1656
-
1657
- /**
1658
- * Specify the number of elements to display per page.
1659
- *
1660
- */
1661
- pageSize: 20,
1662
-
1663
- /**
1664
- * Specify the number of pages to display in the paging list element.
1665
- *
1666
- */
1667
- pagingNumberOfPages: 9,
1668
-
1669
- /**
1670
- * Specify the way of identifying items from the data array:
1671
- *
1672
- * - if this option is false (default), no identification is provided.
1673
- * - if a Function is specified, the function is used to identify:
1674
- * function (id, item) -> boolean
1675
- * - if an int or a string is specified, items are identified by the
1676
- * value corresponding to the key.
1677
- *
1678
- * @type boolean|int|string|Function.
1679
- *
1680
- */
1681
- identify: false,
1682
-
1683
- /**
1684
- * Callback function when the table is updated.
1685
- *
1686
- */
1687
- onChange: function (oldPage, newPage) { },
1688
-
1689
- /**
1690
- * Function used to generate content for the counter div element.
1691
- *
1692
- */
1693
- counterText: function (currentPage, totalPage,
1694
- firstRow, lastRow,
1695
- totalRow, totalRowUnfiltered) {
1696
- var counterText = 'Page ' + currentPage + ' on ' + totalPage
1697
- + '. Showing ' + firstRow + ' to ' + lastRow + ' of ' + totalRow + ' entries';
1698
- if (totalRow != totalRowUnfiltered) {
1699
- counterText += ' (filtered from ' + totalRowUnfiltered + ' total entries)';
1700
- }
1701
- counterText += '.';
1702
- return counterText;
1703
- },
1704
-
1705
- /**
1706
- * Content of the paging item pointing to the first page.
1707
- *
1708
- */
1709
- firstPage: '&lt;&lt;',
1710
-
1711
- /**
1712
- * Content of the paging item pointing to the previous page.
1713
- *
1714
- */
1715
- prevPage: '&lt;',
1716
-
1717
- /**
1718
- *
1719
- */
1720
- pagingPages: false,
1721
-
1722
- /**
1723
- * Content of the paging item pointing to the next page.
1724
- *
1725
- */
1726
- nextPage: '&gt;',
1727
-
1728
- /**
1729
- * Content of the paging item pointing to the last page.
1730
- *
1731
- */
1732
- lastPage: '&gt;&gt;',
1733
-
1734
- /**
1735
- * Specify the type of the columns:
1736
- *
1737
- * - if false, the type is not deduced and values are treated as strings.
1738
- * - if true, the type is deduced automatically.
1739
- * - if an Array is specified, the type of each column is retrieve from the
1740
- * array values, possible values are 'int', 'float' <> 'double', 'date' <> 'datetime',
1741
- * false <> true <> 'string' <> 'str'. A function can also be specified to convert
1742
- * the value.
1743
- *
1744
- * @see forceStrings
1745
- *
1746
- */
1747
- dataTypes: true,
1748
-
1749
- /**
1750
- * Specify the filter options.
1751
- *
1752
- */
1753
- filters: {},
1754
-
1755
- /**
1756
- * Specify the placeholder for the textual input filters.
1757
- */
1758
- filterText: 'Search... ',
1759
-
1760
- /**
1761
- * Specify the placeholder for the select input filters.
1762
- */
1763
- filterEmptySelect: '',
1764
-
1765
- /**
1766
- *
1767
- */
1768
- filterSelectOptions: false,
1769
-
1770
- /**
1771
- *
1772
- */
1773
- filterInputClass: 'form-control',
1774
-
1775
- /**
1776
- *
1777
- */
1778
- filterSelectClass: 'form-control',
1779
-
1780
- /**
1781
- * Callback function before the display is reloaded.
1782
- *
1783
- */
1784
- beforeRefresh: function () { },
1785
-
1786
- /**
1787
- * Callback function after the display has been reloaded.
1788
- *
1789
- */
1790
- afterRefresh: function () { },
1791
-
1792
- /**
1793
- * Function used to generate the row of the table.
1794
- *
1795
- */
1796
- lineFormat: function (id, data) {
1797
- var res = document.createElement('tr');
1798
- res.dataset.id = id;
1799
- for (var key in data) {
1800
- if (data.hasOwnProperty(key)) {
1801
- res.innerHTML += '<td>' + data[key] + '</td>';
1802
- }
1803
- }
1804
- return res;
1805
- }
1806
- };
1807
-
1808
- DataTable.defaultAjaxOptions = {
1809
- url: null,
1810
- size: null,
1811
- refresh: false,
1812
- allInOne: false,
1813
- timeout: 2000
1814
- };