gridstack 4.2.6 → 4.4.0

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 (128) hide show
  1. package/README.md +29 -19
  2. package/dist/es5/gridstack-dd.d.ts +35 -0
  3. package/dist/es5/gridstack-dd.js +683 -0
  4. package/dist/es5/gridstack-dd.js.map +1 -0
  5. package/dist/es5/gridstack-ddi.d.ts +17 -0
  6. package/dist/es5/gridstack-ddi.js +31 -0
  7. package/dist/es5/gridstack-ddi.js.map +1 -0
  8. package/dist/es5/gridstack-engine.d.ts +93 -0
  9. package/dist/es5/gridstack-engine.js +955 -0
  10. package/dist/es5/gridstack-engine.js.map +1 -0
  11. package/dist/es5/gridstack-h5.d.ts +10 -0
  12. package/dist/es5/gridstack-h5.js +3 -0
  13. package/dist/es5/gridstack-h5.js.LICENSE.txt +7 -0
  14. package/dist/es5/gridstack-h5.js.map +1 -0
  15. package/dist/es5/gridstack-jq.d.ts +10 -0
  16. package/dist/es5/gridstack-jq.js +3 -0
  17. package/dist/es5/gridstack-jq.js.LICENSE.txt +99 -0
  18. package/dist/es5/gridstack-jq.js.map +1 -0
  19. package/dist/{gridstack-poly.js → es5/gridstack-poly.js} +152 -1
  20. package/dist/es5/gridstack-static.d.ts +9 -0
  21. package/dist/es5/gridstack-static.js +3 -0
  22. package/dist/es5/gridstack-static.js.LICENSE.txt +7 -0
  23. package/dist/es5/gridstack-static.js.map +1 -0
  24. package/dist/es5/gridstack.d.ts +346 -0
  25. package/dist/es5/gridstack.js +1482 -0
  26. package/dist/es5/gridstack.js.map +1 -0
  27. package/dist/es5/h5/dd-base-impl.d.ts +20 -0
  28. package/dist/es5/h5/dd-base-impl.js +43 -0
  29. package/dist/es5/h5/dd-base-impl.js.map +1 -0
  30. package/dist/es5/h5/dd-draggable.d.ts +30 -0
  31. package/dist/es5/h5/dd-draggable.js +319 -0
  32. package/dist/es5/h5/dd-draggable.js.map +1 -0
  33. package/dist/es5/h5/dd-droppable.d.ts +23 -0
  34. package/dist/es5/h5/dd-droppable.js +182 -0
  35. package/dist/es5/h5/dd-droppable.js.map +1 -0
  36. package/dist/es5/h5/dd-element.d.ts +27 -0
  37. package/dist/es5/h5/dd-element.js +96 -0
  38. package/dist/es5/h5/dd-element.js.map +1 -0
  39. package/dist/es5/h5/dd-manager.d.ts +8 -0
  40. package/dist/es5/h5/dd-manager.js +14 -0
  41. package/dist/es5/h5/dd-manager.js.map +1 -0
  42. package/dist/es5/h5/dd-resizable-handle.d.ts +14 -0
  43. package/dist/es5/h5/dd-resizable-handle.js +83 -0
  44. package/dist/es5/h5/dd-resizable-handle.js.map +1 -0
  45. package/dist/es5/h5/dd-resizable.d.ts +29 -0
  46. package/dist/es5/h5/dd-resizable.js +302 -0
  47. package/dist/es5/h5/dd-resizable.js.map +1 -0
  48. package/dist/es5/h5/dd-utils.d.ts +17 -0
  49. package/dist/es5/h5/dd-utils.js +99 -0
  50. package/dist/es5/h5/dd-utils.js.map +1 -0
  51. package/dist/es5/h5/gridstack-dd-native.d.ts +26 -0
  52. package/dist/es5/h5/gridstack-dd-native.js +175 -0
  53. package/dist/es5/h5/gridstack-dd-native.js.map +1 -0
  54. package/dist/es5/jq/gridstack-dd-jqueryui.d.ts +22 -0
  55. package/dist/es5/jq/gridstack-dd-jqueryui.js +162 -0
  56. package/dist/es5/jq/gridstack-dd-jqueryui.js.map +1 -0
  57. package/dist/es5/types.d.ts +258 -0
  58. package/dist/es5/types.js +7 -0
  59. package/dist/es5/types.js.map +1 -0
  60. package/dist/es5/utils.d.ts +73 -0
  61. package/dist/es5/utils.js +444 -0
  62. package/dist/es5/utils.js.map +1 -0
  63. package/dist/gridstack-dd.d.ts +1 -1
  64. package/dist/gridstack-dd.js +16 -8
  65. package/dist/gridstack-dd.js.map +1 -1
  66. package/dist/gridstack-ddi.d.ts +1 -1
  67. package/dist/gridstack-ddi.js +1 -1
  68. package/dist/gridstack-ddi.js.map +1 -1
  69. package/dist/gridstack-engine.d.ts +3 -2
  70. package/dist/gridstack-engine.js +101 -61
  71. package/dist/gridstack-engine.js.map +1 -1
  72. package/dist/gridstack-extra.css +800 -530
  73. package/dist/gridstack-extra.min.css +1 -1
  74. package/dist/gridstack-h5.d.ts +1 -1
  75. package/dist/gridstack-h5.js +1 -1
  76. package/dist/gridstack-h5.js.LICENSE.txt +1 -1
  77. package/dist/gridstack-h5.js.map +1 -1
  78. package/dist/gridstack-jq.d.ts +1 -1
  79. package/dist/gridstack-jq.js +1 -1
  80. package/dist/gridstack-jq.js.LICENSE.txt +1 -1
  81. package/dist/gridstack-jq.js.map +1 -1
  82. package/dist/gridstack-static.d.ts +1 -1
  83. package/dist/gridstack-static.js +1 -1
  84. package/dist/gridstack-static.js.LICENSE.txt +1 -1
  85. package/dist/gridstack-static.js.map +1 -1
  86. package/dist/gridstack.css +305 -228
  87. package/dist/gridstack.d.ts +1 -1
  88. package/dist/gridstack.js +19 -12
  89. package/dist/gridstack.js.map +1 -1
  90. package/dist/gridstack.min.css +1 -1
  91. package/dist/h5/dd-base-impl.d.ts +1 -1
  92. package/dist/h5/dd-base-impl.js +1 -1
  93. package/dist/h5/dd-base-impl.js.map +1 -1
  94. package/dist/h5/dd-draggable.d.ts +1 -1
  95. package/dist/h5/dd-draggable.js +1 -1
  96. package/dist/h5/dd-draggable.js.map +1 -1
  97. package/dist/h5/dd-droppable.d.ts +1 -1
  98. package/dist/h5/dd-droppable.js +1 -1
  99. package/dist/h5/dd-droppable.js.map +1 -1
  100. package/dist/h5/dd-element.d.ts +1 -1
  101. package/dist/h5/dd-element.js +1 -1
  102. package/dist/h5/dd-element.js.map +1 -1
  103. package/dist/h5/dd-manager.d.ts +1 -1
  104. package/dist/h5/dd-manager.js +1 -1
  105. package/dist/h5/dd-manager.js.map +1 -1
  106. package/dist/h5/dd-resizable-handle.d.ts +1 -1
  107. package/dist/h5/dd-resizable-handle.js +1 -1
  108. package/dist/h5/dd-resizable-handle.js.map +1 -1
  109. package/dist/h5/dd-resizable.d.ts +1 -1
  110. package/dist/h5/dd-resizable.js +2 -1
  111. package/dist/h5/dd-resizable.js.map +1 -1
  112. package/dist/h5/dd-utils.d.ts +1 -1
  113. package/dist/h5/dd-utils.js +1 -1
  114. package/dist/h5/dd-utils.js.map +1 -1
  115. package/dist/h5/gridstack-dd-native.d.ts +1 -1
  116. package/dist/h5/gridstack-dd-native.js +1 -1
  117. package/dist/h5/gridstack-dd-native.js.map +1 -1
  118. package/dist/jq/gridstack-dd-jqueryui.js.map +1 -1
  119. package/dist/src/gridstack-extra.scss +7 -5
  120. package/dist/src/gridstack.scss +9 -7
  121. package/dist/types.d.ts +5 -2
  122. package/dist/types.js +1 -1
  123. package/dist/types.js.map +1 -1
  124. package/dist/utils.d.ts +3 -1
  125. package/dist/utils.js +7 -5
  126. package/dist/utils.js.map +1 -1
  127. package/doc/CHANGES.md +26 -0
  128. package/package.json +6 -5
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * gridstack-engine.ts 4.2.6
3
+ * gridstack-engine.ts 4.4.0
4
4
  * Copyright (c) 2021 Alain Dumesny - see GridStack root license
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -173,6 +173,10 @@ class GridStackEngine {
173
173
  a.x = x;
174
174
  a.y = b.y + b.h; // a -> goes after b
175
175
  }
176
+ else if (a.w != b.w) {
177
+ a.x = b.x + b.w;
178
+ a.y = y; // a -> goes after b
179
+ }
176
180
  else {
177
181
  a.x = x;
178
182
  a.y = y; // a -> old b position
@@ -185,9 +189,9 @@ class GridStackEngine {
185
189
  if (a.w === b.w && a.h === b.h && (a.x === b.x || a.y === b.y) && (touching = utils_1.Utils.isTouching(a, b)))
186
190
  return _doSwap();
187
191
  if (touching === false)
188
- return; // ran test and fail, bail out
192
+ return; // IFF ran test and fail, bail out
189
193
  // check for taking same columns (but different height) and touching
190
- if (a.w === b.w && a.x === b.x && (touching || utils_1.Utils.isTouching(a, b))) {
194
+ if (a.w === b.w && a.x === b.x && (touching || (touching = utils_1.Utils.isTouching(a, b)))) {
191
195
  if (b.y < a.y) {
192
196
  let t = a;
193
197
  a = b;
@@ -195,11 +199,17 @@ class GridStackEngine {
195
199
  } // swap a <-> b vars so a is first
196
200
  return _doSwap();
197
201
  }
198
- /* different X will be weird (expect vertical swap) and different height overlap, so too complex. user regular layout instead
199
- // else check if swapping would not collide with anything else (requiring a re-layout)
200
- if (!this.collide(a, {x: a.x, y: a.y, w: b.w, h: b.h}, b) &&
201
- !this.collide(a, {x: b.x, y: b.y, w: a.w, h: a.h}, b))
202
- return _doSwap(); */
202
+ if (touching === false)
203
+ return;
204
+ // check if taking same row (but different width) and touching
205
+ if (a.h === b.h && a.y === b.y && (touching || (touching = utils_1.Utils.isTouching(a, b)))) {
206
+ if (b.x < a.x) {
207
+ let t = a;
208
+ a = b;
209
+ b = t;
210
+ } // swap a <-> b vars so a is first
211
+ return _doSwap();
212
+ }
203
213
  return false;
204
214
  }
205
215
  isAreaEmpty(x, y, w, h) {
@@ -241,6 +251,9 @@ class GridStackEngine {
241
251
  }
242
252
  /** @internal called to top gravity pack the items back OR revert back to original Y positions when floating */
243
253
  _packNodes() {
254
+ if (this.batchMode) {
255
+ return;
256
+ }
244
257
  this._sortNodes(); // first to last
245
258
  if (this.float) {
246
259
  // restore original Y pos
@@ -333,13 +346,14 @@ class GridStackEngine {
333
346
  }
334
347
  /** part2 of preparing a node to fit inside our grid - checks for x,y from grid dimensions */
335
348
  nodeBoundFix(node, resizing) {
349
+ let before = node._orig || utils_1.Utils.copyPos({}, node);
336
350
  if (node.maxW) {
337
351
  node.w = Math.min(node.w, node.maxW);
338
352
  }
339
353
  if (node.maxH) {
340
354
  node.h = Math.min(node.h, node.maxH);
341
355
  }
342
- if (node.minW) {
356
+ if (node.minW && node.minW <= this.column) {
343
357
  node.w = Math.max(node.w, node.minW);
344
358
  }
345
359
  if (node.minH) {
@@ -348,7 +362,8 @@ class GridStackEngine {
348
362
  if (node.w > this.column) {
349
363
  // if user loaded a larger than allowed widget for current # of columns,
350
364
  // remember it's full width so we can restore back (1 -> 12 column) #1655
351
- if (this.column < 12) {
365
+ // IFF we're not in the middle of column resizing!
366
+ if (this.column < 12 && !this._inColumnResize) {
352
367
  node.w = Math.min(12, node.w);
353
368
  this.cacheOneLayout(node, 12);
354
369
  }
@@ -385,6 +400,9 @@ class GridStackEngine {
385
400
  node.y = this.maxRow - node.h;
386
401
  }
387
402
  }
403
+ if (!utils_1.Utils.samePos(node, before)) {
404
+ node._dirty = true;
405
+ }
388
406
  return node;
389
407
  }
390
408
  getDirtyNodes(verify) {
@@ -437,10 +455,11 @@ class GridStackEngine {
437
455
  }
438
456
  /** call to add the given node to our list, fixing collision and re-packing */
439
457
  addNode(node, triggerAddEvent = false) {
440
- let dup;
441
- if (dup = this.nodes.find(n => n._id === node._id))
458
+ let dup = this.nodes.find(n => n._id === node._id);
459
+ if (dup)
442
460
  return dup; // prevent inserting twice! return it instead.
443
- node = this.prepareNode(node);
461
+ // skip prepareNode if we're in middle of column resize (not new) but do check for bounds!
462
+ node = this._inColumnResize ? this.nodeBoundFix(node) : this.prepareNode(node);
444
463
  delete node._temporaryRemoved;
445
464
  delete node._removeDOM;
446
465
  if (node.autoPosition) {
@@ -461,10 +480,13 @@ class GridStackEngine {
461
480
  }
462
481
  }
463
482
  this.nodes.push(node);
464
- triggerAddEvent && this.addedNodes.push(node);
483
+ if (triggerAddEvent) {
484
+ this.addedNodes.push(node);
485
+ }
465
486
  this._fixCollisions(node);
466
- this._packNodes()
467
- ._notify();
487
+ if (!this.batchMode) {
488
+ this._packNodes()._notify();
489
+ }
468
490
  return node;
469
491
  }
470
492
  removeNode(node, removeDOM = true, triggerEvent = false) {
@@ -522,8 +544,8 @@ class GridStackEngine {
522
544
  // if maxRow make sure we are still valid size
523
545
  if (this.maxRow && canMove) {
524
546
  canMove = (clone.getRow() <= this.maxRow);
525
- // turns out we can't grow, then see if we can swap instead (ex: full grid)
526
- if (!canMove) {
547
+ // turns out we can't grow, then see if we can swap instead (ex: full grid) if we're not resizing
548
+ if (!canMove && !o.resizing) {
527
549
  let collide = this.collide(node, o);
528
550
  if (collide && this.swap(node, collide)) {
529
551
  this._notify();
@@ -667,17 +689,29 @@ class GridStackEngine {
667
689
  }
668
690
  return this;
669
691
  }
670
- /** saves a copy of the current layout returning a list of widgets for serialization */
692
+ /** saves a copy of the largest column layout (eg 12 even when rendering oneColumnMode, so we don't loose orig layout),
693
+ * returning a list of widgets for serialization */
671
694
  save(saveElement = true) {
695
+ var _a;
696
+ // use the highest layout for any saved info so we can have full detail on reload #1849
697
+ let len = (_a = this._layouts) === null || _a === void 0 ? void 0 : _a.length;
698
+ let layout = len && this.column !== (len - 1) ? this._layouts[len - 1] : null;
672
699
  let list = [];
673
700
  this._sortNodes();
674
701
  this.nodes.forEach(n => {
675
- let w = {};
676
- for (let key in n) {
677
- if (key[0] !== '_' && n[key] !== null && n[key] !== undefined)
678
- w[key] = n[key];
702
+ let wl = layout === null || layout === void 0 ? void 0 : layout.find(l => l._id === n._id);
703
+ let w = Object.assign({}, n);
704
+ // use layout info instead if set
705
+ if (wl) {
706
+ w.x = wl.x;
707
+ w.y = wl.y;
708
+ w.w = wl.w;
709
+ }
710
+ // delete internals
711
+ for (let key in w) {
712
+ if (key[0] === '_' || w[key] === null || w[key] === undefined)
713
+ delete w[key];
679
714
  }
680
- // delete other internals
681
715
  delete w.grid;
682
716
  if (!saveElement)
683
717
  delete w.el;
@@ -696,7 +730,7 @@ class GridStackEngine {
696
730
  }
697
731
  /** @internal called whenever a node is added or moved - updates the cached layouts */
698
732
  layoutsNodesChange(nodes) {
699
- if (!this._layouts || this._ignoreLayoutsNodeChange)
733
+ if (!this._layouts || this._inColumnResize)
700
734
  return this;
701
735
  // remove smaller layouts - we will re-generate those on the fly... larger ones need to update
702
736
  this._layouts.forEach((layout, column) => {
@@ -707,14 +741,14 @@ class GridStackEngine {
707
741
  }
708
742
  else {
709
743
  // we save the original x,y,w (h isn't cached) to see what actually changed to propagate better.
710
- // Note: we don't need to check against out of bound scaling/moving as that will be done when using those cache values.
744
+ // NOTE: we don't need to check against out of bound scaling/moving as that will be done when using those cache values. #1785
745
+ let ratio = column / this.column;
711
746
  nodes.forEach(node => {
712
747
  if (!node._orig)
713
748
  return; // didn't change (newly added ?)
714
749
  let n = layout.find(l => l._id === node._id);
715
750
  if (!n)
716
751
  return; // no cache for new nodes. Will use those values.
717
- let ratio = column / this.column;
718
752
  // Y changed, push down same amount
719
753
  // TODO: detect doing item 'swaps' will help instead of move (especially in 1 column mode)
720
754
  if (node.y !== node._orig.y) {
@@ -739,19 +773,24 @@ class GridStackEngine {
739
773
  * Note we store previous layouts (especially original ones) to make it possible to go
740
774
  * from say 12 -> 1 -> 12 and get back to where we were.
741
775
  *
742
- * @param oldColumn previous number of columns
776
+ * @param prevColumn previous number of columns
743
777
  * @param column new column number
744
778
  * @param nodes different sorted list (ex: DOM order) instead of current list
745
779
  * @param layout specify the type of re-layout that will happen (position, size, etc...).
746
780
  * Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column
747
781
  */
748
- updateNodeWidths(oldColumn, column, nodes, layout = 'moveScale') {
749
- if (!this.nodes.length || oldColumn === column)
782
+ updateNodeWidths(prevColumn, column, nodes, layout = 'moveScale') {
783
+ var _a;
784
+ if (!this.nodes.length || !column || prevColumn === column)
750
785
  return this;
751
786
  // cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data
752
- this.cacheLayout(this.nodes, oldColumn);
787
+ this.cacheLayout(this.nodes, prevColumn);
788
+ this.batchUpdate(); // do this EARLY as it will call saveInitial() so we can detect where we started for _dirty and collision
789
+ let newNodes = [];
753
790
  // if we're going to 1 column and using DOM order rather than default sorting, then generate that layout
754
- if (column === 1 && nodes && nodes.length) {
791
+ let domOrder = false;
792
+ if (column === 1 && (nodes === null || nodes === void 0 ? void 0 : nodes.length)) {
793
+ domOrder = true;
755
794
  let top = 0;
756
795
  nodes.forEach(n => {
757
796
  n.x = 0;
@@ -759,33 +798,34 @@ class GridStackEngine {
759
798
  n.y = Math.max(n.y, top);
760
799
  top = n.y + n.h;
761
800
  });
801
+ newNodes = nodes;
802
+ nodes = [];
762
803
  }
763
804
  else {
764
- nodes = utils_1.Utils.sort(this.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision)
765
- }
766
- // see if we have cached previous layout.
767
- let cacheNodes = this._layouts[column] || [];
768
- // if not AND we are going up in size start with the largest layout as down-scaling is more accurate
769
- let lastIndex = this._layouts.length - 1;
770
- if (cacheNodes.length === 0 && column > oldColumn && column < lastIndex) {
771
- cacheNodes = this._layouts[lastIndex] || [];
772
- if (cacheNodes.length) {
773
- // pretend we came from that larger column by assigning those values as starting point
774
- oldColumn = lastIndex;
775
- cacheNodes.forEach(cacheNode => {
776
- let j = nodes.findIndex(n => n._id === cacheNode._id);
777
- if (j !== -1) {
805
+ nodes = utils_1.Utils.sort(this.nodes, -1, prevColumn); // current column reverse sorting so we can insert last to front (limit collision)
806
+ }
807
+ // see if we have cached previous layout IFF we are going up in size (restore) otherwise always
808
+ // generate next size down from where we are (looks more natural as you gradually size down).
809
+ let cacheNodes = [];
810
+ if (column > prevColumn) {
811
+ cacheNodes = this._layouts[column] || [];
812
+ // ...if not, start with the largest layout (if not already there) as down-scaling is more accurate
813
+ // by pretending we came from that larger column by assigning those values as starting point
814
+ let lastIndex = this._layouts.length - 1;
815
+ if (!cacheNodes.length && prevColumn !== lastIndex && ((_a = this._layouts[lastIndex]) === null || _a === void 0 ? void 0 : _a.length)) {
816
+ prevColumn = lastIndex;
817
+ this._layouts[lastIndex].forEach(cacheNode => {
818
+ let n = nodes.find(n => n._id === cacheNode._id);
819
+ if (n) {
778
820
  // still current, use cache info positions
779
- nodes[j].x = cacheNode.x;
780
- nodes[j].y = cacheNode.y;
781
- nodes[j].w = cacheNode.w;
821
+ n.x = cacheNode.x;
822
+ n.y = cacheNode.y;
823
+ n.w = cacheNode.w;
782
824
  }
783
825
  });
784
- cacheNodes = []; // we still don't have new column cached data... will generate from larger one.
785
826
  }
786
827
  }
787
828
  // if we found cache re-use those nodes that are still current
788
- let newNodes = [];
789
829
  cacheNodes.forEach(cacheNode => {
790
830
  let j = nodes.findIndex(n => n._id === cacheNode._id);
791
831
  if (j !== -1) {
@@ -800,15 +840,16 @@ class GridStackEngine {
800
840
  // ...and add any extra non-cached ones
801
841
  if (nodes.length) {
802
842
  if (typeof layout === 'function') {
803
- layout(column, oldColumn, newNodes, nodes);
843
+ layout(column, prevColumn, newNodes, nodes);
804
844
  }
805
- else {
806
- let ratio = column / oldColumn;
845
+ else if (!domOrder) {
846
+ let ratio = column / prevColumn;
807
847
  let move = (layout === 'move' || layout === 'moveScale');
808
848
  let scale = (layout === 'scale' || layout === 'moveScale');
809
849
  nodes.forEach(node => {
850
+ // NOTE: x + w could be outside of the grid, but addNode() below will handle that
810
851
  node.x = (column === 1 ? 0 : (move ? Math.round(node.x * ratio) : Math.min(node.x, column - 1)));
811
- node.w = ((column === 1 || oldColumn === 1) ? 1 :
852
+ node.w = ((column === 1 || prevColumn === 1) ? 1 :
812
853
  scale ? (Math.round(node.w * ratio) || 1) : (Math.min(node.w, column)));
813
854
  newNodes.push(node);
814
855
  });
@@ -817,15 +858,14 @@ class GridStackEngine {
817
858
  }
818
859
  // finally re-layout them in reverse order (to get correct placement)
819
860
  newNodes = utils_1.Utils.sort(newNodes, -1, column);
820
- this._ignoreLayoutsNodeChange = true;
821
- this.batchUpdate();
822
- this.nodes = []; // pretend we have no nodes to start with (we use same structures) to simplify layout
861
+ this._inColumnResize = true; // prevent cache update
862
+ this.nodes = []; // pretend we have no nodes to start with (add() will use same structures) to simplify layout
823
863
  newNodes.forEach(node => {
824
864
  this.addNode(node, false); // 'false' for add event trigger
825
- node._dirty = true; // force attr update
826
- }, this);
865
+ delete node._orig; // make sure the commit doesn't try to restore things back to original
866
+ });
827
867
  this.commit();
828
- delete this._ignoreLayoutsNodeChange;
868
+ delete this._inColumnResize;
829
869
  return this;
830
870
  }
831
871
  /**