From 21ec6b00a8ea529d5e74ccd98f06d8cced7185c1 Mon Sep 17 00:00:00 2001 From: joshq00 Date: Fri, 18 Oct 2013 11:54:34 -0400 Subject: [PATCH 1/7] handleMouseWheel fix Fix for handleMouseWheel setting a negative scrollTop --- slick.grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slick.grid.js b/slick.grid.js index 9c525b003..1fb9b70f6 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -2662,7 +2662,7 @@ if (typeof Slick === "undefined") { } function handleMouseWheel(event, delta, deltaX, deltaY) { - scrollTop = $viewportScrollContainerY[0].scrollTop - (deltaY * options.rowHeight); + scrollTop = Math.max(0, $viewportScrollContainerY[0].scrollTop - (deltaY * options.rowHeight)); scrollLeft = $viewportScrollContainerX[0].scrollLeft + (deltaX * 10); _handleScroll(true); event.preventDefault(); From 6c50a8010ad03a7a3877f17cdfda3ef02b8ab943 Mon Sep 17 00:00:00 2001 From: joshq00 Date: Fri, 18 Oct 2013 11:56:26 -0400 Subject: [PATCH 2/7] 'render' not rendering frozen rows fix Render() was not rendering the frozen rows when the grid had been scrolled and then the frozen options set --- slick.grid.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/slick.grid.js b/slick.grid.js index 1fb9b70f6..a8cab1dbf 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -2643,6 +2643,13 @@ if (typeof Slick === "undefined") { ,leftPx: rendered.leftPx ,rightPx: rendered.rightPx }); + } else { + renderRows({ + top: 0 + ,bottom: actualFrozenRow - 1 + ,leftPx: rendered.leftPx + ,rightPx: rendered.rightPx + }); } postProcessFromRow = visible.top; From cea2e55929f07e1f38c722b929e7f8afe0275296 Mon Sep 17 00:00:00 2001 From: joshq00 Date: Fri, 18 Oct 2013 11:58:08 -0400 Subject: [PATCH 3/7] scrollcellintoview fix ScrollCellIntoView was returning on occasions it shouldn't --- slick.grid.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/slick.grid.js b/slick.grid.js index a8cab1dbf..e5026c26f 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -3249,15 +3249,15 @@ if (typeof Slick === "undefined") { } function scrollCellIntoView(row, cell, doPaging) { + if ( row >= actualFrozenRow ) { + scrollRowIntoView(row, doPaging); + } + // Don't scroll to frozen cells if ( cell <= options.frozenColumn ) { return; } - if ( row < actualFrozenRow ) { - scrollRowIntoView(row, doPaging); - } - var colspan = getColspan(row, cell); var left = columnPosLeft[cell], right = columnPosRight[cell + (colspan > 1 ? colspan - 1 : 0)], From 991c3ab6e1ef77b814b82bed113942809039fc26 Mon Sep 17 00:00:00 2001 From: joshq00 Date: Fri, 18 Oct 2013 12:08:03 -0400 Subject: [PATCH 4/7] Add pgUp pgDn functionality Page Up and Page Down functionality were removed with the 'freeze' branch. Re-implemented --- slick.grid.js | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/slick.grid.js b/slick.grid.js index e5026c26f..66101c1d9 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -2959,6 +2959,12 @@ if (typeof Slick === "undefined") { return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event) } cancelEditAndSetFocus(); + } else if (e.which == 34) { + scrollPageDown(); + handled = true; + } else if (e.which == 33) { + scrollPageUp(); + handled = true; } else if (e.which == 37) { handled = navigateLeft(); } else if (e.which == 39) { @@ -3874,6 +3880,63 @@ if (typeof Slick === "undefined") { } } + function scrollPage(dir) { + var numFrozenRows = options.frozenRow > 0 ? options.frozenRow : 0, + numVisibleRows = Math.floor( viewportH / options.rowHeight ), + numVisibleUnfrozenRows = numVisibleRows - numFrozenRows, + deltaRows, + newFirstRow + ; + + if (activeRow != null) { + if (activeRow >= numFrozenRows) { + deltaRows = dir * numVisibleUnfrozenRows; + } else { + deltaRows = dir * ( numFrozenRows - activeRow ); + } + newFirstRow = activeRow + deltaRows; + } else { + newFirstRow = getRowFromPosition( scrollTop ) + numVisibleRows; + } + scrollRowIntoView( newFirstRow ); + render(); + + + if (options.enableCellNavigation && activeRow != null) { + var row = activeRow + deltaRows; + if (row >= numberOfRows) { + row = numberOfRows - 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 scrollPageDown() { + scrollPage(1); + } + + function scrollPageUp() { + scrollPage(-1); + } + function getCellNode(row, cell) { if (rowsCache[row]) { ensureCellNodesInRowsCache(row); From 204a6c782a685d5702fcb01530885f723dcaf6e0 Mon Sep 17 00:00:00 2001 From: joshq00 Date: Thu, 31 Oct 2013 17:22:08 -0400 Subject: [PATCH 5/7] Add 'active' class to active row --- slick.grid.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/slick.grid.js b/slick.grid.js index 66101c1d9..6b8d65e60 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -3311,6 +3311,10 @@ if (typeof Slick === "undefined") { activeCell = activePosX = activeCell = activePosX = getCellFromNode(activeCellNode[0]); $activeCellNode.addClass("active"); + if (rowsCache[activeRow]) { + $(rowsCache[activeRow].rowNode).addClass('active'); + } + if (options.editable && editMode && isCellPotentiallyEditable(activeRow, activeCell)) { clearTimeout(h_editorLoader); From f3181205c951538e5122247754fd15f43dc90a3f Mon Sep 17 00:00:00 2001 From: joshq00 Date: Thu, 7 Nov 2013 23:29:01 -0500 Subject: [PATCH 6/7] Fix for paging and scrolling --- slick.grid.js | 8442 +++++++++++++++++++++++++------------------------ 1 file changed, 4228 insertions(+), 4214 deletions(-) diff --git a/slick.grid.js b/slick.grid.js index 6b8d65e60..0715a035e 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -19,3872 +19,3881 @@ // make sure required JavaScript modules are loaded if (typeof jQuery === "undefined") { - throw "SlickGrid requires jquery module to be loaded"; + throw "SlickGrid requires jquery module to be loaded"; } if (!jQuery.fn.drag) { - throw "SlickGrid requires jquery.event.drag module to be loaded"; + throw "SlickGrid requires jquery.event.drag module to be loaded"; } if (typeof Slick === "undefined") { - throw "slick.core.js not loaded"; + throw "slick.core.js not loaded"; } (function($) { - // Slick.Grid - $.extend(true, window, { - Slick: { - Grid: SlickGrid - } - }); - - // shared across all grids on the page - var scrollbarDimensions; - var maxSupportedCssHeight; // browser's breaking point - - // //////////////////////////////////////////////////////////////////////////////////////////// - // SlickGrid class implementation (available as Slick.Grid) - - /** - * Creates a new instance of the grid. - * @class SlickGrid - * @constructor - * @param {Node} container Container node to create the grid in. - * @param {Array,Object} data An array of objects for databinding. - * @param {Array} columns An array of column definitions. - * @param {Object} options Grid options. - **/ - function SlickGrid(container, data, columns, options) { - // settings - var defaults = { - explicitInitialization: false, - rowHeight: 25, - defaultColumnWidth: 80, - enableAddRow: false, - leaveSpaceForNewRows: false, - editable: false, - autoEdit: true, - enableCellNavigation: true, - enableColumnReorder: true, - asyncEditorLoading: false, - asyncEditorLoadDelay: 100, - forceFitColumns: false, - enableAsyncPostRender: false, - asyncPostRenderDelay: 50, - autoHeight: false, - editorLock: Slick.GlobalEditorLock, - showHeaderRow: false, - headerRowHeight: 25, - showTopPanel: false, - topPanelHeight: 25, - formatterFactory: null, - editorFactory: null, - cellFlashingCssClass: "flashing", - selectedCellCssClass: "selected", - multiSelect: true, - enableTextSelectionOnCells: false, - dataItemColumnValueExtractor: null, - frozenBottom: false, - frozenColumn: -1, - frozenRow: -1, - fullWidthRows: false, - multiColumnSort: false, - defaultFormatter: defaultFormatter, - forceSyncScrolling: false - }; - - var columnDefaults = { - name: "", - resizable: true, - sortable: false, - minWidth: 30, - rerenderOnResize: false, - headerCssClass: null, - defaultSortAsc: true, - focusable: true, - selectable: true - }; - - // scroller - var th; // virtual height - var h; // real scrollable height - var ph; // page height - var n; // number of pages - var cj; // "jumpiness" coefficient - - var page = 0; // current page - var offset = 0; // current page offset - var vScrollDir = 1; - - // private - var initialized = false; - var $container; - var uid = "slickgrid_" + Math.round(1000000 * Math.random()); - var self = this; - var $focusSink, $focusSink2; - var $headerScroller; - var $headers; - var $headerRow, $headerRowScroller, $headerRowSpacerL, $headerRowSpacerR; - var $topPanelScroller; - var $topPanel; - var $viewport; - var $canvas; - var $style; - var $boundAncestors; - var stylesheet, columnCssRulesL, columnCssRulesR; - var viewportH, viewportW; - var canvasWidth, canvasWidthL, canvasWidthR; - var headersWidth, headersWidthL, headersWidthR; - var viewportHasHScroll, viewportHasVScroll; - var headerColumnWidthDiff = 0, - headerColumnHeightDiff = 0, - // border+padding - cellWidthDiff = 0, - cellHeightDiff = 0; - var absoluteColumnMinWidth; - var numberOfRows = 0; - var hasFrozenRows = false; - var frozenRowsHeight = 0; - var actualFrozenRow = -1; - var paneTopH = 0; - var paneBottomH = 0; - var viewportTopH = 0; - var viewportBottomH = 0; - var topPanelH = 0; - var headerRowH = 0; - - var tabbingDirection = 1; - var $activeCanvasNode; - var $activeViewportNode; - var activePosX; - var activeRow, activeCell; - var activeCellNode = null; - var currentEditor = null; - var serializedEditorValue; - var editController; - - var rowsCache = {}; - var renderedRows = 0; - var numVisibleRows = 0; - var prevScrollTop = 0; - var scrollTop = 0; - var lastRenderedScrollTop = 0; - var lastRenderedScrollLeft = 0; - var prevScrollLeft = 0; - var scrollLeft = 0; - - var selectionModel; - var selectedRows = []; - - var plugins = []; - var cellCssClasses = {}; - - var columnsById = {}; - var sortColumns = []; - var columnPosLeft = []; - var columnPosRight = []; - - // async call handles - var h_editorLoader = null; - var h_render = null; - var h_postrender = null; - var postProcessedRows = {}; - var postProcessToRow = null; - var postProcessFromRow = null; - - // perf counters - var counter_rows_rendered = 0; - var counter_rows_removed = 0; - - var $paneHeaderL; - var $paneHeaderR; - var $paneTopL; - var $paneTopR; - var $paneBottomL; - var $paneBottomR; - - var $headerScrollerL; - var $headerScrollerR; - - var $headerL; - var $headerR; - - var $headerRowScrollerL; - var $headerRowScrollerR; - - var $headerRowL; - var $headerRowR; - - var $topPanelScrollerL; - var $topPanelScrollerR; - - var $topPanelL; - var $topPanelR; - - var $viewportTopL; - var $viewportTopR; - var $viewportBottomL; - var $viewportBottomR; - - var $canvasTopL; - var $canvasTopR; - var $canvasBottomL; - var $canvasBottomR; - - var $viewportScrollContainerX; - var $viewportScrollContainerY; - var $headerScrollContainer; - var $headerRowScrollContainer; - - // //////////////////////////////////////////////////////////////////////////////////////////// - // Initialization - - function init() { - $container = $(container); - if ($container.length < 1) { - throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM."); - } - - // calculate these only once and share between grid instances - maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight(); - scrollbarDimensions = scrollbarDimensions || measureScrollbar(); - - options = $.extend({}, defaults, options); - validateAndEnforceOptions(); - columnDefaults.width = options.defaultColumnWidth; - - columnsById = {}; - for (var i = 0; i < columns.length; i++) { - var m = columns[i] = $.extend({}, columnDefaults, columns[i]); - columnsById[m.id] = i; - if (m.minWidth && m.width < m.minWidth) { - m.width = m.minWidth; - } - if (m.maxWidth && m.width > m.maxWidth) { - m.width = m.maxWidth; - } - } - - // validate loaded JavaScript modules against requested options - if (options.enableColumnReorder && !$.fn.sortable) { - throw new Error("SlickGrid's 'enableColumnReorder = true' option requires jquery-ui.sortable module to be loaded"); - } - - editController = { - "commitCurrentEdit": commitCurrentEdit, - "cancelCurrentEdit": cancelCurrentEdit - }; - - $container - .empty() - .css("overflow", "hidden") - .css("outline", 0) - .addClass(uid) - .addClass("ui-widget"); - - // set up a positioning container if needed - if (!/relative|absolute|fixed/.test($container.css("position"))) { - $container.css("position", "relative"); - } - - $focusSink = $("
").appendTo($container); - - // Containers used for scrolling frozen columns and rows - $paneHeaderL = $("
").appendTo($container); - $paneHeaderR = $("
").appendTo($container); - $paneTopL = $("
").appendTo($container); - $paneTopR = $("
").appendTo($container); - $paneBottomL = $("
").appendTo($container); - $paneBottomR = $("
").appendTo($container); - - // Append the header scroller containers - $headerScrollerL = $("
").appendTo($paneHeaderL); - $headerScrollerR = $("
").appendTo($paneHeaderR); - - // Cache the header scroller containers - $headerScroller = $().add($headerScrollerL).add($headerScrollerR); - - // Append the columnn containers to the headers - $headerL = $("
").appendTo($headerScrollerL); - $headerR = $("
").appendTo($headerScrollerR); - - // Cache the header columns - $headers = $().add($headerL).add($headerR); - - $headerRowScrollerL = $("
").appendTo($paneTopL); - $headerRowScrollerR = $("
").appendTo($paneTopR); - - $headerRowScroller = $().add($headerRowScrollerL).add($headerRowScrollerR); - - $headerRowSpacerL = $("
") - .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") - .appendTo($headerRowScrollerL); - $headerRowSpacerR = $("
") - .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") - .appendTo($headerRowScrollerR); - - - $headerRowL = $("
").appendTo($headerRowScrollerL); - $headerRowR = $("
").appendTo($headerRowScrollerR); - - $headerRow = $().add($headerRowL).add($headerRowR); - - // Append the top panel scroller - $topPanelScrollerL = $("
").appendTo($paneTopL); - $topPanelScrollerR = $("
").appendTo($paneTopR); - - $topPanelScroller = $().add($topPanelScrollerL).add($topPanelScrollerR); - - // Append the top panel - $topPanelL = $("
").appendTo($topPanelScrollerL); - $topPanelR = $("
").appendTo($topPanelScrollerR); - - $topPanel = $().add($topPanelL).add($topPanelR); - - if (!options.showTopPanel) { - $topPanelScroller.hide(); - } - - if (!options.showHeaderRow) { - $headerRowScroller.hide(); - } - - // Append the viewport containers - $viewportTopL = $("
").appendTo($paneTopL); - $viewportTopR = $("
").appendTo($paneTopR); - $viewportBottomL = $("
").appendTo($paneBottomL); - $viewportBottomR = $("
").appendTo($paneBottomR); - - // Cache the viewports - $viewport = $().add($viewportTopL).add($viewportTopR).add($viewportBottomL).add($viewportBottomR); - - // Default the active viewport to the top left - $activeViewportNode = $viewportTopL; - - // Append the canvas containers - $canvasTopL = $("
").appendTo($viewportTopL); - $canvasTopR = $("
").appendTo($viewportTopR); - $canvasBottomL = $("
").appendTo($viewportBottomL); - $canvasBottomR = $("
").appendTo($viewportBottomR); - - // Cache the canvases - $canvas = $().add($canvasTopL).add($canvasTopR).add($canvasBottomL).add($canvasBottomR); - - // Default the active canvas to the top left - $activeCanvasNode = $canvasTopL; - - $focusSink2 = $focusSink.clone().appendTo($container); - - if (!options.explicitInitialization) { - finishInitialization(); - } - } - - function finishInitialization() { - if (!initialized) { - initialized = true; - - getViewportWidth(); - getViewportHeight(); - - // header columns and cells may have different padding/border - // skewing width calculations (box-sizing, hello?) - // calculate the diff so we can set consistent sizes - measureCellPaddingAndBorder(); - - // for usability reasons, all text selection in SlickGrid is - // disabled with the exception of input and textarea elements (selection - // must be enabled there so that editors work as expected); note that - // selection in grid cells (grid body) is already unavailable in - // all browsers except IE - disableSelection($headers); // disable all text selection in header (including input and textarea) - - if (!options.enableTextSelectionOnCells) { - // disable text selection in grid cells except in input and textarea elements - // (this is IE-specific, because selectstart event will only fire in IE) - $viewport.bind("selectstart.ui", function(event) { - return $(event.target).is("input,textarea"); - }); - } - - setFrozenOptions(); - setPaneVisibility(); - setScroller(); - setOverflow(); - - updateColumnCaches(); - createColumnHeaders(); - setupColumnSort(); - createCssRules(); - resizeCanvas(); - bindAncestorScrollEvents(); - - $container - .bind("resize.slickgrid", resizeCanvas); - $viewport - .bind("scroll", handleScroll); - if ( jQuery.fn.mousewheel && ( options.frozenColumn > -1 || hasFrozenRows ) ) { - $viewport - .bind("mousewheel", handleMouseWheel); - } - $headerScroller - .bind("contextmenu", handleHeaderContextMenu) - .bind("click", handleHeaderClick) - .delegate(".slick-header-column", "mouseenter", handleHeaderMouseEnter) - .delegate(".slick-header-column", "mouseleave", handleHeaderMouseLeave); - $headerRowScroller - .bind("scroll", handleHeaderRowScroll); - $focusSink.add($focusSink2) - .bind("keydown", handleKeyDown); - $canvas - .bind("keydown", handleKeyDown) - .bind("click", handleClick) - .bind("dblclick", handleDblClick) - .bind("contextmenu", handleContextMenu) - .bind("draginit", handleDragInit) - .bind("dragstart", {distance: 3}, handleDragStart) - .bind("drag", handleDrag) - .bind("dragend", handleDragEnd) - .delegate(".slick-cell", "mouseenter", handleMouseEnter) - .delegate(".slick-cell", "mouseleave", handleMouseLeave); - } - } - - function registerPlugin(plugin) { - plugins.unshift(plugin); - plugin.init(self); - } - - function unregisterPlugin(plugin) { - for (var i = plugins.length; i >= 0; i--) { - if (plugins[i] === plugin) { - if (plugins[i].destroy) { - plugins[i].destroy(); - } - plugins.splice(i, 1); - break; - } - } - } - - function setSelectionModel(model) { - if (selectionModel) { - selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); - if (selectionModel.destroy) { - selectionModel.destroy(); - } - } - - selectionModel = model; - if (selectionModel) { - selectionModel.init(self); - selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); - } - } - - function getSelectionModel() { - return selectionModel; - } - - function getCanvasNode() { - return $canvas[0]; - } - - function getActiveCanvasNode( element ) { - setActiveCanvasNode( element ); - - return $activeCanvasNode[0]; - } - - function getCanvases() { - return $canvas; - } - - function setActiveCanvasNode( element ) { - if ( element ) { - $activeCanvasNode = $( element.target ).closest( '.grid-canvas' ); - } - } - - function getViewportNode() { - return $viewport[0]; - } - - function getActiveViewportNode( element ) { - setActiveViewPortNode( element ); - - return $activeViewportNode[0]; - } - - function setActiveViewportNode( element ) { - if ( element ) { - $activeViewportNode = $( element.target ).closest( '.slick-viewport' ); - } - } - - function measureScrollbar() { - var $c = $("
").appendTo("body"); - var dim = { - width: $c.width() - $c[0].clientWidth, - height: $c.height() - $c[0].clientHeight - }; - $c.remove(); - return dim; - } - - function getHeadersWidth() { - headersWidth = headersWidthL = headersWidthR = 0; - - for ( var i = 0, ii = columns.length; i < ii; i++ ) { - var width = columns[ i ].width; - - if ( ( options.frozenColumn ) > -1 && ( i > options.frozenColumn ) ) { - headersWidthR += width; - } else { - headersWidthL += width; - } - } - - if ( options.frozenColumn > -1 ) { - headersWidthL = headersWidthL + 1000; - - headersWidthR = Math.max( headersWidthR, viewportW ) + headersWidthL; - headersWidthR += scrollbarDimensions.width; - } else { - headersWidthL += scrollbarDimensions.width; - headersWidthL = Math.max( headersWidthL, viewportW ) + 1000; - } - - headersWidth = headersWidthL + headersWidthR; - } - - function getCanvasWidth() { - var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW; - - var i = columns.length; - - canvasWidthL = canvasWidthR = 0; + // Slick.Grid + $.extend(true, window, { + Slick: { + Grid: SlickGrid + } + }); + + // shared across all grids on the page + var scrollbarDimensions; + var maxSupportedCssHeight; // browser's breaking point + + // //////////////////////////////////////////////////////////////////////////////////////////// + // SlickGrid class implementation (available as Slick.Grid) + + /** + * Creates a new instance of the grid. + * @class SlickGrid + * @constructor + * @param {Node} container Container node to create the grid in. + * @param {Array,Object} data An array of objects for databinding. + * @param {Array} columns An array of column definitions. + * @param {Object} options Grid options. + **/ + function SlickGrid(container, data, columns, options) { + // settings + var defaults = { + explicitInitialization: false, + rowHeight: 25, + defaultColumnWidth: 80, + enableAddRow: false, + leaveSpaceForNewRows: false, + editable: false, + autoEdit: true, + enableCellNavigation: true, + enableColumnReorder: true, + asyncEditorLoading: false, + asyncEditorLoadDelay: 100, + forceFitColumns: false, + enableAsyncPostRender: false, + asyncPostRenderDelay: 50, + autoHeight: false, + editorLock: Slick.GlobalEditorLock, + showHeaderRow: false, + headerRowHeight: 25, + showTopPanel: false, + topPanelHeight: 25, + formatterFactory: null, + editorFactory: null, + cellFlashingCssClass: "flashing", + selectedCellCssClass: "selected", + multiSelect: true, + enableTextSelectionOnCells: false, + dataItemColumnValueExtractor: null, + frozenBottom: false, + frozenColumn: -1, + frozenRow: -1, + fullWidthRows: false, + multiColumnSort: false, + defaultFormatter: defaultFormatter, + forceSyncScrolling: false + }; + + var columnDefaults = { + name: "", + resizable: true, + sortable: false, + minWidth: 30, + rerenderOnResize: false, + headerCssClass: null, + defaultSortAsc: true, + focusable: true, + selectable: true + }; + + // scroller + var th; // virtual height + var h; // real scrollable height + var ph; // page height + var n; // number of pages + var cj; // "jumpiness" coefficient + + var page = 0; // current page + var offset = 0; // current page offset + var vScrollDir = 1; + + // private + var initialized = false; + var $container; + var uid = "slickgrid_" + Math.round(1000000 * Math.random()); + var self = this; + var $focusSink, $focusSink2; + var $headerScroller; + var $headers; + var $headerRow, $headerRowScroller, $headerRowSpacerL, $headerRowSpacerR; + var $topPanelScroller; + var $topPanel; + var $viewport; + var $canvas; + var $style; + var $boundAncestors; + var stylesheet, columnCssRulesL, columnCssRulesR; + var viewportH, viewportW; + var canvasWidth, canvasWidthL, canvasWidthR; + var headersWidth, headersWidthL, headersWidthR; + var viewportHasHScroll, viewportHasVScroll; + var headerColumnWidthDiff = 0, + headerColumnHeightDiff = 0, + // border+padding + cellWidthDiff = 0, + cellHeightDiff = 0; + var absoluteColumnMinWidth; + var numberOfRows = 0; + var hasFrozenRows = false; + var frozenRowsHeight = 0; + var actualFrozenRow = -1; + var paneTopH = 0; + var paneBottomH = 0; + var viewportTopH = 0; + var viewportBottomH = 0; + var topPanelH = 0; + var headerRowH = 0; + + var tabbingDirection = 1; + var $activeCanvasNode; + var $activeViewportNode; + var activePosX; + var activeRow, activeCell; + var activeCellNode = null; + var currentEditor = null; + var serializedEditorValue; + var editController; + + var rowsCache = {}; + var renderedRows = 0; + var numVisibleRows = 0; + var prevScrollTop = 0; + var scrollTop = 0; + var lastRenderedScrollTop = 0; + var lastRenderedScrollLeft = 0; + var prevScrollLeft = 0; + var scrollLeft = 0; + + var selectionModel; + var selectedRows = []; + + var plugins = []; + var cellCssClasses = {}; + + var columnsById = {}; + var sortColumns = []; + var columnPosLeft = []; + var columnPosRight = []; + + // async call handles + var h_editorLoader = null; + var h_render = null; + var h_postrender = null; + var postProcessedRows = {}; + var postProcessToRow = null; + var postProcessFromRow = null; + + // perf counters + var counter_rows_rendered = 0; + var counter_rows_removed = 0; + + var $paneHeaderL; + var $paneHeaderR; + var $paneTopL; + var $paneTopR; + var $paneBottomL; + var $paneBottomR; + + var $headerScrollerL; + var $headerScrollerR; + + var $headerL; + var $headerR; + + var $headerRowScrollerL; + var $headerRowScrollerR; + + var $headerRowL; + var $headerRowR; + + var $topPanelScrollerL; + var $topPanelScrollerR; + + var $topPanelL; + var $topPanelR; + + var $viewportTopL; + var $viewportTopR; + var $viewportBottomL; + var $viewportBottomR; + + var $canvasTopL; + var $canvasTopR; + var $canvasBottomL; + var $canvasBottomR; + + var $viewportScrollContainerX; + var $viewportScrollContainerY; + var $headerScrollContainer; + var $headerRowScrollContainer; + + // //////////////////////////////////////////////////////////////////////////////////////////// + // Initialization + + function init() { + $container = $(container); + if ($container.length < 1) { + throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM."); + } + + // calculate these only once and share between grid instances + maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight(); + scrollbarDimensions = scrollbarDimensions || measureScrollbar(); + + options = $.extend({}, defaults, options); + validateAndEnforceOptions(); + columnDefaults.width = options.defaultColumnWidth; + + columnsById = {}; + for (var i = 0; i < columns.length; i++) { + var m = columns[i] = $.extend({}, columnDefaults, columns[i]); + columnsById[m.id] = i; + if (m.minWidth && m.width < m.minWidth) { + m.width = m.minWidth; + } + if (m.maxWidth && m.width > m.maxWidth) { + m.width = m.maxWidth; + } + } + + // validate loaded JavaScript modules against requested options + if (options.enableColumnReorder && !$.fn.sortable) { + throw new Error("SlickGrid's 'enableColumnReorder = true' option requires jquery-ui.sortable module to be loaded"); + } + + editController = { + "commitCurrentEdit": commitCurrentEdit, + "cancelCurrentEdit": cancelCurrentEdit + }; + + $container + .empty() + .css("overflow", "hidden") + .css("outline", 0) + .addClass(uid) + .addClass("ui-widget"); + + // set up a positioning container if needed + if (!/relative|absolute|fixed/.test($container.css("position"))) { + $container.css("position", "relative"); + } + + $focusSink = $("
").appendTo($container); + + // Containers used for scrolling frozen columns and rows + $paneHeaderL = $("
").appendTo($container); + $paneHeaderR = $("
").appendTo($container); + $paneTopL = $("
").appendTo($container); + $paneTopR = $("
").appendTo($container); + $paneBottomL = $("
").appendTo($container); + $paneBottomR = $("
").appendTo($container); + + // Append the header scroller containers + $headerScrollerL = $("
").appendTo($paneHeaderL); + $headerScrollerR = $("
").appendTo($paneHeaderR); + + // Cache the header scroller containers + $headerScroller = $().add($headerScrollerL).add($headerScrollerR); + + // Append the columnn containers to the headers + $headerL = $("
").appendTo($headerScrollerL); + $headerR = $("
").appendTo($headerScrollerR); + + // Cache the header columns + $headers = $().add($headerL).add($headerR); + + $headerRowScrollerL = $("
").appendTo($paneTopL); + $headerRowScrollerR = $("
").appendTo($paneTopR); + + $headerRowScroller = $().add($headerRowScrollerL).add($headerRowScrollerR); + + $headerRowSpacerL = $("
") + .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") + .appendTo($headerRowScrollerL); + $headerRowSpacerR = $("
") + .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") + .appendTo($headerRowScrollerR); + + + $headerRowL = $("
").appendTo($headerRowScrollerL); + $headerRowR = $("
").appendTo($headerRowScrollerR); + + $headerRow = $().add($headerRowL).add($headerRowR); + + // Append the top panel scroller + $topPanelScrollerL = $("
").appendTo($paneTopL); + $topPanelScrollerR = $("
").appendTo($paneTopR); + + $topPanelScroller = $().add($topPanelScrollerL).add($topPanelScrollerR); + + // Append the top panel + $topPanelL = $("
").appendTo($topPanelScrollerL); + $topPanelR = $("
").appendTo($topPanelScrollerR); + + $topPanel = $().add($topPanelL).add($topPanelR); + + if (!options.showTopPanel) { + $topPanelScroller.hide(); + } + + if (!options.showHeaderRow) { + $headerRowScroller.hide(); + } + + // Append the viewport containers + $viewportTopL = $("
").appendTo($paneTopL); + $viewportTopR = $("
").appendTo($paneTopR); + $viewportBottomL = $("
").appendTo($paneBottomL); + $viewportBottomR = $("
").appendTo($paneBottomR); + + // Cache the viewports + $viewport = $().add($viewportTopL).add($viewportTopR).add($viewportBottomL).add($viewportBottomR); + + // Default the active viewport to the top left + $activeViewportNode = $viewportTopL; + + // Append the canvas containers + $canvasTopL = $("
").appendTo($viewportTopL); + $canvasTopR = $("
").appendTo($viewportTopR); + $canvasBottomL = $("
").appendTo($viewportBottomL); + $canvasBottomR = $("
").appendTo($viewportBottomR); + + // Cache the canvases + $canvas = $().add($canvasTopL).add($canvasTopR).add($canvasBottomL).add($canvasBottomR); + + // Default the active canvas to the top left + $activeCanvasNode = $canvasTopL; + + $focusSink2 = $focusSink.clone().appendTo($container); + + if (!options.explicitInitialization) { + finishInitialization(); + } + } + + function finishInitialization() { + if (!initialized) { + initialized = true; + + getViewportWidth(); + getViewportHeight(); + + // header columns and cells may have different padding/border + // skewing width calculations (box-sizing, hello?) + // calculate the diff so we can set consistent sizes + measureCellPaddingAndBorder(); + + // for usability reasons, all text selection in SlickGrid is + // disabled with the exception of input and textarea elements (selection + // must be enabled there so that editors work as expected); note that + // selection in grid cells (grid body) is already unavailable in + // all browsers except IE + disableSelection($headers); // disable all text selection in header (including input and textarea) + + if (!options.enableTextSelectionOnCells) { + // disable text selection in grid cells except in input and textarea elements + // (this is IE-specific, because selectstart event will only fire in IE) + $viewport.bind("selectstart.ui", function(event) { + return $(event.target).is("input,textarea"); + }); + } + + setFrozenOptions(); + setPaneVisibility(); + setScroller(); + setOverflow(); + + updateColumnCaches(); + createColumnHeaders(); + setupColumnSort(); + createCssRules(); + resizeCanvas(); + bindAncestorScrollEvents(); + + $container + .bind("resize.slickgrid", resizeCanvas); + $viewport + .bind("scroll", handleScroll); + if ( jQuery.fn.mousewheel && ( options.frozenColumn > -1 || hasFrozenRows ) ) { + $viewport + .bind("mousewheel", handleMouseWheel); + } + $headerScroller + .bind("contextmenu", handleHeaderContextMenu) + .bind("click", handleHeaderClick) + .delegate(".slick-header-column", "mouseenter", handleHeaderMouseEnter) + .delegate(".slick-header-column", "mouseleave", handleHeaderMouseLeave); + $headerRowScroller + .bind("scroll", handleHeaderRowScroll); + $focusSink.add($focusSink2) + .bind("keydown", handleKeyDown); + $canvas + .bind("keydown", handleKeyDown) + .bind("click", handleClick) + .bind("dblclick", handleDblClick) + .bind("contextmenu", handleContextMenu) + .bind("draginit", handleDragInit) + .bind("dragstart", {distance: 3}, handleDragStart) + .bind("drag", handleDrag) + .bind("dragend", handleDragEnd) + .delegate(".slick-cell", "mouseenter", handleMouseEnter) + .delegate(".slick-cell", "mouseleave", handleMouseLeave); + } + } + + function registerPlugin(plugin) { + plugins.unshift(plugin); + plugin.init(self); + } + + function unregisterPlugin(plugin) { + for (var i = plugins.length; i >= 0; i--) { + if (plugins[i] === plugin) { + if (plugins[i].destroy) { + plugins[i].destroy(); + } + plugins.splice(i, 1); + break; + } + } + } + + function setSelectionModel(model) { + if (selectionModel) { + selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); + if (selectionModel.destroy) { + selectionModel.destroy(); + } + } + + selectionModel = model; + if (selectionModel) { + selectionModel.init(self); + selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); + } + } + + function getSelectionModel() { + return selectionModel; + } + + function getCanvasNode() { + return $canvas[0]; + } + + function getActiveCanvasNode( element ) { + setActiveCanvasNode( element ); + + return $activeCanvasNode[0]; + } + + function getCanvases() { + return $canvas; + } + + function setActiveCanvasNode( element ) { + if ( element ) { + $activeCanvasNode = $( element.target ).closest( '.grid-canvas' ); + } + } + + function getViewportNode() { + return $viewport[0]; + } + + function getActiveViewportNode( element ) { + setActiveViewPortNode( element ); + + return $activeViewportNode[0]; + } + + function setActiveViewportNode( element ) { + if ( element ) { + $activeViewportNode = $( element.target ).closest( '.slick-viewport' ); + } + } + + function getContainerNode() { + return $container.get(0); + } + + function measureScrollbar() { + var $c = $("
").appendTo("body"); + var dim = { + width: $c.width() - $c[0].clientWidth, + height: $c.height() - $c[0].clientHeight + }; + $c.remove(); + return dim; + } + + function getHeadersWidth() { + headersWidth = headersWidthL = headersWidthR = 0; + + for ( var i = 0, ii = columns.length; i < ii; i++ ) { + var width = columns[ i ].width; + + if ( ( options.frozenColumn ) > -1 && ( i > options.frozenColumn ) ) { + headersWidthR += width; + } else { + headersWidthL += width; + } + } + + if ( options.frozenColumn > -1 ) { + headersWidthL = headersWidthL + 1000; + + headersWidthR = Math.max( headersWidthR, viewportW ) + headersWidthL; + headersWidthR += scrollbarDimensions.width; + } else { + headersWidthL += scrollbarDimensions.width; + headersWidthL = Math.max( headersWidthL, viewportW ) + 1000; + } + + headersWidth = headersWidthL + headersWidthR; + } + + function getCanvasWidth() { + var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW; + + var i = columns.length; + + canvasWidthL = canvasWidthR = 0; + + while (i--) { + if ((options.frozenColumn > -1) && (i > options.frozenColumn)) { + canvasWidthR += columns[i].width; + } else { + canvasWidthL += columns[i].width; + } + } + + var totalRowWidth = canvasWidthL + canvasWidthR; + + return options.fullWidthRows ? Math.max(totalRowWidth, availableWidth) : totalRowWidth; + } + + function updateCanvasWidth(forceColumnWidthsUpdate) { + var oldCanvasWidth = canvasWidth; + var oldCanvasWidthL = canvasWidthL; + var oldCanvasWidthR = canvasWidthR; + var widthChanged; + canvasWidth = getCanvasWidth(); + + widthChanged = canvasWidth !== oldCanvasWidth || canvasWidthL !== oldCanvasWidthL || canvasWidthR !== oldCanvasWidthR; + + if (widthChanged || options.frozenColumn > -1 || hasFrozenRows ) { + $canvasTopL.width(canvasWidthL); + + getHeadersWidth(); + + $headerL.width(headersWidthL); + $headerR.width(headersWidthR); + + if ( options.frozenColumn > -1 ) { + $canvasTopR.width(canvasWidthR); + + $paneHeaderL.width(canvasWidthL); + $paneHeaderR.css('left', canvasWidthL); + + $paneTopL.width(canvasWidthL); + $paneTopR.css('left', canvasWidthL); + + $headerRowScrollerL.width(canvasWidthL); + $headerRowScrollerR.width(viewportW - canvasWidthL); + + $headerRowL.width(canvasWidthL); + $headerRowR.width(canvasWidthR); + + $viewportTopL.width(canvasWidthL); + $viewportTopR.width(viewportW - canvasWidthL); + + if ( hasFrozenRows ) { + $paneBottomL.width(canvasWidthL); + $paneBottomR.css('left', canvasWidthL); + + $viewportBottomL.width(canvasWidthL); + $viewportBottomR.width(viewportW - canvasWidthL); + + $canvasBottomL.width(canvasWidthL); + $canvasBottomR.width(canvasWidthR); + } + } else { + $paneHeaderL.width( '100%' ); + + $paneTopL.width( '100%' ); + + $headerRowScrollerL.width( '100%' ); + + $headerRowL.width( canvasWidth ); + + $viewportTopL.width( '100%' ); + + if ( hasFrozenRows ) { + $viewportBottomL.width( '100%' ); + $canvasBottomL.width(canvasWidthL); + } + } + + viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width); + } + + $headerRowSpacerL.width(canvasWidth + (viewportHasVScroll ? scrollbarDimensions.width : 0)); + $headerRowSpacerR.width(canvasWidth + (viewportHasVScroll ? scrollbarDimensions.width : 0)); + + if (widthChanged || forceColumnWidthsUpdate) { + applyColumnWidths(); + } + } + + function disableSelection($target) { + if ($target && $target.jquery) { + $target.attr("unselectable", "on").css("MozUserSelect", "none").bind("selectstart.ui", function() { + return false; + }); // from jquery:ui.core.js 1.7.2 + } + } + + function getMaxSupportedCssHeight() { + var supportedHeight = 1000000; + // FF reports the height back but still renders blank after ~6M px + var testUpTo = navigator.userAgent.toLowerCase().match(/firefox/) ? 6000000 : 1000000000; + var div = $("
").appendTo(document.body); + + while (true) { + var test = supportedHeight * 2; + div.css("height", test); + if (test > testUpTo || div.height() !== test) { + break; + } else { + supportedHeight = test; + } + } + + div.remove(); + return supportedHeight; + } + + // TODO: this is static. need to handle page mutation. + function bindAncestorScrollEvents() { + var elem = (hasFrozenRows && !options.frozenBottom) ? $canvasBottomL[0] : $canvasTopL[0]; + while ((elem = elem.parentNode) != document.body && elem != null) { + // bind to scroll containers only + if (elem == $viewportTopL[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight) { + var $elem = $(elem); + if (!$boundAncestors) { + $boundAncestors = $elem; + } else { + $boundAncestors = $boundAncestors.add($elem); + } + $elem.bind("scroll." + uid, handleActiveCellPositionChange); + } + } + } + + function unbindAncestorScrollEvents() { + if (!$boundAncestors) { + return; + } + $boundAncestors.unbind("scroll." + uid); + $boundAncestors = null; + } + + function updateColumnHeader(columnId, title, toolTip) { + if (!initialized) { + return; + } + var idx = getColumnIndex(columnId); + if (idx == null) { + return; + } + + var columnDef = columns[idx]; + var $header = $headers.children().eq(idx); + if ($header) { + if (title !== undefined) { + columns[idx].name = title; + } + if (toolTip !== undefined) { + columns[idx].toolTip = toolTip; + } + + trigger(self.onBeforeHeaderCellDestroy, { + "node": $header[0], + "column": columnDef + }); + + $header.attr("title", toolTip || "").children().eq(0).html(title); + + trigger(self.onHeaderCellRendered, { + "node": $header[0], + "column": columnDef + }); + } + } + + function getHeaderRow() { + return (options.frozenColumn > -1) ? $headerRow : $headerRow[0]; + } + + function getHeaderRowColumn(columnId) { + var idx = getColumnIndex(columnId); + + var $headerRowTarget; + + if (options.frozenColumn > -1) { + if (idx <= options.frozenColumn) { + $headerRowTarget = $headerRowL; + } else { + $headerRowTarget = $headerRowR; + + idx -= options.frozenColumn + 1; + } + } else { + $headerRowTarget = $headerRowL; + } + + var $header = $headerRowTarget.children().eq(idx); + return $header && $header[0]; + } + + function createColumnHeaders() { + function onMouseEnter() { + $(this).addClass("ui-state-hover"); + } + + function onMouseLeave() { + $(this).removeClass("ui-state-hover"); + } + + $headers.find(".slick-header-column") + .each(function() { + var columnDef = $(this).data("column"); + if (columnDef) { + trigger(self.onBeforeHeaderCellDestroy, { + "node": this, + "column": columnDef + }); + } + }); + + $headerL.empty(); + $headerR.empty(); + + getHeadersWidth(); + + $headerL.width(headersWidthL); + $headerR.width(headersWidthR); + + $headerRowL.empty(); + $headerRowR.empty(); + + $headerRow.find(".slick-headerrow-column") + .each(function() { + var columnDef = $(this).data("column"); + if (columnDef) { + trigger(self.onBeforeHeaderRowCellDestroy, { + "node": this, + "column": columnDef + }); + } + }); + + for (var i = 0; i < columns.length; i++) { + var m = columns[i]; + + var $headerTarget = (options.frozenColumn > -1) ? ((i <= options.frozenColumn) ? $headerL : $headerR) : $headerL; + var $headerRowTarget = (options.frozenColumn > -1) ? ((i <= options.frozenColumn) ? $headerRowL : $headerRowR) : $headerRowL; + + var header = $("
").html("" + m.name + "").width(m.width - headerColumnWidthDiff).attr("title", m.toolTip || "").data("column", m).addClass(m.headerCssClass || "").appendTo($headerTarget); + + if (options.enableColumnReorder || m.sortable) { + header + .on('mouseenter', onMouseEnter) + .on('mouseleave', onMouseLeave); + } + + if (m.sortable) { + header.addClass("slick-header-sortable"); + header.append(""); + } + + trigger(self.onHeaderCellRendered, { + "node": header[0], + "column": m + }); + + if (options.showHeaderRow) { + var headerRowCell = $("
") + .data("column", m) + .appendTo($headerRowTarget); + + trigger(self.onHeaderRowCellRendered, { + "node": headerRowCell[0], + "column": m + }); + } + } + + setSortColumns(sortColumns); + setupColumnResize(); + if (options.enableColumnReorder) { + setupColumnReorder(); + } + } + + function setupColumnSort() { + $headers.click(function(e) { + // temporary workaround for a bug in jQuery 1.7.1 + // (http://bugs.jquery.com/ticket/11328) + e.metaKey = e.metaKey || e.ctrlKey; + + if ($(e.target).hasClass("slick-resizable-handle")) { + return; + } + + var $col = $(e.target).closest(".slick-header-column"); + if (!$col.length) { + return; + } + + var column = $col.data("column"); + if (column.sortable) { + if (!getEditorLock().commitCurrentEdit()) { + return; + } + + var sortOpts = null; + var i = 0; + for (; i < sortColumns.length; i++) { + if (sortColumns[i].columnId == column.id) { + sortOpts = sortColumns[i]; + sortOpts.sortAsc = !sortOpts.sortAsc; + break; + } + } + + if (e.metaKey && options.multiColumnSort) { + if (sortOpts) { + sortColumns.splice(i, 1); + } + } else { + if ((!e.shiftKey && !e.metaKey) || !options.multiColumnSort) { + sortColumns = []; + } + + if (!sortOpts) { + sortOpts = { + columnId: column.id, + sortAsc: true + }; + sortColumns.push(sortOpts); + } else if (sortColumns.length == 0) { + sortColumns.push(sortOpts); + } + } + + setSortColumns(sortColumns); + + if (!options.multiColumnSort) { + trigger(self.onSort, { + multiColumnSort: false, + sortCol: column, + sortAsc: sortOpts.sortAsc + }, e); + } else { + trigger( + self.onSort, { + multiColumnSort: true, + sortCols: $.map( + sortColumns, function(col) { + return { + sortCol: columns[getColumnIndex(col.columnId)], + sortAsc: col.sortAsc + }; + }) + }, e); + } + } + }); + } + + function setupColumnReorder() { + $headers.filter(":ui-sortable").sortable("destroy"); + var columnScrollTimer = null; + + function scrollColumnsRight() { + $viewportScrollContainerX[0].scrollLeft = $viewportScrollContainerX[0].scrollLeft + 10; + } + + function scrollColumnsLeft() { + $viewportScrollContainerX[0].scrollLeft = $viewportScrollContainerX[0].scrollLeft - 10; + } + + $headers.sortable({ + containment: "parent", + distance: 3, + axis: "x", + cursor: "default", + tolerance: "intersection", + helper: "clone", + placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", + forcePlaceholderSize: true, + start: function(e, ui) { + $(ui.helper).addClass("slick-header-column-active"); + }, + beforeStop: function(e, ui) { + $(ui.helper).removeClass("slick-header-column-active"); + }, + sort: function(e, ui) { + if (e.originalEvent.pageX > $container[0].clientWidth) { + if (!(columnScrollTimer)) { + columnScrollTimer = setInterval( + scrollColumnsRight, 100); + } + } else if (e.originalEvent.pageX < $viewportScrollContainerX.offset().left) { + if (!(columnScrollTimer)) { + columnScrollTimer = setInterval( + scrollColumnsLeft, 100); + } + } else { + clearInterval(columnScrollTimer); + columnScrollTimer = null; + } + }, + stop: function(e) { + clearInterval(columnScrollTimer); + columnScrollTimer = null; + + if (!getEditorLock().commitCurrentEdit()) { + $(this).sortable("cancel"); + return; + } + + var reorderedIds = $headerL.sortable("toArray"); + reorderedIds = reorderedIds.concat($headerR.sortable("toArray")); + + var reorderedColumns = []; + for (var i = 0; i < reorderedIds.length; i++) { + reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid, ""))]); + } + setColumns(reorderedColumns); + + trigger(self.onColumnsReordered, {}); + e.stopPropagation(); + setupColumnResize(); + } + }); + } + + function setupColumnResize() { + var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable; + columnElements = $headers.children(); + columnElements.find(".slick-resizable-handle").remove(); + columnElements.each(function(i, e) { + if (columns[i].resizable) { + if (firstResizable === undefined) { + firstResizable = i; + } + lastResizable = i; + } + }); + if (firstResizable === undefined) { + return; + } + columnElements.each(function(i, e) { + if (i < firstResizable || (options.forceFitColumns && i >= lastResizable)) { + return; + } + $col = $(e); + $("
") + .appendTo(e) + .bind("dragstart", function(e, dd) { + if (!getEditorLock().commitCurrentEdit()) { + return false; + } + pageX = e.pageX; + $(this).parent().addClass("slick-header-column-active"); + var shrinkLeewayOnRight = null, + stretchLeewayOnRight = null; + // lock each column's width option to current width + columnElements.each(function(i, e) { + columns[i].previousWidth = $(e).outerWidth(); + }); + if (options.forceFitColumns) { + shrinkLeewayOnRight = 0; + stretchLeewayOnRight = 0; + // colums on right affect maxPageX/minPageX + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + if (stretchLeewayOnRight !== null) { + if (c.maxWidth) { + stretchLeewayOnRight += c.maxWidth - c.previousWidth; + } else { + stretchLeewayOnRight = null; + } + } + shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); + } + } + } + var shrinkLeewayOnLeft = 0, + stretchLeewayOnLeft = 0; + for (j = 0; j <= i; j++) { + // columns on left only affect minPageX + c = columns[j]; + if (c.resizable) { + if (stretchLeewayOnLeft !== null) { + if (c.maxWidth) { + stretchLeewayOnLeft += c.maxWidth - c.previousWidth; + } else { + stretchLeewayOnLeft = null; + } + } + shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); + } + } + if (shrinkLeewayOnRight === null) { + shrinkLeewayOnRight = 100000; + } + if (shrinkLeewayOnLeft === null) { + shrinkLeewayOnLeft = 100000; + } + if (stretchLeewayOnRight === null) { + stretchLeewayOnRight = 100000; + } + if (stretchLeewayOnLeft === null) { + stretchLeewayOnLeft = 100000; + } + maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft); + minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight); + }).bind("drag", function(e, dd) { + var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, + x; + + if (d < 0) { // shrink column + x = d; + + var newCanvasWidthL = 0, newCanvasWidthR = 0; + + for (j = i; j >= 0; j--) { + c = columns[j]; + if (c.resizable) { + actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); + if (x && c.previousWidth + x < actualMinWidth) { + x += c.previousWidth - actualMinWidth; + c.width = actualMinWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + + for ( k = 0; k <= i; k++ ) { + c = columns[k]; + + if ((options.frozenColumn > -1) && (k > options.frozenColumn)) { + newCanvasWidthR += c.width; + } else { + newCanvasWidthL += c.width; + } + } + + if (options.forceFitColumns) { + x = -d; + + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { + x -= c.maxWidth - c.previousWidth; + c.width = c.maxWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + + if ((options.frozenColumn > -1) && (j > options.frozenColumn)) { + newCanvasWidthR += c.width; + } else { + newCanvasWidthL += c.width; + } + } + } + } else { + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + + if ((options.frozenColumn > -1) && (j > options.frozenColumn)) { + newCanvasWidthR += c.width; + } else { + newCanvasWidthL += c.width; + } + } + } + } else { // stretch column + x = d; + + var newCanvasWidthL = 0, newCanvasWidthR = 0; + + for (j = i; j >= 0; j--) { + c = columns[j]; + if (c.resizable) { + if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { + x -= c.maxWidth - c.previousWidth; + c.width = c.maxWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + + for ( k = 0; k <= i; k++ ) { + c = columns[k]; + + if ((options.frozenColumn > -1) && (k > options.frozenColumn)) { + newCanvasWidthR += c.width; + } else { + newCanvasWidthL += c.width; + } + } + + if (options.forceFitColumns) { + x = -d; + + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); + if (x && c.previousWidth + x < actualMinWidth) { + x += c.previousWidth - actualMinWidth; + c.width = actualMinWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + + if ((options.frozenColumn > -1) && (j > options.frozenColumn)) { + newCanvasWidthR += c.width; + } else { + newCanvasWidthL += c.width; + } + } + } + } else { + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + + if ((options.frozenColumn > -1) && (j > options.frozenColumn)) { + newCanvasWidthR += c.width; + } else { + newCanvasWidthL += c.width; + } + } + } + } + + if ( options.frozenColumn > -1 && newCanvasWidthL != canvasWidthL ) { + $headerL.width( newCanvasWidthL + 1000 ); + $paneHeaderR.css( 'left', newCanvasWidthL ); + } + + applyColumnHeaderWidths(); + if (options.syncColumnCellResize) { + updateCanvasWidth(); + applyColumnWidths(); + } + }).bind("dragend", function(e, dd) { + var newWidth; + $(this).parent().removeClass("slick-header-column-active"); + for (j = 0; j < columnElements.length; j++) { + c = columns[j]; + newWidth = $(columnElements[j]).outerWidth(); + + if (c.previousWidth !== newWidth && c.rerenderOnResize) { + invalidateAllRows(); + } + } + + updateCanvasWidth(true); + render(); + trigger(self.onColumnsResized, {}); + }); + }); + } + + function getVBoxDelta($el) { + var p = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + var delta = 0; + $.each(p, function(n, val) { + delta += parseFloat($el.css(val)) || 0; + }); + return delta; + } + + function setFrozenOptions() { + options.frozenColumn = ( options.frozenColumn >= 0 + && options.frozenColumn < columns.length + ) + ? parseInt(options.frozenColumn) + : -1; + + options.frozenRow = ( options.frozenRow >= 0 + && options.frozenRow < numVisibleRows + ) + ? parseInt(options.frozenRow) + : -1; + + if ( options.frozenRow > -1 ) { + hasFrozenRows = true; + frozenRowsHeight = ( options.frozenRow ) * options.rowHeight; + + var dataLength = getDataLength() || this.data.length; + + actualFrozenRow = ( options.frozenBottom ) + ? ( dataLength - options.frozenRow ) + : options.frozenRow; + } else { + hasFrozenRows = false; + } + } + + function setPaneVisibility() { + if (options.frozenColumn > -1) { + $paneHeaderR.show(); + $paneTopR.show(); + + if ( hasFrozenRows ) { + $paneBottomL.show(); + $paneBottomR.show(); + } else { + $paneBottomR.hide(); + $paneBottomL.hide(); + } + } else { + $paneHeaderR.hide(); + $paneTopR.hide(); + $paneBottomR.hide(); + + if ( hasFrozenRows ) { + $paneBottomL.show(); + } else { + $paneBottomR.hide(); + $paneBottomL.hide(); + } + } + } + + function setOverflow() { + $viewportTopL.css({ + 'overflow-x': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'hidden' : 'scroll' : ( hasFrozenRows ) ? 'hidden' : 'auto', + 'overflow-y': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'hidden' : 'hidden' : ( hasFrozenRows ) ? 'scroll' : 'auto' + }); + + $viewportTopR.css({ + 'overflow-x': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'hidden' : 'scroll' : ( hasFrozenRows ) ? 'hidden' : 'auto', + 'overflow-y': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'scroll' : 'auto' : ( hasFrozenRows ) ? 'scroll' : 'auto' + }); + + $viewportBottomL.css({ + 'overflow-x': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'scroll' : 'auto' : ( hasFrozenRows ) ? 'auto' : 'auto', + 'overflow-y': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'hidden' : 'hidden' : ( hasFrozenRows ) ? 'scroll' : 'auto' + }); + + $viewportBottomR.css({ + 'overflow-x': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'scroll' : 'auto' : ( hasFrozenRows ) ? 'auto' : 'auto', + 'overflow-y': ( options.frozenColumn > -1 ) ? ( hasFrozenRows ) ? 'auto' : 'auto' : ( hasFrozenRows ) ? 'auto' : 'auto' + }); + } + + function setScroller() { + if ( options.frozenColumn > -1 ) { + $headerScrollContainer = $headerScrollerR; + $headerRowScrollContainer = $headerRowScrollerR; + + if ( hasFrozenRows ) { + if ( options.frozenBottom ) { + $viewportScrollContainerX = $viewportBottomR; + $viewportScrollContainerY = $viewportTopR; + } else { + $viewportScrollContainerX = $viewportScrollContainerY = $viewportBottomR; + } + } else { + $viewportScrollContainerX = $viewportScrollContainerY = $viewportTopR; + } + } else { + $headerScrollContainer = $headerScrollerL; + $headerRowScrollContainer = $headerRowScrollerL; + + if ( hasFrozenRows ) { + if ( options.frozenBottom ) { + $viewportScrollContainerX = $viewportBottomL; + $viewportScrollContainerY = $viewportTopL; + } else { + $viewportScrollContainerX = $viewportScrollContainerY = $viewportBottomL; + } + } else { + $viewportScrollContainerX = $viewportScrollContainerY = $viewportTopL; + } + } + } + + function measureCellPaddingAndBorder() { + var el; + var h = ["borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight"]; + var v = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + + el = $("").appendTo($headers); + 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; + }); + el.remove(); + + var r = $("
").appendTo($canvas); + el = $("").appendTo(r); + 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; + }); + r.remove(); + + absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff); + } + + function createCssRules() { + $style = $("