diff --git a/.gitignore b/.gitignore
index 34977ee7df5..f6963c5503f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
node_modules
-.idea
\ No newline at end of file
+.idea
+_SpecRunner.html
+.grunt
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000000..057ad4a1966
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+language: node_js
+
+node_js:
+ - "0.8"
+
+before_script:
+ - "export DISPLAY=:99.0"
+ - "sh -e /etc/init.d/xvfb start"
+ - "npm install -g grunt-cli"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da3473bb257..a7924ca8238 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## HEAD
+
+Features:
+- this may be a **breaking change** for some applications. Now the third parameter to the `alter` method tells the amount of rows/columns to be inserted/removed. This adds more consistency to the API. ([#368](https://github.com/warpech/jquery-handsontable/issues/368))
+- merged pull request [#474](https://github.com/warpech/jquery-handsontable/pull/474) - Travis-CI integration. The tests don't pass yet but don't worry about it yet. It is a work in progress on complete test automation. Making all tests pass on Travis CI headless browser may take few more days or weeks :)
+
+Bugfix:
+- again, this may be a **breaking change** for some applications. Fix very long standing inconsistency. Restored original `setDataAtCell` requirement of the second parameter to be a column number (as described in [README.md](https://github.com/warpech/jquery-handsontable) since always). If you happen to experience an error in `setDataAtCell` after upgrade, change your usage of this method to the new method `setDataAtRowProp` ([#284](https://github.com/warpech/jquery-handsontable/pull/284))
+- new methods `setDataAtRowProp` and `getDataAtRowProp` that set/get data according to the property name in data source. See [README.md](https://github.com/warpech/jquery-handsontable) for clarification
+- merged pull request [#266](https://github.com/warpech/jquery-handsontable/pull/266) - fix countCols for arrays with column settings
+
## [0.8.10](https://github.com/warpech/jquery-handsontable/tree/v0.8.10) (Mar 7, 2013)
Bugfix:
diff --git a/Gruntfile.js b/Gruntfile.js
index 21dd09c87ff..0a4dcfd39a5 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -21,72 +21,94 @@
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
- concat: {
- dist: {
- src: [
- 'tmp/intro.js',
-
- 'tmp/core.js',
- 'src/tableView.js',
- 'src/helpers.js',
- 'src/fillHandle.js',
- 'src/undoRedo.js',
- 'src/selectionPoint.js',
-
- 'src/renderers/textRenderer.js',
- 'src/renderers/autocompleteRenderer.js',
- 'src/renderers/checkboxRenderer.js',
- 'src/renderers/numericRenderer.js',
-
- 'src/editors/textEditor.js',
- 'src/editors/autocompleteEditor.js',
- 'src/editors/checkboxEditor.js',
- 'src/editors/dateEditor.js',
+ meta: {
+ src: [
+ 'tmp/core.js',
+ 'src/tableView.js',
+ 'src/helpers.js',
+ 'src/fillHandle.js',
+ 'src/undoRedo.js',
+ 'src/selectionPoint.js',
- 'src/cellTypes.js',
+ 'src/renderers/textRenderer.js',
+ 'src/renderers/autocompleteRenderer.js',
+ 'src/renderers/checkboxRenderer.js',
+ 'src/renderers/numericRenderer.js',
- 'src/pluginHooks.js',
- 'src/plugins/autoColumnSize.js',
- 'src/plugins/columnSorting.js',
- 'src/plugins/contextMenu.js',
- 'src/plugins/legacy.js',
- 'src/plugins/manualColumnMove.js',
- 'src/plugins/manualColumnResize.js',
+ 'src/editors/textEditor.js',
+ 'src/editors/autocompleteEditor.js',
+ 'src/editors/checkboxEditor.js',
+ 'src/editors/dateEditor.js',
+
+ 'src/cellTypes.js',
- 'src/3rdparty/jquery.autoresize.js',
- 'src/3rdparty/sheetclip.js',
- 'src/3rdparty/walkontable.js',
- 'src/3rdparty/copypaste.js',
+ 'src/pluginHooks.js',
+ 'src/plugins/autoColumnSize.js',
+ 'src/plugins/columnSorting.js',
+ 'src/plugins/contextMenu.js',
+ 'src/plugins/legacy.js',
+ 'src/plugins/manualColumnMove.js',
+ 'src/plugins/manualColumnResize.js',
- 'src/outro.js'
- ],
- dest: 'jquery.handsontable.js'
+ 'src/3rdparty/jquery.autoresize.js',
+ 'src/3rdparty/sheetclip.js',
+ 'src/3rdparty/walkontable.js',
+ 'src/3rdparty/copypaste.js',
+ ],
+ vendor: [
+ 'lib/bootstrap-typeahead.js',
+ 'lib/numeral.js',
+ 'lib/jQuery-contextMenu/jquery.contextMenu.js'
+ // seems to have no effect when turned off on contextmenu.html
+ //'lib/jQuery-contextMenu/jquery.ui.position.js'
+ ]
+ },
+
+ concat: {
+ dist: {
+ files: {
+ 'jquery.handsontable.js': [
+ 'tmp/intro.js',
+ '<%= meta.src %>',
+ 'src/outro.js'
+ ]
+ }
},
full_js: {
- src: [
- 'jquery.handsontable.js',
- 'lib/bootstrap-typeahead.js',
- 'lib/numeral.js',
- 'lib/jQuery-contextMenu/jquery.contextMenu.js'
- //'lib/jQuery-contextMenu/jquery.ui.position.js' //seems to have no effect when turned off on contextmenu.html
- ],
- dest: 'dist/jquery.handsontable.full.js'
+ files: {
+ 'dist/jquery.handsontable.full.js': [
+ 'jquery.handsontable.js',
+ '<%= meta.vendor %>'
+ ]
+ }
},
full_css: {
- src: [
- 'jquery.handsontable.css',
- 'lib/jQuery-contextMenu/jquery.contextMenu.css'
- ],
- dest: 'dist/jquery.handsontable.full.css'
+ files: {
+ 'dist/jquery.handsontable.full.css': [
+ 'jquery.handsontable.css',
+ 'lib/jQuery-contextMenu/jquery.contextMenu.css'
+ ]
+ }
}
},
+
watch: {
- files: ['src/*', 'src/editors/*', 'src/plugins/*', 'src/renderers/*', 'src/3rdparty/*', 'src/css/*', 'lib/*'],
+ files: [
+ 'src/*',
+ 'src/editors/*',
+ 'src/plugins/*',
+ 'src/renderers/*',
+ 'src/3rdparty/*',
+ 'src/css/*',
+ 'lib/*'
+ ],
tasks: ['replace', 'concat', 'clean']
},
+
clean: {
dist: ['tmp']
},
+
replace: {
dist: {
options: {
@@ -101,14 +123,41 @@ module.exports = function (grunt) {
'jquery.handsontable.css': 'src/css/jquery.handsontable.css'
}
}
+ },
+
+ jasmine: {
+ src: [
+ 'lib/jquery.min.js',
+ // '<%= meta.src %>',
+ 'jquery.handsontable.js',
+ 'lib/bootstrap-typeahead.js',
+ 'lib/numeral.js',
+ 'lib/jQuery-contextMenu/jquery.contextMenu.js',
+ 'test/jasmine/spec/SpecHelper.js'
+ ],
+ options: {
+ specs: [
+ 'test/jasmine/spec/*Spec.js',
+ 'test/jasmine/spec/*/*Spec.js'
+ ],
+ template: 'test/JqueryHandsontableRunner.tmpl',
+ templateOptions: {
+ css: [
+ 'lib/jQuery-contextMenu/jquery.contextMenu.css',
+ 'jquery.handsontable.css',
+ ]
+ }
+ }
}
});
// Default task.
grunt.registerTask('default', ['replace', 'concat', 'clean']);
+ grunt.registerTask('test', ['default', 'jasmine']);
grunt.loadNpmTasks('grunt-replace');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-contrib-jasmine');
};
\ No newline at end of file
diff --git a/README.md b/README.md
index 5e9d61a1541..312f3877e0d 100644
--- a/README.md
+++ b/README.md
@@ -58,8 +58,10 @@ Thanks for understanding!
handsontable('updateSettings', options) | Method | Use it if you need to change configuration after initialization
handsontable('loadData', data) | Method | Reset all cells in the grid to contain data from the `data` array
handsontable('render') | Method | Rerender the table
- handsontable('setDataAtCell', row, col, value) | Method | Set new value to a cell. To change many cells at once, pass an array of changes in format [[row, col, value], ...] as the only parameter
- handsontable('getDataAtCell', row, col) | Method | Return cell value at `row`, `col`
+ handsontable('setDataAtCell', row, col, value) | Method | Set new value to a cell. To change many cells at once, pass an array of changes in format [[row, col, value], ...] as the only parameter. Col is the index of **visible** column (note that if columns were reordered, the current order will be used)
+ handsontable('setDataAtRowProp', row, prop, value) | Method | Same as above, except instead of `col`, you provide name of the object property (eq. [0, 'first.name', 'Jennifer'])
+ handsontable('getDataAtCell', row, col) | Method | Return cell value at `row`, `col`. Col is the index of **visible** column (note that if columns were reordered, the current order will be used)
+ handsontable('getDataAtRowProp', row, prop) | Method | Same as above, except instead of `col`, you provide name of the object property (eq. [0, 'first.name'])
handsontable('countRows') | Method | Return total number of rows in the grid
handsontable('countCols') | Method | Return total number of columns in the grid
handsontable('rowOffset') | Method | Return index of first visible row
@@ -69,10 +71,10 @@ Thanks for understanding!
handsontable('clear') | Method | Empty all cells
handsontable('clearUndo') | Method | Clear undo history
handsontable('getData', [r, c, r2, c2]) | Method | Return the current data object (the same that was passed by `data` configuration option or `loadData` method). Optionally you can provide cell range `r`, `c`, `r2`, `c2` to get only a fragment of grid data
- handsontable('alter', 'insert_row', index) | Method | Insert new row above the row at given index
- handsontable('alter', 'insert_col', index) | Method | Insert new column before the column at given index
- handsontable('alter', 'remove_row', index, [toIndex]) | Method | Remove the row at given index [optionally to another index]
- handsontable('alter', 'remove_col', index, [toIndex]) | Method | Remove the column at given index [optionally to another index]
+ handsontable('alter', 'insert_row', amount) | Method | Insert new row(s) above the row at given `index`. Default `amount` equals 1
+ handsontable('alter', 'insert_col', amount) | Method | Insert new column(s) before the column at given `index`. Default `amount` equals 1
+ handsontable('alter', 'remove_row', index, amount) | Method | Remove the row(s) at given `index`. Default `amount` equals 1
+ handsontable('alter', 'remove_col', index, amount) | Method | Remove the column(s) at given `index`. Default `amount` equals 1
handsontable('getCell', row, col) | Method | Return <td> element for given `row,col`
handsontable('getCellMeta', row, col) | Method | Return cell properties for given `row`, `col` coordinates
handsontable('selectCell', r, c, [r2, c2, scrollToSelection=true]) | Method | Select cell `r,c` or range finishing at `r2,c2`. By default, viewport will be scrolled to selection
@@ -131,8 +133,8 @@ The table below presents configuration options that are interpreted by `handsont
`currentColClassName` | string | _undefined_ | Class name for all visible columns in current selection
`onSelection` | function(`r`, `c`, `r2`, `c2`) | _undefined_ | Callback fired before one or more cells is selected. You can call `updateSettings` from inside, e.g. if you want to disable fillHandle for a specific cell. Parameters:
`r` selection start row
`c` selection start column
`r2` selection end column
`c2` selection end column
`onSelectionByProp` | function(`r`, `p`, `r2`, `p2`) | _undefined_ | The same as above, but data source object property name is used instead of the column number
- `onBeforeChange` | function(`changes`) | _undefined_ | Callback fired before one or more cells is changed. Its main purpose is to validate the input. Parameters:
`changes` is a 2D array containing information about each of the edited cells `[ [row, col, oldVal, newVal], ... ]`. You can disregard a single change by setting `changes[i][3]` to false, or cancel all edit by returning false.
- `onChange` | function(`changes`, `source`) | _undefined_ | Callback fired after one or more cells is changed. Its main use case is to save the input. Parameters:
`changes` is a 2D array containing information about each of the edited cells `[ [row, col, oldVal, newVal], ... ]`.
`source` is one of the strings: `"alter"`, `"empty"`, `"edit"`, `"populateFromArray"`, `"loadData"`, `"autofill"`, `"paste"`.
Note: for performance reasone, the `changes` array is null for `"loadData"` source.
+ `onBeforeChange` | function(`changes`) | _undefined_ | Callback fired before one or more cells is changed. Its main purpose is to validate the input. Parameters:
`changes` is a 2D array containing information about each of the edited cells `[ [row, prop, oldVal, newVal], ... ]`. You can disregard a single change by setting `changes[i][3]` to false, or cancel all edit by returning false.
+ `onChange` | function(`changes`, `source`) | _undefined_ | Callback fired after one or more cells is changed. Its main use case is to save the input. Parameters:
`changes` is a 2D array containing information about each of the edited cells `[ [row, prop, oldVal, newVal], ... ]`.
`source` is one of the strings: `"alter"`, `"empty"`, `"edit"`, `"populateFromArray"`, `"loadData"`, `"autofill"`, `"paste"`.
Note: for performance reasone, the `changes` array is null for `"loadData"` source.
`onCopyLimit` | function() | _undefined_ | Callback fired if `copyRowsLimit` or `copyColumnsLimit` was reached. Callback parameters are: `selectedRowsCount`, `selectedColsCount`, `copyRowsLimit`, `copyColsLimit`
## Similar projects
@@ -169,3 +171,9 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+## Travis CI status
+
+[![Build Status](https://travis-ci.org/warpech/jquery-handsontable.png?branch=master)](https://travis-ci.org/warpech/jquery-handsontable)
+
+**Note:** Travis CI build fails because Handsontable test suite is not fully adjusted to run in Travis CI headless browser. Integration with Travis CI is a work in progress since Handsontable 0.8.11. It may take few more weeks until we are fully compatible. See pull request [#474](https://github.com/warpech/jquery-handsontable/pull/474) for more details.
\ No newline at end of file
diff --git a/dist/jquery.handsontable.full.css b/dist/jquery.handsontable.full.css
index e5aaf0f7900..a1095425af5 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: Thu Mar 07 2013 10:13:24 GMT+0100 (Central European Standard Time)
+ * Date: Fri Mar 08 2013 01:14:54 GMT+0100 (Central European Standard Time)
*/
.handsontable {
diff --git a/dist/jquery.handsontable.full.js b/dist/jquery.handsontable.full.js
index 9600b3c3c1c..88e1ec919be 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: Thu Mar 07 2013 10:13:24 GMT+0100 (Central European Standard Time)
+ * Date: Fri Mar 08 2013 01:14:54 GMT+0100 (Central European Standard Time)
*/
/*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */
@@ -26,7 +26,7 @@ var Handsontable = { //class namespace
Handsontable.Core = function (rootElement, settings) {
this.rootElement = rootElement;
- var priv, datamap, grid, selection, editproxy, autofill, validate, self = this;
+ var priv, datamap, grid, selection, editproxy, autofill, self = this;
priv = {
settings: {},
@@ -142,9 +142,9 @@ Handsontable.Core = function (rootElement, settings) {
/**
* Creates row at the bottom of the data array
- * @param {Object} [coords] Optional. Coords of the cell before which the new row will be inserted
+ * @param {Number} [index] Optional. Index of the row before which the new row will be inserted
*/
- createRow: function (coords) {
+ createRow: function (index) {
var row;
if (priv.dataType === 'array') {
row = [];
@@ -155,7 +155,7 @@ Handsontable.Core = function (rootElement, settings) {
else {
row = $.extend(true, {}, datamap.getSchema());
}
- if (!coords || coords.row >= self.countRows()) {
+ if (typeof index !== 'number' || index >= self.countRows()) {
if (priv.settings.onCreateRow) {
priv.settings.onCreateRow(self.countRows(), row);
}
@@ -163,23 +163,23 @@ Handsontable.Core = function (rootElement, settings) {
}
else {
if (priv.settings.onCreateRow) {
- priv.settings.onCreateRow(coords.row, row);
+ priv.settings.onCreateRow(index, row);
}
- priv.settings.data.splice(coords.row, 0, row);
+ priv.settings.data.splice(index, 0, row);
}
self.forceFullRender = true; //used when data was changed
},
/**
* Creates col at the right of the data array
- * @param {Object} [coords] Optional. Coords of the cell before which the new column will be inserted
+ * @param {Object} [index] Optional. Index of the column before which the new column will be inserted
*/
- createCol: function (coords) {
+ createCol: function (index) {
if (priv.dataType === 'object' || priv.settings.columns) {
throw new Error("Cannot create new column. When data source in an object, you can only have as much columns as defined in first data row, data schema or in the 'columns' setting");
}
var r = 0, rlen = self.countRows();
- if (!coords || coords.col >= self.countCols()) {
+ if (typeof index !== 'number' || index >= self.countCols()) {
for (; r < rlen; r++) {
if (typeof priv.settings.data[r] === 'undefined') {
priv.settings.data[r] = [];
@@ -189,47 +189,45 @@ Handsontable.Core = function (rootElement, settings) {
}
else {
for (; r < rlen; r++) {
- priv.settings.data[r].splice(coords.col, 0, '');
+ priv.settings.data[r].splice(index, 0, '');
}
}
self.forceFullRender = true; //used when data was changed
},
/**
- * Removes row at the bottom of the data array
- * @param {Object} [coords] Optional. Coords of the cell which row will be removed
- * @param {Object} [toCoords] Required if coords is defined. Coords of the cell until which all rows will be removed
+ * Removes row from the data array
+ * @param {Number} [index] Optional. Index of the row to be removed. If not provided, the last row will be removed
+ * @param {Number} [amount] Optional. Amount of the rows to be removed. If not provided, one row will be removed
*/
- removeRow: function (coords, toCoords) {
- if (!coords || coords.row === self.countRows() - 1) {
- priv.settings.data.pop();
+ removeRow: function (index, amount) {
+ if (!amount) {
+ amount = 1;
}
- else {
- priv.settings.data.splice(coords.row, toCoords.row - coords.row + 1);
+ if (typeof index !== 'number') {
+ index = -amount;
}
+ priv.settings.data.splice(index, amount);
self.forceFullRender = true; //used when data was changed
},
/**
- * Removes col at the right of the data array
- * @param {Object} [coords] Optional. Coords of the cell which col will be removed
- * @param {Object} [toCoords] Required if coords is defined. Coords of the cell until which all cols will be removed
+ * Removes column from the data array
+ * @param {Number} [index] Optional. Index of the column to be removed. If not provided, the last column will be removed
+ * @param {Number} [amount] Optional. Amount of the columns to be removed. If not provided, one column will be removed
*/
- removeCol: function (coords, toCoords) {
+ removeCol: function (index, amount) {
if (priv.dataType === 'object' || priv.settings.columns) {
throw new Error("cannot remove column with object data source or columns option specified");
}
- var r = 0;
- if (!coords || coords.col === self.countCols() - 1) {
- for (; r < self.countRows(); r++) {
- priv.settings.data[r].pop();
- }
+ if (!amount) {
+ amount = 1;
}
- else {
- var howMany = toCoords.col - coords.col + 1;
- for (; r < self.countRows(); r++) {
- priv.settings.data[r].splice(coords.col, howMany);
- }
+ if (typeof index !== 'number') {
+ index = -amount;
+ }
+ for (var r = 0, rlen = self.countRows(); r < rlen; r++) {
+ priv.settings.data[r].splice(index, amount);
}
self.forceFullRender = true; //used when data was changed
},
@@ -340,22 +338,29 @@ Handsontable.Core = function (rootElement, settings) {
grid = {
/**
- * Alter grid
+ * Inserts or removes rows and columns
* @param {String} action Possible values: "insert_row", "insert_col", "remove_row", "remove_col"
- * @param {Object} coords
- * @param {Object} [toCoords] Required only for actions "remove_row" and "remove_col"
+ * @param {Number} index
+ * @param {Number} amount
*/
- alter: function (action, coords, toCoords) {
- var oldData, newData, changes, r, rlen, c, clen;
+ alter: function (action, index, amount) {
+ var oldData, newData, changes, r, rlen, c, clen, delta;
oldData = $.extend(true, [], datamap.getAll());
switch (action) {
case "insert_row":
- if (self.countRows() < priv.settings.maxRows) {
- datamap.createRow(coords);
- if (priv.selStart.exists() && priv.selStart.row() >= coords.row) {
- priv.selStart.row(priv.selStart.row() + 1);
- selection.transformEnd(1, 0); //will call render() internally
+ if (!amount) {
+ amount = 1;
+ }
+ delta = 0;
+ while (delta < amount && self.countRows() < priv.settings.maxRows) {
+ datamap.createRow(index);
+ delta++;
+ }
+ if (delta) {
+ if (priv.selStart.exists() && priv.selStart.row() >= index) {
+ priv.selStart.row(priv.selStart.row() + delta);
+ selection.transformEnd(delta, 0); //will call render() internally
}
else {
selection.refreshBorders(); //it will call render and prepare methods
@@ -364,11 +369,18 @@ Handsontable.Core = function (rootElement, settings) {
break;
case "insert_col":
- if (self.countCols() < priv.settings.maxCols) {
- datamap.createCol(coords);
- if (priv.selStart.exists() && priv.selStart.col() >= coords.col) {
- priv.selStart.col(priv.selStart.col() + 1);
- selection.transformEnd(0, 1); //will call render() internally
+ if (!amount) {
+ amount = 1;
+ }
+ delta = 0;
+ while (delta < amount && self.countCols() < priv.settings.maxCols) {
+ datamap.createCol(index);
+ delta++;
+ }
+ if (delta) {
+ if (priv.selStart.exists() && priv.selStart.col() >= index) {
+ priv.selStart.col(priv.selStart.col() + delta);
+ selection.transformEnd(0, delta); //will call render() internally
}
else {
selection.refreshBorders(); //it will call render and prepare methods
@@ -377,16 +389,20 @@ Handsontable.Core = function (rootElement, settings) {
break;
case "remove_row":
- datamap.removeRow(coords, toCoords);
+ datamap.removeRow(index, amount);
grid.keepEmptyRows();
selection.refreshBorders(); //it will call render and prepare methods
break;
case "remove_col":
- datamap.removeCol(coords, toCoords);
+ datamap.removeCol(index, amount);
grid.keepEmptyRows();
selection.refreshBorders(); //it will call render and prepare methods
break;
+
+ default:
+ throw Error('There is no such action "' + action + '"');
+ break;
}
changes = [];
@@ -548,8 +564,7 @@ Handsontable.Core = function (rootElement, settings) {
break;
}
if (self.getCellMeta(current.row, current.col).isWritable) {
- var p = datamap.colToProp(current.col);
- setData.push([current.row, p, input[r][c]]);
+ setData.push([current.row, current.col, input[r][c]]);
}
current.col++;
if (end && c === clen - 1) {
@@ -828,7 +843,7 @@ Handsontable.Core = function (rootElement, settings) {
for (r = corners.TL.row; r <= corners.BR.row; r++) {
for (c = corners.TL.col; c <= corners.BR.col; c++) {
if (self.getCellMeta(r, c).isWritable) {
- changes.push([r, datamap.colToProp(c), '']);
+ changes.push([r, c, '']);
}
}
}
@@ -1233,7 +1248,7 @@ Handsontable.Core = function (rootElement, settings) {
Handsontable.PluginHooks.run(self, 'afterInit');
};
- validate = function (changes, source) {
+ function validateChanges(changes, source) {
var validated = $.Deferred();
var deferreds = [];
@@ -1311,7 +1326,7 @@ Handsontable.Core = function (rootElement, settings) {
});
return $.when(validated);
- };
+ }
var fireEvent = function (name, params) {
if (priv.settings.asyncRendering) {
@@ -1343,55 +1358,106 @@ Handsontable.Core = function (rootElement, settings) {
};
/**
- * Set data at given cell
- * @public
- * @param {Number|Array} row or array of changes in format [[row, col, value], ...]
- * @param {Number} prop
- * @param {String} value
- * @param {String} [source='edit'] String that identifies how this change will be described in changes array (useful in onChange callback)
+ * Internal function to apply changes. Called after validateChanges
+ * @param {Array} changes Array in form of [row, prop, oldValue, newValue]
+ * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
*/
- this.setDataAtCell = function (row, prop, value, source) {
- var changes, i, ilen;
+ function applyChanges(changes, source) {
+ var i
+ , ilen;
+
+ for (i = 0, ilen = changes.length; i < ilen; i++) {
+ if (priv.settings.minSpareRows) {
+ while (changes[i][0] > self.countRows() - 1) {
+ datamap.createRow();
+ }
+ }
+ if (priv.dataType === 'array' && priv.settings.minSpareCols) {
+ while (datamap.propToCol(changes[i][1]) > self.countCols() - 1) {
+ datamap.createCol();
+ }
+ }
+ datamap.set(changes[i][0], changes[i][1], changes[i][3]);
+ }
+ self.forceFullRender = true; //used when data was changed
+ grid.keepEmptyRows();
+ selection.refreshBorders();
+ fireEvent("datachange.handsontable", [changes, source || 'edit']);
+ }
- if (typeof row === "object") { //is it an array of changes
- changes = row;
+ function setDataInputToArray(arg0, arg1, arg2) {
+ if (typeof arg0 === "object") { //is it an array of changes
+ return arg0;
}
- else if ($.isPlainObject(value)) { //backwards compatibility
- changes = value;
+ else if ($.isPlainObject(arg2)) { //backwards compatibility
+ return value;
}
else {
- changes = [
- [row, prop, value]
+ return [
+ [arg0, arg1, arg2]
];
}
+ }
- for (i = 0, ilen = changes.length; i < ilen; i++) {
- changes[i].splice(2, 0, datamap.get(changes[i][0], changes[i][1])); //add old value at index 2
+ /**
+ * Set data at given cell
+ * @public
+ * @param {Number|Array} row or array of changes in format [[row, col, value], ...]
+ * @param {Number} col
+ * @param {String} value
+ * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
+ */
+ this.setDataAtCell = function (row, col, value, source) {
+ var input = setDataInputToArray(row, col, value)
+ , i
+ , ilen
+ , changes = []
+ , prop;
+
+ for (i = 0, ilen = input.length; i < ilen; i++) {
+ if (typeof input[i][1] !== 'number') {
+ throw new Exception('Method `setDataAtCell` accepts row and column number as its parameters. If you want to use object property name, use method `setDataAtRowProp`');
+ }
+ prop = datamap.colToProp(input[i][1]);
+ changes.push([
+ input[i][0],
+ prop,
+ datamap.get(input[i][0], prop),
+ input[i][2]
+ ]);
}
- validate(changes, source).then(function () { //when validate is resolved...
- for (i = 0, ilen = changes.length; i < ilen; i++) {
- row = changes[i][0];
- prop = changes[i][1];
- var col = datamap.propToCol(prop);
- value = changes[i][3];
+ validateChanges(changes, source).then(function () {
+ applyChanges(changes, source);
+ });
+ };
- if (priv.settings.minSpareRows) {
- while (row > self.countRows() - 1) {
- datamap.createRow();
- }
- }
- if (priv.dataType === 'array' && priv.settings.minSpareCols) {
- while (col > self.countCols() - 1) {
- datamap.createCol();
- }
- }
- datamap.set(row, prop, value);
- }
- self.forceFullRender = true; //used when data was changed
- grid.keepEmptyRows();
- selection.refreshBorders();
- fireEvent("datachange.handsontable", [changes, source || 'edit']);
+
+ /**
+ * Set data at given row property
+ * @public
+ * @param {Number|Array} row or array of changes in format [[row, prop, value], ...]
+ * @param {Number} prop
+ * @param {String} value
+ * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
+ */
+ this.setDataAtRowProp = function (row, prop, value, source) {
+ var input = setDataInputToArray(row, prop, value)
+ , i
+ , ilen
+ , changes = [];
+
+ for (i = 0, ilen = input.length; i < ilen; i++) {
+ changes.push([
+ input[i][0],
+ input[i][1],
+ datamap.get(input[i][0], input[i][1]),
+ input[i][2]
+ ]);
+ }
+
+ validateChanges(changes, source).then(function () {
+ applyChanges(changes, source);
});
};
@@ -1631,31 +1697,14 @@ Handsontable.Core = function (rootElement, settings) {
};
/**
- * Alters the grid
+ * Inserts or removes rows and columns
* @param {String} action See grid.alter for possible values
- * @param {Number} from
- * @param {Number} [to] Optional. Used only for actions "remove_row" and "remove_col"
+ * @param {Number} index
+ * @param {Number} amount
* @public
*/
- this.alter = function (action, from, to) {
- if (typeof to === "undefined") {
- to = from;
- }
- switch (action) {
- case "insert_row":
- case "remove_row":
- grid.alter(action, {row: from, col: 0}, {row: to, col: 0});
- break;
-
- case "insert_col":
- case "remove_col":
- grid.alter(action, {row: 0, col: from}, {row: 0, col: to});
- break;
-
- default:
- throw Error('There is no such action "' + action + '"');
- break;
- }
+ this.alter = function (action, index, amount) {
+ grid.alter(action, index, amount);
};
/**
@@ -1690,16 +1739,27 @@ Handsontable.Core = function (rootElement, settings) {
};
/**
- * Return cell value at `row`, `col`
+ * Return value at `row`, `col`
* @param {Number} row
* @param {Number} col
* @public
- * @return {string}
+ * @return value (mixed data type)
*/
this.getDataAtCell = function (row, col) {
return datamap.get(row, datamap.colToProp(col));
};
+ /**
+ * Return value at `row`, `prop`
+ * @param {Number} row
+ * @param {Number} prop
+ * @public
+ * @return value (mixed data type)
+ */
+ this.getDataAtRowProp = function (row, prop) {
+ return datamap.get(row, prop);
+ };
+
/**
* Returns cell meta data object corresponding to params row, col
* @param {Number} row
@@ -1847,8 +1907,11 @@ Handsontable.Core = function (rootElement, settings) {
if (priv.settings.columns && priv.settings.columns.length) {
return priv.settings.columns.length;
}
+ else if (priv.settings.data && priv.settings.data[0] && priv.settings.data[0].length) {
+ return priv.settings.data[0].length;
+ }
else {
- return Math.max((priv.settings.columns && priv.settings.columns.length) || 0, (priv.settings.data && priv.settings.data[0] && priv.settings.data[0].length) || 0);
+ return 0;
}
}
};
@@ -2314,7 +2377,7 @@ Handsontable.TableView.prototype.applyCellTypeMethod = function (methodName, td,
var prop = this.instance.colToProp(col)
, cellProperties = this.instance.getCellMeta(row, col);
if (cellProperties[methodName]) {
- return cellProperties[methodName](this.instance, td, row, col, prop, this.instance.getDataAtCell(row, col), cellProperties);
+ return cellProperties[methodName](this.instance, td, row, col, prop, this.instance.getDataAtRowProp(row, prop), cellProperties);
}
};
@@ -2407,7 +2470,7 @@ Handsontable.UndoRedo.prototype.undo = function () {
for (i = 0, ilen = setData.length; i < ilen; i++) {
setData[i].splice(3, 1);
}
- this.instance.setDataAtCell(setData, null, null, 'undo');
+ this.instance.setDataAtRowProp(setData, null, null, 'undo');
this.rev--;
}
};
@@ -2423,7 +2486,7 @@ Handsontable.UndoRedo.prototype.redo = function () {
for (i = 0, ilen = setData.length; i < ilen; i++) {
setData[i].splice(2, 1);
}
- this.instance.setDataAtCell(setData, null, null, 'redo');
+ this.instance.setDataAtRowProp(setData, null, null, 'redo');
}
};
@@ -2577,10 +2640,10 @@ Handsontable.CheckboxRenderer = function (instance, td, row, col, prop, value, c
var $input = $(td).find('input:first');
$input.mousedown(function (event) {
if (!$(this).is(':checked')) {
- instance.setDataAtCell(row, prop, cellProperties.checkedTemplate);
+ instance.setDataAtRowProp(row, prop, cellProperties.checkedTemplate);
}
else {
- instance.setDataAtCell(row, prop, cellProperties.uncheckedTemplate);
+ instance.setDataAtRowProp(row, prop, cellProperties.uncheckedTemplate);
}
event.stopPropagation(); //otherwise can confuse mousedown handler
});
@@ -2993,7 +3056,7 @@ HandsontableAutocompleteEditorClass.prototype.createElements = function () {
this.typeahead.matcher = function () {
return true;
};
-}
+};
HandsontableAutocompleteEditorClass.prototype._bindEvents = HandsontableTextEditorClass.prototype.bindEvents;
@@ -3031,7 +3094,7 @@ HandsontableAutocompleteEditorClass.prototype.bindEvents = function () {
});
this._bindEvents();
-}
+};
HandsontableAutocompleteEditorClass.prototype._bindTemporaryEvents = HandsontableTextEditorClass.prototype.bindTemporaryEvents;
@@ -3047,7 +3110,7 @@ HandsontableAutocompleteEditorClass.prototype.bindTemporaryEvents = function (td
cellProperties.onSelect(row, col, prop, this.$menu.find('.active').attr('data-value'), this.$menu.find('.active').index());
}
else {
- that.instance.setDataAtCell(row, prop, this.$menu.find('.active').attr('data-value'));
+ that.instance.setDataAtRowProp(row, prop, this.$menu.find('.active').attr('data-value'));
}
return output;
};
@@ -3086,7 +3149,7 @@ HandsontableAutocompleteEditorClass.prototype.bindTemporaryEvents = function (td
}
this.instance.view.wt.update('onCellDblClick', onDblClick);
-}
+};
HandsontableAutocompleteEditorClass.prototype._finishEditing = HandsontableTextEditorClass.prototype.finishEditing;
@@ -3095,7 +3158,7 @@ HandsontableAutocompleteEditorClass.prototype.finishEditing = function (isCancel
this.typeahead.select();
}
this._finishEditing(isCancelled, ctrlDown);
-}
+};
HandsontableAutocompleteEditorClass.prototype.isMenuExpanded = function () {
if (this.typeahead.$menu.is(":visible")) {
@@ -3104,7 +3167,7 @@ HandsontableAutocompleteEditorClass.prototype.isMenuExpanded = function () {
else {
return false;
}
-}
+};
/**
* Autocomplete editor
@@ -3126,11 +3189,11 @@ Handsontable.AutocompleteEditor = function (instance, td, row, col, prop, value,
}
};
function toggleCheckboxCell(instance, row, prop, cellProperties) {
- if (Handsontable.helper.stringify(instance.getDataAtCell(row, prop)) === Handsontable.helper.stringify(cellProperties.checkedTemplate)) {
- instance.setDataAtCell(row, prop, cellProperties.uncheckedTemplate);
+ if (Handsontable.helper.stringify(instance.getDataAtRowProp(row, prop)) === Handsontable.helper.stringify(cellProperties.checkedTemplate)) {
+ instance.setDataAtRowProp(row, prop, cellProperties.uncheckedTemplate);
}
else {
- instance.setDataAtCell(row, prop, cellProperties.checkedTemplate);
+ instance.setDataAtRowProp(row, prop, cellProperties.checkedTemplate);
}
}
diff --git a/jquery.handsontable.css b/jquery.handsontable.css
index 4409fbed1cb..fc95ae2eac0 100644
--- a/jquery.handsontable.css
+++ b/jquery.handsontable.css
@@ -6,7 +6,7 @@
* Licensed under the MIT license.
* http://handsontable.com/
*
- * Date: Thu Mar 07 2013 10:13:24 GMT+0100 (Central European Standard Time)
+ * Date: Fri Mar 08 2013 01:14:54 GMT+0100 (Central European Standard Time)
*/
.handsontable {
diff --git a/jquery.handsontable.js b/jquery.handsontable.js
index 9cd4e051d42..a1a43960aeb 100644
--- a/jquery.handsontable.js
+++ b/jquery.handsontable.js
@@ -6,7 +6,7 @@
* Licensed under the MIT license.
* http://handsontable.com/
*
- * Date: Thu Mar 07 2013 10:13:24 GMT+0100 (Central European Standard Time)
+ * Date: Fri Mar 08 2013 01:14:54 GMT+0100 (Central European Standard Time)
*/
/*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */
@@ -26,7 +26,7 @@ var Handsontable = { //class namespace
Handsontable.Core = function (rootElement, settings) {
this.rootElement = rootElement;
- var priv, datamap, grid, selection, editproxy, autofill, validate, self = this;
+ var priv, datamap, grid, selection, editproxy, autofill, self = this;
priv = {
settings: {},
@@ -142,9 +142,9 @@ Handsontable.Core = function (rootElement, settings) {
/**
* Creates row at the bottom of the data array
- * @param {Object} [coords] Optional. Coords of the cell before which the new row will be inserted
+ * @param {Number} [index] Optional. Index of the row before which the new row will be inserted
*/
- createRow: function (coords) {
+ createRow: function (index) {
var row;
if (priv.dataType === 'array') {
row = [];
@@ -155,7 +155,7 @@ Handsontable.Core = function (rootElement, settings) {
else {
row = $.extend(true, {}, datamap.getSchema());
}
- if (!coords || coords.row >= self.countRows()) {
+ if (typeof index !== 'number' || index >= self.countRows()) {
if (priv.settings.onCreateRow) {
priv.settings.onCreateRow(self.countRows(), row);
}
@@ -163,23 +163,23 @@ Handsontable.Core = function (rootElement, settings) {
}
else {
if (priv.settings.onCreateRow) {
- priv.settings.onCreateRow(coords.row, row);
+ priv.settings.onCreateRow(index, row);
}
- priv.settings.data.splice(coords.row, 0, row);
+ priv.settings.data.splice(index, 0, row);
}
self.forceFullRender = true; //used when data was changed
},
/**
* Creates col at the right of the data array
- * @param {Object} [coords] Optional. Coords of the cell before which the new column will be inserted
+ * @param {Object} [index] Optional. Index of the column before which the new column will be inserted
*/
- createCol: function (coords) {
+ createCol: function (index) {
if (priv.dataType === 'object' || priv.settings.columns) {
throw new Error("Cannot create new column. When data source in an object, you can only have as much columns as defined in first data row, data schema or in the 'columns' setting");
}
var r = 0, rlen = self.countRows();
- if (!coords || coords.col >= self.countCols()) {
+ if (typeof index !== 'number' || index >= self.countCols()) {
for (; r < rlen; r++) {
if (typeof priv.settings.data[r] === 'undefined') {
priv.settings.data[r] = [];
@@ -189,47 +189,45 @@ Handsontable.Core = function (rootElement, settings) {
}
else {
for (; r < rlen; r++) {
- priv.settings.data[r].splice(coords.col, 0, '');
+ priv.settings.data[r].splice(index, 0, '');
}
}
self.forceFullRender = true; //used when data was changed
},
/**
- * Removes row at the bottom of the data array
- * @param {Object} [coords] Optional. Coords of the cell which row will be removed
- * @param {Object} [toCoords] Required if coords is defined. Coords of the cell until which all rows will be removed
+ * Removes row from the data array
+ * @param {Number} [index] Optional. Index of the row to be removed. If not provided, the last row will be removed
+ * @param {Number} [amount] Optional. Amount of the rows to be removed. If not provided, one row will be removed
*/
- removeRow: function (coords, toCoords) {
- if (!coords || coords.row === self.countRows() - 1) {
- priv.settings.data.pop();
+ removeRow: function (index, amount) {
+ if (!amount) {
+ amount = 1;
}
- else {
- priv.settings.data.splice(coords.row, toCoords.row - coords.row + 1);
+ if (typeof index !== 'number') {
+ index = -amount;
}
+ priv.settings.data.splice(index, amount);
self.forceFullRender = true; //used when data was changed
},
/**
- * Removes col at the right of the data array
- * @param {Object} [coords] Optional. Coords of the cell which col will be removed
- * @param {Object} [toCoords] Required if coords is defined. Coords of the cell until which all cols will be removed
+ * Removes column from the data array
+ * @param {Number} [index] Optional. Index of the column to be removed. If not provided, the last column will be removed
+ * @param {Number} [amount] Optional. Amount of the columns to be removed. If not provided, one column will be removed
*/
- removeCol: function (coords, toCoords) {
+ removeCol: function (index, amount) {
if (priv.dataType === 'object' || priv.settings.columns) {
throw new Error("cannot remove column with object data source or columns option specified");
}
- var r = 0;
- if (!coords || coords.col === self.countCols() - 1) {
- for (; r < self.countRows(); r++) {
- priv.settings.data[r].pop();
- }
+ if (!amount) {
+ amount = 1;
}
- else {
- var howMany = toCoords.col - coords.col + 1;
- for (; r < self.countRows(); r++) {
- priv.settings.data[r].splice(coords.col, howMany);
- }
+ if (typeof index !== 'number') {
+ index = -amount;
+ }
+ for (var r = 0, rlen = self.countRows(); r < rlen; r++) {
+ priv.settings.data[r].splice(index, amount);
}
self.forceFullRender = true; //used when data was changed
},
@@ -340,22 +338,29 @@ Handsontable.Core = function (rootElement, settings) {
grid = {
/**
- * Alter grid
+ * Inserts or removes rows and columns
* @param {String} action Possible values: "insert_row", "insert_col", "remove_row", "remove_col"
- * @param {Object} coords
- * @param {Object} [toCoords] Required only for actions "remove_row" and "remove_col"
+ * @param {Number} index
+ * @param {Number} amount
*/
- alter: function (action, coords, toCoords) {
- var oldData, newData, changes, r, rlen, c, clen;
+ alter: function (action, index, amount) {
+ var oldData, newData, changes, r, rlen, c, clen, delta;
oldData = $.extend(true, [], datamap.getAll());
switch (action) {
case "insert_row":
- if (self.countRows() < priv.settings.maxRows) {
- datamap.createRow(coords);
- if (priv.selStart.exists() && priv.selStart.row() >= coords.row) {
- priv.selStart.row(priv.selStart.row() + 1);
- selection.transformEnd(1, 0); //will call render() internally
+ if (!amount) {
+ amount = 1;
+ }
+ delta = 0;
+ while (delta < amount && self.countRows() < priv.settings.maxRows) {
+ datamap.createRow(index);
+ delta++;
+ }
+ if (delta) {
+ if (priv.selStart.exists() && priv.selStart.row() >= index) {
+ priv.selStart.row(priv.selStart.row() + delta);
+ selection.transformEnd(delta, 0); //will call render() internally
}
else {
selection.refreshBorders(); //it will call render and prepare methods
@@ -364,11 +369,18 @@ Handsontable.Core = function (rootElement, settings) {
break;
case "insert_col":
- if (self.countCols() < priv.settings.maxCols) {
- datamap.createCol(coords);
- if (priv.selStart.exists() && priv.selStart.col() >= coords.col) {
- priv.selStart.col(priv.selStart.col() + 1);
- selection.transformEnd(0, 1); //will call render() internally
+ if (!amount) {
+ amount = 1;
+ }
+ delta = 0;
+ while (delta < amount && self.countCols() < priv.settings.maxCols) {
+ datamap.createCol(index);
+ delta++;
+ }
+ if (delta) {
+ if (priv.selStart.exists() && priv.selStart.col() >= index) {
+ priv.selStart.col(priv.selStart.col() + delta);
+ selection.transformEnd(0, delta); //will call render() internally
}
else {
selection.refreshBorders(); //it will call render and prepare methods
@@ -377,16 +389,20 @@ Handsontable.Core = function (rootElement, settings) {
break;
case "remove_row":
- datamap.removeRow(coords, toCoords);
+ datamap.removeRow(index, amount);
grid.keepEmptyRows();
selection.refreshBorders(); //it will call render and prepare methods
break;
case "remove_col":
- datamap.removeCol(coords, toCoords);
+ datamap.removeCol(index, amount);
grid.keepEmptyRows();
selection.refreshBorders(); //it will call render and prepare methods
break;
+
+ default:
+ throw Error('There is no such action "' + action + '"');
+ break;
}
changes = [];
@@ -548,8 +564,7 @@ Handsontable.Core = function (rootElement, settings) {
break;
}
if (self.getCellMeta(current.row, current.col).isWritable) {
- var p = datamap.colToProp(current.col);
- setData.push([current.row, p, input[r][c]]);
+ setData.push([current.row, current.col, input[r][c]]);
}
current.col++;
if (end && c === clen - 1) {
@@ -828,7 +843,7 @@ Handsontable.Core = function (rootElement, settings) {
for (r = corners.TL.row; r <= corners.BR.row; r++) {
for (c = corners.TL.col; c <= corners.BR.col; c++) {
if (self.getCellMeta(r, c).isWritable) {
- changes.push([r, datamap.colToProp(c), '']);
+ changes.push([r, c, '']);
}
}
}
@@ -1233,7 +1248,7 @@ Handsontable.Core = function (rootElement, settings) {
Handsontable.PluginHooks.run(self, 'afterInit');
};
- validate = function (changes, source) {
+ function validateChanges(changes, source) {
var validated = $.Deferred();
var deferreds = [];
@@ -1311,7 +1326,7 @@ Handsontable.Core = function (rootElement, settings) {
});
return $.when(validated);
- };
+ }
var fireEvent = function (name, params) {
if (priv.settings.asyncRendering) {
@@ -1343,55 +1358,106 @@ Handsontable.Core = function (rootElement, settings) {
};
/**
- * Set data at given cell
- * @public
- * @param {Number|Array} row or array of changes in format [[row, col, value], ...]
- * @param {Number} prop
- * @param {String} value
- * @param {String} [source='edit'] String that identifies how this change will be described in changes array (useful in onChange callback)
+ * Internal function to apply changes. Called after validateChanges
+ * @param {Array} changes Array in form of [row, prop, oldValue, newValue]
+ * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
*/
- this.setDataAtCell = function (row, prop, value, source) {
- var changes, i, ilen;
+ function applyChanges(changes, source) {
+ var i
+ , ilen;
+
+ for (i = 0, ilen = changes.length; i < ilen; i++) {
+ if (priv.settings.minSpareRows) {
+ while (changes[i][0] > self.countRows() - 1) {
+ datamap.createRow();
+ }
+ }
+ if (priv.dataType === 'array' && priv.settings.minSpareCols) {
+ while (datamap.propToCol(changes[i][1]) > self.countCols() - 1) {
+ datamap.createCol();
+ }
+ }
+ datamap.set(changes[i][0], changes[i][1], changes[i][3]);
+ }
+ self.forceFullRender = true; //used when data was changed
+ grid.keepEmptyRows();
+ selection.refreshBorders();
+ fireEvent("datachange.handsontable", [changes, source || 'edit']);
+ }
- if (typeof row === "object") { //is it an array of changes
- changes = row;
+ function setDataInputToArray(arg0, arg1, arg2) {
+ if (typeof arg0 === "object") { //is it an array of changes
+ return arg0;
}
- else if ($.isPlainObject(value)) { //backwards compatibility
- changes = value;
+ else if ($.isPlainObject(arg2)) { //backwards compatibility
+ return value;
}
else {
- changes = [
- [row, prop, value]
+ return [
+ [arg0, arg1, arg2]
];
}
+ }
- for (i = 0, ilen = changes.length; i < ilen; i++) {
- changes[i].splice(2, 0, datamap.get(changes[i][0], changes[i][1])); //add old value at index 2
+ /**
+ * Set data at given cell
+ * @public
+ * @param {Number|Array} row or array of changes in format [[row, col, value], ...]
+ * @param {Number} col
+ * @param {String} value
+ * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
+ */
+ this.setDataAtCell = function (row, col, value, source) {
+ var input = setDataInputToArray(row, col, value)
+ , i
+ , ilen
+ , changes = []
+ , prop;
+
+ for (i = 0, ilen = input.length; i < ilen; i++) {
+ if (typeof input[i][1] !== 'number') {
+ throw new Exception('Method `setDataAtCell` accepts row and column number as its parameters. If you want to use object property name, use method `setDataAtRowProp`');
+ }
+ prop = datamap.colToProp(input[i][1]);
+ changes.push([
+ input[i][0],
+ prop,
+ datamap.get(input[i][0], prop),
+ input[i][2]
+ ]);
}
- validate(changes, source).then(function () { //when validate is resolved...
- for (i = 0, ilen = changes.length; i < ilen; i++) {
- row = changes[i][0];
- prop = changes[i][1];
- var col = datamap.propToCol(prop);
- value = changes[i][3];
+ validateChanges(changes, source).then(function () {
+ applyChanges(changes, source);
+ });
+ };
- if (priv.settings.minSpareRows) {
- while (row > self.countRows() - 1) {
- datamap.createRow();
- }
- }
- if (priv.dataType === 'array' && priv.settings.minSpareCols) {
- while (col > self.countCols() - 1) {
- datamap.createCol();
- }
- }
- datamap.set(row, prop, value);
- }
- self.forceFullRender = true; //used when data was changed
- grid.keepEmptyRows();
- selection.refreshBorders();
- fireEvent("datachange.handsontable", [changes, source || 'edit']);
+
+ /**
+ * Set data at given row property
+ * @public
+ * @param {Number|Array} row or array of changes in format [[row, prop, value], ...]
+ * @param {Number} prop
+ * @param {String} value
+ * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
+ */
+ this.setDataAtRowProp = function (row, prop, value, source) {
+ var input = setDataInputToArray(row, prop, value)
+ , i
+ , ilen
+ , changes = [];
+
+ for (i = 0, ilen = input.length; i < ilen; i++) {
+ changes.push([
+ input[i][0],
+ input[i][1],
+ datamap.get(input[i][0], input[i][1]),
+ input[i][2]
+ ]);
+ }
+
+ validateChanges(changes, source).then(function () {
+ applyChanges(changes, source);
});
};
@@ -1631,31 +1697,14 @@ Handsontable.Core = function (rootElement, settings) {
};
/**
- * Alters the grid
+ * Inserts or removes rows and columns
* @param {String} action See grid.alter for possible values
- * @param {Number} from
- * @param {Number} [to] Optional. Used only for actions "remove_row" and "remove_col"
+ * @param {Number} index
+ * @param {Number} amount
* @public
*/
- this.alter = function (action, from, to) {
- if (typeof to === "undefined") {
- to = from;
- }
- switch (action) {
- case "insert_row":
- case "remove_row":
- grid.alter(action, {row: from, col: 0}, {row: to, col: 0});
- break;
-
- case "insert_col":
- case "remove_col":
- grid.alter(action, {row: 0, col: from}, {row: 0, col: to});
- break;
-
- default:
- throw Error('There is no such action "' + action + '"');
- break;
- }
+ this.alter = function (action, index, amount) {
+ grid.alter(action, index, amount);
};
/**
@@ -1690,16 +1739,27 @@ Handsontable.Core = function (rootElement, settings) {
};
/**
- * Return cell value at `row`, `col`
+ * Return value at `row`, `col`
* @param {Number} row
* @param {Number} col
* @public
- * @return {string}
+ * @return value (mixed data type)
*/
this.getDataAtCell = function (row, col) {
return datamap.get(row, datamap.colToProp(col));
};
+ /**
+ * Return value at `row`, `prop`
+ * @param {Number} row
+ * @param {Number} prop
+ * @public
+ * @return value (mixed data type)
+ */
+ this.getDataAtRowProp = function (row, prop) {
+ return datamap.get(row, prop);
+ };
+
/**
* Returns cell meta data object corresponding to params row, col
* @param {Number} row
@@ -1847,8 +1907,11 @@ Handsontable.Core = function (rootElement, settings) {
if (priv.settings.columns && priv.settings.columns.length) {
return priv.settings.columns.length;
}
+ else if (priv.settings.data && priv.settings.data[0] && priv.settings.data[0].length) {
+ return priv.settings.data[0].length;
+ }
else {
- return Math.max((priv.settings.columns && priv.settings.columns.length) || 0, (priv.settings.data && priv.settings.data[0] && priv.settings.data[0].length) || 0);
+ return 0;
}
}
};
@@ -2314,7 +2377,7 @@ Handsontable.TableView.prototype.applyCellTypeMethod = function (methodName, td,
var prop = this.instance.colToProp(col)
, cellProperties = this.instance.getCellMeta(row, col);
if (cellProperties[methodName]) {
- return cellProperties[methodName](this.instance, td, row, col, prop, this.instance.getDataAtCell(row, col), cellProperties);
+ return cellProperties[methodName](this.instance, td, row, col, prop, this.instance.getDataAtRowProp(row, prop), cellProperties);
}
};
@@ -2407,7 +2470,7 @@ Handsontable.UndoRedo.prototype.undo = function () {
for (i = 0, ilen = setData.length; i < ilen; i++) {
setData[i].splice(3, 1);
}
- this.instance.setDataAtCell(setData, null, null, 'undo');
+ this.instance.setDataAtRowProp(setData, null, null, 'undo');
this.rev--;
}
};
@@ -2423,7 +2486,7 @@ Handsontable.UndoRedo.prototype.redo = function () {
for (i = 0, ilen = setData.length; i < ilen; i++) {
setData[i].splice(2, 1);
}
- this.instance.setDataAtCell(setData, null, null, 'redo');
+ this.instance.setDataAtRowProp(setData, null, null, 'redo');
}
};
@@ -2577,10 +2640,10 @@ Handsontable.CheckboxRenderer = function (instance, td, row, col, prop, value, c
var $input = $(td).find('input:first');
$input.mousedown(function (event) {
if (!$(this).is(':checked')) {
- instance.setDataAtCell(row, prop, cellProperties.checkedTemplate);
+ instance.setDataAtRowProp(row, prop, cellProperties.checkedTemplate);
}
else {
- instance.setDataAtCell(row, prop, cellProperties.uncheckedTemplate);
+ instance.setDataAtRowProp(row, prop, cellProperties.uncheckedTemplate);
}
event.stopPropagation(); //otherwise can confuse mousedown handler
});
@@ -2613,354 +2676,354 @@ Handsontable.NumericRenderer = function (instance, td, row, col, prop, value, ce
Handsontable.TextRenderer(instance, td, row, col, prop, value, cellProperties);
}
};
-function HandsontableTextEditorClass(instance) {
- if (instance) {
- this.isCellEdited = false;
- this.instance = instance;
- this.createElements();
- this.bindEvents();
- }
-}
-
-HandsontableTextEditorClass.prototype.createElements = function () {
- this.TEXTAREA = $('