slickgrid-bootstrap-rails 0.0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. data/.gitignore +34 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +158 -0
  5. data/Rakefile +1 -0
  6. data/lib/slickgrid-bootstrap-rails/version.rb +7 -0
  7. data/lib/slickgrid-bootstrap-rails.rb +11 -0
  8. data/slickgrid-bootstrap-rails.gemspec +21 -0
  9. data/vendor/assets/images/actions.gif +0 -0
  10. data/vendor/assets/images/ajax-loader-small.gif +0 -0
  11. data/vendor/assets/images/arrow_redo.png +0 -0
  12. data/vendor/assets/images/arrow_right_peppermint.png +0 -0
  13. data/vendor/assets/images/arrow_right_spearmint.png +0 -0
  14. data/vendor/assets/images/arrow_undo.png +0 -0
  15. data/vendor/assets/images/bullet_blue.png +0 -0
  16. data/vendor/assets/images/bullet_star.png +0 -0
  17. data/vendor/assets/images/bullet_toggle_minus.png +0 -0
  18. data/vendor/assets/images/bullet_toggle_plus.png +0 -0
  19. data/vendor/assets/images/calendar.gif +0 -0
  20. data/vendor/assets/images/collapse.gif +0 -0
  21. data/vendor/assets/images/comment_yellow.gif +0 -0
  22. data/vendor/assets/images/down.gif +0 -0
  23. data/vendor/assets/images/drag-handle.png +0 -0
  24. data/vendor/assets/images/editor-helper-bg.gif +0 -0
  25. data/vendor/assets/images/expand.gif +0 -0
  26. data/vendor/assets/images/header-bg.gif +0 -0
  27. data/vendor/assets/images/header-columns-bg.gif +0 -0
  28. data/vendor/assets/images/header-columns-over-bg.gif +0 -0
  29. data/vendor/assets/images/help.png +0 -0
  30. data/vendor/assets/images/info.gif +0 -0
  31. data/vendor/assets/images/listview.gif +0 -0
  32. data/vendor/assets/images/pencil.gif +0 -0
  33. data/vendor/assets/images/row-over-bg.gif +0 -0
  34. data/vendor/assets/images/sort-asc.gif +0 -0
  35. data/vendor/assets/images/sort-asc.png +0 -0
  36. data/vendor/assets/images/sort-desc.gif +0 -0
  37. data/vendor/assets/images/sort-desc.png +0 -0
  38. data/vendor/assets/images/stripes.png +0 -0
  39. data/vendor/assets/images/tag_red.png +0 -0
  40. data/vendor/assets/images/tick.png +0 -0
  41. data/vendor/assets/images/user_identity.gif +0 -0
  42. data/vendor/assets/images/user_identity_plus.gif +0 -0
  43. data/vendor/assets/javascripts/bootstrap/slick.bootstrap.js +86 -0
  44. data/vendor/assets/javascripts/plugins/slick.autotooltips.js +48 -0
  45. data/vendor/assets/javascripts/plugins/slick.cellcopymanager.js +86 -0
  46. data/vendor/assets/javascripts/plugins/slick.cellrangedecorator.js +64 -0
  47. data/vendor/assets/javascripts/plugins/slick.cellrangeselector.js +111 -0
  48. data/vendor/assets/javascripts/plugins/slick.cellselectionmodel.js +92 -0
  49. data/vendor/assets/javascripts/plugins/slick.checkboxselectcolumn.js +153 -0
  50. data/vendor/assets/javascripts/plugins/slick.headerbuttons.js +177 -0
  51. data/vendor/assets/javascripts/plugins/slick.headermenu.js +272 -0
  52. data/vendor/assets/javascripts/plugins/slick.rowmovemanager.js +138 -0
  53. data/vendor/assets/javascripts/plugins/slick.rowselectionmodel.js +187 -0
  54. data/vendor/assets/javascripts/slick.core.js +430 -0
  55. data/vendor/assets/javascripts/slick.dataview.js +917 -0
  56. data/vendor/assets/javascripts/slick.editors.js.erb +512 -0
  57. data/vendor/assets/javascripts/slick.formatters.js.erb +55 -0
  58. data/vendor/assets/javascripts/slick.grid.js +3294 -0
  59. data/vendor/assets/javascripts/slick.groupitemmetadataprovider.js +139 -0
  60. data/vendor/assets/javascripts/slick.remotemodel.js +164 -0
  61. data/vendor/assets/javascripts/slickgrid-bootstrap-rails.js +13 -0
  62. data/vendor/assets/stylesheets/plugins/slick.headerbuttons.css +38 -0
  63. data/vendor/assets/stylesheets/plugins/slick.headermenu.css +58 -0
  64. data/vendor/assets/stylesheets/slick.grid.css.erb +157 -0
  65. data/vendor/assets/stylesheets/slickgrid-bootstrap-rails.css +4 -0
  66. data/vendor/assets/stylesheets/slickgrid-bootstrap.less +159 -0
  67. metadata +143 -0
@@ -0,0 +1,917 @@
1
+ (function ($) {
2
+ $.extend(true, window, {
3
+ Slick: {
4
+ Data: {
5
+ DataView: DataView,
6
+ Aggregators: {
7
+ Avg: AvgAggregator,
8
+ Min: MinAggregator,
9
+ Max: MaxAggregator,
10
+ Sum: SumAggregator
11
+ }
12
+ }
13
+ }
14
+ });
15
+
16
+
17
+ /***
18
+ * A sample Model implementation.
19
+ * Provides a filtered view of the underlying data.
20
+ *
21
+ * Relies on the data item having an "id" property uniquely identifying it.
22
+ */
23
+ function DataView(options) {
24
+ var self = this;
25
+
26
+ var defaults = {
27
+ groupItemMetadataProvider: null,
28
+ inlineFilters: false
29
+ };
30
+
31
+
32
+ // private
33
+ var idProperty = "id"; // property holding a unique row id
34
+ var items = []; // data by index
35
+ var rows = []; // data by row
36
+ var idxById = {}; // indexes by id
37
+ var rowsById = null; // rows by id; lazy-calculated
38
+ var filter = null; // filter function
39
+ var updated = null; // updated item ids
40
+ var suspend = false; // suspends the recalculation
41
+ var sortAsc = true;
42
+ var fastSortField;
43
+ var sortComparer;
44
+ var refreshHints = {};
45
+ var prevRefreshHints = {};
46
+ var filterArgs;
47
+ var filteredItems = [];
48
+ var compiledFilter;
49
+ var compiledFilterWithCaching;
50
+ var filterCache = [];
51
+
52
+ // grouping
53
+ var groupingGetter;
54
+ var groupingGetterIsAFn;
55
+ var groupingFormatter;
56
+ var groupingComparer;
57
+ var groups = [];
58
+ var collapsedGroups = {};
59
+ var aggregators;
60
+ var aggregateCollapsed = false;
61
+ var compiledAccumulators;
62
+
63
+ var pagesize = 0;
64
+ var pagenum = 0;
65
+ var totalRows = 0;
66
+
67
+ // events
68
+ var onRowCountChanged = new Slick.Event();
69
+ var onRowsChanged = new Slick.Event();
70
+ var onPagingInfoChanged = new Slick.Event();
71
+
72
+ options = $.extend(true, {}, defaults, options);
73
+
74
+
75
+ function beginUpdate() {
76
+ suspend = true;
77
+ }
78
+
79
+ function endUpdate() {
80
+ suspend = false;
81
+ refresh();
82
+ }
83
+
84
+ function setRefreshHints(hints) {
85
+ refreshHints = hints;
86
+ }
87
+
88
+ function setFilterArgs(args) {
89
+ filterArgs = args;
90
+ }
91
+
92
+ function updateIdxById(startingIndex) {
93
+ startingIndex = startingIndex || 0;
94
+ var id;
95
+ for (var i = startingIndex, l = items.length; i < l; i++) {
96
+ id = items[i][idProperty];
97
+ if (id === undefined) {
98
+ throw "Each data element must implement a unique 'id' property";
99
+ }
100
+ idxById[id] = i;
101
+ }
102
+ }
103
+
104
+ function ensureIdUniqueness() {
105
+ var id;
106
+ for (var i = 0, l = items.length; i < l; i++) {
107
+ id = items[i][idProperty];
108
+ if (id === undefined || idxById[id] !== i) {
109
+ throw "Each data element must implement a unique 'id' property";
110
+ }
111
+ }
112
+ }
113
+
114
+ function getItems() {
115
+ return items;
116
+ }
117
+
118
+ function setItems(data, objectIdProperty) {
119
+ if (objectIdProperty !== undefined) {
120
+ idProperty = objectIdProperty;
121
+ }
122
+ items = filteredItems = data;
123
+ idxById = {};
124
+ updateIdxById();
125
+ ensureIdUniqueness();
126
+ refresh();
127
+ }
128
+
129
+ function setPagingOptions(args) {
130
+ if (args.pageSize != undefined) {
131
+ pagesize = args.pageSize;
132
+ pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0;
133
+ }
134
+
135
+ if (args.pageNum != undefined) {
136
+ pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1));
137
+ }
138
+
139
+ onPagingInfoChanged.notify(getPagingInfo(), null, self);
140
+
141
+ refresh();
142
+ }
143
+
144
+ function getPagingInfo() {
145
+ var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1;
146
+ return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages};
147
+ }
148
+
149
+ function sort(comparer, ascending) {
150
+ sortAsc = ascending;
151
+ sortComparer = comparer;
152
+ fastSortField = null;
153
+ if (ascending === false) {
154
+ items.reverse();
155
+ }
156
+ items.sort(comparer);
157
+ if (ascending === false) {
158
+ items.reverse();
159
+ }
160
+ idxById = {};
161
+ updateIdxById();
162
+ refresh();
163
+ }
164
+
165
+ /***
166
+ * Provides a workaround for the extremely slow sorting in IE.
167
+ * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString
168
+ * to return the value of that field and then doing a native Array.sort().
169
+ */
170
+ function fastSort(field, ascending) {
171
+ sortAsc = ascending;
172
+ fastSortField = field;
173
+ sortComparer = null;
174
+ var oldToString = Object.prototype.toString;
175
+ Object.prototype.toString = (typeof field == "function") ? field : function () {
176
+ return this[field]
177
+ };
178
+ // an extra reversal for descending sort keeps the sort stable
179
+ // (assuming a stable native sort implementation, which isn't true in some cases)
180
+ if (ascending === false) {
181
+ items.reverse();
182
+ }
183
+ items.sort();
184
+ Object.prototype.toString = oldToString;
185
+ if (ascending === false) {
186
+ items.reverse();
187
+ }
188
+ idxById = {};
189
+ updateIdxById();
190
+ refresh();
191
+ }
192
+
193
+ function reSort() {
194
+ if (sortComparer) {
195
+ sort(sortComparer, sortAsc);
196
+ } else if (fastSortField) {
197
+ fastSort(fastSortField, sortAsc);
198
+ }
199
+ }
200
+
201
+ function setFilter(filterFn) {
202
+ filter = filterFn;
203
+ if (options.inlineFilters) {
204
+ compiledFilter = compileFilter();
205
+ compiledFilterWithCaching = compileFilterWithCaching();
206
+ }
207
+ refresh();
208
+ }
209
+
210
+ function groupBy(valueGetter, valueFormatter, sortComparer) {
211
+ if (!options.groupItemMetadataProvider) {
212
+ options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
213
+ }
214
+
215
+ groupingGetter = valueGetter;
216
+ groupingGetterIsAFn = typeof groupingGetter === "function";
217
+ groupingFormatter = valueFormatter;
218
+ groupingComparer = sortComparer;
219
+ collapsedGroups = {};
220
+ groups = [];
221
+ refresh();
222
+ }
223
+
224
+ function setAggregators(groupAggregators, includeCollapsed) {
225
+ aggregators = groupAggregators;
226
+ aggregateCollapsed = (includeCollapsed !== undefined)
227
+ ? includeCollapsed : aggregateCollapsed;
228
+
229
+ // pre-compile accumulator loops
230
+ compiledAccumulators = [];
231
+ var idx = aggregators.length;
232
+ while (idx--) {
233
+ compiledAccumulators[idx] = compileAccumulatorLoop(aggregators[idx]);
234
+ }
235
+
236
+ refresh();
237
+ }
238
+
239
+ function getItemByIdx(i) {
240
+ return items[i];
241
+ }
242
+
243
+ function getIdxById(id) {
244
+ return idxById[id];
245
+ }
246
+
247
+ function ensureRowsByIdCache() {
248
+ if (!rowsById) {
249
+ rowsById = {};
250
+ for (var i = 0, l = rows.length; i < l; i++) {
251
+ rowsById[rows[i][idProperty]] = i;
252
+ }
253
+ }
254
+ }
255
+
256
+ function getRowById(id) {
257
+ ensureRowsByIdCache();
258
+ return rowsById[id];
259
+ }
260
+
261
+ function getItemById(id) {
262
+ return items[idxById[id]];
263
+ }
264
+
265
+ function mapIdsToRows(idArray) {
266
+ var rows = [];
267
+ ensureRowsByIdCache();
268
+ for (var i = 0; i < idArray.length; i++) {
269
+ var row = rowsById[idArray[i]];
270
+ if (row != null) {
271
+ rows[rows.length] = row;
272
+ }
273
+ }
274
+ return rows;
275
+ }
276
+
277
+ function mapRowsToIds(rowArray) {
278
+ var ids = [];
279
+ for (var i = 0; i < rowArray.length; i++) {
280
+ if (rowArray[i] < rows.length) {
281
+ ids[ids.length] = rows[rowArray[i]][idProperty];
282
+ }
283
+ }
284
+ return ids;
285
+ }
286
+
287
+ function updateItem(id, item) {
288
+ if (idxById[id] === undefined || id !== item[idProperty]) {
289
+ throw "Invalid or non-matching id";
290
+ }
291
+ items[idxById[id]] = item;
292
+ if (!updated) {
293
+ updated = {};
294
+ }
295
+ updated[id] = true;
296
+ refresh();
297
+ }
298
+
299
+ function insertItem(insertBefore, item) {
300
+ items.splice(insertBefore, 0, item);
301
+ updateIdxById(insertBefore);
302
+ refresh();
303
+ }
304
+
305
+ function addItem(item) {
306
+ items.push(item);
307
+ updateIdxById(items.length - 1);
308
+ refresh();
309
+ }
310
+
311
+ function deleteItem(id) {
312
+ var idx = idxById[id];
313
+ if (idx === undefined) {
314
+ throw "Invalid id";
315
+ }
316
+ delete idxById[id];
317
+ items.splice(idx, 1);
318
+ updateIdxById(idx);
319
+ refresh();
320
+ }
321
+
322
+ function getLength() {
323
+ return rows.length;
324
+ }
325
+
326
+ function getItem(i) {
327
+ return rows[i];
328
+ }
329
+
330
+ function getItemMetadata(i) {
331
+ var item = rows[i];
332
+ if (item === undefined) {
333
+ return null;
334
+ }
335
+
336
+ // overrides for group rows
337
+ if (item.__group) {
338
+ return options.groupItemMetadataProvider.getGroupRowMetadata(item);
339
+ }
340
+
341
+ // overrides for totals rows
342
+ if (item.__groupTotals) {
343
+ return options.groupItemMetadataProvider.getTotalsRowMetadata(item);
344
+ }
345
+
346
+ return null;
347
+ }
348
+
349
+ function collapseGroup(groupingValue) {
350
+ collapsedGroups[groupingValue] = true;
351
+ refresh();
352
+ }
353
+
354
+ function expandGroup(groupingValue) {
355
+ delete collapsedGroups[groupingValue];
356
+ refresh();
357
+ }
358
+
359
+ function getGroups() {
360
+ return groups;
361
+ }
362
+
363
+ function extractGroups(rows) {
364
+ var group;
365
+ var val;
366
+ var groups = [];
367
+ var groupsByVal = [];
368
+ var r;
369
+
370
+ for (var i = 0, l = rows.length; i < l; i++) {
371
+ r = rows[i];
372
+ val = (groupingGetterIsAFn) ? groupingGetter(r) : r[groupingGetter];
373
+ val = val || 0;
374
+ group = groupsByVal[val];
375
+ if (!group) {
376
+ group = new Slick.Group();
377
+ group.count = 0;
378
+ group.value = val;
379
+ group.rows = [];
380
+ groups[groups.length] = group;
381
+ groupsByVal[val] = group;
382
+ }
383
+
384
+ group.rows[group.count++] = r;
385
+ }
386
+
387
+ return groups;
388
+ }
389
+
390
+ // TODO: lazy totals calculation
391
+ function calculateGroupTotals(group) {
392
+ if (group.collapsed && !aggregateCollapsed) {
393
+ return;
394
+ }
395
+
396
+ // TODO: try moving iterating over groups into compiled accumulator
397
+ var totals = new Slick.GroupTotals();
398
+ var agg, idx = aggregators.length;
399
+ while (idx--) {
400
+ agg = aggregators[idx];
401
+ agg.init();
402
+ compiledAccumulators[idx].call(agg, group.rows);
403
+ agg.storeResult(totals);
404
+ }
405
+ totals.group = group;
406
+ group.totals = totals;
407
+ }
408
+
409
+ function calculateTotals(groups) {
410
+ var idx = groups.length;
411
+ while (idx--) {
412
+ calculateGroupTotals(groups[idx]);
413
+ }
414
+ }
415
+
416
+ function finalizeGroups(groups) {
417
+ var idx = groups.length, g;
418
+ while (idx--) {
419
+ g = groups[idx];
420
+ g.collapsed = (g.value in collapsedGroups);
421
+ g.title = groupingFormatter ? groupingFormatter(g) : g.value;
422
+ }
423
+ }
424
+
425
+ function flattenGroupedRows(groups) {
426
+ var groupedRows = [], gl = 0, g;
427
+ for (var i = 0, l = groups.length; i < l; i++) {
428
+ g = groups[i];
429
+ groupedRows[gl++] = g;
430
+
431
+ if (!g.collapsed) {
432
+ for (var j = 0, jj = g.rows.length; j < jj; j++) {
433
+ groupedRows[gl++] = g.rows[j];
434
+ }
435
+ }
436
+
437
+ if (g.totals && (!g.collapsed || aggregateCollapsed)) {
438
+ groupedRows[gl++] = g.totals;
439
+ }
440
+ }
441
+ return groupedRows;
442
+ }
443
+
444
+ function getFunctionInfo(fn) {
445
+ var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/;
446
+ var matches = fn.toString().match(fnRegex);
447
+ return {
448
+ params: matches[1].split(","),
449
+ body: matches[2]
450
+ };
451
+ }
452
+
453
+ function compileAccumulatorLoop(aggregator) {
454
+ var accumulatorInfo = getFunctionInfo(aggregator.accumulate);
455
+ var fn = new Function(
456
+ "_items",
457
+ "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" +
458
+ accumulatorInfo.params[0] + " = _items[_i]; " +
459
+ accumulatorInfo.body +
460
+ "}"
461
+ );
462
+ fn.displayName = fn.name = "compiledAccumulatorLoop";
463
+ return fn;
464
+ }
465
+
466
+ function compileFilter() {
467
+ var filterInfo = getFunctionInfo(filter);
468
+
469
+ var filterBody = filterInfo.body
470
+ .replace(/return false[;}]/gi, "{ continue _coreloop; }")
471
+ .replace(/return true[;}]/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }")
472
+ .replace(/return ([^;}]+?);/gi,
473
+ "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }");
474
+
475
+ // This preserves the function template code after JS compression,
476
+ // so that replace() commands still work as expected.
477
+ var tpl = [
478
+ //"function(_items, _args) { ",
479
+ "var _retval = [], _idx = 0; ",
480
+ "var $item$, $args$ = _args; ",
481
+ "_coreloop: ",
482
+ "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
483
+ "$item$ = _items[_i]; ",
484
+ "$filter$; ",
485
+ "} ",
486
+ "return _retval; "
487
+ //"}"
488
+ ].join("");
489
+ tpl = tpl.replace(/\$filter\$/gi, filterBody);
490
+ tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
491
+ tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
492
+
493
+ var fn = new Function("_items,_args", tpl);
494
+ fn.displayName = fn.name = "compiledFilter";
495
+ return fn;
496
+ }
497
+
498
+ function compileFilterWithCaching() {
499
+ var filterInfo = getFunctionInfo(filter);
500
+
501
+ var filterBody = filterInfo.body
502
+ .replace(/return false[;}]/gi, "{ continue _coreloop; }")
503
+ .replace(/return true[;}]/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }")
504
+ .replace(/return ([^;}]+?);/gi,
505
+ "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }");
506
+
507
+ // This preserves the function template code after JS compression,
508
+ // so that replace() commands still work as expected.
509
+ var tpl = [
510
+ //"function(_items, _args, _cache) { ",
511
+ "var _retval = [], _idx = 0; ",
512
+ "var $item$, $args$ = _args; ",
513
+ "_coreloop: ",
514
+ "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
515
+ "$item$ = _items[_i]; ",
516
+ "if (_cache[_i]) { ",
517
+ "_retval[_idx++] = $item$; ",
518
+ "continue _coreloop; ",
519
+ "} ",
520
+ "$filter$; ",
521
+ "} ",
522
+ "return _retval; "
523
+ //"}"
524
+ ].join("");
525
+ tpl = tpl.replace(/\$filter\$/gi, filterBody);
526
+ tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
527
+ tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
528
+
529
+ var fn = new Function("_items,_args,_cache", tpl);
530
+ fn.displayName = fn.name = "compiledFilterWithCaching";
531
+ return fn;
532
+ }
533
+
534
+ function uncompiledFilter(items, args) {
535
+ var retval = [], idx = 0;
536
+
537
+ for (var i = 0, ii = items.length; i < ii; i++) {
538
+ if (filter(items[i], args)) {
539
+ retval[idx++] = items[i];
540
+ }
541
+ }
542
+
543
+ return retval;
544
+ }
545
+
546
+ function uncompiledFilterWithCaching(items, args, cache) {
547
+ var retval = [], idx = 0, item;
548
+
549
+ for (var i = 0, ii = items.length; i < ii; i++) {
550
+ item = items[i];
551
+ if (cache[i]) {
552
+ retval[idx++] = item;
553
+ } else if (filter(item, args)) {
554
+ retval[idx++] = item;
555
+ cache[i] = true;
556
+ }
557
+ }
558
+
559
+ return retval;
560
+ }
561
+
562
+ function getFilteredAndPagedItems(items) {
563
+ if (filter) {
564
+ var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter;
565
+ var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching;
566
+
567
+ if (refreshHints.isFilterNarrowing) {
568
+ filteredItems = batchFilter(filteredItems, filterArgs);
569
+ } else if (refreshHints.isFilterExpanding) {
570
+ filteredItems = batchFilterWithCaching(items, filterArgs, filterCache);
571
+ } else if (!refreshHints.isFilterUnchanged) {
572
+ filteredItems = batchFilter(items, filterArgs);
573
+ }
574
+ } else {
575
+ // special case: if not filtering and not paging, the resulting
576
+ // rows collection needs to be a copy so that changes due to sort
577
+ // can be caught
578
+ filteredItems = pagesize ? items : items.concat();
579
+ }
580
+
581
+ // get the current page
582
+ var paged;
583
+ if (pagesize) {
584
+ if (filteredItems.length < pagenum * pagesize) {
585
+ pagenum = Math.floor(filteredItems.length / pagesize);
586
+ }
587
+ paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize);
588
+ } else {
589
+ paged = filteredItems;
590
+ }
591
+
592
+ return {totalRows: filteredItems.length, rows: paged};
593
+ }
594
+
595
+ function getRowDiffs(rows, newRows) {
596
+ var item, r, eitherIsNonData, diff = [];
597
+ var from = 0, to = newRows.length;
598
+
599
+ if (refreshHints && refreshHints.ignoreDiffsBefore) {
600
+ from = Math.max(0,
601
+ Math.min(newRows.length, refreshHints.ignoreDiffsBefore));
602
+ }
603
+
604
+ if (refreshHints && refreshHints.ignoreDiffsAfter) {
605
+ to = Math.min(newRows.length,
606
+ Math.max(0, refreshHints.ignoreDiffsAfter));
607
+ }
608
+
609
+ for (var i = from, rl = rows.length; i < to; i++) {
610
+ if (i >= rl) {
611
+ diff[diff.length] = i;
612
+ } else {
613
+ item = newRows[i];
614
+ r = rows[i];
615
+
616
+ if ((groupingGetter && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) &&
617
+ item.__group !== r.__group ||
618
+ item.__updated ||
619
+ item.__group && !item.equals(r))
620
+ || (aggregators && eitherIsNonData &&
621
+ // no good way to compare totals since they are arbitrary DTOs
622
+ // deep object comparison is pretty expensive
623
+ // always considering them 'dirty' seems easier for the time being
624
+ (item.__groupTotals || r.__groupTotals))
625
+ || item[idProperty] != r[idProperty]
626
+ || (updated && updated[item[idProperty]])
627
+ ) {
628
+ diff[diff.length] = i;
629
+ }
630
+ }
631
+ }
632
+ return diff;
633
+ }
634
+
635
+ function recalc(_items) {
636
+ rowsById = null;
637
+
638
+ if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing ||
639
+ refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) {
640
+ filterCache = [];
641
+ }
642
+
643
+ var filteredItems = getFilteredAndPagedItems(_items);
644
+ totalRows = filteredItems.totalRows;
645
+ var newRows = filteredItems.rows;
646
+
647
+ groups = [];
648
+ if (groupingGetter != null) {
649
+ groups = extractGroups(newRows);
650
+ if (groups.length) {
651
+ finalizeGroups(groups);
652
+ if (aggregators) {
653
+ calculateTotals(groups);
654
+ }
655
+ groups.sort(groupingComparer);
656
+ newRows = flattenGroupedRows(groups);
657
+ }
658
+ }
659
+
660
+ var diff = getRowDiffs(rows, newRows);
661
+
662
+ rows = newRows;
663
+
664
+ return diff;
665
+ }
666
+
667
+ function refresh() {
668
+ if (suspend) {
669
+ return;
670
+ }
671
+
672
+ var countBefore = rows.length;
673
+ var totalRowsBefore = totalRows;
674
+
675
+ var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit
676
+
677
+ // if the current page is no longer valid, go to last page and recalc
678
+ // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
679
+ if (pagesize && totalRows < pagenum * pagesize) {
680
+ pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1);
681
+ diff = recalc(items, filter);
682
+ }
683
+
684
+ updated = null;
685
+ prevRefreshHints = refreshHints;
686
+ refreshHints = {};
687
+
688
+ if (totalRowsBefore != totalRows) {
689
+ onPagingInfoChanged.notify(getPagingInfo(), null, self);
690
+ }
691
+ if (countBefore != rows.length) {
692
+ onRowCountChanged.notify({previous: countBefore, current: rows.length}, null, self);
693
+ }
694
+ if (diff.length > 0) {
695
+ onRowsChanged.notify({rows: diff}, null, self);
696
+ }
697
+ }
698
+
699
+ function syncGridSelection(grid, preserveHidden) {
700
+ var self = this;
701
+ var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());;
702
+ var inHandler;
703
+
704
+ function update() {
705
+ if (selectedRowIds.length > 0) {
706
+ inHandler = true;
707
+ var selectedRows = self.mapIdsToRows(selectedRowIds);
708
+ if (!preserveHidden) {
709
+ selectedRowIds = self.mapRowsToIds(selectedRows);
710
+ }
711
+ grid.setSelectedRows(selectedRows);
712
+ inHandler = false;
713
+ }
714
+ }
715
+
716
+ grid.onSelectedRowsChanged.subscribe(function(e, args) {
717
+ if (inHandler) { return; }
718
+ selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
719
+ });
720
+
721
+ this.onRowsChanged.subscribe(update);
722
+
723
+ this.onRowCountChanged.subscribe(update);
724
+ }
725
+
726
+ function syncGridCellCssStyles(grid, key) {
727
+ var hashById;
728
+ var inHandler;
729
+
730
+ // since this method can be called after the cell styles have been set,
731
+ // get the existing ones right away
732
+ storeCellCssStyles(grid.getCellCssStyles(key));
733
+
734
+ function storeCellCssStyles(hash) {
735
+ hashById = {};
736
+ for (var row in hash) {
737
+ var id = rows[row][idProperty];
738
+ hashById[id] = hash[row];
739
+ }
740
+ }
741
+
742
+ function update() {
743
+ if (hashById) {
744
+ inHandler = true;
745
+ ensureRowsByIdCache();
746
+ var newHash = {};
747
+ for (var id in hashById) {
748
+ var row = rowsById[id];
749
+ if (row != undefined) {
750
+ newHash[row] = hashById[id];
751
+ }
752
+ }
753
+ grid.setCellCssStyles(key, newHash);
754
+ inHandler = false;
755
+ }
756
+ }
757
+
758
+ grid.onCellCssStylesChanged.subscribe(function(e, args) {
759
+ if (inHandler) { return; }
760
+ if (key != args.key) { return; }
761
+ if (args.hash) {
762
+ storeCellCssStyles(args.hash);
763
+ }
764
+ });
765
+
766
+ this.onRowsChanged.subscribe(update);
767
+
768
+ this.onRowCountChanged.subscribe(update);
769
+ }
770
+
771
+ return {
772
+ // methods
773
+ "beginUpdate": beginUpdate,
774
+ "endUpdate": endUpdate,
775
+ "setPagingOptions": setPagingOptions,
776
+ "getPagingInfo": getPagingInfo,
777
+ "getItems": getItems,
778
+ "setItems": setItems,
779
+ "setFilter": setFilter,
780
+ "sort": sort,
781
+ "fastSort": fastSort,
782
+ "reSort": reSort,
783
+ "groupBy": groupBy,
784
+ "setAggregators": setAggregators,
785
+ "collapseGroup": collapseGroup,
786
+ "expandGroup": expandGroup,
787
+ "getGroups": getGroups,
788
+ "getIdxById": getIdxById,
789
+ "getRowById": getRowById,
790
+ "getItemById": getItemById,
791
+ "getItemByIdx": getItemByIdx,
792
+ "mapRowsToIds": mapRowsToIds,
793
+ "mapIdsToRows": mapIdsToRows,
794
+ "setRefreshHints": setRefreshHints,
795
+ "setFilterArgs": setFilterArgs,
796
+ "refresh": refresh,
797
+ "updateItem": updateItem,
798
+ "insertItem": insertItem,
799
+ "addItem": addItem,
800
+ "deleteItem": deleteItem,
801
+ "syncGridSelection": syncGridSelection,
802
+ "syncGridCellCssStyles": syncGridCellCssStyles,
803
+
804
+ // data provider methods
805
+ "getLength": getLength,
806
+ "getItem": getItem,
807
+ "getItemMetadata": getItemMetadata,
808
+
809
+ // events
810
+ "onRowCountChanged": onRowCountChanged,
811
+ "onRowsChanged": onRowsChanged,
812
+ "onPagingInfoChanged": onPagingInfoChanged
813
+ };
814
+ }
815
+
816
+ function AvgAggregator(field) {
817
+ this.field_ = field;
818
+
819
+ this.init = function () {
820
+ this.count_ = 0;
821
+ this.nonNullCount_ = 0;
822
+ this.sum_ = 0;
823
+ };
824
+
825
+ this.accumulate = function (item) {
826
+ var val = item[this.field_];
827
+ this.count_++;
828
+ if (val != null && val != "" && val != NaN) {
829
+ this.nonNullCount_++;
830
+ this.sum_ += parseFloat(val);
831
+ }
832
+ };
833
+
834
+ this.storeResult = function (groupTotals) {
835
+ if (!groupTotals.avg) {
836
+ groupTotals.avg = {};
837
+ }
838
+ if (this.nonNullCount_ != 0) {
839
+ groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_;
840
+ }
841
+ };
842
+ }
843
+
844
+ function MinAggregator(field) {
845
+ this.field_ = field;
846
+
847
+ this.init = function () {
848
+ this.min_ = null;
849
+ };
850
+
851
+ this.accumulate = function (item) {
852
+ var val = item[this.field_];
853
+ if (val != null && val != "" && val != NaN) {
854
+ if (this.min_ == null || val < this.min_) {
855
+ this.min_ = val;
856
+ }
857
+ }
858
+ };
859
+
860
+ this.storeResult = function (groupTotals) {
861
+ if (!groupTotals.min) {
862
+ groupTotals.min = {};
863
+ }
864
+ groupTotals.min[this.field_] = this.min_;
865
+ }
866
+ }
867
+
868
+ function MaxAggregator(field) {
869
+ this.field_ = field;
870
+
871
+ this.init = function () {
872
+ this.max_ = null;
873
+ };
874
+
875
+ this.accumulate = function (item) {
876
+ var val = item[this.field_];
877
+ if (val != null && val != "" && val != NaN) {
878
+ if (this.max_ == null || val > this.max_) {
879
+ this.max_ = val;
880
+ }
881
+ }
882
+ };
883
+
884
+ this.storeResult = function (groupTotals) {
885
+ if (!groupTotals.max) {
886
+ groupTotals.max = {};
887
+ }
888
+ groupTotals.max[this.field_] = this.max_;
889
+ }
890
+ }
891
+
892
+ function SumAggregator(field) {
893
+ this.field_ = field;
894
+
895
+ this.init = function () {
896
+ this.sum_ = null;
897
+ };
898
+
899
+ this.accumulate = function (item) {
900
+ var val = item[this.field_];
901
+ if (val != null && val != "" && val != NaN) {
902
+ this.sum_ += parseFloat(val);
903
+ }
904
+ };
905
+
906
+ this.storeResult = function (groupTotals) {
907
+ if (!groupTotals.sum) {
908
+ groupTotals.sum = {};
909
+ }
910
+ groupTotals.sum[this.field_] = this.sum_;
911
+ }
912
+ }
913
+
914
+ // TODO: add more built-in aggregators
915
+ // TODO: merge common aggregators in one to prevent needles iterating
916
+
917
+ })(jQuery);