Updated to SlickGrid v2.2, which fixes scroll issue for > 1000 recordsversion-14
@@ -1,45 +1,80 @@ | |||||
(function ($) { | (function ($) { | ||||
// register namespace | |||||
// Register namespace | |||||
$.extend(true, window, { | $.extend(true, window, { | ||||
"Slick": { | "Slick": { | ||||
"AutoTooltips": AutoTooltips | "AutoTooltips": AutoTooltips | ||||
} | } | ||||
}); | }); | ||||
/** | |||||
* AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content. | |||||
* @constructor | |||||
* @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells | |||||
* @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells | |||||
* @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip | |||||
*/ | |||||
function AutoTooltips(options) { | function AutoTooltips(options) { | ||||
var _grid; | var _grid; | ||||
var _self = this; | var _self = this; | ||||
var _defaults = { | var _defaults = { | ||||
enableForCells: true, | |||||
enableForHeaderCells: false, | |||||
maxToolTipLength: null | maxToolTipLength: null | ||||
}; | }; | ||||
/** | |||||
* Initialize plugin. | |||||
*/ | |||||
function init(grid) { | function init(grid) { | ||||
options = $.extend(true, {}, _defaults, options); | options = $.extend(true, {}, _defaults, options); | ||||
_grid = grid; | _grid = grid; | ||||
_grid.onMouseEnter.subscribe(handleMouseEnter); | |||||
if (options.enableForCells) _grid.onMouseEnter.subscribe(handleMouseEnter); | |||||
if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.subscribe(handleHeaderMouseEnter); | |||||
} | } | ||||
/** | |||||
* Destroy plugin. | |||||
*/ | |||||
function destroy() { | function destroy() { | ||||
_grid.onMouseEnter.unsubscribe(handleMouseEnter); | |||||
if (options.enableForCells) _grid.onMouseEnter.unsubscribe(handleMouseEnter); | |||||
if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.unsubscribe(handleHeaderMouseEnter); | |||||
} | } | ||||
function handleMouseEnter(e, args) { | |||||
/** | |||||
* Handle mouse entering grid cell to add/remove tooltip. | |||||
* @param {jQuery.Event} e - The event | |||||
*/ | |||||
function handleMouseEnter(e) { | |||||
var cell = _grid.getCellFromEvent(e); | var cell = _grid.getCellFromEvent(e); | ||||
if (cell) { | if (cell) { | ||||
var node = _grid.getCellNode(cell.row, cell.cell); | |||||
if ($(node).innerWidth() < node.scrollWidth) { | |||||
var text = $.trim($(node).text()); | |||||
var $node = $(_grid.getCellNode(cell.row, cell.cell)); | |||||
var text; | |||||
if ($node.innerWidth() < $node[0].scrollWidth) { | |||||
text = $.trim($node.text()); | |||||
if (options.maxToolTipLength && text.length > options.maxToolTipLength) { | if (options.maxToolTipLength && text.length > options.maxToolTipLength) { | ||||
text = text.substr(0, options.maxToolTipLength - 3) + "..."; | text = text.substr(0, options.maxToolTipLength - 3) + "..."; | ||||
} | } | ||||
$(node).attr("title", text); | |||||
} else { | } else { | ||||
$(node).attr("title", ""); | |||||
text = ""; | |||||
} | } | ||||
$node.attr("title", text); | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Handle mouse entering header cell to add/remove tooltip. | |||||
* @param {jQuery.Event} e - The event | |||||
* @param {object} args.column - The column definition | |||||
*/ | |||||
function handleHeaderMouseEnter(e, args) { | |||||
var column = args.column, | |||||
$node = $(e.target).closest(".slick-header-column"); | |||||
if (!column.toolTip) { | |||||
$node.attr("title", ($node.innerWidth() < $node[0].scrollWidth) ? column.name : ""); | |||||
} | |||||
} | |||||
// Public API | |||||
$.extend(this, { | $.extend(this, { | ||||
"init": init, | "init": init, | ||||
"destroy": destroy | "destroy": destroy | ||||
@@ -20,6 +20,7 @@ | |||||
function CellRangeDecorator(grid, options) { | function CellRangeDecorator(grid, options) { | ||||
var _elem; | var _elem; | ||||
var _defaults = { | var _defaults = { | ||||
selectionCssClass: 'slick-range-decorator', | |||||
selectionCss: { | selectionCss: { | ||||
"zIndex": "9999", | "zIndex": "9999", | ||||
"border": "2px dashed red" | "border": "2px dashed red" | ||||
@@ -32,6 +33,7 @@ | |||||
function show(range) { | function show(range) { | ||||
if (!_elem) { | if (!_elem) { | ||||
_elem = $("<div></div>", {css: options.selectionCss}) | _elem = $("<div></div>", {css: options.selectionCss}) | ||||
.addClass(options.selectionCssClass) | |||||
.css("position", "absolute") | .css("position", "absolute") | ||||
.appendTo(grid.getCanvasNode()); | .appendTo(grid.getCanvasNode()); | ||||
} | } | ||||
@@ -61,4 +63,4 @@ | |||||
"hide": hide | "hide": hide | ||||
}); | }); | ||||
} | } | ||||
})(jQuery); | |||||
})(jQuery); |
@@ -54,6 +54,8 @@ | |||||
return; | return; | ||||
} | } | ||||
_grid.focus(); | |||||
var start = _grid.getCellFromPoint( | var start = _grid.getCellFromPoint( | ||||
dd.startX - $(_canvas).offset().left, | dd.startX - $(_canvas).offset().left, | ||||
dd.startY - $(_canvas).offset().top); | dd.startY - $(_canvas).offset().top); | ||||
@@ -82,6 +82,13 @@ | |||||
} | } | ||||
function handleKeyDown(e) { | function handleKeyDown(e) { | ||||
/*** | |||||
* Кey codes | |||||
* 37 left | |||||
* 38 up | |||||
* 39 right | |||||
* 40 down | |||||
*/ | |||||
var ranges, last; | var ranges, last; | ||||
var active = _grid.getActiveCell(); | var active = _grid.getActiveCell(); | ||||
@@ -119,8 +126,10 @@ | |||||
var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell); | var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell); | ||||
if (removeInvalidRanges([new_last]).length) { | if (removeInvalidRanges([new_last]).length) { | ||||
ranges.push(new_last); | ranges.push(new_last); | ||||
_grid.scrollRowIntoView(dirRow > 0 ? new_last.toRow : new_last.fromRow); | |||||
_grid.scrollCellIntoView(new_last.fromRow, dirCell > 0 ? new_last.toCell : new_last.fromCell); | |||||
var viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow; | |||||
var viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell; | |||||
_grid.scrollRowIntoView(viewRow); | |||||
_grid.scrollCellIntoView(viewRow, viewCell); | |||||
} | } | ||||
else | else | ||||
ranges.push(last); | ranges.push(last); | ||||
@@ -142,4 +151,4 @@ | |||||
"onSelectedRangesChanged": new Slick.Event() | "onSelectedRangesChanged": new Slick.Event() | ||||
}); | }); | ||||
} | } | ||||
})(jQuery); | |||||
})(jQuery); |
@@ -1,4 +1,5 @@ | |||||
.slick-column-name { | |||||
.slick-column-name, | |||||
.slick-sort-indicator { | |||||
/** | /** | ||||
* This makes all "float:right" elements after it that spill over to the next line | * This makes all "float:right" elements after it that spill over to the next line | ||||
* display way below the lower boundary of the column thus hiding them. | * display way below the lower boundary of the column thus hiding them. | ||||
@@ -7,6 +7,7 @@ | |||||
width: 14px; | width: 14px; | ||||
background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
background-position: left center; | background-position: left center; | ||||
background-image: url(../images/down.gif); | |||||
cursor: pointer; | cursor: pointer; | ||||
display: none; | display: none; | ||||
@@ -83,7 +83,7 @@ | |||||
var _handler = new Slick.EventHandler(); | var _handler = new Slick.EventHandler(); | ||||
var _defaults = { | var _defaults = { | ||||
buttonCssClass: null, | buttonCssClass: null, | ||||
buttonImage: "../images/down.gif" | |||||
buttonImage: null | |||||
}; | }; | ||||
var $menu; | var $menu; | ||||
var $activeHeaderColumn; | var $activeHeaderColumn; | ||||
@@ -182,7 +182,7 @@ | |||||
if (!$menu) { | if (!$menu) { | ||||
$menu = $("<div class='slick-header-menu'></div>") | $menu = $("<div class='slick-header-menu'></div>") | ||||
.appendTo(document.body); | |||||
.appendTo(_grid.getContainerNode()); | |||||
} | } | ||||
$menu.empty(); | $menu.empty(); | ||||
@@ -225,14 +225,17 @@ | |||||
// Position the menu. | // Position the menu. | ||||
$menu | $menu | ||||
.css("top", $(this).offset().top + $(this).height()) | |||||
.css("left", $(this).offset().left); | |||||
.offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left }); | |||||
// Mark the header as active to keep the highlighting. | // Mark the header as active to keep the highlighting. | ||||
$activeHeaderColumn = $menuButton.closest(".slick-header-column"); | $activeHeaderColumn = $menuButton.closest(".slick-header-column"); | ||||
$activeHeaderColumn | $activeHeaderColumn | ||||
.addClass("slick-header-column-active"); | .addClass("slick-header-column-active"); | ||||
// Stop propagation so that it doesn't register as a header click event. | |||||
e.preventDefault(); | |||||
e.stopPropagation(); | |||||
} | } | ||||
@@ -269,4 +272,4 @@ | |||||
"onCommand": new Slick.Event() | "onCommand": new Slick.Event() | ||||
}); | }); | ||||
} | } | ||||
})(jQuery); | |||||
})(jQuery); |
@@ -134,34 +134,34 @@ | |||||
return false; | return false; | ||||
} | } | ||||
if (!_grid.getOptions().multiSelect || ( | |||||
!e.ctrlKey && !e.shiftKey && !e.metaKey)) { | |||||
return false; | |||||
} | |||||
var selection = rangesToRows(_ranges); | var selection = rangesToRows(_ranges); | ||||
var idx = $.inArray(cell.row, selection); | var idx = $.inArray(cell.row, selection); | ||||
if (!e.ctrlKey && !e.shiftKey && !e.metaKey) { | |||||
return false; | |||||
} | |||||
else if (_grid.getOptions().multiSelect) { | |||||
if (idx === -1 && (e.ctrlKey || e.metaKey)) { | |||||
selection.push(cell.row); | |||||
_grid.setActiveCell(cell.row, cell.cell); | |||||
} else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { | |||||
selection = $.grep(selection, function (o, i) { | |||||
return (o !== cell.row); | |||||
}); | |||||
_grid.setActiveCell(cell.row, cell.cell); | |||||
} else if (selection.length && e.shiftKey) { | |||||
var last = selection.pop(); | |||||
var from = Math.min(cell.row, last); | |||||
var to = Math.max(cell.row, last); | |||||
selection = []; | |||||
for (var i = from; i <= to; i++) { | |||||
if (i !== last) { | |||||
selection.push(i); | |||||
} | |||||
if (idx === -1 && (e.ctrlKey || e.metaKey)) { | |||||
selection.push(cell.row); | |||||
_grid.setActiveCell(cell.row, cell.cell); | |||||
} else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { | |||||
selection = $.grep(selection, function (o, i) { | |||||
return (o !== cell.row); | |||||
}); | |||||
_grid.setActiveCell(cell.row, cell.cell); | |||||
} else if (selection.length && e.shiftKey) { | |||||
var last = selection.pop(); | |||||
var from = Math.min(cell.row, last); | |||||
var to = Math.max(cell.row, last); | |||||
selection = []; | |||||
for (var i = from; i <= to; i++) { | |||||
if (i !== last) { | |||||
selection.push(i); | |||||
} | } | ||||
selection.push(last); | |||||
_grid.setActiveCell(cell.row, cell.cell); | |||||
} | } | ||||
selection.push(last); | |||||
_grid.setActiveCell(cell.row, cell.cell); | |||||
} | } | ||||
_ranges = rowsToRanges(selection); | _ranges = rowsToRanges(selection); | ||||
@@ -6,16 +6,17 @@ classes should alter those! | |||||
*/ | */ | ||||
.slick-header-columns { | .slick-header-columns { | ||||
background-color: #f2f2f2; | |||||
background: url('images/header-columns-bg.gif') repeat-x center bottom; | |||||
border-bottom: 1px solid silver; | border-bottom: 1px solid silver; | ||||
} | } | ||||
.slick-header-column { | .slick-header-column { | ||||
background: url('images/header-columns-bg.gif') repeat-x center bottom; | |||||
border-right: 1px solid silver; | border-right: 1px solid silver; | ||||
} | } | ||||
.slick-header-column:hover, .slick-header-column-active { | .slick-header-column:hover, .slick-header-column-active { | ||||
background-color: #f5f5f5; | |||||
background: white url('images/header-columns-over-bg.gif') repeat-x center bottom; | |||||
} | } | ||||
.slick-headerrow { | .slick-headerrow { | ||||
@@ -45,10 +46,8 @@ classes should alter those! | |||||
} | } | ||||
.slick-cell { | .slick-cell { | ||||
padding: 1px 4px 0px 5px; | |||||
border-top: 0px; | |||||
border-left: 0px; | |||||
border-right: 1px solid silver; | |||||
padding-left: 4px; | |||||
padding-right: 4px; | |||||
} | } | ||||
.slick-group { | .slick-group { | ||||
@@ -62,11 +61,11 @@ classes should alter those! | |||||
} | } | ||||
.slick-group-toggle.expanded { | .slick-group-toggle.expanded { | ||||
background: url(../frappe/js/lib/slickgrid/images/collapse.gif) no-repeat center center; | |||||
background: url(images/collapse.gif) no-repeat center center; | |||||
} | } | ||||
.slick-group-toggle.collapsed { | .slick-group-toggle.collapsed { | ||||
background: url(../frappe/js/lib/slickgrid/images/expand.gif) no-repeat center center; | |||||
background: url(images/expand.gif) no-repeat center center; | |||||
} | } | ||||
.slick-group-totals { | .slick-group-totals { | ||||
@@ -74,6 +73,10 @@ classes should alter those! | |||||
background: white; | background: white; | ||||
} | } | ||||
.slick-cell.selected { | |||||
background-color: beige; | |||||
} | |||||
.slick-cell.active { | .slick-cell.active { | ||||
border-color: gray; | border-color: gray; | ||||
border-style: solid; | border-style: solid; | ||||
@@ -83,18 +86,10 @@ classes should alter those! | |||||
background: silver !important; | background: silver !important; | ||||
} | } | ||||
.slick-row[row$="1"], .slick-row[row$="3"], .slick-row[row$="5"], .slick-row[row$="7"], .slick-row[row$="9"] { | |||||
.slick-row.odd { | |||||
background: #fafafa; | background: #fafafa; | ||||
} | } | ||||
.slick-row.odd .slick-cell { | |||||
background-color: #f9f9f9; | |||||
} | |||||
.slick-cell.selected { | |||||
background-color: beige !important; | |||||
} | |||||
.slick-row.ui-state-active { | .slick-row.ui-state-active { | ||||
background: #F5F7D7; | background: #F5F7D7; | ||||
} | } | ||||
@@ -106,195 +101,18 @@ classes should alter those! | |||||
.slick-cell.invalid { | .slick-cell.invalid { | ||||
border-color: red; | border-color: red; | ||||
-moz-animation-duration: 0.2s; | |||||
-webkit-animation-duration: 0.2s; | |||||
-moz-animation-name: slickgrid-invalid-hilite; | |||||
-webkit-animation-name: slickgrid-invalid-hilite; | |||||
} | } | ||||
.grid-header { | |||||
border: 1px solid gray; | |||||
border-bottom: 0; | |||||
border-top: 0; | |||||
background: url('../lib/js/lib/slickgrid/images/header-bg.gif') repeat-x center top; | |||||
color: black; | |||||
height: 24px; | |||||
line-height: 24px; | |||||
} | |||||
.grid-header label { | |||||
display: inline-block; | |||||
font-weight: bold; | |||||
margin: auto auto auto 6px; | |||||
} | |||||
.grid-header .ui-icon { | |||||
margin: 4px 4px auto 6px; | |||||
background-color: transparent; | |||||
border-color: transparent; | |||||
} | |||||
.grid-header .ui-icon.ui-state-hover { | |||||
background-color: white; | |||||
} | |||||
.grid-header #txtSearch { | |||||
margin: 0 4px 0 4px; | |||||
padding: 2px 2px; | |||||
-moz-border-radius: 2px; | |||||
-webkit-border-radius: 2px; | |||||
border: 1px solid silver; | |||||
} | |||||
.options-panel { | |||||
-moz-border-radius: 6px; | |||||
-webkit-border-radius: 6px; | |||||
border: 1px solid silver; | |||||
background: #f0f0f0; | |||||
padding: 4px; | |||||
margin-bottom: 20px; | |||||
width: 320px; | |||||
position: absolute; | |||||
top: 0px; | |||||
left: 650px; | |||||
} | |||||
/* Individual cell styles */ | |||||
.slick-cell.task-name { | |||||
font-weight: bold; | |||||
text-align: right; | |||||
} | |||||
.slick-cell.task-percent { | |||||
text-align: right; | |||||
} | |||||
.slick-cell.cell-move-handle { | |||||
font-weight: bold; | |||||
text-align: right; | |||||
border-right: solid gray; | |||||
background: #efefef; | |||||
cursor: move; | |||||
} | |||||
.cell-move-handle:hover { | |||||
background: #b6b9bd; | |||||
} | |||||
.slick-row.selected .cell-move-handle { | |||||
background: #D5DC8D; | |||||
} | |||||
.slick-row .cell-actions { | |||||
text-align: left; | |||||
} | |||||
.slick-row.complete { | |||||
background-color: #DFD; | |||||
color: #555; | |||||
@-moz-keyframes slickgrid-invalid-hilite { | |||||
from { box-shadow: 0 0 6px red; } | |||||
to { box-shadow: none; } | |||||
} | } | ||||
.percent-complete-bar { | |||||
display: inline-block; | |||||
height: 6px; | |||||
-moz-border-radius: 3px; | |||||
-webkit-border-radius: 3px; | |||||
} | |||||
/* Slick.Editors.Text, Slick.Editors.Date */ | |||||
input.editor-text { | |||||
width: 100%; | |||||
height: 100%; | |||||
border: 0; | |||||
margin: 0; | |||||
background: transparent; | |||||
outline: 0; | |||||
padding: 0; | |||||
} | |||||
.ui-datepicker-trigger { | |||||
margin-top: 2px; | |||||
padding: 0; | |||||
vertical-align: top; | |||||
} | |||||
/* Slick.Editors.PercentComplete */ | |||||
input.editor-percentcomplete { | |||||
width: 100%; | |||||
height: 100%; | |||||
border: 0; | |||||
margin: 0; | |||||
background: transparent; | |||||
outline: 0; | |||||
padding: 0; | |||||
float: left; | |||||
} | |||||
.editor-percentcomplete-picker { | |||||
position: relative; | |||||
display: inline-block; | |||||
width: 16px; | |||||
height: 100%; | |||||
background: url("../lib/js/lib/slickgrid/images/pencil.gif") no-repeat center center; | |||||
overflow: visible; | |||||
z-index: 1000; | |||||
float: right; | |||||
} | |||||
.editor-percentcomplete-helper { | |||||
border: 0 solid gray; | |||||
position: absolute; | |||||
top: -2px; | |||||
left: -9px; | |||||
background: url("../lib/js/lib/slickgrid/images/editor-helper-bg.gif") no-repeat top left; | |||||
padding-left: 9px; | |||||
width: 120px; | |||||
height: 140px; | |||||
display: none; | |||||
overflow: visible; | |||||
} | |||||
.editor-percentcomplete-wrapper { | |||||
background: beige; | |||||
padding: 20px 8px; | |||||
width: 100%; | |||||
height: 98px; | |||||
border: 1px solid gray; | |||||
border-left: 0; | |||||
} | |||||
.editor-percentcomplete-buttons { | |||||
float: right; | |||||
} | |||||
.editor-percentcomplete-buttons button { | |||||
width: 80px; | |||||
} | |||||
.editor-percentcomplete-slider { | |||||
float: left; | |||||
} | |||||
.editor-percentcomplete-picker:hover .editor-percentcomplete-helper { | |||||
display: block; | |||||
} | |||||
.editor-percentcomplete-helper:hover { | |||||
display: block; | |||||
} | |||||
/* Slick.Editors.YesNoSelect */ | |||||
select.editor-yesno { | |||||
width: 100%; | |||||
margin: 0; | |||||
vertical-align: middle; | |||||
} | |||||
/* Slick.Editors.Checkbox */ | |||||
input.editor-checkbox { | |||||
margin: 0; | |||||
height: 100%; | |||||
padding: 0; | |||||
border: 0; | |||||
} | |||||
@-webkit-keyframes slickgrid-invalid-hilite { | |||||
from { box-shadow: 0 0 6px red; } | |||||
to { box-shadow: none; } | |||||
} |
@@ -348,7 +348,8 @@ | |||||
Group.prototype.equals = function (group) { | Group.prototype.equals = function (group) { | ||||
return this.value === group.value && | return this.value === group.value && | ||||
this.count === group.count && | this.count === group.count && | ||||
this.collapsed === group.collapsed; | |||||
this.collapsed === group.collapsed && | |||||
this.title === group.title; | |||||
}; | }; | ||||
/*** | /*** | ||||
@@ -369,6 +370,14 @@ | |||||
* @type {Group} | * @type {Group} | ||||
*/ | */ | ||||
this.group = null; | 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(); | GroupTotals.prototype = new NonDataItem(); | ||||
@@ -60,7 +60,8 @@ | |||||
aggregateCollapsed: false, | aggregateCollapsed: false, | ||||
aggregateChildGroups: false, | aggregateChildGroups: false, | ||||
collapsed: false, | collapsed: false, | ||||
displayTotalsRow: true | |||||
displayTotalsRow: true, | |||||
lazyTotalsCalculation: false | |||||
}; | }; | ||||
var groupingInfos = []; | var groupingInfos = []; | ||||
var groups = []; | var groups = []; | ||||
@@ -266,7 +267,7 @@ | |||||
*/ | */ | ||||
function setAggregators(groupAggregators, includeCollapsed) { | function setAggregators(groupAggregators, includeCollapsed) { | ||||
if (!groupingInfos.length) { | if (!groupingInfos.length) { | ||||
throw new Error("At least must setGrouping must be specified before calling setAggregators()."); | |||||
throw new Error("At least one grouping must be specified before calling setAggregators()."); | |||||
} | } | ||||
groupingInfos[0].aggregators = groupAggregators; | groupingInfos[0].aggregators = groupAggregators; | ||||
@@ -304,7 +305,7 @@ | |||||
function mapIdsToRows(idArray) { | function mapIdsToRows(idArray) { | ||||
var rows = []; | var rows = []; | ||||
ensureRowsByIdCache(); | ensureRowsByIdCache(); | ||||
for (var i = 0; i < idArray.length; i++) { | |||||
for (var i = 0, l = idArray.length; i < l; i++) { | |||||
var row = rowsById[idArray[i]]; | var row = rowsById[idArray[i]]; | ||||
if (row != null) { | if (row != null) { | ||||
rows[rows.length] = row; | rows[rows.length] = row; | ||||
@@ -315,7 +316,7 @@ | |||||
function mapRowsToIds(rowArray) { | function mapRowsToIds(rowArray) { | ||||
var ids = []; | var ids = []; | ||||
for (var i = 0; i < rowArray.length; i++) { | |||||
for (var i = 0, l = rowArray.length; i < l; i++) { | |||||
if (rowArray[i] < rows.length) { | if (rowArray[i] < rows.length) { | ||||
ids[ids.length] = rows[rowArray[i]][idProperty]; | ids[ids.length] = rows[rowArray[i]][idProperty]; | ||||
} | } | ||||
@@ -363,7 +364,22 @@ | |||||
} | } | ||||
function getItem(i) { | 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) { | function getItemMetadata(i) { | ||||
@@ -372,7 +388,7 @@ | |||||
return null; | return null; | ||||
} | } | ||||
// overrides for setGrouping rows | |||||
// overrides for grouping rows | |||||
if (item.__group) { | if (item.__group) { | ||||
return options.groupItemMetadataProvider.getGroupRowMetadata(item); | return options.groupItemMetadataProvider.getGroupRowMetadata(item); | ||||
} | } | ||||
@@ -421,7 +437,7 @@ | |||||
* @param varArgs Either a Slick.Group's "groupingKey" property, or a | * @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 | * 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 | * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of | ||||
* the 'high' setGrouping. | |||||
* the 'high' group. | |||||
*/ | */ | ||||
function collapseGroup(varArgs) { | function collapseGroup(varArgs) { | ||||
var args = Array.prototype.slice.call(arguments); | var args = Array.prototype.slice.call(arguments); | ||||
@@ -437,7 +453,7 @@ | |||||
* @param varArgs Either a Slick.Group's "groupingKey" property, or a | * @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 | * 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 | * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of | ||||
* the 'high' setGrouping. | |||||
* the 'high' group. | |||||
*/ | */ | ||||
function expandGroup(varArgs) { | function expandGroup(varArgs) { | ||||
var args = Array.prototype.slice.call(arguments); | var args = Array.prototype.slice.call(arguments); | ||||
@@ -457,7 +473,7 @@ | |||||
var group; | var group; | ||||
var val; | var val; | ||||
var groups = []; | var groups = []; | ||||
var groupsByVal = []; | |||||
var groupsByVal = {}; | |||||
var r; | var r; | ||||
var level = parentGroup ? parentGroup.level + 1 : 0; | var level = parentGroup ? parentGroup.level + 1 : 0; | ||||
var gi = groupingInfos[level]; | var gi = groupingInfos[level]; | ||||
@@ -503,27 +519,50 @@ | |||||
return groups; | 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 gi = groupingInfos[group.level]; | ||||
var isLeafLevel = (group.level == groupingInfos.length); | var isLeafLevel = (group.level == groupingInfos.length); | ||||
var totals = new Slick.GroupTotals(); | |||||
var agg, idx = gi.aggregators.length; | 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--) { | while (idx--) { | ||||
agg = gi.aggregators[idx]; | agg = gi.aggregators[idx]; | ||||
agg.init(); | 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); | agg.storeResult(totals); | ||||
} | } | ||||
totals.initialized = true; | |||||
} | |||||
function addGroupTotals(group) { | |||||
var gi = groupingInfos[group.level]; | |||||
var totals = new Slick.GroupTotals(); | |||||
totals.group = group; | totals.group = group; | ||||
group.totals = totals; | group.totals = totals; | ||||
if (!gi.lazyTotalsCalculation) { | |||||
calculateTotals(totals); | |||||
} | |||||
} | } | ||||
function calculateTotals(groups, level) { | |||||
function addTotals(groups, level) { | |||||
level = level || 0; | level = level || 0; | ||||
var gi = groupingInfos[level]; | var gi = groupingInfos[level]; | ||||
var groupCollapsed = gi.collapsed; | |||||
var toggledGroups = toggledGroupsByLevel[level]; | |||||
var idx = groups.length, g; | var idx = groups.length, g; | ||||
while (idx--) { | while (idx--) { | ||||
g = groups[idx]; | g = groups[idx]; | ||||
@@ -532,38 +571,20 @@ | |||||
continue; | 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) { | if (g.groups) { | ||||
calculateTotals(g.groups, level + 1); | |||||
addTotals(g.groups, level + 1); | |||||
} | } | ||||
if (gi.aggregators.length && ( | if (gi.aggregators.length && ( | ||||
gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.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.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey]; | ||||
g.title = gi.formatter ? gi.formatter(g) : g.value; | 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) { | function flattenGroupedRows(groups, level) { | ||||
level = level || 0; | level = level || 0; | ||||
@@ -613,10 +634,10 @@ | |||||
var filterInfo = getFunctionInfo(filter); | var filterInfo = getFunctionInfo(filter); | ||||
var filterBody = filterInfo.body | var filterBody = filterInfo.body | ||||
.replace(/return false[;}]/gi, "{ continue _coreloop; }") | |||||
.replace(/return true[;}]/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }") | |||||
.replace(/return ([^;}]+?);/gi, | |||||
"{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }"); | |||||
.replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") | |||||
.replace(/return true\s*([;}]|$)/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }$1") | |||||
.replace(/return ([^;}]+?)\s*([;}]|$)/gi, | |||||
"{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); | |||||
// This preserves the function template code after JS compression, | // This preserves the function template code after JS compression, | ||||
// so that replace() commands still work as expected. | // so that replace() commands still work as expected. | ||||
@@ -645,10 +666,10 @@ | |||||
var filterInfo = getFunctionInfo(filter); | var filterInfo = getFunctionInfo(filter); | ||||
var filterBody = filterInfo.body | var filterBody = filterInfo.body | ||||
.replace(/return false[;}]/gi, "{ continue _coreloop; }") | |||||
.replace(/return true[;}]/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }") | |||||
.replace(/return ([^;}]+?);/gi, | |||||
"{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }"); | |||||
.replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") | |||||
.replace(/return true\s*([;}]|$)/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1") | |||||
.replace(/return ([^;}]+?)\s*([;}]|$)/gi, | |||||
"{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); | |||||
// This preserves the function template code after JS compression, | // This preserves the function template code after JS compression, | ||||
// so that replace() commands still work as expected. | // so that replace() commands still work as expected. | ||||
@@ -793,8 +814,7 @@ | |||||
if (groupingInfos.length) { | if (groupingInfos.length) { | ||||
groups = extractGroups(newRows); | groups = extractGroups(newRows); | ||||
if (groups.length) { | if (groups.length) { | ||||
calculateTotals(groups); | |||||
finalizeGroups(groups); | |||||
addTotals(groups); | |||||
newRows = flattenGroupedRows(groups); | newRows = flattenGroupedRows(groups); | ||||
} | } | ||||
} | } | ||||
@@ -838,17 +858,50 @@ | |||||
} | } | ||||
} | } | ||||
function syncGridSelection(grid, preserveHidden) { | |||||
/*** | |||||
* Wires the grid and the DataView together to keep row selection tied to item ids. | |||||
* This is useful since, without it, the grid only knows about rows, so if the items | |||||
* move around, the same rows stay selected instead of the selection moving along | |||||
* with the items. | |||||
* | |||||
* NOTE: This doesn't work with cell selection model. | |||||
* | |||||
* @param grid {Slick.Grid} The grid to sync selection with. | |||||
* @param preserveHidden {Boolean} Whether to keep selected items that go out of the | |||||
* view due to them getting filtered out. | |||||
* @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items | |||||
* that are currently out of the view (see preserveHidden) as selected when selection | |||||
* changes. | |||||
* @return {Slick.Event} An event that notifies when an internal list of selected row ids | |||||
* changes. This is useful since, in combination with the above two options, it allows | |||||
* access to the full list selected row ids, and not just the ones visible to the grid. | |||||
* @method syncGridSelection | |||||
*/ | |||||
function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) { | |||||
var self = this; | var self = this; | ||||
var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());; | |||||
var inHandler; | var inHandler; | ||||
var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); | |||||
var onSelectedRowIdsChanged = new Slick.Event(); | |||||
function setSelectedRowIds(rowIds) { | |||||
if (selectedRowIds.join(",") == rowIds.join(",")) { | |||||
return; | |||||
} | |||||
selectedRowIds = rowIds; | |||||
onSelectedRowIdsChanged.notify({ | |||||
"grid": grid, | |||||
"ids": selectedRowIds | |||||
}, new Slick.EventData(), self); | |||||
} | |||||
function update() { | function update() { | ||||
if (selectedRowIds.length > 0) { | if (selectedRowIds.length > 0) { | ||||
inHandler = true; | inHandler = true; | ||||
var selectedRows = self.mapIdsToRows(selectedRowIds); | var selectedRows = self.mapIdsToRows(selectedRowIds); | ||||
if (!preserveHidden) { | if (!preserveHidden) { | ||||
selectedRowIds = self.mapRowsToIds(selectedRows); | |||||
setSelectedRowIds(self.mapRowsToIds(selectedRows)); | |||||
} | } | ||||
grid.setSelectedRows(selectedRows); | grid.setSelectedRows(selectedRows); | ||||
inHandler = false; | inHandler = false; | ||||
@@ -857,12 +910,22 @@ | |||||
grid.onSelectedRowsChanged.subscribe(function(e, args) { | grid.onSelectedRowsChanged.subscribe(function(e, args) { | ||||
if (inHandler) { return; } | if (inHandler) { return; } | ||||
selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); | |||||
var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); | |||||
if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) { | |||||
setSelectedRowIds(newSelectedRowIds); | |||||
} else { | |||||
// keep the ones that are hidden | |||||
var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; }); | |||||
// add the newly selected ones | |||||
setSelectedRowIds(existing.concat(newSelectedRowIds)); | |||||
} | |||||
}); | }); | ||||
this.onRowsChanged.subscribe(update); | this.onRowsChanged.subscribe(update); | ||||
this.onRowCountChanged.subscribe(update); | this.onRowCountChanged.subscribe(update); | ||||
return onSelectedRowIdsChanged; | |||||
} | } | ||||
function syncGridCellCssStyles(grid, key) { | function syncGridCellCssStyles(grid, key) { | ||||
@@ -910,7 +973,7 @@ | |||||
this.onRowCountChanged.subscribe(update); | this.onRowCountChanged.subscribe(update); | ||||
} | } | ||||
return { | |||||
$.extend(this, { | |||||
// methods | // methods | ||||
"beginUpdate": beginUpdate, | "beginUpdate": beginUpdate, | ||||
"endUpdate": endUpdate, | "endUpdate": endUpdate, | ||||
@@ -956,7 +1019,7 @@ | |||||
"onRowCountChanged": onRowCountChanged, | "onRowCountChanged": onRowCountChanged, | ||||
"onRowsChanged": onRowsChanged, | "onRowsChanged": onRowsChanged, | ||||
"onPagingInfoChanged": onPagingInfoChanged | "onPagingInfoChanged": onPagingInfoChanged | ||||
}; | |||||
}); | |||||
} | } | ||||
function AvgAggregator(field) { | function AvgAggregator(field) { | ||||
@@ -304,14 +304,14 @@ | |||||
this.loadValue = function (item) { | this.loadValue = function (item) { | ||||
defaultValue = !!item[args.column.field]; | defaultValue = !!item[args.column.field]; | ||||
if (defaultValue) { | if (defaultValue) { | ||||
$select.attr("checked", "checked"); | |||||
$select.prop('checked', true); | |||||
} else { | } else { | ||||
$select.removeAttr("checked"); | |||||
$select.prop('checked', false); | |||||
} | } | ||||
}; | }; | ||||
this.serializeValue = function () { | this.serializeValue = function () { | ||||
return !!$select.attr("checked"); | |||||
return $select.prop('checked'); | |||||
}; | }; | ||||
this.applyValue = function (item, state) { | this.applyValue = function (item, state) { | ||||
@@ -0,0 +1,59 @@ | |||||
/*** | |||||
* Contains basic SlickGrid formatters. | |||||
* | |||||
* NOTE: These are merely examples. You will most likely need to implement something more | |||||
* robust/extensible/localizable/etc. for your use! | |||||
* | |||||
* @module Formatters | |||||
* @namespace Slick | |||||
*/ | |||||
(function ($) { | |||||
// register namespace | |||||
$.extend(true, window, { | |||||
"Slick": { | |||||
"Formatters": { | |||||
"PercentComplete": PercentCompleteFormatter, | |||||
"PercentCompleteBar": PercentCompleteBarFormatter, | |||||
"YesNo": YesNoFormatter, | |||||
"Checkmark": CheckmarkFormatter | |||||
} | |||||
} | |||||
}); | |||||
function PercentCompleteFormatter(row, cell, value, columnDef, dataContext) { | |||||
if (value == null || value === "") { | |||||
return "-"; | |||||
} else if (value < 50) { | |||||
return "<span style='color:red;font-weight:bold;'>" + value + "%</span>"; | |||||
} else { | |||||
return "<span style='color:green'>" + value + "%</span>"; | |||||
} | |||||
} | |||||
function PercentCompleteBarFormatter(row, cell, value, columnDef, dataContext) { | |||||
if (value == null || value === "") { | |||||
return ""; | |||||
} | |||||
var color; | |||||
if (value < 30) { | |||||
color = "red"; | |||||
} else if (value < 70) { | |||||
color = "silver"; | |||||
} else { | |||||
color = "green"; | |||||
} | |||||
return "<span class='percent-complete-bar' style='background:" + color + ";width:" + value + "%'></span>"; | |||||
} | |||||
function YesNoFormatter(row, cell, value, columnDef, dataContext) { | |||||
return value ? "Yes" : "No"; | |||||
} | |||||
function CheckmarkFormatter(row, cell, value, columnDef, dataContext) { | |||||
return value ? "<img src='../images/tick.png'>" : ""; | |||||
} | |||||
})(jQuery); |
@@ -48,6 +48,8 @@ classes should alter those! | |||||
width: 8px; | width: 8px; | ||||
height: 5px; | height: 5px; | ||||
margin-left: 4px; | margin-left: 4px; | ||||
margin-top: 6px; | |||||
float: left; | |||||
} | } | ||||
.slick-sort-indicator-desc { | .slick-sort-indicator-desc { | ||||
@@ -1,13 +1,13 @@ | |||||
/** | /** | ||||
* @license | * @license | ||||
* (c) 2009-2012 Michael Leibman | |||||
* (c) 2009-2013 Michael Leibman | |||||
* michael{dot}leibman{at}gmail{dot}com | * michael{dot}leibman{at}gmail{dot}com | ||||
* http://github.com/mleibman/slickgrid | * http://github.com/mleibman/slickgrid | ||||
* | * | ||||
* Distributed under MIT license. | * Distributed under MIT license. | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* SlickGrid v2.1 | |||||
* SlickGrid v2.2 | |||||
* | * | ||||
* NOTES: | * NOTES: | ||||
* Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods. | * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods. | ||||
@@ -85,7 +85,8 @@ if (typeof Slick === "undefined") { | |||||
fullWidthRows: false, | fullWidthRows: false, | ||||
multiColumnSort: false, | multiColumnSort: false, | ||||
defaultFormatter: defaultFormatter, | defaultFormatter: defaultFormatter, | ||||
forceSyncScrolling: false | |||||
forceSyncScrolling: false, | |||||
addNewRowCssClass: "new-row" | |||||
}; | }; | ||||
var columnDefaults = { | var columnDefaults = { | ||||
@@ -133,7 +134,6 @@ if (typeof Slick === "undefined") { | |||||
var headerColumnWidthDiff = 0, headerColumnHeightDiff = 0, // border+padding | var headerColumnWidthDiff = 0, headerColumnHeightDiff = 0, // border+padding | ||||
cellWidthDiff = 0, cellHeightDiff = 0; | cellWidthDiff = 0, cellHeightDiff = 0; | ||||
var absoluteColumnMinWidth; | var absoluteColumnMinWidth; | ||||
var numberOfRows = 0; | |||||
var tabbingDirection = 1; | var tabbingDirection = 1; | ||||
var activePosX; | var activePosX; | ||||
@@ -177,6 +177,11 @@ if (typeof Slick === "undefined") { | |||||
var counter_rows_rendered = 0; | var counter_rows_rendered = 0; | ||||
var counter_rows_removed = 0; | var counter_rows_removed = 0; | ||||
// These two variables work around a bug with inertial scrolling in Webkit/Blink on Mac. | |||||
// See http://crbug.com/312427. | |||||
var rowNodeFromLastMouseWheelEvent; // this node must not be deleted while inertial scrolling | |||||
var zombieRowNodeFromLastMouseWheelEvent; // node that was hidden instead of getting deleted | |||||
////////////////////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////////////////////// | ||||
// Initialization | // Initialization | ||||
@@ -299,6 +304,7 @@ if (typeof Slick === "undefined") { | |||||
$container | $container | ||||
.bind("resize.slickgrid", resizeCanvas); | .bind("resize.slickgrid", resizeCanvas); | ||||
$viewport | $viewport | ||||
//.bind("click", handleClick) | |||||
.bind("scroll", handleScroll); | .bind("scroll", handleScroll); | ||||
$headerScroller | $headerScroller | ||||
.bind("contextmenu", handleHeaderContextMenu) | .bind("contextmenu", handleHeaderContextMenu) | ||||
@@ -320,6 +326,12 @@ if (typeof Slick === "undefined") { | |||||
.bind("dragend", handleDragEnd) | .bind("dragend", handleDragEnd) | ||||
.delegate(".slick-cell", "mouseenter", handleMouseEnter) | .delegate(".slick-cell", "mouseenter", handleMouseEnter) | ||||
.delegate(".slick-cell", "mouseleave", handleMouseLeave); | .delegate(".slick-cell", "mouseleave", handleMouseLeave); | ||||
// Work around http://crbug.com/312427. | |||||
if (navigator.userAgent.toLowerCase().match(/webkit/) && | |||||
navigator.userAgent.toLowerCase().match(/macintosh/)) { | |||||
$canvas.bind("mousewheel", handleMouseWheel); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -547,9 +559,10 @@ if (typeof Slick === "undefined") { | |||||
for (var i = 0; i < columns.length; i++) { | for (var i = 0; i < columns.length; i++) { | ||||
var m = columns[i]; | var m = columns[i]; | ||||
var header = $("<div class='ui-state-default slick-header-column' id='" + uid + m.id + "' />") | |||||
var header = $("<div class='ui-state-default slick-header-column' />") | |||||
.html("<span class='slick-column-name'>" + m.name + "</span>") | .html("<span class='slick-column-name'>" + m.name + "</span>") | ||||
.width(m.width - headerColumnWidthDiff) | .width(m.width - headerColumnWidthDiff) | ||||
.attr("id", "" + uid + m.id) | |||||
.attr("title", m.toolTip || "") | .attr("title", m.toolTip || "") | ||||
.data("column", m) | .data("column", m) | ||||
.addClass(m.headerCssClass || "") | .addClass(m.headerCssClass || "") | ||||
@@ -666,8 +679,8 @@ if (typeof Slick === "undefined") { | |||||
tolerance: "intersection", | tolerance: "intersection", | ||||
helper: "clone", | helper: "clone", | ||||
placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", | placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", | ||||
forcePlaceholderSize: true, | |||||
start: function (e, ui) { | start: function (e, ui) { | ||||
ui.placeholder.width(ui.helper.outerWidth() - headerColumnWidthDiff); | |||||
$(ui.helper).addClass("slick-header-column-active"); | $(ui.helper).addClass("slick-header-column-active"); | ||||
}, | }, | ||||
beforeStop: function (e, ui) { | beforeStop: function (e, ui) { | ||||
@@ -878,23 +891,27 @@ if (typeof Slick === "undefined") { | |||||
el = $("<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>").appendTo($headers); | el = $("<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>").appendTo($headers); | ||||
headerColumnWidthDiff = headerColumnHeightDiff = 0; | headerColumnWidthDiff = headerColumnHeightDiff = 0; | ||||
$.each(h, function (n, val) { | |||||
headerColumnWidthDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
$.each(v, function (n, val) { | |||||
headerColumnHeightDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
if (el.css("box-sizing") != "border-box" && el.css("-moz-box-sizing") != "border-box" && el.css("-webkit-box-sizing") != "border-box") { | |||||
$.each(h, function (n, val) { | |||||
headerColumnWidthDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
$.each(v, function (n, val) { | |||||
headerColumnHeightDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
} | |||||
el.remove(); | el.remove(); | ||||
var r = $("<div class='slick-row' />").appendTo($canvas); | var r = $("<div class='slick-row' />").appendTo($canvas); | ||||
el = $("<div class='slick-cell' id='' style='visibility:hidden'>-</div>").appendTo(r); | el = $("<div class='slick-cell' id='' style='visibility:hidden'>-</div>").appendTo(r); | ||||
cellWidthDiff = cellHeightDiff = 0; | cellWidthDiff = cellHeightDiff = 0; | ||||
$.each(h, function (n, val) { | |||||
cellWidthDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
$.each(v, function (n, val) { | |||||
cellHeightDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
if (el.css("box-sizing") != "border-box" && el.css("-moz-box-sizing") != "border-box" && el.css("-webkit-box-sizing") != "border-box") { | |||||
$.each(h, function (n, val) { | |||||
cellWidthDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
$.each(v, function (n, val) { | |||||
cellHeightDiff += parseFloat(el.css(val)) || 0; | |||||
}); | |||||
} | |||||
r.remove(); | r.remove(); | ||||
absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff); | absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff); | ||||
@@ -975,8 +992,8 @@ if (typeof Slick === "undefined") { | |||||
unregisterPlugin(plugins[i]); | unregisterPlugin(plugins[i]); | ||||
} | } | ||||
if (options.enableColumnReorder && $headers.sortable) { | |||||
$headers.sortable("destroy"); | |||||
if (options.enableColumnReorder) { | |||||
$headers.filter(":ui-sortable").sortable("destroy"); | |||||
} | } | ||||
unbindAncestorScrollEvents(); | unbindAncestorScrollEvents(); | ||||
@@ -1044,7 +1061,7 @@ if (typeof Slick === "undefined") { | |||||
shrinkLeeway -= shrinkSize; | shrinkLeeway -= shrinkSize; | ||||
widths[i] -= shrinkSize; | widths[i] -= shrinkSize; | ||||
} | } | ||||
if (prevTotal == total) { // avoid infinite loop | |||||
if (prevTotal <= total) { // avoid infinite loop | |||||
break; | break; | ||||
} | } | ||||
prevTotal = total; | prevTotal = total; | ||||
@@ -1056,14 +1073,18 @@ if (typeof Slick === "undefined") { | |||||
var growProportion = availWidth / total; | var growProportion = availWidth / total; | ||||
for (i = 0; i < columns.length && total < availWidth; i++) { | for (i = 0; i < columns.length && total < availWidth; i++) { | ||||
c = columns[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; | total += growSize; | ||||
widths[i] += growSize; | widths[i] += growSize; | ||||
} | } | ||||
if (prevTotal == total) { // avoid infinite loop | |||||
if (prevTotal >= total) { // avoid infinite loop | |||||
break; | break; | ||||
} | } | ||||
prevTotal = total; | prevTotal = total; | ||||
@@ -1257,6 +1278,10 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
} | } | ||||
function getDataLengthIncludingAddNew() { | |||||
return getDataLength() + (options.enableAddRow ? 1 : 0); | |||||
} | |||||
function getDataItem(i) { | function getDataItem(i) { | ||||
if (data.getItem) { | if (data.getItem) { | ||||
return data.getItem(i); | return data.getItem(i); | ||||
@@ -1291,6 +1316,10 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
} | } | ||||
function getContainerNode() { | |||||
return $container.get(0); | |||||
} | |||||
////////////////////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////////////////////// | ||||
// Rendering / Scrolling | // Rendering / Scrolling | ||||
@@ -1330,7 +1359,7 @@ if (typeof Slick === "undefined") { | |||||
if (value == null) { | if (value == null) { | ||||
return ""; | return ""; | ||||
} else { | } else { | ||||
return value.toString().replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | |||||
return (value + "").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | |||||
} | } | ||||
} | } | ||||
@@ -1371,14 +1400,18 @@ if (typeof Slick === "undefined") { | |||||
return item[columnDef.field]; | return item[columnDef.field]; | ||||
} | } | ||||
function appendRowHtml(stringArray, row, range) { | |||||
function appendRowHtml(stringArray, row, range, dataLength) { | |||||
var d = getDataItem(row); | var d = getDataItem(row); | ||||
var dataLoading = row < getDataLength() && !d; | |||||
var dataLoading = row < dataLength && !d; | |||||
var rowCss = "slick-row" + | var rowCss = "slick-row" + | ||||
(dataLoading ? " loading" : "") + | (dataLoading ? " loading" : "") + | ||||
(row === activeRow ? " active" : "") + | (row === activeRow ? " active" : "") + | ||||
(row % 2 == 1 ? " odd" : " even"); | (row % 2 == 1 ? " odd" : " even"); | ||||
if (!d) { | |||||
rowCss += " " + options.addNewRowCssClass; | |||||
} | |||||
var metadata = data.getItemMetadata && data.getItemMetadata(row); | var metadata = data.getItemMetadata && data.getItemMetadata(row); | ||||
if (metadata && metadata.cssClasses) { | if (metadata && metadata.cssClasses) { | ||||
@@ -1406,7 +1439,7 @@ if (typeof Slick === "undefined") { | |||||
break; | break; | ||||
} | } | ||||
appendCellHtml(stringArray, row, i, colspan); | |||||
appendCellHtml(stringArray, row, i, colspan, d); | |||||
} | } | ||||
if (colspan > 1) { | if (colspan > 1) { | ||||
@@ -1417,9 +1450,8 @@ if (typeof Slick === "undefined") { | |||||
stringArray.push("</div>"); | stringArray.push("</div>"); | ||||
} | } | ||||
function appendCellHtml(stringArray, row, cell, colspan) { | |||||
function appendCellHtml(stringArray, row, cell, colspan, item) { | |||||
var m = columns[cell]; | var m = columns[cell]; | ||||
var d = getDataItem(row); | |||||
var cellCss = "slick-cell l" + cell + " r" + Math.min(columns.length - 1, cell + colspan - 1) + | var cellCss = "slick-cell l" + cell + " r" + Math.min(columns.length - 1, cell + colspan - 1) + | ||||
(m.cssClass ? " " + m.cssClass : ""); | (m.cssClass ? " " + m.cssClass : ""); | ||||
if (row === activeRow && cell === activeCell) { | if (row === activeRow && cell === activeCell) { | ||||
@@ -1436,9 +1468,9 @@ if (typeof Slick === "undefined") { | |||||
stringArray.push("<div class='" + cellCss + "'>"); | stringArray.push("<div class='" + cellCss + "'>"); | ||||
// if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet) | // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet) | ||||
if (d) { | |||||
var value = getDataItemValueForColumn(d, m); | |||||
stringArray.push(getFormatter(row, m)(row, cell, value, m, d)); | |||||
if (item) { | |||||
var value = getDataItemValueForColumn(item, m); | |||||
stringArray.push(getFormatter(row, m)(row, cell, value, m, item)); | |||||
} | } | ||||
stringArray.push("</div>"); | stringArray.push("</div>"); | ||||
@@ -1476,7 +1508,14 @@ if (typeof Slick === "undefined") { | |||||
if (!cacheEntry) { | if (!cacheEntry) { | ||||
return; | return; | ||||
} | } | ||||
$canvas[0].removeChild(cacheEntry.rowNode); | |||||
if (rowNodeFromLastMouseWheelEvent == cacheEntry.rowNode) { | |||||
cacheEntry.rowNode.style.display = 'none'; | |||||
zombieRowNodeFromLastMouseWheelEvent = rowNodeFromLastMouseWheelEvent; | |||||
} else { | |||||
$canvas[0].removeChild(cacheEntry.rowNode); | |||||
} | |||||
delete rowsCache[row]; | delete rowsCache[row]; | ||||
delete postProcessedRows[row]; | delete postProcessedRows[row]; | ||||
renderedRows--; | renderedRows--; | ||||
@@ -1526,6 +1565,8 @@ if (typeof Slick === "undefined") { | |||||
ensureCellNodesInRowsCache(row); | ensureCellNodesInRowsCache(row); | ||||
var d = getDataItem(row); | |||||
for (var columnIdx in cacheEntry.cellNodesByColumnIdx) { | for (var columnIdx in cacheEntry.cellNodesByColumnIdx) { | ||||
if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) { | if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) { | ||||
continue; | continue; | ||||
@@ -1533,7 +1574,6 @@ if (typeof Slick === "undefined") { | |||||
columnIdx = columnIdx | 0; | columnIdx = columnIdx | 0; | ||||
var m = columns[columnIdx], | var m = columns[columnIdx], | ||||
d = getDataItem(row), | |||||
node = cacheEntry.cellNodesByColumnIdx[columnIdx]; | node = cacheEntry.cellNodesByColumnIdx[columnIdx]; | ||||
if (row === activeRow && columnIdx === activeCell && currentEditor) { | if (row === activeRow && columnIdx === activeCell && currentEditor) { | ||||
@@ -1560,7 +1600,7 @@ if (typeof Slick === "undefined") { | |||||
function resizeCanvas() { | function resizeCanvas() { | ||||
if (!initialized) { return; } | if (!initialized) { return; } | ||||
if (options.autoHeight) { | if (options.autoHeight) { | ||||
viewportH = options.rowHeight * (getDataLength() + (options.enableAddRow ? 1 : 0)); | |||||
viewportH = options.rowHeight * getDataLengthIncludingAddNew(); | |||||
} else { | } else { | ||||
viewportH = getViewportHeight(); | viewportH = getViewportHeight(); | ||||
} | } | ||||
@@ -1577,22 +1617,27 @@ if (typeof Slick === "undefined") { | |||||
updateRowCount(); | updateRowCount(); | ||||
handleScroll(); | handleScroll(); | ||||
// Since the width has changed, force the render() to reevaluate virtually rendered cells. | |||||
lastRenderedScrollLeft = -1; | |||||
render(); | render(); | ||||
} | } | ||||
function updateRowCount() { | function updateRowCount() { | ||||
if (!initialized) { return; } | if (!initialized) { return; } | ||||
numberOfRows = getDataLength() + | |||||
(options.enableAddRow ? 1 : 0) + | |||||
var dataLengthIncludingAddNew = getDataLengthIncludingAddNew(); | |||||
var numberOfRows = dataLengthIncludingAddNew + | |||||
(options.leaveSpaceForNewRows ? numVisibleRows - 1 : 0); | (options.leaveSpaceForNewRows ? numVisibleRows - 1 : 0); | ||||
var oldViewportHasVScroll = viewportHasVScroll; | var oldViewportHasVScroll = viewportHasVScroll; | ||||
// with autoHeight, we do not need to accommodate the vertical scroll bar | // with autoHeight, we do not need to accommodate the vertical scroll bar | ||||
viewportHasVScroll = !options.autoHeight && (numberOfRows * options.rowHeight > viewportH); | viewportHasVScroll = !options.autoHeight && (numberOfRows * options.rowHeight > viewportH); | ||||
makeActiveCellNormal(); | |||||
// remove the rows that are now outside of the data range | // remove the rows that are now outside of the data range | ||||
// this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows | // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows | ||||
var l = options.enableAddRow ? getDataLength() : getDataLength() - 1; | |||||
var l = dataLengthIncludingAddNew - 1; | |||||
for (var i in rowsCache) { | for (var i in rowsCache) { | ||||
if (i >= l) { | if (i >= l) { | ||||
removeRowFromCache(i); | removeRowFromCache(i); | ||||
@@ -1678,7 +1723,7 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
range.top = Math.max(0, range.top); | range.top = Math.max(0, range.top); | ||||
range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, range.bottom); | |||||
range.bottom = Math.min(getDataLengthIncludingAddNew() - 1, range.bottom); | |||||
range.leftPx -= viewportW; | range.leftPx -= viewportW; | ||||
range.rightPx += viewportW; | range.rightPx += viewportW; | ||||
@@ -1747,7 +1792,7 @@ if (typeof Slick === "undefined") { | |||||
var totalCellsAdded = 0; | var totalCellsAdded = 0; | ||||
var colspan; | var colspan; | ||||
for (var row = range.top; row <= range.bottom; row++) { | |||||
for (var row = range.top, btm = range.bottom; row <= btm; row++) { | |||||
cacheEntry = rowsCache[row]; | cacheEntry = rowsCache[row]; | ||||
if (!cacheEntry) { | if (!cacheEntry) { | ||||
continue; | continue; | ||||
@@ -1764,6 +1809,8 @@ if (typeof Slick === "undefined") { | |||||
var metadata = data.getItemMetadata && data.getItemMetadata(row); | var metadata = data.getItemMetadata && data.getItemMetadata(row); | ||||
metadata = metadata && metadata.columns; | metadata = metadata && metadata.columns; | ||||
var d = getDataItem(row); | |||||
// TODO: shorten this loop (index? heuristics? binary search?) | // TODO: shorten this loop (index? heuristics? binary search?) | ||||
for (var i = 0, ii = columns.length; i < ii; i++) { | for (var i = 0, ii = columns.length; i < ii; i++) { | ||||
// Cells to the right are outside the range. | // Cells to the right are outside the range. | ||||
@@ -1787,7 +1834,7 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) { | if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) { | ||||
appendCellHtml(stringArray, row, i, colspan); | |||||
appendCellHtml(stringArray, row, i, colspan, d); | |||||
cellsAdded++; | cellsAdded++; | ||||
} | } | ||||
@@ -1824,9 +1871,10 @@ if (typeof Slick === "undefined") { | |||||
var parentNode = $canvas[0], | var parentNode = $canvas[0], | ||||
stringArray = [], | stringArray = [], | ||||
rows = [], | rows = [], | ||||
needToReselectCell = false; | |||||
needToReselectCell = false, | |||||
dataLength = getDataLength(); | |||||
for (var i = range.top; i <= range.bottom; i++) { | |||||
for (var i = range.top, ii = range.bottom; i <= ii; i++) { | |||||
if (rowsCache[i]) { | if (rowsCache[i]) { | ||||
continue; | continue; | ||||
} | } | ||||
@@ -1851,7 +1899,7 @@ if (typeof Slick === "undefined") { | |||||
"cellRenderQueue": [] | "cellRenderQueue": [] | ||||
}; | }; | ||||
appendRowHtml(stringArray, i, range); | |||||
appendRowHtml(stringArray, i, range, dataLength); | |||||
if (activeCellNode && activeRow === i) { | if (activeCellNode && activeRow === i) { | ||||
needToReselectCell = true; | needToReselectCell = true; | ||||
} | } | ||||
@@ -1910,7 +1958,7 @@ if (typeof Slick === "undefined") { | |||||
renderRows(rendered); | renderRows(rendered); | ||||
postProcessFromRow = visible.top; | postProcessFromRow = visible.top; | ||||
postProcessToRow = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, visible.bottom); | |||||
postProcessToRow = Math.min(getDataLengthIncludingAddNew() - 1, visible.bottom); | |||||
startPostProcessing(); | startPostProcessing(); | ||||
lastRenderedScrollTop = scrollTop; | lastRenderedScrollTop = scrollTop; | ||||
@@ -1982,10 +2030,11 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
function asyncPostProcessRows() { | function asyncPostProcessRows() { | ||||
var dataLength = getDataLength(); | |||||
while (postProcessFromRow <= postProcessToRow) { | while (postProcessFromRow <= postProcessToRow) { | ||||
var row = (vScrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; | var row = (vScrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; | ||||
var cacheEntry = rowsCache[row]; | var cacheEntry = rowsCache[row]; | ||||
if (!cacheEntry || row >= getDataLength()) { | |||||
if (!cacheEntry || row >= dataLength) { | |||||
continue; | continue; | ||||
} | } | ||||
@@ -2106,6 +2155,17 @@ if (typeof Slick === "undefined") { | |||||
////////////////////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////////////////////// | ||||
// Interactivity | // Interactivity | ||||
function handleMouseWheel(e) { | |||||
var rowNode = $(e.target).closest(".slick-row")[0]; | |||||
if (rowNode != rowNodeFromLastMouseWheelEvent) { | |||||
if (zombieRowNodeFromLastMouseWheelEvent && zombieRowNodeFromLastMouseWheelEvent != rowNode) { | |||||
$canvas[0].removeChild(zombieRowNodeFromLastMouseWheelEvent); | |||||
zombieRowNodeFromLastMouseWheelEvent = null; | |||||
} | |||||
rowNodeFromLastMouseWheelEvent = rowNode; | |||||
} | |||||
} | |||||
function handleDragInit(e, dd) { | function handleDragInit(e, dd) { | ||||
var cell = getCellFromEvent(e); | var cell = getCellFromEvent(e); | ||||
if (!cell || !cellExists(cell.row, cell.cell)) { | if (!cell || !cellExists(cell.row, cell.cell)) { | ||||
@@ -2155,6 +2215,12 @@ if (typeof Slick === "undefined") { | |||||
return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event) | return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event) | ||||
} | } | ||||
cancelEditAndSetFocus(); | cancelEditAndSetFocus(); | ||||
} else if (e.which == 34) { | |||||
navigatePageDown(); | |||||
handled = true; | |||||
} else if (e.which == 33) { | |||||
navigatePageUp(); | |||||
handled = true; | |||||
} else if (e.which == 37) { | } else if (e.which == 37) { | ||||
handled = navigateLeft(); | handled = navigateLeft(); | ||||
} else if (e.which == 39) { | } else if (e.which == 39) { | ||||
@@ -2205,7 +2271,8 @@ if (typeof Slick === "undefined") { | |||||
if (!currentEditor) { | if (!currentEditor) { | ||||
// if this click resulted in some cell child node getting focus, | // if this click resulted in some cell child node getting focus, | ||||
// don't steal it back - keyboard events will still bubble up | // don't steal it back - keyboard events will still bubble up | ||||
if (e.target != document.activeElement) { | |||||
// IE9+ seems to default DIVs to tabIndex=0 instead of -1, so check for cell clicks directly. | |||||
if (e.target != document.activeElement || $(e.target).hasClass("slick-cell")) { | |||||
setFocus(); | setFocus(); | ||||
} | } | ||||
} | } | ||||
@@ -2223,7 +2290,7 @@ if (typeof Slick === "undefined") { | |||||
if ((activeCell != cell.cell || activeRow != cell.row) && canCellBeActive(cell.row, cell.cell)) { | if ((activeCell != cell.cell || activeRow != cell.row) && canCellBeActive(cell.row, cell.cell)) { | ||||
if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) { | if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) { | ||||
scrollRowIntoView(cell.row, false); | scrollRowIntoView(cell.row, false); | ||||
setActiveCellInternal(getCellNode(cell.row, cell.cell), (cell.row === getDataLength()) || options.autoEdit); | |||||
setActiveCellInternal(getCellNode(cell.row, cell.cell)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -2406,7 +2473,7 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
} | } | ||||
function setActiveCellInternal(newCell, editMode) { | |||||
function setActiveCellInternal(newCell, opt_editMode) { | |||||
if (activeCellNode !== null) { | if (activeCellNode !== null) { | ||||
makeActiveCellNormal(); | makeActiveCellNormal(); | ||||
$(activeCellNode).removeClass("active"); | $(activeCellNode).removeClass("active"); | ||||
@@ -2422,10 +2489,14 @@ if (typeof Slick === "undefined") { | |||||
activeRow = getRowFromNode(activeCellNode.parentNode); | activeRow = getRowFromNode(activeCellNode.parentNode); | ||||
activeCell = activePosX = getCellFromNode(activeCellNode); | activeCell = activePosX = getCellFromNode(activeCellNode); | ||||
if (opt_editMode == null) { | |||||
opt_editMode = (activeRow == getDataLength()) || options.autoEdit; | |||||
} | |||||
$(activeCellNode).addClass("active"); | $(activeCellNode).addClass("active"); | ||||
$(rowsCache[activeRow].rowNode).addClass("active"); | $(rowsCache[activeRow].rowNode).addClass("active"); | ||||
if (options.editable && editMode && isCellPotentiallyEditable(activeRow, activeCell)) { | |||||
if (options.editable && opt_editMode && isCellPotentiallyEditable(activeRow, activeCell)) { | |||||
clearTimeout(h_editorLoader); | clearTimeout(h_editorLoader); | ||||
if (options.asyncEditorLoading) { | if (options.asyncEditorLoading) { | ||||
@@ -2447,7 +2518,10 @@ if (typeof Slick === "undefined") { | |||||
function clearTextSelection() { | function clearTextSelection() { | ||||
if (document.selection && document.selection.empty) { | if (document.selection && document.selection.empty) { | ||||
document.selection.empty(); | |||||
try { | |||||
//IE fails here if selected element is not in dom | |||||
document.selection.empty(); | |||||
} catch (e) { } | |||||
} else if (window.getSelection) { | } else if (window.getSelection) { | ||||
var sel = window.getSelection(); | var sel = window.getSelection(); | ||||
if (sel && sel.removeAllRanges) { | if (sel && sel.removeAllRanges) { | ||||
@@ -2457,13 +2531,14 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
function isCellPotentiallyEditable(row, cell) { | function isCellPotentiallyEditable(row, cell) { | ||||
var dataLength = getDataLength(); | |||||
// is the data for this row loaded? | // is the data for this row loaded? | ||||
if (row < getDataLength() && !getDataItem(row)) { | |||||
if (row < dataLength && !getDataItem(row)) { | |||||
return false; | return false; | ||||
} | } | ||||
// are we in the Add New row? can we create new from this cell? | // are we in the Add New row? can we create new from this cell? | ||||
if (columns[cell].cannotTriggerInsert && row >= getDataLength()) { | |||||
if (columns[cell].cannotTriggerInsert && row >= dataLength) { | |||||
return false; | return false; | ||||
} | } | ||||
@@ -2489,7 +2564,7 @@ if (typeof Slick === "undefined") { | |||||
if (d) { | if (d) { | ||||
var column = columns[activeCell]; | var column = columns[activeCell]; | ||||
var formatter = getFormatter(activeRow, column); | var formatter = getFormatter(activeRow, column); | ||||
activeCellNode.innerHTML = formatter(activeRow, activeCell, getDataItemValueForColumn(d, column), column, getDataItem(activeRow)); | |||||
activeCellNode.innerHTML = formatter(activeRow, activeCell, getDataItemValueForColumn(d, column), column, d); | |||||
invalidatePostProcessingResults(activeRow); | invalidatePostProcessingResults(activeRow); | ||||
} | } | ||||
} | } | ||||
@@ -2680,6 +2755,47 @@ if (typeof Slick === "undefined") { | |||||
render(); | render(); | ||||
} | } | ||||
function scrollPage(dir) { | |||||
var deltaRows = dir * numVisibleRows; | |||||
scrollTo((getRowFromPosition(scrollTop) + deltaRows) * options.rowHeight); | |||||
render(); | |||||
if (options.enableCellNavigation && activeRow != null) { | |||||
var row = activeRow + deltaRows; | |||||
var dataLengthIncludingAddNew = getDataLengthIncludingAddNew(); | |||||
if (row >= dataLengthIncludingAddNew) { | |||||
row = dataLengthIncludingAddNew - 1; | |||||
} | |||||
if (row < 0) { | |||||
row = 0; | |||||
} | |||||
var cell = 0, prevCell = null; | |||||
var prevActivePosX = activePosX; | |||||
while (cell <= activePosX) { | |||||
if (canCellBeActive(row, cell)) { | |||||
prevCell = cell; | |||||
} | |||||
cell += getColspan(row, cell); | |||||
} | |||||
if (prevCell !== null) { | |||||
setActiveCellInternal(getCellNode(row, prevCell)); | |||||
activePosX = prevActivePosX; | |||||
} else { | |||||
resetActiveCell(); | |||||
} | |||||
} | |||||
} | |||||
function navigatePageDown() { | |||||
scrollPage(1); | |||||
} | |||||
function navigatePageUp() { | |||||
scrollPage(-1); | |||||
} | |||||
function getColspan(row, cell) { | function getColspan(row, cell) { | ||||
var metadata = data.getItemMetadata && data.getItemMetadata(row); | var metadata = data.getItemMetadata && data.getItemMetadata(row); | ||||
if (!metadata || !metadata.columns) { | if (!metadata || !metadata.columns) { | ||||
@@ -2770,8 +2886,9 @@ if (typeof Slick === "undefined") { | |||||
function gotoDown(row, cell, posX) { | function gotoDown(row, cell, posX) { | ||||
var prevCell; | var prevCell; | ||||
var dataLengthIncludingAddNew = getDataLengthIncludingAddNew(); | |||||
while (true) { | while (true) { | ||||
if (++row >= getDataLength() + (options.enableAddRow ? 1 : 0)) { | |||||
if (++row >= dataLengthIncludingAddNew) { | |||||
return null; | return null; | ||||
} | } | ||||
@@ -2832,7 +2949,8 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
var firstFocusableCell = null; | var firstFocusableCell = null; | ||||
while (++row < getDataLength() + (options.enableAddRow ? 1 : 0)) { | |||||
var dataLengthIncludingAddNew = getDataLengthIncludingAddNew(); | |||||
while (++row < dataLengthIncludingAddNew) { | |||||
firstFocusableCell = findFirstFocusableCell(row); | firstFocusableCell = findFirstFocusableCell(row); | ||||
if (firstFocusableCell !== null) { | if (firstFocusableCell !== null) { | ||||
return { | return { | ||||
@@ -2847,7 +2965,7 @@ if (typeof Slick === "undefined") { | |||||
function gotoPrev(row, cell, posX) { | function gotoPrev(row, cell, posX) { | ||||
if (row == null && cell == null) { | if (row == null && cell == null) { | ||||
row = getDataLength() + (options.enableAddRow ? 1 : 0) - 1; | |||||
row = getDataLengthIncludingAddNew() - 1; | |||||
cell = posX = columns.length - 1; | cell = posX = columns.length - 1; | ||||
if (canCellBeActive(row, cell)) { | if (canCellBeActive(row, cell)) { | ||||
return { | return { | ||||
@@ -2947,11 +3065,11 @@ if (typeof Slick === "undefined") { | |||||
if (pos) { | if (pos) { | ||||
var isAddNewRow = (pos.row == getDataLength()); | var isAddNewRow = (pos.row == getDataLength()); | ||||
scrollCellIntoView(pos.row, pos.cell, !isAddNewRow); | scrollCellIntoView(pos.row, pos.cell, !isAddNewRow); | ||||
setActiveCellInternal(getCellNode(pos.row, pos.cell), isAddNewRow || options.autoEdit); | |||||
setActiveCellInternal(getCellNode(pos.row, pos.cell)); | |||||
activePosX = pos.posX; | activePosX = pos.posX; | ||||
return true; | return true; | ||||
} else { | } else { | ||||
setActiveCellInternal(getCellNode(activeRow, activeCell), (activeRow == getDataLength()) || options.autoEdit); | |||||
setActiveCellInternal(getCellNode(activeRow, activeCell)); | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
@@ -2979,7 +3097,7 @@ if (typeof Slick === "undefined") { | |||||
} | } | ||||
function canCellBeActive(row, cell) { | function canCellBeActive(row, cell) { | ||||
if (!options.enableCellNavigation || row >= getDataLength() + (options.enableAddRow ? 1 : 0) || | |||||
if (!options.enableCellNavigation || row >= getDataLengthIncludingAddNew() || | |||||
row < 0 || cell >= columns.length || cell < 0) { | row < 0 || cell >= columns.length || cell < 0) { | ||||
return false; | return false; | ||||
} | } | ||||
@@ -3064,10 +3182,20 @@ if (typeof Slick === "undefined") { | |||||
execute: function () { | execute: function () { | ||||
this.editor.applyValue(item, this.serializedValue); | this.editor.applyValue(item, this.serializedValue); | ||||
updateRow(this.row); | updateRow(this.row); | ||||
trigger(self.onCellChange, { | |||||
row: activeRow, | |||||
cell: activeCell, | |||||
item: item | |||||
}); | |||||
}, | }, | ||||
undo: function () { | undo: function () { | ||||
this.editor.applyValue(item, this.prevSerializedValue); | this.editor.applyValue(item, this.prevSerializedValue); | ||||
updateRow(this.row); | updateRow(this.row); | ||||
trigger(self.onCellChange, { | |||||
row: activeRow, | |||||
cell: activeCell, | |||||
item: item | |||||
}); | |||||
} | } | ||||
}; | }; | ||||
@@ -3079,11 +3207,6 @@ if (typeof Slick === "undefined") { | |||||
makeActiveCellNormal(); | makeActiveCellNormal(); | ||||
} | } | ||||
trigger(self.onCellChange, { | |||||
row: activeRow, | |||||
cell: activeCell, | |||||
item: item | |||||
}); | |||||
} else { | } else { | ||||
var newItem = {}; | var newItem = {}; | ||||
currentEditor.applyValue(newItem, currentEditor.serializeValue()); | currentEditor.applyValue(newItem, currentEditor.serializeValue()); | ||||
@@ -3094,9 +3217,10 @@ if (typeof Slick === "undefined") { | |||||
// check whether the lock has been re-acquired by event handlers | // check whether the lock has been re-acquired by event handlers | ||||
return !getEditorLock().isActive(); | return !getEditorLock().isActive(); | ||||
} else { | } else { | ||||
// TODO: remove and put in onValidationError handlers in examples | |||||
// Re-add the CSS class to trigger transitions, if any. | |||||
$(activeCellNode).removeClass("invalid"); | |||||
$(activeCellNode).width(); // force layout | |||||
$(activeCellNode).addClass("invalid"); | $(activeCellNode).addClass("invalid"); | ||||
$(activeCellNode).stop(true, true).effect("highlight", {color: "red"}, 300); | |||||
trigger(self.onValidationError, { | trigger(self.onValidationError, { | ||||
editor: currentEditor, | editor: currentEditor, | ||||
@@ -3232,6 +3356,7 @@ if (typeof Slick === "undefined") { | |||||
"setSelectionModel": setSelectionModel, | "setSelectionModel": setSelectionModel, | ||||
"getSelectedRows": getSelectedRows, | "getSelectedRows": getSelectedRows, | ||||
"setSelectedRows": setSelectedRows, | "setSelectedRows": setSelectedRows, | ||||
"getContainerNode": getContainerNode, | |||||
"render": render, | "render": render, | ||||
"invalidate": invalidate, | "invalidate": invalidate, | ||||
@@ -3269,6 +3394,8 @@ if (typeof Slick === "undefined") { | |||||
"navigateDown": navigateDown, | "navigateDown": navigateDown, | ||||
"navigateLeft": navigateLeft, | "navigateLeft": navigateLeft, | ||||
"navigateRight": navigateRight, | "navigateRight": navigateRight, | ||||
"navigatePageUp": navigatePageUp, | |||||
"navigatePageDown": navigatePageDown, | |||||
"gotoCell": gotoCell, | "gotoCell": gotoCell, | ||||
"getTopPanel": getTopPanel, | "getTopPanel": getTopPanel, | ||||
"setTopPanelVisibility": setTopPanelVisibility, | "setTopPanelVisibility": setTopPanelVisibility, | ||||
@@ -33,7 +33,9 @@ | |||||
toggleCssClass: "slick-group-toggle", | toggleCssClass: "slick-group-toggle", | ||||
toggleExpandedCssClass: "expanded", | toggleExpandedCssClass: "expanded", | ||||
toggleCollapsedCssClass: "collapsed", | toggleCollapsedCssClass: "collapsed", | ||||
enableExpandCollapse: true | |||||
enableExpandCollapse: true, | |||||
groupFormatter: defaultGroupCellFormatter, | |||||
totalsFormatter: defaultTotalsCellFormatter | |||||
}; | }; | ||||
options = $.extend(true, {}, _defaults, options); | options = $.extend(true, {}, _defaults, options); | ||||
@@ -77,6 +79,12 @@ | |||||
function handleGridClick(e, args) { | function handleGridClick(e, args) { | ||||
var item = this.getDataItem(args.row); | var item = this.getDataItem(args.row); | ||||
if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) { | if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) { | ||||
var range = _grid.getRenderedRange(); | |||||
this.getData().setRefreshHints({ | |||||
ignoreDiffsBefore: range.top, | |||||
ignoreDiffsAfter: range.bottom | |||||
}); | |||||
if (item.collapsed) { | if (item.collapsed) { | ||||
this.getData().expandGroup(item.groupingKey); | this.getData().expandGroup(item.groupingKey); | ||||
} else { | } else { | ||||
@@ -95,6 +103,12 @@ | |||||
if (activeCell) { | if (activeCell) { | ||||
var item = this.getDataItem(activeCell.row); | var item = this.getDataItem(activeCell.row); | ||||
if (item && item instanceof Slick.Group) { | if (item && item instanceof Slick.Group) { | ||||
var range = _grid.getRenderedRange(); | |||||
this.getData().setRefreshHints({ | |||||
ignoreDiffsBefore: range.top, | |||||
ignoreDiffsAfter: range.bottom | |||||
}); | |||||
if (item.collapsed) { | if (item.collapsed) { | ||||
this.getData().expandGroup(item.groupingKey); | this.getData().expandGroup(item.groupingKey); | ||||
} else { | } else { | ||||
@@ -116,7 +130,7 @@ | |||||
columns: { | columns: { | ||||
0: { | 0: { | ||||
colspan: "*", | colspan: "*", | ||||
formatter: defaultGroupCellFormatter, | |||||
formatter: options.groupFormatter, | |||||
editor: null | editor: null | ||||
} | } | ||||
} | } | ||||
@@ -128,7 +142,7 @@ | |||||
selectable: false, | selectable: false, | ||||
focusable: options.totalsFocusable, | focusable: options.totalsFocusable, | ||||
cssClasses: options.totalsCssClass, | cssClasses: options.totalsCssClass, | ||||
formatter: defaultTotalsCellFormatter, | |||||
formatter: options.totalsFormatter, | |||||
editor: null | editor: null | ||||
}; | }; | ||||
} | } | ||||
@@ -0,0 +1,173 @@ | |||||
(function ($) { | |||||
/*** | |||||
* A sample AJAX data store implementation. | |||||
* Right now, it's hooked up to load Hackernews stories, but can | |||||
* easily be extended to support any JSONP-compatible backend that accepts paging parameters. | |||||
*/ | |||||
function RemoteModel() { | |||||
// private | |||||
var PAGESIZE = 50; | |||||
var data = {length: 0}; | |||||
var searchstr = ""; | |||||
var sortcol = null; | |||||
var sortdir = 1; | |||||
var h_request = null; | |||||
var req = null; // ajax request | |||||
// events | |||||
var onDataLoading = new Slick.Event(); | |||||
var onDataLoaded = new Slick.Event(); | |||||
function init() { | |||||
} | |||||
function isDataLoaded(from, to) { | |||||
for (var i = from; i <= to; i++) { | |||||
if (data[i] == undefined || data[i] == null) { | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
function clear() { | |||||
for (var key in data) { | |||||
delete data[key]; | |||||
} | |||||
data.length = 0; | |||||
} | |||||
function ensureData(from, to) { | |||||
if (req) { | |||||
req.abort(); | |||||
for (var i = req.fromPage; i <= req.toPage; i++) | |||||
data[i * PAGESIZE] = undefined; | |||||
} | |||||
if (from < 0) { | |||||
from = 0; | |||||
} | |||||
if (data.length > 0) { | |||||
to = Math.min(to, data.length - 1); | |||||
} | |||||
var fromPage = Math.floor(from / PAGESIZE); | |||||
var toPage = Math.floor(to / PAGESIZE); | |||||
while (data[fromPage * PAGESIZE] !== undefined && fromPage < toPage) | |||||
fromPage++; | |||||
while (data[toPage * PAGESIZE] !== undefined && fromPage < toPage) | |||||
toPage--; | |||||
if (fromPage > toPage || ((fromPage == toPage) && data[fromPage * PAGESIZE] !== undefined)) { | |||||
// TODO: look-ahead | |||||
onDataLoaded.notify({from: from, to: to}); | |||||
return; | |||||
} | |||||
var url = "http://api.thriftdb.com/api.hnsearch.com/items/_search?filter[fields][type][]=submission&q=" + searchstr + "&start=" + (fromPage * PAGESIZE) + "&limit=" + (((toPage - fromPage) * PAGESIZE) + PAGESIZE); | |||||
if (sortcol != null) { | |||||
url += ("&sortby=" + sortcol + ((sortdir > 0) ? "+asc" : "+desc")); | |||||
} | |||||
if (h_request != null) { | |||||
clearTimeout(h_request); | |||||
} | |||||
h_request = setTimeout(function () { | |||||
for (var i = fromPage; i <= toPage; i++) | |||||
data[i * PAGESIZE] = null; // null indicates a 'requested but not available yet' | |||||
onDataLoading.notify({from: from, to: to}); | |||||
req = $.jsonp({ | |||||
url: url, | |||||
callbackParameter: "callback", | |||||
cache: true, | |||||
success: onSuccess, | |||||
error: function () { | |||||
onError(fromPage, toPage) | |||||
} | |||||
}); | |||||
req.fromPage = fromPage; | |||||
req.toPage = toPage; | |||||
}, 50); | |||||
} | |||||
function onError(fromPage, toPage) { | |||||
alert("error loading pages " + fromPage + " to " + toPage); | |||||
} | |||||
function onSuccess(resp) { | |||||
var from = resp.request.start, to = from + resp.results.length; | |||||
data.length = Math.min(parseInt(resp.hits),1000); // limitation of the API | |||||
for (var i = 0; i < resp.results.length; i++) { | |||||
var item = resp.results[i].item; | |||||
// Old IE versions can't parse ISO dates, so change to universally-supported format. | |||||
item.create_ts = item.create_ts.replace(/^(\d+)-(\d+)-(\d+)T(\d+:\d+:\d+)Z$/, "$2/$3/$1 $4 UTC"); | |||||
item.create_ts = new Date(item.create_ts); | |||||
data[from + i] = item; | |||||
data[from + i].index = from + i; | |||||
} | |||||
req = null; | |||||
onDataLoaded.notify({from: from, to: to}); | |||||
} | |||||
function reloadData(from, to) { | |||||
for (var i = from; i <= to; i++) | |||||
delete data[i]; | |||||
ensureData(from, to); | |||||
} | |||||
function setSort(column, dir) { | |||||
sortcol = column; | |||||
sortdir = dir; | |||||
clear(); | |||||
} | |||||
function setSearch(str) { | |||||
searchstr = str; | |||||
clear(); | |||||
} | |||||
init(); | |||||
return { | |||||
// properties | |||||
"data": data, | |||||
// methods | |||||
"clear": clear, | |||||
"isDataLoaded": isDataLoaded, | |||||
"ensureData": ensureData, | |||||
"reloadData": reloadData, | |||||
"setSort": setSort, | |||||
"setSearch": setSearch, | |||||
// events | |||||
"onDataLoading": onDataLoading, | |||||
"onDataLoaded": onDataLoaded | |||||
}; | |||||
} | |||||
// Slick.Data.RemoteModel | |||||
$.extend(true, window, { Slick: { Data: { RemoteModel: RemoteModel }}}); | |||||
})(jQuery); |