@cocreate/resize 1.18.0 → 2.0.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.
package/src/index.js CHANGED
@@ -3,9 +3,9 @@ import observer from "@cocreate/observer";
3
3
  import "./index.css";
4
4
 
5
5
  const EVENTS = [
6
- "mousemove touchmove",
7
- "mousedown touchstart",
8
- "mouseup touchend"
6
+ "mousemove touchmove",
7
+ "mousedown touchstart",
8
+ "mouseup touchend"
9
9
  ];
10
10
  const DIRECTIONS = ["left", "right", "top", "bottom"];
11
11
 
@@ -15,702 +15,792 @@ document.head.appendChild(styleEl);
15
15
  const styleSheet = styleEl.sheet;
16
16
 
17
17
  const coCreateResize = {
18
- selector: "", //'.resize',
19
- resizers: [],
20
- resizeWidgets: [],
21
-
22
- init: function (handleObj) {
23
- for (var handleKey in handleObj)
24
- if (handleObj.hasOwnProperty(handleKey) && handleKey == "selector")
25
- this.selector = handleObj[handleKey];
26
-
27
- this.resizers = document.querySelectorAll(this.selector);
28
- var _this = this;
29
- this.resizers.forEach(function (resize, idx) {
30
- let resizeWidget = new CoCreateResize(resize, handleObj);
31
- _this.resizeWidgets[idx] = resizeWidget;
32
- });
33
- },
34
-
35
- initElement: function (target) {
36
- let resizeWidget = new CoCreateResize(target, {
37
- dragLeft: "[resize='left']",
38
- dragRight: "[resize='right']",
39
- dragTop: "[resize='top']",
40
- dragBottom: "[resize='bottom']"
41
- });
42
- this.resizeWidgets[0] = resizeWidget;
43
- }
18
+ /**
19
+ * CSS selector used by init() to find resizable elements.
20
+ * @public
21
+ * @environment client
22
+ * @type {string}
23
+ */
24
+ selector: "", //'.resize',
25
+ /**
26
+ * Elements matched by the current selector.
27
+ * @public
28
+ * @environment client
29
+ * @type {NodeList<Element>}
30
+ */
31
+ resizers: [],
32
+ /**
33
+ * Resize controller instances created for matching elements.
34
+ * @public
35
+ * @environment client
36
+ * @type {CoCreateResize[]}
37
+ */
38
+ resizeWidgets: [],
39
+
40
+ /**
41
+ * Initializes resize controllers for elements matching handleObj.selector.
42
+ *
43
+ * @public
44
+ * @environment client
45
+ * @param {Object} handleObj - Selector and resize-handle selectors.
46
+ */
47
+ init: function (handleObj) {
48
+ for (var handleKey in handleObj)
49
+ if (handleObj.hasOwnProperty(handleKey) && handleKey == "selector")
50
+ this.selector = handleObj[handleKey];
51
+
52
+ this.resizers = document.querySelectorAll(this.selector);
53
+ var _this = this;
54
+ this.resizers.forEach(function (resize, idx) {
55
+ let resizeWidget = new CoCreateResize(resize, handleObj);
56
+ _this.resizeWidgets[idx] = resizeWidget;
57
+ });
58
+ },
59
+
60
+ /**
61
+ * Initializes a resize controller for one element using the default handle selectors.
62
+ *
63
+ * @public
64
+ * @environment client
65
+ * @param {Element} target - Element to make resizable.
66
+ */
67
+ initElement: function (target) {
68
+ let resizeWidget = new CoCreateResize(target, {
69
+ dragLeft: "[resize='left']",
70
+ dragRight: "[resize='right']",
71
+ dragTop: "[resize='top']",
72
+ dragBottom: "[resize='bottom']"
73
+ });
74
+ this.resizeWidgets[0] = resizeWidget;
75
+ }
44
76
  };
45
77
 
46
78
  function CoCreateResize(resizer, options) {
47
- this.resizeWidget = resizer;
48
- this.cornerSize = 10;
49
- this.init(options);
79
+ this.resizeWidget = resizer;
80
+ this.cornerSize = 10;
81
+ this.init(options);
50
82
  }
51
83
 
52
84
  CoCreateResize.prototype = {
53
- init: function (handleObj) {
54
- if (this.resizeWidget) {
55
- this.initDrags = [];
56
- this.checkCorners = [];
57
- this.doDrags = [];
58
- this.Drags = [];
59
-
60
- //this is for grid
61
- this.isGrid = this.detectGrid();
62
- this.gridWidth = 0;
63
- this.gridHeight = 0;
64
- this.limitSpan = 0;
65
- this.widthSpan = 0;
66
- this.heightSpan = 0;
67
- this.originClassAttribute = this.resizeWidget.getAttribute("class");
68
-
69
- DIRECTIONS.map((d) => {
70
- this.Drags[d] = this.resizeWidget.querySelector(
71
- handleObj["drag" + d.charAt(0).toUpperCase() + d.slice(1)]
72
- );
73
- });
74
-
75
- this.bindListeners();
76
- this.initResize();
77
-
78
- //this is just for grid system
79
- this.getGridProperty();
80
- window.addEventListener("resize", this.checkGridColumns.bind(this));
81
- }
82
- },
83
-
84
- getGridProperty: function () {
85
- if (!this.resizeWidget.parentNode) return;
86
- let compStyles = window.getComputedStyle(this.resizeWidget.parentNode);
87
- let gridColumns = compStyles.gridTemplateColumns;
88
- let height = compStyles.gridAutoRows;
89
-
90
- if (gridColumns !== "none") {
91
- let width = gridColumns.split(" ");
92
- this.limitSpan = width.length;
93
- this.gridColumnGap = Number.parseInt(compStyles.gridColumnGap);
94
- this.gridRowGap = Number.parseInt(compStyles.gridRowGap);
95
- this.gridWidth = Number.parseFloat(width[0]);
96
- this.gridHeight = Number.parseInt(height);
97
- }
98
- },
99
-
100
- checkGridColumns: function () {
101
- if (
102
- typeof this.resizeWidget !== "undefined" &&
103
- this.resizeWidget.parentNode
104
- ) {
105
- let compStylesOfParent = window.getComputedStyle(
106
- this.resizeWidget.parentNode
107
- );
108
- let spans = 0;
109
- let gridColumns = compStylesOfParent.gridTemplateColumns.split(" ");
110
- let temp = gridColumns[0];
111
- gridColumns.map((v) => {
112
- if (v == temp) spans++;
113
- });
114
- this.limitSpan = spans;
115
- if (this.resizeWidget.style["grid-column-end"]) {
116
- let curClassAttributes = this.resizeWidget
117
- .getAttribute("class")
118
- .split(" ");
119
- let prevColumnState =
120
- curClassAttributes[curClassAttributes.length - 1].split(
121
- "_"
122
- );
123
- let prevSpan = Number.parseInt(prevColumnState[1]);
124
- let curColumnState =
125
- this.resizeWidget.style["grid-column-end"].split(" ");
126
- let curSpan = Number.parseInt(curColumnState[1]);
127
-
128
- let span = 0;
129
- if (this.limitSpan >= prevSpan) span = prevSpan;
130
- else if (prevSpan > this.limitSpan) span = this.limitSpan;
131
- else span = Math.min(curSpan, this.limitSpan);
132
-
133
- this.resizeWidget.style["grid-column-end"] = "span " + span;
134
- }
135
- }
136
- },
137
-
138
- initResize: function () {
139
- DIRECTIONS.map((d) => {
140
- if (this.Drags[d])
141
- this.addListenerMulti(
142
- this.Drags[d],
143
- EVENTS[0],
144
- this.checkCorners[d]
145
- );
146
- });
147
- },
148
-
149
- checkDragImplementation: function (e, from, to, offset, fcur, scur) {
150
- if (e.touches) e = e.touches[0];
151
- let other;
152
- if (to == "top") other = "bottom";
153
- else if (to == "bottom") other = "top";
154
- else if (to == "left") other = "right";
155
- else if (to == "right") other = "left";
156
- this.removeListenerMulti(
157
- this.Drags[from],
158
- EVENTS[1],
159
- this.initDrags[from]
160
- );
161
- this.removeListenerMulti(
162
- this.Drags[from],
163
- EVENTS[1],
164
- this.initDrags[to]
165
- );
166
- this.removeListenerMulti(
167
- this.Drags[from],
168
- EVENTS[1],
169
- this.initDrags[other]
170
- );
171
- this.addListenerMulti(
172
- this.Drags[from],
173
- EVENTS[1],
174
- this.initDrags[from]
175
- );
176
-
177
- if (offset < this.cornerSize && this.Drags[to]) {
178
- this.Drags[from].style.cursor = fcur;
179
- this.addListenerMulti(
180
- this.Drags[from],
181
- EVENTS[1],
182
- this.initDrags[to]
183
- );
184
- } else {
185
- this.Drags[from].style.cursor = scur;
186
- }
187
- },
188
-
189
- initDrag: function (e, idx) {
190
- e.preventDefault();
191
-
192
- if (!this.resizeWidget.hasAttribute("resizing")) {
193
- this.resizeWidget.setAttribute("resizing", "");
194
- }
195
-
196
- let selector = document.defaultView.getComputedStyle(this.resizeWidget);
197
- styleSheet.insertRule("iframe {pointer-events: none}", 0);
198
-
199
- if (idx == "top" || idx == "bottom") {
200
- this.startTop = parseInt(selector.top, 10);
201
- this.startHeight = parseInt(selector.height, 10);
202
-
203
- if (e.touches) this.startY = e.touches[0].clientY;
204
- else this.startY = e.clientY;
205
- } else {
206
- this.startLeft = parseInt(selector.left, 10);
207
- this.startRight = parseInt(selector.right, 10);
208
- this.startWidth = parseInt(selector.width, 10);
209
-
210
- if (e.touches) this.startX = e.touches[0].clientX;
211
- else this.startX = e.clientX;
212
- }
213
-
214
- this.addListenerMulti(
215
- document.documentElement,
216
- EVENTS[0],
217
- this.doDrags[idx]
218
- );
219
- this.addListenerMulti(
220
- document.documentElement,
221
- EVENTS[2],
222
- this.stopDrag
223
- );
224
- },
225
-
226
- initTopDrag: function (e) {
227
- this.initDrag(e, "top");
228
- },
229
- initBottomDrag: function (e) {
230
- this.initDrag(e, "bottom");
231
- },
232
- initLeftDrag: function (e) {
233
- this.initDrag(e, "left");
234
- },
235
- initRightDrag: function (e) {
236
- this.initDrag(e, "right");
237
- },
238
-
239
- //this is just for grid system
240
- setRowEnd: function (height) {
241
- if (this.gridHeight) {
242
- this.heightSpan = Math.ceil(
243
- (height - (this.heightSpan - 1) * this.gridRowGap) /
244
- this.gridHeight
245
- );
246
- this.resizeWidget.style["grid-row-end"] = "span " + this.heightSpan;
247
- this.collaborate({ rowEnd: this.heightSpan });
248
- }
249
- },
250
-
251
- doTopDrag: function (e) {
252
- let top, height;
253
-
254
- if (e.touches) e = e.touches[0];
255
- top = this.startTop + e.clientY - this.startY;
256
- height = this.startHeight - e.clientY + this.startY;
257
-
258
- if (height < 10) return;
259
- if (!this.isGrid) {
260
- this.resizeWidget.style.top = top + "px";
261
- this.collaborate({ top, height });
262
- }
263
- this.resizeWidget.style.height = height + "px";
264
- this.setRowEnd(height);
265
- },
266
-
267
- doBottomDrag: function (e) {
268
- let height;
269
-
270
- if (e.touches) e = e.touches[0];
271
-
272
- height = this.startHeight + e.clientY - this.startY;
273
-
274
- if (height < 10) return;
275
- if (!this.isGrid) this.collaborate({ height });
276
-
277
- this.resizeWidget.style.height = height + "px";
278
- this.setRowEnd(height);
279
- },
280
-
281
- //this is just for grid system
282
- setColumnEnd: function (width) {
283
- if (this.gridWidth) {
284
- let spans = Math.ceil(
285
- (width - (this.widthSpan - 1) * this.gridColumnGap) /
286
- this.gridWidth
287
- );
288
- this.widthSpan = spans > this.limitSpan ? this.limitSpan : spans;
289
- this.resizeWidget.style["grid-column-end"] =
290
- "span " + this.widthSpan;
291
- this.collaborate({ columnEnd: this.widthSpan });
292
- }
293
- },
294
-
295
- doLeftDrag: function (e) {
296
- let left, right, width;
297
- if (e.touches) e = e.touches[0];
298
- left = this.startLeft + e.clientX - this.startX;
299
- right = this.startRight;
300
- width = this.startWidth - e.clientX + this.startX;
301
-
302
- if (width < 10) return;
303
- if (!this.isGrid) {
304
- this.resizeWidget.style.left = "";
305
- this.resizeWidget.style.right = right + "px";
306
- this.collaborate({ left, width });
307
- }
308
-
309
- this.resizeWidget.style.width = width + "px";
310
- this.setColumnEnd(width);
311
- },
312
-
313
- doRightDrag: function (e) {
314
- let width;
315
- if (e.touches) e = e.touches[0];
316
- width = this.startWidth + e.clientX - this.startX;
317
- if (width < 10) return;
318
-
319
- if (!this.isGrid) this.collaborate({ width });
320
-
321
- this.resizeWidget.style.width = width + "px";
322
- this.setColumnEnd(width);
323
- },
324
-
325
- detectGrid: function () {
326
- let isGrid = false;
327
- if (this.resizeWidget.parentNode) {
328
- let compStyles = window.getComputedStyle(
329
- this.resizeWidget.parentNode
330
- );
331
- isGrid = compStyles.display === "grid";
332
- }
333
- this.isGrid = isGrid;
334
- return isGrid;
335
- },
336
-
337
- stopDrag: function (e) {
338
- styleSheet.deleteRule(0);
339
- if (this.resizeWidget.hasAttribute("resizing")) {
340
- this.resizeWidget.removeAttribute("resizing", "");
341
- }
342
-
343
- //this is just for grid system
344
- if (this.detectGrid()) {
345
- this.resizeWidget.style.width = null;
346
- this.resizeWidget.style.height = null;
347
- this.resizeWidget.style.left = null;
348
- this.resizeWidget.style.top = null;
349
-
350
- if (this.widthSpan)
351
- this.resizeWidget.setAttribute(
352
- "class",
353
- this.originClassAttribute +
354
- " grid-column-end:span_" +
355
- this.widthSpan
356
- );
357
- if (this.heightSpan)
358
- this.resizeWidget.setAttribute(
359
- "class",
360
- this.originClassAttribute +
361
- " grid-row-end:span_" +
362
- this.heightSpan
363
- );
364
- }
365
-
366
- DIRECTIONS.map((d) => {
367
- this.removeListenerMulti(
368
- document.documentElement,
369
- EVENTS[0],
370
- this.doDrags[d]
371
- );
372
- });
373
- this.removeListenerMulti(
374
- document.documentElement,
375
- EVENTS[2],
376
- this.stopDrag
377
- );
378
- },
379
- checkLeftCorners: function (e) {
380
- let offset =
381
- e.clientY -
382
- this.getDistance(this.Drags["left"], true) +
383
- document.documentElement.scrollTop;
384
- if (offset > 10) {
385
- offset =
386
- this.getDistance(this.Drags["left"], true) +
387
- this.Drags["left"].clientHeight -
388
- e.clientY -
389
- document.documentElement.scrollTop;
390
- this.checkDragImplementation(
391
- e,
392
- "left",
393
- "bottom",
394
- offset,
395
- "ne-resize",
396
- "e-resize"
397
- );
398
- } else {
399
- this.checkDragImplementation(
400
- e,
401
- "left",
402
- "top",
403
- offset,
404
- "se-resize",
405
- "e-resize"
406
- );
407
- }
408
- },
409
- checkTopCorners: function (e) {
410
- let offset =
411
- e.clientX -
412
- this.getDistance(this.Drags["top"], false) +
413
- document.documentElement.scrollTop;
414
- if (offset > 10) {
415
- offset =
416
- this.getDistance(this.Drags["top"], false) +
417
- this.Drags["top"].clientWidth -
418
- e.clientX -
419
- document.documentElement.scrollLeft;
420
- this.checkDragImplementation(
421
- e,
422
- "top",
423
- "right",
424
- offset,
425
- "ne-resize",
426
- "s-resize"
427
- );
428
- } else {
429
- this.checkDragImplementation(
430
- e,
431
- "top",
432
- "left",
433
- offset,
434
- "se-resize",
435
- "s-resize"
436
- );
437
- }
438
- },
439
- checkBottomCorners: function (e) {
440
- let offset =
441
- e.clientX -
442
- this.getDistance(this.Drags["bottom"], false) +
443
- document.documentElement.scrollTop;
444
- if (offset > 10) {
445
- offset =
446
- this.getDistance(this.Drags["bottom"], false) +
447
- this.Drags["bottom"].clientWidth -
448
- e.clientX -
449
- document.documentElement.scrollLeft;
450
- this.checkDragImplementation(
451
- e,
452
- "bottom",
453
- "right",
454
- offset,
455
- "se-resize",
456
- "s-resize"
457
- );
458
- } else {
459
- this.checkDragImplementation(
460
- e,
461
- "bottom",
462
- "left",
463
- offset,
464
- "ne-resize",
465
- "s-resize"
466
- );
467
- }
468
- },
469
- checkRightCorners: function (e) {
470
- let offset =
471
- e.clientY -
472
- this.getDistance(this.Drags["right"], true) +
473
- document.documentElement.scrollTop;
474
- if (offset > 10) {
475
- offset =
476
- this.getDistance(this.Drags["right"], true) +
477
- this.Drags["right"].clientHeight -
478
- e.clientY -
479
- document.documentElement.scrollTop;
480
- this.checkDragImplementation(
481
- e,
482
- "right",
483
- "bottom",
484
- offset,
485
- "se-resize",
486
- "e-resize"
487
- );
488
- } else {
489
- this.checkDragImplementation(
490
- e,
491
- "right",
492
- "top",
493
- offset,
494
- "ne-resize",
495
- "e-resize"
496
- );
497
- }
498
- },
499
-
500
- bindListeners: function () {
501
- this.doDrags["left"] = this.doLeftDrag.bind(this);
502
- this.doDrags["top"] = this.doTopDrag.bind(this);
503
- this.doDrags["right"] = this.doRightDrag.bind(this);
504
- this.doDrags["bottom"] = this.doBottomDrag.bind(this);
505
-
506
- this.stopDrag = this.stopDrag.bind(this);
507
-
508
- this.checkCorners["left"] = this.checkLeftCorners.bind(this);
509
- this.checkCorners["top"] = this.checkTopCorners.bind(this);
510
- this.checkCorners["bottom"] = this.checkBottomCorners.bind(this);
511
- this.checkCorners["right"] = this.checkRightCorners.bind(this);
512
-
513
- this.initDrags["top"] = this.initTopDrag.bind(this);
514
- this.initDrags["left"] = this.initLeftDrag.bind(this);
515
- this.initDrags["bottom"] = this.initBottomDrag.bind(this);
516
- this.initDrags["right"] = this.initRightDrag.bind(this);
517
- },
518
-
519
- getDistance: function (elem, flag) {
520
- var location = 0;
521
- if (!elem) return;
522
- if (elem.offsetParent) {
523
- do {
524
- location += flag ? elem.offsetTop : elem.offsetLeft;
525
- elem = elem.offsetParent;
526
- } while (elem);
527
- }
528
- return location >= 0 ? location : 0;
529
- },
530
-
531
- // Bind multiiple events to a listener
532
- addListenerMulti: function (element, eventNames, listener) {
533
- var events = eventNames.split(" ");
534
- for (var i = 0, iLen = events.length; i < iLen; i++) {
535
- element.addEventListener(events[i], listener, { passive: false });
536
- }
537
- },
538
-
539
- // Remove multiiple events from a listener
540
- removeListenerMulti: function (element, eventNames, listener) {
541
- var events = eventNames.split(" ");
542
- for (var i = 0, iLen = events.length; i < iLen; i++) {
543
- element.removeEventListener(events[i], listener, {
544
- passive: false
545
- });
546
- }
547
- },
548
-
549
- collaborate: function ({ left, top, width, height, rowEnd, columnEnd }) {
550
- if (window.CoCreate && window.CoCreate.text) {
551
- let domTextEditor = this.resizeWidget.closest("[contenteditable]");
552
- if (domTextEditor) {
553
- if (left)
554
- CoCreate.text.setStyle({
555
- domTextEditor,
556
- target: this.resizeWidget,
557
- property: "left",
558
- value: left + "px"
559
- });
560
- if (top)
561
- CoCreate.text.setStyle({
562
- domTextEditor,
563
- target: this.resizeWidget,
564
- property: "top",
565
- value: top + "px"
566
- });
567
- if (width)
568
- CoCreate.text.setStyle({
569
- domTextEditor,
570
- target: this.resizeWidget,
571
- property: "width",
572
- value: width + "px"
573
- });
574
- if (height)
575
- CoCreate.text.setStyle({
576
- domTextEditor,
577
- target: this.resizeWidget,
578
- property: "height",
579
- value: height + "px"
580
- });
581
- if (rowEnd)
582
- CoCreate.text.setStyle({
583
- domTextEditor,
584
- target: this.resizeWidget,
585
- property: "grid-row-end",
586
- value: "span " + rowEnd
587
- });
588
- if (columnEnd)
589
- CoCreate.text.setStyle({
590
- domTextEditor,
591
- target: this.resizeWidget,
592
- property: "grid-column-end",
593
- value: "span " + columnEnd
594
- });
595
- }
596
- }
597
- }
85
+ init: function (handleObj) {
86
+ if (this.resizeWidget) {
87
+ this.initDrags = [];
88
+ this.checkCorners = [];
89
+ this.doDrags = [];
90
+ this.Drags = [];
91
+
92
+ //this is for grid
93
+ this.isGrid = this.detectGrid();
94
+ this.gridWidth = 0;
95
+ this.gridHeight = 0;
96
+ this.limitSpan = 0;
97
+ this.widthSpan = 0;
98
+ this.heightSpan = 0;
99
+ this.originClassAttribute = this.resizeWidget.getAttribute("class");
100
+
101
+ DIRECTIONS.map((d) => {
102
+ this.Drags[d] = this.resizeWidget.querySelector(
103
+ handleObj["drag" + d.charAt(0).toUpperCase() + d.slice(1)]
104
+ );
105
+ });
106
+
107
+ this.bindListeners();
108
+ this.initResize();
109
+
110
+ //this is just for grid system
111
+ this.getGridProperty();
112
+ window.addEventListener("resize", this.checkGridColumns.bind(this));
113
+ }
114
+ },
115
+
116
+ getGridProperty: function () {
117
+ if (!this.resizeWidget.parentNode) return;
118
+ let compStyles = window.getComputedStyle(this.resizeWidget.parentNode);
119
+ let gridColumns = compStyles.gridTemplateColumns;
120
+ let height = compStyles.gridAutoRows;
121
+
122
+ if (gridColumns !== "none") {
123
+ let width = gridColumns.split(" ");
124
+ this.limitSpan = width.length;
125
+ this.gridColumnGap = Number.parseInt(compStyles.gridColumnGap);
126
+ this.gridRowGap = Number.parseInt(compStyles.gridRowGap);
127
+ this.gridWidth = Number.parseFloat(width[0]);
128
+ this.gridHeight = Number.parseInt(height);
129
+ }
130
+ },
131
+
132
+ checkGridColumns: function () {
133
+ if (
134
+ typeof this.resizeWidget !== "undefined" &&
135
+ this.resizeWidget.parentNode
136
+ ) {
137
+ let compStylesOfParent = window.getComputedStyle(
138
+ this.resizeWidget.parentNode
139
+ );
140
+ let spans = 0;
141
+ let gridColumns = compStylesOfParent.gridTemplateColumns.split(" ");
142
+ let temp = gridColumns[0];
143
+ gridColumns.map((v) => {
144
+ if (v == temp) spans++;
145
+ });
146
+ this.limitSpan = spans;
147
+ if (this.resizeWidget.style["grid-column-end"]) {
148
+ let curClassAttributes = this.resizeWidget
149
+ .getAttribute("class")
150
+ .split(" ");
151
+ let prevColumnState =
152
+ curClassAttributes[curClassAttributes.length - 1].split(
153
+ "_"
154
+ );
155
+ let prevSpan = Number.parseInt(prevColumnState[1]);
156
+ let curColumnState =
157
+ this.resizeWidget.style["grid-column-end"].split(" ");
158
+ let curSpan = Number.parseInt(curColumnState[1]);
159
+
160
+ let span = 0;
161
+ if (this.limitSpan >= prevSpan) span = prevSpan;
162
+ else if (prevSpan > this.limitSpan) span = this.limitSpan;
163
+ else span = Math.min(curSpan, this.limitSpan);
164
+
165
+ this.resizeWidget.style["grid-column-end"] = "span " + span;
166
+ }
167
+ }
168
+ },
169
+
170
+ initResize: function () {
171
+ DIRECTIONS.map((d) => {
172
+ if (this.Drags[d])
173
+ this.addListenerMulti(
174
+ this.Drags[d],
175
+ EVENTS[0],
176
+ this.checkCorners[d]
177
+ );
178
+ });
179
+ },
180
+
181
+ checkDragImplementation: function (e, from, to, offset, fcur, scur) {
182
+ if (e.touches) e = e.touches[0];
183
+ let other;
184
+ if (to == "top") other = "bottom";
185
+ else if (to == "bottom") other = "top";
186
+ else if (to == "left") other = "right";
187
+ else if (to == "right") other = "left";
188
+ this.removeListenerMulti(
189
+ this.Drags[from],
190
+ EVENTS[1],
191
+ this.initDrags[from]
192
+ );
193
+ this.removeListenerMulti(
194
+ this.Drags[from],
195
+ EVENTS[1],
196
+ this.initDrags[to]
197
+ );
198
+ this.removeListenerMulti(
199
+ this.Drags[from],
200
+ EVENTS[1],
201
+ this.initDrags[other]
202
+ );
203
+ this.addListenerMulti(
204
+ this.Drags[from],
205
+ EVENTS[1],
206
+ this.initDrags[from]
207
+ );
208
+
209
+ if (offset < this.cornerSize && this.Drags[to]) {
210
+ this.Drags[from].style.cursor = fcur;
211
+ this.addListenerMulti(
212
+ this.Drags[from],
213
+ EVENTS[1],
214
+ this.initDrags[to]
215
+ );
216
+ } else {
217
+ this.Drags[from].style.cursor = scur;
218
+ }
219
+ },
220
+
221
+ initDrag: function (e, idx) {
222
+ e.preventDefault();
223
+
224
+ if (!this.resizeWidget.hasAttribute("resizing")) {
225
+ this.resizeWidget.setAttribute("resizing", "");
226
+ }
227
+
228
+ let selector = document.defaultView.getComputedStyle(this.resizeWidget);
229
+ styleSheet.insertRule("iframe {pointer-events: none}", 0);
230
+
231
+ if (idx == "top" || idx == "bottom") {
232
+ this.startTop = parseInt(selector.top, 10);
233
+ this.startHeight = parseInt(selector.height, 10);
234
+
235
+ if (e.touches) this.startY = e.touches[0].clientY;
236
+ else this.startY = e.clientY;
237
+ } else {
238
+ this.startLeft = parseInt(selector.left, 10);
239
+ this.startRight = parseInt(selector.right, 10);
240
+ this.startWidth = parseInt(selector.width, 10);
241
+
242
+ if (e.touches) this.startX = e.touches[0].clientX;
243
+ else this.startX = e.clientX;
244
+ }
245
+
246
+ this.addListenerMulti(
247
+ document.documentElement,
248
+ EVENTS[0],
249
+ this.doDrags[idx]
250
+ );
251
+ this.addListenerMulti(
252
+ document.documentElement,
253
+ EVENTS[2],
254
+ this.stopDrag
255
+ );
256
+ },
257
+
258
+ initTopDrag: function (e) {
259
+ this.initDrag(e, "top");
260
+ },
261
+ initBottomDrag: function (e) {
262
+ this.initDrag(e, "bottom");
263
+ },
264
+ initLeftDrag: function (e) {
265
+ this.initDrag(e, "left");
266
+ },
267
+ initRightDrag: function (e) {
268
+ this.initDrag(e, "right");
269
+ },
270
+
271
+ //this is just for grid system
272
+ setRowEnd: function (height) {
273
+ if (this.gridHeight) {
274
+ this.heightSpan = Math.ceil(
275
+ (height - (this.heightSpan - 1) * this.gridRowGap) /
276
+ this.gridHeight
277
+ );
278
+ this.resizeWidget.style["grid-row-end"] = "span " + this.heightSpan;
279
+ this.collaborate({ rowEnd: this.heightSpan });
280
+ }
281
+ },
282
+
283
+ doTopDrag: function (e) {
284
+ let top, height;
285
+
286
+ if (e.touches) e = e.touches[0];
287
+ top = this.startTop + e.clientY - this.startY;
288
+ height = this.startHeight - e.clientY + this.startY;
289
+
290
+ if (height < 10) return;
291
+ if (!this.isGrid) {
292
+ this.resizeWidget.style.top = top + "px";
293
+ this.collaborate({ top, height });
294
+ }
295
+ this.resizeWidget.style.height = height + "px";
296
+ this.setRowEnd(height);
297
+ },
298
+
299
+ doBottomDrag: function (e) {
300
+ let height;
301
+
302
+ if (e.touches) e = e.touches[0];
303
+
304
+ height = this.startHeight + e.clientY - this.startY;
305
+
306
+ if (height < 10) return;
307
+ if (!this.isGrid) this.collaborate({ height });
308
+
309
+ this.resizeWidget.style.height = height + "px";
310
+ this.setRowEnd(height);
311
+ },
312
+
313
+ //this is just for grid system
314
+ setColumnEnd: function (width) {
315
+ if (this.gridWidth) {
316
+ let spans = Math.ceil(
317
+ (width - (this.widthSpan - 1) * this.gridColumnGap) /
318
+ this.gridWidth
319
+ );
320
+ this.widthSpan = spans > this.limitSpan ? this.limitSpan : spans;
321
+ this.resizeWidget.style["grid-column-end"] =
322
+ "span " + this.widthSpan;
323
+ this.collaborate({ columnEnd: this.widthSpan });
324
+ }
325
+ },
326
+
327
+ doLeftDrag: function (e) {
328
+ let left, right, width;
329
+ if (e.touches) e = e.touches[0];
330
+ left = this.startLeft + e.clientX - this.startX;
331
+ right = this.startRight;
332
+ width = this.startWidth - e.clientX + this.startX;
333
+
334
+ if (width < 10) return;
335
+ if (!this.isGrid) {
336
+ this.resizeWidget.style.left = "";
337
+ this.resizeWidget.style.right = right + "px";
338
+ this.collaborate({ left, width });
339
+ }
340
+
341
+ this.resizeWidget.style.width = width + "px";
342
+ this.setColumnEnd(width);
343
+ },
344
+
345
+ doRightDrag: function (e) {
346
+ let width;
347
+ if (e.touches) e = e.touches[0];
348
+ width = this.startWidth + e.clientX - this.startX;
349
+ if (width < 10) return;
350
+
351
+ if (!this.isGrid) this.collaborate({ width });
352
+
353
+ this.resizeWidget.style.width = width + "px";
354
+ this.setColumnEnd(width);
355
+ },
356
+
357
+ detectGrid: function () {
358
+ let isGrid = false;
359
+ if (this.resizeWidget.parentNode) {
360
+ let compStyles = window.getComputedStyle(
361
+ this.resizeWidget.parentNode
362
+ );
363
+ isGrid = compStyles.display === "grid";
364
+ }
365
+ this.isGrid = isGrid;
366
+ return isGrid;
367
+ },
368
+
369
+ stopDrag: function (e) {
370
+ styleSheet.deleteRule(0);
371
+ if (this.resizeWidget.hasAttribute("resizing")) {
372
+ this.resizeWidget.removeAttribute("resizing", "");
373
+ }
374
+
375
+ //this is just for grid system
376
+ if (this.detectGrid()) {
377
+ this.resizeWidget.style.width = null;
378
+ this.resizeWidget.style.height = null;
379
+ this.resizeWidget.style.left = null;
380
+ this.resizeWidget.style.top = null;
381
+
382
+ if (this.widthSpan)
383
+ this.resizeWidget.setAttribute(
384
+ "class",
385
+ this.originClassAttribute +
386
+ " grid-column-end:span_" +
387
+ this.widthSpan
388
+ );
389
+ if (this.heightSpan)
390
+ this.resizeWidget.setAttribute(
391
+ "class",
392
+ this.originClassAttribute +
393
+ " grid-row-end:span_" +
394
+ this.heightSpan
395
+ );
396
+
397
+ this.collaborate({});
398
+ }
399
+
400
+ DIRECTIONS.map((d) => {
401
+ this.removeListenerMulti(
402
+ document.documentElement,
403
+ EVENTS[0],
404
+ this.doDrags[d]
405
+ );
406
+ });
407
+ this.removeListenerMulti(
408
+ document.documentElement,
409
+ EVENTS[2],
410
+ this.stopDrag
411
+ );
412
+ },
413
+ checkLeftCorners: function (e) {
414
+ let offset =
415
+ e.clientY -
416
+ this.getDistance(this.Drags["left"], true) +
417
+ document.documentElement.scrollTop;
418
+ if (offset > 10) {
419
+ offset =
420
+ this.getDistance(this.Drags["left"], true) +
421
+ this.Drags["left"].clientHeight -
422
+ e.clientY -
423
+ document.documentElement.scrollTop;
424
+ this.checkDragImplementation(
425
+ e,
426
+ "left",
427
+ "bottom",
428
+ offset,
429
+ "ne-resize",
430
+ "e-resize"
431
+ );
432
+ } else {
433
+ this.checkDragImplementation(
434
+ e,
435
+ "left",
436
+ "top",
437
+ offset,
438
+ "se-resize",
439
+ "e-resize"
440
+ );
441
+ }
442
+ },
443
+ checkTopCorners: function (e) {
444
+ let offset =
445
+ e.clientX -
446
+ this.getDistance(this.Drags["top"], false) +
447
+ document.documentElement.scrollTop;
448
+ if (offset > 10) {
449
+ offset =
450
+ this.getDistance(this.Drags["top"], false) +
451
+ this.Drags["top"].clientWidth -
452
+ e.clientX -
453
+ document.documentElement.scrollLeft;
454
+ this.checkDragImplementation(
455
+ e,
456
+ "top",
457
+ "right",
458
+ offset,
459
+ "ne-resize",
460
+ "s-resize"
461
+ );
462
+ } else {
463
+ this.checkDragImplementation(
464
+ e,
465
+ "top",
466
+ "left",
467
+ offset,
468
+ "se-resize",
469
+ "s-resize"
470
+ );
471
+ }
472
+ },
473
+ checkBottomCorners: function (e) {
474
+ let offset =
475
+ e.clientX -
476
+ this.getDistance(this.Drags["bottom"], false) +
477
+ document.documentElement.scrollTop;
478
+ if (offset > 10) {
479
+ offset =
480
+ this.getDistance(this.Drags["bottom"], false) +
481
+ this.Drags["bottom"].clientWidth -
482
+ e.clientX -
483
+ document.documentElement.scrollLeft;
484
+ this.checkDragImplementation(
485
+ e,
486
+ "bottom",
487
+ "right",
488
+ offset,
489
+ "se-resize",
490
+ "s-resize"
491
+ );
492
+ } else {
493
+ this.checkDragImplementation(
494
+ e,
495
+ "bottom",
496
+ "left",
497
+ offset,
498
+ "ne-resize",
499
+ "s-resize"
500
+ );
501
+ }
502
+ },
503
+ checkRightCorners: function (e) {
504
+ let offset =
505
+ e.clientY -
506
+ this.getDistance(this.Drags["right"], true) +
507
+ document.documentElement.scrollTop;
508
+ if (offset > 10) {
509
+ offset =
510
+ this.getDistance(this.Drags["right"], true) +
511
+ this.Drags["right"].clientHeight -
512
+ e.clientY -
513
+ document.documentElement.scrollTop;
514
+ this.checkDragImplementation(
515
+ e,
516
+ "right",
517
+ "bottom",
518
+ offset,
519
+ "se-resize",
520
+ "e-resize"
521
+ );
522
+ } else {
523
+ this.checkDragImplementation(
524
+ e,
525
+ "right",
526
+ "top",
527
+ offset,
528
+ "ne-resize",
529
+ "e-resize"
530
+ );
531
+ }
532
+ },
533
+
534
+ bindListeners: function () {
535
+ this.doDrags["left"] = this.doLeftDrag.bind(this);
536
+ this.doDrags["top"] = this.doTopDrag.bind(this);
537
+ this.doDrags["right"] = this.doRightDrag.bind(this);
538
+ this.doDrags["bottom"] = this.doBottomDrag.bind(this);
539
+
540
+ this.stopDrag = this.stopDrag.bind(this);
541
+
542
+ this.checkCorners["left"] = this.checkLeftCorners.bind(this);
543
+ this.checkCorners["top"] = this.checkTopCorners.bind(this);
544
+ this.checkCorners["bottom"] = this.checkBottomCorners.bind(this);
545
+ this.checkCorners["right"] = this.checkRightCorners.bind(this);
546
+
547
+ this.initDrags["top"] = this.initTopDrag.bind(this);
548
+ this.initDrags["left"] = this.initLeftDrag.bind(this);
549
+ this.initDrags["bottom"] = this.initBottomDrag.bind(this);
550
+ this.initDrags["right"] = this.initRightDrag.bind(this);
551
+ },
552
+
553
+ getDistance: function (elem, flag) {
554
+ var location = 0;
555
+ if (!elem) return;
556
+ if (elem.offsetParent) {
557
+ do {
558
+ location += flag ? elem.offsetTop : elem.offsetLeft;
559
+ elem = elem.offsetParent;
560
+ } while (elem);
561
+ }
562
+ return location >= 0 ? location : 0;
563
+ },
564
+
565
+ // Bind multiiple events to a listener
566
+ addListenerMulti: function (element, eventNames, listener) {
567
+ var events = eventNames.split(" ");
568
+ for (var i = 0, iLen = events.length; i < iLen; i++) {
569
+ element.addEventListener(events[i], listener, { passive: false });
570
+ }
571
+ },
572
+
573
+ // Remove multiiple events from a listener
574
+ removeListenerMulti: function (element, eventNames, listener) {
575
+ var events = eventNames.split(" ");
576
+ for (var i = 0, iLen = events.length; i < iLen; i++) {
577
+ element.removeEventListener(events[i], listener, {
578
+ passive: false
579
+ });
580
+ }
581
+ },
582
+
583
+ collaborate: function ({ left, top, width, height, rowEnd, columnEnd }) {
584
+ const details = typeof this.resizeWidget.editor?.getStart === "function"
585
+ ? this.resizeWidget.editor : this.resizeWidget.editorDetails;
586
+ const editorElement = details?.element || details?.editorElement;
587
+ if (typeof details?.setAttribute !== "function" && !editorElement?.editor?.setAttribute) return;
588
+
589
+ const style = this.resizeWidget.ownerDocument.createElement("span").style;
590
+ style.cssText = this.resizeWidget.getAttribute("style") || "";
591
+ if (left !== undefined) style.left = left + "px";
592
+ if (top !== undefined) style.top = top + "px";
593
+ if (width !== undefined) style.width = width + "px";
594
+ if (height !== undefined) style.height = height + "px";
595
+ if (rowEnd !== undefined) style.setProperty("grid-row-end", "span " + rowEnd);
596
+ if (columnEnd !== undefined) style.setProperty("grid-column-end", "span " + columnEnd);
597
+
598
+ const result = typeof details.setAttribute === "function"
599
+ ? details.setAttribute("style", style.cssText)
600
+ : editorElement.editor.setAttribute({ target: this.resizeWidget, name: "style", value: style.cssText });
601
+ result?.catch(error => this.resizeWidget.dispatchEvent(new CustomEvent("editor-error", {
602
+ bubbles: true,
603
+ detail: { error }
604
+ })));
605
+ }
598
606
  };
599
607
 
600
608
  // Function to auto-resize the element based on its content
601
609
  function autoResizeElement(element) {
602
- if (!element) return;
603
-
604
- if (element.tagName === "TEXTAREA") {
605
- element.style.height = "auto";
606
-
607
- let scrollHeight;
608
- if (element.contentDocument) {
609
- scrollHeight = element.contentDocument.body.scrollHeight;
610
- } else {
611
- // For other elements, get scrollHeight directly
612
- scrollHeight = element.scrollHeight;
613
- }
614
-
615
- // Set the height of the element to the calculated scrollHeight
616
- element.style.height = scrollHeight + "px";
617
- } else if (element.tagName === "SELECT" && element.multiple) {
618
- element.size = Math.max(element.options.length, 1); // Ensure at least 1 visible option
619
- }
610
+ if (!element) return;
611
+
612
+ if (element.tagName === "TEXTAREA") {
613
+ element.style.height = "auto";
614
+
615
+ const style = getComputedStyle(element);
616
+ const padding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
617
+ const border = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
618
+ // scrollHeight includes padding, but not borders.
619
+ const height = element.scrollHeight +
620
+ (style.boxSizing === "border-box" ? border : -padding);
621
+ element.style.height = Math.max(0, height) + "px";
622
+ } else if (element.tagName === "SELECT" && element.multiple) {
623
+ element.size = Math.max(element.options.length, 1); // Ensure at least 1 visible option
624
+ }
620
625
  }
621
626
 
622
627
  // Observe the resize of the element
623
628
  function observeElementResize(element) {
624
- const resizeObserver = new ResizeObserver(() => {
625
- autoResizeElement(element);
626
- });
629
+ const resizeObserver = new ResizeObserver(() => {
630
+ autoResizeElement(element);
631
+ });
627
632
 
628
- resizeObserver.observe(element);
633
+ resizeObserver.observe(element);
629
634
  }
630
635
 
631
636
  // Listen for input events on the entire document
632
637
  function initAutoHeight(element) {
633
- if (
634
- element &&
635
- !(element instanceof HTMLCollection) &&
636
- !(element instanceof NodeList) &&
637
- !Array.isArray(element)
638
- ) {
639
- element = [element];
640
- } else if (!element) {
641
- element = document.querySelectorAll('[height="auto"]');
642
-
643
- // Add event listener for iframes on load
644
- document.addEventListener(
645
- "input",
646
- function (event) {
647
- // Check if the target element has height="auto"
648
- if (event.target.getAttribute("height") === "auto") {
649
- autoResizeElement(event.target);
650
- }
651
- },
652
- false
653
- );
654
-
655
- // if (element.contentDocument) {
656
- // // Create a MutationObserver for changes within the iframe's body
657
- // const observer = new MutationObserver(autoResizeElement);
658
- // observer.observe(iframeDocument.body, {
659
- // childList: true,
660
- // subtree: true
661
- // });
662
-
663
- // // Handle iframe document changes (e.g., if src or srcdoc is modified)
664
- // element.addEventListener("load", () => {
665
- // observer.disconnect(); // Disconnect the previous observer
666
- // observeIframeContent(); // Reinitialize the observer on new document
667
- // });
668
- // }
669
- }
670
-
671
- for (let i = 0; i < element.length; i++) {
672
- if (element[i].tagName === "TEXTAREA") {
673
- element[i].rows = 1;
674
- }
675
- autoResizeElement(element[i]);
676
- observeElementResize(element[i]); // Observe resize changes.
677
- }
638
+ if (
639
+ element &&
640
+ !(element instanceof HTMLCollection) &&
641
+ !(element instanceof NodeList) &&
642
+ !Array.isArray(element)
643
+ ) {
644
+ element = [element];
645
+ } else if (!element) {
646
+ element = document.querySelectorAll('[height="auto"]');
647
+
648
+ // Add event listener for iframes on load
649
+ document.addEventListener(
650
+ "input",
651
+ function (event) {
652
+ // Check if the target element has height="auto"
653
+ if (event.target.getAttribute("height") === "auto") {
654
+ autoResizeElement(event.target);
655
+ }
656
+ },
657
+ false
658
+ );
659
+ }
660
+
661
+ for (let i = 0; i < element.length; i++) {
662
+ if (element[i].tagName === "TEXTAREA") {
663
+ element[i].rows = 1;
664
+ }
665
+ autoResizeElement(element[i]);
666
+ observeElementResize(element[i]); // Observe resize changes.
667
+ }
678
668
  }
679
669
 
680
670
  initAutoHeight();
681
671
 
682
672
  observer.init({
683
- name: "CoCreateResize",
684
- types: ["addedNodes"],
685
- selector: "[resizable]:not([resizable='false'])",
686
- callback: function (mutation) {
687
- coCreateResize.initElement(mutation.target);
688
- }
673
+ name: "CoCreateResize",
674
+ types: ["addedNodes"],
675
+ selector: "[resizable]:not([resizable='false'])",
676
+ callback: function (mutation) {
677
+ coCreateResize.initElement(mutation.target);
678
+ }
689
679
  });
690
680
 
691
681
  observer.init({
692
- name: "CoCreateResize",
693
- types: ["addedNodes"],
694
- selector: "[height='auto']",
695
- callback: function (mutation) {
696
- initAutoHeight(mutation.target);
697
- }
682
+ name: "CoCreateAutoResize",
683
+ types: ["addedNodes"],
684
+ selector: "[height='auto']",
685
+ callback: function (mutation) {
686
+ initAutoHeight(mutation.target);
687
+ }
698
688
  });
699
689
 
700
690
  observer.init({
701
- name: "CoCreateResize",
702
- types: ["addedNodes"],
703
- selector: "[height='auto'] option",
704
- callback: function (mutation) {
705
- autoResizeElement(mutation.target.parentElement);
706
- }
691
+ name: "CoCreateAutoResizeOptions",
692
+ types: ["addedNodes"],
693
+ selector: "[height='auto'] option",
694
+ callback: function (mutation) {
695
+ autoResizeElement(mutation.target.parentElement);
696
+ }
707
697
  });
708
698
 
709
699
  coCreateResize.init({
710
- selector: "[resizable]:not([resizable='false'])",
711
- dragLeft: "[resize='left']",
712
- dragRight: "[resize='right']",
713
- dragTop: "[resize='top']",
714
- dragBottom: "[resize='bottom']"
700
+ selector: "[resizable]:not([resizable='false'])",
701
+ dragLeft: "[resize='left']",
702
+ dragRight: "[resize='right']",
703
+ dragTop: "[resize='top']",
704
+ dragBottom: "[resize='bottom']"
715
705
  });
716
706
  export default coCreateResize;
707
+
708
+ /**
709
+ * Preserves the element's original class value while resize updates grid sizing.
710
+ * @name class
711
+ * @kind member
712
+ * @attribute
713
+ * @environment client
714
+ * @type {string}
715
+ */
716
+
717
+ /**
718
+ * Internal marker applied to this module's injected style element.
719
+ * @name component
720
+ * @kind member
721
+ * @attribute
722
+ * @environment client
723
+ * @type {string}
724
+ * @private
725
+ */
726
+
727
+ /**
728
+ * Enables automatic content-based resizing when set to "auto". This applies to textareas and multiple selects.
729
+ * @name height
730
+ * @kind member
731
+ * @attribute
732
+ * @environment client
733
+ * @type {string}
734
+ * @acceptedValues auto
735
+ * @example
736
+ * <textarea height="auto"></textarea>
737
+ */
738
+
739
+ /**
740
+ * Runtime state marker added while a resize gesture is active.
741
+ * @name resizing
742
+ * @kind member
743
+ * @attribute
744
+ * @environment client
745
+ * @type {boolean}
746
+ */
747
+
748
+ /**
749
+ * Inline styles updated with the element's resized dimensions and grid span.
750
+ * @name style
751
+ * @kind member
752
+ * @attribute
753
+ * @environment client
754
+ * @type {string}
755
+ */
756
+
757
+ /**
758
+ * Enables interactive resizing unless explicitly set to "false".
759
+ *
760
+ * @name resizable
761
+ * @kind member
762
+ * @attribute
763
+ * @environment client
764
+ * @type {boolean}
765
+ * @acceptedValues false
766
+ * @example
767
+ * <div resizable><span resize="right"></span></div>
768
+ */
769
+
770
+ /**
771
+ * Marks a child element as a resize handle and selects its drag direction.
772
+ *
773
+ * @name resize
774
+ * @kind member
775
+ * @attribute
776
+ * @environment client
777
+ * @type {string}
778
+ * @acceptedValues left, right, top, bottom
779
+ * @example
780
+ * <span resize="right"></span>
781
+ */
782
+
783
+ /**
784
+ * A light weight resize handler make any element resizable by adding data attributes or usinng init object. Great for Customized text box or text area.
785
+ *
786
+ * @module resize
787
+ * @environment client
788
+ */
789
+
790
+ /**
791
+ * Declarative selectors recognized by this module.
792
+ *
793
+ * @name declarative-selectors
794
+ * @kind member
795
+ * @selectors
796
+ * @environment client
797
+ * @example
798
+ * ```json
799
+ * [
800
+ * "",
801
+ * "[resizable]:not([resizable='false'])",
802
+ * "[height='auto']",
803
+ * "[height='auto'] option"
804
+ * ]
805
+ * ```
806
+ */