handsontable 15.2.0 → 15.3.0-next-6f5f494-20250424

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.
Files changed (224) hide show
  1. package/3rdparty/walkontable/src/cell/range.js +14 -0
  2. package/3rdparty/walkontable/src/cell/range.mjs +14 -0
  3. package/3rdparty/walkontable/src/core/_base.js +2 -2
  4. package/3rdparty/walkontable/src/core/_base.mjs +3 -3
  5. package/3rdparty/walkontable/src/overlay/_base.js +0 -2
  6. package/3rdparty/walkontable/src/overlay/_base.mjs +0 -2
  7. package/3rdparty/walkontable/src/overlay/bottom.js +9 -6
  8. package/3rdparty/walkontable/src/overlay/bottom.mjs +9 -6
  9. package/3rdparty/walkontable/src/overlay/inlineStart.js +8 -5
  10. package/3rdparty/walkontable/src/overlay/inlineStart.mjs +8 -5
  11. package/3rdparty/walkontable/src/overlay/top.js +11 -6
  12. package/3rdparty/walkontable/src/overlay/top.mjs +11 -6
  13. package/3rdparty/walkontable/src/overlays.js +15 -11
  14. package/3rdparty/walkontable/src/overlays.mjs +15 -11
  15. package/3rdparty/walkontable/src/renderer/rowHeaders.js +4 -1
  16. package/3rdparty/walkontable/src/renderer/rowHeaders.mjs +4 -1
  17. package/3rdparty/walkontable/src/selection/border/border.js +5 -0
  18. package/3rdparty/walkontable/src/selection/border/border.mjs +5 -0
  19. package/3rdparty/walkontable/src/table/mixin/stickyColumnsStart.js +3 -4
  20. package/3rdparty/walkontable/src/table/mixin/stickyColumnsStart.mjs +3 -4
  21. package/3rdparty/walkontable/src/table/mixin/stickyRowsBottom.js +8 -10
  22. package/3rdparty/walkontable/src/table/mixin/stickyRowsBottom.mjs +8 -10
  23. package/3rdparty/walkontable/src/table/mixin/stickyRowsTop.js +3 -4
  24. package/3rdparty/walkontable/src/table/mixin/stickyRowsTop.mjs +3 -4
  25. package/3rdparty/walkontable/src/table.js +5 -2
  26. package/3rdparty/walkontable/src/table.mjs +5 -2
  27. package/3rdparty/walkontable/src/utils/orderView/viewDiffer/viewOrder.js +0 -2
  28. package/3rdparty/walkontable/src/utils/orderView/viewDiffer/viewOrder.mjs +0 -2
  29. package/CHANGELOG.md +45 -1
  30. package/README.md +1 -1
  31. package/base.js +2 -2
  32. package/base.mjs +2 -2
  33. package/core/focusCatcher/focusDetector.js +1 -1
  34. package/core/focusCatcher/focusDetector.mjs +2 -2
  35. package/core/hooks/constants.js +8 -0
  36. package/core/hooks/constants.mjs +8 -0
  37. package/core/hooks/index.d.ts +1 -0
  38. package/core/viewportScroll/scrollStrategies/columnHeaderScroll.js +7 -5
  39. package/core/viewportScroll/scrollStrategies/columnHeaderScroll.mjs +7 -5
  40. package/core/viewportScroll/scrollStrategies/focusScroll.js +8 -1
  41. package/core/viewportScroll/scrollStrategies/focusScroll.mjs +8 -1
  42. package/core/viewportScroll/scrollStrategies/multipleScroll.js +13 -1
  43. package/core/viewportScroll/scrollStrategies/multipleScroll.mjs +13 -1
  44. package/core/viewportScroll/scrollStrategies/noncontiguousScroll.js +13 -1
  45. package/core/viewportScroll/scrollStrategies/noncontiguousScroll.mjs +13 -1
  46. package/core/viewportScroll/scrollStrategies/rowHeaderScroll.js +7 -5
  47. package/core/viewportScroll/scrollStrategies/rowHeaderScroll.mjs +7 -5
  48. package/core/viewportScroll/scrollStrategies/singleScroll.js +8 -4
  49. package/core/viewportScroll/scrollStrategies/singleScroll.mjs +8 -4
  50. package/core/viewportScroll/utils.js +111 -0
  51. package/core/viewportScroll/utils.mjs +106 -0
  52. package/core.d.ts +2 -3
  53. package/core.js +125 -71
  54. package/core.mjs +126 -72
  55. package/dataMap/dataMap.js +0 -7
  56. package/dataMap/dataMap.mjs +0 -7
  57. package/dataMap/metaManager/index.js +8 -9
  58. package/dataMap/metaManager/index.mjs +8 -9
  59. package/dataMap/metaManager/mods/dynamicCellMeta.js +4 -1
  60. package/dataMap/metaManager/mods/dynamicCellMeta.mjs +4 -1
  61. package/dist/handsontable.css +4 -15
  62. package/dist/handsontable.full.css +4 -17
  63. package/dist/handsontable.full.js +4573 -4013
  64. package/dist/handsontable.full.min.css +3 -3
  65. package/dist/handsontable.full.min.js +148 -148
  66. package/dist/handsontable.js +4287 -3730
  67. package/dist/handsontable.min.css +3 -3
  68. package/dist/handsontable.min.js +19 -19
  69. package/editorManager.js +1 -7
  70. package/editorManager.mjs +1 -7
  71. package/editors/autocompleteEditor/autocompleteEditor.js +31 -7
  72. package/editors/autocompleteEditor/autocompleteEditor.mjs +31 -7
  73. package/focusManager.js +4 -2
  74. package/focusManager.mjs +4 -2
  75. package/helpers/browser.js +1 -1
  76. package/helpers/browser.mjs +1 -1
  77. package/helpers/dom/element.d.ts +1 -0
  78. package/helpers/dom/element.js +20 -0
  79. package/helpers/dom/element.mjs +19 -0
  80. package/helpers/mixed.js +2 -2
  81. package/helpers/mixed.mjs +2 -2
  82. package/helpers/object.js +3 -0
  83. package/helpers/object.mjs +3 -0
  84. package/package.json +1 -1
  85. package/plugins/autoColumnSize/autoColumnSize.js +38 -17
  86. package/plugins/autoColumnSize/autoColumnSize.mjs +38 -17
  87. package/plugins/autoRowSize/autoRowSize.js +12 -6
  88. package/plugins/autoRowSize/autoRowSize.mjs +12 -6
  89. package/plugins/columnSorting/columnSorting.js +0 -4
  90. package/plugins/columnSorting/columnSorting.mjs +0 -4
  91. package/plugins/comments/comments.js +1 -0
  92. package/plugins/comments/comments.mjs +1 -0
  93. package/plugins/contextMenu/menu/defaultShortcutsList.js +2 -2
  94. package/plugins/contextMenu/menu/defaultShortcutsList.mjs +2 -2
  95. package/plugins/contextMenu/menu/menu.js +1 -0
  96. package/plugins/contextMenu/menu/menu.mjs +1 -0
  97. package/plugins/contextMenu/menu/positioner.js +10 -2
  98. package/plugins/contextMenu/menu/positioner.mjs +10 -2
  99. package/plugins/contextMenu/predefinedItems/redo.js +3 -3
  100. package/plugins/contextMenu/predefinedItems/redo.mjs +3 -3
  101. package/plugins/contextMenu/predefinedItems/undo.js +3 -3
  102. package/plugins/contextMenu/predefinedItems/undo.mjs +3 -3
  103. package/plugins/copyPaste/copyPaste.js +12 -9
  104. package/plugins/copyPaste/copyPaste.mjs +12 -9
  105. package/plugins/copyPaste/pasteEvent.js +3 -0
  106. package/plugins/copyPaste/pasteEvent.mjs +3 -0
  107. package/plugins/exportFile/exportFile.d.ts +1 -0
  108. package/plugins/exportFile/exportFile.js +2 -1
  109. package/plugins/exportFile/exportFile.mjs +2 -1
  110. package/plugins/exportFile/types/csv.js +76 -11
  111. package/plugins/exportFile/types/csv.mjs +76 -11
  112. package/plugins/filters/filters.js +24 -23
  113. package/plugins/filters/filters.mjs +24 -23
  114. package/plugins/filters/ui/multipleSelect.js +7 -1
  115. package/plugins/filters/ui/multipleSelect.mjs +7 -1
  116. package/plugins/formulas/formulas.d.ts +1 -1
  117. package/plugins/formulas/formulas.js +57 -60
  118. package/plugins/formulas/formulas.mjs +59 -62
  119. package/plugins/formulas/indexSyncer/axisSyncer.js +5 -1
  120. package/plugins/formulas/indexSyncer/axisSyncer.mjs +5 -1
  121. package/plugins/hiddenColumns/hiddenColumns.js +1 -1
  122. package/plugins/hiddenColumns/hiddenColumns.mjs +1 -1
  123. package/plugins/hiddenRows/hiddenRows.js +1 -1
  124. package/plugins/hiddenRows/hiddenRows.mjs +1 -1
  125. package/plugins/manualColumnResize/manualColumnResize.js +4 -6
  126. package/plugins/manualColumnResize/manualColumnResize.mjs +4 -6
  127. package/plugins/manualRowResize/manualRowResize.js +4 -6
  128. package/plugins/manualRowResize/manualRowResize.mjs +4 -6
  129. package/plugins/mergeCells/mergeCells.js +10 -30
  130. package/plugins/mergeCells/mergeCells.mjs +10 -30
  131. package/plugins/mergeCells/renderer.js +15 -0
  132. package/plugins/mergeCells/renderer.mjs +15 -0
  133. package/plugins/mergeCells/utils.js +31 -0
  134. package/plugins/mergeCells/utils.mjs +27 -0
  135. package/plugins/nestedRows/data/dataManager.js +2 -2
  136. package/plugins/nestedRows/data/dataManager.mjs +2 -2
  137. package/plugins/undoRedo/actions/index.js +0 -2
  138. package/plugins/undoRedo/actions/index.mjs +0 -2
  139. package/plugins/undoRedo/actions/removeColumn.js +19 -14
  140. package/plugins/undoRedo/actions/removeColumn.mjs +19 -14
  141. package/plugins/undoRedo/actions/removeRow.js +12 -4
  142. package/plugins/undoRedo/actions/removeRow.mjs +12 -4
  143. package/selection/selection.js +3 -1
  144. package/selection/selection.mjs +3 -1
  145. package/shortcutContexts/commands/extendCellsSelection/down.js +7 -2
  146. package/shortcutContexts/commands/extendCellsSelection/down.mjs +7 -2
  147. package/shortcutContexts/commands/extendCellsSelection/downByViewportHeight.js +8 -2
  148. package/shortcutContexts/commands/extendCellsSelection/downByViewportHeight.mjs +8 -2
  149. package/shortcutContexts/commands/extendCellsSelection/left.js +7 -2
  150. package/shortcutContexts/commands/extendCellsSelection/left.mjs +7 -2
  151. package/shortcutContexts/commands/extendCellsSelection/right.js +7 -2
  152. package/shortcutContexts/commands/extendCellsSelection/right.mjs +7 -2
  153. package/shortcutContexts/commands/extendCellsSelection/toColumns.js +7 -2
  154. package/shortcutContexts/commands/extendCellsSelection/toColumns.mjs +7 -2
  155. package/shortcutContexts/commands/extendCellsSelection/toMostBottom.js +3 -1
  156. package/shortcutContexts/commands/extendCellsSelection/toMostBottom.mjs +3 -1
  157. package/shortcutContexts/commands/extendCellsSelection/toMostInlineEnd.js +3 -1
  158. package/shortcutContexts/commands/extendCellsSelection/toMostInlineEnd.mjs +3 -1
  159. package/shortcutContexts/commands/extendCellsSelection/toMostInlineStart.js +3 -1
  160. package/shortcutContexts/commands/extendCellsSelection/toMostInlineStart.mjs +3 -1
  161. package/shortcutContexts/commands/extendCellsSelection/toMostLeft.js +3 -1
  162. package/shortcutContexts/commands/extendCellsSelection/toMostLeft.mjs +3 -1
  163. package/shortcutContexts/commands/extendCellsSelection/toMostRight.js +3 -1
  164. package/shortcutContexts/commands/extendCellsSelection/toMostRight.mjs +3 -1
  165. package/shortcutContexts/commands/extendCellsSelection/toMostTop.js +3 -1
  166. package/shortcutContexts/commands/extendCellsSelection/toMostTop.mjs +3 -1
  167. package/shortcutContexts/commands/extendCellsSelection/toRows.js +7 -2
  168. package/shortcutContexts/commands/extendCellsSelection/toRows.mjs +7 -2
  169. package/shortcutContexts/commands/extendCellsSelection/up.js +7 -2
  170. package/shortcutContexts/commands/extendCellsSelection/up.mjs +7 -2
  171. package/shortcutContexts/commands/extendCellsSelection/upByViewportHeight.js +8 -2
  172. package/shortcutContexts/commands/extendCellsSelection/upByViewportHeight.mjs +8 -2
  173. package/shortcutContexts/commands/index.js +0 -2
  174. package/shortcutContexts/commands/index.mjs +0 -2
  175. package/shortcutContexts/commands/moveCellSelection/down.js +2 -0
  176. package/shortcutContexts/commands/moveCellSelection/down.mjs +2 -0
  177. package/shortcutContexts/commands/moveCellSelection/downByViewportHeight.js +6 -1
  178. package/shortcutContexts/commands/moveCellSelection/downByViewportHeight.mjs +6 -1
  179. package/shortcutContexts/commands/moveCellSelection/inlineEnd.js +8 -3
  180. package/shortcutContexts/commands/moveCellSelection/inlineEnd.mjs +8 -3
  181. package/shortcutContexts/commands/moveCellSelection/inlineStart.js +8 -3
  182. package/shortcutContexts/commands/moveCellSelection/inlineStart.mjs +8 -3
  183. package/shortcutContexts/commands/moveCellSelection/left.js +6 -1
  184. package/shortcutContexts/commands/moveCellSelection/left.mjs +6 -1
  185. package/shortcutContexts/commands/moveCellSelection/right.js +6 -1
  186. package/shortcutContexts/commands/moveCellSelection/right.mjs +6 -1
  187. package/shortcutContexts/commands/moveCellSelection/toMostBottom.js +4 -1
  188. package/shortcutContexts/commands/moveCellSelection/toMostBottom.mjs +4 -1
  189. package/shortcutContexts/commands/moveCellSelection/toMostBottomInlineEnd.js +2 -0
  190. package/shortcutContexts/commands/moveCellSelection/toMostBottomInlineEnd.mjs +2 -0
  191. package/shortcutContexts/commands/moveCellSelection/toMostInlineEnd.js +2 -0
  192. package/shortcutContexts/commands/moveCellSelection/toMostInlineEnd.mjs +2 -0
  193. package/shortcutContexts/commands/moveCellSelection/toMostInlineStart.js +2 -0
  194. package/shortcutContexts/commands/moveCellSelection/toMostInlineStart.mjs +2 -0
  195. package/shortcutContexts/commands/moveCellSelection/toMostLeft.js +2 -0
  196. package/shortcutContexts/commands/moveCellSelection/toMostLeft.mjs +2 -0
  197. package/shortcutContexts/commands/moveCellSelection/toMostRight.js +2 -0
  198. package/shortcutContexts/commands/moveCellSelection/toMostRight.mjs +2 -0
  199. package/shortcutContexts/commands/moveCellSelection/toMostTop.js +6 -1
  200. package/shortcutContexts/commands/moveCellSelection/toMostTop.mjs +6 -1
  201. package/shortcutContexts/commands/moveCellSelection/toMostTopInlineStart.js +2 -0
  202. package/shortcutContexts/commands/moveCellSelection/toMostTopInlineStart.mjs +2 -0
  203. package/shortcutContexts/commands/moveCellSelection/up.js +2 -0
  204. package/shortcutContexts/commands/moveCellSelection/up.mjs +2 -0
  205. package/shortcutContexts/commands/moveCellSelection/upByViewportHeight.js +6 -1
  206. package/shortcutContexts/commands/moveCellSelection/upByViewportHeight.mjs +6 -1
  207. package/shortcutContexts/commands/selectAllCells.js +7 -2
  208. package/shortcutContexts/commands/selectAllCells.mjs +7 -2
  209. package/shortcutContexts/commands/selectAllCellsAndHeaders.js +7 -2
  210. package/shortcutContexts/commands/selectAllCellsAndHeaders.mjs +7 -2
  211. package/shortcutContexts/index.js +2 -2
  212. package/shortcutContexts/index.mjs +0 -2
  213. package/styles/handsontable.css +15 -17
  214. package/styles/handsontable.min.css +3 -3
  215. package/styles/ht-theme-horizon.css +2 -2
  216. package/styles/ht-theme-horizon.min.css +2 -2
  217. package/styles/ht-theme-main.css +2 -2
  218. package/styles/ht-theme-main.min.css +2 -2
  219. package/tableView.js +5 -8
  220. package/tableView.mjs +5 -8
  221. package/translations/indexMapper.js +0 -1
  222. package/translations/indexMapper.mjs +0 -1
  223. package/utils/ghostTable.js +3 -0
  224. package/utils/ghostTable.mjs +3 -0
@@ -113,6 +113,7 @@ const ROW_WIDTHS_MAP_NAME = 'autoRowSize';
113
113
  */
114
114
  /* eslint-enable jsdoc/require-description-complete-sentence */
115
115
  var _visualRowsToRefresh = /*#__PURE__*/new WeakMap();
116
+ var _isInitialized = /*#__PURE__*/new WeakMap();
116
117
  var _AutoRowSize_brand = /*#__PURE__*/new WeakSet();
117
118
  class AutoRowSize extends _base.BasePlugin {
118
119
  static get PLUGIN_KEY() {
@@ -215,6 +216,12 @@ class AutoRowSize extends _base.BasePlugin {
215
216
  * @type {number[]}
216
217
  */
217
218
  _classPrivateFieldInitSpec(this, _visualRowsToRefresh, []);
219
+ /**
220
+ * `true` value indicates that the #onInit() function has been already called.
221
+ *
222
+ * @type {boolean}
223
+ */
224
+ _classPrivateFieldInitSpec(this, _isInitialized, false);
218
225
  this.hot.rowIndexMapper.registerMap(ROW_WIDTHS_MAP_NAME, this.rowHeightsMap);
219
226
 
220
227
  // Leave the listener active to allow auto-sizing the rows when the plugin is disabled.
@@ -297,7 +304,7 @@ class AutoRowSize extends _base.BasePlugin {
297
304
  if (firstVisibleRow === -1 || lastVisibleRow === -1) {
298
305
  return;
299
306
  }
300
- const overwriteCache = this.hot.renderCall;
307
+ const overwriteCache = this.hot.forceFullRender;
301
308
  this.calculateRowsHeight({
302
309
  from: firstVisibleRow,
303
310
  to: lastVisibleRow
@@ -398,11 +405,6 @@ class AutoRowSize extends _base.BasePlugin {
398
405
 
399
406
  // @TODO Should call once per render cycle, currently fired separately in different plugins
400
407
  this.hot.view.adjustElementsSize();
401
-
402
- // tmp
403
- if (this.hot.view._wt.wtOverlays.inlineStartOverlay.needFullRender) {
404
- this.hot.view._wt.wtOverlays.inlineStartOverlay.clone.draw();
405
- }
406
408
  }
407
409
  };
408
410
  const syncLimit = this.getSyncCalculationLimit();
@@ -645,6 +647,7 @@ function _onBeforeChange(changes) {
645
647
  */
646
648
  function _onInit() {
647
649
  this.recalculateAllRowsHeight();
650
+ _classPrivateFieldSet(_isInitialized, this, true);
648
651
  }
649
652
  /**
650
653
  * After formulas values updated listener.
@@ -652,6 +655,9 @@ function _onInit() {
652
655
  * @param {Array} changes An array of modified data.
653
656
  */
654
657
  function _onAfterFormulasValuesUpdate(changes) {
658
+ if (!_classPrivateFieldGet(_isInitialized, this)) {
659
+ return;
660
+ }
655
661
  const changedRows = changes.reduce((acc, change) => {
656
662
  var _change$address;
657
663
  const physicalRow = (_change$address = change.address) === null || _change$address === void 0 ? void 0 : _change$address.row;
@@ -109,6 +109,7 @@ const ROW_WIDTHS_MAP_NAME = 'autoRowSize';
109
109
  */
110
110
  /* eslint-enable jsdoc/require-description-complete-sentence */
111
111
  var _visualRowsToRefresh = /*#__PURE__*/new WeakMap();
112
+ var _isInitialized = /*#__PURE__*/new WeakMap();
112
113
  var _AutoRowSize_brand = /*#__PURE__*/new WeakSet();
113
114
  export class AutoRowSize extends BasePlugin {
114
115
  static get PLUGIN_KEY() {
@@ -211,6 +212,12 @@ export class AutoRowSize extends BasePlugin {
211
212
  * @type {number[]}
212
213
  */
213
214
  _classPrivateFieldInitSpec(this, _visualRowsToRefresh, []);
215
+ /**
216
+ * `true` value indicates that the #onInit() function has been already called.
217
+ *
218
+ * @type {boolean}
219
+ */
220
+ _classPrivateFieldInitSpec(this, _isInitialized, false);
214
221
  this.hot.rowIndexMapper.registerMap(ROW_WIDTHS_MAP_NAME, this.rowHeightsMap);
215
222
 
216
223
  // Leave the listener active to allow auto-sizing the rows when the plugin is disabled.
@@ -293,7 +300,7 @@ export class AutoRowSize extends BasePlugin {
293
300
  if (firstVisibleRow === -1 || lastVisibleRow === -1) {
294
301
  return;
295
302
  }
296
- const overwriteCache = this.hot.renderCall;
303
+ const overwriteCache = this.hot.forceFullRender;
297
304
  this.calculateRowsHeight({
298
305
  from: firstVisibleRow,
299
306
  to: lastVisibleRow
@@ -394,11 +401,6 @@ export class AutoRowSize extends BasePlugin {
394
401
 
395
402
  // @TODO Should call once per render cycle, currently fired separately in different plugins
396
403
  this.hot.view.adjustElementsSize();
397
-
398
- // tmp
399
- if (this.hot.view._wt.wtOverlays.inlineStartOverlay.needFullRender) {
400
- this.hot.view._wt.wtOverlays.inlineStartOverlay.clone.draw();
401
- }
402
404
  }
403
405
  };
404
406
  const syncLimit = this.getSyncCalculationLimit();
@@ -640,6 +642,7 @@ function _onBeforeChange(changes) {
640
642
  */
641
643
  function _onInit() {
642
644
  this.recalculateAllRowsHeight();
645
+ _classPrivateFieldSet(_isInitialized, this, true);
643
646
  }
644
647
  /**
645
648
  * After formulas values updated listener.
@@ -647,6 +650,9 @@ function _onInit() {
647
650
  * @param {Array} changes An array of modified data.
648
651
  */
649
652
  function _onAfterFormulasValuesUpdate(changes) {
653
+ if (!_classPrivateFieldGet(_isInitialized, this)) {
654
+ return;
655
+ }
650
656
  const changedRows = changes.reduce((acc, change) => {
651
657
  var _change$address;
652
658
  const physicalRow = (_change$address = change.address) === null || _change$address === void 0 ? void 0 : _change$address.row;
@@ -304,10 +304,6 @@ class ColumnSorting extends _base.BasePlugin {
304
304
  this.hot.runHooks('afterColumnSort', currentSortConfig, sortPossible ? destinationSortConfigs : currentSortConfig, sortPossible);
305
305
  if (sortPossible) {
306
306
  this.hot.render();
307
- // TODO: Workaround? This triggers fast redraw. One test won't pass after removal.
308
- // It should be refactored / described.
309
- this.hot.forceFullRender = false;
310
- this.hot.view.render();
311
307
  }
312
308
  }
313
309
 
@@ -301,10 +301,6 @@ export class ColumnSorting extends BasePlugin {
301
301
  this.hot.runHooks('afterColumnSort', currentSortConfig, sortPossible ? destinationSortConfigs : currentSortConfig, sortPossible);
302
302
  if (sortPossible) {
303
303
  this.hot.render();
304
- // TODO: Workaround? This triggers fast redraw. One test won't pass after removal.
305
- // It should be refactored / described.
306
- this.hot.forceFullRender = false;
307
- this.hot.view.render();
308
304
  }
309
305
  }
310
306
 
@@ -234,6 +234,7 @@ class Comments extends _base.BasePlugin {
234
234
  this.addHook('afterScroll', () => _assertClassBrand(_Comments_brand, this, _onAfterScroll).call(this));
235
235
  this.addHook('afterBeginEditing', () => this.hide());
236
236
  this.addHook('afterDocumentKeyDown', event => _assertClassBrand(_Comments_brand, this, _onAfterDocumentKeyDown).call(this, event));
237
+ this.addHook('beforeCompositionStart', event => _assertClassBrand(_Comments_brand, this, _onAfterDocumentKeyDown).call(this, event));
237
238
  this.addHook('afterSetTheme', function () {
238
239
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
239
240
  args[_key2] = arguments[_key2];
@@ -230,6 +230,7 @@ export class Comments extends BasePlugin {
230
230
  this.addHook('afterScroll', () => _assertClassBrand(_Comments_brand, this, _onAfterScroll).call(this));
231
231
  this.addHook('afterBeginEditing', () => this.hide());
232
232
  this.addHook('afterDocumentKeyDown', event => _assertClassBrand(_Comments_brand, this, _onAfterDocumentKeyDown).call(this, event));
233
+ this.addHook('beforeCompositionStart', event => _assertClassBrand(_Comments_brand, this, _onAfterDocumentKeyDown).call(this, event));
233
234
  this.addHook('afterSetTheme', function () {
234
235
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
235
236
  args[_key2] = arguments[_key2];
@@ -39,7 +39,7 @@ function createDefaultShortcutsList(menu) {
39
39
  keys: [['ArrowUp']],
40
40
  callback: () => menu.getNavigator().toPreviousItem()
41
41
  }, {
42
- keys: [['ArrowRight']],
42
+ keys: [[hot.isRtl() ? 'ArrowLeft' : 'ArrowRight']],
43
43
  callback: () => {
44
44
  const selection = hotMenu.getSelectedLast();
45
45
  if (selection) {
@@ -50,7 +50,7 @@ function createDefaultShortcutsList(menu) {
50
50
  }
51
51
  }
52
52
  }, {
53
- keys: [['ArrowLeft']],
53
+ keys: [[hot.isRtl() ? 'ArrowRight' : 'ArrowLeft']],
54
54
  callback: () => {
55
55
  const selection = hotMenu.getSelectedLast();
56
56
  if (selection && menu.isSubMenu()) {
@@ -35,7 +35,7 @@ export function createDefaultShortcutsList(menu) {
35
35
  keys: [['ArrowUp']],
36
36
  callback: () => menu.getNavigator().toPreviousItem()
37
37
  }, {
38
- keys: [['ArrowRight']],
38
+ keys: [[hot.isRtl() ? 'ArrowLeft' : 'ArrowRight']],
39
39
  callback: () => {
40
40
  const selection = hotMenu.getSelectedLast();
41
41
  if (selection) {
@@ -46,7 +46,7 @@ export function createDefaultShortcutsList(menu) {
46
46
  }
47
47
  }
48
48
  }, {
49
- keys: [['ArrowLeft']],
49
+ keys: [[hot.isRtl() ? 'ArrowRight' : 'ArrowLeft']],
50
50
  callback: () => {
51
51
  const selection = hotMenu.getSelectedLast();
52
52
  if (selection && menu.isSubMenu()) {
@@ -293,6 +293,7 @@ class Menu {
293
293
  layoutDirection: this.hot.isRtl() ? 'rtl' : 'ltr',
294
294
  ariaTags: false,
295
295
  themeName: this.hot.getCurrentThemeName(),
296
+ beforeRefreshDimensions: () => false,
296
297
  beforeOnCellMouseOver: (event, coords) => {
297
298
  _classPrivateFieldGet(_navigator, this).setCurrentPage(coords.row);
298
299
  },
@@ -289,6 +289,7 @@ export class Menu {
289
289
  layoutDirection: this.hot.isRtl() ? 'rtl' : 'ltr',
290
290
  ariaTags: false,
291
291
  themeName: this.hot.getCurrentThemeName(),
292
+ beforeRefreshDimensions: () => false,
292
293
  beforeOnCellMouseOver: (event, coords) => {
293
294
  _classPrivateFieldGet(_navigator, this).setCurrentPage(coords.row);
294
295
  },
@@ -143,7 +143,11 @@ class Positioner {
143
143
  setPositionAboveCursor() {
144
144
  let top = _classPrivateFieldGet(_offset, this).above + _classPrivateFieldGet(_cursor, this).top - _classPrivateFieldGet(_container, this).offsetHeight;
145
145
  if (_classPrivateFieldGet(_parentContainer, this)) {
146
- top = _classPrivateFieldGet(_cursor, this).top + _classPrivateFieldGet(_cursor, this).cellHeight - _classPrivateFieldGet(_container, this).offsetHeight + 3;
146
+ const rootWindow = _classPrivateFieldGet(_parentContainer, this).ownerDocument.defaultView;
147
+ const style = rootWindow.getComputedStyle(_classPrivateFieldGet(_parentContainer, this).querySelector('.ht_master'));
148
+ const paddingTop = Number.parseInt(style.paddingTop, 10);
149
+ const borderTop = Number.parseInt(style.borderTop, 10);
150
+ top = _classPrivateFieldGet(_cursor, this).top + _classPrivateFieldGet(_cursor, this).cellHeight - _classPrivateFieldGet(_container, this).scrollHeight + paddingTop + borderTop;
147
151
  }
148
152
  _classPrivateFieldGet(_container, this).style.top = `${top}px`;
149
153
  }
@@ -154,7 +158,11 @@ class Positioner {
154
158
  setPositionBelowCursor() {
155
159
  let top = _classPrivateFieldGet(_offset, this).below + _classPrivateFieldGet(_cursor, this).top + 1;
156
160
  if (_classPrivateFieldGet(_parentContainer, this)) {
157
- top = _classPrivateFieldGet(_cursor, this).top - 1;
161
+ const rootWindow = _classPrivateFieldGet(_parentContainer, this).ownerDocument.defaultView;
162
+ const style = rootWindow.getComputedStyle(_classPrivateFieldGet(_parentContainer, this).querySelector('.ht_master'));
163
+ const paddingTop = Number.parseInt(style.paddingTop, 10);
164
+ const borderTop = Number.parseInt(style.borderTop, 10);
165
+ top = _classPrivateFieldGet(_cursor, this).top - paddingTop - borderTop - 1;
158
166
  }
159
167
  _classPrivateFieldGet(_container, this).style.top = `${top}px`;
160
168
  }
@@ -140,7 +140,11 @@ export class Positioner {
140
140
  setPositionAboveCursor() {
141
141
  let top = _classPrivateFieldGet(_offset, this).above + _classPrivateFieldGet(_cursor, this).top - _classPrivateFieldGet(_container, this).offsetHeight;
142
142
  if (_classPrivateFieldGet(_parentContainer, this)) {
143
- top = _classPrivateFieldGet(_cursor, this).top + _classPrivateFieldGet(_cursor, this).cellHeight - _classPrivateFieldGet(_container, this).offsetHeight + 3;
143
+ const rootWindow = _classPrivateFieldGet(_parentContainer, this).ownerDocument.defaultView;
144
+ const style = rootWindow.getComputedStyle(_classPrivateFieldGet(_parentContainer, this).querySelector('.ht_master'));
145
+ const paddingTop = Number.parseInt(style.paddingTop, 10);
146
+ const borderTop = Number.parseInt(style.borderTop, 10);
147
+ top = _classPrivateFieldGet(_cursor, this).top + _classPrivateFieldGet(_cursor, this).cellHeight - _classPrivateFieldGet(_container, this).scrollHeight + paddingTop + borderTop;
144
148
  }
145
149
  _classPrivateFieldGet(_container, this).style.top = `${top}px`;
146
150
  }
@@ -151,7 +155,11 @@ export class Positioner {
151
155
  setPositionBelowCursor() {
152
156
  let top = _classPrivateFieldGet(_offset, this).below + _classPrivateFieldGet(_cursor, this).top + 1;
153
157
  if (_classPrivateFieldGet(_parentContainer, this)) {
154
- top = _classPrivateFieldGet(_cursor, this).top - 1;
158
+ const rootWindow = _classPrivateFieldGet(_parentContainer, this).ownerDocument.defaultView;
159
+ const style = rootWindow.getComputedStyle(_classPrivateFieldGet(_parentContainer, this).querySelector('.ht_master'));
160
+ const paddingTop = Number.parseInt(style.paddingTop, 10);
161
+ const borderTop = Number.parseInt(style.borderTop, 10);
162
+ top = _classPrivateFieldGet(_cursor, this).top - paddingTop - borderTop - 1;
155
163
  }
156
164
  _classPrivateFieldGet(_container, this).style.top = `${top}px`;
157
165
  }
@@ -17,11 +17,11 @@ function redoItem() {
17
17
  return this.getTranslatedPhrase(C.CONTEXTMENU_ITEMS_REDO);
18
18
  },
19
19
  callback() {
20
- this.redo();
20
+ this.getPlugin('undoRedo').redo();
21
21
  },
22
22
  hidden() {
23
- const undoRedo = this.getPlugin('undoRedo');
24
- return !undoRedo || !undoRedo.isEnabled();
23
+ const undoRedoPlugin = this.getPlugin('undoRedo');
24
+ return !undoRedoPlugin || !undoRedoPlugin.isEnabled();
25
25
  },
26
26
  disabled() {
27
27
  return !this.getPlugin('undoRedo').isRedoAvailable();
@@ -11,11 +11,11 @@ export default function redoItem() {
11
11
  return this.getTranslatedPhrase(C.CONTEXTMENU_ITEMS_REDO);
12
12
  },
13
13
  callback() {
14
- this.redo();
14
+ this.getPlugin('undoRedo').redo();
15
15
  },
16
16
  hidden() {
17
- const undoRedo = this.getPlugin('undoRedo');
18
- return !undoRedo || !undoRedo.isEnabled();
17
+ const undoRedoPlugin = this.getPlugin('undoRedo');
18
+ return !undoRedoPlugin || !undoRedoPlugin.isEnabled();
19
19
  },
20
20
  disabled() {
21
21
  return !this.getPlugin('undoRedo').isRedoAvailable();
@@ -17,11 +17,11 @@ function undoItem() {
17
17
  return this.getTranslatedPhrase(C.CONTEXTMENU_ITEMS_UNDO);
18
18
  },
19
19
  callback() {
20
- this.undo();
20
+ this.getPlugin('undoRedo').undo();
21
21
  },
22
22
  hidden() {
23
- const undoRedo = this.getPlugin('undoRedo');
24
- return !undoRedo || !undoRedo.isEnabled();
23
+ const undoRedoPlugin = this.getPlugin('undoRedo');
24
+ return !undoRedoPlugin || !undoRedoPlugin.isEnabled();
25
25
  },
26
26
  disabled() {
27
27
  return !this.getPlugin('undoRedo').isUndoAvailable();
@@ -11,11 +11,11 @@ export default function undoItem() {
11
11
  return this.getTranslatedPhrase(C.CONTEXTMENU_ITEMS_UNDO);
12
12
  },
13
13
  callback() {
14
- this.undo();
14
+ this.getPlugin('undoRedo').undo();
15
15
  },
16
16
  hidden() {
17
- const undoRedo = this.getPlugin('undoRedo');
18
- return !undoRedo || !undoRedo.isEnabled();
17
+ const undoRedoPlugin = this.getPlugin('undoRedo');
18
+ return !undoRedoPlugin || !undoRedoPlugin.isEnabled();
19
19
  },
20
20
  disabled() {
21
21
  return !this.getPlugin('undoRedo').isUndoAvailable();
@@ -256,6 +256,9 @@ class CopyPaste extends _base.BasePlugin {
256
256
  return _assertClassBrand(_CopyPaste_brand, _this, _onAfterSelection).call(_this, ...args);
257
257
  });
258
258
  this.addHook('afterSelectionEnd', () => _assertClassBrand(_CopyPaste_brand, this, _onAfterSelectionEnd).call(this));
259
+
260
+ // Events are attached to the document, not the root table element - as it should,
261
+ // for Chrome 133 and lower to copy/paste/cut work properly (#dev-2277).
259
262
  this.eventManager.addEventListener(this.hot.rootDocument, 'copy', function () {
260
263
  return _this.onCopy(...arguments);
261
264
  });
@@ -562,10 +565,10 @@ class CopyPaste extends _base.BasePlugin {
562
565
  * @private
563
566
  */
564
567
  onCopy(event) {
565
- var _event$target;
568
+ const eventTarget = event.composedPath()[0];
566
569
  const focusedElement = this.hot.getFocusManager().getRefocusElement();
567
- const isHotInput = (_event$target = event.target) === null || _event$target === void 0 ? void 0 : _event$target.hasAttribute('data-hot-input');
568
- if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCopy, this) || this.isEditorOpened() || (0, _element.isHTMLElement)(event.target) && (isHotInput && event.target !== focusedElement || !isHotInput && event.target !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(event.target, this.hot.rootElement))) {
570
+ const isHotInput = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.hasAttribute('data-hot-input');
571
+ if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCopy, this) || this.isEditorOpened() || (0, _element.isHTMLElement)(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(eventTarget, this.hot.rootElement))) {
569
572
  return;
570
573
  }
571
574
  event.preventDefault();
@@ -595,10 +598,10 @@ class CopyPaste extends _base.BasePlugin {
595
598
  * @private
596
599
  */
597
600
  onCut(event) {
598
- var _event$target2;
601
+ const eventTarget = event.composedPath()[0];
599
602
  const focusedElement = this.hot.getFocusManager().getRefocusElement();
600
- const isHotInput = (_event$target2 = event.target) === null || _event$target2 === void 0 ? void 0 : _event$target2.hasAttribute('data-hot-input');
601
- if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCut, this) || this.isEditorOpened() || (0, _element.isHTMLElement)(event.target) && (isHotInput && event.target !== focusedElement || !isHotInput && event.target !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(event.target, this.hot.rootElement))) {
603
+ const isHotInput = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.hasAttribute('data-hot-input');
604
+ if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCut, this) || this.isEditorOpened() || (0, _element.isHTMLElement)(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(eventTarget, this.hot.rootElement))) {
602
605
  return;
603
606
  }
604
607
  event.preventDefault();
@@ -627,10 +630,10 @@ class CopyPaste extends _base.BasePlugin {
627
630
  * @private
628
631
  */
629
632
  onPaste(event) {
630
- var _event$target3;
633
+ const eventTarget = event.composedPath()[0];
631
634
  const focusedElement = this.hot.getFocusManager().getRefocusElement();
632
- const isHotInput = (_event$target3 = event.target) === null || _event$target3 === void 0 ? void 0 : _event$target3.hasAttribute('data-hot-input');
633
- if (!this.hot.isListening() || this.isEditorOpened() || !this.hot.getSelected() || (0, _element.isHTMLElement)(event.target) && (isHotInput && event.target !== focusedElement || !isHotInput && event.target !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(event.target, this.hot.rootElement))) {
635
+ const isHotInput = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.hasAttribute('data-hot-input');
636
+ if (!this.hot.isListening() || this.isEditorOpened() || !this.hot.getSelected() || (0, _element.isHTMLElement)(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !(0, _element.isInternalElement)(eventTarget, this.hot.rootElement))) {
634
637
  return;
635
638
  }
636
639
  event.preventDefault();
@@ -252,6 +252,9 @@ export class CopyPaste extends BasePlugin {
252
252
  return _assertClassBrand(_CopyPaste_brand, _this, _onAfterSelection).call(_this, ...args);
253
253
  });
254
254
  this.addHook('afterSelectionEnd', () => _assertClassBrand(_CopyPaste_brand, this, _onAfterSelectionEnd).call(this));
255
+
256
+ // Events are attached to the document, not the root table element - as it should,
257
+ // for Chrome 133 and lower to copy/paste/cut work properly (#dev-2277).
255
258
  this.eventManager.addEventListener(this.hot.rootDocument, 'copy', function () {
256
259
  return _this.onCopy(...arguments);
257
260
  });
@@ -558,10 +561,10 @@ export class CopyPaste extends BasePlugin {
558
561
  * @private
559
562
  */
560
563
  onCopy(event) {
561
- var _event$target;
564
+ const eventTarget = event.composedPath()[0];
562
565
  const focusedElement = this.hot.getFocusManager().getRefocusElement();
563
- const isHotInput = (_event$target = event.target) === null || _event$target === void 0 ? void 0 : _event$target.hasAttribute('data-hot-input');
564
- if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCopy, this) || this.isEditorOpened() || isHTMLElement(event.target) && (isHotInput && event.target !== focusedElement || !isHotInput && event.target !== this.hot.rootDocument.body && !isInternalElement(event.target, this.hot.rootElement))) {
566
+ const isHotInput = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.hasAttribute('data-hot-input');
567
+ if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCopy, this) || this.isEditorOpened() || isHTMLElement(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !isInternalElement(eventTarget, this.hot.rootElement))) {
565
568
  return;
566
569
  }
567
570
  event.preventDefault();
@@ -591,10 +594,10 @@ export class CopyPaste extends BasePlugin {
591
594
  * @private
592
595
  */
593
596
  onCut(event) {
594
- var _event$target2;
597
+ const eventTarget = event.composedPath()[0];
595
598
  const focusedElement = this.hot.getFocusManager().getRefocusElement();
596
- const isHotInput = (_event$target2 = event.target) === null || _event$target2 === void 0 ? void 0 : _event$target2.hasAttribute('data-hot-input');
597
- if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCut, this) || this.isEditorOpened() || isHTMLElement(event.target) && (isHotInput && event.target !== focusedElement || !isHotInput && event.target !== this.hot.rootDocument.body && !isInternalElement(event.target, this.hot.rootElement))) {
599
+ const isHotInput = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.hasAttribute('data-hot-input');
600
+ if (!this.hot.isListening() && !_classPrivateFieldGet(_isTriggeredByCut, this) || this.isEditorOpened() || isHTMLElement(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !isInternalElement(eventTarget, this.hot.rootElement))) {
598
601
  return;
599
602
  }
600
603
  event.preventDefault();
@@ -623,10 +626,10 @@ export class CopyPaste extends BasePlugin {
623
626
  * @private
624
627
  */
625
628
  onPaste(event) {
626
- var _event$target3;
629
+ const eventTarget = event.composedPath()[0];
627
630
  const focusedElement = this.hot.getFocusManager().getRefocusElement();
628
- const isHotInput = (_event$target3 = event.target) === null || _event$target3 === void 0 ? void 0 : _event$target3.hasAttribute('data-hot-input');
629
- if (!this.hot.isListening() || this.isEditorOpened() || !this.hot.getSelected() || isHTMLElement(event.target) && (isHotInput && event.target !== focusedElement || !isHotInput && event.target !== this.hot.rootDocument.body && !isInternalElement(event.target, this.hot.rootElement))) {
631
+ const isHotInput = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.hasAttribute('data-hot-input');
632
+ if (!this.hot.isListening() || this.isEditorOpened() || !this.hot.getSelected() || isHTMLElement(eventTarget) && (isHotInput && eventTarget !== focusedElement || !isHotInput && eventTarget !== this.hot.rootDocument.body && !isInternalElement(eventTarget, this.hot.rootElement))) {
630
633
  return;
631
634
  }
632
635
  event.preventDefault();
@@ -11,5 +11,8 @@ class PasteEvent {
11
11
  this.clipboardData = new _clipboardData.default();
12
12
  }
13
13
  preventDefault() {}
14
+ composedPath() {
15
+ return [];
16
+ }
14
17
  }
15
18
  exports.default = PasteEvent;
@@ -7,4 +7,7 @@ export default class PasteEvent {
7
7
  this.clipboardData = new ClipboardData();
8
8
  }
9
9
  preventDefault() {}
10
+ composedPath() {
11
+ return [];
12
+ }
10
13
  }
@@ -15,6 +15,7 @@ export interface ExportOptions {
15
15
  exportHiddenColumns?: boolean;
16
16
  exportHiddenRows?: boolean;
17
17
  range?: number[];
18
+ sanitizeValues?: boolean | RegExp | ((val: string) => string);
18
19
  }
19
20
  export type Settings = boolean;
20
21
 
@@ -116,6 +116,7 @@ class ExportFile extends _base.BasePlugin {
116
116
  * @property {boolean} [rowHeaders=false] Include row headers in the exported file.
117
117
  * @property {string} [columnDelimiter=','] Column delimiter.
118
118
  * @property {string} [range=[]] Cell range that will be exported to file.
119
+ * @property {boolean|RegExp|Function} [sanitizeValues=false] Controls the sanitization of cell value.
119
120
  */
120
121
 
121
122
  /**
@@ -145,7 +146,7 @@ class ExportFile extends _base.BasePlugin {
145
146
  /**
146
147
  * Exports table data as a downloadable file.
147
148
  *
148
- * @param {string} format Export format type eq. `'csv'`.
149
+ * @param {string} format Export format type eg. `'csv'`.
149
150
  * @param {ExportOptions} options Export options.
150
151
  */
151
152
  downloadFile(format) {
@@ -110,6 +110,7 @@ export class ExportFile extends BasePlugin {
110
110
  * @property {boolean} [rowHeaders=false] Include row headers in the exported file.
111
111
  * @property {string} [columnDelimiter=','] Column delimiter.
112
112
  * @property {string} [range=[]] Cell range that will be exported to file.
113
+ * @property {boolean|RegExp|Function} [sanitizeValues=false] Controls the sanitization of cell value.
113
114
  */
114
115
 
115
116
  /**
@@ -139,7 +140,7 @@ export class ExportFile extends BasePlugin {
139
140
  /**
140
141
  * Exports table data as a downloadable file.
141
142
  *
142
- * @param {string} format Export format type eq. `'csv'`.
143
+ * @param {string} format Export format type eg. `'csv'`.
143
144
  * @param {ExportOptions} options Export options.
144
145
  */
145
146
  downloadFile(format) {
@@ -1,20 +1,41 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
+ require("core-js/modules/es.error.cause.js");
4
5
  require("core-js/modules/esnext.iterator.constructor.js");
5
6
  require("core-js/modules/esnext.iterator.map.js");
6
7
  var _array = require("../../../helpers/array");
7
8
  var _mixed = require("../../../helpers/mixed");
8
9
  var _base = _interopRequireDefault(require("./_base"));
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
12
+ function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
13
+ function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
10
14
  const CHAR_CARRIAGE_RETURN = String.fromCharCode(13);
11
15
  const CHAR_DOUBLE_QUOTES = String.fromCharCode(34);
12
16
  const CHAR_LINE_FEED = String.fromCharCode(10);
17
+ const CHAR_EQUAL = String.fromCharCode(61);
18
+ const CHAR_PLUS = String.fromCharCode(43);
19
+ const CHAR_MINUS = String.fromCharCode(45);
20
+ const CHAR_AT = String.fromCharCode(64);
21
+ const CHAR_TAB = String.fromCharCode(9);
13
22
 
14
23
  /**
15
24
  * @private
16
25
  */
26
+ var _Csv_brand = /*#__PURE__*/new WeakSet();
17
27
  class Csv extends _base.default {
28
+ constructor() {
29
+ super(...arguments);
30
+ /**
31
+ * Sanitize value that may be interpreted as a formula in spreadsheet software.
32
+ * Following the OWASP recommendations: https://owasp.org/www-community/attacks/CSV_Injection.
33
+ *
34
+ * @param {string} value Cell value.
35
+ * @returns {string}
36
+ */
37
+ _classPrivateMethodInitSpec(this, _Csv_brand);
38
+ }
18
39
  /**
19
40
  * Default options for exporting CSV format.
20
41
  *
@@ -26,7 +47,8 @@ class Csv extends _base.default {
26
47
  fileExtension: 'csv',
27
48
  bom: true,
28
49
  columnDelimiter: ',',
29
- rowDelimiter: '\r\n'
50
+ rowDelimiter: '\r\n',
51
+ sanitizeValues: false
30
52
  };
31
53
  }
32
54
 
@@ -44,7 +66,10 @@ class Csv extends _base.default {
44
66
  const hasRowHeaders = rowHeaders.length > 0;
45
67
  let result = options.bom ? String.fromCharCode(0xFEFF) : '';
46
68
  if (hasColumnHeaders) {
47
- columnHeaders = (0, _array.arrayMap)(columnHeaders, value => this._escapeCell(value, true));
69
+ columnHeaders = (0, _array.arrayMap)(columnHeaders, value => this._escapeCell(value, {
70
+ force: true,
71
+ sanitizeValue: options.sanitizeValues
72
+ }));
48
73
  if (hasRowHeaders) {
49
74
  result += options.columnDelimiter;
50
75
  }
@@ -56,9 +81,15 @@ class Csv extends _base.default {
56
81
  result += options.rowDelimiter;
57
82
  }
58
83
  if (hasRowHeaders) {
59
- result += this._escapeCell(rowHeaders[index]) + options.columnDelimiter;
84
+ result += this._escapeCell(rowHeaders[index], {
85
+ sanitizeValue: options.sanitizeValues
86
+ });
87
+ result += options.columnDelimiter;
60
88
  }
61
- result += value.map(cellValue => this._escapeCell(cellValue)).join(options.columnDelimiter);
89
+ const escapedValue = value.map(cellValue => this._escapeCell(cellValue, {
90
+ sanitizeValue: options.sanitizeValues
91
+ })).join(options.columnDelimiter);
92
+ result += escapedValue;
62
93
  });
63
94
  return result;
64
95
  }
@@ -67,17 +98,51 @@ class Csv extends _base.default {
67
98
  * Escape cell value.
68
99
  *
69
100
  * @param {*} value Cell value.
70
- * @param {boolean} [force=false] Indicates if cell value will be escaped forcefully.
101
+ * @param {object} options Options.
102
+ * @param {boolean} [options.force=false] Indicates if cell value will be escaped forcefully.
103
+ * @param {boolean|RegExp|Function} [options.sanitizeValue=false] Controls the sanitization of cell value.
71
104
  * @returns {string}
72
105
  */
73
106
  _escapeCell(value) {
74
- let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
75
- let escapedValue = (0, _mixed.stringify)(value);
76
- if (escapedValue !== '' && (force || escapedValue.indexOf(CHAR_CARRIAGE_RETURN) >= 0 || escapedValue.indexOf(CHAR_DOUBLE_QUOTES) >= 0 || escapedValue.indexOf(CHAR_LINE_FEED) >= 0 || escapedValue.indexOf(this.options.columnDelimiter) >= 0)) {
77
- escapedValue = escapedValue.replace(new RegExp('"', 'g'), '""');
78
- escapedValue = `"${escapedValue}"`;
107
+ let {
108
+ force = false,
109
+ sanitizeValue = false
110
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
111
+ let returnValue = (0, _mixed.stringify)(value);
112
+ if (returnValue === '') {
113
+ return returnValue;
114
+ }
115
+ if (sanitizeValue) {
116
+ force = true;
117
+ }
118
+ if (sanitizeValue instanceof RegExp) {
119
+ returnValue = _assertClassBrand(_Csv_brand, this, _sanitizeValueWithRegExp).call(this, returnValue, sanitizeValue);
120
+ } else if (typeof sanitizeValue === 'function') {
121
+ returnValue = sanitizeValue(returnValue);
122
+ } else if (sanitizeValue) {
123
+ returnValue = _assertClassBrand(_Csv_brand, this, _sanitizeValueWithOWASP).call(this, returnValue);
124
+ }
125
+ if (force || returnValue.indexOf(CHAR_CARRIAGE_RETURN) >= 0 || returnValue.indexOf(CHAR_DOUBLE_QUOTES) >= 0 || returnValue.indexOf(CHAR_LINE_FEED) >= 0 || returnValue.indexOf(this.options.columnDelimiter) >= 0) {
126
+ returnValue = returnValue.replace(new RegExp('"', 'g'), '""');
127
+ returnValue = `"${returnValue}"`;
79
128
  }
80
- return escapedValue;
129
+ return returnValue;
81
130
  }
82
131
  }
132
+ function _sanitizeValueWithOWASP(value) {
133
+ if (value.startsWith(CHAR_EQUAL) || value.startsWith(CHAR_PLUS) || value.startsWith(CHAR_MINUS) || value.startsWith(CHAR_AT) || value.startsWith(CHAR_TAB) || value.startsWith(CHAR_CARRIAGE_RETURN)) {
134
+ return `'${value}`;
135
+ }
136
+ return value;
137
+ }
138
+ /**
139
+ * Sanitize value using regular expression.
140
+ *
141
+ * @param {string} value Cell value.
142
+ * @param {RegExp} regexp Regular expression to test against.
143
+ * @returns {string}
144
+ */
145
+ function _sanitizeValueWithRegExp(value, regexp) {
146
+ return regexp.test(value) ? `'${value}` : value;
147
+ }
83
148
  var _default = exports.default = Csv;