slickgrid 2.3.16
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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +30 -0
- data/lib/slickgrid.rb +6 -0
- data/lib/slickgrid/version.rb +3 -0
- data/slickgrid.gemspec +20 -0
- data/vendor/assets/images/slickgrid/CheckboxN.png +0 -0
- data/vendor/assets/images/slickgrid/CheckboxY.png +0 -0
- data/vendor/assets/images/slickgrid/GrpCheckboxN.png +0 -0
- data/vendor/assets/images/slickgrid/GrpCheckboxY.png +0 -0
- data/vendor/assets/images/slickgrid/actions.gif +0 -0
- data/vendor/assets/images/slickgrid/ajax-loader-small.gif +0 -0
- data/vendor/assets/images/slickgrid/arrow-right.gif +0 -0
- data/vendor/assets/images/slickgrid/arrow_redo.png +0 -0
- data/vendor/assets/images/slickgrid/arrow_right_peppermint.png +0 -0
- data/vendor/assets/images/slickgrid/arrow_right_spearmint.png +0 -0
- data/vendor/assets/images/slickgrid/arrow_undo.png +0 -0
- data/vendor/assets/images/slickgrid/bullet_blue.png +0 -0
- data/vendor/assets/images/slickgrid/bullet_star.png +0 -0
- data/vendor/assets/images/slickgrid/bullet_toggle_minus.png +0 -0
- data/vendor/assets/images/slickgrid/bullet_toggle_plus.png +0 -0
- data/vendor/assets/images/slickgrid/calendar.gif +0 -0
- data/vendor/assets/images/slickgrid/collapse.gif +0 -0
- data/vendor/assets/images/slickgrid/comment_yellow.gif +0 -0
- data/vendor/assets/images/slickgrid/delete.png +0 -0
- data/vendor/assets/images/slickgrid/down.gif +0 -0
- data/vendor/assets/images/slickgrid/drag-handle.png +0 -0
- data/vendor/assets/images/slickgrid/editor-helper-bg.gif +0 -0
- data/vendor/assets/images/slickgrid/expand.gif +0 -0
- data/vendor/assets/images/slickgrid/header-bg.gif +0 -0
- data/vendor/assets/images/slickgrid/header-columns-bg.gif +0 -0
- data/vendor/assets/images/slickgrid/header-columns-over-bg.gif +0 -0
- data/vendor/assets/images/slickgrid/help.png +0 -0
- data/vendor/assets/images/slickgrid/info.gif +0 -0
- data/vendor/assets/images/slickgrid/listview.gif +0 -0
- data/vendor/assets/images/slickgrid/pencil.gif +0 -0
- data/vendor/assets/images/slickgrid/row-over-bg.gif +0 -0
- data/vendor/assets/images/slickgrid/sort-asc.gif +0 -0
- data/vendor/assets/images/slickgrid/sort-asc.png +0 -0
- data/vendor/assets/images/slickgrid/sort-desc.gif +0 -0
- data/vendor/assets/images/slickgrid/sort-desc.png +0 -0
- data/vendor/assets/images/slickgrid/stripes.png +0 -0
- data/vendor/assets/images/slickgrid/tag_red.png +0 -0
- data/vendor/assets/images/slickgrid/tick.png +0 -0
- data/vendor/assets/images/slickgrid/user_identity.gif +0 -0
- data/vendor/assets/images/slickgrid/user_identity_plus.gif +0 -0
- data/vendor/assets/javascripts/slickgrid.js +5 -0
- data/vendor/assets/javascripts/slickgrid/controls/columnpicker.js +221 -0
- data/vendor/assets/javascripts/slickgrid/controls/gridmenu.js +429 -0
- data/vendor/assets/javascripts/slickgrid/controls/pager.js +154 -0
- data/vendor/assets/javascripts/slickgrid/core.js +493 -0
- data/vendor/assets/javascripts/slickgrid/dataview.js +1220 -0
- data/vendor/assets/javascripts/slickgrid/editors.js +640 -0
- data/vendor/assets/javascripts/slickgrid/formatters.js +65 -0
- data/vendor/assets/javascripts/slickgrid/grid.js +3990 -0
- data/vendor/assets/javascripts/slickgrid/groupitemmetadataprovider.js +172 -0
- data/vendor/assets/javascripts/slickgrid/plugins/autotooltips.js +83 -0
- data/vendor/assets/javascripts/slickgrid/plugins/cellcopymanager.js +88 -0
- data/vendor/assets/javascripts/slickgrid/plugins/cellexternalcopymanager.js +452 -0
- data/vendor/assets/javascripts/slickgrid/plugins/cellrangedecorator.js +72 -0
- data/vendor/assets/javascripts/slickgrid/plugins/cellrangeselector.js +123 -0
- data/vendor/assets/javascripts/slickgrid/plugins/cellselectionmodel.js +168 -0
- data/vendor/assets/javascripts/slickgrid/plugins/checkboxselectcolumn.js +202 -0
- data/vendor/assets/javascripts/slickgrid/plugins/draggablegrouping.js +207 -0
- data/vendor/assets/javascripts/slickgrid/plugins/headerbuttons.js +177 -0
- data/vendor/assets/javascripts/slickgrid/plugins/headermenu.js +296 -0
- data/vendor/assets/javascripts/slickgrid/plugins/rowdetailview.js +455 -0
- data/vendor/assets/javascripts/slickgrid/plugins/rowmovemanager.js +138 -0
- data/vendor/assets/javascripts/slickgrid/plugins/rowselectionmodel.js +191 -0
- data/vendor/assets/javascripts/slickgrid/remotemodel.js +169 -0
- data/vendor/assets/stylesheets/slickgrid.scss +1 -0
- data/vendor/assets/stylesheets/slickgrid/controls/columnpicker.css +46 -0
- data/vendor/assets/stylesheets/slickgrid/controls/gridmenu.css +113 -0
- data/vendor/assets/stylesheets/slickgrid/controls/pager.css +41 -0
- data/vendor/assets/stylesheets/slickgrid/default-theme.css +132 -0
- data/vendor/assets/stylesheets/slickgrid/grid.css +189 -0
- data/vendor/assets/stylesheets/slickgrid/plugins/headerbuttons.css +39 -0
- data/vendor/assets/stylesheets/slickgrid/plugins/headermenu.css +59 -0
- data/vendor/assets/stylesheets/slickgrid/plugins/rowdetailview.css +39 -0
- metadata +165 -0
|
@@ -0,0 +1,1220 @@
|
|
|
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 groupingInfoDefaults = {
|
|
54
|
+
getter: null,
|
|
55
|
+
formatter: null,
|
|
56
|
+
comparer: function(a, b) {
|
|
57
|
+
return (a.value === b.value ? 0 :
|
|
58
|
+
(a.value > b.value ? 1 : -1)
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
predefinedValues: [],
|
|
62
|
+
aggregators: [],
|
|
63
|
+
aggregateEmpty: false,
|
|
64
|
+
aggregateCollapsed: false,
|
|
65
|
+
aggregateChildGroups: false,
|
|
66
|
+
collapsed: false,
|
|
67
|
+
displayTotalsRow: true,
|
|
68
|
+
lazyTotalsCalculation: false
|
|
69
|
+
};
|
|
70
|
+
var groupingInfos = [];
|
|
71
|
+
var groups = [];
|
|
72
|
+
var toggledGroupsByLevel = [];
|
|
73
|
+
var groupingDelimiter = ':|:';
|
|
74
|
+
|
|
75
|
+
var pagesize = 0;
|
|
76
|
+
var pagenum = 0;
|
|
77
|
+
var totalRows = 0;
|
|
78
|
+
|
|
79
|
+
// events
|
|
80
|
+
var onRowCountChanged = new Slick.Event();
|
|
81
|
+
var onRowsChanged = new Slick.Event();
|
|
82
|
+
var onPagingInfoChanged = new Slick.Event();
|
|
83
|
+
|
|
84
|
+
options = $.extend(true, {}, defaults, options);
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
function beginUpdate() {
|
|
88
|
+
suspend = true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function endUpdate() {
|
|
92
|
+
suspend = false;
|
|
93
|
+
refresh();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function setRefreshHints(hints) {
|
|
97
|
+
refreshHints = hints;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function setFilterArgs(args) {
|
|
101
|
+
filterArgs = args;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function updateIdxById(startingIndex) {
|
|
105
|
+
startingIndex = startingIndex || 0;
|
|
106
|
+
var id;
|
|
107
|
+
for (var i = startingIndex, l = items.length; i < l; i++) {
|
|
108
|
+
id = items[i][idProperty];
|
|
109
|
+
if (id === undefined) {
|
|
110
|
+
throw new Error("Each data element must implement a unique 'id' property");
|
|
111
|
+
}
|
|
112
|
+
idxById[id] = i;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function ensureIdUniqueness() {
|
|
117
|
+
var id;
|
|
118
|
+
for (var i = 0, l = items.length; i < l; i++) {
|
|
119
|
+
id = items[i][idProperty];
|
|
120
|
+
if (id === undefined || idxById[id] !== i) {
|
|
121
|
+
throw new Error("Each data element must implement a unique 'id' property");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getItems() {
|
|
127
|
+
return items;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function setItems(data, objectIdProperty) {
|
|
131
|
+
if (objectIdProperty !== undefined) {
|
|
132
|
+
idProperty = objectIdProperty;
|
|
133
|
+
}
|
|
134
|
+
items = filteredItems = data;
|
|
135
|
+
idxById = {};
|
|
136
|
+
updateIdxById();
|
|
137
|
+
ensureIdUniqueness();
|
|
138
|
+
refresh();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function setPagingOptions(args) {
|
|
142
|
+
if (args.pageSize != undefined) {
|
|
143
|
+
pagesize = args.pageSize;
|
|
144
|
+
pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (args.pageNum != undefined) {
|
|
148
|
+
pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
onPagingInfoChanged.notify(getPagingInfo(), null, self);
|
|
152
|
+
|
|
153
|
+
refresh();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getPagingInfo() {
|
|
157
|
+
var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1;
|
|
158
|
+
return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages, dataView: self};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function sort(comparer, ascending) {
|
|
162
|
+
sortAsc = ascending;
|
|
163
|
+
sortComparer = comparer;
|
|
164
|
+
fastSortField = null;
|
|
165
|
+
if (ascending === false) {
|
|
166
|
+
items.reverse();
|
|
167
|
+
}
|
|
168
|
+
items.sort(comparer);
|
|
169
|
+
if (ascending === false) {
|
|
170
|
+
items.reverse();
|
|
171
|
+
}
|
|
172
|
+
idxById = {};
|
|
173
|
+
updateIdxById();
|
|
174
|
+
refresh();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/***
|
|
178
|
+
* Provides a workaround for the extremely slow sorting in IE.
|
|
179
|
+
* Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString
|
|
180
|
+
* to return the value of that field and then doing a native Array.sort().
|
|
181
|
+
*/
|
|
182
|
+
function fastSort(field, ascending) {
|
|
183
|
+
sortAsc = ascending;
|
|
184
|
+
fastSortField = field;
|
|
185
|
+
sortComparer = null;
|
|
186
|
+
var oldToString = Object.prototype.toString;
|
|
187
|
+
Object.prototype.toString = (typeof field == "function") ? field : function () {
|
|
188
|
+
return this[field]
|
|
189
|
+
};
|
|
190
|
+
// an extra reversal for descending sort keeps the sort stable
|
|
191
|
+
// (assuming a stable native sort implementation, which isn't true in some cases)
|
|
192
|
+
if (ascending === false) {
|
|
193
|
+
items.reverse();
|
|
194
|
+
}
|
|
195
|
+
items.sort();
|
|
196
|
+
Object.prototype.toString = oldToString;
|
|
197
|
+
if (ascending === false) {
|
|
198
|
+
items.reverse();
|
|
199
|
+
}
|
|
200
|
+
idxById = {};
|
|
201
|
+
updateIdxById();
|
|
202
|
+
refresh();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function reSort() {
|
|
206
|
+
if (sortComparer) {
|
|
207
|
+
sort(sortComparer, sortAsc);
|
|
208
|
+
} else if (fastSortField) {
|
|
209
|
+
fastSort(fastSortField, sortAsc);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getFilteredItems(){
|
|
214
|
+
return filteredItems;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
function getFilter(){
|
|
219
|
+
return filter;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function setFilter(filterFn) {
|
|
223
|
+
filter = filterFn;
|
|
224
|
+
if (options.inlineFilters) {
|
|
225
|
+
compiledFilter = compileFilter();
|
|
226
|
+
compiledFilterWithCaching = compileFilterWithCaching();
|
|
227
|
+
}
|
|
228
|
+
refresh();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getGrouping() {
|
|
232
|
+
return groupingInfos;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function setGrouping(groupingInfo) {
|
|
236
|
+
if (!options.groupItemMetadataProvider) {
|
|
237
|
+
options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
groups = [];
|
|
241
|
+
toggledGroupsByLevel = [];
|
|
242
|
+
groupingInfo = groupingInfo || [];
|
|
243
|
+
groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo];
|
|
244
|
+
|
|
245
|
+
for (var i = 0; i < groupingInfos.length; i++) {
|
|
246
|
+
var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]);
|
|
247
|
+
gi.getterIsAFn = typeof gi.getter === "function";
|
|
248
|
+
|
|
249
|
+
// pre-compile accumulator loops
|
|
250
|
+
gi.compiledAccumulators = [];
|
|
251
|
+
var idx = gi.aggregators.length;
|
|
252
|
+
while (idx--) {
|
|
253
|
+
gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
toggledGroupsByLevel[i] = {};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
refresh();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @deprecated Please use {@link setGrouping}.
|
|
264
|
+
*/
|
|
265
|
+
function groupBy(valueGetter, valueFormatter, sortComparer) {
|
|
266
|
+
if (valueGetter == null) {
|
|
267
|
+
setGrouping([]);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
setGrouping({
|
|
272
|
+
getter: valueGetter,
|
|
273
|
+
formatter: valueFormatter,
|
|
274
|
+
comparer: sortComparer
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @deprecated Please use {@link setGrouping}.
|
|
280
|
+
*/
|
|
281
|
+
function setAggregators(groupAggregators, includeCollapsed) {
|
|
282
|
+
if (!groupingInfos.length) {
|
|
283
|
+
throw new Error("At least one grouping must be specified before calling setAggregators().");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
groupingInfos[0].aggregators = groupAggregators;
|
|
287
|
+
groupingInfos[0].aggregateCollapsed = includeCollapsed;
|
|
288
|
+
|
|
289
|
+
setGrouping(groupingInfos);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function getItemByIdx(i) {
|
|
293
|
+
return items[i];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function getIdxById(id) {
|
|
297
|
+
return idxById[id];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function ensureRowsByIdCache() {
|
|
301
|
+
if (!rowsById) {
|
|
302
|
+
rowsById = {};
|
|
303
|
+
for (var i = 0, l = rows.length; i < l; i++) {
|
|
304
|
+
rowsById[rows[i][idProperty]] = i;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function getRowByItem(item) {
|
|
310
|
+
ensureRowsByIdCache();
|
|
311
|
+
return rowsById[item[idProperty]];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function getRowById(id) {
|
|
315
|
+
ensureRowsByIdCache();
|
|
316
|
+
return rowsById[id];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getItemById(id) {
|
|
320
|
+
return items[idxById[id]];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function mapItemsToRows(itemArray) {
|
|
324
|
+
var rows = [];
|
|
325
|
+
ensureRowsByIdCache();
|
|
326
|
+
for (var i = 0, l = itemArray.length; i < l; i++) {
|
|
327
|
+
var row = rowsById[itemArray[i][idProperty]];
|
|
328
|
+
if (row != null) {
|
|
329
|
+
rows[rows.length] = row;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return rows;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function mapIdsToRows(idArray) {
|
|
336
|
+
var rows = [];
|
|
337
|
+
ensureRowsByIdCache();
|
|
338
|
+
for (var i = 0, l = idArray.length; i < l; i++) {
|
|
339
|
+
var row = rowsById[idArray[i]];
|
|
340
|
+
if (row != null) {
|
|
341
|
+
rows[rows.length] = row;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return rows;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function mapRowsToIds(rowArray) {
|
|
348
|
+
var ids = [];
|
|
349
|
+
for (var i = 0, l = rowArray.length; i < l; i++) {
|
|
350
|
+
if (rowArray[i] < rows.length) {
|
|
351
|
+
ids[ids.length] = rows[rowArray[i]][idProperty];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return ids;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function updateItem(id, item) {
|
|
358
|
+
if (idxById[id] === undefined || id !== item[idProperty]) {
|
|
359
|
+
throw new Error("Invalid or non-matching id");
|
|
360
|
+
}
|
|
361
|
+
items[idxById[id]] = item;
|
|
362
|
+
if (!updated) {
|
|
363
|
+
updated = {};
|
|
364
|
+
}
|
|
365
|
+
updated[id] = true;
|
|
366
|
+
refresh();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function insertItem(insertBefore, item) {
|
|
370
|
+
items.splice(insertBefore, 0, item);
|
|
371
|
+
updateIdxById(insertBefore);
|
|
372
|
+
refresh();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function addItem(item) {
|
|
376
|
+
items.push(item);
|
|
377
|
+
updateIdxById(items.length - 1);
|
|
378
|
+
refresh();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function deleteItem(id) {
|
|
382
|
+
var idx = idxById[id];
|
|
383
|
+
if (idx === undefined) {
|
|
384
|
+
throw new Error("Invalid id");
|
|
385
|
+
}
|
|
386
|
+
delete idxById[id];
|
|
387
|
+
items.splice(idx, 1);
|
|
388
|
+
updateIdxById(idx);
|
|
389
|
+
refresh();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function sortedAddItem(item) {
|
|
393
|
+
if(!sortComparer) {
|
|
394
|
+
throw new Error("sortedAddItem() requires a sort comparer, use sort()");
|
|
395
|
+
}
|
|
396
|
+
insertItem(sortedIndex(item), item);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function sortedUpdateItem(id, item) {
|
|
400
|
+
if (idxById[id] === undefined || id !== item[idProperty]) {
|
|
401
|
+
throw new Error("Invalid or non-matching id " + idxById[id]);
|
|
402
|
+
}
|
|
403
|
+
if(!sortComparer) {
|
|
404
|
+
throw new Error("sortedUpdateItem() requires a sort comparer, use sort()");
|
|
405
|
+
}
|
|
406
|
+
var oldItem = getItemById(id);
|
|
407
|
+
if(sortComparer(oldItem, item) !== 0) {
|
|
408
|
+
// item affects sorting -> must use sorted add
|
|
409
|
+
deleteItem(id);
|
|
410
|
+
sortedAddItem(item);
|
|
411
|
+
}
|
|
412
|
+
else { // update does not affect sorting -> regular update works fine
|
|
413
|
+
updateItem(id, item);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function sortedIndex(searchItem) {
|
|
418
|
+
var low = 0, high = items.length;
|
|
419
|
+
|
|
420
|
+
while (low < high) {
|
|
421
|
+
var mid = low + high >>> 1;
|
|
422
|
+
if (sortComparer(items[mid], searchItem) === -1) {
|
|
423
|
+
low = mid + 1;
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
high = mid;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return low;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function getLength() {
|
|
433
|
+
return rows.length;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function getItem(i) {
|
|
437
|
+
var item = rows[i];
|
|
438
|
+
|
|
439
|
+
// if this is a group row, make sure totals are calculated and update the title
|
|
440
|
+
if (item && item.__group && item.totals && !item.totals.initialized) {
|
|
441
|
+
var gi = groupingInfos[item.level];
|
|
442
|
+
if (!gi.displayTotalsRow) {
|
|
443
|
+
calculateTotals(item.totals);
|
|
444
|
+
item.title = gi.formatter ? gi.formatter(item) : item.value;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// if this is a totals row, make sure it's calculated
|
|
448
|
+
else if (item && item.__groupTotals && !item.initialized) {
|
|
449
|
+
calculateTotals(item);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return item;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function getItemMetadata(i) {
|
|
456
|
+
var item = rows[i];
|
|
457
|
+
if (item === undefined) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// overrides for grouping rows
|
|
462
|
+
if (item.__group) {
|
|
463
|
+
return options.groupItemMetadataProvider.getGroupRowMetadata(item);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// overrides for totals rows
|
|
467
|
+
if (item.__groupTotals) {
|
|
468
|
+
return options.groupItemMetadataProvider.getTotalsRowMetadata(item);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function expandCollapseAllGroups(level, collapse) {
|
|
475
|
+
if (level == null) {
|
|
476
|
+
for (var i = 0; i < groupingInfos.length; i++) {
|
|
477
|
+
toggledGroupsByLevel[i] = {};
|
|
478
|
+
groupingInfos[i].collapsed = collapse;
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
toggledGroupsByLevel[level] = {};
|
|
482
|
+
groupingInfos[level].collapsed = collapse;
|
|
483
|
+
}
|
|
484
|
+
refresh();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @param level {Number} Optional level to collapse. If not specified, applies to all levels.
|
|
489
|
+
*/
|
|
490
|
+
function collapseAllGroups(level) {
|
|
491
|
+
expandCollapseAllGroups(level, true);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @param level {Number} Optional level to expand. If not specified, applies to all levels.
|
|
496
|
+
*/
|
|
497
|
+
function expandAllGroups(level) {
|
|
498
|
+
expandCollapseAllGroups(level, false);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function expandCollapseGroup(level, groupingKey, collapse) {
|
|
502
|
+
toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse;
|
|
503
|
+
refresh();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @param varArgs Either a Slick.Group's "groupingKey" property, or a
|
|
508
|
+
* variable argument list of grouping values denoting a unique path to the row. For
|
|
509
|
+
* example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of
|
|
510
|
+
* the 'high' group.
|
|
511
|
+
*/
|
|
512
|
+
function collapseGroup(varArgs) {
|
|
513
|
+
var args = Array.prototype.slice.call(arguments);
|
|
514
|
+
var arg0 = args[0];
|
|
515
|
+
if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
|
|
516
|
+
expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true);
|
|
517
|
+
} else {
|
|
518
|
+
expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* @param varArgs Either a Slick.Group's "groupingKey" property, or a
|
|
524
|
+
* variable argument list of grouping values denoting a unique path to the row. For
|
|
525
|
+
* example, calling expandGroup('high', '10%') will expand the '10%' subgroup of
|
|
526
|
+
* the 'high' group.
|
|
527
|
+
*/
|
|
528
|
+
function expandGroup(varArgs) {
|
|
529
|
+
var args = Array.prototype.slice.call(arguments);
|
|
530
|
+
var arg0 = args[0];
|
|
531
|
+
if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
|
|
532
|
+
expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false);
|
|
533
|
+
} else {
|
|
534
|
+
expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function getGroups() {
|
|
539
|
+
return groups;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function extractGroups(rows, parentGroup) {
|
|
543
|
+
var group;
|
|
544
|
+
var val;
|
|
545
|
+
var groups = [];
|
|
546
|
+
var groupsByVal = {};
|
|
547
|
+
var r;
|
|
548
|
+
var level = parentGroup ? parentGroup.level + 1 : 0;
|
|
549
|
+
var gi = groupingInfos[level];
|
|
550
|
+
|
|
551
|
+
for (var i = 0, l = gi.predefinedValues.length; i < l; i++) {
|
|
552
|
+
val = gi.predefinedValues[i];
|
|
553
|
+
group = groupsByVal[val];
|
|
554
|
+
if (!group) {
|
|
555
|
+
group = new Slick.Group();
|
|
556
|
+
group.value = val;
|
|
557
|
+
group.level = level;
|
|
558
|
+
group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
|
|
559
|
+
groups[groups.length] = group;
|
|
560
|
+
groupsByVal[val] = group;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
for (var i = 0, l = rows.length; i < l; i++) {
|
|
565
|
+
r = rows[i];
|
|
566
|
+
val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter];
|
|
567
|
+
group = groupsByVal[val];
|
|
568
|
+
if (!group) {
|
|
569
|
+
group = new Slick.Group();
|
|
570
|
+
group.value = val;
|
|
571
|
+
group.level = level;
|
|
572
|
+
group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
|
|
573
|
+
groups[groups.length] = group;
|
|
574
|
+
groupsByVal[val] = group;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
group.rows[group.count++] = r;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (level < groupingInfos.length - 1) {
|
|
581
|
+
for (var i = 0; i < groups.length; i++) {
|
|
582
|
+
group = groups[i];
|
|
583
|
+
group.groups = extractGroups(group.rows, group);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
groups.sort(groupingInfos[level].comparer);
|
|
588
|
+
|
|
589
|
+
return groups;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function calculateTotals(totals) {
|
|
593
|
+
var group = totals.group;
|
|
594
|
+
var gi = groupingInfos[group.level];
|
|
595
|
+
var isLeafLevel = (group.level == groupingInfos.length);
|
|
596
|
+
var agg, idx = gi.aggregators.length;
|
|
597
|
+
|
|
598
|
+
if (!isLeafLevel && gi.aggregateChildGroups) {
|
|
599
|
+
// make sure all the subgroups are calculated
|
|
600
|
+
var i = group.groups.length;
|
|
601
|
+
while (i--) {
|
|
602
|
+
if (!group.groups[i].totals.initialized) {
|
|
603
|
+
calculateTotals(group.groups[i].totals);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
while (idx--) {
|
|
609
|
+
agg = gi.aggregators[idx];
|
|
610
|
+
agg.init();
|
|
611
|
+
if (!isLeafLevel && gi.aggregateChildGroups) {
|
|
612
|
+
gi.compiledAccumulators[idx].call(agg, group.groups);
|
|
613
|
+
} else {
|
|
614
|
+
gi.compiledAccumulators[idx].call(agg, group.rows);
|
|
615
|
+
}
|
|
616
|
+
agg.storeResult(totals);
|
|
617
|
+
}
|
|
618
|
+
totals.initialized = true;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function addGroupTotals(group) {
|
|
622
|
+
var gi = groupingInfos[group.level];
|
|
623
|
+
var totals = new Slick.GroupTotals();
|
|
624
|
+
totals.group = group;
|
|
625
|
+
group.totals = totals;
|
|
626
|
+
if (!gi.lazyTotalsCalculation) {
|
|
627
|
+
calculateTotals(totals);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function addTotals(groups, level) {
|
|
632
|
+
level = level || 0;
|
|
633
|
+
var gi = groupingInfos[level];
|
|
634
|
+
var groupCollapsed = gi.collapsed;
|
|
635
|
+
var toggledGroups = toggledGroupsByLevel[level];
|
|
636
|
+
var idx = groups.length, g;
|
|
637
|
+
while (idx--) {
|
|
638
|
+
g = groups[idx];
|
|
639
|
+
|
|
640
|
+
if (g.collapsed && !gi.aggregateCollapsed) {
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Do a depth-first aggregation so that parent group aggregators can access subgroup totals.
|
|
645
|
+
if (g.groups) {
|
|
646
|
+
addTotals(g.groups, level + 1);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (gi.aggregators.length && (
|
|
650
|
+
gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) {
|
|
651
|
+
addGroupTotals(g);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey];
|
|
655
|
+
g.title = gi.formatter ? gi.formatter(g) : g.value;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function flattenGroupedRows(groups, level) {
|
|
660
|
+
level = level || 0;
|
|
661
|
+
var gi = groupingInfos[level];
|
|
662
|
+
var groupedRows = [], rows, gl = 0, g;
|
|
663
|
+
for (var i = 0, l = groups.length; i < l; i++) {
|
|
664
|
+
g = groups[i];
|
|
665
|
+
groupedRows[gl++] = g;
|
|
666
|
+
|
|
667
|
+
if (!g.collapsed) {
|
|
668
|
+
rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows;
|
|
669
|
+
for (var j = 0, jj = rows.length; j < jj; j++) {
|
|
670
|
+
groupedRows[gl++] = rows[j];
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) {
|
|
675
|
+
groupedRows[gl++] = g.totals;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return groupedRows;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function getFunctionInfo(fn) {
|
|
682
|
+
var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/;
|
|
683
|
+
var matches = fn.toString().match(fnRegex);
|
|
684
|
+
return {
|
|
685
|
+
params: matches[1].split(","),
|
|
686
|
+
body: matches[2]
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function compileAccumulatorLoop(aggregator) {
|
|
691
|
+
var accumulatorInfo = getFunctionInfo(aggregator.accumulate);
|
|
692
|
+
var fn = new Function(
|
|
693
|
+
"_items",
|
|
694
|
+
"for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" +
|
|
695
|
+
accumulatorInfo.params[0] + " = _items[_i]; " +
|
|
696
|
+
accumulatorInfo.body +
|
|
697
|
+
"}"
|
|
698
|
+
);
|
|
699
|
+
fn.displayName = fn.name = "compiledAccumulatorLoop";
|
|
700
|
+
return fn;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function compileFilter() {
|
|
704
|
+
var filterInfo = getFunctionInfo(filter);
|
|
705
|
+
|
|
706
|
+
var filterPath1 = "{ continue _coreloop; }$1";
|
|
707
|
+
var filterPath2 = "{ _retval[_idx++] = $item$; continue _coreloop; }$1";
|
|
708
|
+
// make some allowances for minification - there's only so far we can go with RegEx
|
|
709
|
+
var filterBody = filterInfo.body
|
|
710
|
+
.replace(/return false\s*([;}]|\}|$)/gi, filterPath1)
|
|
711
|
+
.replace(/return!1([;}]|\}|$)/gi, filterPath1)
|
|
712
|
+
.replace(/return true\s*([;}]|\}|$)/gi, filterPath2)
|
|
713
|
+
.replace(/return!0([;}]|\}|$)/gi, filterPath2)
|
|
714
|
+
.replace(/return ([^;}]+?)\s*([;}]|$)/gi,
|
|
715
|
+
"{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
|
|
716
|
+
|
|
717
|
+
// This preserves the function template code after JS compression,
|
|
718
|
+
// so that replace() commands still work as expected.
|
|
719
|
+
var tpl = [
|
|
720
|
+
//"function(_items, _args) { ",
|
|
721
|
+
"var _retval = [], _idx = 0; ",
|
|
722
|
+
"var $item$, $args$ = _args; ",
|
|
723
|
+
"_coreloop: ",
|
|
724
|
+
"for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
|
|
725
|
+
"$item$ = _items[_i]; ",
|
|
726
|
+
"$filter$; ",
|
|
727
|
+
"} ",
|
|
728
|
+
"return _retval; "
|
|
729
|
+
//"}"
|
|
730
|
+
].join("");
|
|
731
|
+
tpl = tpl.replace(/\$filter\$/gi, filterBody);
|
|
732
|
+
tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
|
|
733
|
+
tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
|
|
734
|
+
|
|
735
|
+
var fn = new Function("_items,_args", tpl);
|
|
736
|
+
fn.displayName = fn.name = "compiledFilter";
|
|
737
|
+
return fn;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function compileFilterWithCaching() {
|
|
741
|
+
var filterInfo = getFunctionInfo(filter);
|
|
742
|
+
|
|
743
|
+
var filterPath1 = "{ continue _coreloop; }$1";
|
|
744
|
+
var filterPath2 = "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1";
|
|
745
|
+
// make some allowances for minification - there's only so far we can go with RegEx
|
|
746
|
+
var filterBody = filterInfo.body
|
|
747
|
+
.replace(/return false\s*([;}]|\}|$)/gi, filterPath1)
|
|
748
|
+
.replace(/return!1([;}]|\}|$)/gi, filterPath1)
|
|
749
|
+
.replace(/return true\s*([;}]|\}|$)/gi, filterPath2)
|
|
750
|
+
.replace(/return!0([;}]|\}|$)/gi, filterPath2)
|
|
751
|
+
.replace(/return ([^;}]+?)\s*([;}]|$)/gi,
|
|
752
|
+
"{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
|
|
753
|
+
|
|
754
|
+
// This preserves the function template code after JS compression,
|
|
755
|
+
// so that replace() commands still work as expected.
|
|
756
|
+
var tpl = [
|
|
757
|
+
//"function(_items, _args, _cache) { ",
|
|
758
|
+
"var _retval = [], _idx = 0; ",
|
|
759
|
+
"var $item$, $args$ = _args; ",
|
|
760
|
+
"_coreloop: ",
|
|
761
|
+
"for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
|
|
762
|
+
"$item$ = _items[_i]; ",
|
|
763
|
+
"if (_cache[_i]) { ",
|
|
764
|
+
"_retval[_idx++] = $item$; ",
|
|
765
|
+
"continue _coreloop; ",
|
|
766
|
+
"} ",
|
|
767
|
+
"$filter$; ",
|
|
768
|
+
"} ",
|
|
769
|
+
"return _retval; "
|
|
770
|
+
//"}"
|
|
771
|
+
].join("");
|
|
772
|
+
tpl = tpl.replace(/\$filter\$/gi, filterBody);
|
|
773
|
+
tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
|
|
774
|
+
tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
|
|
775
|
+
|
|
776
|
+
var fn = new Function("_items,_args,_cache", tpl);
|
|
777
|
+
fn.displayName = fn.name = "compiledFilterWithCaching";
|
|
778
|
+
return fn;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function uncompiledFilter(items, args) {
|
|
782
|
+
var retval = [], idx = 0;
|
|
783
|
+
|
|
784
|
+
for (var i = 0, ii = items.length; i < ii; i++) {
|
|
785
|
+
if (filter(items[i], args)) {
|
|
786
|
+
retval[idx++] = items[i];
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return retval;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function uncompiledFilterWithCaching(items, args, cache) {
|
|
794
|
+
var retval = [], idx = 0, item;
|
|
795
|
+
|
|
796
|
+
for (var i = 0, ii = items.length; i < ii; i++) {
|
|
797
|
+
item = items[i];
|
|
798
|
+
if (cache[i]) {
|
|
799
|
+
retval[idx++] = item;
|
|
800
|
+
} else if (filter(item, args)) {
|
|
801
|
+
retval[idx++] = item;
|
|
802
|
+
cache[i] = true;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return retval;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function getFilteredAndPagedItems(items) {
|
|
810
|
+
if (filter) {
|
|
811
|
+
var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter;
|
|
812
|
+
var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching;
|
|
813
|
+
|
|
814
|
+
if (refreshHints.isFilterNarrowing) {
|
|
815
|
+
filteredItems = batchFilter(filteredItems, filterArgs);
|
|
816
|
+
} else if (refreshHints.isFilterExpanding) {
|
|
817
|
+
filteredItems = batchFilterWithCaching(items, filterArgs, filterCache);
|
|
818
|
+
} else if (!refreshHints.isFilterUnchanged) {
|
|
819
|
+
filteredItems = batchFilter(items, filterArgs);
|
|
820
|
+
}
|
|
821
|
+
} else {
|
|
822
|
+
// special case: if not filtering and not paging, the resulting
|
|
823
|
+
// rows collection needs to be a copy so that changes due to sort
|
|
824
|
+
// can be caught
|
|
825
|
+
filteredItems = pagesize ? items : items.concat();
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// get the current page
|
|
829
|
+
var paged;
|
|
830
|
+
if (pagesize) {
|
|
831
|
+
if (filteredItems.length <= pagenum * pagesize) {
|
|
832
|
+
if (filteredItems.length === 0) {
|
|
833
|
+
pagenum = 0;
|
|
834
|
+
} else {
|
|
835
|
+
pagenum = Math.floor((filteredItems.length - 1) / pagesize);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize);
|
|
839
|
+
} else {
|
|
840
|
+
paged = filteredItems;
|
|
841
|
+
}
|
|
842
|
+
return {totalRows: filteredItems.length, rows: paged};
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function getRowDiffs(rows, newRows) {
|
|
846
|
+
var item, r, eitherIsNonData, diff = [];
|
|
847
|
+
var from = 0, to = newRows.length;
|
|
848
|
+
|
|
849
|
+
if (refreshHints && refreshHints.ignoreDiffsBefore) {
|
|
850
|
+
from = Math.max(0,
|
|
851
|
+
Math.min(newRows.length, refreshHints.ignoreDiffsBefore));
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (refreshHints && refreshHints.ignoreDiffsAfter) {
|
|
855
|
+
to = Math.min(newRows.length,
|
|
856
|
+
Math.max(0, refreshHints.ignoreDiffsAfter));
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
for (var i = from, rl = rows.length; i < to; i++) {
|
|
860
|
+
if (i >= rl) {
|
|
861
|
+
diff[diff.length] = i;
|
|
862
|
+
} else {
|
|
863
|
+
item = newRows[i];
|
|
864
|
+
r = rows[i];
|
|
865
|
+
|
|
866
|
+
if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) &&
|
|
867
|
+
item.__group !== r.__group ||
|
|
868
|
+
item.__group && !item.equals(r))
|
|
869
|
+
|| (eitherIsNonData &&
|
|
870
|
+
// no good way to compare totals since they are arbitrary DTOs
|
|
871
|
+
// deep object comparison is pretty expensive
|
|
872
|
+
// always considering them 'dirty' seems easier for the time being
|
|
873
|
+
(item.__groupTotals || r.__groupTotals))
|
|
874
|
+
|| item[idProperty] != r[idProperty]
|
|
875
|
+
|| (updated && updated[item[idProperty]])
|
|
876
|
+
) {
|
|
877
|
+
diff[diff.length] = i;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return diff;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function recalc(_items) {
|
|
885
|
+
rowsById = null;
|
|
886
|
+
|
|
887
|
+
if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing ||
|
|
888
|
+
refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) {
|
|
889
|
+
filterCache = [];
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
var filteredItems = getFilteredAndPagedItems(_items);
|
|
893
|
+
totalRows = filteredItems.totalRows;
|
|
894
|
+
var newRows = filteredItems.rows;
|
|
895
|
+
|
|
896
|
+
groups = [];
|
|
897
|
+
if (groupingInfos.length) {
|
|
898
|
+
groups = extractGroups(newRows);
|
|
899
|
+
if (groups.length) {
|
|
900
|
+
addTotals(groups);
|
|
901
|
+
newRows = flattenGroupedRows(groups);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
var diff = getRowDiffs(rows, newRows);
|
|
906
|
+
|
|
907
|
+
rows = newRows;
|
|
908
|
+
|
|
909
|
+
return diff;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function refresh() {
|
|
913
|
+
if (suspend) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
var countBefore = rows.length;
|
|
918
|
+
var totalRowsBefore = totalRows;
|
|
919
|
+
|
|
920
|
+
var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit
|
|
921
|
+
|
|
922
|
+
// if the current page is no longer valid, go to last page and recalc
|
|
923
|
+
// we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
|
|
924
|
+
if (pagesize && totalRows < pagenum * pagesize) {
|
|
925
|
+
pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1);
|
|
926
|
+
diff = recalc(items, filter);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
updated = null;
|
|
930
|
+
prevRefreshHints = refreshHints;
|
|
931
|
+
refreshHints = {};
|
|
932
|
+
|
|
933
|
+
if (totalRowsBefore !== totalRows) {
|
|
934
|
+
onPagingInfoChanged.notify(getPagingInfo(), null, self);
|
|
935
|
+
}
|
|
936
|
+
if (countBefore !== rows.length) {
|
|
937
|
+
onRowCountChanged.notify({previous: countBefore, current: rows.length, dataView: self}, null, self);
|
|
938
|
+
}
|
|
939
|
+
if (diff.length > 0) {
|
|
940
|
+
onRowsChanged.notify({rows: diff, dataView: self}, null, self);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/***
|
|
945
|
+
* Wires the grid and the DataView together to keep row selection tied to item ids.
|
|
946
|
+
* This is useful since, without it, the grid only knows about rows, so if the items
|
|
947
|
+
* move around, the same rows stay selected instead of the selection moving along
|
|
948
|
+
* with the items.
|
|
949
|
+
*
|
|
950
|
+
* NOTE: This doesn't work with cell selection model.
|
|
951
|
+
*
|
|
952
|
+
* @param grid {Slick.Grid} The grid to sync selection with.
|
|
953
|
+
* @param preserveHidden {Boolean} Whether to keep selected items that go out of the
|
|
954
|
+
* view due to them getting filtered out.
|
|
955
|
+
* @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items
|
|
956
|
+
* that are currently out of the view (see preserveHidden) as selected when selection
|
|
957
|
+
* changes.
|
|
958
|
+
* @return {Slick.Event} An event that notifies when an internal list of selected row ids
|
|
959
|
+
* changes. This is useful since, in combination with the above two options, it allows
|
|
960
|
+
* access to the full list selected row ids, and not just the ones visible to the grid.
|
|
961
|
+
* @method syncGridSelection
|
|
962
|
+
*/
|
|
963
|
+
function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) {
|
|
964
|
+
var self = this;
|
|
965
|
+
var inHandler;
|
|
966
|
+
var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
|
|
967
|
+
var onSelectedRowIdsChanged = new Slick.Event();
|
|
968
|
+
|
|
969
|
+
function setSelectedRowIds(rowIds) {
|
|
970
|
+
if (selectedRowIds.join(",") == rowIds.join(",")) {
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
selectedRowIds = rowIds;
|
|
975
|
+
|
|
976
|
+
onSelectedRowIdsChanged.notify({
|
|
977
|
+
"grid": grid,
|
|
978
|
+
"ids": selectedRowIds,
|
|
979
|
+
"dataView": self
|
|
980
|
+
}, new Slick.EventData(), self);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function update() {
|
|
984
|
+
if (selectedRowIds.length > 0) {
|
|
985
|
+
inHandler = true;
|
|
986
|
+
var selectedRows = self.mapIdsToRows(selectedRowIds);
|
|
987
|
+
if (!preserveHidden) {
|
|
988
|
+
setSelectedRowIds(self.mapRowsToIds(selectedRows));
|
|
989
|
+
}
|
|
990
|
+
grid.setSelectedRows(selectedRows);
|
|
991
|
+
inHandler = false;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
grid.onSelectedRowsChanged.subscribe(function(e, args) {
|
|
996
|
+
if (inHandler) { return; }
|
|
997
|
+
var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
|
|
998
|
+
if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) {
|
|
999
|
+
setSelectedRowIds(newSelectedRowIds);
|
|
1000
|
+
} else {
|
|
1001
|
+
// keep the ones that are hidden
|
|
1002
|
+
var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; });
|
|
1003
|
+
// add the newly selected ones
|
|
1004
|
+
setSelectedRowIds(existing.concat(newSelectedRowIds));
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
this.onRowsChanged.subscribe(update);
|
|
1009
|
+
|
|
1010
|
+
this.onRowCountChanged.subscribe(update);
|
|
1011
|
+
|
|
1012
|
+
return onSelectedRowIdsChanged;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function syncGridCellCssStyles(grid, key) {
|
|
1016
|
+
var hashById;
|
|
1017
|
+
var inHandler;
|
|
1018
|
+
|
|
1019
|
+
// since this method can be called after the cell styles have been set,
|
|
1020
|
+
// get the existing ones right away
|
|
1021
|
+
storeCellCssStyles(grid.getCellCssStyles(key));
|
|
1022
|
+
|
|
1023
|
+
function storeCellCssStyles(hash) {
|
|
1024
|
+
hashById = {};
|
|
1025
|
+
for (var row in hash) {
|
|
1026
|
+
var id = rows[row][idProperty];
|
|
1027
|
+
hashById[id] = hash[row];
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function update() {
|
|
1032
|
+
if (hashById) {
|
|
1033
|
+
inHandler = true;
|
|
1034
|
+
ensureRowsByIdCache();
|
|
1035
|
+
var newHash = {};
|
|
1036
|
+
for (var id in hashById) {
|
|
1037
|
+
var row = rowsById[id];
|
|
1038
|
+
if (row != undefined) {
|
|
1039
|
+
newHash[row] = hashById[id];
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
grid.setCellCssStyles(key, newHash);
|
|
1043
|
+
inHandler = false;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
grid.onCellCssStylesChanged.subscribe(function(e, args) {
|
|
1048
|
+
if (inHandler) { return; }
|
|
1049
|
+
if (key != args.key) { return; }
|
|
1050
|
+
if (args.hash) {
|
|
1051
|
+
storeCellCssStyles(args.hash);
|
|
1052
|
+
} else {
|
|
1053
|
+
grid.onCellCssStylesChanged.unsubscribe(styleChanged);
|
|
1054
|
+
self.onRowsChanged.unsubscribe(update);
|
|
1055
|
+
self.onRowCountChanged.unsubscribe(update);
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
this.onRowsChanged.subscribe(update);
|
|
1060
|
+
|
|
1061
|
+
this.onRowCountChanged.subscribe(update);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
$.extend(this, {
|
|
1065
|
+
// methods
|
|
1066
|
+
"beginUpdate": beginUpdate,
|
|
1067
|
+
"endUpdate": endUpdate,
|
|
1068
|
+
"setPagingOptions": setPagingOptions,
|
|
1069
|
+
"getPagingInfo": getPagingInfo,
|
|
1070
|
+
"getItems": getItems,
|
|
1071
|
+
"setItems": setItems,
|
|
1072
|
+
"setFilter": setFilter,
|
|
1073
|
+
"getFilter": getFilter,
|
|
1074
|
+
"getFilteredItems": getFilteredItems,
|
|
1075
|
+
"sort": sort,
|
|
1076
|
+
"fastSort": fastSort,
|
|
1077
|
+
"reSort": reSort,
|
|
1078
|
+
"setGrouping": setGrouping,
|
|
1079
|
+
"getGrouping": getGrouping,
|
|
1080
|
+
"groupBy": groupBy,
|
|
1081
|
+
"setAggregators": setAggregators,
|
|
1082
|
+
"collapseAllGroups": collapseAllGroups,
|
|
1083
|
+
"expandAllGroups": expandAllGroups,
|
|
1084
|
+
"collapseGroup": collapseGroup,
|
|
1085
|
+
"expandGroup": expandGroup,
|
|
1086
|
+
"getGroups": getGroups,
|
|
1087
|
+
"getIdxById": getIdxById,
|
|
1088
|
+
"getRowByItem": getRowByItem,
|
|
1089
|
+
"getRowById": getRowById,
|
|
1090
|
+
"getItemById": getItemById,
|
|
1091
|
+
"getItemByIdx": getItemByIdx,
|
|
1092
|
+
"mapItemsToRows": mapItemsToRows,
|
|
1093
|
+
"mapRowsToIds": mapRowsToIds,
|
|
1094
|
+
"mapIdsToRows": mapIdsToRows,
|
|
1095
|
+
"setRefreshHints": setRefreshHints,
|
|
1096
|
+
"setFilterArgs": setFilterArgs,
|
|
1097
|
+
"refresh": refresh,
|
|
1098
|
+
"updateItem": updateItem,
|
|
1099
|
+
"insertItem": insertItem,
|
|
1100
|
+
"addItem": addItem,
|
|
1101
|
+
"deleteItem": deleteItem,
|
|
1102
|
+
"sortedAddItem": sortedAddItem,
|
|
1103
|
+
"sortedUpdateItem": sortedUpdateItem,
|
|
1104
|
+
"syncGridSelection": syncGridSelection,
|
|
1105
|
+
"syncGridCellCssStyles": syncGridCellCssStyles,
|
|
1106
|
+
|
|
1107
|
+
// data provider methods
|
|
1108
|
+
"getLength": getLength,
|
|
1109
|
+
"getItem": getItem,
|
|
1110
|
+
"getItemMetadata": getItemMetadata,
|
|
1111
|
+
|
|
1112
|
+
// events
|
|
1113
|
+
"onRowCountChanged": onRowCountChanged,
|
|
1114
|
+
"onRowsChanged": onRowsChanged,
|
|
1115
|
+
"onPagingInfoChanged": onPagingInfoChanged
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function AvgAggregator(field) {
|
|
1120
|
+
this.field_ = field;
|
|
1121
|
+
|
|
1122
|
+
this.init = function () {
|
|
1123
|
+
this.count_ = 0;
|
|
1124
|
+
this.nonNullCount_ = 0;
|
|
1125
|
+
this.sum_ = 0;
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
this.accumulate = function (item) {
|
|
1129
|
+
var val = item[this.field_];
|
|
1130
|
+
this.count_++;
|
|
1131
|
+
if (val != null && val !== "" && !isNaN(val)) {
|
|
1132
|
+
this.nonNullCount_++;
|
|
1133
|
+
this.sum_ += parseFloat(val);
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
this.storeResult = function (groupTotals) {
|
|
1138
|
+
if (!groupTotals.avg) {
|
|
1139
|
+
groupTotals.avg = {};
|
|
1140
|
+
}
|
|
1141
|
+
if (this.nonNullCount_ != 0) {
|
|
1142
|
+
groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_;
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
function MinAggregator(field) {
|
|
1148
|
+
this.field_ = field;
|
|
1149
|
+
|
|
1150
|
+
this.init = function () {
|
|
1151
|
+
this.min_ = null;
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
this.accumulate = function (item) {
|
|
1155
|
+
var val = item[this.field_];
|
|
1156
|
+
if (val != null && val !== "" && !isNaN(val)) {
|
|
1157
|
+
if (this.min_ == null || val < this.min_) {
|
|
1158
|
+
this.min_ = val;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
this.storeResult = function (groupTotals) {
|
|
1164
|
+
if (!groupTotals.min) {
|
|
1165
|
+
groupTotals.min = {};
|
|
1166
|
+
}
|
|
1167
|
+
groupTotals.min[this.field_] = this.min_;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function MaxAggregator(field) {
|
|
1172
|
+
this.field_ = field;
|
|
1173
|
+
|
|
1174
|
+
this.init = function () {
|
|
1175
|
+
this.max_ = null;
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
this.accumulate = function (item) {
|
|
1179
|
+
var val = item[this.field_];
|
|
1180
|
+
if (val != null && val !== "" && !isNaN(val)) {
|
|
1181
|
+
if (this.max_ == null || val > this.max_) {
|
|
1182
|
+
this.max_ = val;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
this.storeResult = function (groupTotals) {
|
|
1188
|
+
if (!groupTotals.max) {
|
|
1189
|
+
groupTotals.max = {};
|
|
1190
|
+
}
|
|
1191
|
+
groupTotals.max[this.field_] = this.max_;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function SumAggregator(field) {
|
|
1196
|
+
this.field_ = field;
|
|
1197
|
+
|
|
1198
|
+
this.init = function () {
|
|
1199
|
+
this.sum_ = null;
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
this.accumulate = function (item) {
|
|
1203
|
+
var val = item[this.field_];
|
|
1204
|
+
if (val != null && val !== "" && !isNaN(val)) {
|
|
1205
|
+
this.sum_ += parseFloat(val);
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
this.storeResult = function (groupTotals) {
|
|
1210
|
+
if (!groupTotals.sum) {
|
|
1211
|
+
groupTotals.sum = {};
|
|
1212
|
+
}
|
|
1213
|
+
groupTotals.sum[this.field_] = this.sum_;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// TODO: add more built-in aggregators
|
|
1218
|
+
// TODO: merge common aggregators in one to prevent needles iterating
|
|
1219
|
+
|
|
1220
|
+
})(jQuery);
|