dataMap.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  4. var _SheetClip = require('./../lib/SheetClip/SheetClip.js');
  5. var _SheetClip2 = _interopRequireDefault(_SheetClip);
  6. var _data = require('./helpers/data');
  7. var _setting = require('./helpers/setting');
  8. var _object = require('./helpers/object');
  9. var _array = require('./helpers/array');
  10. var _interval = require('./utils/interval');
  11. var _interval2 = _interopRequireDefault(_interval);
  12. var _number = require('./helpers/number');
  13. var _multiMap = require('./multiMap');
  14. var _multiMap2 = _interopRequireDefault(_multiMap);
  15. var _pluginHooks = require('./pluginHooks');
  16. var _pluginHooks2 = _interopRequireDefault(_pluginHooks);
  17. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  18. /**
  19. * Utility class that gets and saves data from/to the data source using mapping of columns numbers to object property names
  20. * @todo refactor arguments of methods getRange, getText to be numbers (not objects)
  21. * @todo remove priv, GridSettings from object constructor
  22. *
  23. * @param {Object} instance Instance of Handsontable
  24. * @param {*} priv
  25. * @param {*} GridSettings Grid settings
  26. * @util
  27. * @class DataMap
  28. */
  29. function DataMap(instance, priv, GridSettings) {
  30. var _this = this;
  31. this.instance = instance;
  32. this.priv = priv;
  33. this.GridSettings = GridSettings;
  34. this.dataSource = this.instance.getSettings().data;
  35. this.cachedLength = null;
  36. this.skipCache = false;
  37. this.latestSourceRowsCount = 0;
  38. if (this.dataSource && this.dataSource[0]) {
  39. this.duckSchema = this.recursiveDuckSchema(this.dataSource[0]);
  40. } else {
  41. this.duckSchema = {};
  42. }
  43. this.createMap();
  44. this.interval = _interval2.default.create(function () {
  45. return _this.clearLengthCache();
  46. }, '15fps');
  47. this.instance.addHook('skipLengthCache', function (delay) {
  48. return _this.onSkipLengthCache(delay);
  49. });
  50. this.onSkipLengthCache(500);
  51. }
  52. DataMap.prototype.DESTINATION_RENDERER = 1;
  53. DataMap.prototype.DESTINATION_CLIPBOARD_GENERATOR = 2;
  54. /**
  55. * @param {Object|Array} object
  56. * @returns {Object|Array}
  57. */
  58. DataMap.prototype.recursiveDuckSchema = function (object) {
  59. return (0, _object.duckSchema)(object);
  60. };
  61. /**
  62. * @param {Object} schema
  63. * @param {Number} lastCol
  64. * @param {Number} parent
  65. * @returns {Number}
  66. */
  67. DataMap.prototype.recursiveDuckColumns = function (schema, lastCol, parent) {
  68. var prop, i;
  69. if (typeof lastCol === 'undefined') {
  70. lastCol = 0;
  71. parent = '';
  72. }
  73. if ((typeof schema === 'undefined' ? 'undefined' : _typeof(schema)) === 'object' && !Array.isArray(schema)) {
  74. for (i in schema) {
  75. if ((0, _object.hasOwnProperty)(schema, i)) {
  76. if (schema[i] === null) {
  77. prop = parent + i;
  78. this.colToPropCache.push(prop);
  79. this.propToColCache.set(prop, lastCol);
  80. lastCol++;
  81. } else {
  82. lastCol = this.recursiveDuckColumns(schema[i], lastCol, i + '.');
  83. }
  84. }
  85. }
  86. }
  87. return lastCol;
  88. };
  89. DataMap.prototype.createMap = function () {
  90. var i = void 0;
  91. var schema = this.getSchema();
  92. if (typeof schema === 'undefined') {
  93. throw new Error('trying to create `columns` definition but you didn\'t provide `schema` nor `data`');
  94. }
  95. this.colToPropCache = [];
  96. this.propToColCache = new _multiMap2.default();
  97. var columns = this.instance.getSettings().columns;
  98. if (columns) {
  99. var maxCols = this.instance.getSettings().maxCols;
  100. var columnsLen = Math.min(maxCols, columns.length);
  101. var filteredIndex = 0;
  102. var columnsAsFunc = false;
  103. var schemaLen = (0, _object.deepObjectSize)(schema);
  104. if (typeof columns === 'function') {
  105. columnsLen = schemaLen > 0 ? schemaLen : this.instance.countSourceCols();
  106. columnsAsFunc = true;
  107. }
  108. for (i = 0; i < columnsLen; i++) {
  109. var column = columnsAsFunc ? columns(i) : columns[i];
  110. if ((0, _object.isObject)(column)) {
  111. if (typeof column.data !== 'undefined') {
  112. var index = columnsAsFunc ? filteredIndex : i;
  113. this.colToPropCache[index] = column.data;
  114. this.propToColCache.set(column.data, index);
  115. }
  116. filteredIndex++;
  117. }
  118. }
  119. } else {
  120. this.recursiveDuckColumns(schema);
  121. }
  122. };
  123. /**
  124. * Returns property name that corresponds with the given column index.
  125. *
  126. * @param {Number} col
  127. * @returns {Number}
  128. */
  129. DataMap.prototype.colToProp = function (col) {
  130. col = this.instance.runHooks('modifyCol', col);
  131. if (!isNaN(col) && this.colToPropCache && typeof this.colToPropCache[col] !== 'undefined') {
  132. return this.colToPropCache[col];
  133. }
  134. return col;
  135. };
  136. /**
  137. * @param {Object} prop
  138. * @fires Hooks#modifyCol
  139. * @returns {*}
  140. */
  141. DataMap.prototype.propToCol = function (prop) {
  142. var col;
  143. if (typeof this.propToColCache.get(prop) === 'undefined') {
  144. col = prop;
  145. } else {
  146. col = this.propToColCache.get(prop);
  147. }
  148. col = this.instance.runHooks('unmodifyCol', col);
  149. return col;
  150. };
  151. /**
  152. * @returns {Object}
  153. */
  154. DataMap.prototype.getSchema = function () {
  155. var schema = this.instance.getSettings().dataSchema;
  156. if (schema) {
  157. if (typeof schema === 'function') {
  158. return schema();
  159. }
  160. return schema;
  161. }
  162. return this.duckSchema;
  163. };
  164. /**
  165. * Creates row at the bottom of the data array.
  166. *
  167. * @param {Number} [index] Index of the row before which the new row will be inserted.
  168. * @param {Number} [amount] An amount of rows to add.
  169. * @param {String} [source] Source of method call.
  170. * @fires Hooks#afterCreateRow
  171. * @returns {Number} Returns number of created rows.
  172. */
  173. DataMap.prototype.createRow = function (index, amount, source) {
  174. var row,
  175. colCount = this.instance.countCols(),
  176. numberOfCreatedRows = 0,
  177. currentIndex;
  178. if (!amount) {
  179. amount = 1;
  180. }
  181. if (typeof index !== 'number' || index >= this.instance.countSourceRows()) {
  182. index = this.instance.countSourceRows();
  183. }
  184. this.instance.runHooks('beforeCreateRow', index, amount, source);
  185. currentIndex = index;
  186. var maxRows = this.instance.getSettings().maxRows;
  187. while (numberOfCreatedRows < amount && this.instance.countSourceRows() < maxRows) {
  188. if (this.instance.dataType === 'array') {
  189. if (this.instance.getSettings().dataSchema) {
  190. // Clone template array
  191. row = (0, _object.deepClone)(this.getSchema());
  192. } else {
  193. row = [];
  194. /* eslint-disable no-loop-func */
  195. (0, _number.rangeEach)(colCount - 1, function () {
  196. return row.push(null);
  197. });
  198. }
  199. } else if (this.instance.dataType === 'function') {
  200. row = this.instance.getSettings().dataSchema(index);
  201. } else {
  202. row = {};
  203. (0, _object.deepExtend)(row, this.getSchema());
  204. }
  205. if (index === this.instance.countSourceRows()) {
  206. this.dataSource.push(row);
  207. } else {
  208. this.spliceData(index, 0, row);
  209. }
  210. numberOfCreatedRows++;
  211. currentIndex++;
  212. }
  213. this.instance.runHooks('afterCreateRow', index, numberOfCreatedRows, source);
  214. this.instance.forceFullRender = true; // used when data was changed
  215. return numberOfCreatedRows;
  216. };
  217. /**
  218. * Creates col at the right of the data array.
  219. *
  220. * @param {Number} [index] Index of the column before which the new column will be inserted
  221. * @param {Number} [amount] An amount of columns to add.
  222. * @param {String} [source] Source of method call.
  223. * @fires Hooks#afterCreateCol
  224. * @returns {Number} Returns number of created columns
  225. */
  226. DataMap.prototype.createCol = function (index, amount, source) {
  227. if (!this.instance.isColumnModificationAllowed()) {
  228. 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.' + 'If you want to be able to add new columns, you have to use array datasource.');
  229. }
  230. var rlen = this.instance.countSourceRows(),
  231. data = this.dataSource,
  232. constructor,
  233. numberOfCreatedCols = 0,
  234. currentIndex;
  235. if (!amount) {
  236. amount = 1;
  237. }
  238. if (typeof index !== 'number' || index >= this.instance.countCols()) {
  239. index = this.instance.countCols();
  240. }
  241. this.instance.runHooks('beforeCreateCol', index, amount, source);
  242. currentIndex = index;
  243. var maxCols = this.instance.getSettings().maxCols;
  244. while (numberOfCreatedCols < amount && this.instance.countCols() < maxCols) {
  245. constructor = (0, _setting.columnFactory)(this.GridSettings, this.priv.columnsSettingConflicts);
  246. if (typeof index !== 'number' || index >= this.instance.countCols()) {
  247. if (rlen > 0) {
  248. for (var r = 0; r < rlen; r++) {
  249. if (typeof data[r] === 'undefined') {
  250. data[r] = [];
  251. }
  252. data[r].push(null);
  253. }
  254. } else {
  255. data.push([null]);
  256. }
  257. // Add new column constructor
  258. this.priv.columnSettings.push(constructor);
  259. } else {
  260. for (var _r = 0; _r < rlen; _r++) {
  261. data[_r].splice(currentIndex, 0, null);
  262. }
  263. // Add new column constructor at given index
  264. this.priv.columnSettings.splice(currentIndex, 0, constructor);
  265. }
  266. numberOfCreatedCols++;
  267. currentIndex++;
  268. }
  269. this.instance.runHooks('afterCreateCol', index, numberOfCreatedCols, source);
  270. this.instance.forceFullRender = true; // used when data was changed
  271. return numberOfCreatedCols;
  272. };
  273. /**
  274. * Removes row from the data array.
  275. *
  276. * @param {Number} [index] Index of the row to be removed. If not provided, the last row will be removed
  277. * @param {Number} [amount] Amount of the rows to be removed. If not provided, one row will be removed
  278. * @param {String} [source] Source of method call.
  279. * @fires Hooks#beforeRemoveRow
  280. * @fires Hooks#afterRemoveRow
  281. */
  282. DataMap.prototype.removeRow = function (index, amount, source) {
  283. if (!amount) {
  284. amount = 1;
  285. }
  286. if (typeof index !== 'number') {
  287. index = -amount;
  288. }
  289. amount = this.instance.runHooks('modifyRemovedAmount', amount, index);
  290. index = (this.instance.countSourceRows() + index) % this.instance.countSourceRows();
  291. var logicRows = this.physicalRowsToLogical(index, amount);
  292. var actionWasNotCancelled = this.instance.runHooks('beforeRemoveRow', index, amount, logicRows, source);
  293. if (actionWasNotCancelled === false) {
  294. return;
  295. }
  296. var data = this.dataSource;
  297. var newData = void 0;
  298. newData = this.filterData(index, amount);
  299. if (newData) {
  300. data.length = 0;
  301. Array.prototype.push.apply(data, newData);
  302. }
  303. this.instance.runHooks('afterRemoveRow', index, amount, logicRows, source);
  304. this.instance.forceFullRender = true; // used when data was changed
  305. };
  306. /**
  307. * Removes column from the data array.
  308. *
  309. * @param {Number} [index] Index of the column to be removed. If not provided, the last column will be removed
  310. * @param {Number} [amount] Amount of the columns to be removed. If not provided, one column will be removed
  311. * @param {String} [source] Source of method call.
  312. * @fires Hooks#beforeRemoveCol
  313. * @fires Hooks#afterRemoveCol
  314. */
  315. DataMap.prototype.removeCol = function (index, amount, source) {
  316. if (this.instance.dataType === 'object' || this.instance.getSettings().columns) {
  317. throw new Error('cannot remove column with object data source or columns option specified');
  318. }
  319. if (!amount) {
  320. amount = 1;
  321. }
  322. if (typeof index !== 'number') {
  323. index = -amount;
  324. }
  325. index = (this.instance.countCols() + index) % this.instance.countCols();
  326. var logicColumns = this.physicalColumnsToLogical(index, amount);
  327. var descendingLogicColumns = logicColumns.slice(0).sort(function (a, b) {
  328. return b - a;
  329. });
  330. var actionWasNotCancelled = this.instance.runHooks('beforeRemoveCol', index, amount, logicColumns, source);
  331. if (actionWasNotCancelled === false) {
  332. return;
  333. }
  334. var isTableUniform = true;
  335. var removedColumnsCount = descendingLogicColumns.length;
  336. var data = this.dataSource;
  337. for (var c = 0; c < removedColumnsCount; c++) {
  338. if (isTableUniform && logicColumns[0] !== logicColumns[c] - c) {
  339. isTableUniform = false;
  340. }
  341. }
  342. if (isTableUniform) {
  343. for (var r = 0, rlen = this.instance.countSourceRows(); r < rlen; r++) {
  344. data[r].splice(logicColumns[0], amount);
  345. }
  346. } else {
  347. for (var _r2 = 0, _rlen = this.instance.countSourceRows(); _r2 < _rlen; _r2++) {
  348. for (var _c = 0; _c < removedColumnsCount; _c++) {
  349. data[_r2].splice(descendingLogicColumns[_c], 1);
  350. }
  351. }
  352. for (var _c2 = 0; _c2 < removedColumnsCount; _c2++) {
  353. this.priv.columnSettings.splice(logicColumns[_c2], 1);
  354. }
  355. }
  356. this.instance.runHooks('afterRemoveCol', index, amount, logicColumns, source);
  357. this.instance.forceFullRender = true; // used when data was changed
  358. };
  359. /**
  360. * Add/Removes data from the column.
  361. *
  362. * @param {Number} col Index of column in which do you want to do splice
  363. * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end
  364. * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed
  365. * @returns {Array} Returns removed portion of columns
  366. */
  367. DataMap.prototype.spliceCol = function (col, index, amount /* , elements...*/) {
  368. var elements = arguments.length >= 4 ? [].slice.call(arguments, 3) : [];
  369. var colData = this.instance.getDataAtCol(col);
  370. var removed = colData.slice(index, index + amount);
  371. var after = colData.slice(index + amount);
  372. (0, _array.extendArray)(elements, after);
  373. var i = 0;
  374. while (i < amount) {
  375. elements.push(null); // add null in place of removed elements
  376. i++;
  377. }
  378. (0, _array.to2dArray)(elements);
  379. this.instance.populateFromArray(index, col, elements, null, null, 'spliceCol');
  380. return removed;
  381. };
  382. /**
  383. * Add/Removes data from the row.
  384. *
  385. * @param {Number} row Index of row in which do you want to do splice
  386. * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end
  387. * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed
  388. * @returns {Array} Returns removed portion of rows
  389. */
  390. DataMap.prototype.spliceRow = function (row, index, amount /* , elements...*/) {
  391. var elements = arguments.length >= 4 ? [].slice.call(arguments, 3) : [];
  392. var rowData = this.instance.getSourceDataAtRow(row);
  393. var removed = rowData.slice(index, index + amount);
  394. var after = rowData.slice(index + amount);
  395. (0, _array.extendArray)(elements, after);
  396. var i = 0;
  397. while (i < amount) {
  398. elements.push(null); // add null in place of removed elements
  399. i++;
  400. }
  401. this.instance.populateFromArray(row, index, [elements], null, null, 'spliceRow');
  402. return removed;
  403. };
  404. /**
  405. * Add/remove row(s) to/from the data source.
  406. *
  407. * @param {Number} index Index of the element to remove.
  408. * @param {Number} amount Number of rows to add/remove.
  409. * @param {Object} element Row to add.
  410. */
  411. DataMap.prototype.spliceData = function (index, amount, element) {
  412. var continueSplicing = this.instance.runHooks('beforeDataSplice', index, amount, element);
  413. if (continueSplicing !== false) {
  414. this.dataSource.splice(index, amount, element);
  415. }
  416. };
  417. /**
  418. * Filter unwanted data elements from the data source.
  419. *
  420. * @param {Number} index Index of the element to remove.
  421. * @param {Number} amount Number of rows to add/remove.
  422. * @returns {Array}
  423. */
  424. DataMap.prototype.filterData = function (index, amount) {
  425. var logicRows = this.physicalRowsToLogical(index, amount);
  426. var continueSplicing = this.instance.runHooks('beforeDataFilter', index, amount, logicRows);
  427. if (continueSplicing !== false) {
  428. var newData = this.dataSource.filter(function (row, index) {
  429. return logicRows.indexOf(index) == -1;
  430. });
  431. return newData;
  432. }
  433. };
  434. /**
  435. * Returns single value from the data array.
  436. *
  437. * @param {Number} row
  438. * @param {Number} prop
  439. */
  440. DataMap.prototype.get = function (row, prop) {
  441. row = this.instance.runHooks('modifyRow', row);
  442. var dataRow = this.dataSource[row];
  443. // TODO: To remove, use 'modifyData' hook instead (see below)
  444. var modifiedRowData = this.instance.runHooks('modifyRowData', row);
  445. dataRow = isNaN(modifiedRowData) ? modifiedRowData : dataRow;
  446. //
  447. var value = null;
  448. // try to get value under property `prop` (includes dot)
  449. if (dataRow && dataRow.hasOwnProperty && (0, _object.hasOwnProperty)(dataRow, prop)) {
  450. value = dataRow[prop];
  451. } else if (typeof prop === 'string' && prop.indexOf('.') > -1) {
  452. var sliced = prop.split('.');
  453. var out = dataRow;
  454. if (!out) {
  455. return null;
  456. }
  457. for (var i = 0, ilen = sliced.length; i < ilen; i++) {
  458. out = out[sliced[i]];
  459. if (typeof out === 'undefined') {
  460. return null;
  461. }
  462. }
  463. value = out;
  464. } else if (typeof prop === 'function') {
  465. /**
  466. * allows for interacting with complex structures, for example
  467. * d3/jQuery getter/setter properties:
  468. *
  469. * {columns: [{
  470. * data: function(row, value){
  471. * if(arguments.length === 1){
  472. * return row.property();
  473. * }
  474. * row.property(value);
  475. * }
  476. * }]}
  477. */
  478. value = prop(this.dataSource.slice(row, row + 1)[0]);
  479. }
  480. if (this.instance.hasHook('modifyData')) {
  481. var valueHolder = (0, _object.createObjectPropListener)(value);
  482. this.instance.runHooks('modifyData', row, this.propToCol(prop), valueHolder, 'get');
  483. if (valueHolder.isTouched()) {
  484. value = valueHolder.value;
  485. }
  486. }
  487. return value;
  488. };
  489. var copyableLookup = (0, _data.cellMethodLookupFactory)('copyable', false);
  490. /**
  491. * Returns single value from the data array (intended for clipboard copy to an external application).
  492. *
  493. * @param {Number} row
  494. * @param {Number} prop
  495. * @returns {String}
  496. */
  497. DataMap.prototype.getCopyable = function (row, prop) {
  498. if (copyableLookup.call(this.instance, row, this.propToCol(prop))) {
  499. return this.get(row, prop);
  500. }
  501. return '';
  502. };
  503. /**
  504. * Saves single value to the data array.
  505. *
  506. * @param {Number} row
  507. * @param {Number} prop
  508. * @param {String} value
  509. * @param {String} [source] Source of hook runner.
  510. */
  511. DataMap.prototype.set = function (row, prop, value, source) {
  512. row = this.instance.runHooks('modifyRow', row, source || 'datamapGet');
  513. var dataRow = this.dataSource[row];
  514. // TODO: To remove, use 'modifyData' hook instead (see below)
  515. var modifiedRowData = this.instance.runHooks('modifyRowData', row);
  516. dataRow = isNaN(modifiedRowData) ? modifiedRowData : dataRow;
  517. //
  518. if (this.instance.hasHook('modifyData')) {
  519. var valueHolder = (0, _object.createObjectPropListener)(value);
  520. this.instance.runHooks('modifyData', row, this.propToCol(prop), valueHolder, 'set');
  521. if (valueHolder.isTouched()) {
  522. value = valueHolder.value;
  523. }
  524. }
  525. // try to set value under property `prop` (includes dot)
  526. if (dataRow && dataRow.hasOwnProperty && (0, _object.hasOwnProperty)(dataRow, prop)) {
  527. dataRow[prop] = value;
  528. } else if (typeof prop === 'string' && prop.indexOf('.') > -1) {
  529. var sliced = prop.split('.');
  530. var out = dataRow;
  531. var i = 0;
  532. var ilen = void 0;
  533. for (i = 0, ilen = sliced.length - 1; i < ilen; i++) {
  534. if (typeof out[sliced[i]] === 'undefined') {
  535. out[sliced[i]] = {};
  536. }
  537. out = out[sliced[i]];
  538. }
  539. out[sliced[i]] = value;
  540. } else if (typeof prop === 'function') {
  541. /* see the `function` handler in `get` */
  542. prop(this.dataSource.slice(row, row + 1)[0], value);
  543. } else {
  544. dataRow[prop] = value;
  545. }
  546. };
  547. /**
  548. * This ridiculous piece of code maps rows Id that are present in table data to those displayed for user.
  549. * The trick is, the physical row id (stored in settings.data) is not necessary the same
  550. * as the logical (displayed) row id (e.g. when sorting is applied).
  551. *
  552. * @param {Number} index
  553. * @param {Number} amount
  554. * @fires Hooks#modifyRow
  555. * @returns {Number}
  556. */
  557. DataMap.prototype.physicalRowsToLogical = function (index, amount) {
  558. var totalRows = this.instance.countSourceRows();
  559. var physicRow = (totalRows + index) % totalRows;
  560. var logicRows = [];
  561. var rowsToRemove = amount;
  562. var row;
  563. while (physicRow < totalRows && rowsToRemove) {
  564. row = this.instance.runHooks('modifyRow', physicRow);
  565. logicRows.push(row);
  566. rowsToRemove--;
  567. physicRow++;
  568. }
  569. return logicRows;
  570. };
  571. /**
  572. *
  573. * @param index
  574. * @param amount
  575. * @returns {Array}
  576. */
  577. DataMap.prototype.physicalColumnsToLogical = function (index, amount) {
  578. var totalCols = this.instance.countCols();
  579. var physicalCol = (totalCols + index) % totalCols;
  580. var logicalCols = [];
  581. var colsToRemove = amount;
  582. while (physicalCol < totalCols && colsToRemove) {
  583. var col = this.instance.runHooks('modifyCol', physicalCol);
  584. logicalCols.push(col);
  585. colsToRemove--;
  586. physicalCol++;
  587. }
  588. return logicalCols;
  589. };
  590. /**
  591. * Clears the data array.
  592. */
  593. DataMap.prototype.clear = function () {
  594. for (var r = 0; r < this.instance.countSourceRows(); r++) {
  595. for (var c = 0; c < this.instance.countCols(); c++) {
  596. this.set(r, this.colToProp(c), '');
  597. }
  598. }
  599. };
  600. /**
  601. * Clear cached data length.
  602. */
  603. DataMap.prototype.clearLengthCache = function () {
  604. this.cachedLength = null;
  605. };
  606. /**
  607. * Get data length.
  608. *
  609. * @returns {Number}
  610. */
  611. DataMap.prototype.getLength = function () {
  612. var _this2 = this;
  613. var maxRows = void 0,
  614. maxRowsFromSettings = this.instance.getSettings().maxRows;
  615. if (maxRowsFromSettings < 0 || maxRowsFromSettings === 0) {
  616. maxRows = 0;
  617. } else {
  618. maxRows = maxRowsFromSettings || Infinity;
  619. }
  620. var length = this.instance.countSourceRows();
  621. if (this.instance.hasHook('modifyRow')) {
  622. var reValidate = this.skipCache;
  623. this.interval.start();
  624. if (length !== this.latestSourceRowsCount) {
  625. reValidate = true;
  626. }
  627. this.latestSourceRowsCount = length;
  628. if (this.cachedLength === null || reValidate) {
  629. (0, _number.rangeEach)(length - 1, function (row) {
  630. row = _this2.instance.runHooks('modifyRow', row);
  631. if (row === null) {
  632. --length;
  633. }
  634. });
  635. this.cachedLength = length;
  636. } else {
  637. length = this.cachedLength;
  638. }
  639. } else {
  640. this.interval.stop();
  641. }
  642. return Math.min(length, maxRows);
  643. };
  644. /**
  645. * Returns the data array.
  646. *
  647. * @returns {Array}
  648. */
  649. DataMap.prototype.getAll = function () {
  650. var start = {
  651. row: 0,
  652. col: 0
  653. };
  654. var end = {
  655. row: Math.max(this.instance.countSourceRows() - 1, 0),
  656. col: Math.max(this.instance.countCols() - 1, 0)
  657. };
  658. if (start.row - end.row === 0 && !this.instance.countSourceRows()) {
  659. return [];
  660. }
  661. return this.getRange(start, end, DataMap.prototype.DESTINATION_RENDERER);
  662. };
  663. /**
  664. * Returns data range as array.
  665. *
  666. * @param {Object} [start] Start selection position
  667. * @param {Object} [end] End selection position
  668. * @param {Number} destination Destination of datamap.get
  669. * @returns {Array}
  670. */
  671. DataMap.prototype.getRange = function (start, end, destination) {
  672. var r,
  673. rlen,
  674. c,
  675. clen,
  676. output = [],
  677. row;
  678. var maxRows = this.instance.getSettings().maxRows;
  679. var maxCols = this.instance.getSettings().maxCols;
  680. if (maxRows === 0 || maxCols === 0) {
  681. return [];
  682. }
  683. var getFn = destination === this.DESTINATION_CLIPBOARD_GENERATOR ? this.getCopyable : this.get;
  684. rlen = Math.min(Math.max(maxRows - 1, 0), Math.max(start.row, end.row));
  685. clen = Math.min(Math.max(maxCols - 1, 0), Math.max(start.col, end.col));
  686. for (r = Math.min(start.row, end.row); r <= rlen; r++) {
  687. row = [];
  688. var physicalRow = this.instance.runHooks('modifyRow', r);
  689. for (c = Math.min(start.col, end.col); c <= clen; c++) {
  690. if (physicalRow === null) {
  691. break;
  692. }
  693. row.push(getFn.call(this, r, this.colToProp(c)));
  694. }
  695. if (physicalRow !== null) {
  696. output.push(row);
  697. }
  698. }
  699. return output;
  700. };
  701. /**
  702. * Return data as text (tab separated columns).
  703. *
  704. * @param {Object} [start] Start selection position
  705. * @param {Object} [end] End selection position
  706. * @returns {String}
  707. */
  708. DataMap.prototype.getText = function (start, end) {
  709. return _SheetClip2.default.stringify(this.getRange(start, end, this.DESTINATION_RENDERER));
  710. };
  711. /**
  712. * Return data as copyable text (tab separated columns intended for clipboard copy to an external application).
  713. *
  714. * @param {Object} [start] Start selection position
  715. * @param {Object} [end] End selection position
  716. * @returns {String}
  717. */
  718. DataMap.prototype.getCopyableText = function (start, end) {
  719. return _SheetClip2.default.stringify(this.getRange(start, end, this.DESTINATION_CLIPBOARD_GENERATOR));
  720. };
  721. /**
  722. * `skipLengthCache` callback.
  723. * @private
  724. * @param {Number} delay Time of the delay in milliseconds.
  725. */
  726. DataMap.prototype.onSkipLengthCache = function (delay) {
  727. var _this3 = this;
  728. this.skipCache = true;
  729. setTimeout(function () {
  730. _this3.skipCache = false;
  731. }, delay);
  732. };
  733. /**
  734. * Destroy instance.
  735. */
  736. DataMap.prototype.destroy = function () {
  737. this.interval.stop();
  738. this.interval = null;
  739. this.instance = null;
  740. this.priv = null;
  741. this.GridSettings = null;
  742. this.dataSource = null;
  743. this.cachedLength = null;
  744. this.duckSchema = null;
  745. };
  746. exports.default = DataMap;