slickgrid-rails 0.1.0

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