diff options
author | Michael Leibman <michael.leibman@gmail.com> | 2012-07-06 13:52:44 -0700 |
---|---|---|
committer | mleibman <michael.leibman@gmail.com> | 2012-07-06 13:52:44 -0700 |
commit | e4f25cbb63722475618f019b92d26aa935a0e1ad (patch) | |
tree | 4c5b07bd9b7793b39b9e704216cdfa4bacc39a6e /slick.grid.js | |
parent | 5b42d6bda060d72ec0c10d1ecdd10d87d6f92f4b (diff) | |
download | SlickGrid-e4f25cbb63722475618f019b92d26aa935a0e1ad.zip SlickGrid-e4f25cbb63722475618f019b92d26aa935a0e1ad.tar.gz SlickGrid-e4f25cbb63722475618f019b92d26aa935a0e1ad.tar.bz2 |
SlickGrid v2.1 - Implement virtual rendering for columns.
Diffstat (limited to 'slick.grid.js')
-rw-r--r-- | slick.grid.js | 423 |
1 files changed, 337 insertions, 86 deletions
diff --git a/slick.grid.js b/slick.grid.js index 474e790..32ca332 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -7,7 +7,7 @@ * Distributed under MIT license. * All rights reserved. * - * SlickGrid v2.0 + * SlickGrid v2.1 * * NOTES: * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods. @@ -68,7 +68,7 @@ if (typeof Slick === "undefined") { asyncEditorLoadDelay: 100, forceFitColumns: false, enableAsyncPostRender: false, - asyncPostRenderDelay: 60, + asyncPostRenderDelay: 50, autoHeight: false, editorLock: Slick.GlobalEditorLock, showHeaderRow: false, @@ -105,7 +105,7 @@ if (typeof Slick === "undefined") { var page = 0; // current page var offset = 0; // current page offset - var scrollDir = 1; + var vScrollDir = 1; // private var initialized = false; @@ -143,7 +143,9 @@ if (typeof Slick === "undefined") { var prevScrollTop = 0; var scrollTop = 0; var lastRenderedScrollTop = 0; + var lastRenderedScrollLeft = 0; var prevScrollLeft = 0; + var scrollLeft = 0; var selectionModel; var selectedRows = []; @@ -153,6 +155,8 @@ if (typeof Slick === "undefined") { var columnsById = {}; var sortColumns = []; + var columnPosLeft = []; + var columnPosRight = []; // async call handles @@ -184,6 +188,12 @@ if (typeof Slick === "undefined") { options = $.extend({}, defaults, options); 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; + } + // 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"); @@ -209,7 +219,8 @@ if (typeof Slick === "undefined") { $focusSink = $("<div tabIndex='0' hideFocus style='position:fixed;width:0;height:0;top:0;left:0;outline:0;'></div>").appendTo($container); $headerScroller = $("<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); - $headers = $("<div class='slick-header-columns' style='width:10000px; left:-1000px' />").appendTo($headerScroller); + $headers = $("<div class='slick-header-columns' style='left:-1000px' />").appendTo($headerScroller); + $headers.width(getHeadersWidth()); $headerRowScroller = $("<div class='slick-headerrow ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container); $headerRow = $("<div class='slick-headerrow-columns' />").appendTo($headerRowScroller); @@ -260,6 +271,7 @@ if (typeof Slick === "undefined") { }); } + updateColumnCaches(); createColumnHeaders(); setupColumnSort(); createCssRules(); @@ -341,12 +353,22 @@ if (typeof Slick === "undefined") { return dim; } + function getHeadersWidth() { + var headersWidth = 0; + for (var i = 0, ii = columns.length; i < ii; i++) { + var width = columns[i].width; + headersWidth += width; + } + headersWidth += scrollbarDimensions.width; + return Math.max(headersWidth, viewportW) + 1000; + } + function getCanvasWidth() { var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW; var rowWidth = 0; var i = columns.length; while (i--) { - rowWidth += (columns[i].width || columnDefaults.width); + rowWidth += columns[i].width; } return options.fullWidthRows ? Math.max(rowWidth, availableWidth) : rowWidth; } @@ -358,6 +380,7 @@ if (typeof Slick === "undefined") { if (canvasWidth != oldCanvasWidth) { $canvas.width(canvasWidth); $headerRow.width(canvasWidth); + $headers.width(getHeadersWidth()); viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width); } @@ -472,12 +495,11 @@ if (typeof Slick === "undefined") { }); $headers.empty(); + $headers.width(getHeadersWidth()); $headerRow.empty(); - columnsById = {}; for (var i = 0; i < columns.length; i++) { - var m = columns[i] = $.extend({}, columnDefaults, columns[i]); - columnsById[m.id] = i; + var m = columns[i]; var header = $("<div class='ui-state-default slick-header-column' id='" + uid + m.id + "' />") .html("<span class='slick-column-name'>" + m.name + "</span>") @@ -1021,6 +1043,8 @@ if (typeof Slick === "undefined") { h.width(columns[i].width - headerColumnWidthDiff); } } + + updateColumnCaches(); } function applyColumnWidths() { @@ -1093,8 +1117,29 @@ if (typeof Slick === "undefined") { return columns; } + function updateColumnCaches() { + // Pre-calculate cell boundaries. + columnPosLeft = []; + columnPosRight = []; + var x = 0; + for (var i = 0, ii = columns.length; i < ii; i++) { + columnPosLeft[i] = x; + columnPosRight[i] = x + columns[i].width; + x += columns[i].width; + } + } + function setColumns(columnDefinitions) { columns = columnDefinitions; + + columnsById = {}; + for (var i = 0; i < columns.length; i++) { + var m = columns[i] = $.extend({}, columnDefaults, columns[i]); + columnsById[m.id] = i; + } + + updateColumnCaches(); + if (initialized) { invalidateAllRows(); createColumnHeaders(); @@ -1195,12 +1240,12 @@ if (typeof Slick === "undefined") { if (offset != oldOffset) { var range = getVisibleRange(newScrollTop); - cleanupRows(range.top, range.bottom); + cleanupRows(range); updateRowPositions(); } if (prevScrollTop != newScrollTop) { - scrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1; + vScrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1; $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop); trigger(self.onViewportChanged, {}); @@ -1252,7 +1297,8 @@ if (typeof Slick === "undefined") { return item[columnDef.field]; } - function appendRowHtml(stringArray, row) { + function appendRowHtml(stringArray, row, range) { + var cacheEntry = rowsCache[row]; var d = getDataItem(row); var dataLoading = row < getDataLength() && !d; var cellCss; @@ -1269,39 +1315,66 @@ if (typeof Slick === "undefined") { stringArray.push("<div class='ui-widget-content " + rowCss + "' style='top:" + (options.rowHeight * row - offset) + "px'>"); var colspan, m; - for (var i = 0, cols = columns.length; i < cols; i++) { + for (var i = 0, ii = columns.length; i < ii; i++) { m = columns[i]; - colspan = getColspan(row, i); - cellCss = "slick-cell l" + i + " r" + Math.min(columns.length - 1, i + colspan - 1) + (m.cssClass ? " " + m.cssClass : ""); - if (row === activeRow && i === activeCell) { - cellCss += (" active"); + colspan = 1; + if (metadata && metadata.columns) { + var columnData = metadata.columns[m.id] || metadata.columns[i]; + colspan = (columnData && columnData.colspan) || 1; + if (colspan === "*") { + colspan = ii - i; + } } - // TODO: merge them together in the setter - for (var key in cellCssClasses) { - if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) { - cellCss += (" " + cellCssClasses[key][row][m.id]); + // Do not render cells outside of the viewport. + if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) { + if (columnPosLeft[i] > range.rightPx) { + // All columns to the right are outside the range. + break; } - } - stringArray.push("<div class='" + cellCss + "'>"); + appendCellHtml(stringArray, row, i, colspan); + } - // 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, i, value, m, d)); + if (colspan > 1) { + i += (colspan - 1); } + } - stringArray.push("</div>"); + stringArray.push("</div>"); + } - if (colspan) { - i += (colspan - 1); + function appendCellHtml(stringArray, row, cell, colspan) { + var m = columns[cell]; + var d = getDataItem(row); + var cellCss = "slick-cell l" + cell + " r" + Math.min(columns.length - 1, cell + colspan - 1) + + (m.cssClass ? " " + m.cssClass : ""); + if (row === activeRow && cell === activeCell) { + cellCss += (" active"); + } + + // TODO: merge them together in the setter + for (var key in cellCssClasses) { + if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) { + cellCss += (" " + cellCssClasses[key][row][m.id]); } } + 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 (d) { + var value = getDataItemValueForColumn(d, m); + stringArray.push(getFormatter(row, m)(row, cell, value, m, d)); + } + stringArray.push("</div>"); + + rowsCache[row].cellRenderQueue.push(cell); + rowsCache[row].cellColSpans[cell] = colspan; } + function cleanupRows(rangeToKeep) { for (var i in rowsCache) { if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) { @@ -1342,7 +1415,7 @@ if (typeof Slick === "undefined") { if (!rows || !rows.length) { return; } - scrollDir = 0; + vScrollDir = 0; for (i = 0, rl = rows.length; i < rl; i++) { if (currentEditor && activeRow === rows[i]) { makeActiveCellNormal(); @@ -1487,26 +1560,31 @@ if (typeof Slick === "undefined") { updateCanvasWidth(false); } - function getVisibleRange(viewportTop) { + function getVisibleRange(viewportTop, viewportLeft) { if (viewportTop == null) { viewportTop = scrollTop; } + if (viewportLeft == null) { + viewportLeft = scrollLeft; + } return { top: Math.floor((viewportTop + offset) / options.rowHeight), - bottom: Math.ceil((viewportTop + offset + viewportH) / options.rowHeight) + bottom: Math.ceil((viewportTop + offset + viewportH) / options.rowHeight), + leftPx: viewportLeft, + rightPx: viewportLeft + viewportW }; } - function getRenderedRange(viewportTop) { - var range = getVisibleRange(viewportTop); + function getRenderedRange(viewportTop, viewportLeft) { + var range = getVisibleRange(viewportTop, viewportLeft); var buffer = Math.round(viewportH / options.rowHeight); var minBuffer = 3; - if (scrollDir == -1) { + if (vScrollDir == -1) { range.top -= buffer; range.bottom += minBuffer; - } else if (scrollDir == 1) { + } else if (vScrollDir == 1) { range.top -= minBuffer; range.bottom += buffer; } else { @@ -1517,25 +1595,146 @@ if (typeof Slick === "undefined") { range.top = Math.max(0, range.top); range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, range.bottom); + range.leftPx -= viewportW; + range.rightPx += viewportW; + + range.leftPx = Math.max(0, range.leftPx); + range.rightPx = Math.min(canvasWidth, range.rightPx); + return range; } function ensureCellNodesInRowsCache(row) { var cacheEntry = rowsCache[row]; if (cacheEntry) { - if (!cacheEntry.cellNodes) { - cacheEntry.cellNodes = []; - cacheEntry.cellNodesByColumnIdx = []; - - var columnIdx = 0, cellNodes = cacheEntry.rowNode.childNodes; - for (var j = 0, jj = cellNodes.length; j < jj; j++) { - cacheEntry.cellNodesByColumnIdx[columnIdx] = cacheEntry.cellNodes[j] = cellNodes[j]; - columnIdx += getColspan(row, columnIdx); + if (cacheEntry.cellRenderQueue.length) { + var lastChild = cacheEntry.rowNode.lastChild; + while (cacheEntry.cellRenderQueue.length) { + var columnIdx = cacheEntry.cellRenderQueue.pop(); + cacheEntry.cellNodesByColumnIdx[columnIdx] = lastChild; + lastChild = lastChild.previousSibling; } } } } + function cleanUpCells(range, row) { + var totalCellsRemoved = 0; + var cacheEntry = rowsCache[row]; + + // Remove cells outside the range. + var cellsToRemove = []; + for (var i in cacheEntry.cellNodesByColumnIdx) { + // I really hate it when people mess with Array.prototype. + if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(i)) { + continue; + } + + // This is a string, so it needs to be cast back to a number. + i = i | 0; + + var colspan = cacheEntry.cellColSpans[i]; + if (columnPosLeft[i] > range.rightPx || + columnPosRight[Math.min(columns.length - 1, i + colspan - 1)] < range.leftPx) { + if (!(row == activeRow && i == activeCell)) { + cellsToRemove.push(i); + } + } + } + + var cellToRemove; + while ((cellToRemove = cellsToRemove.pop()) != null) { + cacheEntry.rowNode.removeChild(cacheEntry.cellNodesByColumnIdx[cellToRemove]); + delete cacheEntry.cellColSpans[cellToRemove]; + delete cacheEntry.cellNodesByColumnIdx[cellToRemove]; + if (postProcessedRows[row]) { + delete postProcessedRows[row][cellToRemove]; + } + totalCellsRemoved++; + } + } + + function cleanUpAndRenderCells(range) { + var cacheEntry; + var stringArray = []; + var processedRows = []; + var cellsAdded; + var totalCellsAdded = 0; + var colspan; + + for (var row = range.top; row <= range.bottom; row++) { + cacheEntry = rowsCache[row]; + if (!cacheEntry) { + continue; + } + + // cellRenderQueue populated in renderRows() needs to be cleared first + ensureCellNodesInRowsCache(row); + + cleanUpCells(range, row); + + // Render missing cells. + cellsAdded = 0; + + var metadata = data.getItemMetadata && data.getItemMetadata(row); + metadata = metadata && metadata.columns; + + // TODO: shorten this loop (index? heuristics? binary search?) + for (var i = 0, ii = columns.length; i < ii; i++) { + // Cells to the right are outside the range. + if (columnPosLeft[i] > range.rightPx) { + break; + } + + // Already rendered. + if ((colspan = cacheEntry.cellColSpans[i]) != null) { + i += (colspan > 1 ? colspan - 1 : 0); + continue; + } + + colspan = 1; + if (metadata) { + var columnData = metadata[columns[i].id] || metadata[i]; + colspan = (columnData && columnData.colspan) || 1; + if (colspan === "*") { + colspan = ii - i; + } + } + + if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) { + appendCellHtml(stringArray, row, i, colspan); + cellsAdded++; + } + + i += (colspan > 1 ? colspan - 1 : 0); + } + + if (cellsAdded) { + totalCellsAdded += cellsAdded; + processedRows.push(row); + } + } + + if (!stringArray.length) { + return; + } + + var x = document.createElement("div"); + x.innerHTML = stringArray.join(""); + + var processedRow; + var node; + while ((processedRow = processedRows.pop()) != null) { + cacheEntry = rowsCache[processedRow]; + var columnIdx; + while ((columnIdx = cacheEntry.cellRenderQueue.pop()) != null) { + node = x.lastChild; + cacheEntry.rowNode.appendChild(node); + cacheEntry.cellNodesByColumnIdx[columnIdx] = node; + } + } + } + function renderRows(range) { var parentNode = $canvas[0], stringArray = [], @@ -1548,7 +1747,26 @@ if (typeof Slick === "undefined") { } renderedRows++; rows.push(i); - appendRowHtml(stringArray, i); + + // Create an entry right away so that appendRowHtml() can + // start populatating it. + rowsCache[i] = { + "rowNode": null, + + // ColSpans of rendered cells (by column idx). + // Can also be used for checking whether a cell has been rendered. + "cellColSpans": [], + + // Cell nodes (by column idx). Lazy-populated by ensureCellNodesInRowsCache(). + "cellNodesByColumnIdx": [], + + // Column indices of cell nodes that have been rendered, but not yet indexed in + // cellNodesByColumnIdx. These are in the same order as cell nodes added at the + // end of the row. + "cellRenderQueue": [] + }; + + appendRowHtml(stringArray, i, range); if (activeCellNode && activeRow === i) { needToReselectCell = true; } @@ -1560,12 +1778,8 @@ if (typeof Slick === "undefined") { var x = document.createElement("div"); x.innerHTML = stringArray.join(""); - for (var i = 0, ii = x.childNodes.length; i < ii; i++) { - rowsCache[rows[i]] = { - "rowNode": parentNode.appendChild(x.firstChild), - "cellNodes": null, - "cellNodesByColumnIdx": null - }; + for (var i = 0, ii = rows.length; i < ii; i++) { + rowsCache[rows[i]].rowNode = parentNode.appendChild(x.firstChild); } if (needToReselectCell) { @@ -1602,7 +1816,12 @@ if (typeof Slick === "undefined") { // remove rows no longer in the viewport cleanupRows(rendered); - // add new rows + // add new rows & missing cells in existing rows + if (lastRenderedScrollLeft != scrollLeft) { + cleanUpAndRenderCells(rendered); + } + + // render missing rows renderRows(rendered); postProcessFromRow = visible.top; @@ -1610,27 +1829,29 @@ if (typeof Slick === "undefined") { startPostProcessing(); lastRenderedScrollTop = scrollTop; + lastRenderedScrollLeft = scrollLeft; h_render = null; } function handleScroll() { scrollTop = $viewport[0].scrollTop; - var scrollLeft = $viewport[0].scrollLeft; - var scrollDist = Math.abs(scrollTop - prevScrollTop); + scrollLeft = $viewport[0].scrollLeft; + var vScrollDist = Math.abs(scrollTop - prevScrollTop); + var hScrollDist = Math.abs(scrollLeft - prevScrollLeft); - if (scrollLeft !== prevScrollLeft) { + if (hScrollDist) { prevScrollLeft = scrollLeft; $headerScroller[0].scrollLeft = scrollLeft; $topPanelScroller[0].scrollLeft = scrollLeft; $headerRowScroller[0].scrollLeft = scrollLeft; } - if (scrollDist) { - scrollDir = prevScrollTop < scrollTop ? 1 : -1; + if (vScrollDist) { + vScrollDir = prevScrollTop < scrollTop ? 1 : -1; prevScrollTop = scrollTop; // switch virtual pages if needed - if (scrollDist < viewportH) { + if (vScrollDist < viewportH) { scrollTo(scrollTop + offset); } else { var oldOffset = offset; @@ -1640,20 +1861,24 @@ if (typeof Slick === "undefined") { invalidateAllRows(); } } + } + if (hScrollDist || vScrollDist) { if (h_render) { clearTimeout(h_render); } - if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH) { - if (Math.abs(lastRenderedScrollTop - scrollTop) > 20) { + if (Math.abs(lastRenderedScrollTop - scrollTop) > 20 || + Math.abs(lastRenderedScrollLeft - scrollLeft) > 20) { + if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH && + Math.abs(lastRenderedScrollLeft - scrollLeft) < viewportW) { render(); + } else { + h_render = setTimeout(render, 50); } - } else { - h_render = setTimeout(render, 50); - } - trigger(self.onViewportChanged, {}); + trigger(self.onViewportChanged, {}); + } } trigger(self.onScroll, {scrollLeft: scrollLeft, scrollTop: scrollTop}); @@ -1661,22 +1886,34 @@ if (typeof Slick === "undefined") { function asyncPostProcessRows() { while (postProcessFromRow <= postProcessToRow) { - var row = (scrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; + var row = (vScrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--; var cacheEntry = rowsCache[row]; - if (!cacheEntry || postProcessedRows[row] || row >= getDataLength()) { + if (!cacheEntry || row >= getDataLength()) { continue; } + if (!postProcessedRows[row]) { + postProcessedRows[row] = {}; + } + ensureCellNodesInRowsCache(row); - for (var i = 0; i < cacheEntry.cellNodesByColumnIdx.length; i++) { - var m = columns[i]; - if (m.asyncPostRender) { - var node = cacheEntry.cellNodesByColumnIdx[i]; - m.asyncPostRender(node, postProcessFromRow, getDataItem(row), m); + for (var columnIdx in cacheEntry.cellNodesByColumnIdx) { + if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) { + continue; + } + + columnIdx = columnIdx | 0; + + var m = columns[columnIdx]; + if (m.asyncPostRender && !postProcessedRows[row][columnIdx]) { + var node = cacheEntry.cellNodesByColumnIdx[columnIdx]; + if (node) { + m.asyncPostRender(node, postProcessFromRow, getDataItem(row), m); + } + postProcessedRows[row][columnIdx] = true; } } - postProcessedRows[row] = true; h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay); return; } @@ -2051,18 +2288,26 @@ if (typeof Slick === "undefined") { $focusSink[0].focus(); } - function scrollActiveCellIntoView() { - if (activeCellNode) { - var left = $(activeCellNode).position().left, - right = left + $(activeCellNode).outerWidth(), - scrollLeft = $viewport.scrollLeft(), - scrollRight = scrollLeft + $viewport.width(); + function scrollCellIntoView(row, cell) { + var colspan = getColspan(row, cell); + var left = columnPosLeft[cell], + right = columnPosRight[cell + (colspan > 1 ? colspan - 1 : 0)], + scrollRight = scrollLeft + viewportW; - if (left < scrollLeft) { - $viewport.scrollLeft(left); - } else if (right > scrollRight) { - $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth)); - } + if (left < scrollLeft) { + $viewport.scrollLeft(left); + handleScroll(); + render(); + } else if (right > scrollRight) { + $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth)); + handleScroll(); + render(); + } + } + + function scrollActiveCellIntoView() { + if (activeRow != null && activeCell != null) { + scrollCellIntoView(activeRow, activeCell); } } @@ -2097,7 +2342,6 @@ if (typeof Slick === "undefined") { } if (activeCellChanged) { - scrollActiveCellIntoView(); trigger(self.onActiveCellChanged, getActiveCell()); } } @@ -2347,8 +2591,11 @@ if (typeof Slick === "undefined") { var colspan = (columnData && columnData.colspan); if (colspan === "*") { colspan = columns.length - cell; + } else { + colspan = colspan || 1; } - return (colspan || 1); + + return colspan; } function findFirstFocusableCell(row) { @@ -2559,6 +2806,7 @@ if (typeof Slick === "undefined") { if (pos) { var isAddNewRow = (pos.row == getDataLength()); scrollRowIntoView(pos.row, !isAddNewRow); + scrollCellIntoView(pos.row, pos.cell); setActiveCellInternal(getCellNode(pos.row, pos.cell), isAddNewRow || options.autoEdit); activePosX = pos.posX; } else { @@ -2585,6 +2833,7 @@ if (typeof Slick === "undefined") { } scrollRowIntoView(row, false); + scrollCellIntoView(row, cell); setActiveCellInternal(getCellNode(row, cell), false); } @@ -2647,6 +2896,7 @@ if (typeof Slick === "undefined") { } scrollRowIntoView(row, false); + scrollCellIntoView(row, cell); var newCell = getCellNode(row, cell); @@ -2778,7 +3028,7 @@ if (typeof Slick === "undefined") { s += ("\n" + "n(umber of pages): " + n); s += ("\n" + "(current) page: " + page); s += ("\n" + "page height (ph): " + ph); - s += ("\n" + "scrollDir: " + scrollDir); + s += ("\n" + "vScrollDir: " + vScrollDir); alert(s); }; @@ -2862,6 +3112,7 @@ if (typeof Slick === "undefined") { "updateRowCount": updateRowCount, "scrollRowIntoView": scrollRowIntoView, "scrollRowToTop": scrollRowToTop, + "scrollCellIntoView": scrollCellIntoView, "getCanvasNode": getCanvasNode, "focus": setFocus, |