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.
- data/.gitignore +34 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +158 -0
- data/Rakefile +1 -0
- data/lib/slickgrid-bootstrap-rails/version.rb +7 -0
- data/lib/slickgrid-bootstrap-rails.rb +11 -0
- data/slickgrid-bootstrap-rails.gemspec +21 -0
- data/vendor/assets/images/actions.gif +0 -0
- data/vendor/assets/images/ajax-loader-small.gif +0 -0
- data/vendor/assets/images/arrow_redo.png +0 -0
- data/vendor/assets/images/arrow_right_peppermint.png +0 -0
- data/vendor/assets/images/arrow_right_spearmint.png +0 -0
- data/vendor/assets/images/arrow_undo.png +0 -0
- data/vendor/assets/images/bullet_blue.png +0 -0
- data/vendor/assets/images/bullet_star.png +0 -0
- data/vendor/assets/images/bullet_toggle_minus.png +0 -0
- data/vendor/assets/images/bullet_toggle_plus.png +0 -0
- data/vendor/assets/images/calendar.gif +0 -0
- data/vendor/assets/images/collapse.gif +0 -0
- data/vendor/assets/images/comment_yellow.gif +0 -0
- data/vendor/assets/images/down.gif +0 -0
- data/vendor/assets/images/drag-handle.png +0 -0
- data/vendor/assets/images/editor-helper-bg.gif +0 -0
- data/vendor/assets/images/expand.gif +0 -0
- data/vendor/assets/images/header-bg.gif +0 -0
- data/vendor/assets/images/header-columns-bg.gif +0 -0
- data/vendor/assets/images/header-columns-over-bg.gif +0 -0
- data/vendor/assets/images/help.png +0 -0
- data/vendor/assets/images/info.gif +0 -0
- data/vendor/assets/images/listview.gif +0 -0
- data/vendor/assets/images/pencil.gif +0 -0
- data/vendor/assets/images/row-over-bg.gif +0 -0
- data/vendor/assets/images/sort-asc.gif +0 -0
- data/vendor/assets/images/sort-asc.png +0 -0
- data/vendor/assets/images/sort-desc.gif +0 -0
- data/vendor/assets/images/sort-desc.png +0 -0
- data/vendor/assets/images/stripes.png +0 -0
- data/vendor/assets/images/tag_red.png +0 -0
- data/vendor/assets/images/tick.png +0 -0
- data/vendor/assets/images/user_identity.gif +0 -0
- data/vendor/assets/images/user_identity_plus.gif +0 -0
- data/vendor/assets/javascripts/bootstrap/slick.bootstrap.js +86 -0
- data/vendor/assets/javascripts/plugins/slick.autotooltips.js +48 -0
- data/vendor/assets/javascripts/plugins/slick.cellcopymanager.js +86 -0
- data/vendor/assets/javascripts/plugins/slick.cellrangedecorator.js +64 -0
- data/vendor/assets/javascripts/plugins/slick.cellrangeselector.js +111 -0
- data/vendor/assets/javascripts/plugins/slick.cellselectionmodel.js +92 -0
- data/vendor/assets/javascripts/plugins/slick.checkboxselectcolumn.js +153 -0
- data/vendor/assets/javascripts/plugins/slick.headerbuttons.js +177 -0
- data/vendor/assets/javascripts/plugins/slick.headermenu.js +272 -0
- data/vendor/assets/javascripts/plugins/slick.rowmovemanager.js +138 -0
- data/vendor/assets/javascripts/plugins/slick.rowselectionmodel.js +187 -0
- data/vendor/assets/javascripts/slick.core.js +430 -0
- data/vendor/assets/javascripts/slick.dataview.js +917 -0
- data/vendor/assets/javascripts/slick.editors.js.erb +512 -0
- data/vendor/assets/javascripts/slick.formatters.js.erb +55 -0
- data/vendor/assets/javascripts/slick.grid.js +3294 -0
- data/vendor/assets/javascripts/slick.groupitemmetadataprovider.js +139 -0
- data/vendor/assets/javascripts/slick.remotemodel.js +164 -0
- data/vendor/assets/javascripts/slickgrid-bootstrap-rails.js +13 -0
- data/vendor/assets/stylesheets/plugins/slick.headerbuttons.css +38 -0
- data/vendor/assets/stylesheets/plugins/slick.headermenu.css +58 -0
- data/vendor/assets/stylesheets/slick.grid.css.erb +157 -0
- data/vendor/assets/stylesheets/slickgrid-bootstrap-rails.css +4 -0
- data/vendor/assets/stylesheets/slickgrid-bootstrap.less +159 -0
- 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);
|