From 8df5de9a345a650a8e588bc4fe3da5d3e2075972 Mon Sep 17 00:00:00 2001 From: Marcin Warpechowski Date: Mon, 18 Feb 2013 19:44:11 +0100 Subject: [PATCH] bugfix: clear currentRowClassName and currentColumnClassName when refreshing selections --- CHANGELOG.md | 5 ++ dist/jquery.handsontable.full.css | 2 +- dist/jquery.handsontable.full.js | 142 ++++++++++++++++++----------- jquery.handsontable.css | 2 +- jquery.handsontable.js | 142 ++++++++++++++++++----------- src/3rdparty/walkontable.js | 143 ++++++++++++++++++------------ src/tableView.js | 6 +- 7 files changed, 271 insertions(+), 171 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d444bafbdd3..ece01a7d276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## HEAD + +Bugfix: +- clear currentRowClassName and currentColumnClassName when refreshing selections ([#398](https://github.com/warpech/jquery-handsontable/issues/398)) - thanks @jaycfields + ## [0.8.4](https://github.com/warpech/jquery-handsontable/tree/v0.8.4) (Feb 17, 2013) Features: diff --git a/dist/jquery.handsontable.full.css b/dist/jquery.handsontable.full.css index 66c392f0bcb..989498e666e 100644 --- a/dist/jquery.handsontable.full.css +++ b/dist/jquery.handsontable.full.css @@ -6,7 +6,7 @@ * Licensed under the MIT license. * http://handsontable.com/ * - * Date: Sun Feb 17 2013 21:43:36 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:34:23 GMT+0100 (Central European Standard Time) */ .handsontable { diff --git a/dist/jquery.handsontable.full.js b/dist/jquery.handsontable.full.js index eb3a3ccd50f..9bcef1756bc 100644 --- a/dist/jquery.handsontable.full.js +++ b/dist/jquery.handsontable.full.js @@ -6,7 +6,7 @@ * Licensed under the MIT license. * http://handsontable.com/ * - * Date: Sun Feb 17 2013 21:43:36 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:34:23 GMT+0100 (Central European Standard Time) */ /*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */ @@ -2170,11 +2170,11 @@ Handsontable.TableView = function (instance) { cellRenderer: function (row, column, TD) { that.applyCellTypeMethod('renderer', TD, {row: row, col: column}, instance.getDataAtCell(row, column)); }, - currentRowClassName: settings.currentRowClassName, - currentColumnClassName: settings.currentColClassName, selections: { current: { className: 'current', + highlightRowClassName: settings.currentRowClassName, + highlightColumnClassName: settings.currentColClassName, border: { width: 2, color: '#5292F7', @@ -2186,6 +2186,8 @@ Handsontable.TableView = function (instance) { }, area: { className: 'area', + highlightRowClassName: settings.currentRowClassName, + highlightColumnClassName: settings.currentColClassName, border: { width: 1, color: '#89AFF9', @@ -4222,14 +4224,14 @@ Handsontable.PluginHooks.push('afterGetColWidth', htManualColumnResize.getColWid /** * walkontable 0.1 * - * Date: Wed Jan 23 2013 01:38:29 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:31:46 GMT+0100 (Central European Standard Time) */ function WalkontableBorder(instance, settings) { //reference to instance this.instance = instance; this.settings = settings; - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; this.main = document.createElement("div"); this.main.style.position = 'absolute'; @@ -4409,11 +4411,11 @@ function Walkontable(settings) { //bootstrap from settings this.wtSettings = new WalkontableSettings(this, settings); + this.wtDom = new WalkontableDom(this); this.wtTable = new WalkontableTable(this); this.wtScroll = new WalkontableScroll(this); this.wtWheel = new WalkontableWheel(this); this.wtEvent = new WalkontableEvent(this); - this.wtDom = new WalkontableDom(); //find original headers if (this.wtTable.THEAD.childNodes.length && this.wtTable.THEAD.childNodes[0].childNodes.length) { @@ -4505,7 +4507,11 @@ Walkontable.prototype.getSetting = function (key, param1, param2, param3) { Walkontable.prototype.hasSetting = function (key) { return this.wtSettings.has(key); }; -function WalkontableDom() { +function WalkontableDom(instance) { + if (instance) { + this.instance = instance; + } + this.tdCache = []; } //goes up the DOM tree (including given element) until it finds an element that matches the nodeName @@ -4529,6 +4535,50 @@ WalkontableDom.prototype.prevSiblings = function (elem) { return out; }; +WalkontableDom.prototype.tdHasClass = function (trIndex, tdIndex, cls) { + return !!(this.tdCache[trIndex] && this.tdCache[trIndex][tdIndex] && this.tdCache[trIndex][tdIndex][cls]); +}; + +WalkontableDom.prototype.tdAddClass = function (trIndex, tdIndex, cls) { + if (!this.tdHasClass(trIndex, tdIndex, cls)) { + if (!this.tdCache[trIndex]) { + this.tdCache[trIndex] = []; + } + if (!this.tdCache[trIndex][tdIndex]) { + this.tdCache[trIndex][tdIndex] = {}; + this.tdCache[trIndex][tdIndex]._node = this.instance.wtTable.getCell([trIndex + this.instance.getSetting('offsetRow'), tdIndex + this.instance.getSetting('offsetColumn')]); + } + this.tdCache[trIndex][tdIndex]._node.className += " " + cls; + this.tdCache[trIndex][tdIndex][cls] = true; + } +}; + +WalkontableDom.prototype.tdRemoveClass = function (trIndex, tdIndex, cls) { + if (this.tdHasClass(trIndex, tdIndex, cls)) { + var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + this.tdCache[trIndex][tdIndex]._node.className = this.tdCache[trIndex][tdIndex]._node.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); //last 2 replaces do right trim (see http://blog.stevenlevithan.com/archives/faster-trim-javascript) + this.tdCache[trIndex][tdIndex][cls] = false; + } +}; + +WalkontableDom.prototype.tdResetCache = function () { + for (var i in this.tdCache) { + if (this.tdCache.hasOwnProperty(i)) { + for (var j in this.tdCache[i]) { + if (this.tdCache[i].hasOwnProperty(j)) { + for (var k in this.tdCache[i][j]) { + if (this.tdCache[i][j].hasOwnProperty(k)) { + if (k !== '_node') { + this.tdCache[i][j][k] = false; + } + } + } + } + } + } + } +}; + //http://snipplr.com/view/3561/addclass-removeclass-hasclass/ WalkontableDom.prototype.hasClass = function (ele, cls) { return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); @@ -4668,7 +4718,7 @@ function WalkontableEvent(instance) { //reference to instance this.instance = instance; - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; var dblClickOrigin = [null, null, null, null] , dblClickTimeout = null; @@ -5201,25 +5251,10 @@ function WalkontableSelection(instance, settings) { this.instance = instance; this.settings = settings; this.selected = []; + this.wtDom = new WalkontableDom(this.instance); if (settings.border) { this.border = new WalkontableBorder(instance, settings); } - this.onAdd = function (coords) { - var TD = instance.wtTable.getCell(coords); - if (typeof TD === 'object') { - if (settings.className) { - instance.wtDom.addClass(TD, settings.className); - } - } - }; - /*this.onRemove = function (coords) { - var TD = instance.wtTable.getCell(coords); - if (typeof TD === 'object') { - if (settings.className) { - instance.wtDom.removeClass(TD, settings.className); - } - } - };*/ } WalkontableSelection.prototype.add = function (coords) { @@ -5289,44 +5324,41 @@ WalkontableSelection.prototype.getCorners = function () { }; WalkontableSelection.prototype.draw = function (selectionsOnly) { - var TDs, TD, i, ilen, corners, r, c; + var corners, r, c; - ilen = this.selected.length; - if (ilen) { + 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; + + if (this.selected.length) { corners = this.getCorners(); - 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 - , currentRowClassName = this.instance.getSetting('currentRowClassName') - , currentColumnClassName = this.instance.getSetting('currentColumnClassName'); for (r = offsetRow; r <= lastVisibleRow; r++) { for (c = offsetColumn; c <= lastVisibleColumn; c++) { - TD = this.instance.wtTable.getCell([r, c]); if (r >= corners[0] && r <= corners[2] && c >= corners[1] && c <= corners[3]) { //selected cell - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - this.onAdd([r, c], TD); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.className); } else if (r >= corners[0] && r <= corners[2]) { //selection is in this row - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - currentRowClassName && this.instance.wtDom.addClass(TD, currentRowClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.highlightRowClassName && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } else if (c >= corners[1] && c <= corners[3]) { //selection is in this column - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.addClass(TD, currentColumnClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } else { //no selection - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } } } @@ -5334,12 +5366,16 @@ WalkontableSelection.prototype.draw = function (selectionsOnly) { this.border && this.border.appear(corners); } else { - if (selectionsOnly && this.settings.className) { - TDs = this.instance.wtTable.TABLE.getElementsByTagName('TD'); - for (i = 0, ilen = TDs.length; i < ilen; i++) { - this.instance.wtDom.removeClass(TDs[i], this.settings.className); + if (selectionsOnly) { + for (r = 0; r <= lastVisibleRow - offsetRow; r++) { + for (c = 0; c <= lastVisibleColumn - offsetColumn; c++) { + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r, c, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r, c, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r, c, this.settings.className); + } } } + this.border && this.border.disappear(); } }; @@ -5621,7 +5657,7 @@ function WalkontableTable(instance) { //reference to instance this.instance = instance; this.TABLE = this.instance.getSetting('table'); - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; this.wtDom.removeTextNodes(this.TABLE); this.hasEmptyCellProblem = ($.browser.msie && (parseInt($.browser.version, 10) <= 7)); @@ -5869,9 +5905,6 @@ WalkontableTable.prototype.adjustAvailableNodes = function () { for (c = 0; c < frozenColumnsCount; c++) { TR.appendChild(document.createElement('TH')); } - for (c = 0; c < displayTds; c++) { - TR.appendChild(document.createElement('TD')); - } this.TBODY.appendChild(TR); this.tbodyChildrenLength++; } @@ -5998,6 +6031,7 @@ WalkontableTable.prototype._doDraw = function () { var visibilityFullRow = null; var visibilityFullColumn = null; + this.wtDom.tdResetCache(); for (c = 0; c < displayTds; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes if (this.visibilityEdgeColumn !== null && offsetColumn + c > this.visibilityEdgeColumn) { diff --git a/jquery.handsontable.css b/jquery.handsontable.css index 37bcdcd8bd2..4b8e2e924f0 100644 --- a/jquery.handsontable.css +++ b/jquery.handsontable.css @@ -6,7 +6,7 @@ * Licensed under the MIT license. * http://handsontable.com/ * - * Date: Sun Feb 17 2013 21:43:36 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:34:23 GMT+0100 (Central European Standard Time) */ .handsontable { diff --git a/jquery.handsontable.js b/jquery.handsontable.js index d03b35b31a9..45f40f896ec 100644 --- a/jquery.handsontable.js +++ b/jquery.handsontable.js @@ -6,7 +6,7 @@ * Licensed under the MIT license. * http://handsontable.com/ * - * Date: Sun Feb 17 2013 21:43:36 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:34:23 GMT+0100 (Central European Standard Time) */ /*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */ @@ -2170,11 +2170,11 @@ Handsontable.TableView = function (instance) { cellRenderer: function (row, column, TD) { that.applyCellTypeMethod('renderer', TD, {row: row, col: column}, instance.getDataAtCell(row, column)); }, - currentRowClassName: settings.currentRowClassName, - currentColumnClassName: settings.currentColClassName, selections: { current: { className: 'current', + highlightRowClassName: settings.currentRowClassName, + highlightColumnClassName: settings.currentColClassName, border: { width: 2, color: '#5292F7', @@ -2186,6 +2186,8 @@ Handsontable.TableView = function (instance) { }, area: { className: 'area', + highlightRowClassName: settings.currentRowClassName, + highlightColumnClassName: settings.currentColClassName, border: { width: 1, color: '#89AFF9', @@ -4222,14 +4224,14 @@ Handsontable.PluginHooks.push('afterGetColWidth', htManualColumnResize.getColWid /** * walkontable 0.1 * - * Date: Wed Jan 23 2013 01:38:29 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:31:46 GMT+0100 (Central European Standard Time) */ function WalkontableBorder(instance, settings) { //reference to instance this.instance = instance; this.settings = settings; - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; this.main = document.createElement("div"); this.main.style.position = 'absolute'; @@ -4409,11 +4411,11 @@ function Walkontable(settings) { //bootstrap from settings this.wtSettings = new WalkontableSettings(this, settings); + this.wtDom = new WalkontableDom(this); this.wtTable = new WalkontableTable(this); this.wtScroll = new WalkontableScroll(this); this.wtWheel = new WalkontableWheel(this); this.wtEvent = new WalkontableEvent(this); - this.wtDom = new WalkontableDom(); //find original headers if (this.wtTable.THEAD.childNodes.length && this.wtTable.THEAD.childNodes[0].childNodes.length) { @@ -4505,7 +4507,11 @@ Walkontable.prototype.getSetting = function (key, param1, param2, param3) { Walkontable.prototype.hasSetting = function (key) { return this.wtSettings.has(key); }; -function WalkontableDom() { +function WalkontableDom(instance) { + if (instance) { + this.instance = instance; + } + this.tdCache = []; } //goes up the DOM tree (including given element) until it finds an element that matches the nodeName @@ -4529,6 +4535,50 @@ WalkontableDom.prototype.prevSiblings = function (elem) { return out; }; +WalkontableDom.prototype.tdHasClass = function (trIndex, tdIndex, cls) { + return !!(this.tdCache[trIndex] && this.tdCache[trIndex][tdIndex] && this.tdCache[trIndex][tdIndex][cls]); +}; + +WalkontableDom.prototype.tdAddClass = function (trIndex, tdIndex, cls) { + if (!this.tdHasClass(trIndex, tdIndex, cls)) { + if (!this.tdCache[trIndex]) { + this.tdCache[trIndex] = []; + } + if (!this.tdCache[trIndex][tdIndex]) { + this.tdCache[trIndex][tdIndex] = {}; + this.tdCache[trIndex][tdIndex]._node = this.instance.wtTable.getCell([trIndex + this.instance.getSetting('offsetRow'), tdIndex + this.instance.getSetting('offsetColumn')]); + } + this.tdCache[trIndex][tdIndex]._node.className += " " + cls; + this.tdCache[trIndex][tdIndex][cls] = true; + } +}; + +WalkontableDom.prototype.tdRemoveClass = function (trIndex, tdIndex, cls) { + if (this.tdHasClass(trIndex, tdIndex, cls)) { + var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + this.tdCache[trIndex][tdIndex]._node.className = this.tdCache[trIndex][tdIndex]._node.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); //last 2 replaces do right trim (see http://blog.stevenlevithan.com/archives/faster-trim-javascript) + this.tdCache[trIndex][tdIndex][cls] = false; + } +}; + +WalkontableDom.prototype.tdResetCache = function () { + for (var i in this.tdCache) { + if (this.tdCache.hasOwnProperty(i)) { + for (var j in this.tdCache[i]) { + if (this.tdCache[i].hasOwnProperty(j)) { + for (var k in this.tdCache[i][j]) { + if (this.tdCache[i][j].hasOwnProperty(k)) { + if (k !== '_node') { + this.tdCache[i][j][k] = false; + } + } + } + } + } + } + } +}; + //http://snipplr.com/view/3561/addclass-removeclass-hasclass/ WalkontableDom.prototype.hasClass = function (ele, cls) { return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); @@ -4668,7 +4718,7 @@ function WalkontableEvent(instance) { //reference to instance this.instance = instance; - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; var dblClickOrigin = [null, null, null, null] , dblClickTimeout = null; @@ -5201,25 +5251,10 @@ function WalkontableSelection(instance, settings) { this.instance = instance; this.settings = settings; this.selected = []; + this.wtDom = new WalkontableDom(this.instance); if (settings.border) { this.border = new WalkontableBorder(instance, settings); } - this.onAdd = function (coords) { - var TD = instance.wtTable.getCell(coords); - if (typeof TD === 'object') { - if (settings.className) { - instance.wtDom.addClass(TD, settings.className); - } - } - }; - /*this.onRemove = function (coords) { - var TD = instance.wtTable.getCell(coords); - if (typeof TD === 'object') { - if (settings.className) { - instance.wtDom.removeClass(TD, settings.className); - } - } - };*/ } WalkontableSelection.prototype.add = function (coords) { @@ -5289,44 +5324,41 @@ WalkontableSelection.prototype.getCorners = function () { }; WalkontableSelection.prototype.draw = function (selectionsOnly) { - var TDs, TD, i, ilen, corners, r, c; + var corners, r, c; - ilen = this.selected.length; - if (ilen) { + 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; + + if (this.selected.length) { corners = this.getCorners(); - 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 - , currentRowClassName = this.instance.getSetting('currentRowClassName') - , currentColumnClassName = this.instance.getSetting('currentColumnClassName'); for (r = offsetRow; r <= lastVisibleRow; r++) { for (c = offsetColumn; c <= lastVisibleColumn; c++) { - TD = this.instance.wtTable.getCell([r, c]); if (r >= corners[0] && r <= corners[2] && c >= corners[1] && c <= corners[3]) { //selected cell - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - this.onAdd([r, c], TD); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.className); } else if (r >= corners[0] && r <= corners[2]) { //selection is in this row - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - currentRowClassName && this.instance.wtDom.addClass(TD, currentRowClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.highlightRowClassName && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } else if (c >= corners[1] && c <= corners[3]) { //selection is in this column - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.addClass(TD, currentColumnClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } else { //no selection - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } } } @@ -5334,12 +5366,16 @@ WalkontableSelection.prototype.draw = function (selectionsOnly) { this.border && this.border.appear(corners); } else { - if (selectionsOnly && this.settings.className) { - TDs = this.instance.wtTable.TABLE.getElementsByTagName('TD'); - for (i = 0, ilen = TDs.length; i < ilen; i++) { - this.instance.wtDom.removeClass(TDs[i], this.settings.className); + if (selectionsOnly) { + for (r = 0; r <= lastVisibleRow - offsetRow; r++) { + for (c = 0; c <= lastVisibleColumn - offsetColumn; c++) { + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r, c, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r, c, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r, c, this.settings.className); + } } } + this.border && this.border.disappear(); } }; @@ -5621,7 +5657,7 @@ function WalkontableTable(instance) { //reference to instance this.instance = instance; this.TABLE = this.instance.getSetting('table'); - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; this.wtDom.removeTextNodes(this.TABLE); this.hasEmptyCellProblem = ($.browser.msie && (parseInt($.browser.version, 10) <= 7)); @@ -5869,9 +5905,6 @@ WalkontableTable.prototype.adjustAvailableNodes = function () { for (c = 0; c < frozenColumnsCount; c++) { TR.appendChild(document.createElement('TH')); } - for (c = 0; c < displayTds; c++) { - TR.appendChild(document.createElement('TD')); - } this.TBODY.appendChild(TR); this.tbodyChildrenLength++; } @@ -5998,6 +6031,7 @@ WalkontableTable.prototype._doDraw = function () { var visibilityFullRow = null; var visibilityFullColumn = null; + this.wtDom.tdResetCache(); for (c = 0; c < displayTds; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes if (this.visibilityEdgeColumn !== null && offsetColumn + c > this.visibilityEdgeColumn) { diff --git a/src/3rdparty/walkontable.js b/src/3rdparty/walkontable.js index b14b52f70c6..66cb870ad5b 100644 --- a/src/3rdparty/walkontable.js +++ b/src/3rdparty/walkontable.js @@ -1,14 +1,14 @@ /** * walkontable 0.1 * - * Date: Wed Jan 23 2013 01:38:29 GMT+0100 (Central European Standard Time) + * Date: Mon Feb 18 2013 19:31:46 GMT+0100 (Central European Standard Time) */ function WalkontableBorder(instance, settings) { //reference to instance this.instance = instance; this.settings = settings; - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; this.main = document.createElement("div"); this.main.style.position = 'absolute'; @@ -188,11 +188,11 @@ function Walkontable(settings) { //bootstrap from settings this.wtSettings = new WalkontableSettings(this, settings); + this.wtDom = new WalkontableDom(this); this.wtTable = new WalkontableTable(this); this.wtScroll = new WalkontableScroll(this); this.wtWheel = new WalkontableWheel(this); this.wtEvent = new WalkontableEvent(this); - this.wtDom = new WalkontableDom(); //find original headers if (this.wtTable.THEAD.childNodes.length && this.wtTable.THEAD.childNodes[0].childNodes.length) { @@ -284,7 +284,11 @@ Walkontable.prototype.getSetting = function (key, param1, param2, param3) { Walkontable.prototype.hasSetting = function (key) { return this.wtSettings.has(key); }; -function WalkontableDom() { +function WalkontableDom(instance) { + if (instance) { + this.instance = instance; + } + this.tdCache = []; } //goes up the DOM tree (including given element) until it finds an element that matches the nodeName @@ -308,6 +312,50 @@ WalkontableDom.prototype.prevSiblings = function (elem) { return out; }; +WalkontableDom.prototype.tdHasClass = function (trIndex, tdIndex, cls) { + return !!(this.tdCache[trIndex] && this.tdCache[trIndex][tdIndex] && this.tdCache[trIndex][tdIndex][cls]); +}; + +WalkontableDom.prototype.tdAddClass = function (trIndex, tdIndex, cls) { + if (!this.tdHasClass(trIndex, tdIndex, cls)) { + if (!this.tdCache[trIndex]) { + this.tdCache[trIndex] = []; + } + if (!this.tdCache[trIndex][tdIndex]) { + this.tdCache[trIndex][tdIndex] = {}; + this.tdCache[trIndex][tdIndex]._node = this.instance.wtTable.getCell([trIndex + this.instance.getSetting('offsetRow'), tdIndex + this.instance.getSetting('offsetColumn')]); + } + this.tdCache[trIndex][tdIndex]._node.className += " " + cls; + this.tdCache[trIndex][tdIndex][cls] = true; + } +}; + +WalkontableDom.prototype.tdRemoveClass = function (trIndex, tdIndex, cls) { + if (this.tdHasClass(trIndex, tdIndex, cls)) { + var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + this.tdCache[trIndex][tdIndex]._node.className = this.tdCache[trIndex][tdIndex]._node.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); //last 2 replaces do right trim (see http://blog.stevenlevithan.com/archives/faster-trim-javascript) + this.tdCache[trIndex][tdIndex][cls] = false; + } +}; + +WalkontableDom.prototype.tdResetCache = function () { + for (var i in this.tdCache) { + if (this.tdCache.hasOwnProperty(i)) { + for (var j in this.tdCache[i]) { + if (this.tdCache[i].hasOwnProperty(j)) { + for (var k in this.tdCache[i][j]) { + if (this.tdCache[i][j].hasOwnProperty(k)) { + if (k !== '_node') { + this.tdCache[i][j][k] = false; + } + } + } + } + } + } + } +}; + //http://snipplr.com/view/3561/addclass-removeclass-hasclass/ WalkontableDom.prototype.hasClass = function (ele, cls) { return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); @@ -447,7 +495,7 @@ function WalkontableEvent(instance) { //reference to instance this.instance = instance; - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; var dblClickOrigin = [null, null, null, null] , dblClickTimeout = null; @@ -980,25 +1028,10 @@ function WalkontableSelection(instance, settings) { this.instance = instance; this.settings = settings; this.selected = []; + this.wtDom = new WalkontableDom(this.instance); if (settings.border) { this.border = new WalkontableBorder(instance, settings); } - this.onAdd = function (coords) { - var TD = instance.wtTable.getCell(coords); - if (typeof TD === 'object') { - if (settings.className) { - instance.wtDom.addClass(TD, settings.className); - } - } - }; - /*this.onRemove = function (coords) { - var TD = instance.wtTable.getCell(coords); - if (typeof TD === 'object') { - if (settings.className) { - instance.wtDom.removeClass(TD, settings.className); - } - } - };*/ } WalkontableSelection.prototype.add = function (coords) { @@ -1068,44 +1101,41 @@ WalkontableSelection.prototype.getCorners = function () { }; WalkontableSelection.prototype.draw = function (selectionsOnly) { - var TDs, TD, i, ilen, corners, r, c; + var corners, r, c; - ilen = this.selected.length; - if (ilen) { + 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; + + if (this.selected.length) { corners = this.getCorners(); - 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 - , currentRowClassName = this.instance.getSetting('currentRowClassName') - , currentColumnClassName = this.instance.getSetting('currentColumnClassName'); for (r = offsetRow; r <= lastVisibleRow; r++) { for (c = offsetColumn; c <= lastVisibleColumn; c++) { - TD = this.instance.wtTable.getCell([r, c]); if (r >= corners[0] && r <= corners[2] && c >= corners[1] && c <= corners[3]) { //selected cell - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - this.onAdd([r, c], TD); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.className); } else if (r >= corners[0] && r <= corners[2]) { //selection is in this row - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - currentRowClassName && this.instance.wtDom.addClass(TD, currentRowClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.highlightRowClassName && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } else if (c >= corners[1] && c <= corners[3]) { //selection is in this column - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.addClass(TD, currentColumnClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdAddClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } else { //no selection - currentRowClassName && this.instance.wtDom.removeClass(TD, currentRowClassName); - currentColumnClassName && this.instance.wtDom.removeClass(TD, currentColumnClassName); - this.instance.wtDom.removeClass(TD, this.settings.className); + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r - offsetRow, c - offsetColumn, this.settings.className); } } } @@ -1113,12 +1143,16 @@ WalkontableSelection.prototype.draw = function (selectionsOnly) { this.border && this.border.appear(corners); } else { - if (selectionsOnly && this.settings.className) { - TDs = this.instance.wtTable.TABLE.getElementsByTagName('TD'); - for (i = 0, ilen = TDs.length; i < ilen; i++) { - this.instance.wtDom.removeClass(TDs[i], this.settings.className); + if (selectionsOnly) { + for (r = 0; r <= lastVisibleRow - offsetRow; r++) { + for (c = 0; c <= lastVisibleColumn - offsetColumn; c++) { + this.settings.highlightRowClassName && this.wtDom.tdRemoveClass(r, c, this.settings.highlightRowClassName); + this.settings.highlightColumnClassName && this.wtDom.tdRemoveClass(r, c, this.settings.highlightColumnClassName); + this.settings.className && this.wtDom.tdRemoveClass(r, c, this.settings.className); + } } } + this.border && this.border.disappear(); } }; @@ -1400,7 +1434,7 @@ function WalkontableTable(instance) { //reference to instance this.instance = instance; this.TABLE = this.instance.getSetting('table'); - this.wtDom = new WalkontableDom(); + this.wtDom = this.instance.wtDom; this.wtDom.removeTextNodes(this.TABLE); this.hasEmptyCellProblem = ($.browser.msie && (parseInt($.browser.version, 10) <= 7)); @@ -1648,9 +1682,6 @@ WalkontableTable.prototype.adjustAvailableNodes = function () { for (c = 0; c < frozenColumnsCount; c++) { TR.appendChild(document.createElement('TH')); } - for (c = 0; c < displayTds; c++) { - TR.appendChild(document.createElement('TD')); - } this.TBODY.appendChild(TR); this.tbodyChildrenLength++; } @@ -1777,6 +1808,7 @@ WalkontableTable.prototype._doDraw = function () { var visibilityFullRow = null; var visibilityFullColumn = null; + this.wtDom.tdResetCache(); for (c = 0; c < displayTds; c++) { //in future use nextSibling; http://jsperf.com/nextsibling-vs-indexed-childnodes if (this.visibilityEdgeColumn !== null && offsetColumn + c > this.visibilityEdgeColumn) { @@ -1827,15 +1859,8 @@ WalkontableTable.prototype.refreshPositions = function (selectionsOnly) { }; WalkontableTable.prototype.refreshSelections = function (selectionsOnly) { - var r, i, ilen, TDs - , currentRowClassName = this.instance.getSetting("currentRowClassName") - , currentColumnClassName = this.instance.getSetting("currentColumnClassName"); + var r; if (this.instance.selections) { - TDs = this.instance.wtTable.TABLE.getElementsByTagName('TD'); - for (i = 0, ilen = TDs.length; i < ilen; i++) { - this.instance.wtDom.removeClass(TDs[i], currentRowClassName); - this.instance.wtDom.removeClass(TDs[i], currentColumnClassName); - } for (r in this.instance.selections) { if (this.instance.selections.hasOwnProperty(r)) { this.instance.selections[r].draw(selectionsOnly); diff --git a/src/tableView.js b/src/tableView.js index bc6ec24982d..4de071336b6 100644 --- a/src/tableView.js +++ b/src/tableView.js @@ -135,11 +135,11 @@ Handsontable.TableView = function (instance) { cellRenderer: function (row, column, TD) { that.applyCellTypeMethod('renderer', TD, {row: row, col: column}, instance.getDataAtCell(row, column)); }, - currentRowClassName: settings.currentRowClassName, - currentColumnClassName: settings.currentColClassName, selections: { current: { className: 'current', + highlightRowClassName: settings.currentRowClassName, + highlightColumnClassName: settings.currentColClassName, border: { width: 2, color: '#5292F7', @@ -151,6 +151,8 @@ Handsontable.TableView = function (instance) { }, area: { className: 'area', + highlightRowClassName: settings.currentRowClassName, + highlightColumnClassName: settings.currentColClassName, border: { width: 1, color: '#89AFF9',