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