@innovastudio/contentbuilder 1.4.146 → 1.4.148

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.
@@ -10355,6 +10355,14 @@ class HtmlUtil {
10355
10355
  this.builder.opts.onRender();
10356
10356
  }
10357
10357
  if (mode === 'full') {
10358
+ // freeform
10359
+ if (this.builder.docContainer) {
10360
+ this.builder.loadHTML(html);
10361
+ util.clearControls();
10362
+ this.builder.hideModal(modal);
10363
+ return;
10364
+ }
10365
+
10358
10366
  // area.innerHTML = html;
10359
10367
  area.innerHTML = '';
10360
10368
  // Use createContextualFragment() to make embedded script executable
@@ -11204,6 +11212,23 @@ class HtmlUtil {
11204
11212
  elms = tmp.querySelectorAll('[data-html-25]');
11205
11213
  dom$k.removeAttributes(elms, 'data-html-25');
11206
11214
  }
11215
+
11216
+ // freeform
11217
+ elms = tmp.querySelectorAll('.is-block .handle, .is-block .rotate-handle');
11218
+ dom$k.removeElements(elms);
11219
+ elms = tmp.querySelectorAll('.is-block');
11220
+ Array.prototype.forEach.call(elms, elm => {
11221
+ dom$k.removeClass(elm, 'active');
11222
+ dom$k.removeClass(elm, 'multi');
11223
+ dom$k.removeClass(elm, 'editable');
11224
+ dom$k.removeClass(elm, 'fluid');
11225
+ dom$k.removeClass(elm, 'cloned');
11226
+ elm.removeAttribute('data-fluid-val');
11227
+ elm.removeAttribute('data-fluid');
11228
+ });
11229
+ tmp.querySelectorAll('.is-block.clone').forEach(elm => elm.parentNode.removeChild(elm));
11230
+ // tmp.querySelectorAll('.is-block.cloned').forEach(elm=>elm.classList.remove('cloned'));
11231
+
11207
11232
  html = tmp.innerHTML.trim();
11208
11233
  html = html.replace(/<font/g, '<span').replace(/<\/font/g, '</span');
11209
11234
  }
@@ -13287,26 +13312,26 @@ const renderQuickAdd = builder => {
13287
13312
  elm = quickadd.querySelector('.add-button');
13288
13313
  if (elm) dom.addEventListener(elm, 'click', () => {
13289
13314
  const mode = quickadd.getAttribute('data-mode');
13290
- let html = `<div class="flex">
13291
- <a href="#" role="button" class="transition-all inline-block whitespace-nowrap cursor-pointer no-underline border-2 border-solid mr-2 mt-2 mb-1 py-2 size-17 px-6 text-black leading-14 rounded border-transparent hover:border-transparent font-normal tracking-wide" style="background-color: rgb(240, 240, 240);">Read More</a>
13315
+ let html = `<div>
13316
+ <a href="#" role="button" class="transition-all inline-block whitespace-nowrap cursor-pointer no-underline border-2 border-solid mr-2 mt-2 mb-1 py-2 size-17 px-6 text-black leading-14 rounded border-transparent hover:border-transparent font-normal tracking-wide" style="background-color: rgb(240, 240, 240);">Read More</a>
13292
13317
  </div>`;
13293
13318
  if (builder.opts.emailMode) {
13294
- html = '<div class="flex"><a href="#" role="button" style="margin-top: ;margin-right: ;margin-bottom: ;margin-left: ;display: inline-block; text-decoration: none; transition: all 0.16s ease 0s; border-style: solid; cursor: pointer; background-color: rgb(220, 220, 220); color: rgb(0, 0, 0); border-color: rgb(220, 220, 220); border-width: 2px; border-radius: 0px; padding: 13px 28px; line-height: 21px; text-transform: uppercase; font-weight: 400; font-size: 14px; letter-spacing: 3px;">Read More</a></div>';
13319
+ html = '<div><a href="#" role="button" style="margin-top: ;margin-right: ;margin-bottom: ;margin-left: ;display: inline-block; text-decoration: none; transition: all 0.16s ease 0s; border-style: solid; cursor: pointer; background-color: rgb(220, 220, 220); color: rgb(0, 0, 0); border-color: rgb(220, 220, 220); border-width: 2px; border-radius: 0px; padding: 13px 28px; line-height: 21px; text-transform: uppercase; font-weight: 400; font-size: 14px; letter-spacing: 3px;">Read More</a></div>';
13295
13320
  }
13296
13321
  util.addContent(html, mode);
13297
13322
  });
13298
13323
  elm = quickadd.querySelector('.add-twobutton');
13299
13324
  if (elm) dom.addEventListener(elm, 'click', () => {
13300
13325
  const mode = quickadd.getAttribute('data-mode');
13301
- let html = `<div class="flex">
13326
+ let html = `<div>
13302
13327
  <a href="#" role="button" class="transition-all inline-block whitespace-nowrap cursor-pointer no-underline border-2 border-solid mr-2 mt-2 mb-1 py-2 size-17 px-6 text-black leading-14 rounded border-transparent hover:border-transparent font-normal tracking-wide" style="background-color: rgb(240, 240, 240);">Read More</a>
13303
13328
  <a href="#" role="button" class="transition-all inline-block whitespace-nowrap cursor-pointer no-underline border-2 border-solid mr-2 mt-2 mb-1 py-2 size-17 px-6 border-current hover:border-current font-normal leading-14 rounded tracking-wide">Get Started</a>
13304
13329
  </div>`;
13305
13330
  if (builder.opts.emailMode) {
13306
- html = `<div class="flex">
13307
- <a href="#" role="button" style="margin-top: ;margin-right: ;margin-bottom: ;margin-left: ;display: inline-block; text-decoration: none; transition: all 0.16s ease 0s; border-style: solid; cursor: pointer; background-color: rgb(220, 220, 220); color: rgb(0, 0, 0); border-color: rgb(220, 220, 220); border-width: 2px; border-radius: 0px; padding: 13px 28px; line-height: 21px; text-transform: uppercase; font-weight: 400; font-size: 14px; letter-spacing: 3px;">Read More</a> &nbsp;
13308
- <a href="#" role="button" style="display: inline-block; text-decoration: none; transition: all 0.16s ease 0s; border-style: solid; cursor: pointer; background-color: rgba(0, 0, 0, 0); border-color: rgb(53, 53, 53); border-width: 2px; border-radius: 0px; padding: 13px 28px; line-height: 21px; text-transform: uppercase; font-weight: 600; font-size: 14px; letter-spacing: 3px; color: rgb(53, 53, 53);">Get Started</a>
13309
- </div>`;
13331
+ html = `<div>
13332
+ <a href="#" role="button" style="margin-top: ;margin-right: ;margin-bottom: ;margin-left: ;display: inline-block; text-decoration: none; transition: all 0.16s ease 0s; border-style: solid; cursor: pointer; background-color: rgb(220, 220, 220); color: rgb(0, 0, 0); border-color: rgb(220, 220, 220); border-width: 2px; border-radius: 0px; padding: 13px 28px; line-height: 21px; text-transform: uppercase; font-weight: 400; font-size: 14px; letter-spacing: 3px;">Read More</a> &nbsp;
13333
+ <a href="#" role="button" style="display: inline-block; text-decoration: none; transition: all 0.16s ease 0s; border-style: solid; cursor: pointer; background-color: rgba(0, 0, 0, 0); border-color: rgb(53, 53, 53); border-width: 2px; border-radius: 0px; padding: 13px 28px; line-height: 21px; text-transform: uppercase; font-weight: 600; font-size: 14px; letter-spacing: 3px; color: rgb(53, 53, 53);">Get Started</a>
13334
+ </div>`;
13310
13335
  }
13311
13336
  util.addContent(html, mode);
13312
13337
  });
@@ -14530,7 +14555,7 @@ Insipred by: https://www.kirupa.com/html5/drag.htm
14530
14555
  */
14531
14556
 
14532
14557
  let initialX, initialY, currentX, currentY, xOffset, yOffset, dragActive, activeDraggableBox;
14533
- class Draggable$1 {
14558
+ class Draggable$2 {
14534
14559
  constructor(opts = {}) {
14535
14560
  this.opts = opts;
14536
14561
  const elms = document.querySelectorAll(this.opts.selector);
@@ -14694,7 +14719,7 @@ const renderGridEditor = builder => {
14694
14719
  //<button title="${util.out('Element Tool')}" class="cell-elmtool"><svg class="is-icon-flex" style="width:12px;height:12px;"><use xlink:href="#ion-ios-gear"></use></svg></button>
14695
14720
 
14696
14721
  dom.appendHtml(builderStuff, html);
14697
- new Draggable$1({
14722
+ new Draggable$2({
14698
14723
  selector: '.is-draggable'
14699
14724
  });
14700
14725
  const grideditor = document.querySelector('.grideditor');
@@ -28675,7 +28700,7 @@ function dragControlCondition(moveable, e) {
28675
28700
  return false;
28676
28701
  }
28677
28702
 
28678
- var Rotatable = {
28703
+ var Rotatable$1 = {
28679
28704
  name: "rotatable",
28680
28705
  canPinch: true,
28681
28706
  props: {
@@ -28814,7 +28839,7 @@ var Rotatable = {
28814
28839
  datas.startValue = rotatation * Math.PI / 180;
28815
28840
  }
28816
28841
  }, fillTransformStartEvent(e)), {
28817
- dragStart: Draggable.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
28842
+ dragStart: Draggable$1.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
28818
28843
  }));
28819
28844
  var result = triggerEvent(moveable, "onRotateStart", params);
28820
28845
  datas.isRotate = result !== false;
@@ -32515,7 +32540,7 @@ var Snappable = {
32515
32540
  * @description Draggable refers to the ability to drag and move targets.
32516
32541
  */
32517
32542
 
32518
- var Draggable = {
32543
+ var Draggable$1 = {
32519
32544
  name: "draggable",
32520
32545
  props: {
32521
32546
  draggable: Boolean,
@@ -33403,7 +33428,7 @@ function fillTransformEvent(moveable, nextTransform, delta, isPinch, e) {
33403
33428
  fillOriginalTransform(e, nextTransform);
33404
33429
  return {
33405
33430
  transform: nextTransform,
33406
- drag: Draggable.drag(moveable, setCustomDrag(e, moveable.state, delta, isPinch, false))
33431
+ drag: Draggable$1.drag(moveable, setCustomDrag(e, moveable.state, delta, isPinch, false))
33407
33432
  };
33408
33433
  }
33409
33434
 
@@ -35300,7 +35325,7 @@ var Pinchable = makeAble("pinchable", {
35300
35325
  * @description Resizable indicates whether the target's width and height can be increased or decreased.
35301
35326
  */
35302
35327
 
35303
- var Resizable = {
35328
+ var Resizable$1 = {
35304
35329
  name: "resizable",
35305
35330
  ableGroup: "size",
35306
35331
  canPinch: true,
@@ -35445,7 +35470,7 @@ var Resizable = {
35445
35470
  setOrigin: function (origin) {
35446
35471
  datas.transformOrigin = origin;
35447
35472
  },
35448
- dragStart: Draggable.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
35473
+ dragStart: Draggable$1.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
35449
35474
  });
35450
35475
  var result = triggerEvent(moveable, "onResizeStart", params);
35451
35476
 
@@ -35630,7 +35655,7 @@ var Resizable = {
35630
35655
  dist: [distWidth, distHeight],
35631
35656
  delta: delta,
35632
35657
  isPinch: !!isPinch,
35633
- drag: Draggable.drag(moveable, setCustomDrag(e, moveable.state, inverseDelta, !!isPinch, false))
35658
+ drag: Draggable$1.drag(moveable, setCustomDrag(e, moveable.state, inverseDelta, !!isPinch, false))
35634
35659
  });
35635
35660
  triggerEvent(moveable, "onResize", params);
35636
35661
  return params;
@@ -36222,7 +36247,7 @@ var Scalable = {
36222
36247
  setRatio: setRatio,
36223
36248
  setFixedDirection: setFixedDirection
36224
36249
  }, fillTransformStartEvent(e)), {
36225
- dragStart: Draggable.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
36250
+ dragStart: Draggable$1.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
36226
36251
  }));
36227
36252
  var result = triggerEvent(moveable, "onScaleStart", params);
36228
36253
 
@@ -39365,7 +39390,7 @@ var OriginDraggable = {
39365
39390
  var datas = e.datas;
39366
39391
  setDragStart(moveable, e);
39367
39392
  var params = fillParams(moveable, e, {
39368
- dragStart: Draggable.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
39393
+ dragStart: Draggable$1.dragStart(moveable, new CustomGesto().dragStart([0, 0], e))
39369
39394
  });
39370
39395
  var result = triggerEvent(moveable, "onDragOriginStart", params);
39371
39396
  datas.startOrigin = moveable.state.transformOrigin;
@@ -39428,7 +39453,7 @@ var OriginDraggable = {
39428
39453
  dist: dist,
39429
39454
  delta: delta,
39430
39455
  transformOrigin: transformOrigin,
39431
- drag: Draggable.drag(moveable, setCustomDrag(e, moveable.state, dragDelta, !!isPinch, false))
39456
+ drag: Draggable$1.drag(moveable, setCustomDrag(e, moveable.state, dragDelta, !!isPinch, false))
39432
39457
  });
39433
39458
  triggerEvent(moveable, "onDragOrigin", params);
39434
39459
  return params;
@@ -41840,13 +41865,13 @@ var edgeDraggable = makeAble("edgeDraggable", {
41840
41865
  return hasClass(target, prefix("direction")) && hasClass(target, prefix("line"));
41841
41866
  },
41842
41867
  dragControlStart: function (moveable, e) {
41843
- return Draggable.dragStart(moveable, getDraggableEvent(e));
41868
+ return Draggable$1.dragStart(moveable, getDraggableEvent(e));
41844
41869
  },
41845
41870
  dragControl: function (moveable, e) {
41846
- return Draggable.drag(moveable, getDraggableEvent(e));
41871
+ return Draggable$1.drag(moveable, getDraggableEvent(e));
41847
41872
  },
41848
41873
  dragControlEnd: function (moveable, e) {
41849
- return Draggable.dragEnd(moveable, getDraggableEvent(e));
41874
+ return Draggable$1.dragEnd(moveable, getDraggableEvent(e));
41850
41875
  },
41851
41876
  dragGroupControlCondition: function (moveable, e) {
41852
41877
  if (!moveable.props.edgeDraggable || !e.inputEvent) {
@@ -41857,16 +41882,16 @@ var edgeDraggable = makeAble("edgeDraggable", {
41857
41882
  return hasClass(target, prefix("direction")) && hasClass(target, prefix("line"));
41858
41883
  },
41859
41884
  dragGroupControlStart: function (moveable, e) {
41860
- return Draggable.dragGroupStart(moveable, getDraggableEvent(e));
41885
+ return Draggable$1.dragGroupStart(moveable, getDraggableEvent(e));
41861
41886
  },
41862
41887
  dragGroupControl: function (moveable, e) {
41863
- return Draggable.dragGroup(moveable, getDraggableEvent(e));
41888
+ return Draggable$1.dragGroup(moveable, getDraggableEvent(e));
41864
41889
  },
41865
41890
  dragGroupControlEnd: function (moveable, e) {
41866
- return Draggable.dragGroupEnd(moveable, getDraggableEvent(e));
41891
+ return Draggable$1.dragGroupEnd(moveable, getDraggableEvent(e));
41867
41892
  },
41868
41893
  unset: function (moveable) {
41869
- return Draggable.unset(moveable);
41894
+ return Draggable$1.unset(moveable);
41870
41895
  }
41871
41896
  });
41872
41897
  /**
@@ -41890,7 +41915,7 @@ var IndividualGroupable = {
41890
41915
  },
41891
41916
  events: {}
41892
41917
  };
41893
- var MOVEABLE_ABLES = [BeforeRenderable, Default, Snappable, Pinchable, Draggable, edgeDraggable, Rotatable, Resizable, Scalable, Warpable, Scrollable, Padding, Origin, OriginDraggable, Clippable, Roundable, Groupable, IndividualGroupable, Clickable, DragArea, Renderable];
41918
+ var MOVEABLE_ABLES = [BeforeRenderable, Default, Snappable, Pinchable, Draggable$1, edgeDraggable, Rotatable$1, Resizable$1, Scalable, Warpable, Scrollable, Padding, Origin, OriginDraggable, Clippable, Roundable, Groupable, IndividualGroupable, Clickable, DragArea, Renderable];
41894
41919
  var MOVEABLE_EVENTS_PROPS_MAP = /*#__PURE__*/MOVEABLE_ABLES.reduce(function (current, able) {
41895
41920
  return __assign$2(__assign$2({}, current), "events" in able ? able.events : {});
41896
41921
  }, {});
@@ -52087,7 +52112,7 @@ class ColorPicker {
52087
52112
  });
52088
52113
  };
52089
52114
  setupTabKeys(pickcolor);
52090
- new Draggable$1({
52115
+ new Draggable$2({
52091
52116
  selector: '#' + this.id + ' .is-draggable'
52092
52117
  });
52093
52118
  let tmp = document.createElement('div');
@@ -52952,7 +52977,7 @@ class GradientPicker {
52952
52977
  });
52953
52978
  };
52954
52979
  setupTabKeys(pickGradient);
52955
- new Draggable$1({
52980
+ new Draggable$2({
52956
52981
  selector: '#' + this.id + ' .is-draggable'
52957
52982
  });
52958
52983
  const colorPicker = new ColorPicker({
@@ -75403,7 +75428,2106 @@ class Similarity {
75403
75428
  }
75404
75429
  }
75405
75430
 
75406
- // import ControlPanel from './controlpanel.js';
75431
+ class Common {
75432
+ constructor(options) {
75433
+ this.selector = options && options.selector || '.is-block';
75434
+ this.doc = options && options.doc || document;
75435
+ this.win = options && options.win || window;
75436
+ this.onDuplicate = options && options.onDuplicate || null;
75437
+ }
75438
+ transform2d(block, x1, y1, x2, y2, x3, y3, x4, y4) {
75439
+ this.matrix3d = new Matrix3D();
75440
+ return this.matrix3d.transform2d(block, x1, y1, x2, y2, x3, y3, x4, y4);
75441
+ }
75442
+ applyPercentage(block) {
75443
+ const rect = this.getRect(block);
75444
+ const container = block.parentNode;
75445
+ let isChildBlock = false;
75446
+ if (block.parentNode.matches(this.selector)) {
75447
+ // child block
75448
+ isChildBlock = true;
75449
+ }
75450
+
75451
+ // const containerRect = container.getBoundingClientRect(); // if container has top/left
75452
+ const containerRect = this.getRect(container); // if container has top/left
75453
+ const left = (rect.left - containerRect.left) / container.offsetWidth * 100;
75454
+ const top = (rect.top - containerRect.top) / container.offsetHeight * 100;
75455
+ let isBlockFixed = block.classList.contains('block-steady');
75456
+ if (isBlockFixed) {
75457
+ let dividerTop = (container.offsetHeight - block.offsetHeight) / (rect.top - containerRect.top);
75458
+ if (isNaN(dividerTop)) {
75459
+ // there is possibility of 0/0
75460
+ dividerTop = 1;
75461
+ }
75462
+ block.style.top = `calc((100% - ${block.offsetHeight}px)/${dividerTop})`;
75463
+ if (isChildBlock) {
75464
+ block.style.top = top + '%';
75465
+ }
75466
+ let dividerLeft = (container.offsetWidth - block.offsetWidth) / (rect.left - containerRect.left);
75467
+ if (isNaN(dividerLeft)) {
75468
+ // there is possibility of 0/0
75469
+ dividerLeft = 1;
75470
+ }
75471
+ // if(rect.left-containerRect.left===0) { // dividerLeft = Infinity
75472
+ // dividerLeft=1;
75473
+ // }
75474
+ block.style.left = `calc((100% - ${block.offsetWidth}px)/${dividerLeft})`;
75475
+ if (isChildBlock) {
75476
+ block.style.left = left + '%';
75477
+ }
75478
+ } else {
75479
+ const width = block.offsetWidth / container.offsetWidth * 100;
75480
+ const height = block.offsetHeight / container.offsetHeight * 100;
75481
+ block.style.width = width + '%';
75482
+ block.style.height = height + '%';
75483
+ block.style.top = top + '%';
75484
+ block.style.left = left + '%';
75485
+ }
75486
+ }
75487
+ applyPixels(block) {
75488
+ const rect = this.getRect(block);
75489
+ const containerRect = this.getRect(block.parentNode); // if container has top/left
75490
+ block.style.left = rect.left - containerRect.left + 'px';
75491
+ block.style.top = rect.top - containerRect.top + 'px';
75492
+ block.style.width = block.offsetWidth + 'px';
75493
+ }
75494
+ updateHeight(block) {
75495
+ if (!block.parentNode) return;
75496
+ let clonedDiv = block.cloneNode(true);
75497
+ clonedDiv.style.position = 'absolute';
75498
+ clonedDiv.style.top = '-9999px';
75499
+ clonedDiv.style.left = '-9999px';
75500
+ block.parentNode.appendChild(clonedDiv);
75501
+ clonedDiv.style.height = '';
75502
+ setTimeout(() => {
75503
+ let clonedDivHeight = clonedDiv.offsetHeight;
75504
+ if (block.offsetHeight <= clonedDivHeight) {
75505
+ block.style.height = clonedDivHeight + 'px';
75506
+ setTimeout(() => {
75507
+ block.parentNode.removeChild(clonedDiv);
75508
+ }, 10);
75509
+ } else {
75510
+ block.parentNode.removeChild(clonedDiv);
75511
+ }
75512
+ }, 10);
75513
+ }
75514
+ duplicate() {
75515
+ const block = this.doc.querySelector(this.selector + '.active');
75516
+ if (!block) return;
75517
+ if (this.onDuplicate) this.onDuplicate(block);
75518
+ }
75519
+ getRect(element) {
75520
+ let group = element.parentNode.closest(this.selector);
75521
+ if (element.closest(this.selector) && group) {
75522
+ let transform = group.style.transform;
75523
+ if (transform.includes('rotate')) {
75524
+ // Create a temporary element with the same dimensions and position
75525
+ const tempGroup = this.doc.createElement('div');
75526
+ tempGroup.style.position = 'absolute';
75527
+ tempGroup.style.width = group.offsetWidth + 'px';
75528
+ tempGroup.style.height = group.offsetHeight + 'px';
75529
+ tempGroup.style.left = group.style.left;
75530
+ tempGroup.style.top = group.style.top;
75531
+ // tempGroup.style.border ='red 1px solid';
75532
+ group.parentNode.appendChild(tempGroup);
75533
+ // const wrapperRect = tempGroup.getBoundingClientRect(); // Get the unrotated position
75534
+
75535
+ // Create a temporary element with the same dimensions and position
75536
+ const tempElement = this.doc.createElement('div');
75537
+ tempElement.style.position = 'absolute';
75538
+ tempElement.style.width = element.offsetWidth + 'px';
75539
+ tempElement.style.height = element.offsetHeight + 'px';
75540
+ tempElement.style.left = element.style.left;
75541
+ tempElement.style.top = element.style.top;
75542
+ // tempElement.style.border ='green 1px solid';
75543
+ tempGroup.appendChild(tempElement);
75544
+ const tempRect = tempElement.getBoundingClientRect(); // Get the unrotated position
75545
+
75546
+ // let top = tempRect.top - wrapperRect.top;
75547
+ // let left = tempRect.left - wrapperRect.left;
75548
+
75549
+ setTimeout(() => {
75550
+ tempElement.parentNode.removeChild(tempElement);
75551
+ tempGroup.parentNode.removeChild(tempGroup);
75552
+ }, 0);
75553
+ return tempRect;
75554
+ }
75555
+ }
75556
+ const rect = element.getBoundingClientRect();
75557
+ const transform = this.win.getComputedStyle(element).getPropertyValue('transform');
75558
+ if (transform !== 'none') {
75559
+ // Create a temporary element with the same dimensions and position
75560
+ const tempElement = this.doc.createElement('div');
75561
+ tempElement.style.position = 'absolute';
75562
+ tempElement.style.width = element.offsetWidth + 'px';
75563
+ tempElement.style.height = element.offsetHeight + 'px';
75564
+ tempElement.style.left = element.style.left;
75565
+ tempElement.style.top = element.style.top;
75566
+ // tempElement.style.border ='green 1px solid';
75567
+ element.parentNode.appendChild(tempElement);
75568
+
75569
+ // Get the unrotated position of the temporary element
75570
+ const tempRect = tempElement.getBoundingClientRect();
75571
+ setTimeout(() => {
75572
+ tempElement.parentNode.removeChild(tempElement);
75573
+ }, 1);
75574
+ return tempRect;
75575
+ } else {
75576
+ return rect;
75577
+ }
75578
+ }
75579
+ getTranslateValues(transformString) {
75580
+ // Regular expression to match the translate values in the transform string
75581
+ const regex = /translate\((-?\d+\.?\d*)px, (-?\d+\.?\d*)px\)/;
75582
+
75583
+ // Use the regular expression to extract x and y values
75584
+ const match = transformString.match(regex);
75585
+
75586
+ // If there's a match, return an object with x and y values
75587
+ if (match) {
75588
+ const [, x, y] = match.map(parseFloat);
75589
+ return {
75590
+ x,
75591
+ y
75592
+ };
75593
+ }
75594
+
75595
+ // If no match, return null or handle it as needed
75596
+ return null;
75597
+ }
75598
+ group(className = 'is-block', groupClassName = 'is-group') {
75599
+ let top = Infinity;
75600
+ let left = Infinity;
75601
+ let bottom = -Infinity;
75602
+ let right = -Infinity;
75603
+
75604
+ // Check group
75605
+ let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active'));
75606
+ if (blocks.length <= 1) return;
75607
+
75608
+ // Check if there is selected block that is part of other group
75609
+ let ok = true;
75610
+ blocks.forEach(block => {
75611
+ if (block.parentNode.matches(this.selector)) {
75612
+ // TODO: detach block
75613
+ alert('One or more blocks belong to another group.');
75614
+ ok = false;
75615
+ }
75616
+ });
75617
+ if (!ok) return;
75618
+ blocks.forEach(block => {
75619
+ let elms = block.querySelectorAll(this.selector);
75620
+ if (elms.length > 0) {
75621
+ elms.forEach(elm => elm.classList.add('active'));
75622
+ this.unGroup(block);
75623
+ }
75624
+ });
75625
+ blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active'));
75626
+ blocks.forEach(block => {
75627
+ if (!block.parentNode.matches(this.selector)) {
75628
+ const rect = this.getRect(block);
75629
+ let blockTop = rect.top;
75630
+ let blockLeft = rect.left;
75631
+ let blockWidth = block.offsetWidth;
75632
+ let blockHeight = block.offsetHeight;
75633
+ top = Math.min(top, blockTop);
75634
+ left = Math.min(left, blockLeft);
75635
+ bottom = Math.max(bottom, blockTop + blockHeight);
75636
+ right = Math.max(right, blockLeft + blockWidth);
75637
+ }
75638
+ });
75639
+ const group = this.doc.createElement('div');
75640
+ group.classList.add(className);
75641
+ group.classList.add(groupClassName);
75642
+ group.classList.add('block-steady');
75643
+ group.style.top = top + 'px';
75644
+ group.style.left = left + 'px';
75645
+ group.style.width = right + 1 - left + 'px';
75646
+ group.style.height = bottom + 1 - top + 'px';
75647
+ const container = blocks[0].parentNode;
75648
+ container.appendChild(group);
75649
+ blocks.forEach(block => {
75650
+ if (!block.parentNode.matches(this.selector)) {
75651
+ this.applyPixels(block);
75652
+ let blockTop = parseFloat(block.style.top) || 0;
75653
+ let blockLeft = parseFloat(block.style.left) || 0;
75654
+
75655
+ // Adjust position relative to the group div
75656
+ block.style.top = blockTop - top + 'px';
75657
+ block.style.left = blockLeft - left + 'px';
75658
+ group.appendChild(block);
75659
+ this.applyPercentage(block);
75660
+ }
75661
+ });
75662
+ this.applyPercentage(group);
75663
+ blocks.forEach(block => {
75664
+ block.classList.remove('active');
75665
+ });
75666
+ group.classList.add('active');
75667
+ blocks = group.querySelectorAll(this.selector);
75668
+ blocks.forEach(target => {
75669
+ // Remove all breakpoints
75670
+ let attributesToRemove = [];
75671
+ const attributes = target.attributes;
75672
+ for (let i = 0; i < attributes.length; i++) {
75673
+ const attributeName = attributes[i].name;
75674
+ if (attributeName.includes('data--y-') || attributeName.includes('data--x-') || attributeName.includes('data--w-') || attributeName.includes('data--h-')) {
75675
+ attributesToRemove.push(attributeName);
75676
+ }
75677
+ }
75678
+ attributesToRemove.forEach(attr => {
75679
+ target.removeAttribute(attr);
75680
+ });
75681
+
75682
+ // Make/save current inline style as default
75683
+ target.removeAttribute('data-breakpoint');
75684
+ target.setAttribute('data--y', target.style.top);
75685
+ target.setAttribute('data--x', target.style.left);
75686
+ target.setAttribute('data--w', target.style.width);
75687
+ target.setAttribute('data--h', target.style.height);
75688
+ });
75689
+ return group;
75690
+ }
75691
+ unGroup(element) {
75692
+ let group;
75693
+ if (element) group = element;else {
75694
+ let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active'));
75695
+ blocks.forEach(block => {
75696
+ if (block.querySelector(this.selector)) {
75697
+ // this block is a group
75698
+ this.unGroup(block);
75699
+ }
75700
+ });
75701
+ return;
75702
+ }
75703
+ if (!group) return;
75704
+ if (!group.querySelector(this.selector)) return;
75705
+ this.applyPixels(group);
75706
+ const rect = this.getRect(group);
75707
+ const top = rect.top;
75708
+ const left = rect.left;
75709
+ const container = group.parentNode;
75710
+ group.querySelectorAll(this.selector).forEach(block => {
75711
+ this.applyPixels(block);
75712
+ let blockTop = parseFloat(block.style.top) || 0;
75713
+ let blockLeft = parseFloat(block.style.left) || 0;
75714
+
75715
+ // Adjust position relative to the group div
75716
+ block.style.top = blockTop + top + 'px';
75717
+ block.style.left = blockLeft + left + 'px';
75718
+ container.appendChild(block);
75719
+
75720
+ // Remove all breakpoints
75721
+ let attributesToRemove = [];
75722
+ const attributes = block.attributes;
75723
+ for (let i = 0; i < attributes.length; i++) {
75724
+ const attributeName = attributes[i].name;
75725
+ if (attributeName.includes('data--y-') || attributeName.includes('data--x-') || attributeName.includes('data--w-') || attributeName.includes('data--h-')) {
75726
+ attributesToRemove.push(attributeName);
75727
+ }
75728
+ }
75729
+ attributesToRemove.forEach(attr => {
75730
+ block.removeAttribute(attr);
75731
+ });
75732
+ // Make/save current inline style as default
75733
+ block.removeAttribute('data-breakpoint');
75734
+ block.setAttribute('data--y', block.style.top);
75735
+ block.setAttribute('data--x', block.style.left);
75736
+ block.setAttribute('data--w', block.style.width);
75737
+ block.setAttribute('data--h', block.style.height);
75738
+ this.applyPercentage(block);
75739
+ });
75740
+ group.parentElement.removeChild(group);
75741
+ }
75742
+ addBreakpoint() {
75743
+ const viewportWidth = this.win.innerWidth;
75744
+ this.doc.body.setAttribute('data-breakpoint', viewportWidth);
75745
+ }
75746
+
75747
+ /*
75748
+ clearBreakpoint(target) {
75749
+ let breakpoint = target.getAttribute('data-breakpoint');
75750
+ if(!breakpoint) return;
75751
+ if(target.hasAttribute('data--y-'+breakpoint)) {
75752
+ target.removeAttribute('data--y-'+breakpoint);
75753
+ let top = target.getAttribute('data--y');
75754
+ if(top) target.style.top = top;
75755
+ }
75756
+ if(target.hasAttribute('data--x-'+breakpoint)) {
75757
+ target.removeAttribute('data--x-'+breakpoint);
75758
+ let left = target.getAttribute('data--x');
75759
+ if(left) target.style.left = left;
75760
+ }
75761
+ if(target.hasAttribute('data--w-'+breakpoint)) {
75762
+ target.removeAttribute('data--w-'+breakpoint);
75763
+ let width = target.getAttribute('data--w');
75764
+ if(width) target.style.width = width;
75765
+ }
75766
+ if(target.hasAttribute('data--h-'+breakpoint)) {
75767
+ target.removeAttribute('data--h-'+breakpoint);
75768
+ let height = target.getAttribute('data--h');
75769
+ if(height) target.style.height = height;
75770
+ }
75771
+ target.removeAttribute('data-breakpoint');
75772
+ // reset
75773
+ let top = target.getAttribute('data--y');
75774
+ if(top)target.style.top = top;
75775
+ let left = target.getAttribute('data--x');
75776
+ if(left) target.style.left = left;
75777
+ let width = target.getAttribute('data--w');
75778
+ if(width) target.style.width = width;
75779
+ let height = target.getAttribute('data--h');
75780
+ if(height) target.style.height = height;
75781
+ }
75782
+ */
75783
+
75784
+ clearBreakpoint(target) {
75785
+ let attributesToRemove = [];
75786
+ const attributes = target.attributes;
75787
+ for (let i = 0; i < attributes.length; i++) {
75788
+ const attributeName = attributes[i].name;
75789
+ if (attributeName.includes('data--y-') || attributeName.includes('data--x-') || attributeName.includes('data--w-') || attributeName.includes('data--h-') || attributeName.includes('data--transform-') || attributeName.includes('data--matrix-')) {
75790
+ attributesToRemove.push(attributeName);
75791
+ }
75792
+ }
75793
+ attributesToRemove.forEach(attr => {
75794
+ target.removeAttribute(attr);
75795
+ });
75796
+ target.removeAttribute('data-breakpoint');
75797
+
75798
+ // reset
75799
+ let top = target.getAttribute('data--y');
75800
+ if (top) target.style.top = top;
75801
+ let left = target.getAttribute('data--x');
75802
+ if (left) target.style.left = left;
75803
+ let width = target.getAttribute('data--w');
75804
+ if (width) target.style.width = width;
75805
+ let height = target.getAttribute('data--h');
75806
+ if (height) target.style.height = height;
75807
+ }
75808
+ clearAllBreakpoints(container) {
75809
+ // REVIEW (Currently not used)
75810
+ const elements = container.querySelectorAll(this.selector);
75811
+ elements.forEach(target => {
75812
+ let attributesToRemove = [];
75813
+ const attributes = target.attributes;
75814
+ for (let i = 0; i < attributes.length; i++) {
75815
+ const attributeName = attributes[i].name;
75816
+ if (attributeName.includes('data--y-') || attributeName.includes('data--x-') || attributeName.includes('data--w-') || attributeName.includes('data--h-') || attributeName.includes('data--transform-') || attributeName.includes('data--matrix-')) {
75817
+ attributesToRemove.push(attributeName);
75818
+ }
75819
+ }
75820
+ attributesToRemove.forEach(attr => {
75821
+ target.removeAttribute(attr);
75822
+ });
75823
+ });
75824
+ elements.forEach(target => {
75825
+ let y = target.getAttribute('data--y');
75826
+ let x = target.getAttribute('data--x');
75827
+ let w = target.getAttribute('data--w');
75828
+ let h = target.getAttribute('data--h');
75829
+ if (y) target.style.top = y;
75830
+ if (x) target.style.left = x;
75831
+ if (w) target.style.width = w;
75832
+ if (h) target.style.height = h;
75833
+ });
75834
+ }
75835
+ getBreakpoints(target) {
75836
+ let breakpoints = [];
75837
+ const attributes = target.attributes;
75838
+ for (let i = 0; i < attributes.length; i++) {
75839
+ const attributeName = attributes[i].name;
75840
+ if (attributeName.includes('data--y-') || attributeName.includes('data--x-') || attributeName.includes('data--w-') || attributeName.includes('data--h-')) {
75841
+ let val = attributeName.replace('data--y-', '');
75842
+ val = val.replace('data--x-', '');
75843
+ val = val.replace('data--w-', '');
75844
+ val = val.replace('data--h-', '');
75845
+ if (!breakpoints.includes(val)) breakpoints.push(val);
75846
+ }
75847
+ }
75848
+ return breakpoints;
75849
+ }
75850
+ getSelectedBlock() {
75851
+ const blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && !elm.classList.contains('cloned'));
75852
+ if (blocks.length === 1) {
75853
+ const block = blocks[0];
75854
+ return block;
75855
+ }
75856
+ return false;
75857
+ }
75858
+ checkOverlap(target) {
75859
+ const targetRect = target.getBoundingClientRect();
75860
+ const blocks = Array.from(target.parentNode.querySelectorAll(this.selector)).filter(elm => !elm.classList.contains('cloned'));
75861
+ let overlappedBlocks = [];
75862
+ for (const block of blocks) {
75863
+ if (block !== target) {
75864
+ const blockRect = block.getBoundingClientRect();
75865
+ if (targetRect.top < blockRect.bottom && targetRect.bottom > blockRect.top && targetRect.left < blockRect.right && targetRect.right > blockRect.left) {
75866
+ overlappedBlocks.push(block);
75867
+ }
75868
+ }
75869
+ }
75870
+ return overlappedBlocks;
75871
+ }
75872
+ }
75873
+ class Matrix3D {
75874
+ adj(m) {
75875
+ return [m[4] * m[8] - m[5] * m[7], m[2] * m[7] - m[1] * m[8], m[1] * m[5] - m[2] * m[4], m[5] * m[6] - m[3] * m[8], m[0] * m[8] - m[2] * m[6], m[2] * m[3] - m[0] * m[5], m[3] * m[7] - m[4] * m[6], m[1] * m[6] - m[0] * m[7], m[0] * m[4] - m[1] * m[3]];
75876
+ }
75877
+ multmm(a, b) {
75878
+ const c = Array(9);
75879
+ for (var i = 0; i != 3; ++i) {
75880
+ for (var j = 0; j != 3; ++j) {
75881
+ var cij = 0;
75882
+ for (var k = 0; k != 3; ++k) {
75883
+ cij += a[3 * i + k] * b[3 * k + j];
75884
+ }
75885
+ c[3 * i + j] = cij;
75886
+ }
75887
+ }
75888
+ return c;
75889
+ }
75890
+ multmv(m, v) {
75891
+ return [m[0] * v[0] + m[1] * v[1] + m[2] * v[2], m[3] * v[0] + m[4] * v[1] + m[5] * v[2], m[6] * v[0] + m[7] * v[1] + m[8] * v[2]];
75892
+ }
75893
+ pdbg(m, v) {
75894
+ const r = this.multmv(m, v);
75895
+ return r + ' (' + r[0] / r[2] + ', ' + r[1] / r[2] + ')';
75896
+ }
75897
+ basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4) {
75898
+ const m = [x1, x2, x3, y1, y2, y3, 1, 1, 1];
75899
+ const v = this.multmv(this.adj(m), [x4, y4, 1]);
75900
+ return this.multmm(m, [v[0], 0, 0, 0, v[1], 0, 0, 0, v[2]]);
75901
+ }
75902
+ general2DProjection(x1s, y1s, x1d, y1d, x2s, y2s, x2d, y2d, x3s, y3s, x3d, y3d, x4s, y4s, x4d, y4d) {
75903
+ const s = this.basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s);
75904
+ const d = this.basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d);
75905
+ return this.multmm(d, this.adj(s));
75906
+ }
75907
+ project(m, x, y) {
75908
+ const v = this.multmv(m, [x, y, 1]);
75909
+ return [v[0] / v[2], v[1] / v[2]];
75910
+ }
75911
+ transform2d(element, x1, y1, x2, y2, x3, y3, x4, y4) {
75912
+ const w = element.offsetWidth,
75913
+ h = element.offsetHeight;
75914
+ let t = this.general2DProjection(0, 0, x1, y1, w, 0, x2, y2, 0, h, x3, y3, w, h, x4, y4);
75915
+ for (let i = 0; i != 9; ++i) t[i] = t[i] / t[8];
75916
+ t = [t[0], t[3], 0, t[6], t[1], t[4], 0, t[7], 0, 0, 1, 0, t[2], t[5], 0, t[8]];
75917
+ t = 'matrix3d(' + t.join(', ') + ')';
75918
+ return t;
75919
+ }
75920
+ }
75921
+ class Ruler {
75922
+ constructor(options) {
75923
+ this.selector = options && options.selector || '.is-block';
75924
+ this.doc = options && options.doc || document;
75925
+ this.win = options && options.win || window;
75926
+ const rulerHTML = `
75927
+ <div class="ruler horizontal-ruler" id="horizontal-ruler-top"></div>
75928
+ <div class="ruler horizontal-ruler" id="horizontal-ruler-bottom"></div>
75929
+ <div class="ruler horizontal-ruler" id="horizontal-ruler-middle"></div>
75930
+ <div class="ruler vertical-ruler" id="vertical-ruler-left"></div>
75931
+ <div class="ruler vertical-ruler" id="vertical-ruler-right"></div>
75932
+ <div class="ruler vertical-ruler" id="vertical-ruler-center"></div>
75933
+ `;
75934
+ if (!this.doc.getElementById('horizontal-ruler-top')) this.doc.body.insertAdjacentHTML('beforeend', rulerHTML);
75935
+ this.horizontalRulerTop = this.doc.getElementById('horizontal-ruler-top');
75936
+ this.horizontalRulerBottom = this.doc.getElementById('horizontal-ruler-bottom');
75937
+ this.horizontalRulerMiddle = this.doc.getElementById('horizontal-ruler-middle');
75938
+ this.verticalRulerLeft = this.doc.getElementById('vertical-ruler-left');
75939
+ this.verticalRulerRight = this.doc.getElementById('vertical-ruler-right');
75940
+ this.verticalRulerCenter = this.doc.getElementById('vertical-ruler-center');
75941
+ this.setup();
75942
+ }
75943
+ setup() {
75944
+ this.elements = this.doc.querySelectorAll(this.selector);
75945
+ }
75946
+ refresh() {
75947
+ this.elements = this.doc.querySelectorAll(this.selector);
75948
+ }
75949
+ destroy() {
75950
+ [this.horizontalRulerTop, this.horizontalRulerBottom, this.verticalRulerLeft, this.verticalRulerRight].forEach(elm => {
75951
+ if (elm.parentNode) elm.parentNode.removeChild(elm);
75952
+ });
75953
+ }
75954
+ hideRulers() {
75955
+ this.horizontalRulerTop.style.top = '-1000px';
75956
+ this.horizontalRulerBottom.style.top = '-1000px';
75957
+ this.horizontalRulerMiddle.style.top = '-1000px';
75958
+ this.verticalRulerLeft.style.left = '-1000px';
75959
+ this.verticalRulerRight.style.left = '-1000px';
75960
+ this.verticalRulerCenter.style.left = '-1000px';
75961
+ this.rulerTop = null;
75962
+ this.rulerLeft = null;
75963
+ this.rulerRight = null;
75964
+ }
75965
+ updateRulers(block) {
75966
+ // if(block.querySelector(this.selector)) return; // group (because updateRules also calls parent group if child block is dragged)
75967
+
75968
+ let transform = block.style.transform;
75969
+ if (transform.includes('rotate') || transform.includes('matrix3d')) return;
75970
+ let parentTransform = block.parentNode.style.transform;
75971
+ if (parentTransform.includes('rotate')) return;
75972
+ this.hideRulers();
75973
+ const rect = block.getBoundingClientRect();
75974
+ const top = rect.top;
75975
+ const bottom = top + block.offsetHeight;
75976
+ const left = rect.left;
75977
+ const right = left + block.offsetWidth;
75978
+ const center = left + block.offsetWidth / 2;
75979
+ const middle = top + block.offsetHeight / 2;
75980
+ this.rulerTop = null;
75981
+ this.rulerLeft = null;
75982
+ this.rulerRight = null;
75983
+ let containerTop = 0;
75984
+ const container = block.parentNode;
75985
+ if (container) {
75986
+ this.elements = container.querySelectorAll(this.selector);
75987
+ containerTop = this.win.scrollY;
75988
+ }
75989
+ this.elements.forEach(element => {
75990
+ if (!this.doc.body.contains(element)) return; // in case element removed (eg. unGroup, block deleted)
75991
+
75992
+ if (block.contains(element)) return; // In case of group moving
75993
+
75994
+ let transform = element.style.transform;
75995
+ let parentTransform = element.parentNode.style.transform;
75996
+ if (!transform.includes('rotate') && !parentTransform.includes('rotate') && !transform.includes('matrix3d') && !parentTransform.includes('matrix3d') && element !== block && !element.classList.contains('cloned')) {
75997
+ const rect = element.getBoundingClientRect();
75998
+ const otherTop = rect.top;
75999
+ const otherBottom = rect.top + element.offsetHeight;
76000
+ const otherLeft = rect.left;
76001
+ const otherRight = rect.left + element.offsetWidth;
76002
+ const otherMiddle = rect.top + element.offsetHeight / 2;
76003
+ const otherCenter = rect.left + element.offsetWidth / 2;
76004
+ if (otherMiddle - 4 <= middle && middle <= otherMiddle + 4) {
76005
+ this.horizontalRulerMiddle.style.top = containerTop + otherTop + (element.offsetHeight - block.offsetHeight) / 2 + block.offsetHeight / 2 + 'px';
76006
+ let val = otherTop + (element.offsetHeight - block.offsetHeight) / 2;
76007
+ if (this.rulerTop === null) this.rulerTop = val;
76008
+ }
76009
+ if (otherCenter - 4 <= center && center <= otherCenter + 4) {
76010
+ this.verticalRulerCenter.style.left = otherLeft + (element.offsetWidth - block.offsetWidth) / 2 + block.offsetWidth / 2 + 'px';
76011
+ this.verticalRulerCenter.style.top = containerTop + 'px'; // adj
76012
+
76013
+ let val = otherLeft + (element.offsetWidth - block.offsetWidth) / 2;
76014
+ if (this.rulerLeft === null) this.rulerLeft = val;
76015
+ }
76016
+
76017
+ // block top
76018
+ if (otherTop - 4 <= top && top <= otherTop + 4) {
76019
+ this.horizontalRulerTop.style.top = containerTop + otherTop + 'px';
76020
+ let val = otherTop;
76021
+ if (this.rulerTop === null) this.rulerTop = val;
76022
+ }
76023
+ if (otherBottom - 4 <= top && top <= otherBottom + 4) {
76024
+ this.horizontalRulerTop.style.top = containerTop + otherBottom + 'px';
76025
+ let val = otherBottom;
76026
+ if (this.rulerTop === null) this.rulerTop = val;
76027
+ }
76028
+
76029
+ // block bottom
76030
+ if (otherTop - 4 <= bottom && bottom <= otherTop + 4) {
76031
+ this.horizontalRulerBottom.style.top = containerTop + otherTop + 'px';
76032
+ let val = otherTop - block.offsetHeight;
76033
+ if (this.rulerTop === null) this.rulerTop = val;
76034
+ }
76035
+ if (otherBottom - 4 <= bottom && bottom <= otherBottom + 4) {
76036
+ this.horizontalRulerBottom.style.top = containerTop + otherBottom + 'px';
76037
+ let val = otherBottom - block.offsetHeight;
76038
+ if (this.rulerTop === null) this.rulerTop = val;
76039
+ }
76040
+
76041
+ // block left
76042
+ if (otherLeft - 4 <= left && left <= otherLeft + 4) {
76043
+ this.verticalRulerLeft.style.left = otherLeft + 'px';
76044
+ this.verticalRulerLeft.style.top = containerTop + 'px'; // adj
76045
+
76046
+ let val = otherLeft;
76047
+ if (this.rulerLeft === null) this.rulerLeft = val;
76048
+ }
76049
+ if (otherRight - 4 <= left && left <= otherRight + 4) {
76050
+ this.verticalRulerLeft.style.left = otherRight + 'px';
76051
+ this.verticalRulerLeft.style.top = containerTop + 'px'; // adj
76052
+
76053
+ let val = otherRight;
76054
+ if (this.rulerLeft === null) this.rulerLeft = val;
76055
+ }
76056
+
76057
+ // block right
76058
+ if (otherLeft - 4 <= right && right <= otherLeft + 4) {
76059
+ this.verticalRulerRight.style.left = otherLeft + 'px';
76060
+ this.verticalRulerRight.style.top = containerTop + 'px'; // adj
76061
+
76062
+ let val = otherLeft;
76063
+ if (this.rulerRight === null) this.rulerRight = val;
76064
+ }
76065
+ if (otherRight - 4 <= right && right <= otherRight + 4) {
76066
+ this.verticalRulerRight.style.left = otherRight + 'px';
76067
+ this.verticalRulerRight.style.top = containerTop + 'px'; // adj
76068
+
76069
+ let val = otherRight;
76070
+ if (this.rulerRight === null) this.rulerRight = val;
76071
+ }
76072
+ }
76073
+ });
76074
+
76075
+ // Edges
76076
+ const conRect = block.parentNode.getBoundingClientRect();
76077
+ const conTop = conRect.top;
76078
+ const conBottom = conRect.top + block.parentNode.offsetHeight;
76079
+ const conLeft = conRect.left;
76080
+ const conRight = conRect.left + block.parentNode.offsetWidth;
76081
+ const conCenter = conRect.left + block.parentNode.offsetWidth / 2;
76082
+ const conMiddle = conRect.top + block.parentNode.offsetHeight / 2;
76083
+ if (conMiddle - 4 <= middle && middle <= conMiddle + 4) {
76084
+ this.horizontalRulerMiddle.style.top = containerTop + conTop + (block.parentNode.offsetHeight - block.offsetHeight) / 2 + block.offsetHeight / 2 + 'px';
76085
+ let val = conTop + (block.parentNode.offsetHeight - block.offsetHeight) / 2;
76086
+ if (this.rulerTop === null) this.rulerTop = val;
76087
+ }
76088
+ if (conCenter - 4 <= center && center <= conCenter + 4) {
76089
+ this.verticalRulerCenter.style.left = conLeft + (block.parentNode.offsetWidth - block.offsetWidth) / 2 + block.offsetWidth / 2 + 'px';
76090
+ this.verticalRulerCenter.style.top = containerTop + 'px';
76091
+ let val = conLeft + (block.parentNode.offsetWidth - block.offsetWidth) / 2;
76092
+ if (this.rulerLeft === null) this.rulerLeft = val;
76093
+ }
76094
+
76095
+ // block top
76096
+ if (conTop - 4 <= top && top <= conTop + 4) {
76097
+ this.horizontalRulerTop.style.top = containerTop + conTop + 'px';
76098
+ let val = conTop;
76099
+ if (this.rulerTop === null) this.rulerTop = val;
76100
+ }
76101
+ // block bottom
76102
+ if (conBottom - 4 <= bottom && bottom <= conBottom + 4) {
76103
+ this.horizontalRulerBottom.style.top = containerTop + conBottom - 3 + 'px'; // -3 is an adjustment to make the line visible
76104
+
76105
+ let val = conBottom - block.offsetHeight;
76106
+ if (this.rulerTop === null) this.rulerTop = val;
76107
+ }
76108
+ // block left
76109
+ if (conLeft - 4 <= left && left <= conLeft + 4) {
76110
+ this.verticalRulerLeft.style.left = conLeft + 2 + 'px'; // +3 is an adjustment
76111
+ this.verticalRulerLeft.style.top = containerTop + 'px';
76112
+ let val = conLeft;
76113
+ if (this.rulerLeft === null) this.rulerLeft = val;
76114
+ }
76115
+ // block right
76116
+ if (conRight - 4 <= right && right <= conRight + 4) {
76117
+ this.verticalRulerRight.style.left = conRight - 2 + 'px'; // -2 is an adjustment
76118
+ this.verticalRulerRight.style.top = containerTop + 'px';
76119
+ let val = conRight;
76120
+ if (this.rulerRight === null) this.rulerRight = val;
76121
+ }
76122
+ }
76123
+ }
76124
+ class Rotatable {
76125
+ constructor(options) {
76126
+ this.selector = options && options.selector || '.is-block';
76127
+ this.doc = options && options.doc || document;
76128
+ this.win = options && options.win || window;
76129
+ this.onBeforeChange = options && options.onBeforeChange || null;
76130
+ this.onChange = options && options.onChange || null;
76131
+ this.disableOnMobile = options && options.disableOnMobile || 0;
76132
+ this.centerX = 0;
76133
+ this.centerY = 0;
76134
+ this.initialRotation = 0;
76135
+ this.handleRotateStart = this.handleRotateStart.bind(this);
76136
+ this.handleDblClick = this.handleDblClick.bind(this);
76137
+ this.handleRotateMove = this.handleRotateMove.bind(this);
76138
+ this.handleRotateEnd = this.handleRotateEnd.bind(this);
76139
+ this.setup();
76140
+ this.common = new Common({
76141
+ selector: this.selector
76142
+ });
76143
+ }
76144
+ setup() {
76145
+ this.elements = this.doc.querySelectorAll(this.selector);
76146
+ this.elements.forEach(element => {
76147
+ element.querySelectorAll('.rotate-handle').forEach(elm => elm.parentNode.removeChild(elm));
76148
+ const handleHTML = '<div class="rotate-handle" contentEditable="false"><div></div></div>';
76149
+ element.insertAdjacentHTML('afterbegin', handleHTML);
76150
+ const handles = Array.from(element.querySelectorAll('.rotate-handle')).filter(handle => handle.parentElement === element);
76151
+ handles.forEach(handle => {
76152
+ handle.addEventListener('mousedown', this.handleRotateStart);
76153
+ handle.addEventListener('touchstart', this.handleRotateStart, {
76154
+ passive: false
76155
+ });
76156
+ handle.addEventListener('dblclick', this.handleDblClick);
76157
+ });
76158
+ });
76159
+ }
76160
+ refresh() {
76161
+ this.elements = this.doc.querySelectorAll(this.selector);
76162
+ this.elements.forEach(element => {
76163
+ let handles = Array.from(element.querySelectorAll('.rotate-handle')).filter(handle => handle.parentElement === element); // only one handle
76164
+ handles.forEach(handle => {
76165
+ handle.removeEventListener('mousedown', this.handleRotateStart);
76166
+ handle.removeEventListener('touchstart', this.handleRotateStart);
76167
+ handle.removeEventListener('dblclick', this.handleDblClick);
76168
+ handle.parentNode.removeChild(handle);
76169
+ });
76170
+ const handleHTML = '<div class="rotate-handle" contentEditable="false"><div></div></div>';
76171
+ element.insertAdjacentHTML('afterbegin', handleHTML);
76172
+ handles = Array.from(element.querySelectorAll('.rotate-handle')).filter(handle => handle.parentElement === element); // only one handle
76173
+ handles.forEach(handle => {
76174
+ handle.addEventListener('mousedown', this.handleRotateStart);
76175
+ handle.addEventListener('touchstart', this.handleRotateStart, {
76176
+ passive: false
76177
+ });
76178
+ handle.addEventListener('dblclick', this.handleDblClick);
76179
+ });
76180
+ });
76181
+ }
76182
+ destroy(block) {
76183
+ if (block) {
76184
+ this.elements = this.doc.querySelectorAll(this.selector);
76185
+ this.elements.forEach(element => {
76186
+ if (element === block) {
76187
+ const handles = Array.from(element.querySelectorAll('.rotate-handle')).filter(handle => handle.parentElement === element);
76188
+ handles.forEach(handle => {
76189
+ handle.removeEventListener('mousedown', this.handleRotateStart);
76190
+ handle.removeEventListener('touchstart', this.handleRotateStart);
76191
+ handle.removeEventListener('dblclick', this.handleDblClick);
76192
+ handle.parentNode.removeChild(handle);
76193
+ });
76194
+ }
76195
+ });
76196
+ } else {
76197
+ this.elements = this.doc.querySelectorAll(this.selector);
76198
+ this.elements.forEach(element => {
76199
+ const handles = Array.from(element.querySelectorAll('.rotate-handle')).filter(handle => handle.parentElement === element);
76200
+ handles.forEach(handle => {
76201
+ handle.removeEventListener('mousedown', this.handleRotateStart);
76202
+ handle.removeEventListener('touchstart', this.handleRotateStart);
76203
+ handle.removeEventListener('dblclick', this.handleDblClick);
76204
+ handle.parentNode.removeChild(handle);
76205
+ });
76206
+ });
76207
+ }
76208
+ }
76209
+ handleDblClick(event) {
76210
+ if (this.onBeforeChange) this.onBeforeChange();
76211
+ event.preventDefault();
76212
+ event.stopImmediatePropagation();
76213
+ const block = event.target.parentNode;
76214
+ const transform = block.style.transform;
76215
+ if (transform.includes('rotate')) block.style.transform = '';
76216
+ this.clonedTarget = this.doc.querySelector(this.selector + '.cloned');
76217
+ if (this.clonedTarget) this.clonedTarget.style.transform = '';
76218
+ if (this.onChange) this.onChange();
76219
+ }
76220
+ handleRotateStart(event) {
76221
+ const viewportWidth = this.win.innerWidth;
76222
+ if (viewportWidth <= this.disableOnMobile) return;
76223
+ if (event.type === 'touchstart' && event.touches.length !== 1) {
76224
+ return; // Do nothing if more than one touch point is detected
76225
+ }
76226
+
76227
+ if (event.type === 'mousedown' && event.button !== 0) {
76228
+ return; // Do nothing if the right mouse button is clicked
76229
+ }
76230
+
76231
+ event.preventDefault();
76232
+ this.target = event.target.parentNode; // block
76233
+
76234
+ this.clonedTarget = this.doc.querySelector(this.selector + '.cloned');
76235
+ this.dragStartTimeout = setTimeout(() => {
76236
+ // Set a timeout before starting the drag (to differentiate from click)
76237
+
76238
+ if (event.type === 'mousedown') {
76239
+ this.startRotation(event.clientX, event.clientY);
76240
+ } else if (event.type === 'touchstart') {
76241
+ const touch = event.touches[0];
76242
+ this.startRotation(touch.clientX, touch.clientY);
76243
+ }
76244
+ this.doc.addEventListener('mousemove', this.handleRotateMove);
76245
+ this.doc.addEventListener('touchmove', this.handleRotateMove);
76246
+ }, 120);
76247
+ this.doc.addEventListener('mouseup', this.handleRotateEnd);
76248
+ this.doc.addEventListener('touchend', this.handleRotateEnd);
76249
+ }
76250
+ handleRotateMove() {
76251
+ let clientX, clientY;
76252
+ if (event.type === 'mousemove') {
76253
+ clientX = event.clientX;
76254
+ clientY = event.clientY;
76255
+ } else if (event.type === 'touchmove') {
76256
+ const touch = event.touches[0];
76257
+ clientX = touch.clientX;
76258
+ clientY = touch.clientY;
76259
+ }
76260
+ this.updateRotation(clientX, clientY);
76261
+ }
76262
+ handleRotateEnd() {
76263
+ clearTimeout(this.dragStartTimeout);
76264
+ this.doc.removeEventListener('mousemove', this.handleRotateMove);
76265
+ this.doc.removeEventListener('touchmove', this.handleRotateMove);
76266
+ this.doc.removeEventListener('mouseup', this.handleRotateEnd);
76267
+ this.doc.removeEventListener('touchend', this.handleRotateEnd);
76268
+ if (this.onChange) this.onChange();
76269
+ }
76270
+ getCurrentRotation(transform) {
76271
+ const match = transform.match(/rotate\(([-+]?\d*\.?\d*)deg\)/);
76272
+ return match ? parseFloat(match[1]) : 0;
76273
+ }
76274
+ startRotation() {
76275
+ if (this.onBeforeChange) this.onBeforeChange();
76276
+ const target = this.target;
76277
+ const rect = target.getBoundingClientRect();
76278
+ this.centerX = rect.left + rect.width / 2;
76279
+ this.centerY = rect.top + rect.height / 2;
76280
+
76281
+ // Store the initial rotation
76282
+ if (target.parentNode.matches(this.selector)) {
76283
+ const currentTransform = target.style.transform;
76284
+ let currentRotation = this.getCurrentRotation(currentTransform);
76285
+ this.initialRotation = currentRotation;
76286
+ const parentTransform = target.parentNode.style.transform;
76287
+ let parentRotation = this.getCurrentRotation(parentTransform);
76288
+ if (parentRotation) {
76289
+ this.initialRotation = parentRotation;
76290
+ }
76291
+ } else {
76292
+ this.initialRotation = 0; // If no child blocks
76293
+ }
76294
+ }
76295
+
76296
+ updateRotation(x, y) {
76297
+ const target = this.target;
76298
+ const angle = Math.atan2(y - this.centerY, x - this.centerX);
76299
+ const degrees = angle * (180 / Math.PI);
76300
+ const initialRotation = this.initialRotation || 0;
76301
+ target.style.transform = `rotate(${degrees - initialRotation}deg)`;
76302
+ if (this.clonedTarget) this.clonedTarget.style.transform = `rotate(${degrees - initialRotation}deg)`;
76303
+ }
76304
+ }
76305
+ class Resizable {
76306
+ constructor(options) {
76307
+ this.selector = options && options.selector || '.is-block';
76308
+ this.doc = options && options.doc || document;
76309
+ this.win = options && options.win || window;
76310
+ this.onBeforeChange = options && options.onBeforeChange || null;
76311
+ this.onChange = options && options.onChange || null;
76312
+ this.disableOnMobile = options && options.disableOnMobile || 0;
76313
+ this.isResizing = false;
76314
+ this.resizeHandle = null;
76315
+ this.startX = 0;
76316
+ this.startY = 0;
76317
+ this.elements = this.doc.querySelectorAll(this.selector);
76318
+ this.startResizing = this.startResizing.bind(this);
76319
+ this.handleResizeMove = this.handleResizeMove.bind(this);
76320
+ this.handleResizeEnd = this.handleResizeEnd.bind(this);
76321
+ this.setup();
76322
+ this.ruler = new Ruler({
76323
+ selector: this.selector,
76324
+ doc: this.doc,
76325
+ win: this.win
76326
+ });
76327
+ this.common = new Common({
76328
+ selector: this.selector,
76329
+ doc: this.doc,
76330
+ win: this.win
76331
+ });
76332
+ }
76333
+ setup() {
76334
+ this.elements.forEach(element => {
76335
+ element.querySelectorAll('.handle').forEach(elm => elm.parentNode.removeChild(elm));
76336
+ const handleHTML = `
76337
+ <div class="handle top-left"></div>
76338
+ <div class="handle top-right"></div>
76339
+ <div class="handle bottom-left"></div>
76340
+ <div class="handle bottom-right"></div>
76341
+
76342
+ <div class="handle top"></div>
76343
+ <div class="handle bottom"></div>
76344
+ <div class="handle left"></div>
76345
+ <div class="handle right"></div>`;
76346
+ element.insertAdjacentHTML('afterbegin', handleHTML);
76347
+ const handles = Array.from(element.querySelectorAll('.handle')).filter(handle => handle.parentElement === element);
76348
+ handles.forEach(handle => {
76349
+ handle.addEventListener('mousedown', this.startResizing);
76350
+ handle.addEventListener('touchstart', this.startResizing, {
76351
+ passive: false
76352
+ });
76353
+ });
76354
+ });
76355
+ }
76356
+ refresh() {
76357
+ this.elements = this.doc.querySelectorAll(this.selector);
76358
+ this.elements.forEach(element => {
76359
+ let handles = Array.from(element.querySelectorAll('.handle')).filter(handle => handle.parentElement === element);
76360
+ handles.forEach(handle => {
76361
+ handle.removeEventListener('mousedown', this.startResizing);
76362
+ handle.removeEventListener('touchstart', this.startResizing);
76363
+ handle.parentNode.removeChild(handle);
76364
+ });
76365
+ const handleHTML = `
76366
+ <div class="handle top-left"></div>
76367
+ <div class="handle top-right"></div>
76368
+ <div class="handle bottom-left"></div>
76369
+ <div class="handle bottom-right"></div>
76370
+
76371
+ <div class="handle top"></div>
76372
+ <div class="handle bottom"></div>
76373
+ <div class="handle left"></div>
76374
+ <div class="handle right"></div>`;
76375
+ element.insertAdjacentHTML('afterbegin', handleHTML);
76376
+ handles = Array.from(element.querySelectorAll('.handle')).filter(handle => handle.parentElement === element);
76377
+ handles.forEach(handle => {
76378
+ handle.addEventListener('mousedown', this.startResizing);
76379
+ handle.addEventListener('touchstart', this.startResizing, {
76380
+ passive: false
76381
+ });
76382
+ });
76383
+ });
76384
+ this.ruler.refresh();
76385
+ }
76386
+ destroy(block) {
76387
+ if (block) {
76388
+ const handles = Array.from(block.querySelectorAll('.handle')).filter(handle => handle.parentElement === block);
76389
+ handles.forEach(handle => {
76390
+ handle.removeEventListener('mousedown', this.startResizing);
76391
+ handle.removeEventListener('touchstart', this.startResizing);
76392
+ handle.parentNode.removeChild(handle);
76393
+ });
76394
+ } else {
76395
+ const handles = this.doc.querySelectorAll('.handle');
76396
+ handles.forEach(handle => {
76397
+ handle.removeEventListener('mousedown', this.startResizing);
76398
+ handle.removeEventListener('touchstart', this.startResizing);
76399
+ handle.parentNode.removeChild(handle);
76400
+ });
76401
+ this.ruler.destroy();
76402
+ }
76403
+ }
76404
+ startResizing(event) {
76405
+ const viewportWidth = this.win.innerWidth;
76406
+ if (viewportWidth <= this.disableOnMobile) return;
76407
+ if (event.type === 'touchstart' && event.touches.length !== 1) {
76408
+ return; // Do nothing if more than one touch point is detected
76409
+ }
76410
+
76411
+ if (event.type === 'mousedown' && event.button !== 0) {
76412
+ return; // Do nothing if the right mouse button is clicked
76413
+ }
76414
+
76415
+ this.target = event.target.parentNode; // block
76416
+
76417
+ this.clonedTarget = this.doc.querySelector(this.selector + '.cloned');
76418
+ event.preventDefault();
76419
+ event.stopPropagation();
76420
+ this.dragStartTimeout = setTimeout(() => {
76421
+ // Set a timeout before starting the drag (to differentiate from click)
76422
+
76423
+ if (this.onBeforeChange) this.onBeforeChange();
76424
+ const handle = event.target.closest('.handle');
76425
+ this.isResizing = true;
76426
+ this.resizeHandle = handle.classList[1];
76427
+ if (event.type === 'mousedown') {
76428
+ this.startX = event.clientX;
76429
+ this.startY = event.clientY;
76430
+ } else if (event.type === 'touchstart') {
76431
+ const touch = event.touches[0];
76432
+ this.startX = touch.clientX;
76433
+ this.startY = touch.clientY;
76434
+ }
76435
+
76436
+ //Initial
76437
+ this.initialWidth = parseFloat(getComputedStyle(this.target).width);
76438
+ this.initialHeight = parseFloat(getComputedStyle(this.target).height);
76439
+ const containerRect = this.common.getRect(this.target.parentNode);
76440
+ const rect = this.common.getRect(this.target);
76441
+ this.initialLeft = rect.left - containerRect.left;
76442
+ this.initialTop = rect.top - containerRect.top;
76443
+ this.common.applyPixels(this.target);
76444
+ }, 120);
76445
+ this.doc.addEventListener('mousemove', this.handleResizeMove);
76446
+ this.doc.addEventListener('mouseup', this.handleResizeEnd);
76447
+ this.doc.addEventListener('touchmove', this.handleResizeMove);
76448
+ this.doc.addEventListener('touchend', this.handleResizeEnd);
76449
+ }
76450
+ handleResizeMove(event) {
76451
+ if (this.isResizing) {
76452
+ let deltaX, deltaY;
76453
+ if (event.type === 'mousemove') {
76454
+ deltaX = event.clientX - this.startX;
76455
+ deltaY = event.clientY - this.startY;
76456
+ } else if (event.type === 'touchmove') {
76457
+ const touch = event.touches[0];
76458
+ deltaX = touch.clientX - this.startX;
76459
+ deltaY = touch.clientY - this.startY;
76460
+ }
76461
+ this.target.style.minWidth = ''; // to ensure no resize limit
76462
+ if (this.clonedTarget) this.clonedTarget.style.minWidth = '';
76463
+ switch (this.resizeHandle) {
76464
+ case 'top-left':
76465
+ this.resizeTopLeft(deltaX, deltaY);
76466
+ break;
76467
+ case 'top-right':
76468
+ this.resizeTopRight(deltaX, deltaY);
76469
+ break;
76470
+ case 'bottom-left':
76471
+ this.resizeBottomLeft(deltaX, deltaY);
76472
+ break;
76473
+ case 'bottom-right':
76474
+ this.resizeBottomRight(deltaX, deltaY);
76475
+ break;
76476
+ case 'top':
76477
+ this.resizeTop(deltaY);
76478
+ break;
76479
+ case 'left':
76480
+ this.resizeLeft(deltaX);
76481
+ break;
76482
+ case 'bottom':
76483
+ this.resizeBottom(deltaY);
76484
+ break;
76485
+ case 'right':
76486
+ this.resizeRight(deltaX);
76487
+ break;
76488
+ }
76489
+ this.ruler.updateRulers(this.target);
76490
+ }
76491
+ }
76492
+ handleResizeEnd() {
76493
+ clearTimeout(this.dragStartTimeout);
76494
+ this.stopResizing();
76495
+ this.doc.removeEventListener('mousemove', this.handleResizeMove);
76496
+ this.doc.removeEventListener('mouseup', this.handleResizeEnd);
76497
+ this.doc.removeEventListener('touchmove', this.handleResizeMove);
76498
+ this.doc.removeEventListener('touchend', this.handleResizeEnd);
76499
+ this.ruler.hideRulers();
76500
+ }
76501
+ resizeTopLeft(deltaX, deltaY) {
76502
+ this.target.style.left = this.initialLeft + deltaX + 'px';
76503
+ this.target.style.top = this.initialTop + deltaY + 'px';
76504
+ this.target.style.width = this.initialWidth - deltaX + 'px';
76505
+ this.target.style.height = this.initialHeight - deltaY + 'px';
76506
+ if (this.clonedTarget) {
76507
+ this.clonedTarget.style.left = this.initialLeft + deltaX + 'px';
76508
+ this.clonedTarget.style.top = this.initialTop + deltaY + 'px';
76509
+ this.clonedTarget.style.width = this.initialWidth - deltaX + 'px';
76510
+ this.clonedTarget.style.height = this.initialHeight - deltaY + 'px';
76511
+ }
76512
+ }
76513
+ resizeTopRight(deltaX, deltaY) {
76514
+ this.target.style.width = this.initialWidth + deltaX + 'px';
76515
+ this.target.style.top = this.initialTop + deltaY + 'px';
76516
+ this.target.style.height = this.initialHeight - deltaY + 'px';
76517
+ if (this.clonedTarget) {
76518
+ this.clonedTarget.style.width = this.initialWidth + deltaX + 'px';
76519
+ this.clonedTarget.style.top = this.initialTop + deltaY + 'px';
76520
+ this.clonedTarget.style.height = this.initialHeight - deltaY + 'px';
76521
+ }
76522
+ }
76523
+ resizeBottomLeft(deltaX, deltaY) {
76524
+ this.target.style.width = this.initialWidth - deltaX + 'px';
76525
+ this.target.style.height = this.initialHeight + deltaY + 'px';
76526
+ this.target.style.left = this.initialLeft + deltaX + 'px';
76527
+ if (this.clonedTarget) {
76528
+ this.clonedTarget.style.width = this.initialWidth - deltaX + 'px';
76529
+ this.clonedTarget.style.height = this.initialHeight + deltaY + 'px';
76530
+ this.clonedTarget.style.left = this.initialLeft + deltaX + 'px';
76531
+ }
76532
+ }
76533
+ resizeBottomRight(deltaX, deltaY) {
76534
+ this.target.style.width = this.initialWidth + deltaX + 'px';
76535
+ this.target.style.height = this.initialHeight + deltaY + 'px';
76536
+ if (this.clonedTarget) {
76537
+ this.clonedTarget.style.width = this.initialWidth + deltaX + 'px';
76538
+ this.clonedTarget.style.height = this.initialHeight + deltaY + 'px';
76539
+ }
76540
+ }
76541
+ resizeTop(deltaY) {
76542
+ this.target.style.top = this.initialTop + deltaY + 'px';
76543
+ this.target.style.height = this.initialHeight - deltaY + 'px';
76544
+ if (this.clonedTarget) {
76545
+ this.clonedTarget.style.top = this.initialTop + deltaY + 'px';
76546
+ this.clonedTarget.style.height = this.initialHeight - deltaY + 'px';
76547
+ }
76548
+ }
76549
+ resizeBottom(deltaY) {
76550
+ this.target.style.height = this.initialHeight + deltaY + 'px';
76551
+ if (this.clonedTarget) {
76552
+ this.clonedTarget.style.height = this.initialHeight + deltaY + 'px';
76553
+ }
76554
+ }
76555
+ resizeLeft(deltaX) {
76556
+ this.target.style.width = this.initialWidth - deltaX + 'px';
76557
+ this.target.style.left = this.initialLeft + deltaX + 'px';
76558
+ if (this.clonedTarget) {
76559
+ this.clonedTarget.style.width = this.initialWidth - deltaX + 'px';
76560
+ this.clonedTarget.style.left = this.initialLeft + deltaX + 'px';
76561
+ }
76562
+ }
76563
+ resizeRight(deltaX) {
76564
+ this.target.style.width = this.initialWidth + deltaX + 'px';
76565
+ if (this.clonedTarget) {
76566
+ this.clonedTarget.style.width = this.initialWidth + deltaX + 'px';
76567
+ }
76568
+ }
76569
+ stopResizing() {
76570
+ if (this.isResizing) {
76571
+ if (this.target) {
76572
+ this.updateBlockStyle(this.target);
76573
+ if (this.clonedTarget) this.updateBlockStyle(this.clonedTarget);
76574
+ }
76575
+ this.isResizing = false;
76576
+ }
76577
+ }
76578
+ updateBlockStyle(target) {
76579
+ if (target.querySelector(this.selector)) ; else {
76580
+ // this.common.updateHeight(target);
76581
+ if (target.classList.contains('height-auto')) target.style.height = '';
76582
+ }
76583
+
76584
+ // Replace with ruler's alignment
76585
+ if (this.ruler.rulerTop !== null) {
76586
+ target.style.top = this.ruler.rulerTop + 'px';
76587
+ // if container has top/left
76588
+ if (target.parentNode.matches(this.selector)) {
76589
+ const containerRect = target.parentNode.getBoundingClientRect();
76590
+ target.style.top = this.ruler.rulerTop - containerRect.top + 'px';
76591
+ }
76592
+ }
76593
+
76594
+ // If resizing by dragging the right corners (top or bottom)
76595
+ if (this.resizeHandle === 'top-right' || this.resizeHandle === 'bottom-right' || this.resizeHandle === 'right') {
76596
+ // Check if vertical right ruler visible (has value)
76597
+ if (this.ruler.rulerRight !== null) {
76598
+ // If so, keep the left position
76599
+ // target.style.left = newLeft + 'px';
76600
+
76601
+ // And update the width to align with vertical right ruler
76602
+ const rect = target.getBoundingClientRect();
76603
+ target.style.width = this.ruler.rulerRight - rect.left + 'px';
76604
+ }
76605
+ } else if (this.resizeHandle === 'top-left' || this.resizeHandle === 'bottom-left' || this.resizeHandle === 'left') {
76606
+ if (this.ruler.rulerLeft !== null) {
76607
+ const currentRight = this.initialLeft + this.initialWidth;
76608
+
76609
+ // if container has top/left
76610
+ const containerRect = target.parentNode.getBoundingClientRect();
76611
+ this.ruler.rulerLeft = this.ruler.rulerLeft - containerRect.left;
76612
+
76613
+ // And update the width to align with vertical left ruler
76614
+ target.style.left = this.ruler.rulerLeft + 'px';
76615
+ let newWidth = currentRight - this.ruler.rulerLeft;
76616
+ target.style.width = newWidth + 'px';
76617
+ }
76618
+ }
76619
+
76620
+ // Convert px to %
76621
+ this.common.applyPercentage(target);
76622
+ setTimeout(() => {
76623
+ const breakpoint = this.doc.body.getAttribute('data-breakpoint');
76624
+
76625
+ // const screenWidth = window.innerWidth;
76626
+ // let defaultPoint;
76627
+ // if(screenWidth<=1920) {
76628
+ // defaultPoint = 1366;
76629
+ // } else {
76630
+ // defaultPoint = 1900;
76631
+ // }
76632
+
76633
+ if (breakpoint && breakpoint < 1920) {
76634
+ target.setAttribute('data--y-' + breakpoint, target.style.top);
76635
+ target.setAttribute('data--x-' + breakpoint, target.style.left);
76636
+ target.setAttribute('data--w-' + breakpoint, target.style.width);
76637
+ target.setAttribute('data--h-' + breakpoint, target.style.height);
76638
+ } else {
76639
+ target.setAttribute('data--y', target.style.top);
76640
+ target.setAttribute('data--x', target.style.left);
76641
+ target.setAttribute('data--w', target.style.width);
76642
+ target.setAttribute('data--h', target.style.height);
76643
+ }
76644
+ target.removeAttribute('data-prev'); // reset
76645
+ target.removeAttribute('data-fluid');
76646
+ }, 30); // delay needed since we use updateHeight() previously that has 20ms process
76647
+
76648
+ if (this.onChange) this.onChange();
76649
+ }
76650
+ }
76651
+ class Draggable {
76652
+ constructor(options) {
76653
+ this.selector = options && options.selector || '.is-block';
76654
+ this.doc = options && options.doc || document;
76655
+ this.win = options && options.win || window;
76656
+ this.onDelete = options && options.onDelete || null;
76657
+ this.onBeforeChange = options && options.onBeforeChange || null;
76658
+ this.onChange = options && options.onChange || null;
76659
+ this.onSelectStart = options && options.onSelectStart || null;
76660
+ this.onSelectClear = options && options.onSelectClear || null;
76661
+ this.disableOnMobile = options && options.disableOnMobile || 0;
76662
+ this.onMultipleSelect = options && options.onMultipleSelect || null;
76663
+ this.onSelectBlock = options && options.onSelectBlock || null;
76664
+ this.isDragging = false;
76665
+ this.startX = 0;
76666
+ this.startY = 0;
76667
+ this.handleDragStart = this.handleDragStart.bind(this);
76668
+ this.handleDragMove = this.handleDragMove.bind(this);
76669
+ this.handleDragEnd = this.handleDragEnd.bind(this);
76670
+ this.handleSelect = this.handleSelect.bind(this);
76671
+ this.handleKeyDown = this.handleKeyDown.bind(this);
76672
+ this.setup();
76673
+ this.ruler = new Ruler({
76674
+ selector: this.selector,
76675
+ doc: this.doc,
76676
+ win: this.win
76677
+ });
76678
+ this.common = new Common({
76679
+ selector: this.selector,
76680
+ doc: this.doc,
76681
+ win: this.win
76682
+ });
76683
+ }
76684
+ setup() {
76685
+ this.elements = this.doc.querySelectorAll(this.selector);
76686
+ this.elements.forEach(element => {
76687
+ element.addEventListener('mousedown', this.handleDragStart);
76688
+ element.addEventListener('touchstart', this.handleDragStart, {
76689
+ passive: false
76690
+ });
76691
+ });
76692
+ this.doc.addEventListener('mousedown', this.handleSelect);
76693
+ this.doc.addEventListener('touchstart', this.handleSelect, {
76694
+ passive: false
76695
+ });
76696
+ this.doc.addEventListener('keydown', this.handleKeyDown);
76697
+ }
76698
+ refresh() {
76699
+ this.elements = this.doc.querySelectorAll(this.selector);
76700
+ this.elements.forEach(element => {
76701
+ element.removeEventListener('mousedown', this.handleDragStart);
76702
+ element.removeEventListener('touchstart', this.handleDragStart);
76703
+ });
76704
+ this.elements.forEach(element => {
76705
+ element.addEventListener('mousedown', this.handleDragStart);
76706
+ element.addEventListener('touchstart', this.handleDragStart, {
76707
+ passive: false
76708
+ });
76709
+ });
76710
+ this.ruler.refresh();
76711
+ }
76712
+ destroy() {
76713
+ this.ruler.destroy();
76714
+ this.elements.forEach(element => {
76715
+ element.removeEventListener('mousedown', this.handleDragStart);
76716
+ element.removeEventListener('touchstart', this.handleDragStart);
76717
+ });
76718
+ this.doc.removeEventListener('mousedown', this.handleSelect);
76719
+ this.doc.removeEventListener('touchstart', this.handleSelect);
76720
+ this.doc.removeEventListener('keydown', this.handleKeyDown);
76721
+ const blocks = this.doc.querySelectorAll(this.selector);
76722
+ blocks.forEach(elm => elm.classList.remove('active'));
76723
+ }
76724
+ disableDrag(block) {
76725
+ this.elements.forEach(element => {
76726
+ if (block === element) {
76727
+ element.removeEventListener('mousedown', this.handleDragStart);
76728
+ element.removeEventListener('touchstart', this.handleDragStart);
76729
+ }
76730
+ });
76731
+ }
76732
+ enableDrag(block) {
76733
+ this.elements.forEach(element => {
76734
+ if (block === element) {
76735
+ element.addEventListener('mousedown', this.handleDragStart);
76736
+ element.addEventListener('touchstart', this.handleDragStart, {
76737
+ passive: false
76738
+ });
76739
+ }
76740
+ });
76741
+ }
76742
+ handleDragStart(event) {
76743
+ const viewportWidth = this.win.innerWidth;
76744
+ if (viewportWidth <= this.disableOnMobile) return;
76745
+ if (event.target.classList.contains('rotate-handle')) return;
76746
+ if (event.type === 'touchstart' && event.touches.length !== 1) {
76747
+ return; // Do nothing if more than one touch point is detected
76748
+ }
76749
+
76750
+ if (event.type === 'mousedown' && event.button !== 0) {
76751
+ return; // Do nothing if the right mouse button is clicked
76752
+ }
76753
+
76754
+ const currentTarget = event.currentTarget;
76755
+ const clickedBlock = event.target.closest(this.selector);
76756
+ if (currentTarget !== clickedBlock) {
76757
+ // Triggered Group click by child block click
76758
+ return; // Return here, so that when block events are working, group events won't
76759
+ }
76760
+
76761
+ this.clickedBlock = clickedBlock;
76762
+ this.dragStartTimeout = setTimeout(() => {
76763
+ // Set a timeout before starting the drag (to differentiate from click)
76764
+
76765
+ if (event.type === 'mousedown') {
76766
+ this.startDragging(event.clientX, event.clientY, currentTarget);
76767
+ } else if (event.type === 'touchstart') {
76768
+ const touch = event.touches[0];
76769
+ this.startDragging(touch.clientX, touch.clientY, currentTarget);
76770
+ }
76771
+ this.doc.addEventListener('mousemove', this.handleDragMove);
76772
+ this.doc.addEventListener('touchmove', this.handleDragMove);
76773
+ }, 120);
76774
+ this.doc.addEventListener('mouseup', this.handleDragEnd);
76775
+ this.doc.addEventListener('touchend', this.handleDragEnd);
76776
+ }
76777
+ startDragging(startX, startY) {
76778
+ if (this.onBeforeChange) this.onBeforeChange();
76779
+ this.isDragging = true;
76780
+ const blocks = this.doc.querySelectorAll(this.selector + '.active');
76781
+ blocks.forEach(target => {
76782
+ // const containerRect = target.parentNode.getBoundingClientRect(); // if container has top/left
76783
+ const containerRect = this.common.getRect(target.parentNode); // if container has top/left
76784
+ const rect = this.common.getRect(target);
76785
+ const x = startX - rect.left + containerRect.left;
76786
+ const y = startY - rect.top + containerRect.top;
76787
+ target.setAttribute('data-startx', x);
76788
+ target.setAttribute('data-starty', y);
76789
+ this.common.applyPixels(target);
76790
+ });
76791
+ this.clickedBlock = this.common.getSelectedBlock();
76792
+ }
76793
+ handleDragMove(event) {
76794
+ let clientX, clientY;
76795
+ if (event.type === 'mousemove') {
76796
+ clientX = event.clientX;
76797
+ clientY = event.clientY;
76798
+ } else if (event.type === 'touchmove') {
76799
+ const touch = event.touches[0];
76800
+ clientX = touch.clientX;
76801
+ clientY = touch.clientY;
76802
+ }
76803
+ if (this.isDragging) {
76804
+ // Allows dragging multiple selected blocks
76805
+
76806
+ let blocks = this.doc.querySelectorAll(this.selector + '.active');
76807
+ blocks.forEach(target => {
76808
+ this.updatePosition(clientX, clientY, target);
76809
+ });
76810
+
76811
+ // Ruler works for single block selection only
76812
+ if (this.clickedBlock) this.ruler.updateRulers(this.clickedBlock);
76813
+ }
76814
+ event.preventDefault();
76815
+ event.stopImmediatePropagation();
76816
+ }
76817
+ updatePosition(x, y, target) {
76818
+ if (this.isDragging) {
76819
+ const startX = target.getAttribute('data-startx');
76820
+ const startY = target.getAttribute('data-starty');
76821
+ const newX = x - startX;
76822
+ const newY = y - startY;
76823
+ target.style.left = newX + 'px';
76824
+ target.style.top = newY + 'px';
76825
+ }
76826
+ }
76827
+ handleDragEnd() {
76828
+ clearTimeout(this.dragStartTimeout);
76829
+ if (this.isDragging) {
76830
+ this.doc.removeEventListener('mousemove', this.handleDragMove);
76831
+ this.doc.removeEventListener('touchmove', this.handleDragMove);
76832
+ }
76833
+ this.doc.removeEventListener('mouseup', this.handleDragEnd);
76834
+ this.doc.removeEventListener('touchend', this.handleDragEnd);
76835
+ this.stopDragging();
76836
+ this.ruler.hideRulers();
76837
+ let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && !elm.classList.contains('multi'));
76838
+ if (blocks.length === 1) {
76839
+ const block = blocks[0];
76840
+ if (this.onSelectStart) this.onSelectStart(block); // on first block select (before clone)
76841
+ }
76842
+ }
76843
+
76844
+ stopDragging() {
76845
+ if (this.isDragging) {
76846
+ const blocks = this.doc.querySelectorAll(this.selector + '.active');
76847
+ blocks.forEach(target => {
76848
+ this.updateBlockStyle(target);
76849
+ });
76850
+ this.isDragging = false;
76851
+ }
76852
+ }
76853
+ updateBlockStyle(target) {
76854
+ // Replace with ruler's alignment
76855
+ const containerRect = this.common.getRect(target.parentNode); // if container has top/left
76856
+ const initialWidth = parseFloat(getComputedStyle(target).width);
76857
+ if (this.ruler.rulerTop !== null) target.style.top = this.ruler.rulerTop - containerRect.top + 'px';
76858
+ if (this.ruler.rulerLeft !== null) target.style.left = this.ruler.rulerLeft - containerRect.left + 'px';else if (this.ruler.rulerRight !== null) target.style.left = this.ruler.rulerRight - initialWidth - containerRect.left + 'px';
76859
+ this.doc.querySelectorAll('[data-startx]').forEach(elm => elm.removeAttribute('data-startx'));
76860
+ this.doc.querySelectorAll('[data-starty]').forEach(elm => elm.removeAttribute('data-starty'));
76861
+ this.common.applyPercentage(target);
76862
+ const breakpoint = this.doc.body.getAttribute('data-breakpoint');
76863
+
76864
+ // const screenWidth = window.innerWidth;
76865
+ // let defaultPoint;
76866
+ // if(screenWidth<=1920) {
76867
+ // defaultPoint = 1366;
76868
+ // } else {
76869
+ // defaultPoint = 1900;
76870
+ // }
76871
+
76872
+ if (breakpoint && breakpoint < 1920) {
76873
+ target.setAttribute('data--y-' + breakpoint, target.style.top);
76874
+ target.setAttribute('data--x-' + breakpoint, target.style.left);
76875
+ target.setAttribute('data--w-' + breakpoint, target.style.width);
76876
+ target.setAttribute('data--h-' + breakpoint, target.style.height);
76877
+ } else {
76878
+ target.setAttribute('data--y', target.style.top);
76879
+ target.setAttribute('data--x', target.style.left);
76880
+ target.setAttribute('data--w', target.style.width);
76881
+ target.setAttribute('data--h', target.style.height);
76882
+ }
76883
+ target.removeAttribute('data-prev'); // reset
76884
+ target.removeAttribute('data-fluid');
76885
+ if (this.onChange) this.onChange();
76886
+ }
76887
+ handleSelect(event) {
76888
+ const element = event.target;
76889
+ if (element.classList.contains('rotate-handle')) return;
76890
+ const block = element.closest(this.selector);
76891
+ if (block) {
76892
+ if (block.classList.contains('active')) {
76893
+ let actualBlock = block.classList.contains('clone') ? this.doc.querySelector(this.selector + '.cloned') : null;
76894
+ if (!actualBlock) actualBlock = block;
76895
+ if (this.onSelectBlock) this.onSelectBlock(actualBlock);
76896
+ return; // if clicked block is active
76897
+ }
76898
+ // if(block.parentNode.classList.contains('active')) return; // if group is active
76899
+
76900
+ if (block.matches(this.selector) && block.querySelector(this.selector)) {
76901
+ // if group is clicked, remove active childe's blocks
76902
+ block.querySelectorAll(this.selector).forEach(elm => elm.classList.remove('active'));
76903
+ }
76904
+ if (event.shiftKey || this.doc.body.classList.contains('multi-select')) {
76905
+ // multi select
76906
+ block.classList.add('active');
76907
+ if (this.onMultipleSelect) this.onMultipleSelect();
76908
+ } else {
76909
+ if (this.onSelectClear) this.onSelectClear();
76910
+ this.doc.querySelectorAll(this.selector + '.active').forEach(elm => elm.classList.remove('active'));
76911
+ block.classList.add('active');
76912
+ let actualBlock = block.classList.contains('clone') ? this.doc.querySelector(this.selector + '.cloned') : null;
76913
+ if (!actualBlock) actualBlock = block;
76914
+ if (this.onSelectBlock) this.onSelectBlock(actualBlock);
76915
+ }
76916
+ if (!block.parentNode) return; // just in case
76917
+
76918
+ // Higlight parent group if current child block is clicked
76919
+ if (block.parentNode.matches(this.selector)) {
76920
+ block.parentNode.classList.add('block-active');
76921
+ } else {
76922
+ this.doc.querySelectorAll('.block-active').forEach(elm => elm.classList.remove('block-active'));
76923
+ }
76924
+ } else {
76925
+ if (event.target.closest('.keep-selection')) return;
76926
+
76927
+ // // Make empty height set with value
76928
+ // // if(this.clickedBlock) this.common.updateHeight(this.clickedBlock);
76929
+ // // Check
76930
+ // if(this.clickedBlock && !this.clickedBlock.classList.contains('clone')) {
76931
+ // this.common.updateHeight(this.clickedBlock);
76932
+ // }
76933
+
76934
+ this.doc.querySelectorAll(this.selector + '.active').forEach(elm => elm.classList.remove('active'));
76935
+ this.doc.querySelectorAll('.block-active').forEach(elm => elm.classList.remove('block-active'));
76936
+ if (this.onSelectClear) this.onSelectClear();
76937
+ }
76938
+
76939
+ // Check if multiple selection occurs. If so, add 'multi' class to hide all the handles
76940
+ let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && !elm.classList.contains('clone'));
76941
+ if (blocks.length > 1) {
76942
+ blocks.forEach(block => {
76943
+ block.classList.add('multi');
76944
+ });
76945
+ } else {
76946
+ const elms = this.doc.querySelectorAll('.multi');
76947
+ elms.forEach(elm => elm.classList.remove('multi'));
76948
+ }
76949
+ }
76950
+ delete() {
76951
+ let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && elm.classList.contains('editable'));
76952
+ if (blocks.length > 0) return;
76953
+ if (this.onBeforeChange) this.onBeforeChange();
76954
+ blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active'));
76955
+ blocks.forEach(element => {
76956
+ element.removeEventListener('mousedown', this.handleDragStart);
76957
+ element.removeEventListener('touchstart', this.handleDragStart);
76958
+ if (this.onDelete) this.onDelete(element);
76959
+ element.parentNode.removeChild(element);
76960
+ });
76961
+ if (this.onChange) this.onChange();
76962
+ }
76963
+ handleKeyDown(event) {
76964
+ if (event.key === 'Delete' || event.key === 'Backspace' || event.keyCode === 46) {
76965
+ this.delete();
76966
+ }
76967
+ }
76968
+ }
76969
+ class Editable {
76970
+ constructor(options) {
76971
+ this.selector = options && options.selector || '.is-block';
76972
+ this.controlSelector = options && options.controlSelector || null;
76973
+ this.onEditStart = options && options.onEditStart || null;
76974
+ this.onEditEnd = options && options.onEditEnd || null;
76975
+ this.doc = options && options.doc || document;
76976
+ this.win = options && options.win || window;
76977
+ this.onContentClick = options && options.onContentClick || null;
76978
+ this.elements = this.doc.querySelectorAll(this.selector);
76979
+ this.handleEditStart = this.handleEditStart.bind(this);
76980
+ this.handleMouseClick = this.handleMouseClick.bind(this);
76981
+ this.handleTouchStart = this.handleTouchStart.bind(this);
76982
+ this.setup();
76983
+ }
76984
+ setup() {
76985
+ this.elements.forEach(element => {
76986
+ element.addEventListener('dblclick', this.handleEditStart);
76987
+ element.addEventListener('touchstart', this.handleTouchStart, {
76988
+ passive: false
76989
+ });
76990
+ });
76991
+ }
76992
+ refresh() {
76993
+ this.elements = this.doc.querySelectorAll(this.selector);
76994
+ this.elements.forEach(element => {
76995
+ element.removeEventListener('dblclick', this.handleEditStart);
76996
+ element.removeEventListener('touchstart', this.handleTouchStart);
76997
+ });
76998
+ this.elements.forEach(element => {
76999
+ element.addEventListener('dblclick', this.handleEditStart);
77000
+ element.addEventListener('touchstart', this.handleTouchStart, {
77001
+ passive: false
77002
+ });
77003
+ });
77004
+ }
77005
+ destroy(element) {
77006
+ if (element) {
77007
+ element.removeEventListener('dblclick', this.handleEditStart);
77008
+ element.removeEventListener('touchstart', this.handleTouchStart);
77009
+ } else {
77010
+ this.elements = this.doc.querySelectorAll(this.selector);
77011
+ this.elements.forEach(element => {
77012
+ element.removeEventListener('dblclick', this.handleEditStart);
77013
+ element.removeEventListener('touchstart', this.handleTouchStart);
77014
+ });
77015
+ }
77016
+ }
77017
+ handleEditStart(event) {
77018
+ const currentTarget = event.currentTarget;
77019
+ if (currentTarget.querySelector(this.selector)) {
77020
+ // if group, no edit needed
77021
+ return;
77022
+ }
77023
+ const block = event.target.closest(this.selector);
77024
+ if (currentTarget !== block) {
77025
+ // Triggered Group click by child block click
77026
+ return; // Return here, so that when block events are working, group events won't
77027
+ }
77028
+
77029
+ if (block.parentNode.matches(this.selector) && block.parentNode.classList.contains('active')) {
77030
+ // if group is active, no edit needed
77031
+ return;
77032
+ }
77033
+ this.clickedBlock = currentTarget;
77034
+
77035
+ // currentTarget.classList.add('editable');
77036
+ // currentTarget.setAttribute('contentEditable', true);
77037
+ // currentTarget.style.cursor = 'auto'; // Change cursor to normal
77038
+ // currentTarget.focus();
77039
+ // this.placeCursorAtEnd(currentTarget);
77040
+
77041
+ this.elements.forEach(element => {
77042
+ if (currentTarget === element) {
77043
+ element.removeEventListener('dblclick', this.handleEditStart);
77044
+ element.removeEventListener('touchstart', this.handleTouchStart);
77045
+ const clonedTarget = this.doc.querySelector(this.selector + '.cloned');
77046
+ if (clonedTarget) {
77047
+ clonedTarget.removeEventListener('dblclick', this.handleEditStart);
77048
+ clonedTarget.removeEventListener('touchstart', this.handleTouchStart);
77049
+ this.clickedBlock = clonedTarget;
77050
+ }
77051
+ }
77052
+ });
77053
+ this.doc.addEventListener('mousedown', this.handleMouseClick);
77054
+ this.doc.addEventListener('touchstart', this.handleMouseClick, {
77055
+ passive: false
77056
+ });
77057
+ if (this.onEditStart) this.onEditStart(currentTarget);
77058
+ }
77059
+ handleTouchStart(event) {
77060
+ // Handle Double Tab
77061
+ if (event.touches.length === 1) {
77062
+ const now = new Date().getTime();
77063
+ const lastTouchTime = this.lastTouchTime || now;
77064
+ const timeDiff = now - lastTouchTime;
77065
+ if (timeDiff < 300 && timeDiff > 0) {
77066
+ // Less than 300ms since the last touch, consider it a double-tap
77067
+ this.handleDoubleTap(event);
77068
+ }
77069
+ this.lastTouchTime = now;
77070
+ }
77071
+ }
77072
+ handleDoubleTap(event) {
77073
+ this.handleEditStart(event);
77074
+ }
77075
+ handleMouseClick(event) {
77076
+ const element = event.target;
77077
+ if (element.closest(this.selector) === this.clickedBlock || this.controlSelector && element.closest(this.controlSelector)) {
77078
+ // Do Nothing
77079
+ // Continue editing
77080
+ if (element.parentNode.matches(this.selector)) {
77081
+ if (this.onContentClick) this.onContentClick(event);
77082
+ // this.doc.querySelectorAll('.elm-active').forEach(elm=>elm.classList.remove('elm-active'));
77083
+ // element.classList.add('elm-active');
77084
+ }
77085
+ } else {
77086
+ let clickedBlock = this.clickedBlock;
77087
+
77088
+ // currentTarget.classList.remove('editable');
77089
+ // clickedBlock.removeAttribute('contentEditable');
77090
+ // clickedBlock.style.cursor = '';
77091
+
77092
+ if (this.onEditEnd) this.onEditEnd(this.clickedBlock);
77093
+ this.elements.forEach(element => {
77094
+ if (clickedBlock === element) {
77095
+ element.addEventListener('dblclick', this.handleEditStart);
77096
+ element.addEventListener('touchstart', this.handleTouchStart, {
77097
+ passive: false
77098
+ });
77099
+ }
77100
+ });
77101
+ this.doc.removeEventListener('mousedown', this.handleMouseClick);
77102
+ this.doc.removeEventListener('touchstart', this.handleMouseClick);
77103
+
77104
+ // this.doc.querySelectorAll('.elm-active').forEach(elm=>elm.classList.remove('elm-active'));
77105
+ }
77106
+ }
77107
+
77108
+ quitEditable(block) {
77109
+ if (this.onEditEnd) this.onEditEnd(block);
77110
+ block.addEventListener('dblclick', this.handleEditStart);
77111
+ block.addEventListener('touchstart', this.handleTouchStart, {
77112
+ passive: false
77113
+ });
77114
+ this.doc.removeEventListener('mousedown', this.handleMouseClick);
77115
+ this.doc.removeEventListener('touchstart', this.handleMouseClick);
77116
+ }
77117
+ placeCursorAtEnd(element) {
77118
+ const range = this.doc.createRange();
77119
+ const sel = this.win.getSelection();
77120
+ range.setStart(element, element.childNodes.length);
77121
+ range.collapse(true);
77122
+ sel.removeAllRanges();
77123
+ sel.addRange(range);
77124
+ }
77125
+ }
77126
+ class BlockSelector {
77127
+ constructor(options) {
77128
+ this.selector = options && options.selector || '.is-block';
77129
+ this.parentSelector = options && options.parentSelector || 'body';
77130
+ this.doc = options && options.doc || document;
77131
+ this.win = options && options.win || window;
77132
+ this.disableOnMobile = options && options.disableOnMobile || 0;
77133
+ this.onMultipleSelect = options && options.onMultipleSelect || null;
77134
+ this.isDragging = false;
77135
+ this.startCoords = {
77136
+ x: 0,
77137
+ y: 0
77138
+ };
77139
+ const handleHTML = `
77140
+ <div class="selection-rectangle"></div>`;
77141
+ this.doc.body.insertAdjacentHTML('afterbegin', handleHTML);
77142
+ this.selectionRect = this.doc.querySelector('.selection-rectangle');
77143
+ const styleHTML = `
77144
+ <style id="styleSelector">
77145
+ .selection-rectangle {
77146
+ position: absolute;
77147
+ border: 1px solid #c2e2ff;
77148
+ pointer-events: none;
77149
+ display: none;
77150
+ background-color: #a9c4e442;
77151
+ z-index:1
77152
+ }
77153
+ <style>
77154
+ `;
77155
+ const elmStyle = this.doc.querySelector('#styleSelector');
77156
+ if (!elmStyle) this.doc.head.insertAdjacentHTML('beforeend', styleHTML);
77157
+ this.handleDragStart = this.handleDragStart.bind(this);
77158
+ this.handleDragMove = this.handleDragMove.bind(this);
77159
+ this.handleDragEnd = this.handleDragEnd.bind(this);
77160
+ this.doc.addEventListener('mousedown', this.handleDragStart);
77161
+ this.doc.addEventListener('touchstart', this.handleDragStart, {
77162
+ passive: false
77163
+ });
77164
+ }
77165
+ destroy() {
77166
+ const elmStyle = this.doc.querySelector('#styleSelector');
77167
+ if (elmStyle) elmStyle.parentNode.removeChild(elmStyle);
77168
+ const selectionRect = this.doc.querySelector('.selection-rectangle');
77169
+ if (selectionRect) selectionRect.parentNode.removeChild(selectionRect);
77170
+ this.doc.removeEventListener('mousedown', this.handleDragStart);
77171
+ this.doc.removeEventListener('touchstart', this.handleDragStart);
77172
+ }
77173
+ handleDragStart(event) {
77174
+ const viewportWidth = this.win.innerWidth;
77175
+ if (viewportWidth <= this.disableOnMobile) return;
77176
+ if (event.type === 'touchstart' && event.touches.length !== 1) {
77177
+ return; // Do nothing if more than one touch point is detected
77178
+ }
77179
+
77180
+ if (event.type === 'mousedown' && event.button !== 0) {
77181
+ return; // Do nothing if the right mouse button is clicked
77182
+ }
77183
+
77184
+ if (event.target.closest(this.selector)) return;
77185
+ if (!event.target.closest(this.parentSelector)) return;
77186
+ if (event.target.closest('.keep-selection')) return;
77187
+ this.dragStartTimeout = setTimeout(() => {
77188
+ // Set a timeout before starting the drag (to differentiate from click)
77189
+
77190
+ this.isDragging = true;
77191
+ let clientX, clientY;
77192
+ if (event.type === 'mousedown') {
77193
+ clientX = event.clientX;
77194
+ clientY = event.clientY;
77195
+ } else if (event.type === 'touchstart') {
77196
+ const touch = event.touches[0];
77197
+ clientX = touch.clientX;
77198
+ clientY = touch.clientY;
77199
+ }
77200
+ this.startCoords = {
77201
+ x: clientX,
77202
+ y: clientY
77203
+ };
77204
+ this.updateSelectionRect(this.startCoords, this.startCoords);
77205
+ this.doc.addEventListener('mousemove', this.handleDragMove);
77206
+ this.doc.addEventListener('touchmove', this.handleDragMove);
77207
+ this.doc.body.style.userSelect = 'none';
77208
+ }, 100);
77209
+ this.doc.addEventListener('mouseup', this.handleDragEnd);
77210
+ this.doc.addEventListener('touchend', this.handleDragEnd);
77211
+ }
77212
+ handleDragMove(event) {
77213
+ if (!this.isDragging) return;
77214
+ if (event.target.closest('.keep-selection')) return;
77215
+ let clientX, clientY;
77216
+ if (event.type === 'mousemove') {
77217
+ clientX = event.clientX;
77218
+ clientY = event.clientY;
77219
+ } else if (event.type === 'touchmove') {
77220
+ const touch = event.touches[0];
77221
+ clientX = touch.clientX;
77222
+ clientY = touch.clientY;
77223
+ }
77224
+ this.updateSelectionRect(this.startCoords, {
77225
+ x: clientX,
77226
+ y: clientY
77227
+ });
77228
+ this.selectBlocksInArea(this.startCoords, {
77229
+ x: clientX,
77230
+ y: clientY
77231
+ });
77232
+ }
77233
+ handleDragEnd() {
77234
+ clearTimeout(this.dragStartTimeout);
77235
+ this.isDragging = false;
77236
+ this.selectionRect.style.display = 'none';
77237
+ this.doc.removeEventListener('mousemove', this.handleDragMove);
77238
+ this.doc.removeEventListener('touchmove', this.handleDragMove);
77239
+ this.doc.removeEventListener('mouseup', this.handleDragEnd);
77240
+ this.doc.removeEventListener('touchend', this.handleDragEnd);
77241
+ this.doc.body.style.userSelect = '';
77242
+ const elms = this.doc.querySelectorAll(this.selector + '.active');
77243
+ if (elms.length > 1) this.onMultipleSelect();
77244
+ }
77245
+ updateSelectionRect(start, end) {
77246
+ const minX = Math.min(start.x, end.x);
77247
+ const minY = Math.min(start.y, end.y) + this.win.scrollY;
77248
+ const width = Math.abs(start.x - end.x);
77249
+ const height = Math.abs(start.y - end.y);
77250
+ this.selectionRect.style.left = `${minX}px`;
77251
+ this.selectionRect.style.top = `${minY}px`;
77252
+ this.selectionRect.style.width = `${width}px`;
77253
+ this.selectionRect.style.height = `${height}px`;
77254
+ this.selectionRect.style.display = 'block';
77255
+ }
77256
+ selectBlocksInArea(start, end) {
77257
+ const blocks = this.doc.querySelectorAll(this.selector);
77258
+ blocks.forEach(block => {
77259
+ const rect = block.getBoundingClientRect();
77260
+ if (rect.left < Math.max(start.x, end.x) && rect.right > Math.min(start.x, end.x) && rect.top < Math.max(start.y, end.y) && rect.bottom > Math.min(start.y, end.y)) {
77261
+ if (block.parentNode.matches(this.selector)) ; else {
77262
+ block.classList.add('active');
77263
+ let elms = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && !elm.classList.contains('clone'));
77264
+ if (elms.length > 1) block.classList.add('multi');
77265
+ }
77266
+ } else {
77267
+ block.classList.remove('active');
77268
+ block.classList.remove('multi');
77269
+ }
77270
+ });
77271
+ }
77272
+ unSelect() {
77273
+ const blocks = this.doc.querySelectorAll(this.selector);
77274
+ blocks.forEach(elm => elm.classList.remove('active'));
77275
+ }
77276
+ }
77277
+ class EditableBlocks {
77278
+ constructor(options = {}) {
77279
+ const defaults = {
77280
+ selector: '.is-block',
77281
+ parentSelector: 'body',
77282
+ // controlSelector: '.is-tool',
77283
+ doc: document,
77284
+ win: window,
77285
+ // onBeforeChange: () => {},
77286
+ // onChange: () => {},
77287
+ // onContentClick: () => {},
77288
+ // onEditStart: () => {},
77289
+ // onEditEnd: () => {},
77290
+ // onDuplicate: () => {},
77291
+ // onSelectBlock: (block) => {},
77292
+ // onUnSelectBlock: () => {},
77293
+ // onMultipleSelect: () => {},
77294
+ disableOnMobile: 0,
77295
+ rotate: true,
77296
+ clone: true
77297
+ // onDelete: () = {}
77298
+ };
77299
+
77300
+ Object.assign(this, defaults, options);
77301
+ this.init();
77302
+ }
77303
+ checkOverlap(target) {
77304
+ return this.common.checkOverlap(target);
77305
+ }
77306
+ forward(target) {
77307
+ let newZIndex;
77308
+ if (!target.style.zIndex) {
77309
+ newZIndex = 1;
77310
+ } else {
77311
+ newZIndex = parseInt(target.style.zIndex) + 1;
77312
+ }
77313
+ target.style.zIndex = newZIndex;
77314
+ }
77315
+ backward(target) {
77316
+ let newZIndex;
77317
+ if (!target.style.zIndex) {
77318
+ newZIndex = 0;
77319
+ } else {
77320
+ if (parseInt(target.style.zIndex) - 1 < 0) newZIndex = 0;else newZIndex = parseInt(target.style.zIndex) - 1;
77321
+ }
77322
+ target.style.zIndex = newZIndex;
77323
+ }
77324
+ selectStart(block) {
77325
+ if (block.classList.contains('is-shape')) return; // do not clone if block is shape
77326
+
77327
+ if (!this.clone) return;
77328
+ const viewportWidth = this.win.innerWidth;
77329
+ if (block.closest('.autolayout') && viewportWidth <= 760) {
77330
+ return;
77331
+ }
77332
+ if (block.classList.contains('is-group')) return; // do not clone if block is shape
77333
+
77334
+ if (!block.classList.contains('clone')) {
77335
+ let clonedDiv = block.cloneNode(true);
77336
+ clonedDiv.classList.add('clone');
77337
+ block.parentNode.appendChild(clonedDiv);
77338
+ block.classList.add('cloned');
77339
+ this.refresh();
77340
+ }
77341
+ }
77342
+ selectClear() {
77343
+ this.doc.querySelectorAll('.clone').forEach(elm => elm.parentNode.removeChild(elm));
77344
+ this.doc.querySelectorAll('.cloned').forEach(elm => elm.classList.remove('cloned'));
77345
+ }
77346
+ setMultiSelect(multiSelect) {
77347
+ if (multiSelect) this.doc.body.classList.add('multi-select');else this.doc.body.classList.remove('multi-select');
77348
+ }
77349
+ init() {
77350
+ this.doc.body.classList.add('editableblocks');
77351
+ this.draggable = new Draggable({
77352
+ selector: this.selector,
77353
+ disableOnMobile: this.disableOnMobile,
77354
+ doc: this.doc,
77355
+ win: this.win,
77356
+ onDelete: element => {
77357
+ if (this.rotatable) this.rotatable.destroy(element);
77358
+ this.resizable.destroy(element);
77359
+ this.editable.destroy(element);
77360
+ if (this.onDelete) this.onDelete();
77361
+ },
77362
+ onBeforeChange: () => {
77363
+ if (this.onBeforeChange) this.onBeforeChange();
77364
+ },
77365
+ onChange: () => {
77366
+ if (this.onChange) this.onChange();
77367
+ },
77368
+ onMultipleSelect: this.onMultipleSelect,
77369
+ onSelectStart: block => {
77370
+ // for cloning
77371
+ this.selectStart(block);
77372
+ },
77373
+ onSelectClear: () => {
77374
+ // for removing clones
77375
+ this.selectClear();
77376
+ if (this.onUnselectBlock) this.onUnselectBlock();
77377
+ },
77378
+ onSelectBlock: this.onSelectBlock
77379
+ });
77380
+ this.resizable = new Resizable({
77381
+ selector: this.selector,
77382
+ disableOnMobile: this.disableOnMobile,
77383
+ doc: this.doc,
77384
+ win: this.win,
77385
+ onBeforeChange: () => {
77386
+ if (this.onBeforeChange) this.onBeforeChange();
77387
+ },
77388
+ onChange: () => {
77389
+ if (this.onChange) this.onChange();
77390
+ }
77391
+ });
77392
+ if (this.rotate) {
77393
+ this.rotatable = new Rotatable({
77394
+ selector: this.selector,
77395
+ disableOnMobile: this.disableOnMobile,
77396
+ doc: this.doc,
77397
+ win: this.win,
77398
+ onBeforeChange: () => {
77399
+ if (this.onBeforeChange) this.onBeforeChange();
77400
+ },
77401
+ onChange: () => {
77402
+ if (this.onChange) this.onChange();
77403
+ }
77404
+ });
77405
+ }
77406
+ this.editable = new Editable({
77407
+ selector: this.selector,
77408
+ controlSelector: this.controlSelector,
77409
+ doc: this.doc,
77410
+ win: this.win,
77411
+ onContentClick: this.onContentClick,
77412
+ onEditStart: block => {
77413
+ if (block.classList.contains('clone')) {
77414
+ const clonedTarget = this.doc.querySelector(this.selector + '.cloned');
77415
+ this.onEditStart(clonedTarget);
77416
+ this.selectClear();
77417
+
77418
+ // Disable drag during editing
77419
+ if (!this.draggable) return;
77420
+ this.draggable.disableDrag(clonedTarget);
77421
+ return;
77422
+ }
77423
+ this.onEditStart(block);
77424
+
77425
+ // Disable drag during editing
77426
+ if (!this.draggable) return;
77427
+ this.draggable.disableDrag(block);
77428
+ },
77429
+ onEditEnd: block => {
77430
+ this.onEditEnd(block);
77431
+
77432
+ // Enable drag
77433
+ if (!this.draggable) return;
77434
+ this.draggable.enableDrag(block);
77435
+ }
77436
+ });
77437
+ this.blockSelector = new BlockSelector({
77438
+ selector: this.selector,
77439
+ parentSelector: this.parentSelector,
77440
+ disableOnMobile: this.disableOnMobile,
77441
+ onMultipleSelect: this.onMultipleSelect,
77442
+ doc: this.doc,
77443
+ win: this.win
77444
+ });
77445
+ this.common = new Common({
77446
+ selector: this.selector,
77447
+ doc: this.doc,
77448
+ win: this.win,
77449
+ onDuplicate: this.onDuplicate
77450
+ });
77451
+ }
77452
+ duplicate() {
77453
+ this.common.duplicate();
77454
+ this.refresh();
77455
+ }
77456
+ delete() {
77457
+ this.draggable.delete();
77458
+ }
77459
+ getBreakpoints(block) {
77460
+ return this.common.getBreakpoints(block);
77461
+ }
77462
+ placeCursorAtEnd(block) {
77463
+ this.editable.placeCursorAtEnd(block);
77464
+ }
77465
+ quitEditable(block) {
77466
+ this.editable.quitEditable(block);
77467
+ }
77468
+ destroy() {
77469
+ if (!this.draggable) return;
77470
+ this.draggable.destroy();
77471
+ this.resizable.destroy();
77472
+ if (this.rotatable) this.rotatable.destroy();
77473
+ this.editable.destroy();
77474
+ this.blockSelector.destroy();
77475
+ this.draggable = null;
77476
+ this.doc.body.classList.remove('editableblocks');
77477
+ }
77478
+ refresh() {
77479
+ // Call refresh() whenever blocks are added
77480
+ this.draggable.refresh();
77481
+ this.resizable.refresh();
77482
+ if (this.rotatable) this.rotatable.refresh();
77483
+ this.editable.refresh();
77484
+ }
77485
+ addBlock(html, container) {
77486
+ this.selectClear(); // clear clones
77487
+ this.blockSelector.unSelect(); // clear active
77488
+
77489
+ const parser = new DOMParser();
77490
+ let doc = parser.parseFromString(html, 'text/html');
77491
+ const block = doc.querySelector('.is-block');
77492
+ block.classList.add('block-dummy');
77493
+ html = doc.body.innerHTML;
77494
+ container.insertAdjacentHTML('beforeend', html);
77495
+ const newBlock = document.querySelector('.block-dummy');
77496
+ if (newBlock) {
77497
+ newBlock.classList.remove('block-dummy');
77498
+ if (this.onAddBlock) this.onAddBlock(newBlock);
77499
+ newBlock.classList.add('active');
77500
+ }
77501
+ this.refresh();
77502
+ }
77503
+ group() {
77504
+ const group = this.common.group('is-block', 'is-group');
77505
+ this.refresh();
77506
+ return group;
77507
+ }
77508
+ unGroup() {
77509
+ this.common.unGroup();
77510
+ }
77511
+ addBreakpoint() {
77512
+ this.common.addBreakpoint();
77513
+ }
77514
+ clearBreakpoint(target) {
77515
+ this.common.clearBreakpoint(target);
77516
+ if (target.classList.contains('cloned')) {
77517
+ const cloneTarget = this.doc.querySelector(this.selector + '.clone');
77518
+ this.common.clearBreakpoint(cloneTarget);
77519
+ }
77520
+ }
77521
+ clearAllBreakpoints(container) {
77522
+ this.common.clearAllBreakpoints(container);
77523
+ }
77524
+ isMultiSelect() {
77525
+ // Check if multiple selection occurs. If so, add 'multi' class to hide all the handles
77526
+ let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && !elm.classList.contains('clone'));
77527
+ if (blocks.length > 1) return true;
77528
+ return false;
77529
+ }
77530
+ }
75407
77531
 
75408
77532
  class ContentBuilder {
75409
77533
  constructor(opts = {}) {
@@ -75644,9 +77768,11 @@ class ContentBuilder {
75644
77768
  defaultEmailSnippetCategory: 14,
75645
77769
  undoRedoStyles: false,
75646
77770
  // specialElementClasses: ['sl-wrapper', 'sl-overlay'] // specify elements that when clicked will not affect the builder interface (active selection). Usefull for external code, ex lightbox, etc.
75647
- // onUndo: function () { },
75648
- // onRedo: function () { }
75649
77771
 
77772
+ // freeform
77773
+ onUndo: function () {},
77774
+ onRedo: function () {},
77775
+ onBlockCanvasAdd: function () {},
75650
77776
  /*
75651
77777
  Deprecated:
75652
77778
  snippetSampleImage: '',
@@ -76528,6 +78654,26 @@ class ContentBuilder {
76528
78654
  this.colTool.lockIndicator.style.display = '';
76529
78655
  return ret;
76530
78656
  };
78657
+
78658
+ // freeform
78659
+ let oldOnBlockCanvasAdd = this.opts.onBlockCanvasAdd;
78660
+ this.opts.onBlockCanvasAdd = () => {
78661
+ let ret = oldOnBlockCanvasAdd.apply(this, arguments);
78662
+ if (this.eb) this.eb.refresh();
78663
+ return ret;
78664
+ };
78665
+ let oldOnUndo = this.opts.onUndo;
78666
+ this.opts.onUndo = () => {
78667
+ let ret = oldOnUndo.apply(this, arguments);
78668
+ if (this.eb) this.eb.refresh();
78669
+ return ret;
78670
+ };
78671
+ let oldOnRedo = this.opts.onRedo;
78672
+ this.opts.onRedo = () => {
78673
+ let ret = oldOnRedo.apply(this, arguments);
78674
+ if (this.eb) this.eb.refresh();
78675
+ return ret;
78676
+ };
76531
78677
  this.elmTool = new ElementTool(this); // Render Element Tool
76532
78678
 
76533
78679
  // Render controls or behavior for handling element editing
@@ -76595,6 +78741,68 @@ class ContentBuilder {
76595
78741
  }
76596
78742
  */
76597
78743
 
78744
+ // freeform
78745
+ if (this.canvas) this.eb = new EditableBlocks({
78746
+ doc: this.doc,
78747
+ win: this.win,
78748
+ selector: '.is-block',
78749
+ controlSelector: '.is-tool,.is-pop,.is-modal,#divImageResizer,.is-rte-tool,is-elementrte-tool,.is-rte-pop,.keep-selection',
78750
+ parentSelector: '.box-canvas',
78751
+ rotate: true,
78752
+ // disableOnMobile: 760,
78753
+ onBeforeChange: () => {
78754
+ this.uo.saveForUndo();
78755
+ },
78756
+ onChange: () => {
78757
+ this.opts.onChange();
78758
+ },
78759
+ onEditStart: block => {
78760
+ block.classList.add('editable');
78761
+ const cols = block.querySelectorAll('[data-click="true"]'); // or [contentEditable="true"]
78762
+ if (cols.length > 0) {
78763
+ let col = cols[cols.length - 1];
78764
+ if (col.lastElementChild) col.lastElementChild.click();
78765
+ }
78766
+ },
78767
+ onEditEnd: block => {
78768
+ block.classList.remove('editable');
78769
+ this.util.clearActiveCell();
78770
+ this.util.clearPops();
78771
+ this.hideElementTools();
78772
+ },
78773
+ onDuplicate: block => {
78774
+ const builder = block.querySelector(this.container);
78775
+ let html = '';
78776
+ if (builder) {
78777
+ html = this.readHtml(builder);
78778
+ }
78779
+ let clonedDiv = block.cloneNode(true);
78780
+ clonedDiv.style.top = '20%';
78781
+ clonedDiv.style.left = '20%';
78782
+ if (builder) {
78783
+ const cloneBuilder = clonedDiv.querySelector(this.container);
78784
+ cloneBuilder.innerHTML = '';
78785
+ block.parentNode.appendChild(clonedDiv);
78786
+ const range = document.createRange();
78787
+ cloneBuilder.appendChild(range.createContextualFragment(html));
78788
+ this.applyBehaviorOn(cloneBuilder);
78789
+ cloneBuilder.click();
78790
+ } else {
78791
+ block.parentNode.appendChild(clonedDiv);
78792
+ }
78793
+ block.classList.remove('active');
78794
+ this.doc.querySelectorAll('.clone').forEach(elm => elm.parentNode.removeChild(elm));
78795
+ this.doc.querySelectorAll('.cloned').forEach(elm => elm.classList.remove('cloned'));
78796
+ },
78797
+ onAddBlock: block => {
78798
+ const builder = block.querySelector(this.container);
78799
+ this.applyBehaviorOn(builder);
78800
+ },
78801
+ onMultipleSelect: this.onMultipleSelect,
78802
+ onDelete: this.onDelete,
78803
+ onSelectBlock: this.onSelectBlock,
78804
+ onUnselectBlock: this.onUnselectBlock
78805
+ });
76598
78806
  if (this.iframe) {
76599
78807
  this.win.addEventListener('scroll', this.doWindowScroll = () => {
76600
78808
  this.util.hidePops();
@@ -77415,6 +79623,13 @@ class ContentBuilder {
77415
79623
  this.setZoomOnControl(builder);
77416
79624
  }
77417
79625
  html(area) {
79626
+ if (this.docContainer) {
79627
+ // freeform
79628
+
79629
+ const docContainer = this.doc.querySelector(this.docContainer);
79630
+ const html = this.readHtml(docContainer, false, true);
79631
+ return html;
79632
+ }
77418
79633
  const util = this.util;
77419
79634
  const htmlutil = new HtmlUtil(this);
77420
79635
  if (area) ; else {
@@ -77481,7 +79696,87 @@ class ContentBuilder {
77481
79696
  simpleColorPicker(onPick, mode) {
77482
79697
  return this.colorClassPicker.open(onPick, mode);
77483
79698
  }
79699
+
79700
+ // freeform
79701
+ refresh() {
79702
+ if (this.eb) this.eb.refresh();
79703
+ }
79704
+ group() {
79705
+ if (!this.eb) return;
79706
+ this.uo.saveForUndo();
79707
+ this.eb.group();
79708
+ this.opts.onChange();
79709
+ }
79710
+ unGroup() {
79711
+ if (!this.eb) return;
79712
+ this.uo.saveForUndo();
79713
+ this.eb.unGroup();
79714
+ this.opts.onChange();
79715
+ }
79716
+ delete() {
79717
+ if (!this.eb) return;
79718
+ this.uo.saveForUndo();
79719
+ this.eb.delete();
79720
+ this.opts.onChange();
79721
+ }
79722
+ forward(target) {
79723
+ if (!this.eb) return;
79724
+ this.uo.saveForUndo();
79725
+ this.eb.forward(target);
79726
+ this.opts.onChange();
79727
+ }
79728
+ backward(target) {
79729
+ if (!this.eb) return;
79730
+ this.uo.saveForUndo();
79731
+ this.eb.backward(target);
79732
+ this.opts.onChange();
79733
+ }
79734
+ enableShape() {
79735
+ if (!this.eb) return;
79736
+ this.uo.saveForUndo();
79737
+ this.eb.enableShape();
79738
+ this.opts.onChange();
79739
+ }
79740
+ removeShape() {
79741
+ if (!this.eb) return;
79742
+ this.uo.saveForUndo();
79743
+ this.eb.removeShape();
79744
+ this.opts.onChange();
79745
+ }
79746
+ addBlock(html, blockContainer) {
79747
+ if (!this.eb) return;
79748
+ this.uo.saveForUndo();
79749
+ this.eb.addBlock(html, blockContainer);
79750
+ this.opts.onChange();
79751
+ this.opts.onRender();
79752
+ }
79753
+ duplicate() {
79754
+ if (!this.eb) return;
79755
+ this.uo.saveForUndo();
79756
+ this.eb.duplicate();
79757
+ this.opts.onChange();
79758
+ this.opts.onRender();
79759
+ }
79760
+ addBreakpoint() {
79761
+ if (!this.eb) return;
79762
+ this.eb.addBreakpoint();
79763
+ }
79764
+ getBreakpoints(target) {
79765
+ if (!this.eb) return;
79766
+ return this.eb.getBreakpoints(target);
79767
+ }
79768
+ clearBreakpoint(target) {
79769
+ if (!this.eb) return;
79770
+ this.uo.saveForUndo();
79771
+ this.eb.clearBreakpoint(target);
79772
+ this.opts.onChange();
79773
+ }
79774
+ quitEditable(target) {
79775
+ if (!this.eb) return;
79776
+ this.eb.quitEditable(target);
79777
+ }
77484
79778
  destroy() {
79779
+ if (this.eb) this.eb.destroy();
77485
79780
  this.doc.body.classList.remove('data-editor');
77486
79781
  document.removeEventListener('click', this.doDocumentClick, false);
77487
79782
  document.removeEventListener('keydown', this.doDocumentKeydown, false);
@@ -78103,7 +80398,7 @@ class ContentBuilder {
78103
80398
  this.opts.onPluginsLoaded();
78104
80399
  }
78105
80400
  this.tooltip.setAll();
78106
- new Draggable$1({
80401
+ new Draggable$2({
78107
80402
  selector: '.is-draggable'
78108
80403
  }); //draggable for plugins
78109
80404
 
@@ -78164,7 +80459,7 @@ class ContentBuilder {
78164
80459
  this.opts.onPluginsLoaded();
78165
80460
  }
78166
80461
  this.tooltip.setAll();
78167
- new Draggable$1({
80462
+ new Draggable$2({
78168
80463
  selector: '.is-draggable'
78169
80464
  }); //draggable for plugins
78170
80465
 
@@ -78320,7 +80615,7 @@ class ContentBuilder {
78320
80615
  }
78321
80616
 
78322
80617
  draggable(selector) {
78323
- new Draggable$1({
80618
+ new Draggable$2({
78324
80619
  selector: selector
78325
80620
  });
78326
80621
  }
@@ -78331,7 +80626,7 @@ class ContentBuilder {
78331
80626
  embeddedModal = this.builderStuff.querySelector(selector);
78332
80627
  }
78333
80628
  this.showModal(embeddedModal, overlayStay, cancelCallback, animated, overflowHidden);
78334
- new Draggable$1({
80629
+ new Draggable$2({
78335
80630
  selector: '.is-draggable'
78336
80631
  });
78337
80632
  return embeddedModal;
@@ -78457,6 +80752,10 @@ class ContentBuilder {
78457
80752
  // /Plugins related
78458
80753
 
78459
80754
  viewHtml(area) {
80755
+ if (this.docContainer) {
80756
+ // freeform
80757
+ area = this.doc.querySelector(this.docContainer);
80758
+ }
78460
80759
  const htmlutil = new HtmlUtil(this);
78461
80760
  htmlutil.view('full', area);
78462
80761
  }
@@ -78503,6 +80802,20 @@ class ContentBuilder {
78503
80802
  return false;
78504
80803
  }
78505
80804
  loadHtml(html, area) {
80805
+ if (this.docContainer) {
80806
+ // freeform
80807
+ const docContainer = this.doc.querySelector(this.docContainer);
80808
+ let range = this.doc.createRange();
80809
+ docContainer.innerHTML = '';
80810
+ docContainer.appendChild(range.createContextualFragment(html)); // We use createContextualFragment so that embedded javascript code (code block) will be executed
80811
+
80812
+ const builders = docContainer.querySelectorAll(this.container);
80813
+ builders.forEach(builder => {
80814
+ this.applyBehaviorOn(builder);
80815
+ });
80816
+ this.refresh();
80817
+ return;
80818
+ }
78506
80819
  const util = this.util;
78507
80820
  if (area) ; else {
78508
80821
  const builders = this.doc.querySelectorAll(this.opts.container);
@@ -78561,6 +80874,7 @@ class ContentBuilder {
78561
80874
  // }
78562
80875
  loadHTML(html) {
78563
80876
  //backward
80877
+
78564
80878
  this.loadHtml(html);
78565
80879
  }
78566
80880
  async setUIColor(mode, csslink) {