diff --git a/slick.grid.js b/slick.grid.js index c12bae9bb..8d3177321 100644 --- a/slick.grid.js +++ b/slick.grid.js @@ -17,3406 +17,3956 @@ */ // make sure required JavaScript modules are loaded -if (typeof jQuery === "undefined") { - throw "SlickGrid requires jquery module to be loaded"; +;if (typeof jQuery === "undefined") { + 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, - fullWidthRows: false, - multiColumnSort: false, - defaultFormatter: defaultFormatter, - forceSyncScrolling: false, - addNewRowCssClass: "new-row" - }; - - 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, $headerRowSpacer; - var $topPanelScroller; - var $topPanel; - var $viewport; - var $canvas; - var $style; - var $boundAncestors; - var stylesheet, columnCssRulesL, columnCssRulesR; - var viewportH, viewportW; - var canvasWidth; - var viewportHasHScroll, viewportHasVScroll; - var headerColumnWidthDiff = 0, headerColumnHeightDiff = 0, // border+padding - cellWidthDiff = 0, cellHeightDiff = 0; - var absoluteColumnMinWidth; - - var tabbingDirection = 1; - var activePosX; - var activeRow, activeCell; - var activeCellNode = null; - var currentEditor = null; - var serializedEditorValue; - var editController; - - var rowsCache = {}; - var renderedRows = 0; - var numVisibleRows; - 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; - - // 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 +;(function ($) { + // Slick.Grid + $.extend(true, window, { + Slick: { + Grid: SlickGrid + } + }); + // shared across all grids on the page + var scrollbarDimensions; + var maxSupportedCssHeight; // browser's breaking point ////////////////////////////////////////////////////////////////////////////////////////////// - // Initialization + // SlickGrid class implementation (available as Slick.Grid) - function init() { - $container = $(container); - if ($container.length < 1) { - throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM."); - } + /** + * 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, + enableAsyncPostRenderCleanup: false, + asyncPostRenderCleanupDelay: 40, + autoHeight: false, + editorLock: Slick.GlobalEditorLock, + showHeaderRow: false, + headerRowHeight: 25, + createFooterRow: false, + showFooterRow: false, + footerRowHeight: 25, + showTopPanel: false, + topPanelHeight: 25, + formatterFactory: null, + editorFactory: null, + cellFlashingCssClass: "flashing", + selectedCellCssClass: "selected", + multiSelect: true, + enableTextSelectionOnCells: false, + dataItemColumnValueExtractor: null, + fullWidthRows: false, + multiColumnSort: false, + defaultFormatter: defaultFormatter, + forceSyncScrolling: false, + addNewRowCssClass: "new-row", + defaultAriaAttr: defaultAriaAttr + }; - // calculate these only once and share between grid instances - maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight(); - scrollbarDimensions = scrollbarDimensions || measureScrollbar(); + var columnDefaults = { + name: "", + resizable: true, + sortable: false, + minWidth: 30, + rerenderOnResize: false, + headerCssClass: null, + defaultSortAsc: true, + focusable: true, + selectable: true + }; - options = $.extend({}, defaults, options); - validateAndEnforceOptions(); - columnDefaults.width = options.defaultColumnWidth; + // 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 $ariaLive; + var $headerScroller; + var $headers; + var $headerRow, $headerRowScroller, $headerRowSpacer; + var $footerRow, $footerRowScroller, $footerRowSpacer; + var $topPanelScroller; + var $topPanel; + var $viewport; + var $canvas; + var $style; + var $boundAncestors; + var stylesheet, columnCssRulesL, columnCssRulesR; + var viewportH, viewportW; + var canvasWidth; + var viewportHasHScroll, viewportHasVScroll; + var headerColumnWidthDiff = 0, headerColumnHeightDiff = 0, // border+padding + cellWidthDiff = 0, cellHeightDiff = 0, jQueryNewWidthBehaviour = false; + var absoluteColumnMinWidth; + + var tabbingDirection = 1; + var activePosX; + var activeRow, activeCell; + var activeCellNode = null; + var currentEditor = null; + var serializedEditorValue; + var editController; + + var rowsCache = {}; + var renderedRows = 0; + var numVisibleRows; + 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 = []; + + var isNavigatedOnFocus = false; + var isAriaDescriptionRead = false; + var ariaDescriptionReadTimer; + + + // async call handles + var h_editorLoader = null; + var h_render = null; + var h_postrender = null; + var h_postrenderCleanup = null; + var postProcessedRows = {}; + var postProcessToRow = null; + var postProcessFromRow = null; + var postProcessedCleanupQueue = []; + var postProcessgroupId = 0; + + // perf counters + var counter_rows_rendered = 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 + var zombieRowCacheFromLastMouseWheelEvent; // row cache for above node + var zombieRowPostProcessedFromLastMouseWheelEvent; // post processing references for above node + + // store css attributes if display:none is active in container or parent + var cssShow = { position: 'absolute', visibility: 'hidden', display: 'block' }; + var $hiddenParents; + var oldProps = []; + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Initialization + + function init() { + $container = $(container); + if ($container.length < 1) { + throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM."); + } - 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; - } - } + cacheCssForHiddenInit(); - // 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"); - } + // calculate these only once and share between grid instances + maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight(); + scrollbarDimensions = scrollbarDimensions || measureScrollbar(); - editController = { - "commitCurrentEdit": commitCurrentEdit, - "cancelCurrentEdit": cancelCurrentEdit - }; + options = $.extend({}, defaults, options); + validateAndEnforceOptions(); + columnDefaults.width = options.defaultColumnWidth; - $container - .empty() - .css("overflow", "hidden") - .css("outline", 0) - .addClass(uid) - .addClass("ui-widget"); + 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; + } + } - // set up a positioning container if needed - if (!/relative|absolute|fixed/.test($container.css("position"))) { - $container.css("position", "relative"); - } + // 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"); + } - $focusSink = $("
").appendTo($container); + editController = { + "commitCurrentEdit": commitCurrentEdit, + "cancelCurrentEdit": cancelCurrentEdit + }; + + $container + .empty() + .css("overflow", "hidden") + .css("outline", 0) + .addClass(uid) + .addClass("ui-widget") + .attr('role', 'application') + .attr('tabindex', '0'); + + // set up a positioning container if needed + if (!/relative|absolute|fixed/.test($container.css("position"))) { + $container.css("position", "relative"); + } - $headerScroller = $("
").appendTo($container); - $headers = $("
").appendTo($headerScroller); - $headers.width(getHeadersWidth()); + $ariaLive = $("
").appendTo($container); + $focusSink = $("
").appendTo($container); + - $headerRowScroller = $("
").appendTo($container); - $headerRow = $("
").appendTo($headerRowScroller); - $headerRowSpacer = $("
") - .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") - .appendTo($headerRowScroller); + $headerScroller = $("
").appendTo($container); + $headers = $("
").appendTo($headerScroller); + $headers.width(getHeadersWidth()); - $topPanelScroller = $("
").appendTo($container); - $topPanel = $("
").appendTo($topPanelScroller); + $headerRowScroller = $("
").appendTo($container); + $headerRow = $("
").appendTo($headerRowScroller); + $headerRowSpacer = $("
") + .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") + .appendTo($headerRowScroller); - if (!options.showTopPanel) { - $topPanelScroller.hide(); - } + $topPanelScroller = $("
").appendTo($container); + $topPanel = $("
").appendTo($topPanelScroller); - if (!options.showHeaderRow) { - $headerRowScroller.hide(); - } + if (!options.showTopPanel) { + $topPanelScroller.hide(); + } - $viewport = $("
").appendTo($container); - $viewport.css("overflow-y", options.autoHeight ? "hidden" : "auto"); + if (!options.showHeaderRow) { + $headerRowScroller.hide(); + } - $canvas = $("
").appendTo($viewport); + $viewport = $("
").appendTo($container); + $viewport.css("overflow-y", options.autoHeight ? "hidden" : "auto"); - $focusSink2 = $focusSink.clone().appendTo($container); + $canvas = $("
").appendTo($viewport); - if (!options.explicitInitialization) { - finishInitialization(); - } - } + if (options.createFooterRow) { + $footerRowScroller = $("
").appendTo($container); + $footerRow = $("
").appendTo($footerRowScroller); + $footerRowSpacer = $("
") + .css("width", getCanvasWidth() + scrollbarDimensions.width + "px") + .appendTo($footerRowScroller); - function finishInitialization() { - if (!initialized) { - initialized = true; - - viewportW = parseFloat($.css($container[0], "width", true)); - - // 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"); - }); - } - - updateColumnCaches(); - createColumnHeaders(); - setupColumnSort(); - createCssRules(); - resizeCanvas(); - bindAncestorScrollEvents(); - - $container - .bind("resize.slickgrid", resizeCanvas); - $viewport - //.bind("click", handleClick) - .bind("scroll", handleScroll); - $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); - - // Work around http://crbug.com/312427. - if (navigator.userAgent.toLowerCase().match(/webkit/) && - navigator.userAgent.toLowerCase().match(/macintosh/)) { - $canvas.bind("mousewheel", handleMouseWheel); - } - } - } + if (!options.showFooterRow) { + $footerRowScroller.hide(); + } + } - function registerPlugin(plugin) { - plugins.unshift(plugin); - plugin.init(self); - } + $focusSink2 = $focusSink.clone().appendTo($container); - 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; - } - } - } + if (!options.explicitInitialization) { + finishInitialization(); + } - function setSelectionModel(model) { - if (selectionModel) { - selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); - if (selectionModel.destroy) { - selectionModel.destroy(); + setTimeout(function () { $('.' + uid).css('display', 'block'); },0); } - } - selectionModel = model; - if (selectionModel) { - selectionModel.init(self); - selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); - } - } + function finishInitialization() { + if (!initialized) { + initialized = true; - function getSelectionModel() { - return selectionModel; - } + viewportW = parseFloat($.css($container[0], "width", true)); - function getCanvasNode() { - return $canvas[0]; - } + // 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(); - function measureScrollbar() { - var $c = $("
").appendTo("body"); - var dim = { - width: $c.width() - $c[0].clientWidth, - height: $c.height() - $c[0].clientHeight - }; - $c.remove(); - return dim; - } + // 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) - 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; - } + 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"); + }); + } - function getCanvasWidth() { - var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW; - var rowWidth = 0; - var i = columns.length; - while (i--) { - rowWidth += columns[i].width; - } - return options.fullWidthRows ? Math.max(rowWidth, availableWidth) : rowWidth; - } + updateColumnCaches(); + createColumnHeaders(); + setupColumnSort(); + createCssRules(); + resizeCanvas(); + bindAncestorScrollEvents(); + + $container + .bind("resize.slickgrid", resizeCanvas); + $viewport + //.bind("click", handleClick) + .bind("scroll", handleScroll); + $headerScroller + .bind("contextmenu", handleHeaderContextMenu) + .bind("click", handleHeaderClick) + .delegate(".slick-header-column", "mouseenter", handleHeaderMouseEnter) + .delegate(".slick-header-column", "mouseleave", handleHeaderMouseLeave); + $headerRowScroller + .bind("scroll", handleHeaderRowScroll); + + if (options.createFooterRow) { + $footerRowScroller + .bind("scroll", handleFooterRowScroll); + } - function updateCanvasWidth(forceColumnWidthsUpdate) { - var oldCanvasWidth = canvasWidth; - canvasWidth = getCanvasWidth(); + $focusSink.add($focusSink2) + .bind("keydown", handleKeyDown) + .bind("focus", handleFocusSink) + .bind("blur", handleBlurSink); + $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); + + // Work around http://crbug.com/312427. + if (navigator.userAgent.toLowerCase().match(/webkit/) && + navigator.userAgent.toLowerCase().match(/macintosh/)) { + $canvas.bind("mousewheel", handleMouseWheel); + } + restoreCssFromHiddenInit(); + } + } - if (canvasWidth != oldCanvasWidth) { - $canvas.width(canvasWidth); - $headerRow.width(canvasWidth); - $headers.width(getHeadersWidth()); - viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width); - } + function cacheCssForHiddenInit() { + // handle display:none on container or container parents + $hiddenParents = $container.parents().andSelf().not(':visible'); + $hiddenParents.each(function() { + var old = {}; + for ( var name in cssShow ) { + old[ name ] = this.style[ name ]; + this.style[ name ] = cssShow[ name ]; + } + oldProps.push(old); + }); + } - $headerRowSpacer.width(canvasWidth + (viewportHasVScroll ? scrollbarDimensions.width : 0)); + function restoreCssFromHiddenInit() { + // finish handle display:none on container or container parents + // - put values back the way they were + $hiddenParents.each(function(i) { + var old = oldProps[i]; + for ( var name in cssShow ) { + this.style[ name ] = old[ name ]; + } + }); + } - if (canvasWidth != oldCanvasWidth || forceColumnWidthsUpdate) { - applyColumnWidths(); - } - } + function registerPlugin(plugin) { + plugins.unshift(plugin); + plugin.init(self); + } - 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 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 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); + function setSelectionModel(model) { + if (selectionModel) { + selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); + if (selectionModel.destroy) { + selectionModel.destroy(); + } + } - while (true) { - var test = supportedHeight * 2; - div.css("height", test); - if (test > testUpTo || div.height() !== test) { - break; - } else { - supportedHeight = test; + selectionModel = model; + if (selectionModel) { + selectionModel.init(self); + selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); + } } - } - - div.remove(); - return supportedHeight; - } - // TODO: this is static. need to handle page mutation. - function bindAncestorScrollEvents() { - var elem = $canvas[0]; - while ((elem = elem.parentNode) != document.body && elem != null) { - // bind to scroll containers only - if (elem == $viewport[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 getSelectionModel() { + return selectionModel; + } - function unbindAncestorScrollEvents() { - if (!$boundAncestors) { - return; - } - $boundAncestors.unbind("scroll." + uid); - $boundAncestors = null; - } + function getCanvasNode() { + return $canvas[0]; + } - function updateColumnHeader(columnId, title, toolTip) { - if (!initialized) { return; } - var idx = getColumnIndex(columnId); - if (idx == null) { - return; - } + function measureScrollbar() { + var $c = $("
").appendTo("body"); + var dim = { + width: $c.width() - $c[0].clientWidth, + height: $c.height() - $c[0].clientHeight + }; + $c.remove(); + return dim; + } - var columnDef = columns[idx]; - var $header = $headers.children().eq(idx); - if ($header) { - if (title !== undefined) { - columns[idx].name = title; + 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; } - if (toolTip !== undefined) { - columns[idx].toolTip = toolTip; + + function getCanvasWidth() { + var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW; + var rowWidth = 0; + var i = columns.length; + while (i--) { + rowWidth += columns[i].width; + } + return options.fullWidthRows ? Math.max(rowWidth, availableWidth) : rowWidth; } - trigger(self.onBeforeHeaderCellDestroy, { - "node": $header[0], - "column": columnDef - }); + function updateCanvasWidth(forceColumnWidthsUpdate) { + var oldCanvasWidth = canvasWidth; + canvasWidth = getCanvasWidth(); - $header - .attr("title", toolTip || "") - .children().eq(0).html(title); + if (canvasWidth != oldCanvasWidth) { + $canvas.width(canvasWidth); + $headerRow.width(canvasWidth); + if (options.createFooterRow) { $footerRow.width(canvasWidth); } + $headers.width(getHeadersWidth()); + viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width); + } - trigger(self.onHeaderCellRendered, { - "node": $header[0], - "column": columnDef - }); - } - } + var w=canvasWidth + (viewportHasVScroll ? scrollbarDimensions.width : 0); + $headerRowSpacer.width(w); + if (options.createFooterRow) { $footerRowSpacer.width(w); } - function getHeaderRow() { - return $headerRow[0]; - } + if (canvasWidth != oldCanvasWidth || forceColumnWidthsUpdate) { + applyColumnWidths(); + } + } - function getHeaderRowColumn(columnId) { - var idx = getColumnIndex(columnId); - var $header = $headerRow.children().eq(idx); - return $header && $header[0]; - } + 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 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 - }); - } - }); - $headers.empty(); - $headers.width(getHeadersWidth()); - - $headerRow.find(".slick-headerrow-column") - .each(function() { - var columnDef = $(this).data("column"); - if (columnDef) { - trigger(self.onBeforeHeaderRowCellDestroy, { - "node": this, - "column": columnDef - }); - } - }); - $headerRow.empty(); + 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); - for (var i = 0; i < columns.length; i++) { - var m = columns[i]; + while (true) { + var test = supportedHeight * 2; + div.css("height", test); + if (test > testUpTo || div.height() !== test) { + break; + } else { + supportedHeight = test; + } + } - var header = $("
") - .html("" + m.name + "") - .width(m.width - headerColumnWidthDiff) - .attr("id", "" + uid + m.id) - .attr("title", m.toolTip || "") - .data("column", m) - .addClass(m.headerCssClass || "") - .appendTo($headers); + div.remove(); + return supportedHeight; + } - if (options.enableColumnReorder || m.sortable) { - header - .on('mouseenter', onMouseEnter) - .on('mouseleave', onMouseLeave); + // TODO: this is static. need to handle page mutation. + function bindAncestorScrollEvents() { + var elem = $canvas[0]; + while ((elem = elem.parentNode) != document.body && elem != null) { + // bind to scroll containers only + if (elem == $viewport[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); + } + } } - if (m.sortable) { - header.addClass("slick-header-sortable"); - header.append(""); + function unbindAncestorScrollEvents() { + if (!$boundAncestors) { + return; + } + $boundAncestors.unbind("scroll." + uid); + $boundAncestors = null; } - trigger(self.onHeaderCellRendered, { - "node": header[0], - "column": m - }); + 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; + } - if (options.showHeaderRow) { - var headerRowCell = $("
") - .data("column", m) - .appendTo($headerRow); + trigger(self.onBeforeHeaderCellDestroy, { + "node": $header[0], + "column": columnDef, + "grid": self + }); + + $header + .attr("title", toolTip || "") + .children().eq(0).html(title); - trigger(self.onHeaderRowCellRendered, { - "node": headerRowCell[0], - "column": m - }); + trigger(self.onHeaderCellRendered, { + "node": $header[0], + "column": columnDef, + "grid": self + }); + } } - } - setSortColumns(sortColumns); - setupColumnResize(); - if (options.enableColumnReorder) { - setupColumnReorder(); - } - } + function getHeaderRow() { + return $headerRow[0]; + } - 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; + function getFooterRow() { + return $footerRow[0]; + } - if ($(e.target).hasClass("slick-resizable-handle")) { - return; + function getHeaderRowColumn(columnId) { + var idx = getColumnIndex(columnId); + var $header = $headerRow.children().eq(idx); + return $header && $header[0]; } - var $col = $(e.target).closest(".slick-header-column"); - if (!$col.length) { - return; + function getFooterRowColumn(columnId) { + var idx = getColumnIndex(columnId); + var $footer = $footerRow.children().eq(idx); + return $footer && $footer[0]; } - var column = $col.data("column"); - if (column.sortable) { - if (!getEditorLock().commitCurrentEdit()) { - return; - } + function createColumnHeaders() { + function onMouseEnter() { + $(this).addClass("ui-state-hover"); + } - 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; + function onMouseLeave() { + $(this).removeClass("ui-state-hover"); } - } - if (e.metaKey && options.multiColumnSort) { - if (sortOpts) { - sortColumns.splice(i, 1); + $headers.find(".slick-header-column") + .each(function() { + var columnDef = $(this).data("column"); + if (columnDef) { + trigger(self.onBeforeHeaderCellDestroy, { + "node": this, + "column": columnDef, + "grid": self + }); + } + }); + $headers.empty(); + $headers.width(getHeadersWidth()); + + $headerRow.find(".slick-headerrow-column") + .each(function() { + var columnDef = $(this).data("column"); + if (columnDef) { + trigger(self.onBeforeHeaderRowCellDestroy, { + "node": this, + "column": columnDef, + "grid": self + }); + } + }); + $headerRow.empty(); + + if (options.createFooterRow) { + $footerRow.find(".slick-footerrow-column") + .each(function() { + var columnDef = $(this).data("column"); + if (columnDef) { + trigger(self.onBeforeFooterRowCellDestroy, { + "node": this, + "column": columnDef + }); + } + }); + $footerRow.empty(); } - } - else { - if ((!e.shiftKey && !e.metaKey) || !options.multiColumnSort) { - sortColumns = []; + + for (var i = 0; i < columns.length; i++) { + var m = columns[i]; + + var header = $("
") + .html("" + m.name + "") + .width(m.width - headerColumnWidthDiff) + .attr("id", "" + uid + m.id) + .attr("title", m.toolTip || "") + .data("column", m) + .addClass(m.headerCssClass || "") + .appendTo($headers); + + 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, + "grid": self + }); + + if (options.showHeaderRow) { + var headerRowCell = $("
") + .data("column", m) + .appendTo($headerRow); + + trigger(self.onHeaderRowCellRendered, { + "node": headerRowCell[0], + "column": m, + "grid": self + }); + } + if (options.createFooterRow && options.showFooterRow) { + var footerRowCell = $("
") + .data("column", m) + .appendTo($footerRow); + + trigger(self.onFooterRowCellRendered, { + "node": footerRowCell[0], + "column": m + }); + } } - if (!sortOpts) { - sortOpts = { columnId: column.id, sortAsc: column.defaultSortAsc }; - sortColumns.push(sortOpts); - } else if (sortColumns.length == 0) { - sortColumns.push(sortOpts); + 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; + } + } - setSortColumns(sortColumns); + 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: column.defaultSortAsc }; + sortColumns.push(sortOpts); + } else if (sortColumns.length == 0) { + sortColumns.push(sortOpts); + } + } - 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); - } + setSortColumns(sortColumns); + + if (!options.multiColumnSort) { + trigger(self.onSort, { + multiColumnSort: false, + sortCol: column, + sortAsc: sortOpts.sortAsc, + grid: self}, e); + } else { + trigger(self.onSort, { + multiColumnSort: true, + sortCols: $.map(sortColumns, function(col) { + return {sortCol: columns[getColumnIndex(col.columnId)], sortAsc: col.sortAsc }; + }), + grid: self}, e); + } + } + }); } - }); - } - function setupColumnReorder() { - $headers.filter(":ui-sortable").sortable("destroy"); - $headers.sortable({ - containment: "parent", - distance: 3, - axis: "x", - cursor: "default", - tolerance: "intersection", - helper: "clone", - placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", - start: function (e, ui) { - ui.placeholder.width(ui.helper.outerWidth() - headerColumnWidthDiff); - $(ui.helper).addClass("slick-header-column-active"); - }, - beforeStop: function (e, ui) { - $(ui.helper).removeClass("slick-header-column-active"); - }, - stop: function (e) { - if (!getEditorLock().commitCurrentEdit()) { - $(this).sortable("cancel"); - return; - } - - var reorderedIds = $headers.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 setupColumnReorder() { + $headers.filter(":ui-sortable").sortable("destroy"); + $headers.sortable({ + containment: "parent", + distance: 3, + axis: "x", + cursor: "default", + tolerance: "intersection", + helper: "clone", + placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", + start: function (e, ui) { + ui.placeholder.width(ui.helper.outerWidth() - headerColumnWidthDiff); + $(ui.helper).addClass("slick-header-column-active"); + }, + beforeStop: function (e, ui) { + $(ui.helper).removeClass("slick-header-column-active"); + }, + stop: function (e) { + if (!getEditorLock().commitCurrentEdit()) { + $(this).sortable("cancel"); + return; + } - 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; - } + var reorderedIds = $headers.sortable("toArray"); + var reorderedColumns = []; + for (var i = 0; i < reorderedIds.length; i++) { + reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid, ""))]); } - shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); - } + setColumns(reorderedColumns); + + trigger(self.onColumnsReordered, {grid: self}); + e.stopPropagation(); + setupColumnResize(); } - } - 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; + }); + } + + 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; } - } - 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; - 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; + 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; + 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; + } + } + } + + 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; + } + } + } + } + } else { // stretch column + x = d; + 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; + } + } + } + + 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; + } + } + } + } + } + applyColumnHeaderWidths(); + if (options.syncColumnCellResize) { + 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, {grid: self}); + }); + }); + } + + 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 measureCellPaddingAndBorder() { + var el; + var h = ["borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight"]; + var v = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + + // jquery prior to version 1.8 handles .width setter/getter as a direct css write/read + // jquery 1.8 changed .width to read the true inner element width if box-sizing is set to border-box, and introduced a setter for .outerWidth + // so for equivalent functionality, prior to 1.8 use .width, and after use .outerWidth + var verArray = $.fn.jquery.split('.'); + jQueryNewWidthBehaviour = (verArray[0]==1 && verArray[1]>=8) || verArray[0] >=2; + + el = $("").appendTo($headers); + headerColumnWidthDiff = headerColumnHeightDiff = 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(); + + var r = $("
").appendTo($canvas); + el = $("").appendTo(r); + cellWidthDiff = cellHeightDiff = 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(); + + absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff); + } + + function createCssRules() { + $style = $("