handsontable 12.3.1-next-83e8a23-20230111 → 12.3.1-next-8792e1c-20230202

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,14 @@
1
1
  function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
2
2
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
3
3
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
5
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
6
4
  function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
7
5
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
6
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
7
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
8
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
9
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
10
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
11
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
8
12
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9
13
  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
10
14
  function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
@@ -24,6 +28,12 @@ import "core-js/modules/es.object.to-string.js";
24
28
  import "core-js/modules/es.string.iterator.js";
25
29
  import "core-js/modules/es.weak-map.js";
26
30
  import "core-js/modules/web.dom-collections.iterator.js";
31
+ import "core-js/modules/web.dom-collections.for-each.js";
32
+ import "core-js/modules/es.set.js";
33
+ import "core-js/modules/es.array.map.js";
34
+ import "core-js/modules/es.function.name.js";
35
+ import "core-js/modules/es.regexp.exec.js";
36
+ import "core-js/modules/es.string.replace.js";
27
37
  import "core-js/modules/es.array.includes.js";
28
38
  import "core-js/modules/es.string.includes.js";
29
39
  import "core-js/modules/es.object.set-prototype-of.js";
@@ -37,10 +47,8 @@ import "core-js/modules/es.symbol.js";
37
47
  import "core-js/modules/es.symbol.description.js";
38
48
  import "core-js/modules/es.number.constructor.js";
39
49
  import "core-js/modules/es.symbol.iterator.js";
40
- import "core-js/modules/es.array.slice.js";
41
- import "core-js/modules/es.function.name.js";
42
50
  import "core-js/modules/es.array.from.js";
43
- import "core-js/modules/es.regexp.exec.js";
51
+ import "core-js/modules/es.array.slice.js";
44
52
  import { BasePlugin } from "../base/index.mjs";
45
53
  import Hooks from "../../pluginHooks.mjs";
46
54
  import MergedCellsCollection from "./cellsCollection.mjs";
@@ -53,6 +61,8 @@ import { isObject, clone } from "../../helpers/object.mjs";
53
61
  import { warn } from "../../helpers/console.mjs";
54
62
  import { rangeEach } from "../../helpers/number.mjs";
55
63
  import { applySpanProperties } from "./utils.mjs";
64
+ import { getStyle } from "../../helpers/dom/element.mjs";
65
+ import { isChrome } from "../../helpers/browser.mjs";
56
66
  Hooks.getSingleton().register('beforeMergeCells');
57
67
  Hooks.getSingleton().register('afterMergeCells');
58
68
  Hooks.getSingleton().register('beforeUnmergeCells');
@@ -252,7 +262,8 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
252
262
  /**
253
263
  * Updates the plugin's state.
254
264
  *
255
- * This method is executed when [`updateSettings()`](@/api/core.md#updatesettings) is invoked with any of the following configuration options:
265
+ * This method is executed when [`updateSettings()`](@/api/core.md#updatesettings) is invoked with any of the
266
+ * following configuration options:
256
267
  * - [`mergeCells`](@/api/options.md#mergecells)
257
268
  */
258
269
  }, {
@@ -265,6 +276,56 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
265
276
  _get(_getPrototypeOf(MergeCells.prototype), "updatePlugin", this).call(this);
266
277
  }
267
278
 
279
+ /**
280
+ * If the browser is recognized as Chrome, force an additional repaint to prevent showing the effects of a Chrome bug.
281
+ *
282
+ * Issue described in https://github.com/handsontable/dev-handsontable/issues/521.
283
+ *
284
+ * @private
285
+ */
286
+ }, {
287
+ key: "ifChromeForceRepaint",
288
+ value: function ifChromeForceRepaint() {
289
+ var _this3 = this;
290
+ if (!isChrome()) {
291
+ return;
292
+ }
293
+ var rowsToRefresh = [];
294
+ var rowIndexesToRefresh = [];
295
+ this.mergedCellsCollection.mergedCells.forEach(function (mergedCell) {
296
+ var row = mergedCell.row,
297
+ rowspan = mergedCell.rowspan;
298
+ for (var r = row + 1; r < row + rowspan; r++) {
299
+ rowIndexesToRefresh.push(r);
300
+ }
301
+ });
302
+
303
+ // Remove duplicates
304
+ rowIndexesToRefresh = _toConsumableArray(new Set(rowIndexesToRefresh));
305
+ rowIndexesToRefresh.forEach(function (rowIndex) {
306
+ var renderableRowIndex = _this3.hot.rowIndexMapper.getRenderableFromVisualIndex(rowIndex);
307
+ _this3.hot.view._wt.wtOverlays.getOverlays(true).map(function (overlay) {
308
+ return (overlay === null || overlay === void 0 ? void 0 : overlay.name) === 'master' ? overlay : overlay.clone.wtTable;
309
+ }).forEach(function (wtTableRef) {
310
+ var rowToRefresh = wtTableRef.getRow(renderableRowIndex);
311
+ if (rowToRefresh) {
312
+ // Modify the TR's `background` property to later modify it asynchronously.
313
+ // The background color is getting modified only with the alpha, so the change should not be visible (and is
314
+ // covered by the TDs' background color).
315
+ rowToRefresh.style.background = getStyle(rowToRefresh, 'backgroundColor').replace(')', ', 0.99)');
316
+ rowsToRefresh.push(rowToRefresh);
317
+ }
318
+ });
319
+ });
320
+
321
+ // Asynchronously revert the TRs' `background` property to force a fresh repaint.
322
+ this.hot._registerTimeout(function () {
323
+ rowsToRefresh.forEach(function (rowElement) {
324
+ rowElement.style.background = getStyle(rowElement, 'backgroundColor').replace(', 0.99)', ')');
325
+ });
326
+ }, 1);
327
+ }
328
+
268
329
  /**
269
330
  * Validates a single setting object, represented by a single merged cell information object.
270
331
  *
@@ -304,19 +365,19 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
304
365
  }, {
305
366
  key: "generateFromSettings",
306
367
  value: function generateFromSettings(settings) {
307
- var _this3 = this;
368
+ var _this4 = this;
308
369
  if (Array.isArray(settings)) {
309
370
  var populatedNulls = [];
310
371
  arrayEach(settings, function (setting) {
311
- if (!_this3.validateSetting(setting)) {
372
+ if (!_this4.validateSetting(setting)) {
312
373
  return;
313
374
  }
314
- var highlight = _this3.hot._createCellCoords(setting.row, setting.col);
315
- var rangeEnd = _this3.hot._createCellCoords(setting.row + setting.rowspan - 1, setting.col + setting.colspan - 1);
316
- var mergeRange = _this3.hot._createCellRange(highlight, highlight, rangeEnd);
375
+ var highlight = _this4.hot._createCellCoords(setting.row, setting.col);
376
+ var rangeEnd = _this4.hot._createCellCoords(setting.row + setting.rowspan - 1, setting.col + setting.colspan - 1);
377
+ var mergeRange = _this4.hot._createCellRange(highlight, highlight, rangeEnd);
317
378
 
318
379
  // Merging without data population.
319
- _this3.mergeRange(mergeRange, true, true);
380
+ _this4.mergeRange(mergeRange, true, true);
320
381
  rangeEach(setting.row, setting.row + setting.rowspan - 1, function (rowIndex) {
321
382
  rangeEach(setting.col, setting.col + setting.colspan - 1, function (columnIndex) {
322
383
  // Not resetting a cell representing a merge area's value.
@@ -422,15 +483,17 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
422
483
  * @private
423
484
  * @param {CellRange} cellRange Cell range to merge.
424
485
  * @param {boolean} [auto=false] `true` if is called automatically, e.g. At initialization.
425
- * @param {boolean} [preventPopulation=false] `true`, if the method should not run `populateFromArray` at the end, but rather return its arguments.
426
- * @returns {Array|boolean} Returns an array of [row, column, dataUnderCollection] if preventPopulation is set to true. If the the merging process went successful, it returns `true`, otherwise - `false`.
486
+ * @param {boolean} [preventPopulation=false] `true`, if the method should not run `populateFromArray` at the end,
487
+ * but rather return its arguments.
488
+ * @returns {Array|boolean} Returns an array of [row, column, dataUnderCollection] if preventPopulation is set to
489
+ * true. If the the merging process went successful, it returns `true`, otherwise - `false`.
427
490
  * @fires Hooks#beforeMergeCells
428
491
  * @fires Hooks#afterMergeCells
429
492
  */
430
493
  }, {
431
494
  key: "mergeRange",
432
495
  value: function mergeRange(cellRange) {
433
- var _this4 = this;
496
+ var _this5 = this;
434
497
  var auto = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
435
498
  var preventPopulation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
436
499
  var topStart = cellRange.getTopStartCorner();
@@ -454,9 +517,9 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
454
517
  clearedData[i] = [];
455
518
  }
456
519
  if (i === 0 && j === 0) {
457
- clearedValue = _this4.hot.getSourceDataAtCell(_this4.hot.toPhysicalRow(mergeParent.row), _this4.hot.toPhysicalColumn(mergeParent.col));
520
+ clearedValue = _this5.hot.getSourceDataAtCell(_this5.hot.toPhysicalRow(mergeParent.row), _this5.hot.toPhysicalColumn(mergeParent.col));
458
521
  } else {
459
- _this4.hot.setCellMeta(mergeParent.row + i, mergeParent.col + j, 'hidden', true);
522
+ _this5.hot.setCellMeta(mergeParent.row + i, mergeParent.col + j, 'hidden', true);
460
523
  }
461
524
  clearedData[i][j] = clearedValue;
462
525
  });
@@ -469,6 +532,9 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
469
532
  } else {
470
533
  this.hot.populateFromArray(mergeParent.row, mergeParent.col, clearedData, void 0, void 0, this.pluginName);
471
534
  }
535
+ if (!auto) {
536
+ this.ifChromeForceRepaint();
537
+ }
472
538
  this.hot.runHooks('afterMergeCells', cellRange, mergeParent, auto);
473
539
  return populationInfo;
474
540
  }
@@ -488,7 +554,7 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
488
554
  }, {
489
555
  key: "unmergeRange",
490
556
  value: function unmergeRange(cellRange) {
491
- var _this5 = this;
557
+ var _this6 = this;
492
558
  var auto = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
493
559
  var mergedCells = this.mergedCellsCollection.getWithinRange(cellRange);
494
560
  if (!mergedCells) {
@@ -496,13 +562,13 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
496
562
  }
497
563
  this.hot.runHooks('beforeUnmergeCells', cellRange, auto);
498
564
  arrayEach(mergedCells, function (currentCollection) {
499
- _this5.mergedCellsCollection.remove(currentCollection.row, currentCollection.col);
565
+ _this6.mergedCellsCollection.remove(currentCollection.row, currentCollection.col);
500
566
  rangeEach(0, currentCollection.rowspan - 1, function (i) {
501
567
  rangeEach(0, currentCollection.colspan - 1, function (j) {
502
- _this5.hot.removeCellMeta(currentCollection.row + i, currentCollection.col + j, 'hidden');
568
+ _this6.hot.removeCellMeta(currentCollection.row + i, currentCollection.col + j, 'hidden');
503
569
  });
504
570
  });
505
- _this5.hot.removeCellMeta(currentCollection.row, currentCollection.col, 'spanned');
571
+ _this6.hot.removeCellMeta(currentCollection.row, currentCollection.col, 'spanned');
506
572
  });
507
573
  this.hot.runHooks('afterUnmergeCells', cellRange, auto);
508
574
  this.hot.render();
@@ -582,14 +648,14 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
582
648
  }, {
583
649
  key: "registerShortcuts",
584
650
  value: function registerShortcuts() {
585
- var _this6 = this;
651
+ var _this7 = this;
586
652
  var shortcutManager = this.hot.getShortcutManager();
587
653
  var gridContext = shortcutManager.getContext('grid');
588
654
  gridContext.addShortcut({
589
655
  keys: [['Control', 'm']],
590
656
  callback: function callback() {
591
- _this6.toggleMerge(_this6.hot.getSelectedRangeLast());
592
- _this6.hot.render();
657
+ _this7.toggleMerge(_this7.hot.getSelectedRangeLast());
658
+ _this7.hot.render();
593
659
  },
594
660
  runOnlyIf: function runOnlyIf(event) {
595
661
  return !event.altKey;
@@ -613,7 +679,8 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
613
679
  }
614
680
 
615
681
  /**
616
- * Modifies the information on whether the current selection contains multiple cells. The `afterIsMultipleSelection` hook callback.
682
+ * Modifies the information on whether the current selection contains multiple cells. The `afterIsMultipleSelection`
683
+ * hook callback.
617
684
  *
618
685
  * @private
619
686
  * @param {boolean} isMultiple Determines whether the current selection contains multiple cells.
@@ -709,7 +776,7 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
709
776
  }, {
710
777
  key: "onModifyTransformEnd",
711
778
  value: function onModifyTransformEnd(delta) {
712
- var _this7 = this;
779
+ var _this8 = this;
713
780
  var currentSelectionRange = this.hot.getSelectedRangeLast();
714
781
  var newDelta = clone(delta);
715
782
  var newSelectionRange = this.selectionCalculations.getUpdatedSelectionRange(currentSelectionRange, delta);
@@ -719,7 +786,7 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
719
786
  tempDelta = clone(newDelta);
720
787
  this.selectionCalculations.getUpdatedSelectionRange(currentSelectionRange, newDelta);
721
788
  arrayEach(mergedCellsWithinRange, function (mergedCell) {
722
- _this7.selectionCalculations.snapDelta(newDelta, currentSelectionRange, mergedCell);
789
+ _this8.selectionCalculations.snapDelta(newDelta, currentSelectionRange, mergedCell);
723
790
  });
724
791
  } while (newDelta.row !== tempDelta.row || newDelta.col !== tempDelta.col);
725
792
  delta.row = newDelta.row;
@@ -837,7 +904,8 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
837
904
 
838
905
  /**
839
906
  * `beforeSetRangeEnd` hook callback.
840
- * While selecting cells with keyboard or mouse, make sure that rectangular area is expanded to the extent of the merged cell.
907
+ * While selecting cells with keyboard or mouse, make sure that rectangular area is expanded to the extent of the
908
+ * merged cell.
841
909
  *
842
910
  * Note: Please keep in mind that callback may modify both start and end range coordinates by the reference.
843
911
  *
@@ -1209,13 +1277,15 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
1209
1277
  }
1210
1278
 
1211
1279
  /**
1212
- * `afterModifyTransformStart` hook callback. Fixes a problem with navigating through merged cells at the edges of the table
1213
- * with the ENTER/SHIFT+ENTER/TAB/SHIFT+TAB keys.
1280
+ * `afterModifyTransformStart` hook callback. Fixes a problem with navigating through merged cells at the edges of
1281
+ * the table with the ENTER/SHIFT+ENTER/TAB/SHIFT+TAB keys.
1214
1282
  *
1215
1283
  * @private
1216
1284
  * @param {CellCoords} coords Coordinates of the to-be-selected cell.
1217
- * @param {number} rowTransformDir Row transformation direction (negative value = up, 0 = none, positive value = down).
1218
- * @param {number} colTransformDir Column transformation direction (negative value = up, 0 = none, positive value = down).
1285
+ * @param {number} rowTransformDir Row transformation direction (negative value = up, 0 = none, positive value =
1286
+ * down).
1287
+ * @param {number} colTransformDir Column transformation direction (negative value = up, 0 = none, positive value =
1288
+ * down).
1219
1289
  */
1220
1290
  }, {
1221
1291
  key: "onAfterModifyTransformStart",
@@ -1247,9 +1317,11 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
1247
1317
  * @private
1248
1318
  * @param {number} currentRow Visual row index of the currently processed cell.
1249
1319
  * @param {number} currentColumn Visual column index of the currently cell.
1250
- * @param {Array} cornersOfSelection Array of the current selection in a form of `[startRow, startColumn, endRow, endColumn]`.
1320
+ * @param {Array} cornersOfSelection Array of the current selection in a form of `[startRow, startColumn, endRow,
1321
+ * endColumn]`.
1251
1322
  * @param {number|undefined} layerLevel Number indicating which layer of selection is currently processed.
1252
- * @returns {string|undefined} A `String`, which will act as an additional `className` to be added to the currently processed cell.
1323
+ * @returns {string|undefined} A `String`, which will act as an additional `className` to be added to the currently
1324
+ * processed cell.
1253
1325
  */
1254
1326
  }, {
1255
1327
  key: "onAfterDrawSelection",
@@ -1265,7 +1337,8 @@ export var MergeCells = /*#__PURE__*/function (_BasePlugin) {
1265
1337
  * `beforeRemoveCellClassNames` hook callback. Used to remove additional class name from all cells in the table.
1266
1338
  *
1267
1339
  * @private
1268
- * @returns {string[]} An `Array` of `String`s. Each of these strings will act like class names to be removed from all the cells in the table.
1340
+ * @returns {string[]} An `Array` of `String`s. Each of these strings will act like class names to be removed from
1341
+ * all the cells in the table.
1269
1342
  */
1270
1343
  }, {
1271
1344
  key: "onBeforeRemoveCellClassNames",
@@ -151,6 +151,15 @@ var createShortcutManager = function createShortcutManager(_ref) {
151
151
  isCtrlPressed: function isCtrlPressed() {
152
152
  return !isCtrlKeySilenced && (keyRecorder.isPressed('control') || keyRecorder.isPressed('meta'));
153
153
  },
154
+ /**
155
+ * Release every previously pressed key.
156
+ *
157
+ * @type {Function}
158
+ * @memberof ShortcutManager#
159
+ */
160
+ releasePressedKeys: function releasePressedKeys() {
161
+ return keyRecorder.releasePressedKeys();
162
+ },
154
163
  /**
155
164
  * Destroy a context manager instance.
156
165
  *
@@ -146,6 +146,15 @@ export var createShortcutManager = function createShortcutManager(_ref) {
146
146
  isCtrlPressed: function isCtrlPressed() {
147
147
  return !isCtrlKeySilenced && (keyRecorder.isPressed('control') || keyRecorder.isPressed('meta'));
148
148
  },
149
+ /**
150
+ * Release every previously pressed key.
151
+ *
152
+ * @type {Function}
153
+ * @memberof ShortcutManager#
154
+ */
155
+ releasePressedKeys: function releasePressedKeys() {
156
+ return keyRecorder.releasePressedKeys();
157
+ },
149
158
  /**
150
159
  * Destroy a context manager instance.
151
160
  *
@@ -160,6 +160,9 @@ function useRecorder(ownerWindow, handleEvent, beforeKeyDown, afterKeyDown, call
160
160
  unmount: unmount,
161
161
  isPressed: function isPressed(key) {
162
162
  return modifierKeysObserver.isPressed(key);
163
+ },
164
+ releasePressedKeys: function releasePressedKeys() {
165
+ return modifierKeysObserver.releaseAll();
163
166
  }
164
167
  };
165
168
  }
@@ -156,6 +156,9 @@ export function useRecorder(ownerWindow, handleEvent, beforeKeyDown, afterKeyDow
156
156
  unmount: unmount,
157
157
  isPressed: function isPressed(key) {
158
158
  return modifierKeysObserver.isPressed(key);
159
+ },
160
+ releasePressedKeys: function releasePressedKeys() {
161
+ return modifierKeysObserver.releaseAll();
159
162
  }
160
163
  };
161
164
  }