diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d0b874c198..7eb9031a632 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## HEAD
+## [0.8.18](https://github.com/warpech/jquery-handsontable/tree/v0.8.18) (Apr 12, 2013)
Features:
- added "Maximize HOT table" button in first example on [Scroll demo](handsontable.com/demo/scroll.html) page ((#495)[https://github.com/warpech/jquery-handsontable/issues/495])
diff --git a/dist/jquery.handsontable.full.css b/dist/jquery.handsontable.full.css
index 9cb7f6b9bf2..27484c595ae 100644
--- a/dist/jquery.handsontable.full.css
+++ b/dist/jquery.handsontable.full.css
@@ -1,12 +1,12 @@
/**
- * Handsontable 0.8.17
+ * Handsontable 0.8.18
* Handsontable is a simple jQuery plugin for editable tables with basic copy-paste compatibility with Excel and Google Docs
*
* Copyright 2012, Marcin Warpechowski
* Licensed under the MIT license.
* http://handsontable.com/
*
- * Date: Sun Mar 31 2013 19:46:52 GMT+0200 (Central European Daylight Time)
+ * Date: Fri Apr 12 2013 11:44:21 GMT+0200 (Central European Daylight Time)
*/
.handsontable {
diff --git a/dist/jquery.handsontable.full.js b/dist/jquery.handsontable.full.js
index b0ad0b55905..909af93ec9d 100644
--- a/dist/jquery.handsontable.full.js
+++ b/dist/jquery.handsontable.full.js
@@ -1,12 +1,12 @@
/**
- * Handsontable 0.8.17
+ * Handsontable 0.8.18
* Handsontable is a simple jQuery plugin for editable tables with basic copy-paste compatibility with Excel and Google Docs
*
* Copyright 2012, Marcin Warpechowski
* Licensed under the MIT license.
* http://handsontable.com/
*
- * Date: Sun Mar 31 2013 19:46:52 GMT+0200 (Central European Daylight Time)
+ * Date: Fri Apr 12 2013 11:44:21 GMT+0200 (Central European Daylight Time)
*/
/*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */
@@ -35,6 +35,7 @@ Handsontable.Core = function (rootElement, settings) {
priv = {
settings: {},
+ settingsFromDOM: {},
selStart: (new Handsontable.SelectionPoint()),
selEnd: (new Handsontable.SelectionPoint()),
editProxy: false,
@@ -1197,6 +1198,7 @@ Handsontable.Core = function (rootElement, settings) {
else {
rangeModifier({row: priv.selStart.row(), col: 0});
}
+ event.preventDefault(); //don't scroll the window
event.stopPropagation(); //required by HandsontableEditor
break;
@@ -1207,6 +1209,7 @@ Handsontable.Core = function (rootElement, settings) {
else {
rangeModifier({row: priv.selStart.row(), col: self.countCols() - 1});
}
+ event.preventDefault(); //don't scroll the window
event.stopPropagation(); //required by HandsontableEditor
break;
@@ -1303,6 +1306,7 @@ Handsontable.Core = function (rootElement, settings) {
bindEvents();
this.updateSettings(settings);
+ this.parseSettingsFromDOM();
this.view = new Handsontable.TableView(this);
this.forceFullRender = true; //used when data was changed
@@ -1580,6 +1584,38 @@ Handsontable.Core = function (rootElement, settings) {
}
};
+ /**
+ * Parse settings from DOM and CSS
+ * @public
+ */
+ this.parseSettingsFromDOM = function () {
+ var overflow = this.rootElement.css('overflow');
+ if (overflow === 'scroll' || overflow === 'auto') {
+ this.rootElement[0].style.overflow = 'visible';
+ priv.settingsFromDOM.overflow = overflow;
+ }
+ else if (priv.settings.width === void 0 || priv.settings.height === void 0) {
+ priv.settingsFromDOM.overflow = 'auto';
+ }
+
+ if (priv.settings.width === void 0) {
+ priv.settingsFromDOM.width = this.rootElement.width();
+ }
+ else {
+ priv.settingsFromDOM.width = void 0;
+ }
+
+ priv.settingsFromDOM.height = void 0;
+ if (priv.settings.height === void 0) {
+ if (priv.settingsFromDOM.overflow === 'scroll' || priv.settingsFromDOM.overflow === 'auto') {
+ var computedHeight = this.rootElement.height();
+ if (computedHeight > 0) {
+ priv.settingsFromDOM.height = computedHeight;
+ }
+ }
+ }
+ };
+
/**
* Render visible data
* @public
@@ -1732,6 +1768,14 @@ Handsontable.Core = function (rootElement, settings) {
return priv.settings;
};
+ /**
+ * Returns current settingsFromDOM object
+ * @return {Object}
+ */
+ this.getSettingsFromDOM = function () {
+ return priv.settingsFromDOM;
+ };
+
/**
* Clears grid
* @public
@@ -1913,9 +1957,9 @@ Handsontable.Core = function (rootElement, settings) {
*/
this.getColHeader = function (col, TH) {
col = Handsontable.PluginModifiers.run(self, 'col', col);
- var DIV = document.createElement('DIV'),
- SPAN = document.createElement('SPAN'),
- avoidInnerHTML = self.view.wt.wtDom.avoidInnerHTML;
+ var DIV = document.createElement('DIV'),
+ SPAN = document.createElement('SPAN'),
+ avoidInnerHTML = self.view.wt.wtDom.avoidInnerHTML;
DIV.className = 'relative';
SPAN.className = 'colHeader';
@@ -1930,15 +1974,7 @@ Handsontable.Core = function (rootElement, settings) {
avoidInnerHTML(SPAN, priv.settings.colHeaders(col));
}
else if (priv.settings.colHeaders && typeof priv.settings.colHeaders !== 'string' && typeof priv.settings.colHeaders !== 'number') {
- var dividend = col + 1;
- var columnLabel = '';
- var modulo;
- while (dividend > 0) {
- modulo = (dividend - 1) % 26;
- columnLabel = String.fromCharCode(65 + modulo) + columnLabel;
- dividend = parseInt((dividend - modulo) / 26, 10);
- }
- SPAN.appendChild(document.createTextNode(columnLabel));
+ SPAN.appendChild(document.createTextNode(Handsontable.helper.spreadsheetColumnLabel(col)));
}
else {
avoidInnerHTML(SPAN, priv.settings.colHeaders);
@@ -2219,7 +2255,7 @@ Handsontable.Core = function (rootElement, settings) {
/**
* Handsontable version
*/
- this.version = '0.8.17'; //inserted by grunt from package.json
+ this.version = '0.8.18'; //inserted by grunt from package.json
};
var settings = {
@@ -2297,39 +2333,26 @@ $.fn.handsontable = function (action) {
* @param {Object} instance
*/
Handsontable.TableView = function (instance) {
- var that = this;
- var $window = $(window);
+ var that = this
+ , $window = $(window)
+ , $documentElement = $(document.documentElement);
this.instance = instance;
- var settings = this.instance.getSettings();
+ this.settings = instance.getSettings();
+ this.settingsFromDOM = instance.getSettingsFromDOM();
instance.rootElement.data('originalStyle', instance.rootElement.attr('style')); //needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
instance.rootElement.addClass('handsontable');
var table = document.createElement('TABLE');
- table.className = 'htCore';
+ table.className = 'htCore';
+ table.appendChild(document.createElement('THEAD'));
+ table.appendChild(document.createElement('TBODY'));
- table.appendChild(document.createElement('THEAD'));
- table.appendChild(document.createElement('TBODY'));
+ instance.$table = $(table);
+ instance.rootElement.prepend(instance.$table);
- var $table = $(table);
-
- instance.$table = $table;
- instance.rootElement.prepend($table);
-
- this.overflow = instance.rootElement.css('overflow');
- if ((settings.width || settings.height) && !(this.overflow === 'scroll' || this.overflow === 'auto')) {
- this.overflow = 'auto';
- }
- if (this.overflow === 'scroll' || this.overflow === 'auto') {
- instance.rootElement[0].style.overflow = 'visible';
- //instance.rootElement[0].style.overflow = 'hidden';
- }
- this.determineContainerSize();
- //instance.rootElement[0].style.height = '';
- //instance.rootElement[0].style.width = '';
-
- $(document.documentElement).on('keyup.' + instance.guid, function (event) {
+ $documentElement.on('keyup.' + instance.guid, function (event) {
if (instance.selection.isInProgress() && !event.shiftKey) {
instance.selection.finish();
}
@@ -2338,7 +2361,7 @@ Handsontable.TableView = function (instance) {
var isMouseDown
, dragInterval;
- $(document.documentElement).on('mouseup.' + instance.guid, function (event) {
+ $documentElement.on('mouseup.' + instance.guid, function (event) {
if (instance.selection.isInProgress() && event.which === 1) { //is left mouse button
instance.selection.finish();
}
@@ -2355,7 +2378,7 @@ Handsontable.TableView = function (instance) {
}
});
- $(document.documentElement).on('mousedown.' + instance.guid, function (event) {
+ $documentElement.on('mousedown.' + instance.guid, function (event) {
var next = event.target;
if (next !== that.wt.wtTable.spreader) { //immediate click on "spreader" means click on the right side of vertical scrollbar
@@ -2367,34 +2390,34 @@ Handsontable.TableView = function (instance) {
}
}
- if (that.instance.getSettings().outsideClickDeselects) {
- that.instance.deselectCell();
+ if (that.settings.outsideClickDeselects) {
+ instance.deselectCell();
}
else {
- that.instance.destroyEditor();
+ instance.destroyEditor();
}
});
- $table.on('selectstart', function (event) {
+ instance.$table.on('selectstart', function (event) {
//https://github.com/warpech/jquery-handsontable/issues/160
//selectstart is IE only event. Prevent text from being selected when performing drag down in IE8
event.preventDefault();
});
- $table.on('mouseenter', function () {
+ instance.$table.on('mouseenter', function () {
if (dragInterval) { //if dragInterval was set (that means mouse was really outside of table, not over an element that is outside of
in DOM
clearInterval(dragInterval);
dragInterval = null;
}
});
- $table.on('mouseleave', function (event) {
+ instance.$table.on('mouseleave', function (event) {
if (!(isMouseDown || (instance.autofill.handle && instance.autofill.handle.isDragged))) {
return;
}
var tolerance = 1 //this is needed because width() and height() contains stuff like cell borders
- , offset = that.wt.wtDom.offset($table[0])
+ , offset = that.wt.wtDom.offset(table)
, offsetTop = offset.top + tolerance
, offsetLeft = offset.left + tolerance
, width = that.containerWidth - that.wt.getSetting('scrollbarWidth') - 2 * tolerance
@@ -2447,20 +2470,18 @@ Handsontable.TableView = function (instance) {
};
var walkontableConfig = {
- table: $table[0],
- async: settings.asyncRendering,
- stretchH: settings.stretchH,
+ table: table,
+ async: that.settings.asyncRendering,
+ stretchH: that.settings.stretchH,
data: instance.getDataAtCell,
totalRows: instance.countRows,
totalColumns: instance.countCols,
offsetRow: 0,
offsetColumn: 0,
- displayRows: null,
- displayColumns: null,
- width: this.containerWidth,
- height: this.containerHeight,
- frozenColumns: settings.rowHeaders ? [instance.getRowHeader] : null,
- columnHeaders: settings.colHeaders ? instance.getColHeader : null,
+ width: this.getWidth(),
+ height: this.getHeight(),
+ rowHeaders: that.settings.rowHeaders ? [instance.getRowHeader] : null,
+ columnHeaders: that.settings.colHeaders ? instance.getColHeader : null,
columnWidth: instance.getColWidth,
cellRenderer: function (row, column, TD) {
that.applyCellTypeMethod('renderer', TD, row, column);
@@ -2468,27 +2489,27 @@ Handsontable.TableView = function (instance) {
selections: {
current: {
className: 'current',
- highlightRowClassName: settings.currentRowClassName,
- highlightColumnClassName: settings.currentColClassName,
+ highlightRowClassName: that.settings.currentRowClassName,
+ highlightColumnClassName: that.settings.currentColClassName,
border: {
width: 2,
color: '#5292F7',
style: 'solid',
cornerVisible: function () {
- return settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
+ return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
}
}
},
area: {
className: 'area',
- highlightRowClassName: settings.currentRowClassName,
- highlightColumnClassName: settings.currentColClassName,
+ highlightRowClassName: that.settings.currentRowClassName,
+ highlightColumnClassName: that.settings.currentColClassName,
border: {
width: 1,
color: '#89AFF9',
style: 'solid',
cornerVisible: function () {
- return settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
+ return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
}
}
},
@@ -2517,8 +2538,8 @@ Handsontable.TableView = function (instance) {
event.preventDefault();
clearTextSelection();
- if (settings.afterOnCellMouseDown) {
- settings.afterOnCellMouseDown.call(that.instance, event, coords, TD);
+ if (that.settings.afterOnCellMouseDown) {
+ that.settings.afterOnCellMouseDown.call(instance, event, coords, TD);
}
},
onCellMouseOver: function (event, coords, TD) {
@@ -2540,28 +2561,22 @@ Handsontable.TableView = function (instance) {
}
};
- Handsontable.PluginHooks.run(this.instance, 'walkontableConfig', walkontableConfig);
+ Handsontable.PluginHooks.run(instance, 'walkontableConfig', walkontableConfig);
this.wt = new Walkontable(walkontableConfig);
- // this.instance.forceFullRender = true; //used when data was changed
+ // instance.forceFullRender = true; //used when data was changed
// this.render();
- var lastContainerWidth = that.containerWidth;
- var lastContainerHeight = that.containerHeight;
-
$window.on('resize.' + instance.guid, function () {
- that.instance.registerTimeout('resizeTimeout', function () {
- that.determineContainerSize();
- var newContainerWidth = that.containerWidth;
- var newContainerHeight = that.containerHeight;
-
- if (lastContainerWidth !== newContainerWidth || lastContainerHeight !== newContainerHeight) {
- that.wt.update('width', newContainerWidth);
- that.wt.update('height', newContainerHeight);
- that.instance.forceFullRender = true;
+ instance.registerTimeout('resizeTimeout', function () {
+ instance.parseSettingsFromDOM();
+ var newWidth = that.getWidth();
+ var newHeight = that.getHeight();
+ if (walkontableConfig.width !== newWidth || walkontableConfig.height !== newHeight) {
+ instance.forceFullRender = true;
that.render();
- lastContainerWidth = newContainerWidth;
- lastContainerHeight = newContainerHeight;
+ walkontableConfig.width = newWidth;
+ walkontableConfig.height = newHeight;
}
}, 60);
});
@@ -2572,44 +2587,28 @@ Handsontable.TableView = function (instance) {
}
});
- // $table[0].focus(); //otherwise TextEditor tests do not pass in IE8
+ // table.focus(); //otherwise TextEditor tests do not pass in IE8
};
Handsontable.TableView.prototype.isCellEdited = function () {
return (this.instance.textEditor && this.instance.textEditor.isCellEdited) || (this.instance.autocompleteEditor && this.instance.autocompleteEditor.isCellEdited) || (this.instance.handsontableEditor && this.instance.handsontableEditor.isCellEdited);
};
-Handsontable.TableView.prototype.determineContainerSize = function () {
- var settings = this.instance.getSettings();
-
- this.containerWidth = typeof settings.width === 'function' ? settings.width() : settings.width;
- this.containerHeight = typeof settings.height === 'function' ? settings.height() : settings.height;
-
- var computedWidth = this.instance.rootElement.width();
- var computedHeight = this.instance.rootElement.height();
-
- if (settings.width === void 0 && computedWidth > 0) {
- this.containerWidth = computedWidth;
- }
-
- if (this.overflow === 'scroll' || this.overflow === 'auto') {
- if (settings.height === void 0 && computedHeight > 0) {
- this.containerHeight = computedHeight;
- }
+Handsontable.TableView.prototype.getWidth = function () {
+ var val = typeof this.settings.width !== void 0 ? this.settings.width : this.settingsFromDOM.width;
+ return typeof val === 'function' ? val() : val;
+};
- if (this.instance.rootElement[0].style.height === '') {
- if (this.wt && this.wt.wtScroll.wtScrollbarV.visible) {
- if (typeof this.containerHeight === 'number') { //TODO move this to Handsontable, then this typeof can be removed
- this.containerHeight += this.wt.getSetting('scrollbarHeight');
- }
- }
- }
- }
+Handsontable.TableView.prototype.getHeight = function () {
+ var val = this.settings.height !== void 0 ? this.settings.height : this.settingsFromDOM.height;
+ return typeof val === 'function' ? val() : val;
};
Handsontable.TableView.prototype.render = function () {
if (this.instance.forceFullRender) {
Handsontable.PluginHooks.run(this.instance, 'beforeRender');
+ this.wt.update('width', this.getWidth());
+ this.wt.update('height', this.getHeight());
}
this.wt.draw(!this.instance.forceFullRender);
this.instance.rootElement.triggerHandler('render.handsontable');
@@ -2692,6 +2691,23 @@ Handsontable.helper.stringify = function (value) {
}
};
+/**
+ * Generates spreadsheet-like column names: A, B, C, ..., Z, AA, AB, etc
+ * @param index
+ * @returns {String}
+ */
+Handsontable.helper.spreadsheetColumnLabel = function (index) {
+ var dividend = index + 1;
+ var columnLabel = '';
+ var modulo;
+ while (dividend > 0) {
+ modulo = (dividend - 1) % 26;
+ columnLabel = String.fromCharCode(65 + modulo) + columnLabel;
+ dividend = parseInt((dividend - modulo) / 26, 10);
+ }
+ return columnLabel;
+};
+
/**
* Checks if child is a descendant of given parent node
* http://stackoverflow.com/questions/2234979/how-to-check-in-javascript-if-one-element-is-a-child-of-another
@@ -2735,6 +2751,7 @@ Handsontable.helper.randomString = function () {
Handsontable.helper.inherit = function (Child, Parent) {
function Bridge() {
}
+
Bridge.prototype = Parent.prototype;
Child.prototype = new Bridge();
Child.prototype.constructor = Child;
@@ -2960,23 +2977,28 @@ Handsontable.CheckboxRenderer = function (instance, TD, row, col, prop, value, c
//this is faster than innerHTML. See: https://github.com/warpech/jquery-handsontable/wiki/JavaScript-&-DOM-performance-tips
}
- if (!instance.checkboxInputMousedownListener) {
- //not very elegant but easy and fast
- instance.checkboxInputMousedownListener = function (event) {
+ var $input = $(INPUT);
+
+ if (cellProperties.readOnly) {
+ $input.on('click', function (event) {
+ event.preventDefault();
+ });
+ }
+ else {
+ $input.on('mousedown', function (event) {
if (!this.checked) {
instance.setDataAtRowProp(row, prop, cellProperties.checkedTemplate);
}
else {
instance.setDataAtRowProp(row, prop, cellProperties.uncheckedTemplate);
}
- event.stopPropagation(); //otherwise can confuse mousedown handler
- };
- instance.rootElement.on('mousedown', '.htCheckboxRendererInput', instance.checkboxInputMousedownListener); //this way we don't bind event listener to each arrow. We rely on propagation instead
- instance.checkboxInputMouseupListener = function (event) {
- event.stopPropagation(); //otherwise can confuse dblclick handler
- };
- instance.rootElement.on('mouseup', '.htCheckboxRendererInput', instance.checkboxInputMouseupListener); //this way we don't bind event listener to each arrow. We rely on propagation instead
+ event.stopPropagation(); //otherwise can confuse cell mousedown handler
+ });
+
+ $input.on('mouseup', function (event) {
+ event.stopPropagation(); //otherwise can confuse cell dblclick handler
+ });
}
return TD;
@@ -3577,17 +3599,18 @@ Handsontable.CheckboxEditor = function (instance, td, row, col, prop, value, cel
}
});
- function onDblClick() {
+ instance.view.wt.update('onCellDblClick', function () {
toggleCheckboxCell(instance, row, prop, cellProperties);
- }
-
- instance.view.wt.update('onCellDblClick', onDblClick);
+ });
return function () {
instance.$table.off(".editor");
instance.view.wt.update('onCellDblClick', null);
}
};
+
+
+
function HandsontableDateEditorClass(instance) {
this.isCellEdited = false;
this.instance = instance;
@@ -5065,7 +5088,7 @@ WalkontableBorder.prototype.appear = function (corners) {
var offsetRow = this.instance.getSetting('offsetRow')
, offsetColumn = this.instance.getSetting('offsetColumn')
, displayRows = this.instance.getSetting('displayRows')
- , displayColumns = this.instance.getSetting('displayColumns');
+ , lastColumn = this.instance.wtTable.getLastVisibleColumn();
var hideTop = false, hideLeft = false, hideBottom = false, hideRight = false;
@@ -5085,19 +5108,17 @@ WalkontableBorder.prototype.appear = function (corners) {
}
}
- if (displayColumns !== null) {
- if (corners[1] > offsetColumn + displayColumns - 1 || corners[3] < offsetColumn) {
- hideTop = hideLeft = hideBottom = hideRight = true;
+ if (corners[1] > lastColumn || corners[3] < offsetColumn) {
+ hideTop = hideLeft = hideBottom = hideRight = true;
+ }
+ else {
+ if (corners[1] < offsetColumn) {
+ corners[1] = offsetColumn;
+ hideLeft = true;
}
- else {
- if (corners[1] < offsetColumn) {
- corners[1] = offsetColumn;
- hideLeft = true;
- }
- if (corners[3] > offsetColumn + displayColumns - 1) {
- corners[3] = offsetColumn + displayColumns - 1;
- hideRight = true;
- }
+ if (corners[3] > lastColumn) {
+ corners[3] = lastColumn;
+ hideRight = true;
}
}
@@ -5196,6 +5217,121 @@ WalkontableBorder.prototype.hasSetting = function (setting) {
}
return !!setting;
};
+/**
+ * WalkontableColumnStrategy
+ * @param containerSizeFn
+ * @param cellRanges
+ * @param sizeAtIndex
+ * @param strategy - all, last, none
+ * @constructor
+ */
+function WalkontableColumnStrategy(containerSizeFn, cellRanges, sizeAtIndex, strategy) {
+ var low
+ , high
+ , cur
+ , size
+ , i
+ , ilen;
+
+ this.containerSizeFn = containerSizeFn;
+ this.cacheLength = 0;
+ this.cacheTotalSize = 0;
+ this.cache = {};
+ this.visibleCellRanges = [];
+
+ if (!cellRanges) {
+ return;
+ }
+ else if (cellRanges % 2 === 1) {
+ throw new Error('cellRanges must have even number of elements');
+ }
+ else if (!this.isSorted(cellRanges)) {
+ throw new Error('cellRanges must be in ascending order');
+ }
+
+ //step 1 - determine cells that fit containerSize and cache their widths
+ for (i = 0, ilen = cellRanges.length / 2; i < ilen; i++) {
+ low = cellRanges[2 * i];
+ high = cellRanges[2 * i + 1];
+ cur = low;
+ while (cur <= high) {
+ size = sizeAtIndex(cur);
+ if (this.cacheTotalSize >= this.getContainerSize(this.cacheTotalSize + size)) {
+ break;
+ }
+ this.cache[cur] = size;
+ this.cacheTotalSize += size;
+ this.cacheLength++;
+
+ if (cur === low) {
+ this.visibleCellRanges[2 * i] = cur;
+ }
+ this.visibleCellRanges[2 * i + 1] = cur;
+
+ cur++;
+ }
+ if (this.cacheTotalSize >= this.getContainerSize(this.cacheTotalSize)) {
+ break;
+ }
+ }
+
+ var containerSize = this.getContainerSize(this.cacheTotalSize)
+ , remainingSize = containerSize - this.cacheTotalSize;
+
+ //step 2 - apply stretching strategy
+ if (strategy === 'all') {
+ var ratio = containerSize / this.cacheTotalSize;
+ var newSize;
+
+ if (ratio > 1) { //if the ratio is smaller than 1 then it means last cell is not completely visible (=there is nothing to stretch)
+ for (i = 0, ilen = this.visibleCellRanges.length / 2; i < ilen; i++) {
+ low = this.visibleCellRanges[2 * i];
+ high = this.visibleCellRanges[2 * i + 1];
+ cur = low;
+ while (cur <= high) {
+ if (i === ilen - 1 && cur === high) {
+ this.cache[cur] += remainingSize;
+ }
+ else {
+ newSize = Math.floor(ratio * this.cache[cur]);
+ remainingSize -= newSize - this.cache[cur];
+ this.cache[cur] = newSize;
+ }
+ cur++;
+ }
+ }
+
+ }
+ }
+ else if (strategy === 'last') {
+ if (remainingSize > 0) {
+ this.cache[this.visibleCellRanges[this.visibleCellRanges.length - 1]] += remainingSize;
+ }
+ }
+}
+
+WalkontableColumnStrategy.prototype.getContainerSize = function (proposedWidth) {
+ var containerSize = typeof this.containerSizeFn === 'function' ? this.containerSizeFn(proposedWidth) : this.containerSizeFn;
+ if (containerSize === void 0 || containerSize === null || containerSize < 1) {
+ containerSize = Infinity;
+ }
+ return containerSize;
+};
+
+WalkontableColumnStrategy.prototype.getSize = function (index) {
+ return this.cache[index];
+};
+
+WalkontableColumnStrategy.prototype.isSorted = function (cellRanges) {
+ for (var i = 0, ilen = cellRanges.length; i < ilen; i++) {
+ if (i > 0) {
+ if (cellRanges[i - 1] > cellRanges[i]) {
+ return false;
+ }
+ }
+ }
+ return true;
+};
function Walkontable(settings) {
var self = this,
originalHeaders = [];
@@ -6159,7 +6295,7 @@ WalkontableSelection.prototype.draw = function (selectionsOnly) {
var offsetRow = this.instance.getSetting('offsetRow')
, lastVisibleRow = offsetRow + this.instance.getSetting('displayRows') - 1
, offsetColumn = this.instance.getSetting('offsetColumn')
- , lastVisibleColumn = offsetColumn + this.instance.getSetting('displayColumns') - 1;
+ , lastVisibleColumn = this.instance.wtTable.getLastVisibleColumn();
if (this.selected.length) {
corners = this.getCorners();
@@ -6230,7 +6366,7 @@ function WalkontableSettings(instance, settings) {
data: void 0,
offsetRow: 0,
offsetColumn: 0,
- frozenColumns: null,
+ rowHeaders: null,
columnHeaders: null, //this must be a function in format: function (col, TH) {}
totalRows: void 0,
totalColumns: void 0,
@@ -6238,12 +6374,7 @@ function WalkontableSettings(instance, settings) {
height: null,
cellRenderer: function (row, column, TD) {
var cellData = that.getSetting('data', row, column);
- if (cellData !== void 0) {
- that.instance.wtDom.avoidInnerHTML(TD, cellData);
- }
- else {
- this.wtDom.empty(TD);
- }
+ that.instance.wtDom.avoidInnerHTML(TD, cellData === void 0 || cellData === null ? '' : cellData);
},
columnWidth: 50,
selections: null,
@@ -6361,29 +6492,6 @@ WalkontableSettings.prototype.displayRows = function () {
}
};
-WalkontableSettings.prototype.displayColumns = function () {
- var estimated
- , calculated;
-
- if (this.settings['width']) {
- if (typeof this.settings['width'] !== 'number') {
- throw new Error('Walkontable width parameter must be a number (' + typeof this.settings['width'] + ' given)');
- }
- estimated = Math.ceil(this.settings['width'] / 50); //silly assumption but should be fine for now
- calculated = this.getSetting('totalColumns') - this.getSetting('offsetColumn');
- if (calculated < 0) {
- this.update('offsetColumn', Math.max(0, this.getSetting('totalColumns') - estimated));
- return estimated;
- }
- else {
- return Math.min(estimated, calculated);
- }
- }
- else {
- return this.getSetting('totalColumns');
- }
-};
-
WalkontableSettings.prototype.viewportRows = function () {
if (this.instance.wtTable.visibilityEdgeRow !== null) {
return this.instance.wtTable.visibilityEdgeRow - this.instance.wtTable.visibilityStartRow;
@@ -6395,7 +6503,7 @@ WalkontableSettings.prototype.viewportColumns = function () {
if (this.instance.wtTable.visibilityEdgeColumn !== null) {
return this.instance.wtTable.visibilityEdgeColumn - this.instance.wtTable.visibilityStartColumn;
}
- return this.getSetting('displayColumns');
+ return this.instance.wtTable.getLastVisibleColumn();
};
var FLAG_VISIBLE_HORIZONTAL = 0x1; // 000001
var FLAG_VISIBLE_VERTICAL = 0x2; // 000010
@@ -6524,20 +6632,29 @@ WalkontableTable.prototype.refreshHiderDimensions = function () {
};
WalkontableTable.prototype.refreshStretching = function () {
- var stretchH = this.instance.getSetting('stretchH')
- , totalColumns = this.instance.getSetting('totalColumns')
- , displayColumns = this.instance.getSetting('displayColumns')
- , displayTds = Math.min(displayColumns, totalColumns)
- , offsetColumn = this.instance.getSetting('offsetColumn')
- , frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0;
+ var instance = this.instance
+ , stretchH = instance.getSetting('stretchH')
+ , scrollH = instance.getSetting('scrollH')
+ , scrollbarWidth = instance.getSetting('scrollbarWidth')
+ , totalColumns = instance.getSetting('totalColumns')
+ , offsetColumn = instance.getSetting('offsetColumn')
+ , rowHeaders = instance.getSetting('rowHeaders')
+ , rowHeaderWidth = rowHeaders && rowHeaders.length ? 50 : 0
+ , containerWidth = this.instance.getSetting('width') - rowHeaderWidth;
- if (!this.instance.hasSetting('columnWidth')) {
- return;
- }
+ var containerWidthFn = function (cacheWidth) {
+ if (scrollH === 'scroll' || (scrollH === 'auto' && cacheWidth > containerWidth)) {
+ return containerWidth - scrollbarWidth;
+ }
+ return containerWidth;
+ };
+
+ var columnWidthFn = function (index) {
+ return instance.getSetting('columnWidth', index)
+ };
if (stretchH === 'hybrid') {
- if (this.instance.wtScroll.wtScrollbarH.visible) {
+ if (offsetColumn > 0) {
stretchH = 'last';
}
else {
@@ -6545,110 +6662,41 @@ WalkontableTable.prototype.refreshStretching = function () {
}
}
- var TD;
- if (this.instance.wtTable.TBODY.firstChild && this.instance.wtTable.TBODY.firstChild.firstChild) {
- TD = this.instance.wtTable.TBODY.firstChild.firstChild;
- }
- else if (this.instance.wtTable.THEAD.firstChild && this.instance.wtTable.THEAD.firstChild.firstChild) {
- TD = this.instance.wtTable.THEAD.firstChild.firstChild;
- }
-
- if (frozenColumnsCount) {
- TD = TD.nextSibling;
- }
-
- if (!TD) {
- return;
- }
-
- var cellOffset = this.instance.wtDom.offset(TD)
- , tableOffset = this.instance.wtTable.tableOffset
- , rowHeaderWidth = cellOffset.left - tableOffset.left
- , widths = []
- , widthSum = 0
- , c;
- for (c = 0; c < displayTds; c++) {
- widths.push(this.instance.getSetting('columnWidth', offsetColumn + c));
- widthSum += widths[c];
- }
- var domWidth = widthSum + rowHeaderWidth;
-
- if (stretchH === 'all' || stretchH === 'last') {
- var containerWidth = this.instance.getSetting('width');
- if (this.instance.wtScroll.wtScrollbarV.visible) {
- containerWidth -= this.instance.getSetting('scrollbarWidth');
- }
-
- var diff = containerWidth - domWidth;
- if (diff > 0) {
- if (stretchH === 'all') {
- var newWidth;
- var remainingDiff = diff;
- var ratio = diff / widthSum;
-
- for (c = 0; c < displayTds; c++) {
- if (widths[c]) {
- if (c === displayTds - 1) {
- newWidth = widths[c] + remainingDiff;
- }
- else {
- newWidth = widths[c] + Math.floor(ratio * widths[c]);
- remainingDiff -= Math.floor(ratio * widths[c]);
- }
- }
- widths[c] = newWidth;
- }
- }
- else {
- if (widths[widths.length - 1]) {
- widths[widths.length - 1] = widths[widths.length - 1] + diff;
- }
- }
- }
- }
-
- for (c = 0; c < displayTds; c++) {
- if (widths[c]) {
- this.COLGROUP.childNodes[c + frozenColumnsCount].style.width = widths[c] + 'px';
- }
- else {
- this.COLGROUP.childNodes[c + frozenColumnsCount].style.width = '';
- }
- }
+ this.columnStrategy = new WalkontableColumnStrategy(containerWidthFn, totalColumns ? [offsetColumn, totalColumns - 1] : null, columnWidthFn, stretchH);
};
WalkontableTable.prototype.adjustAvailableNodes = function () {
var instance = this.instance
, totalRows = instance.getSetting('totalRows')
- , totalColumns = instance.getSetting('totalColumns')
, displayRows = instance.getSetting('displayRows')
- , displayColumns = instance.getSetting('displayColumns')
, displayTds
- , frozenColumns = instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0
+ , rowHeaders = instance.getSetting('rowHeaders')
+ , rowHeadersCount = rowHeaders ? rowHeaders.length : 0
, TR
, c;
displayRows = Math.min(displayRows, totalRows);
- displayTds = Math.min(displayColumns, totalColumns);
+ this.refreshStretching();
+ displayTds = this.columnStrategy.cacheLength;
+
//adjust COLGROUP
- while (this.colgroupChildrenLength < displayTds + frozenColumnsCount) {
+ while (this.colgroupChildrenLength < displayTds + rowHeadersCount) {
this.COLGROUP.appendChild(document.createElement('COL'));
this.colgroupChildrenLength++;
}
- while (this.colgroupChildrenLength > displayTds + frozenColumnsCount) {
+ while (this.colgroupChildrenLength > displayTds + rowHeadersCount) {
this.COLGROUP.removeChild(this.COLGROUP.lastChild);
this.colgroupChildrenLength--;
}
//adjust THEAD
if (this.instance.hasSetting('columnHeaders')) {
- while (this.theadChildrenLength < displayTds + frozenColumnsCount) {
+ while (this.theadChildrenLength < displayTds + rowHeadersCount) {
this.THEAD.firstChild.appendChild(document.createElement('TH'));
this.theadChildrenLength++;
}
- while (this.theadChildrenLength > displayTds + frozenColumnsCount) {
+ while (this.theadChildrenLength > displayTds + rowHeadersCount) {
this.THEAD.firstChild.removeChild(this.THEAD.firstChild.lastChild);
this.theadChildrenLength--;
}
@@ -6657,7 +6705,7 @@ WalkontableTable.prototype.adjustAvailableNodes = function () {
//adjust TBODY
while (this.tbodyChildrenLength < displayRows) {
TR = document.createElement('TR');
- for (c = 0; c < frozenColumnsCount; c++) {
+ for (c = 0; c < rowHeadersCount; c++) {
TR.appendChild(document.createElement('TH'));
}
this.TBODY.appendChild(TR);
@@ -6672,13 +6720,13 @@ WalkontableTable.prototype.adjustAvailableNodes = function () {
var trChildrenLength;
for (var r = 0, rlen = TRs.length; r < rlen; r++) {
trChildrenLength = TRs[r].childNodes.length;
- while (trChildrenLength < displayTds + frozenColumnsCount) {
+ while (trChildrenLength < displayTds + rowHeadersCount) {
var TD = document.createElement('TD');
TD.setAttribute('tabindex', 10000); //http://www.barryvan.com.au/2009/01/onfocus-and-onblur-for-divs-in-fx/; 32767 is max tabindex for IE7,8
TRs[r].appendChild(TD);
trChildrenLength++;
}
- while (trChildrenLength > displayTds + frozenColumnsCount) {
+ while (trChildrenLength > displayTds + rowHeadersCount) {
TRs[r].removeChild(TRs[r].lastChild);
trChildrenLength--;
}
@@ -6706,26 +6754,23 @@ WalkontableTable.prototype._doDraw = function () {
, offsetRow = this.instance.getSetting('offsetRow')
, offsetColumn = this.instance.getSetting('offsetColumn')
, totalRows = this.instance.getSetting('totalRows')
- , totalColumns = this.instance.getSetting('totalColumns')
, displayRows = this.instance.getSetting('displayRows')
- , displayColumns = this.instance.getSetting('displayColumns')
- , displayTds
- , frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0
+ , displayTds = this.columnStrategy.cacheLength
+ , rowHeaders = this.instance.getSetting('rowHeaders')
+ , rowHeadersCount = rowHeaders ? rowHeaders.length : 0
, TR
, TH
, TD
, cellData;
displayRows = Math.min(displayRows, totalRows);
- displayTds = Math.min(displayColumns, totalColumns);
//draw COLGROUP
for (c = 0; c < this.colgroupChildrenLength; c++) {
- if (c < frozenColumnsCount) {
+ if (c < rowHeadersCount) {
this.wtDom.addClass(this.COLGROUP.childNodes[c], 'rowHeader');
- if (typeof frozenColumns[c] === "function") {
- frozenColumns[c](null, this.COLGROUP.childNodes[c])
+ if (typeof rowHeaders[c] === "function") {
+ rowHeaders[c](null, this.COLGROUP.childNodes[c])
}
}
else {
@@ -6733,14 +6778,12 @@ WalkontableTable.prototype._doDraw = function () {
}
}
- this.refreshStretching(); //needed here or otherwise scrollbarH is not shown
-
//draw THEAD
- if (frozenColumnsCount && this.instance.hasSetting('columnHeaders')) {
- for (c = 0; c < frozenColumnsCount; c++) {
+ if (rowHeadersCount && this.instance.hasSetting('columnHeaders')) {
+ for (c = 0; c < rowHeadersCount; c++) {
TH = this.THEAD.childNodes[0].childNodes[c];
- if (typeof frozenColumns[c] === "function") {
- frozenColumns[c](null, TH);
+ if (typeof rowHeaders[c] === "function") {
+ rowHeaders[c](null, TH);
}
else {
this.wtDom.empty(TH);
@@ -6751,10 +6794,12 @@ WalkontableTable.prototype._doDraw = function () {
}
}
- if (this.instance.hasSetting('columnHeaders')) {
- for (c = 0; c < displayTds; c++) {
- this.instance.getSetting('columnHeaders', offsetColumn + c, this.THEAD.childNodes[0].childNodes[frozenColumnsCount + c]);
+ var columnHeaders = this.instance.hasSetting('columnHeaders');
+ for (c = 0; c < displayTds; c++) {
+ if (columnHeaders) {
+ this.instance.getSetting('columnHeaders', offsetColumn + c, this.THEAD.childNodes[0].childNodes[rowHeadersCount + c]);
}
+ this.COLGROUP.childNodes[c + rowHeadersCount].style.width = this.columnStrategy.getSize(offsetColumn + c) + 'px';
}
//draw TBODY
@@ -6763,14 +6808,14 @@ WalkontableTable.prototype._doDraw = function () {
this.visibilityStartColumn = offsetColumn;
for (r = 0; r < displayRows; r++) {
TR = this.TBODY.childNodes[r];
- for (c = 0; c < frozenColumnsCount; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes
+ for (c = 0; c < rowHeadersCount; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes
TH = TR.childNodes[c];
- cellData = typeof frozenColumns[c] === "function" ? frozenColumns[c](offsetRow + r, TH) : frozenColumns[c];
+ cellData = typeof rowHeaders[c] === "function" ? rowHeaders[c](offsetRow + r, TH) : rowHeaders[c];
if (cellData !== void 0) {
this.wtDom.avoidInnerHTML(TH, cellData);
}
/*
- we can assume that frozenColumns[c] function took care of inserting content into TH
+ we can assume that rowHeaders[c] function took care of inserting content into TH
else {
TH.innerHTML = '';
}*/
@@ -6785,7 +6830,7 @@ WalkontableTable.prototype._doDraw = function () {
break;
}
else {
- TD = TR.childNodes[c + frozenColumnsCount];
+ TD = TR.childNodes[c + rowHeadersCount];
TD.className = '';
TD.removeAttribute('style');
this.instance.getSetting('cellRenderer', offsetRow + r, offsetColumn + c, TD);
@@ -6825,7 +6870,6 @@ WalkontableTable.prototype._doDraw = function () {
WalkontableTable.prototype.refreshPositions = function (selectionsOnly) {
this.instance.wtScroll.refreshScrollbars();
this.refreshHiderDimensions();
- this.refreshStretching();
this.refreshSelections(selectionsOnly);
};
@@ -6853,8 +6897,6 @@ WalkontableTable.prototype.recalcViewportCells = function () {
WalkontableTable.prototype.isCellVisible = function (r, c, TD) {
var out = 0
- , scrollV = this.instance.getSetting('scrollV')
- , scrollH = this.instance.getSetting('scrollH')
, cellOffset = this.wtDom.offset(TD)
, tableOffset = this.tableOffset
, innerOffsetTop = cellOffset.top - tableOffset.top
@@ -6924,12 +6966,12 @@ WalkontableTable.prototype.getCell = function (coords) {
if (coords[1] < offsetColumn) {
return -3; //column before viewport
}
- else if (coords[1] > offsetColumn + this.instance.getSetting('displayColumns') - 1) {
+ else if (coords[1] > this.getLastVisibleColumn()) {
return -4; //column after viewport
}
else {
- var frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = (frozenColumns ? frozenColumns.length : 0)
+ var rowHeaders = this.instance.getSetting('rowHeaders')
+ , rowHeadersCount = (rowHeaders ? rowHeaders.length : 0)
, tr = this.TBODY.childNodes[coords[0] - offsetRow];
if (typeof tr === "undefined") { //this block is only needed in async mode
@@ -6937,19 +6979,23 @@ WalkontableTable.prototype.getCell = function (coords) {
tr = this.TBODY.childNodes[coords[0] - offsetRow];
}
- return tr.childNodes[coords[1] - offsetColumn + frozenColumnsCount];
+ return tr.childNodes[coords[1] - offsetColumn + rowHeadersCount];
}
}
};
WalkontableTable.prototype.getCoords = function (TD) {
- var frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0;
+ var rowHeaders = this.instance.getSetting('rowHeaders')
+ , rowHeadersCount = rowHeaders ? rowHeaders.length : 0;
return [
this.wtDom.prevSiblings(TD.parentNode).length + this.instance.getSetting('offsetRow'),
- TD.cellIndex + this.instance.getSetting('offsetColumn') - frozenColumnsCount
+ TD.cellIndex + this.instance.getSetting('offsetColumn') - rowHeadersCount
];
};
+
+WalkontableTable.prototype.getLastVisibleColumn = function () {
+ return this.columnStrategy.visibleCellRanges[this.columnStrategy.visibleCellRanges.length - 1]
+};
function WalkontableWheel(instance) {
var that = this;
diff --git a/handsontable.jquery.json b/handsontable.jquery.json
index 82ccb08d5e7..492f8962013 100644
--- a/handsontable.jquery.json
+++ b/handsontable.jquery.json
@@ -1,7 +1,7 @@
{
"name": "handsontable",
"title": "Handsontable",
- "version": "0.8.17",
+ "version": "0.8.18",
"author": {
"name": "Marcin Warpechowski",
"email": "marcin@nextgen.pl",
diff --git a/jquery.handsontable.css b/jquery.handsontable.css
index 41c379d7516..681faa04780 100644
--- a/jquery.handsontable.css
+++ b/jquery.handsontable.css
@@ -1,12 +1,12 @@
/**
- * Handsontable 0.8.17
+ * Handsontable 0.8.18
* Handsontable is a simple jQuery plugin for editable tables with basic copy-paste compatibility with Excel and Google Docs
*
* Copyright 2012, Marcin Warpechowski
* Licensed under the MIT license.
* http://handsontable.com/
*
- * Date: Sun Mar 31 2013 19:46:52 GMT+0200 (Central European Daylight Time)
+ * Date: Fri Apr 12 2013 11:44:21 GMT+0200 (Central European Daylight Time)
*/
.handsontable {
diff --git a/jquery.handsontable.js b/jquery.handsontable.js
index 7d7dcd01d90..fdf046b09e8 100644
--- a/jquery.handsontable.js
+++ b/jquery.handsontable.js
@@ -1,12 +1,12 @@
/**
- * Handsontable 0.8.17
+ * Handsontable 0.8.18
* Handsontable is a simple jQuery plugin for editable tables with basic copy-paste compatibility with Excel and Google Docs
*
* Copyright 2012, Marcin Warpechowski
* Licensed under the MIT license.
* http://handsontable.com/
*
- * Date: Sun Mar 31 2013 19:46:52 GMT+0200 (Central European Daylight Time)
+ * Date: Fri Apr 12 2013 11:44:21 GMT+0200 (Central European Daylight Time)
*/
/*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */
@@ -35,6 +35,7 @@ Handsontable.Core = function (rootElement, settings) {
priv = {
settings: {},
+ settingsFromDOM: {},
selStart: (new Handsontable.SelectionPoint()),
selEnd: (new Handsontable.SelectionPoint()),
editProxy: false,
@@ -1197,6 +1198,7 @@ Handsontable.Core = function (rootElement, settings) {
else {
rangeModifier({row: priv.selStart.row(), col: 0});
}
+ event.preventDefault(); //don't scroll the window
event.stopPropagation(); //required by HandsontableEditor
break;
@@ -1207,6 +1209,7 @@ Handsontable.Core = function (rootElement, settings) {
else {
rangeModifier({row: priv.selStart.row(), col: self.countCols() - 1});
}
+ event.preventDefault(); //don't scroll the window
event.stopPropagation(); //required by HandsontableEditor
break;
@@ -1303,6 +1306,7 @@ Handsontable.Core = function (rootElement, settings) {
bindEvents();
this.updateSettings(settings);
+ this.parseSettingsFromDOM();
this.view = new Handsontable.TableView(this);
this.forceFullRender = true; //used when data was changed
@@ -1580,6 +1584,38 @@ Handsontable.Core = function (rootElement, settings) {
}
};
+ /**
+ * Parse settings from DOM and CSS
+ * @public
+ */
+ this.parseSettingsFromDOM = function () {
+ var overflow = this.rootElement.css('overflow');
+ if (overflow === 'scroll' || overflow === 'auto') {
+ this.rootElement[0].style.overflow = 'visible';
+ priv.settingsFromDOM.overflow = overflow;
+ }
+ else if (priv.settings.width === void 0 || priv.settings.height === void 0) {
+ priv.settingsFromDOM.overflow = 'auto';
+ }
+
+ if (priv.settings.width === void 0) {
+ priv.settingsFromDOM.width = this.rootElement.width();
+ }
+ else {
+ priv.settingsFromDOM.width = void 0;
+ }
+
+ priv.settingsFromDOM.height = void 0;
+ if (priv.settings.height === void 0) {
+ if (priv.settingsFromDOM.overflow === 'scroll' || priv.settingsFromDOM.overflow === 'auto') {
+ var computedHeight = this.rootElement.height();
+ if (computedHeight > 0) {
+ priv.settingsFromDOM.height = computedHeight;
+ }
+ }
+ }
+ };
+
/**
* Render visible data
* @public
@@ -1732,6 +1768,14 @@ Handsontable.Core = function (rootElement, settings) {
return priv.settings;
};
+ /**
+ * Returns current settingsFromDOM object
+ * @return {Object}
+ */
+ this.getSettingsFromDOM = function () {
+ return priv.settingsFromDOM;
+ };
+
/**
* Clears grid
* @public
@@ -1913,9 +1957,9 @@ Handsontable.Core = function (rootElement, settings) {
*/
this.getColHeader = function (col, TH) {
col = Handsontable.PluginModifiers.run(self, 'col', col);
- var DIV = document.createElement('DIV'),
- SPAN = document.createElement('SPAN'),
- avoidInnerHTML = self.view.wt.wtDom.avoidInnerHTML;
+ var DIV = document.createElement('DIV'),
+ SPAN = document.createElement('SPAN'),
+ avoidInnerHTML = self.view.wt.wtDom.avoidInnerHTML;
DIV.className = 'relative';
SPAN.className = 'colHeader';
@@ -1930,15 +1974,7 @@ Handsontable.Core = function (rootElement, settings) {
avoidInnerHTML(SPAN, priv.settings.colHeaders(col));
}
else if (priv.settings.colHeaders && typeof priv.settings.colHeaders !== 'string' && typeof priv.settings.colHeaders !== 'number') {
- var dividend = col + 1;
- var columnLabel = '';
- var modulo;
- while (dividend > 0) {
- modulo = (dividend - 1) % 26;
- columnLabel = String.fromCharCode(65 + modulo) + columnLabel;
- dividend = parseInt((dividend - modulo) / 26, 10);
- }
- SPAN.appendChild(document.createTextNode(columnLabel));
+ SPAN.appendChild(document.createTextNode(Handsontable.helper.spreadsheetColumnLabel(col)));
}
else {
avoidInnerHTML(SPAN, priv.settings.colHeaders);
@@ -2219,7 +2255,7 @@ Handsontable.Core = function (rootElement, settings) {
/**
* Handsontable version
*/
- this.version = '0.8.17'; //inserted by grunt from package.json
+ this.version = '0.8.18'; //inserted by grunt from package.json
};
var settings = {
@@ -2297,39 +2333,26 @@ $.fn.handsontable = function (action) {
* @param {Object} instance
*/
Handsontable.TableView = function (instance) {
- var that = this;
- var $window = $(window);
+ var that = this
+ , $window = $(window)
+ , $documentElement = $(document.documentElement);
this.instance = instance;
- var settings = this.instance.getSettings();
+ this.settings = instance.getSettings();
+ this.settingsFromDOM = instance.getSettingsFromDOM();
instance.rootElement.data('originalStyle', instance.rootElement.attr('style')); //needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
instance.rootElement.addClass('handsontable');
var table = document.createElement('TABLE');
- table.className = 'htCore';
+ table.className = 'htCore';
+ table.appendChild(document.createElement('THEAD'));
+ table.appendChild(document.createElement('TBODY'));
- table.appendChild(document.createElement('THEAD'));
- table.appendChild(document.createElement('TBODY'));
+ instance.$table = $(table);
+ instance.rootElement.prepend(instance.$table);
- var $table = $(table);
-
- instance.$table = $table;
- instance.rootElement.prepend($table);
-
- this.overflow = instance.rootElement.css('overflow');
- if ((settings.width || settings.height) && !(this.overflow === 'scroll' || this.overflow === 'auto')) {
- this.overflow = 'auto';
- }
- if (this.overflow === 'scroll' || this.overflow === 'auto') {
- instance.rootElement[0].style.overflow = 'visible';
- //instance.rootElement[0].style.overflow = 'hidden';
- }
- this.determineContainerSize();
- //instance.rootElement[0].style.height = '';
- //instance.rootElement[0].style.width = '';
-
- $(document.documentElement).on('keyup.' + instance.guid, function (event) {
+ $documentElement.on('keyup.' + instance.guid, function (event) {
if (instance.selection.isInProgress() && !event.shiftKey) {
instance.selection.finish();
}
@@ -2338,7 +2361,7 @@ Handsontable.TableView = function (instance) {
var isMouseDown
, dragInterval;
- $(document.documentElement).on('mouseup.' + instance.guid, function (event) {
+ $documentElement.on('mouseup.' + instance.guid, function (event) {
if (instance.selection.isInProgress() && event.which === 1) { //is left mouse button
instance.selection.finish();
}
@@ -2355,7 +2378,7 @@ Handsontable.TableView = function (instance) {
}
});
- $(document.documentElement).on('mousedown.' + instance.guid, function (event) {
+ $documentElement.on('mousedown.' + instance.guid, function (event) {
var next = event.target;
if (next !== that.wt.wtTable.spreader) { //immediate click on "spreader" means click on the right side of vertical scrollbar
@@ -2367,34 +2390,34 @@ Handsontable.TableView = function (instance) {
}
}
- if (that.instance.getSettings().outsideClickDeselects) {
- that.instance.deselectCell();
+ if (that.settings.outsideClickDeselects) {
+ instance.deselectCell();
}
else {
- that.instance.destroyEditor();
+ instance.destroyEditor();
}
});
- $table.on('selectstart', function (event) {
+ instance.$table.on('selectstart', function (event) {
//https://github.com/warpech/jquery-handsontable/issues/160
//selectstart is IE only event. Prevent text from being selected when performing drag down in IE8
event.preventDefault();
});
- $table.on('mouseenter', function () {
+ instance.$table.on('mouseenter', function () {
if (dragInterval) { //if dragInterval was set (that means mouse was really outside of table, not over an element that is outside of in DOM
clearInterval(dragInterval);
dragInterval = null;
}
});
- $table.on('mouseleave', function (event) {
+ instance.$table.on('mouseleave', function (event) {
if (!(isMouseDown || (instance.autofill.handle && instance.autofill.handle.isDragged))) {
return;
}
var tolerance = 1 //this is needed because width() and height() contains stuff like cell borders
- , offset = that.wt.wtDom.offset($table[0])
+ , offset = that.wt.wtDom.offset(table)
, offsetTop = offset.top + tolerance
, offsetLeft = offset.left + tolerance
, width = that.containerWidth - that.wt.getSetting('scrollbarWidth') - 2 * tolerance
@@ -2447,20 +2470,18 @@ Handsontable.TableView = function (instance) {
};
var walkontableConfig = {
- table: $table[0],
- async: settings.asyncRendering,
- stretchH: settings.stretchH,
+ table: table,
+ async: that.settings.asyncRendering,
+ stretchH: that.settings.stretchH,
data: instance.getDataAtCell,
totalRows: instance.countRows,
totalColumns: instance.countCols,
offsetRow: 0,
offsetColumn: 0,
- displayRows: null,
- displayColumns: null,
- width: this.containerWidth,
- height: this.containerHeight,
- frozenColumns: settings.rowHeaders ? [instance.getRowHeader] : null,
- columnHeaders: settings.colHeaders ? instance.getColHeader : null,
+ width: this.getWidth(),
+ height: this.getHeight(),
+ rowHeaders: that.settings.rowHeaders ? [instance.getRowHeader] : null,
+ columnHeaders: that.settings.colHeaders ? instance.getColHeader : null,
columnWidth: instance.getColWidth,
cellRenderer: function (row, column, TD) {
that.applyCellTypeMethod('renderer', TD, row, column);
@@ -2468,27 +2489,27 @@ Handsontable.TableView = function (instance) {
selections: {
current: {
className: 'current',
- highlightRowClassName: settings.currentRowClassName,
- highlightColumnClassName: settings.currentColClassName,
+ highlightRowClassName: that.settings.currentRowClassName,
+ highlightColumnClassName: that.settings.currentColClassName,
border: {
width: 2,
color: '#5292F7',
style: 'solid',
cornerVisible: function () {
- return settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
+ return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
}
}
},
area: {
className: 'area',
- highlightRowClassName: settings.currentRowClassName,
- highlightColumnClassName: settings.currentColClassName,
+ highlightRowClassName: that.settings.currentRowClassName,
+ highlightColumnClassName: that.settings.currentColClassName,
border: {
width: 1,
color: '#89AFF9',
style: 'solid',
cornerVisible: function () {
- return settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
+ return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
}
}
},
@@ -2517,8 +2538,8 @@ Handsontable.TableView = function (instance) {
event.preventDefault();
clearTextSelection();
- if (settings.afterOnCellMouseDown) {
- settings.afterOnCellMouseDown.call(that.instance, event, coords, TD);
+ if (that.settings.afterOnCellMouseDown) {
+ that.settings.afterOnCellMouseDown.call(instance, event, coords, TD);
}
},
onCellMouseOver: function (event, coords, TD) {
@@ -2540,28 +2561,22 @@ Handsontable.TableView = function (instance) {
}
};
- Handsontable.PluginHooks.run(this.instance, 'walkontableConfig', walkontableConfig);
+ Handsontable.PluginHooks.run(instance, 'walkontableConfig', walkontableConfig);
this.wt = new Walkontable(walkontableConfig);
- // this.instance.forceFullRender = true; //used when data was changed
+ // instance.forceFullRender = true; //used when data was changed
// this.render();
- var lastContainerWidth = that.containerWidth;
- var lastContainerHeight = that.containerHeight;
-
$window.on('resize.' + instance.guid, function () {
- that.instance.registerTimeout('resizeTimeout', function () {
- that.determineContainerSize();
- var newContainerWidth = that.containerWidth;
- var newContainerHeight = that.containerHeight;
-
- if (lastContainerWidth !== newContainerWidth || lastContainerHeight !== newContainerHeight) {
- that.wt.update('width', newContainerWidth);
- that.wt.update('height', newContainerHeight);
- that.instance.forceFullRender = true;
+ instance.registerTimeout('resizeTimeout', function () {
+ instance.parseSettingsFromDOM();
+ var newWidth = that.getWidth();
+ var newHeight = that.getHeight();
+ if (walkontableConfig.width !== newWidth || walkontableConfig.height !== newHeight) {
+ instance.forceFullRender = true;
that.render();
- lastContainerWidth = newContainerWidth;
- lastContainerHeight = newContainerHeight;
+ walkontableConfig.width = newWidth;
+ walkontableConfig.height = newHeight;
}
}, 60);
});
@@ -2572,44 +2587,28 @@ Handsontable.TableView = function (instance) {
}
});
- // $table[0].focus(); //otherwise TextEditor tests do not pass in IE8
+ // table.focus(); //otherwise TextEditor tests do not pass in IE8
};
Handsontable.TableView.prototype.isCellEdited = function () {
return (this.instance.textEditor && this.instance.textEditor.isCellEdited) || (this.instance.autocompleteEditor && this.instance.autocompleteEditor.isCellEdited) || (this.instance.handsontableEditor && this.instance.handsontableEditor.isCellEdited);
};
-Handsontable.TableView.prototype.determineContainerSize = function () {
- var settings = this.instance.getSettings();
-
- this.containerWidth = typeof settings.width === 'function' ? settings.width() : settings.width;
- this.containerHeight = typeof settings.height === 'function' ? settings.height() : settings.height;
-
- var computedWidth = this.instance.rootElement.width();
- var computedHeight = this.instance.rootElement.height();
-
- if (settings.width === void 0 && computedWidth > 0) {
- this.containerWidth = computedWidth;
- }
-
- if (this.overflow === 'scroll' || this.overflow === 'auto') {
- if (settings.height === void 0 && computedHeight > 0) {
- this.containerHeight = computedHeight;
- }
+Handsontable.TableView.prototype.getWidth = function () {
+ var val = typeof this.settings.width !== void 0 ? this.settings.width : this.settingsFromDOM.width;
+ return typeof val === 'function' ? val() : val;
+};
- if (this.instance.rootElement[0].style.height === '') {
- if (this.wt && this.wt.wtScroll.wtScrollbarV.visible) {
- if (typeof this.containerHeight === 'number') { //TODO move this to Handsontable, then this typeof can be removed
- this.containerHeight += this.wt.getSetting('scrollbarHeight');
- }
- }
- }
- }
+Handsontable.TableView.prototype.getHeight = function () {
+ var val = this.settings.height !== void 0 ? this.settings.height : this.settingsFromDOM.height;
+ return typeof val === 'function' ? val() : val;
};
Handsontable.TableView.prototype.render = function () {
if (this.instance.forceFullRender) {
Handsontable.PluginHooks.run(this.instance, 'beforeRender');
+ this.wt.update('width', this.getWidth());
+ this.wt.update('height', this.getHeight());
}
this.wt.draw(!this.instance.forceFullRender);
this.instance.rootElement.triggerHandler('render.handsontable');
@@ -2692,6 +2691,23 @@ Handsontable.helper.stringify = function (value) {
}
};
+/**
+ * Generates spreadsheet-like column names: A, B, C, ..., Z, AA, AB, etc
+ * @param index
+ * @returns {String}
+ */
+Handsontable.helper.spreadsheetColumnLabel = function (index) {
+ var dividend = index + 1;
+ var columnLabel = '';
+ var modulo;
+ while (dividend > 0) {
+ modulo = (dividend - 1) % 26;
+ columnLabel = String.fromCharCode(65 + modulo) + columnLabel;
+ dividend = parseInt((dividend - modulo) / 26, 10);
+ }
+ return columnLabel;
+};
+
/**
* Checks if child is a descendant of given parent node
* http://stackoverflow.com/questions/2234979/how-to-check-in-javascript-if-one-element-is-a-child-of-another
@@ -2735,6 +2751,7 @@ Handsontable.helper.randomString = function () {
Handsontable.helper.inherit = function (Child, Parent) {
function Bridge() {
}
+
Bridge.prototype = Parent.prototype;
Child.prototype = new Bridge();
Child.prototype.constructor = Child;
@@ -2960,23 +2977,28 @@ Handsontable.CheckboxRenderer = function (instance, TD, row, col, prop, value, c
//this is faster than innerHTML. See: https://github.com/warpech/jquery-handsontable/wiki/JavaScript-&-DOM-performance-tips
}
- if (!instance.checkboxInputMousedownListener) {
- //not very elegant but easy and fast
- instance.checkboxInputMousedownListener = function (event) {
+ var $input = $(INPUT);
+
+ if (cellProperties.readOnly) {
+ $input.on('click', function (event) {
+ event.preventDefault();
+ });
+ }
+ else {
+ $input.on('mousedown', function (event) {
if (!this.checked) {
instance.setDataAtRowProp(row, prop, cellProperties.checkedTemplate);
}
else {
instance.setDataAtRowProp(row, prop, cellProperties.uncheckedTemplate);
}
- event.stopPropagation(); //otherwise can confuse mousedown handler
- };
- instance.rootElement.on('mousedown', '.htCheckboxRendererInput', instance.checkboxInputMousedownListener); //this way we don't bind event listener to each arrow. We rely on propagation instead
- instance.checkboxInputMouseupListener = function (event) {
- event.stopPropagation(); //otherwise can confuse dblclick handler
- };
- instance.rootElement.on('mouseup', '.htCheckboxRendererInput', instance.checkboxInputMouseupListener); //this way we don't bind event listener to each arrow. We rely on propagation instead
+ event.stopPropagation(); //otherwise can confuse cell mousedown handler
+ });
+
+ $input.on('mouseup', function (event) {
+ event.stopPropagation(); //otherwise can confuse cell dblclick handler
+ });
}
return TD;
@@ -3577,17 +3599,18 @@ Handsontable.CheckboxEditor = function (instance, td, row, col, prop, value, cel
}
});
- function onDblClick() {
+ instance.view.wt.update('onCellDblClick', function () {
toggleCheckboxCell(instance, row, prop, cellProperties);
- }
-
- instance.view.wt.update('onCellDblClick', onDblClick);
+ });
return function () {
instance.$table.off(".editor");
instance.view.wt.update('onCellDblClick', null);
}
};
+
+
+
function HandsontableDateEditorClass(instance) {
this.isCellEdited = false;
this.instance = instance;
@@ -5065,7 +5088,7 @@ WalkontableBorder.prototype.appear = function (corners) {
var offsetRow = this.instance.getSetting('offsetRow')
, offsetColumn = this.instance.getSetting('offsetColumn')
, displayRows = this.instance.getSetting('displayRows')
- , displayColumns = this.instance.getSetting('displayColumns');
+ , lastColumn = this.instance.wtTable.getLastVisibleColumn();
var hideTop = false, hideLeft = false, hideBottom = false, hideRight = false;
@@ -5085,19 +5108,17 @@ WalkontableBorder.prototype.appear = function (corners) {
}
}
- if (displayColumns !== null) {
- if (corners[1] > offsetColumn + displayColumns - 1 || corners[3] < offsetColumn) {
- hideTop = hideLeft = hideBottom = hideRight = true;
+ if (corners[1] > lastColumn || corners[3] < offsetColumn) {
+ hideTop = hideLeft = hideBottom = hideRight = true;
+ }
+ else {
+ if (corners[1] < offsetColumn) {
+ corners[1] = offsetColumn;
+ hideLeft = true;
}
- else {
- if (corners[1] < offsetColumn) {
- corners[1] = offsetColumn;
- hideLeft = true;
- }
- if (corners[3] > offsetColumn + displayColumns - 1) {
- corners[3] = offsetColumn + displayColumns - 1;
- hideRight = true;
- }
+ if (corners[3] > lastColumn) {
+ corners[3] = lastColumn;
+ hideRight = true;
}
}
@@ -5196,6 +5217,121 @@ WalkontableBorder.prototype.hasSetting = function (setting) {
}
return !!setting;
};
+/**
+ * WalkontableColumnStrategy
+ * @param containerSizeFn
+ * @param cellRanges
+ * @param sizeAtIndex
+ * @param strategy - all, last, none
+ * @constructor
+ */
+function WalkontableColumnStrategy(containerSizeFn, cellRanges, sizeAtIndex, strategy) {
+ var low
+ , high
+ , cur
+ , size
+ , i
+ , ilen;
+
+ this.containerSizeFn = containerSizeFn;
+ this.cacheLength = 0;
+ this.cacheTotalSize = 0;
+ this.cache = {};
+ this.visibleCellRanges = [];
+
+ if (!cellRanges) {
+ return;
+ }
+ else if (cellRanges % 2 === 1) {
+ throw new Error('cellRanges must have even number of elements');
+ }
+ else if (!this.isSorted(cellRanges)) {
+ throw new Error('cellRanges must be in ascending order');
+ }
+
+ //step 1 - determine cells that fit containerSize and cache their widths
+ for (i = 0, ilen = cellRanges.length / 2; i < ilen; i++) {
+ low = cellRanges[2 * i];
+ high = cellRanges[2 * i + 1];
+ cur = low;
+ while (cur <= high) {
+ size = sizeAtIndex(cur);
+ if (this.cacheTotalSize >= this.getContainerSize(this.cacheTotalSize + size)) {
+ break;
+ }
+ this.cache[cur] = size;
+ this.cacheTotalSize += size;
+ this.cacheLength++;
+
+ if (cur === low) {
+ this.visibleCellRanges[2 * i] = cur;
+ }
+ this.visibleCellRanges[2 * i + 1] = cur;
+
+ cur++;
+ }
+ if (this.cacheTotalSize >= this.getContainerSize(this.cacheTotalSize)) {
+ break;
+ }
+ }
+
+ var containerSize = this.getContainerSize(this.cacheTotalSize)
+ , remainingSize = containerSize - this.cacheTotalSize;
+
+ //step 2 - apply stretching strategy
+ if (strategy === 'all') {
+ var ratio = containerSize / this.cacheTotalSize;
+ var newSize;
+
+ if (ratio > 1) { //if the ratio is smaller than 1 then it means last cell is not completely visible (=there is nothing to stretch)
+ for (i = 0, ilen = this.visibleCellRanges.length / 2; i < ilen; i++) {
+ low = this.visibleCellRanges[2 * i];
+ high = this.visibleCellRanges[2 * i + 1];
+ cur = low;
+ while (cur <= high) {
+ if (i === ilen - 1 && cur === high) {
+ this.cache[cur] += remainingSize;
+ }
+ else {
+ newSize = Math.floor(ratio * this.cache[cur]);
+ remainingSize -= newSize - this.cache[cur];
+ this.cache[cur] = newSize;
+ }
+ cur++;
+ }
+ }
+
+ }
+ }
+ else if (strategy === 'last') {
+ if (remainingSize > 0) {
+ this.cache[this.visibleCellRanges[this.visibleCellRanges.length - 1]] += remainingSize;
+ }
+ }
+}
+
+WalkontableColumnStrategy.prototype.getContainerSize = function (proposedWidth) {
+ var containerSize = typeof this.containerSizeFn === 'function' ? this.containerSizeFn(proposedWidth) : this.containerSizeFn;
+ if (containerSize === void 0 || containerSize === null || containerSize < 1) {
+ containerSize = Infinity;
+ }
+ return containerSize;
+};
+
+WalkontableColumnStrategy.prototype.getSize = function (index) {
+ return this.cache[index];
+};
+
+WalkontableColumnStrategy.prototype.isSorted = function (cellRanges) {
+ for (var i = 0, ilen = cellRanges.length; i < ilen; i++) {
+ if (i > 0) {
+ if (cellRanges[i - 1] > cellRanges[i]) {
+ return false;
+ }
+ }
+ }
+ return true;
+};
function Walkontable(settings) {
var self = this,
originalHeaders = [];
@@ -6159,7 +6295,7 @@ WalkontableSelection.prototype.draw = function (selectionsOnly) {
var offsetRow = this.instance.getSetting('offsetRow')
, lastVisibleRow = offsetRow + this.instance.getSetting('displayRows') - 1
, offsetColumn = this.instance.getSetting('offsetColumn')
- , lastVisibleColumn = offsetColumn + this.instance.getSetting('displayColumns') - 1;
+ , lastVisibleColumn = this.instance.wtTable.getLastVisibleColumn();
if (this.selected.length) {
corners = this.getCorners();
@@ -6230,7 +6366,7 @@ function WalkontableSettings(instance, settings) {
data: void 0,
offsetRow: 0,
offsetColumn: 0,
- frozenColumns: null,
+ rowHeaders: null,
columnHeaders: null, //this must be a function in format: function (col, TH) {}
totalRows: void 0,
totalColumns: void 0,
@@ -6238,12 +6374,7 @@ function WalkontableSettings(instance, settings) {
height: null,
cellRenderer: function (row, column, TD) {
var cellData = that.getSetting('data', row, column);
- if (cellData !== void 0) {
- that.instance.wtDom.avoidInnerHTML(TD, cellData);
- }
- else {
- this.wtDom.empty(TD);
- }
+ that.instance.wtDom.avoidInnerHTML(TD, cellData === void 0 || cellData === null ? '' : cellData);
},
columnWidth: 50,
selections: null,
@@ -6361,29 +6492,6 @@ WalkontableSettings.prototype.displayRows = function () {
}
};
-WalkontableSettings.prototype.displayColumns = function () {
- var estimated
- , calculated;
-
- if (this.settings['width']) {
- if (typeof this.settings['width'] !== 'number') {
- throw new Error('Walkontable width parameter must be a number (' + typeof this.settings['width'] + ' given)');
- }
- estimated = Math.ceil(this.settings['width'] / 50); //silly assumption but should be fine for now
- calculated = this.getSetting('totalColumns') - this.getSetting('offsetColumn');
- if (calculated < 0) {
- this.update('offsetColumn', Math.max(0, this.getSetting('totalColumns') - estimated));
- return estimated;
- }
- else {
- return Math.min(estimated, calculated);
- }
- }
- else {
- return this.getSetting('totalColumns');
- }
-};
-
WalkontableSettings.prototype.viewportRows = function () {
if (this.instance.wtTable.visibilityEdgeRow !== null) {
return this.instance.wtTable.visibilityEdgeRow - this.instance.wtTable.visibilityStartRow;
@@ -6395,7 +6503,7 @@ WalkontableSettings.prototype.viewportColumns = function () {
if (this.instance.wtTable.visibilityEdgeColumn !== null) {
return this.instance.wtTable.visibilityEdgeColumn - this.instance.wtTable.visibilityStartColumn;
}
- return this.getSetting('displayColumns');
+ return this.instance.wtTable.getLastVisibleColumn();
};
var FLAG_VISIBLE_HORIZONTAL = 0x1; // 000001
var FLAG_VISIBLE_VERTICAL = 0x2; // 000010
@@ -6524,20 +6632,29 @@ WalkontableTable.prototype.refreshHiderDimensions = function () {
};
WalkontableTable.prototype.refreshStretching = function () {
- var stretchH = this.instance.getSetting('stretchH')
- , totalColumns = this.instance.getSetting('totalColumns')
- , displayColumns = this.instance.getSetting('displayColumns')
- , displayTds = Math.min(displayColumns, totalColumns)
- , offsetColumn = this.instance.getSetting('offsetColumn')
- , frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0;
+ var instance = this.instance
+ , stretchH = instance.getSetting('stretchH')
+ , scrollH = instance.getSetting('scrollH')
+ , scrollbarWidth = instance.getSetting('scrollbarWidth')
+ , totalColumns = instance.getSetting('totalColumns')
+ , offsetColumn = instance.getSetting('offsetColumn')
+ , rowHeaders = instance.getSetting('rowHeaders')
+ , rowHeaderWidth = rowHeaders && rowHeaders.length ? 50 : 0
+ , containerWidth = this.instance.getSetting('width') - rowHeaderWidth;
- if (!this.instance.hasSetting('columnWidth')) {
- return;
- }
+ var containerWidthFn = function (cacheWidth) {
+ if (scrollH === 'scroll' || (scrollH === 'auto' && cacheWidth > containerWidth)) {
+ return containerWidth - scrollbarWidth;
+ }
+ return containerWidth;
+ };
+
+ var columnWidthFn = function (index) {
+ return instance.getSetting('columnWidth', index)
+ };
if (stretchH === 'hybrid') {
- if (this.instance.wtScroll.wtScrollbarH.visible) {
+ if (offsetColumn > 0) {
stretchH = 'last';
}
else {
@@ -6545,110 +6662,41 @@ WalkontableTable.prototype.refreshStretching = function () {
}
}
- var TD;
- if (this.instance.wtTable.TBODY.firstChild && this.instance.wtTable.TBODY.firstChild.firstChild) {
- TD = this.instance.wtTable.TBODY.firstChild.firstChild;
- }
- else if (this.instance.wtTable.THEAD.firstChild && this.instance.wtTable.THEAD.firstChild.firstChild) {
- TD = this.instance.wtTable.THEAD.firstChild.firstChild;
- }
-
- if (frozenColumnsCount) {
- TD = TD.nextSibling;
- }
-
- if (!TD) {
- return;
- }
-
- var cellOffset = this.instance.wtDom.offset(TD)
- , tableOffset = this.instance.wtTable.tableOffset
- , rowHeaderWidth = cellOffset.left - tableOffset.left
- , widths = []
- , widthSum = 0
- , c;
- for (c = 0; c < displayTds; c++) {
- widths.push(this.instance.getSetting('columnWidth', offsetColumn + c));
- widthSum += widths[c];
- }
- var domWidth = widthSum + rowHeaderWidth;
-
- if (stretchH === 'all' || stretchH === 'last') {
- var containerWidth = this.instance.getSetting('width');
- if (this.instance.wtScroll.wtScrollbarV.visible) {
- containerWidth -= this.instance.getSetting('scrollbarWidth');
- }
-
- var diff = containerWidth - domWidth;
- if (diff > 0) {
- if (stretchH === 'all') {
- var newWidth;
- var remainingDiff = diff;
- var ratio = diff / widthSum;
-
- for (c = 0; c < displayTds; c++) {
- if (widths[c]) {
- if (c === displayTds - 1) {
- newWidth = widths[c] + remainingDiff;
- }
- else {
- newWidth = widths[c] + Math.floor(ratio * widths[c]);
- remainingDiff -= Math.floor(ratio * widths[c]);
- }
- }
- widths[c] = newWidth;
- }
- }
- else {
- if (widths[widths.length - 1]) {
- widths[widths.length - 1] = widths[widths.length - 1] + diff;
- }
- }
- }
- }
-
- for (c = 0; c < displayTds; c++) {
- if (widths[c]) {
- this.COLGROUP.childNodes[c + frozenColumnsCount].style.width = widths[c] + 'px';
- }
- else {
- this.COLGROUP.childNodes[c + frozenColumnsCount].style.width = '';
- }
- }
+ this.columnStrategy = new WalkontableColumnStrategy(containerWidthFn, totalColumns ? [offsetColumn, totalColumns - 1] : null, columnWidthFn, stretchH);
};
WalkontableTable.prototype.adjustAvailableNodes = function () {
var instance = this.instance
, totalRows = instance.getSetting('totalRows')
- , totalColumns = instance.getSetting('totalColumns')
, displayRows = instance.getSetting('displayRows')
- , displayColumns = instance.getSetting('displayColumns')
, displayTds
- , frozenColumns = instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0
+ , rowHeaders = instance.getSetting('rowHeaders')
+ , rowHeadersCount = rowHeaders ? rowHeaders.length : 0
, TR
, c;
displayRows = Math.min(displayRows, totalRows);
- displayTds = Math.min(displayColumns, totalColumns);
+ this.refreshStretching();
+ displayTds = this.columnStrategy.cacheLength;
+
//adjust COLGROUP
- while (this.colgroupChildrenLength < displayTds + frozenColumnsCount) {
+ while (this.colgroupChildrenLength < displayTds + rowHeadersCount) {
this.COLGROUP.appendChild(document.createElement('COL'));
this.colgroupChildrenLength++;
}
- while (this.colgroupChildrenLength > displayTds + frozenColumnsCount) {
+ while (this.colgroupChildrenLength > displayTds + rowHeadersCount) {
this.COLGROUP.removeChild(this.COLGROUP.lastChild);
this.colgroupChildrenLength--;
}
//adjust THEAD
if (this.instance.hasSetting('columnHeaders')) {
- while (this.theadChildrenLength < displayTds + frozenColumnsCount) {
+ while (this.theadChildrenLength < displayTds + rowHeadersCount) {
this.THEAD.firstChild.appendChild(document.createElement('TH'));
this.theadChildrenLength++;
}
- while (this.theadChildrenLength > displayTds + frozenColumnsCount) {
+ while (this.theadChildrenLength > displayTds + rowHeadersCount) {
this.THEAD.firstChild.removeChild(this.THEAD.firstChild.lastChild);
this.theadChildrenLength--;
}
@@ -6657,7 +6705,7 @@ WalkontableTable.prototype.adjustAvailableNodes = function () {
//adjust TBODY
while (this.tbodyChildrenLength < displayRows) {
TR = document.createElement('TR');
- for (c = 0; c < frozenColumnsCount; c++) {
+ for (c = 0; c < rowHeadersCount; c++) {
TR.appendChild(document.createElement('TH'));
}
this.TBODY.appendChild(TR);
@@ -6672,13 +6720,13 @@ WalkontableTable.prototype.adjustAvailableNodes = function () {
var trChildrenLength;
for (var r = 0, rlen = TRs.length; r < rlen; r++) {
trChildrenLength = TRs[r].childNodes.length;
- while (trChildrenLength < displayTds + frozenColumnsCount) {
+ while (trChildrenLength < displayTds + rowHeadersCount) {
var TD = document.createElement('TD');
TD.setAttribute('tabindex', 10000); //http://www.barryvan.com.au/2009/01/onfocus-and-onblur-for-divs-in-fx/; 32767 is max tabindex for IE7,8
TRs[r].appendChild(TD);
trChildrenLength++;
}
- while (trChildrenLength > displayTds + frozenColumnsCount) {
+ while (trChildrenLength > displayTds + rowHeadersCount) {
TRs[r].removeChild(TRs[r].lastChild);
trChildrenLength--;
}
@@ -6706,26 +6754,23 @@ WalkontableTable.prototype._doDraw = function () {
, offsetRow = this.instance.getSetting('offsetRow')
, offsetColumn = this.instance.getSetting('offsetColumn')
, totalRows = this.instance.getSetting('totalRows')
- , totalColumns = this.instance.getSetting('totalColumns')
, displayRows = this.instance.getSetting('displayRows')
- , displayColumns = this.instance.getSetting('displayColumns')
- , displayTds
- , frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0
+ , displayTds = this.columnStrategy.cacheLength
+ , rowHeaders = this.instance.getSetting('rowHeaders')
+ , rowHeadersCount = rowHeaders ? rowHeaders.length : 0
, TR
, TH
, TD
, cellData;
displayRows = Math.min(displayRows, totalRows);
- displayTds = Math.min(displayColumns, totalColumns);
//draw COLGROUP
for (c = 0; c < this.colgroupChildrenLength; c++) {
- if (c < frozenColumnsCount) {
+ if (c < rowHeadersCount) {
this.wtDom.addClass(this.COLGROUP.childNodes[c], 'rowHeader');
- if (typeof frozenColumns[c] === "function") {
- frozenColumns[c](null, this.COLGROUP.childNodes[c])
+ if (typeof rowHeaders[c] === "function") {
+ rowHeaders[c](null, this.COLGROUP.childNodes[c])
}
}
else {
@@ -6733,14 +6778,12 @@ WalkontableTable.prototype._doDraw = function () {
}
}
- this.refreshStretching(); //needed here or otherwise scrollbarH is not shown
-
//draw THEAD
- if (frozenColumnsCount && this.instance.hasSetting('columnHeaders')) {
- for (c = 0; c < frozenColumnsCount; c++) {
+ if (rowHeadersCount && this.instance.hasSetting('columnHeaders')) {
+ for (c = 0; c < rowHeadersCount; c++) {
TH = this.THEAD.childNodes[0].childNodes[c];
- if (typeof frozenColumns[c] === "function") {
- frozenColumns[c](null, TH);
+ if (typeof rowHeaders[c] === "function") {
+ rowHeaders[c](null, TH);
}
else {
this.wtDom.empty(TH);
@@ -6751,10 +6794,12 @@ WalkontableTable.prototype._doDraw = function () {
}
}
- if (this.instance.hasSetting('columnHeaders')) {
- for (c = 0; c < displayTds; c++) {
- this.instance.getSetting('columnHeaders', offsetColumn + c, this.THEAD.childNodes[0].childNodes[frozenColumnsCount + c]);
+ var columnHeaders = this.instance.hasSetting('columnHeaders');
+ for (c = 0; c < displayTds; c++) {
+ if (columnHeaders) {
+ this.instance.getSetting('columnHeaders', offsetColumn + c, this.THEAD.childNodes[0].childNodes[rowHeadersCount + c]);
}
+ this.COLGROUP.childNodes[c + rowHeadersCount].style.width = this.columnStrategy.getSize(offsetColumn + c) + 'px';
}
//draw TBODY
@@ -6763,14 +6808,14 @@ WalkontableTable.prototype._doDraw = function () {
this.visibilityStartColumn = offsetColumn;
for (r = 0; r < displayRows; r++) {
TR = this.TBODY.childNodes[r];
- for (c = 0; c < frozenColumnsCount; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes
+ for (c = 0; c < rowHeadersCount; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes
TH = TR.childNodes[c];
- cellData = typeof frozenColumns[c] === "function" ? frozenColumns[c](offsetRow + r, TH) : frozenColumns[c];
+ cellData = typeof rowHeaders[c] === "function" ? rowHeaders[c](offsetRow + r, TH) : rowHeaders[c];
if (cellData !== void 0) {
this.wtDom.avoidInnerHTML(TH, cellData);
}
/*
- we can assume that frozenColumns[c] function took care of inserting content into TH
+ we can assume that rowHeaders[c] function took care of inserting content into TH
else {
TH.innerHTML = '';
}*/
@@ -6785,7 +6830,7 @@ WalkontableTable.prototype._doDraw = function () {
break;
}
else {
- TD = TR.childNodes[c + frozenColumnsCount];
+ TD = TR.childNodes[c + rowHeadersCount];
TD.className = '';
TD.removeAttribute('style');
this.instance.getSetting('cellRenderer', offsetRow + r, offsetColumn + c, TD);
@@ -6825,7 +6870,6 @@ WalkontableTable.prototype._doDraw = function () {
WalkontableTable.prototype.refreshPositions = function (selectionsOnly) {
this.instance.wtScroll.refreshScrollbars();
this.refreshHiderDimensions();
- this.refreshStretching();
this.refreshSelections(selectionsOnly);
};
@@ -6853,8 +6897,6 @@ WalkontableTable.prototype.recalcViewportCells = function () {
WalkontableTable.prototype.isCellVisible = function (r, c, TD) {
var out = 0
- , scrollV = this.instance.getSetting('scrollV')
- , scrollH = this.instance.getSetting('scrollH')
, cellOffset = this.wtDom.offset(TD)
, tableOffset = this.tableOffset
, innerOffsetTop = cellOffset.top - tableOffset.top
@@ -6924,12 +6966,12 @@ WalkontableTable.prototype.getCell = function (coords) {
if (coords[1] < offsetColumn) {
return -3; //column before viewport
}
- else if (coords[1] > offsetColumn + this.instance.getSetting('displayColumns') - 1) {
+ else if (coords[1] > this.getLastVisibleColumn()) {
return -4; //column after viewport
}
else {
- var frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = (frozenColumns ? frozenColumns.length : 0)
+ var rowHeaders = this.instance.getSetting('rowHeaders')
+ , rowHeadersCount = (rowHeaders ? rowHeaders.length : 0)
, tr = this.TBODY.childNodes[coords[0] - offsetRow];
if (typeof tr === "undefined") { //this block is only needed in async mode
@@ -6937,19 +6979,23 @@ WalkontableTable.prototype.getCell = function (coords) {
tr = this.TBODY.childNodes[coords[0] - offsetRow];
}
- return tr.childNodes[coords[1] - offsetColumn + frozenColumnsCount];
+ return tr.childNodes[coords[1] - offsetColumn + rowHeadersCount];
}
}
};
WalkontableTable.prototype.getCoords = function (TD) {
- var frozenColumns = this.instance.getSetting('frozenColumns')
- , frozenColumnsCount = frozenColumns ? frozenColumns.length : 0;
+ var rowHeaders = this.instance.getSetting('rowHeaders')
+ , rowHeadersCount = rowHeaders ? rowHeaders.length : 0;
return [
this.wtDom.prevSiblings(TD.parentNode).length + this.instance.getSetting('offsetRow'),
- TD.cellIndex + this.instance.getSetting('offsetColumn') - frozenColumnsCount
+ TD.cellIndex + this.instance.getSetting('offsetColumn') - rowHeadersCount
];
};
+
+WalkontableTable.prototype.getLastVisibleColumn = function () {
+ return this.columnStrategy.visibleCellRanges[this.columnStrategy.visibleCellRanges.length - 1]
+};
function WalkontableWheel(instance) {
var that = this;
diff --git a/package.json b/package.json
index 84e10dc8b0b..0555c4a39d9 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"url": "https://github.com/warpech/jquery-handsontable/issues"
},
"author": "Marcin Warpechowski ",
- "version": "0.8.17",
+ "version": "0.8.18",
"devDependencies": {
"grunt": "~0.4.0",
"grunt-replace": "~0.4.0",