rails_handsontable 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  module RailsHandsontable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Handsontable 0.10.1
2
+ * Handsontable 0.10.2
3
3
  * Handsontable is a simple jQuery plugin for editable tables with basic copy-paste compatibility with Excel and Google Docs
4
4
  *
5
5
  * Copyright 2012, Marcin Warpechowski
6
6
  * Licensed under the MIT license.
7
7
  * http://handsontable.com/
8
8
  *
9
- * Date: Sun Jan 12 2014 13:55:41 GMT+0100 (Central European Standard Time)
9
+ * Date: Thu Jan 23 2014 23:06:23 GMT+0100 (CET)
10
10
  */
11
11
  /*jslint white: true, browser: true, plusplus: true, indent: 4, maxerr: 50 */
12
12
 
@@ -227,486 +227,9 @@ Handsontable.Core = function (rootElement, userSettings) {
227
227
  isPopulated: null,
228
228
  scrollable: null,
229
229
  extensions: {},
230
- colToProp: null,
231
- propToCol: null,
232
- dataSchema: null,
233
- dataType: 'array',
234
230
  firstRun: true
235
231
  };
236
232
 
237
- datamap = {
238
- recursiveDuckSchema: function (obj) {
239
- var schema;
240
- if ($.isPlainObject(obj)) {
241
- schema = {};
242
- for (var i in obj) {
243
- if (obj.hasOwnProperty(i)) {
244
- if ($.isPlainObject(obj[i])) {
245
- schema[i] = datamap.recursiveDuckSchema(obj[i]);
246
- }
247
- else {
248
- schema[i] = null;
249
- }
250
- }
251
- }
252
- }
253
- else {
254
- schema = [];
255
- }
256
- return schema;
257
- },
258
-
259
- recursiveDuckColumns: function (schema, lastCol, parent) {
260
- var prop, i;
261
- if (typeof lastCol === 'undefined') {
262
- lastCol = 0;
263
- parent = '';
264
- }
265
- if ($.isPlainObject(schema)) {
266
- for (i in schema) {
267
- if (schema.hasOwnProperty(i)) {
268
- if (schema[i] === null) {
269
- prop = parent + i;
270
- priv.colToProp.push(prop);
271
- priv.propToCol[prop] = lastCol;
272
- lastCol++;
273
- }
274
- else {
275
- lastCol = datamap.recursiveDuckColumns(schema[i], lastCol, i + '.');
276
- }
277
- }
278
- }
279
- }
280
- return lastCol;
281
- },
282
-
283
- createMap: function () {
284
- if (typeof datamap.getSchema() === "undefined") {
285
- throw new Error("trying to create `columns` definition but you didnt' provide `schema` nor `data`");
286
- }
287
- var i, ilen, schema = datamap.getSchema();
288
- priv.colToProp = [];
289
- priv.propToCol = {};
290
- if (priv.settings.columns) {
291
- for (i = 0, ilen = priv.settings.columns.length; i < ilen; i++) {
292
- priv.colToProp[i] = priv.settings.columns[i].data;
293
- priv.propToCol[priv.settings.columns[i].data] = i;
294
- }
295
- }
296
- else {
297
- datamap.recursiveDuckColumns(schema);
298
- }
299
- },
300
-
301
- colToProp: function (col) {
302
- col = Handsontable.PluginHooks.execute(instance, 'modifyCol', col);
303
- if (priv.colToProp && typeof priv.colToProp[col] !== 'undefined') {
304
- return priv.colToProp[col];
305
- }
306
- else {
307
- return col;
308
- }
309
- },
310
-
311
- propToCol: function (prop) {
312
- var col;
313
- if (typeof priv.propToCol[prop] !== 'undefined') {
314
- col = priv.propToCol[prop];
315
- }
316
- else {
317
- col = prop;
318
- }
319
- col = Handsontable.PluginHooks.execute(instance, 'modifyCol', col);
320
- return col;
321
- },
322
-
323
- getSchema: function () {
324
- if (priv.settings.dataSchema) {
325
- if (typeof priv.settings.dataSchema === 'function') {
326
- return priv.settings.dataSchema();
327
- }
328
- return priv.settings.dataSchema;
329
- }
330
- return priv.duckDataSchema;
331
- },
332
-
333
- /**
334
- * Creates row at the bottom of the data array
335
- * @param {Number} [index] Optional. Index of the row before which the new row will be inserted
336
- */
337
- createRow: function (index, amount) {
338
- var row
339
- , colCount = instance.countCols()
340
- , numberOfCreatedRows = 0
341
- , currentIndex;
342
-
343
- if (!amount) {
344
- amount = 1;
345
- }
346
-
347
- if (typeof index !== 'number' || index >= instance.countRows()) {
348
- index = instance.countRows();
349
- }
350
-
351
- currentIndex = index;
352
- while (numberOfCreatedRows < amount && instance.countRows() < priv.settings.maxRows) {
353
-
354
- if (priv.dataType === 'array') {
355
- row = [];
356
- for (var c = 0; c < colCount; c++) {
357
- row.push(null);
358
- }
359
- }
360
- else if (priv.dataType === 'function') {
361
- row = priv.settings.dataSchema(index);
362
- }
363
- else {
364
- row = $.extend(true, {}, datamap.getSchema());
365
- }
366
-
367
- if (index === instance.countRows()) {
368
- GridSettings.prototype.data.push(row);
369
- }
370
- else {
371
- GridSettings.prototype.data.splice(index, 0, row);
372
- }
373
-
374
- numberOfCreatedRows++;
375
- currentIndex++;
376
- }
377
-
378
-
379
- instance.PluginHooks.run('afterCreateRow', index, numberOfCreatedRows);
380
- instance.forceFullRender = true; //used when data was changed
381
-
382
- return numberOfCreatedRows;
383
- },
384
-
385
- /**
386
- * Creates col at the right of the data array
387
- * @param {Number} [index] Optional. Index of the column before which the new column will be inserted
388
- * * @param {Number} [amount] Optional.
389
- */
390
- createCol: function (index, amount) {
391
- if (priv.dataType === 'object' || priv.settings.columns) {
392
- throw new Error("Cannot create new column. When data source in an object, " +
393
- "you can only have as much columns as defined in first data row, data schema or in the 'columns' setting." +
394
- "If you want to be able to add new columns, you have to use array datasource.");
395
- }
396
- var rlen = instance.countRows()
397
- , data = GridSettings.prototype.data
398
- , constructor
399
- , numberOfCreatedCols = 0
400
- , currentIndex;
401
-
402
- if (!amount) {
403
- amount = 1;
404
- }
405
-
406
- currentIndex = index;
407
-
408
- while (numberOfCreatedCols < amount && instance.countCols() < priv.settings.maxCols){
409
- constructor = Handsontable.helper.columnFactory(GridSettings, priv.columnsSettingConflicts);
410
- if (typeof index !== 'number' || index >= instance.countCols()) {
411
- for (var r = 0; r < rlen; r++) {
412
- if (typeof data[r] === 'undefined') {
413
- data[r] = [];
414
- }
415
- data[r].push(null);
416
- }
417
- // Add new column constructor
418
- priv.columnSettings.push(constructor);
419
- }
420
- else {
421
- for (var r = 0 ; r < rlen; r++) {
422
- data[r].splice(currentIndex, 0, null);
423
- }
424
- // Add new column constructor at given index
425
- priv.columnSettings.splice(currentIndex, 0, constructor);
426
- }
427
-
428
- numberOfCreatedCols++;
429
- currentIndex++;
430
- }
431
-
432
- instance.PluginHooks.run('afterCreateCol', index, numberOfCreatedCols);
433
- instance.forceFullRender = true; //used when data was changed
434
-
435
- return numberOfCreatedCols;
436
- },
437
-
438
- /**
439
- * Removes row from the data array
440
- * @param {Number} [index] Optional. Index of the row to be removed. If not provided, the last row will be removed
441
- * @param {Number} [amount] Optional. Amount of the rows to be removed. If not provided, one row will be removed
442
- */
443
- removeRow: function (index, amount) {
444
- if (!amount) {
445
- amount = 1;
446
- }
447
- if (typeof index !== 'number') {
448
- index = -amount;
449
- }
450
-
451
- index = (instance.countRows() + index) % instance.countRows();
452
-
453
- // We have to map the physical row ids to logical and than perform removing with (possibly) new row id
454
- var logicRows = this.physicalRowsToLogical(index, amount);
455
-
456
- var actionWasNotCancelled = instance.PluginHooks.execute('beforeRemoveRow', index, amount);
457
-
458
- if(actionWasNotCancelled === false){
459
- return;
460
- }
461
-
462
- var newData = GridSettings.prototype.data.filter(function (row, index) {
463
- return logicRows.indexOf(index) == -1;
464
- });
465
-
466
- GridSettings.prototype.data.length = 0;
467
- Array.prototype.push.apply(GridSettings.prototype.data, newData);
468
-
469
- instance.PluginHooks.run('afterRemoveRow', index, amount);
470
-
471
- instance.forceFullRender = true; //used when data was changed
472
- },
473
-
474
- /**
475
- * Removes column from the data array
476
- * @param {Number} [index] Optional. Index of the column to be removed. If not provided, the last column will be removed
477
- * @param {Number} [amount] Optional. Amount of the columns to be removed. If not provided, one column will be removed
478
- */
479
- removeCol: function (index, amount) {
480
- if (priv.dataType === 'object' || priv.settings.columns) {
481
- throw new Error("cannot remove column with object data source or columns option specified");
482
- }
483
- if (!amount) {
484
- amount = 1;
485
- }
486
- if (typeof index !== 'number') {
487
- index = -amount;
488
- }
489
-
490
- index = (instance.countCols() + index) % instance.countCols();
491
-
492
- var actionWasNotCancelled = instance.PluginHooks.execute('beforeRemoveCol', index, amount);
493
-
494
- if(actionWasNotCancelled === false){
495
- return;
496
- }
497
-
498
- var data = GridSettings.prototype.data;
499
- for (var r = 0, rlen = instance.countRows(); r < rlen; r++) {
500
- data[r].splice(index, amount);
501
- }
502
- priv.columnSettings.splice(index, amount);
503
-
504
- instance.PluginHooks.run('afterRemoveCol', index, amount);
505
- instance.forceFullRender = true; //used when data was changed
506
- },
507
-
508
- /**
509
- * Add / removes data from the column
510
- * @param {Number} col Index of column in which do you want to do splice.
511
- * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end
512
- * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed
513
- * param {...*} elements Optional. The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array
514
- */
515
- spliceCol: function (col, index, amount/*, elements...*/) {
516
- var elements = 4 <= arguments.length ? [].slice.call(arguments, 3) : [];
517
-
518
- var colData = instance.getDataAtCol(col);
519
- var removed = colData.slice(index, index + amount);
520
- var after = colData.slice(index + amount);
521
-
522
- Handsontable.helper.extendArray(elements, after);
523
- var i = 0;
524
- while (i < amount) {
525
- elements.push(null); //add null in place of removed elements
526
- i++;
527
- }
528
- Handsontable.helper.to2dArray(elements);
529
- instance.populateFromArray(index, col, elements, null, null, 'spliceCol');
530
-
531
- return removed;
532
- },
533
-
534
- /**
535
- * Add / removes data from the row
536
- * @param {Number} row Index of row in which do you want to do splice.
537
- * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end
538
- * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed
539
- * param {...*} elements Optional. The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array
540
- */
541
- spliceRow: function (row, index, amount/*, elements...*/) {
542
- var elements = 4 <= arguments.length ? [].slice.call(arguments, 3) : [];
543
-
544
- var rowData = instance.getDataAtRow(row);
545
- var removed = rowData.slice(index, index + amount);
546
- var after = rowData.slice(index + amount);
547
-
548
- Handsontable.helper.extendArray(elements, after);
549
- var i = 0;
550
- while (i < amount) {
551
- elements.push(null); //add null in place of removed elements
552
- i++;
553
- }
554
- instance.populateFromArray(row, index, [elements], null, null, 'spliceRow');
555
-
556
- return removed;
557
- },
558
-
559
- /**
560
- * Returns single value from the data array
561
- * @param {Number} row
562
- * @param {Number} prop
563
- */
564
- getVars: {},
565
- get: function (row, prop) {
566
- datamap.getVars.row = row;
567
- datamap.getVars.prop = prop;
568
- instance.PluginHooks.run('beforeGet', datamap.getVars);
569
- if (typeof datamap.getVars.prop === 'string' && datamap.getVars.prop.indexOf('.') > -1) {
570
- var sliced = datamap.getVars.prop.split(".");
571
- var out = priv.settings.data[datamap.getVars.row];
572
- if (!out) {
573
- return null;
574
- }
575
- for (var i = 0, ilen = sliced.length; i < ilen; i++) {
576
- out = out[sliced[i]];
577
- if (typeof out === 'undefined') {
578
- return null;
579
- }
580
- }
581
- return out;
582
- }
583
- else if (typeof datamap.getVars.prop === 'function') {
584
- /**
585
- * allows for interacting with complex structures, for example
586
- * d3/jQuery getter/setter properties:
587
- *
588
- * {columns: [{
589
- * data: function(row, value){
590
- * if(arguments.length === 1){
591
- * return row.property();
592
- * }
593
- * row.property(value);
594
- * }
595
- * }]}
596
- */
597
- return datamap.getVars.prop(priv.settings.data.slice(
598
- datamap.getVars.row,
599
- datamap.getVars.row + 1
600
- )[0]);
601
- }
602
- else {
603
- return priv.settings.data[datamap.getVars.row] ? priv.settings.data[datamap.getVars.row][datamap.getVars.prop] : null;
604
- }
605
- },
606
-
607
- /**
608
- * Saves single value to the data array
609
- * @param {Number} row
610
- * @param {Number} prop
611
- * @param {String} value
612
- * @param {String} [source] Optional. Source of hook runner.
613
- */
614
- setVars: {},
615
- set: function (row, prop, value, source) {
616
- datamap.setVars.row = row;
617
- datamap.setVars.prop = prop;
618
- datamap.setVars.value = value;
619
- instance.PluginHooks.run('beforeSet', datamap.setVars, source || "datamapGet");
620
- if (typeof datamap.setVars.prop === 'string' && datamap.setVars.prop.indexOf('.') > -1) {
621
- var sliced = datamap.setVars.prop.split(".");
622
- var out = priv.settings.data[datamap.setVars.row];
623
- for (var i = 0, ilen = sliced.length - 1; i < ilen; i++) {
624
- out = out[sliced[i]];
625
- }
626
- out[sliced[i]] = datamap.setVars.value;
627
- }
628
- else if (typeof datamap.setVars.prop === 'function') {
629
- /* see the `function` handler in `get` */
630
- datamap.setVars.prop(priv.settings.data.slice(
631
- datamap.setVars.row,
632
- datamap.setVars.row + 1
633
- )[0], datamap.setVars.value);
634
- }
635
- else {
636
- priv.settings.data[datamap.setVars.row][datamap.setVars.prop] = datamap.setVars.value;
637
- }
638
- },
639
- /**
640
- * This ridiculous piece of code maps rows Id that are present in table data to those displayed for user.
641
- * The trick is, the physical row id (stored in settings.data) is not necessary the same
642
- * as the logical (displayed) row id (e.g. when sorting is applied).
643
- */
644
- physicalRowsToLogical: function (index, amount) {
645
- var physicRow = (GridSettings.prototype.data.length + index) % GridSettings.prototype.data.length;
646
- var logicRows = [];
647
- var rowsToRemove = amount;
648
-
649
- while (physicRow < GridSettings.prototype.data.length && rowsToRemove) {
650
- this.get(physicRow, 0); //this performs an actual mapping and saves the result to getVars
651
- logicRows.push(this.getVars.row);
652
-
653
- rowsToRemove--;
654
- physicRow++;
655
- }
656
-
657
- return logicRows;
658
- },
659
-
660
- /**
661
- * Clears the data array
662
- */
663
- clear: function () {
664
- for (var r = 0; r < instance.countRows(); r++) {
665
- for (var c = 0; c < instance.countCols(); c++) {
666
- datamap.set(r, datamap.colToProp(c), '');
667
- }
668
- }
669
- },
670
-
671
- /**
672
- * Returns the data array
673
- * @return {Array}
674
- */
675
- getAll: function () {
676
- return priv.settings.data;
677
- },
678
-
679
- /**
680
- * Returns data range as array
681
- * @param {Object} start Start selection position
682
- * @param {Object} end End selection position
683
- * @return {Array}
684
- */
685
- getRange: function (start, end) {
686
- var r, rlen, c, clen, output = [], row;
687
- rlen = Math.max(start.row, end.row);
688
- clen = Math.max(start.col, end.col);
689
- for (r = Math.min(start.row, end.row); r <= rlen; r++) {
690
- row = [];
691
- for (c = Math.min(start.col, end.col); c <= clen; c++) {
692
- row.push(datamap.get(r, datamap.colToProp(c)));
693
- }
694
- output.push(row);
695
- }
696
- return output;
697
- },
698
-
699
- /**
700
- * Return data as text (tab separated columns)
701
- * @param {Object} start (Optional) Start selection position
702
- * @param {Object} end (Optional) End selection position
703
- * @return {String}
704
- */
705
- getText: function (start, end) {
706
- return SheetClip.stringify(datamap.getRange(start, end));
707
- }
708
- };
709
-
710
233
  grid = {
711
234
  /**
712
235
  * Inserts or removes rows and columns
@@ -828,7 +351,7 @@ Handsontable.Core = function (rootElement, userSettings) {
828
351
  }
829
352
 
830
353
  //should I add empty cols to meet minSpareCols?
831
- if (!priv.settings.columns && priv.dataType === 'array' && emptyCols < priv.settings.minSpareCols) {
354
+ if (!priv.settings.columns && instance.dataType === 'array' && emptyCols < priv.settings.minSpareCols) {
832
355
  for (; emptyCols < priv.settings.minSpareCols && instance.countCols() < priv.settings.maxCols; emptyCols++) {
833
356
  datamap.createCol();
834
357
  }
@@ -1580,7 +1103,7 @@ Handsontable.Core = function (rootElement, userSettings) {
1580
1103
  }
1581
1104
  }
1582
1105
 
1583
- if (priv.dataType === 'array' && priv.settings.minSpareCols) {
1106
+ if (instance.dataType === 'array' && priv.settings.minSpareCols) {
1584
1107
  while (datamap.propToCol(changes[i][1]) > instance.countCols() - 1) {
1585
1108
  datamap.createCol();
1586
1109
  }
@@ -1779,7 +1302,7 @@ Handsontable.Core = function (rootElement, userSettings) {
1779
1302
  * param {...*} elements Optional. The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array
1780
1303
  */
1781
1304
  this.spliceCol = function (col, index, amount/*, elements... */) {
1782
- return datamap.spliceCol.apply(null, arguments);
1305
+ return datamap.spliceCol.apply(datamap, arguments);
1783
1306
  };
1784
1307
 
1785
1308
  /**
@@ -1790,7 +1313,7 @@ Handsontable.Core = function (rootElement, userSettings) {
1790
1313
  * param {...*} elements Optional. The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array
1791
1314
  */
1792
1315
  this.spliceRow = function (row, index, amount/*, elements... */) {
1793
- return datamap.spliceRow.apply(null, arguments);
1316
+ return datamap.spliceRow.apply(datamap, arguments);
1794
1317
  };
1795
1318
 
1796
1319
  /**
@@ -1902,22 +1425,17 @@ Handsontable.Core = function (rootElement, userSettings) {
1902
1425
  GridSettings.prototype.data = data;
1903
1426
 
1904
1427
  if (priv.settings.dataSchema instanceof Array || data[0] instanceof Array) {
1905
- priv.dataType = 'array';
1428
+ instance.dataType = 'array';
1906
1429
  }
1907
1430
  else if (typeof priv.settings.dataSchema === 'function') {
1908
- priv.dataType = 'function';
1431
+ instance.dataType = 'function';
1909
1432
  }
1910
1433
  else {
1911
- priv.dataType = 'object';
1434
+ instance.dataType = 'object';
1912
1435
  }
1913
1436
 
1914
- if (data[0]) {
1915
- priv.duckDataSchema = datamap.recursiveDuckSchema(data[0]);
1916
- }
1917
- else {
1918
- priv.duckDataSchema = {};
1919
- }
1920
- datamap.createMap();
1437
+ datamap = new Handsontable.DataMap(instance, priv, GridSettings);
1438
+
1921
1439
  clearCellSettingCache();
1922
1440
 
1923
1441
  grid.adjustRowsAndCols();
@@ -1954,12 +1472,12 @@ Handsontable.Core = function (rootElement, userSettings) {
1954
1472
  return datamap.getAll();
1955
1473
  }
1956
1474
  else {
1957
- return datamap.getRange({row: r, col: c}, {row: r2, col: c2});
1475
+ return datamap.getRange({row: r, col: c}, {row: r2, col: c2}, datamap.DESTINATION_RENDERER);
1958
1476
  }
1959
1477
  };
1960
1478
 
1961
1479
  this.getCopyableData = function (startRow, startCol, endRow, endCol) {
1962
- return datamap.getText({row: startRow, col: startCol}, {row: endRow, col: endCol});
1480
+ return datamap.getCopyableText({row: startRow, col: startCol}, {row: endRow, col: endCol});
1963
1481
  }
1964
1482
 
1965
1483
  /**
@@ -2204,7 +1722,7 @@ Handsontable.Core = function (rootElement, userSettings) {
2204
1722
  * @return value (mixed data type)
2205
1723
  */
2206
1724
  this.getDataAtCol = function (col) {
2207
- return [].concat.apply([], datamap.getRange({row: 0, col: col}, {row: priv.settings.data.length - 1, col: col}));
1725
+ return [].concat.apply([], datamap.getRange({row: 0, col: col}, {row: priv.settings.data.length - 1, col: col}, datamap.DESTINATION_RENDERER));
2208
1726
  };
2209
1727
 
2210
1728
  /**
@@ -2214,7 +1732,7 @@ Handsontable.Core = function (rootElement, userSettings) {
2214
1732
  * @return value (mixed data type)
2215
1733
  */
2216
1734
  this.getDataAtProp = function (prop) {
2217
- return [].concat.apply([], datamap.getRange({row: 0, col: datamap.propToCol(prop)}, {row: priv.settings.data.length - 1, col: datamap.propToCol(prop)}));
1735
+ return [].concat.apply([], datamap.getRange({row: 0, col: datamap.propToCol(prop)}, {row: priv.settings.data.length - 1, col: datamap.propToCol(prop)}, datamap.DESTINATION_RENDERER));
2218
1736
  };
2219
1737
 
2220
1738
  /**
@@ -2300,8 +1818,9 @@ Handsontable.Core = function (rootElement, userSettings) {
2300
1818
  }
2301
1819
  };
2302
1820
 
1821
+ var rendererLookup = Handsontable.helper.cellMethodLookupFactory('renderer');
2303
1822
  this.getCellRenderer = function (row, col) {
2304
- var renderer = Handsontable.helper.cellMethodLookupFactory('renderer').call(this, row, col);
1823
+ var renderer = rendererLookup.call(this, row, col);
2305
1824
  return Handsontable.renderers.getRenderer(renderer);
2306
1825
 
2307
1826
  };
@@ -2477,15 +1996,15 @@ Handsontable.Core = function (rootElement, userSettings) {
2477
1996
  * @return {Number}
2478
1997
  */
2479
1998
  this.countCols = function () {
2480
- if (priv.dataType === 'object' || priv.dataType === 'function') {
1999
+ if (instance.dataType === 'object' || instance.dataType === 'function') {
2481
2000
  if (priv.settings.columns && priv.settings.columns.length) {
2482
2001
  return priv.settings.columns.length;
2483
2002
  }
2484
2003
  else {
2485
- return priv.colToProp.length;
2004
+ return datamap.colToPropCache.length;
2486
2005
  }
2487
2006
  }
2488
- else if (priv.dataType === 'array') {
2007
+ else if (instance.dataType === 'array') {
2489
2008
  if (priv.settings.columns && priv.settings.columns.length) {
2490
2009
  return priv.settings.columns.length;
2491
2010
  }
@@ -2752,7 +2271,7 @@ Handsontable.Core = function (rootElement, userSettings) {
2752
2271
  /**
2753
2272
  * Handsontable version
2754
2273
  */
2755
- this.version = '0.10.1'; //inserted by grunt from package.json
2274
+ this.version = '0.10.2'; //inserted by grunt from package.json
2756
2275
  };
2757
2276
 
2758
2277
  var DefaultSettings = function () {};
@@ -2816,6 +2335,7 @@ DefaultSettings.prototype = {
2816
2335
  readOnly: false,
2817
2336
  nativeScrollbars: false,
2818
2337
  type: 'text',
2338
+ copyable: true,
2819
2339
  debug: false //shows debug overlays in Walkontable
2820
2340
  };
2821
2341
  Handsontable.DefaultSettings = DefaultSettings;
@@ -4200,6 +3720,545 @@ Handsontable.SelectionPoint.prototype.arr = function (arr) {
4200
3720
  }
4201
3721
  return [this._row, this._col]
4202
3722
  };
3723
+ (function (Handsontable) {
3724
+ 'use strict';
3725
+
3726
+ /**
3727
+ * Utility class that gets and saves data from/to the data source using mapping of columns numbers to object property names
3728
+ * TODO refactor arguments of methods getRange, getText to be numbers (not objects)
3729
+ * TODO remove priv, GridSettings from object constructor
3730
+ *
3731
+ * @param instance
3732
+ * @param priv
3733
+ * @param GridSettings
3734
+ * @constructor
3735
+ */
3736
+ Handsontable.DataMap = function (instance, priv, GridSettings) {
3737
+ this.instance = instance;
3738
+ this.priv = priv;
3739
+ this.GridSettings = GridSettings;
3740
+ this.dataSource = this.instance.getSettings().data;
3741
+
3742
+ if (this.dataSource[0]) {
3743
+ this.duckSchema = this.recursiveDuckSchema(this.dataSource[0]);
3744
+ }
3745
+ else {
3746
+ this.duckSchema = {};
3747
+ }
3748
+ this.createMap();
3749
+
3750
+ this.getVars = {}; //used by modifier
3751
+ this.setVars = {}; //used by modifier
3752
+ };
3753
+
3754
+ Handsontable.DataMap.prototype.DESTINATION_RENDERER = 1;
3755
+ Handsontable.DataMap.prototype.DESTINATION_CLIPBOARD_GENERATOR = 2;
3756
+
3757
+ Handsontable.DataMap.prototype.recursiveDuckSchema = function (obj) {
3758
+ var schema;
3759
+ if ($.isPlainObject(obj)) {
3760
+ schema = {};
3761
+ for (var i in obj) {
3762
+ if (obj.hasOwnProperty(i)) {
3763
+ if ($.isPlainObject(obj[i])) {
3764
+ schema[i] = this.recursiveDuckSchema(obj[i]);
3765
+ }
3766
+ else {
3767
+ schema[i] = null;
3768
+ }
3769
+ }
3770
+ }
3771
+ }
3772
+ else {
3773
+ schema = [];
3774
+ }
3775
+ return schema;
3776
+ };
3777
+
3778
+ Handsontable.DataMap.prototype.recursiveDuckColumns = function (schema, lastCol, parent) {
3779
+ var prop, i;
3780
+ if (typeof lastCol === 'undefined') {
3781
+ lastCol = 0;
3782
+ parent = '';
3783
+ }
3784
+ if ($.isPlainObject(schema)) {
3785
+ for (i in schema) {
3786
+ if (schema.hasOwnProperty(i)) {
3787
+ if (schema[i] === null) {
3788
+ prop = parent + i;
3789
+ this.colToPropCache.push(prop);
3790
+ this.propToColCache[prop] = lastCol;
3791
+ lastCol++;
3792
+ }
3793
+ else {
3794
+ lastCol = this.recursiveDuckColumns(schema[i], lastCol, i + '.');
3795
+ }
3796
+ }
3797
+ }
3798
+ }
3799
+ return lastCol;
3800
+ };
3801
+
3802
+ Handsontable.DataMap.prototype.createMap = function () {
3803
+ if (typeof this.getSchema() === "undefined") {
3804
+ throw new Error("trying to create `columns` definition but you didnt' provide `schema` nor `data`");
3805
+ }
3806
+ var i, ilen, schema = this.getSchema();
3807
+ this.colToPropCache = [];
3808
+ this.propToColCache = {};
3809
+ var columns = this.instance.getSettings().columns;
3810
+ if (columns) {
3811
+ for (i = 0, ilen = columns.length; i < ilen; i++) {
3812
+ this.colToPropCache[i] = columns[i].data;
3813
+ this.propToColCache[columns[i].data] = i;
3814
+ }
3815
+ }
3816
+ else {
3817
+ this.recursiveDuckColumns(schema);
3818
+ }
3819
+ };
3820
+
3821
+ Handsontable.DataMap.prototype.colToProp = function (col) {
3822
+ col = Handsontable.PluginHooks.execute(this.instance, 'modifyCol', col);
3823
+ if (this.colToPropCache && typeof this.colToPropCache[col] !== 'undefined') {
3824
+ return this.colToPropCache[col];
3825
+ }
3826
+ else {
3827
+ return col;
3828
+ }
3829
+ };
3830
+
3831
+ Handsontable.DataMap.prototype.propToCol = function (prop) {
3832
+ var col;
3833
+ if (typeof this.propToColCache[prop] !== 'undefined') {
3834
+ col = this.propToColCache[prop];
3835
+ }
3836
+ else {
3837
+ col = prop;
3838
+ }
3839
+ col = Handsontable.PluginHooks.execute(this.instance, 'modifyCol', col);
3840
+ return col;
3841
+ };
3842
+
3843
+ Handsontable.DataMap.prototype.getSchema = function () {
3844
+ var schema = this.instance.getSettings().dataSchema;
3845
+ if (schema) {
3846
+ if (typeof schema === 'function') {
3847
+ return schema();
3848
+ }
3849
+ return schema;
3850
+ }
3851
+ return this.duckSchema;
3852
+ };
3853
+
3854
+ /**
3855
+ * Creates row at the bottom of the data array
3856
+ * @param {Number} [index] Optional. Index of the row before which the new row will be inserted
3857
+ */
3858
+ Handsontable.DataMap.prototype.createRow = function (index, amount) {
3859
+ var row
3860
+ , colCount = this.instance.countCols()
3861
+ , numberOfCreatedRows = 0
3862
+ , currentIndex;
3863
+
3864
+ if (!amount) {
3865
+ amount = 1;
3866
+ }
3867
+
3868
+ if (typeof index !== 'number' || index >= this.instance.countRows()) {
3869
+ index = this.instance.countRows();
3870
+ }
3871
+
3872
+ currentIndex = index;
3873
+ var maxRows = this.instance.getSettings().maxRows;
3874
+ while (numberOfCreatedRows < amount && this.instance.countRows() < maxRows) {
3875
+
3876
+ if (this.instance.dataType === 'array') {
3877
+ row = [];
3878
+ for (var c = 0; c < colCount; c++) {
3879
+ row.push(null);
3880
+ }
3881
+ }
3882
+ else if (this.instance.dataType === 'function') {
3883
+ row = this.instance.getSettings().dataSchema(index);
3884
+ }
3885
+ else {
3886
+ row = $.extend(true, {}, this.getSchema());
3887
+ }
3888
+
3889
+ if (index === this.instance.countRows()) {
3890
+ this.dataSource.push(row);
3891
+ }
3892
+ else {
3893
+ this.dataSource.splice(index, 0, row);
3894
+ }
3895
+
3896
+ numberOfCreatedRows++;
3897
+ currentIndex++;
3898
+ }
3899
+
3900
+
3901
+ this.instance.PluginHooks.run('afterCreateRow', index, numberOfCreatedRows);
3902
+ this.instance.forceFullRender = true; //used when data was changed
3903
+
3904
+ return numberOfCreatedRows;
3905
+ };
3906
+
3907
+ /**
3908
+ * Creates col at the right of the data array
3909
+ * @param {Number} [index] Optional. Index of the column before which the new column will be inserted
3910
+ * * @param {Number} [amount] Optional.
3911
+ */
3912
+ Handsontable.DataMap.prototype.createCol = function (index, amount) {
3913
+ if (this.instance.dataType === 'object' || this.instance.getSettings().columns) {
3914
+ throw new Error("Cannot create new column. When data source in an object, " +
3915
+ "you can only have as much columns as defined in first data row, data schema or in the 'columns' setting." +
3916
+ "If you want to be able to add new columns, you have to use array datasource.");
3917
+ }
3918
+ var rlen = this.instance.countRows()
3919
+ , data = this.dataSource
3920
+ , constructor
3921
+ , numberOfCreatedCols = 0
3922
+ , currentIndex;
3923
+
3924
+ if (!amount) {
3925
+ amount = 1;
3926
+ }
3927
+
3928
+ currentIndex = index;
3929
+
3930
+ var maxCols = this.instance.getSettings().maxCols;
3931
+ while (numberOfCreatedCols < amount && this.instance.countCols() < maxCols) {
3932
+ constructor = Handsontable.helper.columnFactory(this.GridSettings, this.priv.columnsSettingConflicts);
3933
+ if (typeof index !== 'number' || index >= this.instance.countCols()) {
3934
+ for (var r = 0; r < rlen; r++) {
3935
+ if (typeof data[r] === 'undefined') {
3936
+ data[r] = [];
3937
+ }
3938
+ data[r].push(null);
3939
+ }
3940
+ // Add new column constructor
3941
+ this.priv.columnSettings.push(constructor);
3942
+ }
3943
+ else {
3944
+ for (var r = 0; r < rlen; r++) {
3945
+ data[r].splice(currentIndex, 0, null);
3946
+ }
3947
+ // Add new column constructor at given index
3948
+ this.priv.columnSettings.splice(currentIndex, 0, constructor);
3949
+ }
3950
+
3951
+ numberOfCreatedCols++;
3952
+ currentIndex++;
3953
+ }
3954
+
3955
+ this.instance.PluginHooks.run('afterCreateCol', index, numberOfCreatedCols);
3956
+ this.instance.forceFullRender = true; //used when data was changed
3957
+
3958
+ return numberOfCreatedCols;
3959
+ };
3960
+
3961
+ /**
3962
+ * Removes row from the data array
3963
+ * @param {Number} [index] Optional. Index of the row to be removed. If not provided, the last row will be removed
3964
+ * @param {Number} [amount] Optional. Amount of the rows to be removed. If not provided, one row will be removed
3965
+ */
3966
+ Handsontable.DataMap.prototype.removeRow = function (index, amount) {
3967
+ if (!amount) {
3968
+ amount = 1;
3969
+ }
3970
+ if (typeof index !== 'number') {
3971
+ index = -amount;
3972
+ }
3973
+
3974
+ index = (this.instance.countRows() + index) % this.instance.countRows();
3975
+
3976
+ // We have to map the physical row ids to logical and than perform removing with (possibly) new row id
3977
+ var logicRows = this.physicalRowsToLogical(index, amount);
3978
+
3979
+ var actionWasNotCancelled = this.instance.PluginHooks.execute('beforeRemoveRow', index, amount);
3980
+
3981
+ if (actionWasNotCancelled === false) {
3982
+ return;
3983
+ }
3984
+
3985
+ var data = this.dataSource;
3986
+ var newData = data.filter(function (row, index) {
3987
+ return logicRows.indexOf(index) == -1;
3988
+ });
3989
+
3990
+ data.length = 0;
3991
+ Array.prototype.push.apply(data, newData);
3992
+
3993
+ this.instance.PluginHooks.run('afterRemoveRow', index, amount);
3994
+
3995
+ this.instance.forceFullRender = true; //used when data was changed
3996
+ };
3997
+
3998
+ /**
3999
+ * Removes column from the data array
4000
+ * @param {Number} [index] Optional. Index of the column to be removed. If not provided, the last column will be removed
4001
+ * @param {Number} [amount] Optional. Amount of the columns to be removed. If not provided, one column will be removed
4002
+ */
4003
+ Handsontable.DataMap.prototype.removeCol = function (index, amount) {
4004
+ if (this.instance.dataType === 'object' || this.instance.getSettings().columns) {
4005
+ throw new Error("cannot remove column with object data source or columns option specified");
4006
+ }
4007
+ if (!amount) {
4008
+ amount = 1;
4009
+ }
4010
+ if (typeof index !== 'number') {
4011
+ index = -amount;
4012
+ }
4013
+
4014
+ index = (this.instance.countCols() + index) % this.instance.countCols();
4015
+
4016
+ var actionWasNotCancelled = this.instance.PluginHooks.execute('beforeRemoveCol', index, amount);
4017
+
4018
+ if (actionWasNotCancelled === false) {
4019
+ return;
4020
+ }
4021
+
4022
+ var data = this.dataSource;
4023
+ for (var r = 0, rlen = this.instance.countRows(); r < rlen; r++) {
4024
+ data[r].splice(index, amount);
4025
+ }
4026
+ this.priv.columnSettings.splice(index, amount);
4027
+
4028
+ this.instance.PluginHooks.run('afterRemoveCol', index, amount);
4029
+ this.instance.forceFullRender = true; //used when data was changed
4030
+ };
4031
+
4032
+ /**
4033
+ * Add / removes data from the column
4034
+ * @param {Number} col Index of column in which do you want to do splice.
4035
+ * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end
4036
+ * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed
4037
+ * param {...*} elements Optional. The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array
4038
+ */
4039
+ Handsontable.DataMap.prototype.spliceCol = function (col, index, amount/*, elements...*/) {
4040
+ var elements = 4 <= arguments.length ? [].slice.call(arguments, 3) : [];
4041
+
4042
+ var colData = this.instance.getDataAtCol(col);
4043
+ var removed = colData.slice(index, index + amount);
4044
+ var after = colData.slice(index + amount);
4045
+
4046
+ Handsontable.helper.extendArray(elements, after);
4047
+ var i = 0;
4048
+ while (i < amount) {
4049
+ elements.push(null); //add null in place of removed elements
4050
+ i++;
4051
+ }
4052
+ Handsontable.helper.to2dArray(elements);
4053
+ this.instance.populateFromArray(index, col, elements, null, null, 'spliceCol');
4054
+
4055
+ return removed;
4056
+ };
4057
+
4058
+ /**
4059
+ * Add / removes data from the row
4060
+ * @param {Number} row Index of row in which do you want to do splice.
4061
+ * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end
4062
+ * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed
4063
+ * param {...*} elements Optional. The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array
4064
+ */
4065
+ Handsontable.DataMap.prototype.spliceRow = function (row, index, amount/*, elements...*/) {
4066
+ var elements = 4 <= arguments.length ? [].slice.call(arguments, 3) : [];
4067
+
4068
+ var rowData = this.instance.getDataAtRow(row);
4069
+ var removed = rowData.slice(index, index + amount);
4070
+ var after = rowData.slice(index + amount);
4071
+
4072
+ Handsontable.helper.extendArray(elements, after);
4073
+ var i = 0;
4074
+ while (i < amount) {
4075
+ elements.push(null); //add null in place of removed elements
4076
+ i++;
4077
+ }
4078
+ this.instance.populateFromArray(row, index, [elements], null, null, 'spliceRow');
4079
+
4080
+ return removed;
4081
+ };
4082
+
4083
+ /**
4084
+ * Returns single value from the data array
4085
+ * @param {Number} row
4086
+ * @param {Number} prop
4087
+ */
4088
+ Handsontable.DataMap.prototype.get = function (row, prop) {
4089
+ this.getVars.row = row;
4090
+ this.getVars.prop = prop;
4091
+ this.instance.PluginHooks.run('beforeGet', this.getVars);
4092
+ if (typeof this.getVars.prop === 'string' && this.getVars.prop.indexOf('.') > -1) {
4093
+ var sliced = this.getVars.prop.split(".");
4094
+ var out = this.dataSource[this.getVars.row];
4095
+ if (!out) {
4096
+ return null;
4097
+ }
4098
+ for (var i = 0, ilen = sliced.length; i < ilen; i++) {
4099
+ out = out[sliced[i]];
4100
+ if (typeof out === 'undefined') {
4101
+ return null;
4102
+ }
4103
+ }
4104
+ return out;
4105
+ }
4106
+ else if (typeof this.getVars.prop === 'function') {
4107
+ /**
4108
+ * allows for interacting with complex structures, for example
4109
+ * d3/jQuery getter/setter properties:
4110
+ *
4111
+ * {columns: [{
4112
+ * data: function(row, value){
4113
+ * if(arguments.length === 1){
4114
+ * return row.property();
4115
+ * }
4116
+ * row.property(value);
4117
+ * }
4118
+ * }]}
4119
+ */
4120
+ return this.getVars.prop(this.dataSource.slice(
4121
+ this.getVars.row,
4122
+ this.getVars.row + 1
4123
+ )[0]);
4124
+ }
4125
+ else {
4126
+ return this.dataSource[this.getVars.row] ? this.dataSource[this.getVars.row][this.getVars.prop] : null;
4127
+ }
4128
+ };
4129
+
4130
+ var copyableLookup = Handsontable.helper.cellMethodLookupFactory('copyable');
4131
+
4132
+ /**
4133
+ * Returns single value from the data array (intended for clipboard copy to an external application)
4134
+ * @param {Number} row
4135
+ * @param {Number} prop
4136
+ * @return {String}
4137
+ */
4138
+ Handsontable.DataMap.prototype.getCopyable = function (row, prop) {
4139
+ if (copyableLookup.call(this.instance, row, this.propToCol(prop))) {
4140
+ return this.get(row, prop);
4141
+ }
4142
+ return '';
4143
+ };
4144
+
4145
+ /**
4146
+ * Saves single value to the data array
4147
+ * @param {Number} row
4148
+ * @param {Number} prop
4149
+ * @param {String} value
4150
+ * @param {String} [source] Optional. Source of hook runner.
4151
+ */
4152
+ Handsontable.DataMap.prototype.set = function (row, prop, value, source) {
4153
+ this.setVars.row = row;
4154
+ this.setVars.prop = prop;
4155
+ this.setVars.value = value;
4156
+ this.instance.PluginHooks.run('beforeSet', this.setVars, source || "datamapGet");
4157
+ if (typeof this.setVars.prop === 'string' && this.setVars.prop.indexOf('.') > -1) {
4158
+ var sliced = this.setVars.prop.split(".");
4159
+ var out = this.dataSource[this.setVars.row];
4160
+ for (var i = 0, ilen = sliced.length - 1; i < ilen; i++) {
4161
+ out = out[sliced[i]];
4162
+ }
4163
+ out[sliced[i]] = this.setVars.value;
4164
+ }
4165
+ else if (typeof this.setVars.prop === 'function') {
4166
+ /* see the `function` handler in `get` */
4167
+ this.setVars.prop(this.dataSource.slice(
4168
+ this.setVars.row,
4169
+ this.setVars.row + 1
4170
+ )[0], this.setVars.value);
4171
+ }
4172
+ else {
4173
+ this.dataSource[this.setVars.row][this.setVars.prop] = this.setVars.value;
4174
+ }
4175
+ };
4176
+
4177
+ /**
4178
+ * This ridiculous piece of code maps rows Id that are present in table data to those displayed for user.
4179
+ * The trick is, the physical row id (stored in settings.data) is not necessary the same
4180
+ * as the logical (displayed) row id (e.g. when sorting is applied).
4181
+ */
4182
+ Handsontable.DataMap.prototype.physicalRowsToLogical = function (index, amount) {
4183
+ var totalRows = this.instance.countRows();
4184
+ var physicRow = (totalRows + index) % totalRows;
4185
+ var logicRows = [];
4186
+ var rowsToRemove = amount;
4187
+
4188
+ while (physicRow < totalRows && rowsToRemove) {
4189
+ this.get(physicRow, 0); //this performs an actual mapping and saves the result to getVars
4190
+ logicRows.push(this.getVars.row);
4191
+
4192
+ rowsToRemove--;
4193
+ physicRow++;
4194
+ }
4195
+
4196
+ return logicRows;
4197
+ };
4198
+
4199
+ /**
4200
+ * Clears the data array
4201
+ */
4202
+ Handsontable.DataMap.prototype.clear = function () {
4203
+ for (var r = 0; r < this.instance.countRows(); r++) {
4204
+ for (var c = 0; c < this.instance.countCols(); c++) {
4205
+ this.set(r, this.colToProp(c), '');
4206
+ }
4207
+ }
4208
+ };
4209
+
4210
+ /**
4211
+ * Returns the data array
4212
+ * @return {Array}
4213
+ */
4214
+ Handsontable.DataMap.prototype.getAll = function () {
4215
+ return this.dataSource;
4216
+ };
4217
+
4218
+ /**
4219
+ * Returns data range as array
4220
+ * @param {Object} start Start selection position
4221
+ * @param {Object} end End selection position
4222
+ * @param {Number} destination Destination of datamap.get
4223
+ * @return {Array}
4224
+ */
4225
+ Handsontable.DataMap.prototype.getRange = function (start, end, destination) {
4226
+ var r, rlen, c, clen, output = [], row;
4227
+ var getFn = destination === this.DESTINATION_CLIPBOARD_GENERATOR ? this.getCopyable : this.get;
4228
+ rlen = Math.max(start.row, end.row);
4229
+ clen = Math.max(start.col, end.col);
4230
+ for (r = Math.min(start.row, end.row); r <= rlen; r++) {
4231
+ row = [];
4232
+ for (c = Math.min(start.col, end.col); c <= clen; c++) {
4233
+ row.push(getFn.call(this, r, this.colToProp(c)));
4234
+ }
4235
+ output.push(row);
4236
+ }
4237
+ return output;
4238
+ };
4239
+
4240
+ /**
4241
+ * Return data as text (tab separated columns)
4242
+ * @param {Object} start (Optional) Start selection position
4243
+ * @param {Object} end (Optional) End selection position
4244
+ * @return {String}
4245
+ */
4246
+ Handsontable.DataMap.prototype.getText = function (start, end) {
4247
+ return SheetClip.stringify(this.getRange(start, end, this.DESTINATION_RENDERER));
4248
+ };
4249
+
4250
+ /**
4251
+ * Return data as copyable text (tab separated columns intended for clipboard copy to an external application)
4252
+ * @param {Object} start (Optional) Start selection position
4253
+ * @param {Object} end (Optional) End selection position
4254
+ * @return {String}
4255
+ */
4256
+ Handsontable.DataMap.prototype.getCopyableText = function (start, end) {
4257
+ return SheetClip.stringify(this.getRange(start, end, this.DESTINATION_CLIPBOARD_GENERATOR));
4258
+ };
4259
+
4260
+ })(Handsontable);
4261
+
4203
4262
  (function (Handsontable) {
4204
4263
  'use strict';
4205
4264
 
@@ -5711,7 +5770,8 @@ Handsontable.HandsontableCell = {
5711
5770
 
5712
5771
  Handsontable.PasswordCell = {
5713
5772
  editor: Handsontable.editors.PasswordEditor,
5714
- renderer: Handsontable.renderers.PasswordRenderer
5773
+ renderer: Handsontable.renderers.PasswordRenderer,
5774
+ copyable: false
5715
5775
  };
5716
5776
 
5717
5777
  Handsontable.DropdownCell = {