slickgrid-rails 0.2.0 → 0.3.0
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 +2 -0
- data/README.md +9 -0
- data/Rakefile +33 -0
- data/slickgrid-rails.gemspec +1 -2
- data/vendor/assets/javascripts/slick/controls/columnpicker.css +31 -0
- data/vendor/assets/javascripts/slick/controls/columnpicker.js +32 -0
- data/vendor/assets/javascripts/slick/controls/pager.css +41 -0
- data/vendor/assets/javascripts/slick/controls/pager.js +4 -8
- data/vendor/assets/javascripts/slick/core.js +35 -1
- data/vendor/assets/javascripts/slick/dataview.js +240 -85
- data/vendor/assets/javascripts/slick/editors.js +5 -5
- data/vendor/assets/javascripts/slick/formatters.js +5 -1
- data/vendor/assets/javascripts/slick/grid.css +157 -0
- data/vendor/assets/javascripts/slick/grid.js +770 -269
- data/vendor/assets/javascripts/slick/groupitemmetadataprovider.js +15 -10
- data/vendor/assets/javascripts/slick/plugins/autotooltips.js +49 -14
- data/vendor/assets/javascripts/slick/plugins/cellrangeselector.js +9 -8
- data/vendor/assets/javascripts/slick/plugins/cellselectionmodel.js +62 -2
- data/vendor/assets/javascripts/slick/plugins/checkboxselectcolumn.js +8 -9
- data/vendor/assets/javascripts/slick/plugins/headerbuttons.css +39 -0
- data/vendor/assets/javascripts/slick/plugins/headerbuttons.js +177 -0
- data/vendor/assets/javascripts/slick/plugins/headermenu.css +59 -0
- data/vendor/assets/javascripts/slick/plugins/headermenu.js +275 -0
- data/vendor/assets/javascripts/slick/plugins/rowmovemanager.js +17 -11
- data/vendor/assets/javascripts/slick/plugins/rowselectionmodel.js +1 -1
- data/vendor/assets/javascripts/slick/remotemodel.js +164 -0
- data/vendor/assets/stylesheets/slick/controls/columnpicker.css +31 -0
- data/vendor/assets/stylesheets/slick/controls/pager.css +41 -0
- data/vendor/assets/stylesheets/slick/grid.css +157 -0
- data/vendor/assets/stylesheets/slick/plugins/headerbuttons.css +39 -0
- data/vendor/assets/stylesheets/slick/plugins/headermenu.css +59 -0
- metadata +51 -17
- data/.rvmrc +0 -1
- data/Gemfile.lock +0 -85
- data/fetch.sh +0 -8
- data/lib/slickgrid/rails/version.rb +0 -6
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -125,6 +125,15 @@ gem):
|
|
125
125
|
@grid.onCellChange.subscribe (e, args) =>
|
126
126
|
@model.writeData(args)
|
127
127
|
|
128
|
+
## Update SlickGrid
|
129
|
+
|
130
|
+
To upgrade SlickGrid version just run
|
131
|
+
|
132
|
+
rake slickgrid:update
|
133
|
+
|
134
|
+
It will clone the current SlickGrid master and will copy all javascript and stylesheet files into this repository.
|
135
|
+
Don't forget to update the gemspec version!
|
136
|
+
|
128
137
|
## Contributing
|
129
138
|
|
130
139
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -1,2 +1,35 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
namespace :slickgrid do
|
5
|
+
desc "Update SlickGrid library from current master"
|
6
|
+
task :update => "tmp/SlickGrid" do
|
7
|
+
cd "tmp/SlickGrid" do
|
8
|
+
js_files = Dir.glob("*.js") +
|
9
|
+
Dir.glob("plugins/*.js") +
|
10
|
+
Dir.glob("controls/*.js")
|
11
|
+
|
12
|
+
js_files.each do |file|
|
13
|
+
mkdir_p "../../vendor/assets/javascript/slick/#{File.dirname(file)}"
|
14
|
+
sh "cp #{file} ../../vendor/assets/javascripts/slick/#{file.gsub("slick.", "")}"
|
15
|
+
end
|
16
|
+
|
17
|
+
css_files = Dir.glob("*.css") +
|
18
|
+
Dir.glob("plugins/*.css") +
|
19
|
+
Dir.glob("controls/*.css")
|
20
|
+
|
21
|
+
css_files.each do |file|
|
22
|
+
mkdir_p "../../vendor/assets/stylesheets/slick/#{File.dirname(file)}"
|
23
|
+
sh "cp #{file} ../../vendor/assets/stylesheets/slick/#{file.gsub("slick.", "")}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
file "tmp/SlickGrid" do
|
29
|
+
mkdir_p "tmp"
|
30
|
+
|
31
|
+
cd "tmp" do
|
32
|
+
sh "git clone https://github.com/mleibman/SlickGrid.git"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/slickgrid-rails.gemspec
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require File.expand_path('../lib/slickgrid/rails/version', __FILE__)
|
3
2
|
|
4
3
|
Gem::Specification.new do |gem|
|
5
4
|
gem.name = "slickgrid-rails"
|
6
|
-
gem.version =
|
5
|
+
gem.version = "0.3.0"
|
7
6
|
gem.authors = ["Benedikt Böhm"]
|
8
7
|
gem.email = ["benedikt.boehm@madvertise.com"]
|
9
8
|
gem.description = %q{SlickGrid Integration for Rails 3.x}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
.slick-columnpicker {
|
2
|
+
border: 1px solid #718BB7;
|
3
|
+
background: #f0f0f0;
|
4
|
+
padding: 6px;
|
5
|
+
-moz-box-shadow: 2px 2px 2px silver;
|
6
|
+
-webkit-box-shadow: 2px 2px 2px silver;
|
7
|
+
box-shadow: 2px 2px 2px silver;
|
8
|
+
min-width: 100px;
|
9
|
+
cursor: default;
|
10
|
+
}
|
11
|
+
|
12
|
+
.slick-columnpicker li {
|
13
|
+
list-style: none;
|
14
|
+
margin: 0;
|
15
|
+
padding: 0;
|
16
|
+
background: none;
|
17
|
+
}
|
18
|
+
|
19
|
+
.slick-columnpicker input {
|
20
|
+
margin: 4px;
|
21
|
+
}
|
22
|
+
|
23
|
+
.slick-columnpicker li a {
|
24
|
+
display: block;
|
25
|
+
padding: 4px;
|
26
|
+
font-weight: bold;
|
27
|
+
}
|
28
|
+
|
29
|
+
.slick-columnpicker li a:hover {
|
30
|
+
background: white;
|
31
|
+
}
|
@@ -9,6 +9,7 @@
|
|
9
9
|
|
10
10
|
function init() {
|
11
11
|
grid.onHeaderContextMenu.subscribe(handleHeaderContextMenu);
|
12
|
+
grid.onColumnsReordered.subscribe(updateColumnOrder);
|
12
13
|
options = $.extend({}, defaults, options);
|
13
14
|
|
14
15
|
$menu = $("<span class='slick-columnpicker' style='display:none;position:absolute;z-index:20;' />").appendTo(document.body);
|
@@ -23,6 +24,7 @@
|
|
23
24
|
function handleHeaderContextMenu(e, args) {
|
24
25
|
e.preventDefault();
|
25
26
|
$menu.empty();
|
27
|
+
updateColumnOrder();
|
26
28
|
columnCheckboxes = [];
|
27
29
|
|
28
30
|
var $li, $input;
|
@@ -68,6 +70,28 @@
|
|
68
70
|
.fadeIn(options.fadeSpeed);
|
69
71
|
}
|
70
72
|
|
73
|
+
function updateColumnOrder() {
|
74
|
+
// Because columns can be reordered, we have to update the `columns`
|
75
|
+
// to reflect the new order, however we can't just take `grid.getColumns()`,
|
76
|
+
// as it does not include columns currently hidden by the picker.
|
77
|
+
// We create a new `columns` structure by leaving currently-hidden
|
78
|
+
// columns in their original ordinal position and interleaving the results
|
79
|
+
// of the current column sort.
|
80
|
+
var current = grid.getColumns().slice(0);
|
81
|
+
var ordered = new Array(columns.length);
|
82
|
+
for (var i = 0; i < ordered.length; i++) {
|
83
|
+
if ( grid.getColumnIndex(columns[i].id) === undefined ) {
|
84
|
+
// If the column doesn't return a value from getColumnIndex,
|
85
|
+
// it is hidden. Leave it in this position.
|
86
|
+
ordered[i] = columns[i];
|
87
|
+
} else {
|
88
|
+
// Otherwise, grab the next visible column.
|
89
|
+
ordered[i] = current.shift();
|
90
|
+
}
|
91
|
+
}
|
92
|
+
columns = ordered;
|
93
|
+
}
|
94
|
+
|
71
95
|
function updateColumn(e) {
|
72
96
|
if ($(e.target).data("option") == "autoresize") {
|
73
97
|
if (e.target.checked) {
|
@@ -105,7 +129,15 @@
|
|
105
129
|
}
|
106
130
|
}
|
107
131
|
|
132
|
+
function getAllColumns() {
|
133
|
+
return columns;
|
134
|
+
}
|
135
|
+
|
108
136
|
init();
|
137
|
+
|
138
|
+
return {
|
139
|
+
"getAllColumns": getAllColumns
|
140
|
+
};
|
109
141
|
}
|
110
142
|
|
111
143
|
// Slick.Controls.ColumnPicker
|
@@ -0,0 +1,41 @@
|
|
1
|
+
.slick-pager {
|
2
|
+
width: 100%;
|
3
|
+
height: 26px;
|
4
|
+
border: 1px solid gray;
|
5
|
+
border-top: 0;
|
6
|
+
background: url('../images/header-columns-bg.gif') repeat-x center bottom;
|
7
|
+
vertical-align: middle;
|
8
|
+
}
|
9
|
+
|
10
|
+
.slick-pager .slick-pager-status {
|
11
|
+
display: inline-block;
|
12
|
+
padding: 6px;
|
13
|
+
}
|
14
|
+
|
15
|
+
.slick-pager .ui-icon-container {
|
16
|
+
display: inline-block;
|
17
|
+
margin: 2px;
|
18
|
+
border-color: gray;
|
19
|
+
}
|
20
|
+
|
21
|
+
.slick-pager .slick-pager-nav {
|
22
|
+
display: inline-block;
|
23
|
+
float: left;
|
24
|
+
padding: 2px;
|
25
|
+
}
|
26
|
+
|
27
|
+
.slick-pager .slick-pager-settings {
|
28
|
+
display: block;
|
29
|
+
float: right;
|
30
|
+
padding: 2px;
|
31
|
+
}
|
32
|
+
|
33
|
+
.slick-pager .slick-pager-settings * {
|
34
|
+
vertical-align: middle;
|
35
|
+
}
|
36
|
+
|
37
|
+
.slick-pager .slick-pager-settings a {
|
38
|
+
padding: 2px;
|
39
|
+
text-decoration: underline;
|
40
|
+
cursor: pointer;
|
41
|
+
}
|
@@ -14,18 +14,14 @@
|
|
14
14
|
function getNavState() {
|
15
15
|
var cannotLeaveEditMode = !Slick.GlobalEditorLock.commitCurrentEdit();
|
16
16
|
var pagingInfo = dataView.getPagingInfo();
|
17
|
-
var lastPage =
|
18
|
-
if (lastPage < 0) {
|
19
|
-
lastPage = 0;
|
20
|
-
}
|
17
|
+
var lastPage = pagingInfo.totalPages - 1;
|
21
18
|
|
22
19
|
return {
|
23
20
|
canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0,
|
24
21
|
canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum != lastPage,
|
25
22
|
canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0,
|
26
23
|
canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum < lastPage,
|
27
|
-
pagingInfo: pagingInfo
|
28
|
-
lastPage: lastPage
|
24
|
+
pagingInfo: pagingInfo
|
29
25
|
}
|
30
26
|
}
|
31
27
|
|
@@ -45,7 +41,7 @@
|
|
45
41
|
function gotoLast() {
|
46
42
|
var state = getNavState();
|
47
43
|
if (state.canGotoLast) {
|
48
|
-
dataView.setPagingOptions({pageNum: state.
|
44
|
+
dataView.setPagingOptions({pageNum: state.pagingInfo.totalPages - 1});
|
49
45
|
}
|
50
46
|
}
|
51
47
|
|
@@ -139,7 +135,7 @@
|
|
139
135
|
if (pagingInfo.pageSize == 0) {
|
140
136
|
$status.text("Showing all " + pagingInfo.totalRows + " rows");
|
141
137
|
} else {
|
142
|
-
$status.text("Showing page " + (pagingInfo.pageNum + 1) + " of " +
|
138
|
+
$status.text("Showing page " + (pagingInfo.pageNum + 1) + " of " + pagingInfo.totalPages);
|
143
139
|
}
|
144
140
|
}
|
145
141
|
|
@@ -139,6 +139,8 @@
|
|
139
139
|
handler: handler
|
140
140
|
});
|
141
141
|
event.subscribe(handler);
|
142
|
+
|
143
|
+
return this; // allow chaining
|
142
144
|
};
|
143
145
|
|
144
146
|
this.unsubscribe = function (event, handler) {
|
@@ -151,6 +153,8 @@
|
|
151
153
|
return;
|
152
154
|
}
|
153
155
|
}
|
156
|
+
|
157
|
+
return this; // allow chaining
|
154
158
|
};
|
155
159
|
|
156
160
|
this.unsubscribeAll = function () {
|
@@ -159,6 +163,8 @@
|
|
159
163
|
handlers[i].event.unsubscribe(handlers[i].handler);
|
160
164
|
}
|
161
165
|
handlers = [];
|
166
|
+
|
167
|
+
return this; // allow chaining
|
162
168
|
}
|
163
169
|
}
|
164
170
|
|
@@ -265,7 +271,13 @@
|
|
265
271
|
*/
|
266
272
|
function Group() {
|
267
273
|
this.__group = true;
|
268
|
-
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Grouping level, starting with 0.
|
277
|
+
* @property level
|
278
|
+
* @type {Number}
|
279
|
+
*/
|
280
|
+
this.level = 0;
|
269
281
|
|
270
282
|
/***
|
271
283
|
* Number of rows in the group.
|
@@ -301,6 +313,28 @@
|
|
301
313
|
* @type {GroupTotals}
|
302
314
|
*/
|
303
315
|
this.totals = null;
|
316
|
+
|
317
|
+
/**
|
318
|
+
* Rows that are part of the group.
|
319
|
+
* @property rows
|
320
|
+
* @type {Array}
|
321
|
+
*/
|
322
|
+
this.rows = [];
|
323
|
+
|
324
|
+
/**
|
325
|
+
* Sub-groups that are part of the group.
|
326
|
+
* @property groups
|
327
|
+
* @type {Array}
|
328
|
+
*/
|
329
|
+
this.groups = null;
|
330
|
+
|
331
|
+
/**
|
332
|
+
* A unique key used to identify the group. This key can be used in calls to DataView
|
333
|
+
* collapseGroup() or expandGroup().
|
334
|
+
* @property groupingKey
|
335
|
+
* @type {Object}
|
336
|
+
*/
|
337
|
+
this.groupingKey = null;
|
304
338
|
}
|
305
339
|
|
306
340
|
Group.prototype = new NonDataItem();
|
@@ -50,15 +50,22 @@
|
|
50
50
|
var filterCache = [];
|
51
51
|
|
52
52
|
// grouping
|
53
|
-
var
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
var groupingInfoDefaults = {
|
54
|
+
getter: null,
|
55
|
+
formatter: null,
|
56
|
+
comparer: function(a, b) { return a.value - b.value; },
|
57
|
+
predefinedValues: [],
|
58
|
+
aggregators: [],
|
59
|
+
aggregateEmpty: false,
|
60
|
+
aggregateCollapsed: false,
|
61
|
+
aggregateChildGroups: false,
|
62
|
+
collapsed: false,
|
63
|
+
displayTotalsRow: true
|
64
|
+
};
|
65
|
+
var groupingInfos = [];
|
57
66
|
var groups = [];
|
58
|
-
var
|
59
|
-
var
|
60
|
-
var aggregateCollapsed = false;
|
61
|
-
var compiledAccumulators;
|
67
|
+
var toggledGroupsByLevel = [];
|
68
|
+
var groupingDelimiter = ':|:';
|
62
69
|
|
63
70
|
var pagesize = 0;
|
64
71
|
var pagenum = 0;
|
@@ -129,11 +136,11 @@
|
|
129
136
|
function setPagingOptions(args) {
|
130
137
|
if (args.pageSize != undefined) {
|
131
138
|
pagesize = args.pageSize;
|
132
|
-
pagenum = Math.min(pagenum, Math.ceil(totalRows / pagesize));
|
139
|
+
pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0;
|
133
140
|
}
|
134
141
|
|
135
142
|
if (args.pageNum != undefined) {
|
136
|
-
pagenum = Math.min(args.pageNum, Math.ceil(totalRows / pagesize));
|
143
|
+
pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1));
|
137
144
|
}
|
138
145
|
|
139
146
|
onPagingInfoChanged.notify(getPagingInfo(), null, self);
|
@@ -142,7 +149,8 @@
|
|
142
149
|
}
|
143
150
|
|
144
151
|
function getPagingInfo() {
|
145
|
-
|
152
|
+
var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1;
|
153
|
+
return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages};
|
146
154
|
}
|
147
155
|
|
148
156
|
function sort(comparer, ascending) {
|
@@ -206,33 +214,65 @@
|
|
206
214
|
refresh();
|
207
215
|
}
|
208
216
|
|
209
|
-
function
|
217
|
+
function getGrouping() {
|
218
|
+
return groupingInfos;
|
219
|
+
}
|
220
|
+
|
221
|
+
function setGrouping(groupingInfo) {
|
210
222
|
if (!options.groupItemMetadataProvider) {
|
211
223
|
options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
|
212
224
|
}
|
213
225
|
|
214
|
-
groupingGetter = valueGetter;
|
215
|
-
groupingGetterIsAFn = typeof groupingGetter === "function";
|
216
|
-
groupingFormatter = valueFormatter;
|
217
|
-
groupingComparer = sortComparer;
|
218
|
-
collapsedGroups = {};
|
219
226
|
groups = [];
|
227
|
+
toggledGroupsByLevel = [];
|
228
|
+
groupingInfo = groupingInfo || [];
|
229
|
+
groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo];
|
230
|
+
|
231
|
+
for (var i = 0; i < groupingInfos.length; i++) {
|
232
|
+
var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]);
|
233
|
+
gi.getterIsAFn = typeof gi.getter === "function";
|
234
|
+
|
235
|
+
// pre-compile accumulator loops
|
236
|
+
gi.compiledAccumulators = [];
|
237
|
+
var idx = gi.aggregators.length;
|
238
|
+
while (idx--) {
|
239
|
+
gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]);
|
240
|
+
}
|
241
|
+
|
242
|
+
toggledGroupsByLevel[i] = {};
|
243
|
+
}
|
244
|
+
|
220
245
|
refresh();
|
221
246
|
}
|
222
247
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
248
|
+
/**
|
249
|
+
* @deprecated Please use {@link setGrouping}.
|
250
|
+
*/
|
251
|
+
function groupBy(valueGetter, valueFormatter, sortComparer) {
|
252
|
+
if (valueGetter == null) {
|
253
|
+
setGrouping([]);
|
254
|
+
return;
|
255
|
+
}
|
227
256
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
257
|
+
setGrouping({
|
258
|
+
getter: valueGetter,
|
259
|
+
formatter: valueFormatter,
|
260
|
+
comparer: sortComparer
|
261
|
+
});
|
262
|
+
}
|
263
|
+
|
264
|
+
/**
|
265
|
+
* @deprecated Please use {@link setGrouping}.
|
266
|
+
*/
|
267
|
+
function setAggregators(groupAggregators, includeCollapsed) {
|
268
|
+
if (!groupingInfos.length) {
|
269
|
+
throw new Error("At least one grouping must be specified before calling setAggregators().");
|
233
270
|
}
|
234
271
|
|
235
|
-
|
272
|
+
groupingInfos[0].aggregators = groupAggregators;
|
273
|
+
groupingInfos[0].aggregateCollapsed = includeCollapsed;
|
274
|
+
|
275
|
+
setGrouping(groupingInfos);
|
236
276
|
}
|
237
277
|
|
238
278
|
function getItemByIdx(i) {
|
@@ -332,7 +372,7 @@
|
|
332
372
|
return null;
|
333
373
|
}
|
334
374
|
|
335
|
-
// overrides for
|
375
|
+
// overrides for grouping rows
|
336
376
|
if (item.__group) {
|
337
377
|
return options.groupItemMetadataProvider.getGroupRowMetadata(item);
|
338
378
|
}
|
@@ -345,37 +385,105 @@
|
|
345
385
|
return null;
|
346
386
|
}
|
347
387
|
|
348
|
-
function
|
349
|
-
|
388
|
+
function expandCollapseAllGroups(level, collapse) {
|
389
|
+
if (level == null) {
|
390
|
+
for (var i = 0; i < groupingInfos.length; i++) {
|
391
|
+
toggledGroupsByLevel[i] = {};
|
392
|
+
groupingInfos[i].collapsed = collapse;
|
393
|
+
}
|
394
|
+
} else {
|
395
|
+
toggledGroupsByLevel[level] = {};
|
396
|
+
groupingInfos[level].collapsed = collapse;
|
397
|
+
}
|
350
398
|
refresh();
|
351
399
|
}
|
352
400
|
|
353
|
-
|
354
|
-
|
401
|
+
/**
|
402
|
+
* @param level {Number} Optional level to collapse. If not specified, applies to all levels.
|
403
|
+
*/
|
404
|
+
function collapseAllGroups(level) {
|
405
|
+
expandCollapseAllGroups(level, true);
|
406
|
+
}
|
407
|
+
|
408
|
+
/**
|
409
|
+
* @param level {Number} Optional level to expand. If not specified, applies to all levels.
|
410
|
+
*/
|
411
|
+
function expandAllGroups(level) {
|
412
|
+
expandCollapseAllGroups(level, false);
|
413
|
+
}
|
414
|
+
|
415
|
+
function expandCollapseGroup(level, groupingKey, collapse) {
|
416
|
+
toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse;
|
355
417
|
refresh();
|
356
418
|
}
|
357
419
|
|
420
|
+
/**
|
421
|
+
* @param varArgs Either a Slick.Group's "groupingKey" property, or a
|
422
|
+
* variable argument list of grouping values denoting a unique path to the row. For
|
423
|
+
* example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of
|
424
|
+
* the 'high' setGrouping.
|
425
|
+
*/
|
426
|
+
function collapseGroup(varArgs) {
|
427
|
+
var args = Array.prototype.slice.call(arguments);
|
428
|
+
var arg0 = args[0];
|
429
|
+
if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
|
430
|
+
expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true);
|
431
|
+
} else {
|
432
|
+
expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true);
|
433
|
+
}
|
434
|
+
}
|
435
|
+
|
436
|
+
/**
|
437
|
+
* @param varArgs Either a Slick.Group's "groupingKey" property, or a
|
438
|
+
* variable argument list of grouping values denoting a unique path to the row. For
|
439
|
+
* example, calling expandGroup('high', '10%') will expand the '10%' subgroup of
|
440
|
+
* the 'high' setGrouping.
|
441
|
+
*/
|
442
|
+
function expandGroup(varArgs) {
|
443
|
+
var args = Array.prototype.slice.call(arguments);
|
444
|
+
var arg0 = args[0];
|
445
|
+
if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
|
446
|
+
expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false);
|
447
|
+
} else {
|
448
|
+
expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false);
|
449
|
+
}
|
450
|
+
}
|
451
|
+
|
358
452
|
function getGroups() {
|
359
453
|
return groups;
|
360
454
|
}
|
361
455
|
|
362
|
-
function extractGroups(rows) {
|
456
|
+
function extractGroups(rows, parentGroup) {
|
363
457
|
var group;
|
364
458
|
var val;
|
365
459
|
var groups = [];
|
366
460
|
var groupsByVal = [];
|
367
461
|
var r;
|
462
|
+
var level = parentGroup ? parentGroup.level + 1 : 0;
|
463
|
+
var gi = groupingInfos[level];
|
464
|
+
|
465
|
+
for (var i = 0, l = gi.predefinedValues.length; i < l; i++) {
|
466
|
+
val = gi.predefinedValues[i];
|
467
|
+
group = groupsByVal[val];
|
468
|
+
if (!group) {
|
469
|
+
group = new Slick.Group();
|
470
|
+
group.value = val;
|
471
|
+
group.level = level;
|
472
|
+
group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
|
473
|
+
groups[groups.length] = group;
|
474
|
+
groupsByVal[val] = group;
|
475
|
+
}
|
476
|
+
}
|
368
477
|
|
369
478
|
for (var i = 0, l = rows.length; i < l; i++) {
|
370
479
|
r = rows[i];
|
371
|
-
val =
|
372
|
-
val = val || 0;
|
480
|
+
val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter];
|
373
481
|
group = groupsByVal[val];
|
374
482
|
if (!group) {
|
375
483
|
group = new Slick.Group();
|
376
|
-
group.count = 0;
|
377
484
|
group.value = val;
|
378
|
-
group.
|
485
|
+
group.level = level;
|
486
|
+
group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
|
379
487
|
groups[groups.length] = group;
|
380
488
|
groupsByVal[val] = group;
|
381
489
|
}
|
@@ -383,57 +491,96 @@
|
|
383
491
|
group.rows[group.count++] = r;
|
384
492
|
}
|
385
493
|
|
494
|
+
if (level < groupingInfos.length - 1) {
|
495
|
+
for (var i = 0; i < groups.length; i++) {
|
496
|
+
group = groups[i];
|
497
|
+
group.groups = extractGroups(group.rows, group);
|
498
|
+
}
|
499
|
+
}
|
500
|
+
|
501
|
+
groups.sort(groupingInfos[level].comparer);
|
502
|
+
|
386
503
|
return groups;
|
387
504
|
}
|
388
505
|
|
389
506
|
// TODO: lazy totals calculation
|
390
507
|
function calculateGroupTotals(group) {
|
391
|
-
if (group.collapsed && !aggregateCollapsed) {
|
392
|
-
return;
|
393
|
-
}
|
394
|
-
|
395
508
|
// TODO: try moving iterating over groups into compiled accumulator
|
509
|
+
var gi = groupingInfos[group.level];
|
510
|
+
var isLeafLevel = (group.level == groupingInfos.length);
|
396
511
|
var totals = new Slick.GroupTotals();
|
397
|
-
var agg, idx = aggregators.length;
|
512
|
+
var agg, idx = gi.aggregators.length;
|
398
513
|
while (idx--) {
|
399
|
-
agg = aggregators[idx];
|
514
|
+
agg = gi.aggregators[idx];
|
400
515
|
agg.init();
|
401
|
-
compiledAccumulators[idx].call(agg,
|
516
|
+
gi.compiledAccumulators[idx].call(agg,
|
517
|
+
(!isLeafLevel && gi.aggregateChildGroups) ? group.groups : group.rows);
|
402
518
|
agg.storeResult(totals);
|
403
519
|
}
|
404
520
|
totals.group = group;
|
405
521
|
group.totals = totals;
|
406
522
|
}
|
407
523
|
|
408
|
-
function calculateTotals(groups) {
|
409
|
-
|
524
|
+
function calculateTotals(groups, level) {
|
525
|
+
level = level || 0;
|
526
|
+
var gi = groupingInfos[level];
|
527
|
+
var idx = groups.length, g;
|
410
528
|
while (idx--) {
|
411
|
-
|
529
|
+
g = groups[idx];
|
530
|
+
|
531
|
+
if (g.collapsed && !gi.aggregateCollapsed) {
|
532
|
+
continue;
|
533
|
+
}
|
534
|
+
|
535
|
+
// Do a depth-first aggregation so that parent setGrouping aggregators can access subgroup totals.
|
536
|
+
if (g.groups) {
|
537
|
+
calculateTotals(g.groups, level + 1);
|
538
|
+
}
|
539
|
+
|
540
|
+
if (gi.aggregators.length && (
|
541
|
+
gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) {
|
542
|
+
calculateGroupTotals(g);
|
543
|
+
}
|
412
544
|
}
|
413
545
|
}
|
414
546
|
|
415
|
-
function finalizeGroups(groups) {
|
547
|
+
function finalizeGroups(groups, level) {
|
548
|
+
level = level || 0;
|
549
|
+
var gi = groupingInfos[level];
|
550
|
+
var groupCollapsed = gi.collapsed;
|
551
|
+
var toggledGroups = toggledGroupsByLevel[level];
|
416
552
|
var idx = groups.length, g;
|
417
553
|
while (idx--) {
|
418
554
|
g = groups[idx];
|
419
|
-
g.collapsed =
|
420
|
-
g.title =
|
555
|
+
g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey];
|
556
|
+
g.title = gi.formatter ? gi.formatter(g) : g.value;
|
557
|
+
|
558
|
+
if (g.groups) {
|
559
|
+
finalizeGroups(g.groups, level + 1);
|
560
|
+
// Let the non-leaf setGrouping rows get garbage-collected.
|
561
|
+
// They may have been used by aggregates that go over all of the descendants,
|
562
|
+
// but at this point they are no longer needed.
|
563
|
+
g.rows = [];
|
564
|
+
}
|
421
565
|
}
|
422
566
|
}
|
423
567
|
|
424
|
-
function flattenGroupedRows(groups) {
|
425
|
-
|
568
|
+
function flattenGroupedRows(groups, level) {
|
569
|
+
level = level || 0;
|
570
|
+
var gi = groupingInfos[level];
|
571
|
+
var groupedRows = [], rows, gl = 0, g;
|
426
572
|
for (var i = 0, l = groups.length; i < l; i++) {
|
427
573
|
g = groups[i];
|
428
574
|
groupedRows[gl++] = g;
|
429
575
|
|
430
576
|
if (!g.collapsed) {
|
431
|
-
|
432
|
-
|
577
|
+
rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows;
|
578
|
+
for (var j = 0, jj = rows.length; j < jj; j++) {
|
579
|
+
groupedRows[gl++] = rows[j];
|
433
580
|
}
|
434
581
|
}
|
435
582
|
|
436
|
-
if (g.totals && (!g.collapsed || aggregateCollapsed)) {
|
583
|
+
if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) {
|
437
584
|
groupedRows[gl++] = g.totals;
|
438
585
|
}
|
439
586
|
}
|
@@ -456,7 +603,7 @@
|
|
456
603
|
"for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" +
|
457
604
|
accumulatorInfo.params[0] + " = _items[_i]; " +
|
458
605
|
accumulatorInfo.body +
|
459
|
-
|
606
|
+
"}"
|
460
607
|
);
|
461
608
|
fn.displayName = fn.name = "compiledAccumulatorLoop";
|
462
609
|
return fn;
|
@@ -612,11 +759,10 @@
|
|
612
759
|
item = newRows[i];
|
613
760
|
r = rows[i];
|
614
761
|
|
615
|
-
if ((
|
762
|
+
if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) &&
|
616
763
|
item.__group !== r.__group ||
|
617
|
-
item.__updated ||
|
618
764
|
item.__group && !item.equals(r))
|
619
|
-
|| (
|
765
|
+
|| (eitherIsNonData &&
|
620
766
|
// no good way to compare totals since they are arbitrary DTOs
|
621
767
|
// deep object comparison is pretty expensive
|
622
768
|
// always considering them 'dirty' seems easier for the time being
|
@@ -644,14 +790,11 @@
|
|
644
790
|
var newRows = filteredItems.rows;
|
645
791
|
|
646
792
|
groups = [];
|
647
|
-
if (
|
793
|
+
if (groupingInfos.length) {
|
648
794
|
groups = extractGroups(newRows);
|
649
795
|
if (groups.length) {
|
796
|
+
calculateTotals(groups);
|
650
797
|
finalizeGroups(groups);
|
651
|
-
if (aggregators) {
|
652
|
-
calculateTotals(groups);
|
653
|
-
}
|
654
|
-
groups.sort(groupingComparer);
|
655
798
|
newRows = flattenGroupedRows(groups);
|
656
799
|
}
|
657
800
|
}
|
@@ -676,7 +819,7 @@
|
|
676
819
|
// if the current page is no longer valid, go to last page and recalc
|
677
820
|
// we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
|
678
821
|
if (pagesize && totalRows < pagenum * pagesize) {
|
679
|
-
pagenum = Math.
|
822
|
+
pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1);
|
680
823
|
diff = recalc(items, filter);
|
681
824
|
}
|
682
825
|
|
@@ -700,12 +843,7 @@
|
|
700
843
|
var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());;
|
701
844
|
var inHandler;
|
702
845
|
|
703
|
-
|
704
|
-
if (inHandler) { return; }
|
705
|
-
selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
|
706
|
-
});
|
707
|
-
|
708
|
-
this.onRowsChanged.subscribe(function(e, args) {
|
846
|
+
function update() {
|
709
847
|
if (selectedRowIds.length > 0) {
|
710
848
|
inHandler = true;
|
711
849
|
var selectedRows = self.mapIdsToRows(selectedRowIds);
|
@@ -715,7 +853,16 @@
|
|
715
853
|
grid.setSelectedRows(selectedRows);
|
716
854
|
inHandler = false;
|
717
855
|
}
|
856
|
+
}
|
857
|
+
|
858
|
+
grid.onSelectedRowsChanged.subscribe(function(e, args) {
|
859
|
+
if (inHandler) { return; }
|
860
|
+
selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
|
718
861
|
});
|
862
|
+
|
863
|
+
this.onRowsChanged.subscribe(update);
|
864
|
+
|
865
|
+
this.onRowCountChanged.subscribe(update);
|
719
866
|
}
|
720
867
|
|
721
868
|
function syncGridCellCssStyles(grid, key) {
|
@@ -734,15 +881,7 @@
|
|
734
881
|
}
|
735
882
|
}
|
736
883
|
|
737
|
-
|
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) {
|
884
|
+
function update() {
|
746
885
|
if (hashById) {
|
747
886
|
inHandler = true;
|
748
887
|
ensureRowsByIdCache();
|
@@ -756,7 +895,19 @@
|
|
756
895
|
grid.setCellCssStyles(key, newHash);
|
757
896
|
inHandler = false;
|
758
897
|
}
|
898
|
+
}
|
899
|
+
|
900
|
+
grid.onCellCssStylesChanged.subscribe(function(e, args) {
|
901
|
+
if (inHandler) { return; }
|
902
|
+
if (key != args.key) { return; }
|
903
|
+
if (args.hash) {
|
904
|
+
storeCellCssStyles(args.hash);
|
905
|
+
}
|
759
906
|
});
|
907
|
+
|
908
|
+
this.onRowsChanged.subscribe(update);
|
909
|
+
|
910
|
+
this.onRowCountChanged.subscribe(update);
|
760
911
|
}
|
761
912
|
|
762
913
|
return {
|
@@ -771,8 +922,12 @@
|
|
771
922
|
"sort": sort,
|
772
923
|
"fastSort": fastSort,
|
773
924
|
"reSort": reSort,
|
925
|
+
"setGrouping": setGrouping,
|
926
|
+
"getGrouping": getGrouping,
|
774
927
|
"groupBy": groupBy,
|
775
928
|
"setAggregators": setAggregators,
|
929
|
+
"collapseAllGroups": collapseAllGroups,
|
930
|
+
"expandAllGroups": expandAllGroups,
|
776
931
|
"collapseGroup": collapseGroup,
|
777
932
|
"expandGroup": expandGroup,
|
778
933
|
"getGroups": getGroups,
|
@@ -816,7 +971,7 @@
|
|
816
971
|
this.accumulate = function (item) {
|
817
972
|
var val = item[this.field_];
|
818
973
|
this.count_++;
|
819
|
-
if (val != null && val
|
974
|
+
if (val != null && val !== "" && val !== NaN) {
|
820
975
|
this.nonNullCount_++;
|
821
976
|
this.sum_ += parseFloat(val);
|
822
977
|
}
|
@@ -841,7 +996,7 @@
|
|
841
996
|
|
842
997
|
this.accumulate = function (item) {
|
843
998
|
var val = item[this.field_];
|
844
|
-
if (val != null && val
|
999
|
+
if (val != null && val !== "" && val !== NaN) {
|
845
1000
|
if (this.min_ == null || val < this.min_) {
|
846
1001
|
this.min_ = val;
|
847
1002
|
}
|
@@ -865,7 +1020,7 @@
|
|
865
1020
|
|
866
1021
|
this.accumulate = function (item) {
|
867
1022
|
var val = item[this.field_];
|
868
|
-
if (val != null && val
|
1023
|
+
if (val != null && val !== "" && val !== NaN) {
|
869
1024
|
if (this.max_ == null || val > this.max_) {
|
870
1025
|
this.max_ = val;
|
871
1026
|
}
|
@@ -889,7 +1044,7 @@
|
|
889
1044
|
|
890
1045
|
this.accumulate = function (item) {
|
891
1046
|
var val = item[this.field_];
|
892
|
-
if (val != null && val
|
1047
|
+
if (val != null && val !== "" && val !== NaN) {
|
893
1048
|
this.sum_ += parseFloat(val);
|
894
1049
|
}
|
895
1050
|
};
|
@@ -905,4 +1060,4 @@
|
|
905
1060
|
// TODO: add more built-in aggregators
|
906
1061
|
// TODO: merge common aggregators in one to prevent needles iterating
|
907
1062
|
|
908
|
-
})(jQuery);
|
1063
|
+
})(jQuery);
|