slickgrid-bootstrap-rails 0.0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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);