@innovastudio/contentbuilder 1.4.146 → 1.4.148

Sign up to get free protection for your applications and to get access to all the features.
@@ -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) {