@innovastudio/contentbuilder 1.5.48 → 1.5.50

Sign up to get free protection for your applications and to get access to all the features.
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 = [];