@innovastudio/contentbuilder 1.5.48 → 1.5.50

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
  "name": "@innovastudio/contentbuilder",
3
3
  "type": "module",
4
- "version": "1.5.48",
4
+ "version": "1.5.50",
5
5
  "description": "",
6
6
  "main": "public/contentbuilder/contentbuilder.esm.js",
7
7
  "types": "index.d.ts",
@@ -3711,6 +3711,24 @@ class Dom {
3711
3711
  constructor(builder) {
3712
3712
  this.builder = builder;
3713
3713
  }
3714
+ detectMobileOrTablet() {
3715
+ const userAgent = navigator.userAgent.toLowerCase();
3716
+ const isIOS = /ipad|iphone|ipod/.test(userAgent) && !window.MSStream;
3717
+ const isAndroid = /android/.test(userAgent);
3718
+ const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
3719
+ const screenWidth = window.innerWidth <= 1600;
3720
+ if (isIOS || isAndroid) {
3721
+ return true; // Definitely a mobile or tablet
3722
+ }
3723
+
3724
+ // Additional check for other touch devices with screen width under 1600px
3725
+ if (isTouchDevice && screenWidth) {
3726
+ return true; // Likely a mobile or tablet
3727
+ }
3728
+
3729
+ return false; // Likely not a mobile or tablet
3730
+ }
3731
+
3714
3732
  getScale(container) {
3715
3733
  let matrix = window.getComputedStyle(container).transform;
3716
3734
  if (matrix === 'none') return 1;
@@ -17223,7 +17241,26 @@ const renderSnippetPanel = (builder, snippetOpen) => {
17223
17241
 
17224
17242
  let activeBuilderArea;
17225
17243
  let itemHeight;
17226
- new Sortable(snippetlist, {
17244
+ const isMobile = dom.detectMobileOrTablet();
17245
+ let snippetid;
17246
+ let useClick = false;
17247
+ if (isMobile && builder.isContentBox) {
17248
+ const items = snippetlist.querySelectorAll('.snippet-item');
17249
+ items.forEach(item => {
17250
+ item.addEventListener('touchstart', () => {
17251
+ snippetid = item.getAttribute('data-id');
17252
+ }, {
17253
+ passive: false
17254
+ });
17255
+ item.addEventListener('touchend', () => {
17256
+ builder.dropSnippet(snippetid);
17257
+ }, {
17258
+ passive: false
17259
+ });
17260
+ });
17261
+ useClick = true;
17262
+ }
17263
+ if (!useClick) new Sortable(snippetlist, {
17227
17264
  // forceFallback: safariAgent,
17228
17265
  group: {
17229
17266
  name: 'shared',
@@ -66679,6 +66716,7 @@ class Rte {
66679
66716
  btnFront.forEach(btn => {
66680
66717
  btn.addEventListener('click', () => {
66681
66718
  let activeBlock = this.builder.doc.querySelector('.is-block.cloned');
66719
+ if (!activeBlock) activeBlock = this.builder.doc.querySelector('.is-block.active');
66682
66720
  if (!activeBlock) return;
66683
66721
  this.builder.forward(activeBlock);
66684
66722
  });
@@ -66687,6 +66725,7 @@ class Rte {
66687
66725
  btnBackward.forEach(btn => {
66688
66726
  btn.addEventListener('click', () => {
66689
66727
  let activeBlock = this.builder.doc.querySelector('.is-block.cloned');
66728
+ if (!activeBlock) activeBlock = this.builder.doc.querySelector('.is-block.active');
66690
66729
  if (!activeBlock) return;
66691
66730
  if (activeBlock.style.zIndex === '0') {
66692
66731
  this.builder.moveUp(activeBlock);
@@ -66714,6 +66753,7 @@ class Rte {
66714
66753
  btnDuplicate.forEach(btn => {
66715
66754
  btn.addEventListener('click', () => {
66716
66755
  let activeBlock = this.builder.doc.querySelector('.is-block.cloned');
66756
+ if (!activeBlock) activeBlock = this.builder.doc.querySelector('.is-block.active');
66717
66757
  if (!activeBlock) return;
66718
66758
  this.builder.duplicate(activeBlock);
66719
66759
  });
@@ -83277,6 +83317,7 @@ class Draggable {
83277
83317
  passive: false
83278
83318
  });
83279
83319
  this.doc.addEventListener('keydown', this.handleKeyDown);
83320
+ if (this.doc !== document) document.addEventListener('keydown', this.handleKeyDown);
83280
83321
  }
83281
83322
  refresh() {
83282
83323
  this.elements = this.doc.querySelectorAll(this.selector);
@@ -83302,6 +83343,7 @@ class Draggable {
83302
83343
  this.doc.removeEventListener('mousedown', this.handleSelect);
83303
83344
  this.doc.removeEventListener('touchstart', this.handleSelect);
83304
83345
  this.doc.removeEventListener('keydown', this.handleKeyDown);
83346
+ if (this.doc !== document) document.removeEventListener('keydown', this.handleKeyDown);
83305
83347
  const blocks = this.doc.querySelectorAll(this.selector);
83306
83348
  blocks.forEach(elm => elm.classList.remove('active'));
83307
83349
  }
@@ -83612,14 +83654,18 @@ class Draggable {
83612
83654
  let blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active') && elm.classList.contains('editable'));
83613
83655
  if (blocks.length > 0) return;
83614
83656
  if (this.onBeforeChange) this.onBeforeChange();
83657
+ let isDeleted = false;
83615
83658
  blocks = Array.from(this.doc.querySelectorAll(this.selector)).filter(elm => elm.classList.contains('active'));
83616
83659
  blocks.forEach(element => {
83617
83660
  element.removeEventListener('mousedown', this.handleDragStart);
83618
83661
  element.removeEventListener('touchstart', this.handleDragStart);
83619
83662
  if (this.onDelete) this.onDelete(element);
83620
83663
  element.parentNode.removeChild(element);
83664
+ isDeleted = true;
83621
83665
  });
83622
- if (this.onChange) this.onChange();
83666
+ if (isDeleted && this.onChange) {
83667
+ this.onChange();
83668
+ }
83623
83669
  }
83624
83670
  handleKeyDown(event) {
83625
83671
  if (event.key === 'Delete' || event.key === 'Backspace' || event.keyCode === 46) {
@@ -83995,7 +84041,8 @@ class EditableBlocks {
83995
84041
  }
83996
84042
  if (block.classList.contains('is-group')) return; // do not clone if block is shape
83997
84043
 
83998
- if (!block.classList.contains('clone')) {
84044
+ const isMobileOrTablet = this.detectMobileOrTablet();
84045
+ if (!block.classList.contains('clone') & !isMobileOrTablet) {
83999
84046
  let clonedDiv = block.cloneNode(true);
84000
84047
  clonedDiv.classList.add('clone');
84001
84048
  block.parentNode.appendChild(clonedDiv);
@@ -84003,6 +84050,24 @@ class EditableBlocks {
84003
84050
  this.refresh();
84004
84051
  }
84005
84052
  }
84053
+ detectMobileOrTablet() {
84054
+ const userAgent = navigator.userAgent.toLowerCase();
84055
+ const isIOS = /ipad|iphone|ipod/.test(userAgent) && !window.MSStream;
84056
+ const isAndroid = /android/.test(userAgent);
84057
+ const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
84058
+ const screenWidth = window.innerWidth <= 1600;
84059
+ if (isIOS || isAndroid) {
84060
+ return true; // Definitely a mobile or tablet
84061
+ }
84062
+
84063
+ // Additional check for other touch devices with screen width under 1600px
84064
+ if (isTouchDevice && screenWidth) {
84065
+ return true; // Likely a mobile or tablet
84066
+ }
84067
+
84068
+ return false; // Likely not a mobile or tablet
84069
+ }
84070
+
84006
84071
  selectClear() {
84007
84072
  this.doc.querySelectorAll('.clone').forEach(elm => elm.parentNode.removeChild(elm));
84008
84073
  this.doc.querySelectorAll('.cloned').forEach(elm => elm.classList.remove('cloned'));
@@ -92316,6 +92381,224 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
92316
92381
  // return currentScript.replace(currentScriptFile, '');
92317
92382
  }
92318
92383
 
92384
+ dropSnippet(snippetid) {
92385
+ // snippetJSON is snippet's JSON (from assets/minimalist-blocks/content.js) that store all snippets' html
92386
+ const result = this.opts.snippetJSON.snippets.filter(item => {
92387
+ if (item.id + '' === snippetid) return item;else return false;
92388
+ });
92389
+ let html = result[0].html;
92390
+ let noedit = result[0].noedit;
92391
+ let html2 = '';
92392
+ if (result[0].mode === 'canvas') {
92393
+ html2 = result[0].html2;
92394
+ if (!html2) {
92395
+ html2 = `
92396
+ <div class="row">
92397
+ <div class="column pt-0 pb-0 pl-0 pr-0 flex flex-col justify-center">
92398
+ <img src="${this.opts.snippetPath}images/img-2400x1350.png" alt="">
92399
+ </div>
92400
+ </div>
92401
+ `;
92402
+ }
92403
+ }
92404
+ let bSnippet;
92405
+ if (html.indexOf('"row') === -1) {
92406
+ bSnippet = true; // Just snippet (without row/column grid)
92407
+ } else {
92408
+ bSnippet = false; // Snippet is wrapped in row/colum
92409
+ }
92410
+
92411
+ if (this.opts.emailMode) bSnippet = false;
92412
+
92413
+ // check if is block
92414
+ let isBlock = false;
92415
+ if (html.includes('"is-block')) {
92416
+ isBlock = true;
92417
+ bSnippet = false;
92418
+ }
92419
+
92420
+ // Convert snippet into your defined 12 columns grid
92421
+ let rowClass = this.opts.row; //row
92422
+ let colClass = this.opts.cols; //['col s1', 'col s2', 'col s3', 'col s4', 'col s5', 'col s6', 'col s7', 'col s8', 'col s9', 'col s10', 'col s11', 'col s12']
92423
+ if (rowClass !== '' && colClass.length === 12) {
92424
+ // html = html.replace(new RegExp('row clearfix', 'g'), rowClass);
92425
+ html = html.replace(new RegExp('row clearfix', 'g'), 'row'); // backward
92426
+ html = html.replace(new RegExp('"row', 'g'), '"' + rowClass);
92427
+ html = html.replace(new RegExp('column full', 'g'), colClass[11]);
92428
+ html = html.replace(new RegExp('column half', 'g'), colClass[5]);
92429
+ html = html.replace(new RegExp('column third', 'g'), colClass[3]);
92430
+ html = html.replace(new RegExp('column fourth', 'g'), colClass[2]);
92431
+ html = html.replace(new RegExp('column fifth', 'g'), colClass[1]);
92432
+ html = html.replace(new RegExp('column sixth', 'g'), colClass[1]);
92433
+ html = html.replace(new RegExp('column two-third', 'g'), colClass[7]);
92434
+ html = html.replace(new RegExp('column two-fourth', 'g'), colClass[8]);
92435
+ html = html.replace(new RegExp('column two-fifth', 'g'), colClass[9]);
92436
+ html = html.replace(new RegExp('column two-sixth', 'g'), colClass[9]);
92437
+ }
92438
+ html = html.replace(/{id}/g, this.util.makeId());
92439
+ if (this.opts.onAdd) {
92440
+ html = this.opts.onAdd(html);
92441
+ }
92442
+ if (this.opts.snippetPathReplace.length > 0) {
92443
+ // try {
92444
+ if (this.opts.snippetPathReplace[0] !== '') {
92445
+ let regex = new RegExp(this.opts.snippetPathReplace[0], 'g');
92446
+ html = html.replace(regex, this.opts.snippetPathReplace[1]);
92447
+ let string1 = this.opts.snippetPathReplace[0].replace(/\//g, '%2F');
92448
+ let string2 = this.opts.snippetPathReplace[1].replace(/\//g, '%2F');
92449
+ let regex2 = new RegExp(string1, 'g');
92450
+ html = html.replace(regex2, string2);
92451
+ }
92452
+ // } catch (e) { 1; }
92453
+ }
92454
+
92455
+ // this.addSnippet(html, bSnippet, noedit);
92456
+
92457
+ if (bSnippet) {
92458
+ // Just snippet (without row/column grid), ex. buttons, line, social, video, map.
92459
+ // Can be inserted after current row, column (cell), element, or last row.
92460
+
92461
+ html = `<div class="${this.opts.row}"><div class="${this.opts.cols[this.opts.cols.length - 1]}"${noedit ? ' data-noedit' : ''}>${html}</div></div>`;
92462
+ } else if (isBlock) ; else {
92463
+ // Snippet is wrapped in row/colum (may contain custom code or has [data-html] attribute)
92464
+ // Can only be inserted after current row or last row (not on column or element).
92465
+
92466
+ let snippet = this.dom.createElement('div');
92467
+ snippet.innerHTML = html;
92468
+ let blocks = snippet.querySelectorAll('[data-html]');
92469
+ Array.prototype.forEach.call(blocks, block => {
92470
+ // Render custom code block
92471
+ html = decodeURIComponent(block.getAttribute('data-html'));
92472
+ html = html.replace(/{id}/g, this.util.makeId());
92473
+ html = html.replace(/<script>/g, `${this.nonce ? `<script nonce="${this.nonce}">` : '<script>'}`);
92474
+ for (var i = 1; i <= 20; i++) {
92475
+ html = html.replace('[%HTML' + i + '%]', block.getAttribute('data-html-' + i) === undefined ? '' : decodeURIComponent(block.getAttribute('data-html-' + i))); //render editable area
92476
+ }
92477
+
92478
+ block.innerHTML = html;
92479
+ });
92480
+ html = snippet.innerHTML;
92481
+ }
92482
+ let activeBox = this.doc.querySelector('.box-select');
92483
+ if (!activeBox) activeBox = this.activeBox;
92484
+ const activeRow = this.doc.querySelector('.row-active');
92485
+ if (activeRow) {
92486
+ if (html2) html = html2; // if it is canvas block, change it to normal using html2
92487
+ this.addRow(html, activeBox);
92488
+ } else {
92489
+ if (activeBox) {
92490
+ if (activeBox.classList.contains('box-canvas')) {
92491
+ // Canvas Mode
92492
+
92493
+ let snippet = this.dom.createElement('div');
92494
+ snippet.innerHTML = html;
92495
+ let blocks = snippet.querySelectorAll('[data-html]');
92496
+ Array.prototype.forEach.call(blocks, block => {
92497
+ // Render custom code block
92498
+ html = decodeURIComponent(block.getAttribute('data-html'));
92499
+ html = html.replace(/{id}/g, this.util.makeId());
92500
+ html = html.replace(/<script>/g, `${this.nonce ? `<script nonce="${this.nonce}">` : '<script>'}`);
92501
+ for (var i = 1; i <= 20; i++) {
92502
+ html = html.replace('[%HTML' + i + '%]', block.getAttribute('data-html-' + i) === undefined ? '' : decodeURIComponent(block.getAttribute('data-html-' + i))); //render editable area
92503
+ }
92504
+
92505
+ block.innerHTML = html;
92506
+ });
92507
+ html = snippet.innerHTML;
92508
+ this.uo.saveForUndo();
92509
+ if (isBlock) {
92510
+ this.eb.addBlock(html, activeBox);
92511
+ } else {
92512
+ const blockTemplate = `
92513
+ <div class="is-block block-steady height-auto" data-new-dummy="1" style="top: 20%; left: 20%; width: 760px;">
92514
+ <div class="is-container container-new size-18 leading-14">
92515
+ [%CONTENT%]
92516
+ </div>
92517
+ </div>
92518
+ `; // data-new-dummy will be used by onSort to apply top/left position (snippetpanel.js)
92519
+ // html = blockTemplate.replace('[%CONTENT%]', html);
92520
+
92521
+ this.eb.addBlock(blockTemplate, activeBox);
92522
+ }
92523
+ const builders = activeBox.querySelectorAll('.is-container.container-new');
92524
+ builders.forEach(builder => {
92525
+ // After snippet has been added, re-apply behavior on builder areas
92526
+
92527
+ var range = document.createRange();
92528
+ range.setStart(builder, 0);
92529
+ builder.appendChild(range.createContextualFragment(html));
92530
+ this.applyBehaviorOn(builder);
92531
+ builder.classList.remove('container-new');
92532
+ });
92533
+ this.opts.onChange();
92534
+ this.opts.onRender();
92535
+ if (this.opts.onBlockCanvasAdd) this.opts.onBlockCanvasAdd();
92536
+ } else {
92537
+ let container = activeBox.querySelector('.builder-active');
92538
+ if (container) {
92539
+ if (html2) html = html2;
92540
+ this.addRow(html, activeBox);
92541
+ } else {
92542
+ container = activeBox.querySelector('.is-container');
92543
+ if (container) {
92544
+ if (html2) html = html2;
92545
+ this.addRow(html, activeBox);
92546
+ }
92547
+ }
92548
+ }
92549
+ }
92550
+ }
92551
+ this.activeCol = null;
92552
+ }
92553
+ addRow(html, box) {
92554
+ this.uo.saveForUndo();
92555
+ let rowElement;
92556
+ let bAddLast = false;
92557
+
92558
+ // Add after selected row
92559
+ let cell = this.activeCol;
92560
+ let row;
92561
+ if (cell) {
92562
+ row = cell.parentNode; // in email mode, cell active is also under row active (incorrect, but cell active is not needed in email mode. So this line works!)
92563
+ } else {
92564
+ // If no active cell, check if it is from .row-add-initial (empty info)
92565
+ row = this.doc.querySelector('.row-active');
92566
+ if (!row) {
92567
+ bAddLast = true;
92568
+ }
92569
+ }
92570
+ // Add after last row
92571
+ if (bAddLast) {
92572
+ const container = box.querySelector('.is-builder');
92573
+ const rows = this.dom.elementChildren(container);
92574
+ const lastrow = rows[rows.length - 1];
92575
+ row = lastrow;
92576
+ }
92577
+
92578
+ // Use createContextualFragment() to make embedded script executable
92579
+ let range = document.createRange();
92580
+ row.parentNode.insertBefore(range.createContextualFragment(html), row.nextSibling);
92581
+ // rowElement = snippet.childNodes[0];
92582
+
92583
+ rowElement = row.nextElementSibling; // a must. Must be before applyBehavior to prevent element delete during fixLayout
92584
+
92585
+ // checkEmpty & onRender called here
92586
+ let builderActive = box.querySelector('.builder-active');
92587
+ if (builderActive) this.applyBehaviorOn(builderActive);else {
92588
+ builderActive = box.querySelector('.is-builder');
92589
+ if (builderActive) this.applyBehaviorOn(builderActive);
92590
+ }
92591
+ let cellElement = rowElement.querySelector('div');
92592
+ if (cellElement) cellElement.click(); //change active block to the newly created
92593
+
92594
+ // Change to row selection
92595
+ rowElement.className = rowElement.className.replace('row-outline', '');
92596
+
92597
+ //Hide Column tool (new!)
92598
+ this.util.hideColumnTool();
92599
+ this.opts.onChange();
92600
+ this.opts.onRender();
92601
+ }
92319
92602
  sectionDropSetup() {
92320
92603
  if (this.blockContainer) {
92321
92604
  this.sortableOnCanvas = [];