diff options
author | Michael Leibman <michael.leibman@gmail.com> | 2013-12-03 21:13:54 -0800 |
---|---|---|
committer | Michael Leibman <michael.leibman@gmail.com> | 2013-12-03 21:13:54 -0800 |
commit | a5ffbc24ef12d8a21c897968228fa06b658cac49 (patch) | |
tree | eb9e956e7d8627ed68ba16d7867149702e9292f2 | |
parent | 1ab24902e9b1e67716a54d7d42bda7eca79c05f3 (diff) | |
parent | 0af2978b890b2ff418c172004dd6cfa4ab4af5de (diff) | |
download | SlickGrid-origin/gh-pages.zip SlickGrid-origin/gh-pages.tar.gz SlickGrid-origin/gh-pages.tar.bz2 |
Merge branch 'master' into gh-pagesorigin/gh-pages
-rw-r--r-- | examples/example-grouping.html | 23 | ||||
-rw-r--r-- | slick.core.js | 8 | ||||
-rw-r--r-- | slick.dataview.js | 90 | ||||
-rw-r--r-- | slick.grid.js | 14 | ||||
-rw-r--r-- | slick.groupitemmetadataprovider.js | 12 | ||||
-rw-r--r-- | tests/scrolling benchmark raf.html | 154 |
6 files changed, 253 insertions, 48 deletions
diff --git a/examples/example-grouping.html b/examples/example-grouping.html index 86bbd4a..356ade7 100644 --- a/examples/example-grouping.html +++ b/examples/example-grouping.html @@ -170,7 +170,8 @@ function groupByDuration() { new Slick.Data.Aggregators.Avg("percentComplete"), new Slick.Data.Aggregators.Sum("cost") ], - aggregateCollapsed: false + aggregateCollapsed: false, + lazyTotalsCalculation: true }); } @@ -187,7 +188,8 @@ function groupByDurationOrderByCount(aggregateCollapsed) { new Slick.Data.Aggregators.Avg("percentComplete"), new Slick.Data.Aggregators.Sum("cost") ], - aggregateCollapsed: aggregateCollapsed + aggregateCollapsed: aggregateCollapsed, + lazyTotalsCalculation: true }); } @@ -202,7 +204,8 @@ function groupByDurationEffortDriven() { new Slick.Data.Aggregators.Sum("duration"), new Slick.Data.Aggregators.Sum("cost") ], - aggregateCollapsed: true + aggregateCollapsed: true, + lazyTotalsCalculation: true }, { getter: "effortDriven", @@ -213,7 +216,8 @@ function groupByDurationEffortDriven() { new Slick.Data.Aggregators.Avg("percentComplete"), new Slick.Data.Aggregators.Sum("cost") ], - collapsed: true + collapsed: true, + lazyTotalsCalculation: true } ]); } @@ -229,7 +233,8 @@ function groupByDurationEffortDrivenPercent() { new Slick.Data.Aggregators.Sum("duration"), new Slick.Data.Aggregators.Sum("cost") ], - aggregateCollapsed: true + aggregateCollapsed: true, + lazyTotalsCalculation: true }, { getter: "effortDriven", @@ -239,7 +244,8 @@ function groupByDurationEffortDrivenPercent() { aggregators :[ new Slick.Data.Aggregators.Sum("duration"), new Slick.Data.Aggregators.Sum("cost") - ] + ], + lazyTotalsCalculation: true }, { getter: "percentComplete", @@ -250,7 +256,8 @@ function groupByDurationEffortDrivenPercent() { new Slick.Data.Aggregators.Avg("percentComplete") ], aggregateCollapsed: true, - collapsed: true + collapsed: true, + lazyTotalsCalculation: true } ]); } @@ -265,7 +272,7 @@ function loadData(count) { d["id"] = "id_" + i; d["num"] = i; d["title"] = "Task " + i; - d["duration"] = Math.round(Math.random() * 14); + d["duration"] = Math.round(Math.random() * 30); d["percentComplete"] = Math.round(Math.random() * 100); d["start"] = someDates[ Math.floor((Math.random()*2)) ]; d["finish"] = someDates[ Math.floor((Math.random()*2)) ]; diff --git a/slick.core.js b/slick.core.js index 48a0416..2f097b1 100644 --- a/slick.core.js +++ b/slick.core.js @@ -370,6 +370,14 @@ * @type {Group} */ this.group = null; + + /*** + * Whether the totals have been fully initialized / calculated. + * Will be set to false for lazy-calculated group totals. + * @param initialized + * @type {Boolean} + */ + this.initialized = false; } GroupTotals.prototype = new NonDataItem(); diff --git a/slick.dataview.js b/slick.dataview.js index 3c3d204..f1c1b5e 100644 --- a/slick.dataview.js +++ b/slick.dataview.js @@ -60,7 +60,8 @@ aggregateCollapsed: false, aggregateChildGroups: false, collapsed: false, - displayTotalsRow: true + displayTotalsRow: true, + lazyTotalsCalculation: false }; var groupingInfos = []; var groups = []; @@ -363,7 +364,22 @@ } function getItem(i) { - return rows[i]; + var item = rows[i]; + + // if this is a group row, make sure totals are calculated and update the title + if (item && item.__group && item.totals && !item.totals.initialized) { + var gi = groupingInfos[item.level]; + if (!gi.displayTotalsRow) { + calculateTotals(item.totals); + item.title = gi.formatter ? gi.formatter(item) : item.value; + } + } + // if this is a totals row, make sure it's calculated + else if (item && item.__groupTotals && !item.initialized) { + calculateTotals(item); + } + + return item; } function getItemMetadata(i) { @@ -421,7 +437,7 @@ * @param varArgs Either a Slick.Group's "groupingKey" property, or a * variable argument list of grouping values denoting a unique path to the row. For * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of - * the 'high' setGrouping. + * the 'high' group. */ function collapseGroup(varArgs) { var args = Array.prototype.slice.call(arguments); @@ -437,7 +453,7 @@ * @param varArgs Either a Slick.Group's "groupingKey" property, or a * variable argument list of grouping values denoting a unique path to the row. For * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of - * the 'high' setGrouping. + * the 'high' group. */ function expandGroup(varArgs) { var args = Array.prototype.slice.call(arguments); @@ -503,27 +519,50 @@ return groups; } - // TODO: lazy totals calculation - function calculateGroupTotals(group) { - // TODO: try moving iterating over groups into compiled accumulator + function calculateTotals(totals) { + var group = totals.group; var gi = groupingInfos[group.level]; var isLeafLevel = (group.level == groupingInfos.length); - var totals = new Slick.GroupTotals(); var agg, idx = gi.aggregators.length; + + if (!isLeafLevel && gi.aggregateChildGroups) { + // make sure all the subgroups are calculated + var i = group.groups.length; + while (i--) { + if (!group.groups[i].initialized) { + calculateTotals(group.groups[i]); + } + } + } + while (idx--) { agg = gi.aggregators[idx]; agg.init(); - gi.compiledAccumulators[idx].call(agg, - (!isLeafLevel && gi.aggregateChildGroups) ? group.groups : group.rows); + if (!isLeafLevel && gi.aggregateChildGroups) { + gi.compiledAccumulators[idx].call(agg, group.groups); + } else { + gi.compiledAccumulators[idx].call(agg, group.rows); + } agg.storeResult(totals); } + totals.initialized = true; + } + + function addGroupTotals(group) { + var gi = groupingInfos[group.level]; + var totals = new Slick.GroupTotals(); totals.group = group; group.totals = totals; + if (!gi.lazyTotalsCalculation) { + calculateTotals(totals); + } } - function calculateTotals(groups, level) { + function addTotals(groups, level) { level = level || 0; var gi = groupingInfos[level]; + var groupCollapsed = gi.collapsed; + var toggledGroups = toggledGroupsByLevel[level]; var idx = groups.length, g; while (idx--) { g = groups[idx]; @@ -532,38 +571,20 @@ continue; } - // Do a depth-first aggregation so that parent setGrouping aggregators can access subgroup totals. + // Do a depth-first aggregation so that parent group aggregators can access subgroup totals. if (g.groups) { - calculateTotals(g.groups, level + 1); + addTotals(g.groups, level + 1); } if (gi.aggregators.length && ( gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) { - calculateGroupTotals(g); + addGroupTotals(g); } - } - } - function finalizeGroups(groups, level) { - level = level || 0; - var gi = groupingInfos[level]; - var groupCollapsed = gi.collapsed; - var toggledGroups = toggledGroupsByLevel[level]; - var idx = groups.length, g; - while (idx--) { - g = groups[idx]; g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey]; g.title = gi.formatter ? gi.formatter(g) : g.value; - - if (g.groups) { - finalizeGroups(g.groups, level + 1); - // Let the non-leaf setGrouping rows get garbage-collected. - // They may have been used by aggregates that go over all of the descendants, - // but at this point they are no longer needed. - g.rows = []; - } } - } + } function flattenGroupedRows(groups, level) { level = level || 0; @@ -793,8 +814,7 @@ if (groupingInfos.length) { groups = extractGroups(newRows); if (groups.length) { - calculateTotals(groups); - finalizeGroups(groups); + addTotals(groups); newRows = flattenGroupedRows(groups); } } diff --git a/slick.grid.js b/slick.grid.js index 539b260..c12bae9 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -1061,7 +1061,7 @@ if (typeof Slick === "undefined") { shrinkLeeway -= shrinkSize;
widths[i] -= shrinkSize;
}
- if (prevTotal == total) { // avoid infinite loop
+ if (prevTotal <= total) { // avoid infinite loop
break;
}
prevTotal = total;
@@ -1073,14 +1073,18 @@ if (typeof Slick === "undefined") { var growProportion = availWidth / total;
for (i = 0; i < columns.length && total < availWidth; i++) {
c = columns[i];
- if (!c.resizable || c.maxWidth <= c.width) {
- continue;
+ var currentWidth = widths[i];
+ var growSize;
+
+ if (!c.resizable || c.maxWidth <= currentWidth) {
+ growSize = 0;
+ } else {
+ growSize = Math.min(Math.floor(growProportion * currentWidth) - currentWidth, (c.maxWidth - currentWidth) || 1000000) || 1;
}
- var growSize = Math.min(Math.floor(growProportion * c.width) - c.width, (c.maxWidth - c.width) || 1000000) || 1;
total += growSize;
widths[i] += growSize;
}
- if (prevTotal == total) { // avoid infinite loop
+ if (prevTotal >= total) { // avoid infinite loop
break;
}
prevTotal = total;
diff --git a/slick.groupitemmetadataprovider.js b/slick.groupitemmetadataprovider.js index 1744fbf..c40551c 100644 --- a/slick.groupitemmetadataprovider.js +++ b/slick.groupitemmetadataprovider.js @@ -79,6 +79,12 @@ function handleGridClick(e, args) { var item = this.getDataItem(args.row); if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) { + var range = _grid.getRenderedRange(); + dataView.setRefreshHints({ + ignoreDiffsBefore: range.top, + ignoreDiffsAfter: range.bottom + }); + if (item.collapsed) { this.getData().expandGroup(item.groupingKey); } else { @@ -97,6 +103,12 @@ if (activeCell) { var item = this.getDataItem(activeCell.row); if (item && item instanceof Slick.Group) { + var range = _grid.getRenderedRange(); + dataView.setRefreshHints({ + ignoreDiffsBefore: range.top, + ignoreDiffsAfter: range.bottom + }); + if (item.collapsed) { this.getData().expandGroup(item.groupingKey); } else { diff --git a/tests/scrolling benchmark raf.html b/tests/scrolling benchmark raf.html new file mode 100644 index 0000000..01ca0c5 --- /dev/null +++ b/tests/scrolling benchmark raf.html @@ -0,0 +1,154 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <title>SlickGrid Scrolling Benchmark</title> + <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="../examples/examples.css" type="text/css" media="screen" charset="utf-8" /> + <style> + .cell-title { + font-weight: bold; + } + + .cell-effort-driven { + text-align: center; + } + </style> +</head> +<body> + <script src="../lib/firebugx.js"></script> + <script src="../lib/jquery-1.7.min.js"></script> + <script src="../lib/jquery-ui-1.8.16.custom.min.js"></script> + <script src="../lib/jquery.event.drag-2.2.js"></script> + + <script src="../slick.core.js"></script> + <script src="../slick.grid.js"></script> + <script src="../slick.formatters.js"></script> + + <button onclick="grid.debug()">Debug info</button> + <button onclick="startBench(100)">Run (+= 100px)</button> + <button onclick="startBench(300)">Run (+= 300px)</button> + <button onclick="startBench(550)">Run (+= 550px; simulate paging)</button> + <button onclick="startBench(1000)">Run (+= 1'000px)</button> + <button onclick="startBench(5000)">Run (+= 5'000px)</button> + <hr/> + + <div id="myGrid" style="width:600px;height:600px;"></div> + + <script> + var grid; + var data = []; + var extraColumns = 100; + + function testPostRender(cellNode, row, dataContext, colDef) { + cellNode.style.backgroundColor = 'yellow'; + } + + var columns = [ + {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title"}, + {id:"duration", name:"Duration", field:"duration", selectable:false}, + {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:Slick.Formatters.PercentCompleteBar}, + {id:"start", name:"Start", field:"start", minWidth:60}, + {id:"finish", name:"Finish", field:"finish", minWidth:60}, + {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:Slick.Formatters.Checkmark, asyncPostRender:testPostRender} + ]; + + for (var i=0; i<extraColumns; i++) { + columns.push({id:"col" + i, name:"col" + i, field:"title"}); + } + + var options = { + editable: false, + enableAddRow: false, + enableCellNavigation: true, + enableAsyncPostRender: true, + forceFitColumns: false + }; + + + function generateRandomTask(index) { + return { + "id": "id_" + index, + "title": "Task " + index, + "duration": "5 days", + "percentComplete": Math.round(Math.random() * 100), + "start": "01/01/2014", + "finish": "01/05/2014", + "effortDriven": (index % 5 == 0) + }; + } + + $(function() + { + data.getLength = function() { return 5000; }; + + data.getItem = function(index) { + // lazy-generate + if (!data[index]) { + data[index] = generateRandomTask(index); + } + return data[index]; + }; + + data.getItemMetadata = function (row) { + if (row % 2 === 1) { + return { + "columns": { + "duration": { + "colspan": 3 + } + } + }; + } + }; + + // initialize the grid + grid = new Slick.Grid("#myGrid", data, columns, options); + + $grid = $("#myGrid div.grid-canvas").parent(); + }); + + + var $grid; + var lastScrollTop = 0; + var startTime; + var scrollAmount = 1000; + + function queue(callback) { + var raf = window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.msRequestAnimationFrame || + window.webkitRequestAnimationFrame; + if (raf) { + raf(callback); + } else { + window.setTimeout(callback, 10); + } + } + + function startBench(scroll) { + scrollAmount = scroll; + lastScrollTop = 0; + $grid.scrollTop(0); + + queue(function(){ + startTime = new Date(); + bench(); + }); + } + + function bench() { + var scrollTop = $grid[0].scrollTop = lastScrollTop + scrollAmount; + + if ($grid[0].scrollTop == lastScrollTop + scrollAmount) { + lastScrollTop = scrollTop; + queue(bench); + } + else { + alert((new Date() - startTime)); + } + } + </script> +</body> +</html> |