@innovastudio/contentbuilder 1.5.213 → 1.5.215

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@innovastudio/contentbuilder",
4
- "version": "1.5.213",
4
+ "version": "1.5.215",
5
5
  "description": "",
6
6
  "main": "public/contentbuilder/contentbuilder.esm.js",
7
7
  "types": "index.d.ts",
@@ -8888,11 +8888,13 @@ class Dom {
8888
8888
  // source: https://stackoverflow.com/questions/2871081/jquery-setting-cursor-position-in-contenteditable-div
8889
8889
  moveCursorToElement(element) {
8890
8890
  var sel, range;
8891
- if (window.getSelection && this.builder.doc.createRange) {
8891
+ if (this.builder.win.getSelection && this.builder.doc.createRange) {
8892
8892
  range = this.builder.doc.createRange();
8893
8893
  range.selectNodeContents(element);
8894
8894
  range.collapse(false);
8895
- sel = window.getSelection();
8895
+ // The CONTENT window's selection — with the editor in an iframe the
8896
+ // global one belongs to the host page, so the caret never moved.
8897
+ sel = this.builder.win.getSelection();
8896
8898
  sel.removeAllRanges();
8897
8899
  sel.addRange(range);
8898
8900
  } else if (this.builder.doc.body.createTextRange) {
@@ -32220,11 +32222,23 @@ class Plugin {
32220
32222
  }
32221
32223
  };
32222
32224
  if (!this.builder.handlePluginClick_) {
32223
- document.addEventListener('click', handlePluginClick);
32224
- if (this.builder.iframeDocument) {
32225
- this.builder.doc.addEventListener('click', handlePluginClick);
32226
- }
32227
32225
  this.builder.handlePluginClick_ = true;
32226
+ // Register on the NEXT task, not now. showPluginEditor() can be
32227
+ // reached from a microtask inside the very click that opens the
32228
+ // panel: the RTE button runs convertSelectionToPlugin(), whose
32229
+ // runtime.reinitialize().then() resolves while that click is still
32230
+ // propagating towards document. A listener added mid-propagation
32231
+ // still receives that same click, so the panel was closed the
32232
+ // instant it opened — and only on a first open, because afterwards
32233
+ // the previous listener had already fired and removed itself.
32234
+ // Deferring means this listener can never see the click that
32235
+ // created it, whichever toolbar or panel opened the panel.
32236
+ setTimeout(() => {
32237
+ document.addEventListener('click', handlePluginClick);
32238
+ if (this.builder.iframeDocument) {
32239
+ this.builder.doc.addEventListener('click', handlePluginClick);
32240
+ }
32241
+ }, 0);
32228
32242
  }
32229
32243
  }
32230
32244
  hidePluginEditor() {
@@ -69540,16 +69554,42 @@ class Resize {
69540
69554
  enable() {
69541
69555
  const col = this.element;
69542
69556
  const row = col.parentNode;
69557
+
69558
+ // The builder injects its own elements into a row — tools, the add-row
69559
+ // bar, and the row overlay that appears once the row is selected in the
69560
+ // side panel. They are not columns, and measuring them as columns makes
69561
+ // the totals below nonsense. Filter them out in ONE place so a new
69562
+ // injected element can never silently break the maths again.
69563
+ const isBuilderUI = item => item.nodeType !== 1 || item.classList.contains('is-tool') || item.classList.contains('is-row-tool') || item.classList.contains('is-col-tool') || item.classList.contains('is-rowadd-tool') || item.classList.contains('is-row-overlay');
69564
+ const columnsOf = parent => Array.from(parent.children).filter(item => !isBuilderUI(item));
69565
+
69566
+ // A column width is only ever stored as a percentage; pixels are used
69567
+ // purely while a drag is in progress. resizeEnd normally converts them
69568
+ // back, but it does not run if the drag is interrupted — and pixel
69569
+ // widths saved into the page push content off screen on smaller
69570
+ // devices. This is the backstop for that: whatever happens to the drag,
69571
+ // a leftover pixel width is converted on the next tick. Capture phase,
69572
+ // so it still runs if something stops the event bubbling.
69573
+ const normalizeToPercent = () => {
69574
+ setTimeout(() => {
69575
+ if (!col.style.width || col.style.width.indexOf('px') === -1) return;
69576
+ const parent = col.parentNode;
69577
+ if (!parent || !parent.offsetWidth) return;
69578
+ const pct = col.offsetWidth / parent.offsetWidth * 100;
69579
+ if (isFinite(pct) && pct > 0) col.style.width = pct + '%';
69580
+ }, 0);
69581
+ };
69582
+ this.normalizeToPercent = normalizeToPercent;
69583
+ this.eventTarget = col.ownerDocument;
69584
+ this.eventTarget.addEventListener('mouseup', normalizeToPercent, true);
69585
+ this.eventTarget.addEventListener('touchend', normalizeToPercent, true);
69543
69586
  this.resize = new Resizeable({
69544
69587
  pane: col,
69545
69588
  resizeHeight: this.builder.resizeHeight,
69546
69589
  onResize: (width, height) => {
69547
69590
  this.builder.util.hideControls();
69548
69591
  let numOfCols = 1;
69549
- Array.from(col.parentNode.children).map(item => {
69550
- if (item.classList.contains('is-row-tool')) return;
69551
- if (item.classList.contains('is-rowadd-tool')) return;
69552
- if (item.classList.contains('is-col-tool')) return;
69592
+ columnsOf(col.parentNode).map(item => {
69553
69593
  if (item === col) return;
69554
69594
  numOfCols++;
69555
69595
  });
@@ -69560,10 +69600,7 @@ class Resize {
69560
69600
  // adjust others
69561
69601
  const maxWidth = () => {
69562
69602
  let otherWidth = 0;
69563
- Array.from(col.parentNode.children).map(item => {
69564
- if (item.classList.contains('is-row-tool')) return;
69565
- if (item.classList.contains('is-rowadd-tool')) return;
69566
- if (item.classList.contains('is-col-tool')) return;
69603
+ columnsOf(col.parentNode).map(item => {
69567
69604
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69568
69605
 
69569
69606
  if (item === col) return;
@@ -69577,10 +69614,7 @@ class Resize {
69577
69614
  // percentage = (max / row.offsetWidth) * 100;
69578
69615
  // Or make others auto
69579
69616
 
69580
- Array.from(col.parentNode.children).map(item => {
69581
- if (item.classList.contains('is-row-tool')) return;
69582
- if (item.classList.contains('is-rowadd-tool')) return;
69583
- if (item.classList.contains('is-col-tool')) return;
69617
+ columnsOf(col.parentNode).map(item => {
69584
69618
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69585
69619
 
69586
69620
  if (item === col) return;
@@ -69618,10 +69652,7 @@ class Resize {
69618
69652
  }
69619
69653
  } else {
69620
69654
  // New: if other cols has no width, set to 100%
69621
- Array.from(col.parentNode.children).map(item => {
69622
- if (item.classList.contains('is-row-tool')) return;
69623
- if (item.classList.contains('is-rowadd-tool')) return;
69624
- if (item.classList.contains('is-col-tool')) return;
69655
+ columnsOf(col.parentNode).map(item => {
69625
69656
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69626
69657
 
69627
69658
  if (item === col) return;
@@ -69651,10 +69682,7 @@ class Resize {
69651
69682
  },
69652
69683
  resizeEnd: () => {
69653
69684
  let numOfCols = 1;
69654
- Array.from(col.parentNode.children).map(item => {
69655
- if (item.classList.contains('is-row-tool')) return;
69656
- if (item.classList.contains('is-rowadd-tool')) return;
69657
- if (item.classList.contains('is-col-tool')) return;
69685
+ columnsOf(col.parentNode).map(item => {
69658
69686
  if (item === col) return;
69659
69687
  numOfCols++;
69660
69688
  });
@@ -69666,13 +69694,9 @@ class Resize {
69666
69694
  // adjust others
69667
69695
  const maxWidth = () => {
69668
69696
  let otherWidth = 0;
69669
- Array.from(col.parentNode.children).map(item => {
69670
- if (item.classList.contains('is-row-tool')) return;
69671
- if (item.classList.contains('is-rowadd-tool')) return;
69672
- if (item.classList.contains('is-col-tool')) return;
69697
+ columnsOf(col.parentNode).map(item => {
69673
69698
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69674
69699
 
69675
- if (item.classList.contains('row-tool')) return;
69676
69700
  if (item === col) return;
69677
69701
  otherWidth += item.offsetWidth;
69678
69702
  });
@@ -69684,10 +69708,7 @@ class Resize {
69684
69708
  // percentage = (max / row.offsetWidth) * 100;
69685
69709
  // Or make others auto
69686
69710
 
69687
- Array.from(col.parentNode.children).map(item => {
69688
- if (item.classList.contains('is-row-tool')) return;
69689
- if (item.classList.contains('is-rowadd-tool')) return;
69690
- if (item.classList.contains('is-col-tool')) return;
69711
+ columnsOf(col.parentNode).map(item => {
69691
69712
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69692
69713
 
69693
69714
  if (item === col) return;
@@ -69717,10 +69738,7 @@ class Resize {
69717
69738
  // New: Final fix (if column resized exceeds its max)
69718
69739
  let othersWidth = 0;
69719
69740
  let activeColumnWidth = 0;
69720
- Array.from(col.parentNode.children).map(item => {
69721
- if (item.classList.contains('is-row-tool')) return;
69722
- if (item.classList.contains('is-rowadd-tool')) return;
69723
- if (item.classList.contains('is-col-tool')) return;
69741
+ columnsOf(col.parentNode).map(item => {
69724
69742
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69725
69743
 
69726
69744
  // // Refresh Module
@@ -69738,13 +69756,23 @@ class Resize {
69738
69756
  if (activeColumnWidth > maxResult) {
69739
69757
  percentage = maxResult / row.offsetWidth * 100;
69740
69758
  }
69741
- col.style.width = percentage + '%';
69759
+
69760
+ // The column is carrying a pixel width at this point (set
69761
+ // just above, to measure it). A width is only ever stored as
69762
+ // a percentage, so if the maths produced something CSS will
69763
+ // reject — negative, zero, NaN, Infinity — the assignment
69764
+ // would silently fail and leave those pixels behind, which
69765
+ // then breaks the layout on smaller screens. Fall back to
69766
+ // the column's real share of the row instead.
69767
+ if (!isFinite(percentage) || percentage <= 0) {
69768
+ percentage = col.offsetWidth / row.offsetWidth * 100;
69769
+ }
69770
+ if (isFinite(percentage) && percentage > 0) {
69771
+ col.style.width = percentage + '%';
69772
+ }
69742
69773
 
69743
69774
  // Refresh Module
69744
- Array.from(col.parentNode.children).map(item => {
69745
- if (item.classList.contains('is-row-tool')) return;
69746
- if (item.classList.contains('is-rowadd-tool')) return;
69747
- if (item.classList.contains('is-col-tool')) return;
69775
+ columnsOf(col.parentNode).map(item => {
69748
69776
  if (item.getAttribute('data-html')) {
69749
69777
  let moduleWidth = item.offsetWidth / row.offsetWidth * 100;
69750
69778
  item.style.width = moduleWidth + '%';
@@ -69756,10 +69784,7 @@ class Resize {
69756
69784
  if (col.style.height) {
69757
69785
  col.style.minHeight = col.style.height;
69758
69786
  col.style.height = '';
69759
- Array.from(col.parentNode.children).map(item => {
69760
- if (item.classList.contains('is-row-tool')) return;
69761
- if (item.classList.contains('is-rowadd-tool')) return;
69762
- if (item.classList.contains('is-col-tool')) return;
69787
+ columnsOf(col.parentNode).map(item => {
69763
69788
  // if(this.opts.filterSibling) if(item.classList.contains(this.opts.filterSibling)) return;
69764
69789
 
69765
69790
  if (item === col) return;
@@ -69779,6 +69804,10 @@ class Resize {
69779
69804
  });
69780
69805
  }
69781
69806
  destroy() {
69807
+ if (this.eventTarget && this.normalizeToPercent) {
69808
+ this.eventTarget.removeEventListener('mouseup', this.normalizeToPercent, true);
69809
+ this.eventTarget.removeEventListener('touchend', this.normalizeToPercent, true);
69810
+ }
69782
69811
  this.resize.destroy();
69783
69812
  }
69784
69813
  }
@@ -84958,20 +84987,6 @@ class ContentBuilder {
84958
84987
  // force (and the option itself) disappears at the final step.
84959
84988
  this.opts.engine = 'inscribe';
84960
84989
  this.engine = 'inscribe';
84961
- if (!this.opts.uploadFile && !this.opts.upload) {
84962
- this.opts.uploadFile = async e => {
84963
- const selectedFile = e.target.files[0];
84964
- const reader = new FileReader();
84965
- const result = await new Promise((resolve, reject) => {
84966
- reader.onload = () => resolve(reader.result);
84967
- reader.onerror = reject;
84968
- reader.readAsDataURL(selectedFile);
84969
- });
84970
- return {
84971
- url: result
84972
- };
84973
- };
84974
- }
84975
84990
  if (this.opts.uploadFile) {
84976
84991
  const uploadHandler = async e => {
84977
84992
  const data = await this.opts.uploadFile(e);
@@ -85172,6 +85187,26 @@ class ContentBuilder {
85172
85187
  this.opts.onLargerImageUpload = this.opts.onImageUpload;
85173
85188
  }
85174
85189
 
85190
+ // Last-resort uploader for a builder configured without one (the demo
85191
+ // and no-backend setups): embed the picked file as a data URL. Filled
85192
+ // in per upload type, and only where nothing else provided a handler,
85193
+ // so it can never overwrite `upload`, `uploadFile`, or a legacy
85194
+ // per-type callback — overwriting those turned every upload into a
85195
+ // silent base64 embed instead of calling the host app's uploader.
85196
+ const embedAsDataUrl = async e => {
85197
+ const selectedFile = e.target.files[0];
85198
+ const reader = new FileReader();
85199
+ const result = await new Promise((resolve, reject) => {
85200
+ reader.onload = () => resolve(reader.result);
85201
+ reader.onerror = reject;
85202
+ reader.readAsDataURL(selectedFile);
85203
+ });
85204
+ this.returnUrl(result);
85205
+ };
85206
+ ['onImageUpload', 'onLargerImageUpload', 'onVideoUpload', 'onAudioUpload', 'onMediaUpload', 'onFileUpload'].forEach(name => {
85207
+ if (!this.opts[name]) this.opts[name] = embedAsDataUrl;
85208
+ });
85209
+
85175
85210
  // useButtonPlugin
85176
85211
  if (this.opts.emailMode) {
85177
85212
  this.useButtonPlugin = true;
@@ -90674,7 +90709,11 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90674
90709
  handleCellKeydown(col, e) {
90675
90710
  if (e.which === 46 || e.which === 8) {
90676
90711
  //delete or backspace
90677
- if (this.activeIcon) {
90712
+ // Only when the selected icon is still live AND belongs to THIS
90713
+ // column: a stale activeIcon (already deleted, or left over from
90714
+ // another column) would otherwise swallow the key — cancelling the
90715
+ // delete without removing anything, or throwing on a detached node.
90716
+ if (this.activeIcon && this.activeIcon.isConnected && col.contains(this.activeIcon)) {
90678
90717
  // Delete icon
90679
90718
  if (this.activeIcon.parentNode.tagName.toLowerCase() === 'a' && this.activeIcon.parentNode.innerText.trim() === '') {
90680
90719
  let link = this.activeIcon.parentNode;
@@ -90762,46 +90801,56 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90762
90801
  }
90763
90802
  }
90764
90803
  }
90765
- if (e.keyCode === 46) {
90766
- // console.log("delete");
90767
- let curr;
90804
+
90805
+ // LEGACY empty-block guards for Delete/Backspace (module/custom-code
90806
+ // columns only). Inscribe owns block joining in its regions
90807
+ // (_joinBack/_joinFwd + ensureBlocks), so running these there is both
90808
+ // unnecessary and harmful: the Delete branch mutates the DOM behind
90809
+ // Inscribe's back (bypassing its history) and the Backspace branch
90810
+ // cancels the key outright.
90811
+ //
90812
+ // Both read the caret from `this.win` — the CONTENT window — not the
90813
+ // global `window`, and only act when the caret really is inside this
90814
+ // column. Reading the global selection was the cause of "Backspace
90815
+ // stopped working": with the editor in an iframe (or after a click
90816
+ // landed a caret in the editor UI), the global selection points at
90817
+ // some unrelated node whose textContent is '' and which has no
90818
+ // previousElementSibling, so EVERY Backspace was preventDefault()ed —
90819
+ // typing kept working, and only a page reload cleared it.
90820
+ const legacyDeleteGuard = !this.inscribeRegionAt(col, e.target);
90821
+ if (legacyDeleteGuard && (e.keyCode === 46 || e.keyCode === 8)) {
90822
+ let curr = null;
90768
90823
  try {
90769
- if (window.getSelection) {
90770
- curr = window.getSelection().getRangeAt(0).commonAncestorContainer;
90771
- } else if (document.selection) {
90772
- curr = document.selection.createRange();
90773
- }
90774
- if (curr.innerHTML === '<br>') {
90775
- let next = curr.nextElementSibling;
90776
- if (next) {
90777
- curr.parentNode.removeChild(curr); //without this, empty P, H1, H2 (contains only <br />) cannot be deleted (when there is another P below)
90778
- // Of course, we can backspace from the P below, but the formatting will change.
90779
- e.preventDefault();
90824
+ const sel = this.win.getSelection ? this.win.getSelection() : null;
90825
+ if (sel && sel.rangeCount) curr = sel.getRangeAt(0).commonAncestorContainer;
90826
+ } catch (err) {
90827
+ curr = null;
90828
+ }
90829
+ if (curr && col.contains(curr)) {
90830
+ if (e.keyCode === 46) {
90831
+ if (curr.innerHTML === '<br>') {
90832
+ let next = curr.nextElementSibling;
90833
+ if (next) {
90834
+ curr.parentNode.removeChild(curr); //without this, empty P, H1, H2 (contains only <br />) cannot be deleted (when there is another P below)
90835
+ // Of course, we can backspace from the P below, but the formatting will change.
90836
+ e.preventDefault();
90837
+ }
90780
90838
  }
90781
90839
  }
90782
- } catch (e) {
90783
- // Do Nothing
90784
- }
90785
- }
90786
- if (e.keyCode === 8) {
90787
- // delete on Mac
90788
- let curr;
90789
- try {
90790
- if (window.getSelection) {
90791
- curr = window.getSelection().getRangeAt(0).commonAncestorContainer;
90792
- } else if (document.selection) {
90793
- curr = document.selection.createRange();
90794
- }
90795
- if (curr.textContent === '') {
90796
- let prev = curr.previousElementSibling;
90797
- if (!prev) {
90798
- e.preventDefault(); //Without this, empty P, H1, H2 (that doesn't have prev element) will be lost => can make empty column.
90840
+ if (e.keyCode === 8) {
90841
+ // delete on Mac
90842
+ // Element nodes only: a text node has no previousElementSibling,
90843
+ // so an empty text node under the caret would always cancel the key.
90844
+ if (curr.nodeType === 1 && curr.textContent === '') {
90845
+ let prev = curr.previousElementSibling;
90846
+ if (!prev) {
90847
+ e.preventDefault(); //Without this, empty P, H1, H2 (that doesn't have prev element) will be lost => can make empty column.
90848
+ }
90799
90849
  }
90800
90850
  }
90801
- } catch (e) {
90802
- // Do Nothing
90803
90851
  }
90804
90852
  }
90853
+
90805
90854
  if (e.which === 9 && !e.shiftKey) {
90806
90855
  // tab key pressed
90807
90856
  const activeCol = this.activeCol;
@@ -90812,7 +90861,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90812
90861
  if (target) target.click();
90813
90862
  // if(target.firstElementChild) target.firstElementChild.click();
90814
90863
 
90815
- let selection = window.getSelection();
90864
+ let selection = this.win.getSelection();
90816
90865
  let range = this.createRange(target, {
90817
90866
  count: 0
90818
90867
  });
@@ -90836,7 +90885,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90836
90885
  if (firstCol) firstCol.click();
90837
90886
  // if(firstCol.firstElementChild) firstCol.firstElementChild.click();
90838
90887
 
90839
- let selection = window.getSelection();
90888
+ let selection = this.win.getSelection();
90840
90889
  let range = this.createRange(firstCol, {
90841
90890
  count: 0
90842
90891
  });
@@ -90865,7 +90914,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90865
90914
  const target = activeCol.previousElementSibling;
90866
90915
  if (target) target.click();
90867
90916
  if (target.firstElementChild) target.firstElementChild.click();
90868
- let selection = window.getSelection();
90917
+ let selection = this.win.getSelection();
90869
90918
  let range = this.createRange(target, {
90870
90919
  count: 0
90871
90920
  });
@@ -90888,7 +90937,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90888
90937
  const target = divs[divs.length - 1];
90889
90938
  if (target) target.click();
90890
90939
  if (target.firstElementChild) target.firstElementChild.click();
90891
- let selection = window.getSelection();
90940
+ let selection = this.win.getSelection();
90892
90941
  let range = this.createRange(target, {
90893
90942
  count: 0
90894
90943
  });
@@ -90957,7 +91006,7 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
90957
91006
  }
90958
91007
  createRange(node, chars, range) {
90959
91008
  if (!range) {
90960
- range = document.createRange();
91009
+ range = this.doc.createRange();
90961
91010
  range.selectNode(node);
90962
91011
  range.setStart(node, 0);
90963
91012
  }