@innovastudio/contentbox 1.6.108 → 1.6.110

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.
@@ -11531,34 +11531,38 @@ class PanelImage {
11531
11531
 
11532
11532
  let html = `
11533
11533
  <div class="submain">
11534
- <div class="asset-preview"></div>
11535
11534
 
11536
- <label class="label mt-3">
11537
- <div>${out('Source')}:</div>
11538
- <input type="text" class="inp-src" id="_inp_src_${this.random()}">
11539
- </label>
11535
+ <div class="div-image-settings">
11540
11536
 
11541
- <div class="group mt-4">
11542
- <input class="inp-file" type="file" accept="image/*" style="display:none"/>
11543
- <button title="${out('Upload')}" class="btn-upload"><svg><use xlink:href="#icon-upload"></use></svg></button>
11544
- <button title="${out('Select')}" class="btn-asset"><svg><use xlink:href="#icon-folder"></use></svg></button>
11545
- <button title="${out('Link')}" class="btn-link"><svg style="transform:rotate(45deg)"><use xlink:href="#icon-link"></use></svg></button>
11546
- <button title="${out('Edit')}" class="btn-edit"><svg><use xlink:href="#icon-pencil"></use></svg></button>
11547
- </div>
11537
+ <div class="asset-preview"></div>
11548
11538
 
11549
- <!--
11550
- <div class="group">
11551
- <button title="${out('Align Left')}" data-align="left"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-left"></use></svg></button>
11552
- <button title="${out('Align Center')}" data-align="center"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-center"></use></svg></button>
11553
- <button title="${out('Align Right')}" data-align="right"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-right"></use></svg></button>
11554
- <button title="${out('Align Full')}" data-align="justify"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-full"></use></svg></button>
11555
- </div>
11556
- -->
11539
+ <label class="label mt-3">
11540
+ <div>${out('Source')}:</div>
11541
+ <input type="text" class="inp-src" id="_inp_src_${this.random()}">
11542
+ </label>
11557
11543
 
11558
- <label class="label mt-2">
11559
- <div>${out('Title')}:</div>
11560
- <input type="text" class="inp-title" id="_inp_title_${this.random()}">
11561
- </label>
11544
+ <div class="group mt-4">
11545
+ <input class="inp-file" type="file" accept="image/*" style="display:none"/>
11546
+ <button title="${out('Upload')}" class="btn-upload"><svg><use xlink:href="#icon-upload"></use></svg></button>
11547
+ <button title="${out('Select')}" class="btn-asset"><svg><use xlink:href="#icon-folder"></use></svg></button>
11548
+ <button title="${out('Link')}" class="btn-link"><svg style="transform:rotate(45deg)"><use xlink:href="#icon-link"></use></svg></button>
11549
+ <button title="${out('Edit')}" class="btn-edit"><svg><use xlink:href="#icon-pencil"></use></svg></button>
11550
+ </div>
11551
+
11552
+ <!--
11553
+ <div class="group">
11554
+ <button title="${out('Align Left')}" data-align="left"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-left"></use></svg></button>
11555
+ <button title="${out('Align Center')}" data-align="center"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-center"></use></svg></button>
11556
+ <button title="${out('Align Right')}" data-align="right"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-right"></use></svg></button>
11557
+ <button title="${out('Align Full')}" data-align="justify"><svg style="width:13px;height:13px;"><use xlink:href="#icon-align-full"></use></svg></button>
11558
+ </div>
11559
+ -->
11560
+
11561
+ <label class="label mt-2">
11562
+ <div>${out('Title')}:</div>
11563
+ <input type="text" class="inp-title" id="_inp_title_${this.random()}">
11564
+ </label>
11565
+ </div>
11562
11566
  </div>
11563
11567
  `;
11564
11568
  panel.insertAdjacentHTML('beforeend', html);
@@ -11667,6 +11671,14 @@ class PanelImage {
11667
11671
  inpSrc.value = 'image/jpeg (base64)';
11668
11672
  }
11669
11673
 
11674
+ const divSettings = panel.querySelector('.div-image-settings');
11675
+
11676
+ if (img.closest('[data-html]')) {
11677
+ divSettings.style.display = 'none';
11678
+ } else {
11679
+ divSettings.style.display = '';
11680
+ }
11681
+
11670
11682
  const inpTitle = panel.querySelector('.inp-title');
11671
11683
  inpTitle.value = alt;
11672
11684
  }
@@ -11713,28 +11725,32 @@ class PanelVideo {
11713
11725
  this.builder = builder;
11714
11726
  let html = `
11715
11727
  <div class="submain">
11716
- <div class="asset-preview"></div>
11717
11728
 
11718
- <label class="label mt-3">
11719
- <div>${out('Source')}:</div>
11720
- <input type="text" class="inp-src">
11721
- </label>
11729
+ <div class="div-video-settings">
11722
11730
 
11723
- <div class="group mt-4">
11724
- <input class="inp-file" type="file" accept="video/mp4" style="display:none"/>
11725
- <button title="${out('Upload')}" class="btn-upload"><svg><use xlink:href="#icon-upload"></use></svg></button>
11726
- <button title="${out('Select')}" class="btn-asset"><svg><use xlink:href="#icon-folder"></use></svg></button>
11727
- </div>
11731
+ <div class="asset-preview"></div>
11732
+
11733
+ <label class="label mt-3">
11734
+ <div>${out('Source')}:</div>
11735
+ <input type="text" class="inp-src">
11736
+ </label>
11728
11737
 
11729
- <label class="label checkbox mt-4">
11730
- <input class="inp-video-controls" type="checkbox" /> <span>${out('Show Controls')}</span>
11731
- </label>
11732
- <label class="label checkbox mt-2">
11733
- <input class="inp-video-loop" type="checkbox" /> <span>${out('Loop')}</span>
11734
- </label>
11735
- <label class="label checkbox mt-2">
11736
- <input class="inp-video-autoplay" type="checkbox" /> <span>${out('Autoplay')}</span>
11737
- </label>
11738
+ <div class="group mt-4">
11739
+ <input class="inp-file" type="file" accept="video/mp4" style="display:none"/>
11740
+ <button title="${out('Upload')}" class="btn-upload"><svg><use xlink:href="#icon-upload"></use></svg></button>
11741
+ <button title="${out('Select')}" class="btn-asset"><svg><use xlink:href="#icon-folder"></use></svg></button>
11742
+ </div>
11743
+
11744
+ <label class="label checkbox mt-4">
11745
+ <input class="inp-video-controls" type="checkbox" /> <span>${out('Show Controls')}</span>
11746
+ </label>
11747
+ <label class="label checkbox mt-2">
11748
+ <input class="inp-video-loop" type="checkbox" /> <span>${out('Loop')}</span>
11749
+ </label>
11750
+ <label class="label checkbox mt-2">
11751
+ <input class="inp-video-autoplay" type="checkbox" /> <span>${out('Autoplay')}</span>
11752
+ </label>
11753
+ </div>
11738
11754
 
11739
11755
  </div>
11740
11756
  `;
@@ -11839,7 +11855,22 @@ class PanelVideo {
11839
11855
  const src = source ? source.getAttribute('src') : '';
11840
11856
  this.updatePreview(src);
11841
11857
  const inpSrc = panel.querySelector('.inp-src');
11842
- inpSrc.value = src;
11858
+
11859
+ if (src.indexOf('base64') === -1) {
11860
+ inpSrc.value = src;
11861
+ } else {
11862
+ // inpSrc.value = '[Image Data]';
11863
+ inpSrc.value = 'video/mp4 (base64)';
11864
+ }
11865
+
11866
+ const divSettings = panel.querySelector('.div-video-settings');
11867
+
11868
+ if (video.closest('[data-html]')) {
11869
+ divSettings.style.display = 'none';
11870
+ } else {
11871
+ divSettings.style.display = '';
11872
+ }
11873
+
11843
11874
  const inpShowControls = panel.querySelector('.inp-video-controls');
11844
11875
 
11845
11876
  if (video.hasAttribute('controls')) {
@@ -12673,7 +12704,7 @@ class PanelCode {
12673
12704
  </button>
12674
12705
  </div>
12675
12706
 
12676
- <div class="group flex">
12707
+ <div class="group flex" style="width:100%">
12677
12708
  <button title="${out('Configure')}" class="btn-editmodule">
12678
12709
  <svg><use xlink:href="#icon-settings"></use></svg>
12679
12710
  <span>${out('Configure')}</span>
@@ -27271,6 +27302,14 @@ class Snippets {
27271
27302
  // }
27272
27303
 
27273
27304
  }
27305
+
27306
+
27307
+ if(!(parent._cb.win.FormViewer)){
27308
+ const result = snippets.filter((item)=>{
27309
+ return !(item.type === 'form');
27310
+ });
27311
+ snippets = [...result];
27312
+ }
27274
27313
 
27275
27314
 
27276
27315
  for (let i = 0; i <snippets.length; i++) {
@@ -42499,6 +42538,12 @@ const prepareSvgIcons = builder => {
42499
42538
  <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
42500
42539
  <path d="M5 12l14 0"></path>
42501
42540
  </symbol>
42541
+ <symbol id="icon-upload" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
42542
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
42543
+ <path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2"></path>
42544
+ <path d="M7 9l5 -5l5 5"></path>
42545
+ <path d="M12 4l0 12"></path>
42546
+ </symbol>
42502
42547
  </svg>`;
42503
42548
  builder.dom.appendHtml(builder.builderStuff, html);
42504
42549
  };
@@ -44430,6 +44475,15 @@ const renderSnippetPanel = (builder, snippetOpen) => {
44430
44475
 
44431
44476
  builder.opts.snippetJSON.snippets = [...snippets];
44432
44477
  }
44478
+
44479
+ // Exclude form snippet if FormViewer not found.
44480
+ if (!builder.win.FormViewer) {
44481
+ const result = snippets.filter(item => {
44482
+ return !(item.type === 'form');
44483
+ });
44484
+ snippets = [...result];
44485
+ }
44486
+ builder.opts.snippetJSON.snippets = [...snippets];
44433
44487
  let index = 1;
44434
44488
  if (builder.opts.emailMode) {
44435
44489
  builder.opts.snippetJSON.snippets.forEach(item => {
@@ -71888,6 +71942,106 @@ var loadImageOrientation = {exports: {}};
71888
71942
 
71889
71943
  var js$2 = loadImage.exports;
71890
71944
 
71945
+ class DragDropImageUploader {
71946
+ constructor(element, builder) {
71947
+ this.element = element;
71948
+ this.builder = builder;
71949
+ this.targetImage = null;
71950
+ this.isAttached = false;
71951
+ this._onDragOver = this._onDragOver.bind(this);
71952
+ this._onDragLeave = this._onDragLeave.bind(this);
71953
+ this._onDrop = this._onDrop.bind(this);
71954
+ }
71955
+ attach() {
71956
+ if (this.isAttached) return; // Prevent duplicate attachment
71957
+
71958
+ this.element.addEventListener('dragover', this._onDragOver);
71959
+ this.element.addEventListener('dragleave', this._onDragLeave);
71960
+ this.element.addEventListener('drop', this._onDrop);
71961
+ this.isAttached = true;
71962
+ }
71963
+ detach() {
71964
+ if (!this.isAttached) return;
71965
+ this.element.removeEventListener('dragover', this._onDragOver);
71966
+ this.element.removeEventListener('dragleave', this._onDragLeave);
71967
+ this.element.removeEventListener('drop', this._onDrop);
71968
+ this.isAttached = false;
71969
+ }
71970
+ _getImageUnderCursor(e) {
71971
+ const doc = this.element.ownerDocument || document;
71972
+ const range = doc.caretRangeFromPoint ? doc.caretRangeFromPoint(e.clientX, e.clientY) : doc.caretPositionFromPoint(e.clientX, e.clientY);
71973
+ if (!range) return null;
71974
+ const node = range.startContainer.nodeType === 1 ? range.startContainer : range.startContainer.parentElement;
71975
+ if (this.element.getAttribute('id') === 'divImageResizer') {
71976
+ return this.builder.activeImage;
71977
+ }
71978
+ return node.tagName === 'IMG' ? node : node.querySelector && node.querySelector('img');
71979
+ }
71980
+ async _onDrop(e) {
71981
+ e.preventDefault();
71982
+ if (this.targetImage) {
71983
+ this.targetImage.classList.remove('drag-over-image');
71984
+ }
71985
+ const files = e.dataTransfer.files;
71986
+ if (files && files.length > 0 && files[0].type.startsWith('image/')) {
71987
+ const file = files[0];
71988
+ const result = await this._onUpload(file);
71989
+ if (result && this.targetImage) {
71990
+ this.targetImage.setAttribute('src', result.url);
71991
+
71992
+ // finished steps:
71993
+
71994
+ // let imageProgress = document.querySelector('#divImageProgress');
71995
+ // if(imageProgress) imageProgress.style.display = 'none';
71996
+
71997
+ //Check if image is part of module snippet. If so, refresh the (active) module (hide imageTool). If not, refresh imageTool position
71998
+ this.builder.element.image.refreshIfIsModule(this.targetImage);
71999
+ this.builder.element.image.startImageMonitor(this.targetImage);
72000
+
72001
+ //Trigger Change event
72002
+ this.builder.opts.onChange();
72003
+ this.builder.elmTool.refresh();
72004
+ if (this.builder.onImageChange) this.builder.onImageChange();
72005
+ }
72006
+ }
72007
+ this.targetImage = null;
72008
+ }
72009
+ _onDragOver(e) {
72010
+ e.preventDefault();
72011
+ const img = this._getImageUnderCursor(e);
72012
+ if (img) {
72013
+ if (this.targetImage && this.targetImage !== img) {
72014
+ this.targetImage.classList.remove('drag-over-image');
72015
+ }
72016
+ this.targetImage = img;
72017
+ this.targetImage.classList.add('drag-over-image');
72018
+ }
72019
+ }
72020
+ _onDragLeave() {
72021
+ if (this.targetImage) {
72022
+ this.targetImage.classList.remove('drag-over-image');
72023
+ this.targetImage = null;
72024
+ }
72025
+ }
72026
+ _onUpload(file) {
72027
+ return new Promise(resolve => {
72028
+ this.builder.opts.onAssetUpload = url => {
72029
+ resolve({
72030
+ url: url,
72031
+ name: file.name,
72032
+ size: file.size
72033
+ });
72034
+ };
72035
+ const fakeEvent = {
72036
+ target: {
72037
+ files: [file]
72038
+ }
72039
+ };
72040
+ this.builder.opts.onImageUpload(fakeEvent);
72041
+ });
72042
+ }
72043
+ }
72044
+
71891
72045
  class Image$1 {
71892
72046
  constructor(builder) {
71893
72047
  this.builder = builder;
@@ -71929,6 +72083,15 @@ class Image$1 {
71929
72083
  this.imageTool = imageTool;
71930
72084
  imageResizer = document.querySelector('#divImageResizer');
71931
72085
  this.imageResizer = imageResizer;
72086
+
72087
+ // upload
72088
+ if (this.builder.opts.onImageUpload) {
72089
+ if (!imageResizer._dragDropUploaderAttached) {
72090
+ imageResizer._dragDropUploaderAttached = true;
72091
+ const uploader = new DragDropImageUploader(imageResizer, this.builder);
72092
+ uploader.attach();
72093
+ }
72094
+ }
71932
72095
  imageResizer.addEventListener('wheel', () => {
71933
72096
  this.hideImageResizer();
71934
72097
  }, {
@@ -72072,20 +72235,70 @@ class Image$1 {
72072
72235
 
72073
72236
  // Browse local image
72074
72237
  let elm = imageTool.querySelector('#fileEmbedImage');
72075
- dom.addEventListener(elm, 'change', e => {
72238
+ dom.addEventListener(elm, 'change', async e => {
72076
72239
  this.builder.uo.saveForUndo();
72077
72240
  var input = e.target;
72078
72241
  let img = this.builder.activeImage;
72242
+
72243
+ // content scaling
72244
+ let scale = 1;
72245
+ const contentView = document.querySelector('.is-content-view');
72246
+ if (contentView) {
72247
+ const style = window.getComputedStyle(contentView);
72248
+ const transform = style.transform;
72249
+ if (transform.startsWith('matrix(')) {
72250
+ const matrix = new DOMMatrix(transform);
72251
+ if (matrix.b === 0 && matrix.c === 0 && matrix.a === matrix.d) {
72252
+ scale = matrix.a; // only if pure scale()
72253
+ }
72254
+ }
72255
+ }
72256
+
72079
72257
  let imageProgress = builderStuff.querySelector('#divImageProgress');
72080
72258
  imageProgress.style.display = 'table';
72081
- imageProgress.style.width = img.offsetWidth * this.builder.opts.zoom + 'px';
72082
- imageProgress.style.height = img.offsetHeight * this.builder.opts.zoom + 'px';
72259
+ imageProgress.style.width = img.offsetWidth * this.builder.opts.zoom * scale + 'px';
72260
+ imageProgress.style.height = img.offsetHeight * this.builder.opts.zoom * scale + 'px';
72083
72261
  const newPos = this.builder.util.getElementPosition(img);
72084
72262
  let top = newPos.top + window.pageYOffset;
72085
72263
  let left = newPos.left + window.pageXOffset;
72086
72264
  imageProgress.style.top = top + 'px';
72087
72265
  imageProgress.style.left = left + 'px';
72088
72266
 
72267
+ // upload
72268
+ const onUpload = async e => {
72269
+ let file = e.target.files[0];
72270
+ return new Promise(resolve => {
72271
+ this.builder.opts.onAssetUpload = url => {
72272
+ resolve({
72273
+ url: url,
72274
+ name: file.name,
72275
+ size: file.size
72276
+ });
72277
+ };
72278
+ this.builder.opts.onImageUpload(e);
72279
+ });
72280
+ };
72281
+ if (this.builder.opts.onImageUpload) {
72282
+ const result = await onUpload(e);
72283
+ img.setAttribute('src', result.url);
72284
+
72285
+ // finished steps:
72286
+
72287
+ imageProgress.style.display = 'none';
72288
+ elm = imageTool.querySelector('#fileEmbedImage');
72289
+ elm.value = ''; //clear input
72290
+
72291
+ //Check if image is part of module snippet. If so, refresh the (active) module (hide imageTool). If not, refresh imageTool position
72292
+ this.refreshIfIsModule(img);
72293
+ this.startImageMonitor(img);
72294
+
72295
+ //Trigger Change event
72296
+ this.builder.opts.onChange();
72297
+ this.builder.elmTool.refresh();
72298
+ if (this.builder.onImageChange) this.builder.onImageChange();
72299
+ return;
72300
+ }
72301
+
72089
72302
  //The #fileEmbedImage triggered 2 times in IE (because of clearInputs below). This makes input.files[0].name returns error on 2nd trigger. Just add try{}!
72090
72303
  try {
72091
72304
  img.setAttribute('data-filename', input.files[0].name); //needed for saveimage.js |
@@ -72302,7 +72515,7 @@ class Image$1 {
72302
72515
  <div class="image-larger1 is-btn classic" style="position:relative;flex:none;box-shadow: 0px 3px 6px -6px rgba(0, 0, 0, 0.32);">
72303
72516
  <form class="form-upload-larger" target="frameTargetImageUpload" method="post" action="${this.builder.opts.largerImageHandler}" enctype="multipart/form-data" style="border-radius:1px;position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;">
72304
72517
  <input id="hidRefId1" name="hidRefId" type="hidden" value="" />
72305
- <svg class="is-icon-flex" style="width:18px;height:18px;"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
72518
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
72306
72519
  <input onclick="blur()" title="${util.out('Select')}" id="fileImage1" name="fileImage" type="file" accept="image/*" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;">
72307
72520
  </form>
72308
72521
 
@@ -72325,7 +72538,7 @@ class Image$1 {
72325
72538
  <div class="image-larger2 is-btn classic" style="position:relative;flex:none;box-shadow: 0px 3px 6px -6px rgba(0, 0, 0, 0.32);">
72326
72539
  <form class="form-upload-larger" target="frameTargetImageUpload" method="post" action="${this.builder.opts.fileHandler ? this.builder.opts.fileHandler : this.builder.opts.largerImageHandler}" enctype="multipart/form-data" style="border-radius:1px;position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;">
72327
72540
  <input id="hidRefId2" name="hidRefId" type="hidden" value="" />
72328
- <svg class="is-icon-flex"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
72541
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
72329
72542
  <input onclick="blur()" title="${util.out('Select')}" id="fileImage2" name="fileImage" type="file" accept="*" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;">
72330
72543
  </form>
72331
72544
  </div>
@@ -72607,9 +72820,113 @@ class Image$1 {
72607
72820
 
72608
72821
  // Apply (crop) image
72609
72822
  elm = modalImageEdit.querySelector('.input-ok');
72610
- dom.addEventListener(elm, 'click', () => {
72823
+ dom.addEventListener(elm, 'click', async () => {
72611
72824
  this.builder.uo.saveForUndo();
72612
72825
  let img = this.builder.activeImage;
72826
+ if (this.builder.opts.onImageUpload) {
72827
+ let basename, extension, newname, newfilename;
72828
+ let src = img.getAttribute('src');
72829
+ if (src.indexOf('base64') === -1) {
72830
+ let filename = src.substring(src.lastIndexOf('/') + 1);
72831
+ basename = dom.baseName(filename);
72832
+ extension = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
72833
+ } else if (img.getAttribute('data-filename')) {
72834
+ let attr = img.getAttribute('data-filename');
72835
+ basename = dom.baseName(attr);
72836
+ extension = attr.substr(attr.lastIndexOf('.') + 1).toLowerCase();
72837
+ } else {
72838
+ basename = util.makeId();
72839
+ extension = 'png';
72840
+ const match = src.match(/^data:image\/(.*?);base64,/);
72841
+ if (match) {
72842
+ extension = match[1]; // e.g., "png", "jpeg", "gif"
72843
+ }
72844
+ }
72845
+
72846
+ if (basename.indexOf('-edit') !== -1) {
72847
+ basename = basename.substr(0, basename.indexOf('-edit'));
72848
+ }
72849
+ newname = basename + '-edit' + util.makeId();
72850
+ newname = newname.replaceAll('%20', '-'); // fix 404 error after file upload
72851
+
72852
+ newfilename = newname + '.' + extension;
72853
+ let base64;
72854
+ if (extension === 'jpg' || extension === 'jpeg' || extension === 'webm' || extension === 'webp') {
72855
+ base64 = this.cropper.getCroppedCanvas({
72856
+ fillColor: '#fff',
72857
+ imageSmoothingEnabled: true,
72858
+ imageSmoothingQuality: 'high'
72859
+ }).toDataURL('image/jpeg');
72860
+ } else {
72861
+ base64 = this.cropper.getCroppedCanvas({}).toDataURL();
72862
+ }
72863
+
72864
+ // await this.builder.opts.onBase64Upload(img, base64, newfilename);
72865
+ const getMimeTypeFromFilename = filename => {
72866
+ const ext = filename.split('.').pop().toLowerCase();
72867
+ const mimeTypes = {
72868
+ jpg: 'image/jpeg',
72869
+ jpeg: 'image/jpeg',
72870
+ png: 'image/png',
72871
+ gif: 'image/gif',
72872
+ webp: 'image/webp',
72873
+ svg: 'image/svg+xml',
72874
+ pdf: 'application/pdf',
72875
+ txt: 'text/plain',
72876
+ mp3: 'audio/mpeg',
72877
+ mp4: 'video/mp4',
72878
+ mov: 'video/quicktime',
72879
+ wav: 'audio/wav',
72880
+ json: 'application/json'
72881
+ // add more as needed
72882
+ };
72883
+
72884
+ return mimeTypes[ext] || 'application/octet-stream';
72885
+ };
72886
+ const base64ToFile = (base64, filename) => {
72887
+ const mimeType = getMimeTypeFromFilename(filename);
72888
+ const byteString = atob(base64.split(',')[1]);
72889
+ const byteArray = new Uint8Array(byteString.length);
72890
+ for (let i = 0; i < byteString.length; i++) {
72891
+ byteArray[i] = byteString.charCodeAt(i);
72892
+ }
72893
+ return new File([byteArray], filename, {
72894
+ type: mimeType
72895
+ });
72896
+ };
72897
+ const onUpload = file => {
72898
+ return new Promise(resolve => {
72899
+ this.builder.opts.onAssetUpload = url => {
72900
+ resolve({
72901
+ url: url,
72902
+ name: file.name,
72903
+ size: file.size
72904
+ });
72905
+ };
72906
+ const fakeEvent = {
72907
+ target: {
72908
+ files: [file]
72909
+ }
72910
+ };
72911
+ this.builder.opts.onImageUpload(fakeEvent);
72912
+ });
72913
+ };
72914
+ const file = base64ToFile(base64, newfilename);
72915
+ const result = await onUpload(file);
72916
+ img.setAttribute('src', result.url);
72917
+
72918
+ // finished steps:
72919
+
72920
+ //Check if image is part of module snippet. If so, refresh the (active) module (hide imageTool). If not, refresh imageTool position
72921
+ this.refreshIfIsModule(img);
72922
+
72923
+ //Trigger Change event
72924
+ this.builder.opts.onChange();
72925
+ util.hideModal(modalImageEdit);
72926
+ this.builder.elmTool.refresh();
72927
+ if (this.builder.onImageChange) this.builder.onImageChange();
72928
+ return;
72929
+ }
72613
72930
  let attr = img.getAttribute('data-filename');
72614
72931
  let extension = 'jpg';
72615
72932
  if (attr) {
@@ -75360,7 +75677,7 @@ class Hyperlink {
75360
75677
  <div class="div-anyfile-upload is-btn classic" style="position: relative; flex: 0 0 auto; width: 50px; height: 43px; box-shadow: rgba(0, 0, 0, 0.32) 0px 3px 6px -6px;">
75361
75678
  <form class="form-upload-larger" target="frameTargetAnyfileUpload" method="post" action="${this.builder.opts.fileHandler}" enctype="multipart/form-data" style="overflow:hidden;position:absolute;top:0;left:0;width:100%;height:100%;border-radius:1px;display:flex;align-items: center;justify-content: center;">
75362
75679
  <input class="input-anyfile-customval" name="hidRefId" type="hidden" value="${this.builder.customval}" />
75363
- <svg class="is-icon-flex"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
75680
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
75364
75681
  <input type="file" onclick="blur()" tabindex="0" class="input-anyfile-upload" name="fileImage" accept="*" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;"/>
75365
75682
  </form>
75366
75683
  <iframe tabindex="-1" id="frameTargetAnyfileUpload" name="frameTargetAnyfileUpload" src="about:blank" style="width:1px;height:1px;position:absolute;top:0;right:-100000px"></iframe>
@@ -76357,6 +76674,9 @@ class Module {
76357
76674
  let result = await fetch(this.builder.opts.modulePath + modulename + '.html');
76358
76675
  result = await result.text();
76359
76676
  result = result.replace(/<script>/g, `${this.builder.nonce ? `<script nonce="${this.builder.nonce}">` : '<script>'}`);
76677
+
76678
+ // Replace assets path
76679
+ result = result.replace(/="assets\//g, '="' + this.builder.settings.assetPath);
76360
76680
  iframe.srcdoc = result;
76361
76681
  setTimeout(() => {
76362
76682
  iframe.style.opacity = '';
@@ -77346,7 +77666,7 @@ class Video {
77346
77666
  <div class="video-larger1 input-upload is-btn classic" style="position:relative;flex:none;box-shadow: 0px 3px 6px -6px rgba(0, 0, 0, 0.32);">
77347
77667
  <form class="form-upload-larger" target="frameTargetVideoUpload" method="post" action="${this.builder.opts.videoHandler}" enctype="multipart/form-data" style="position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;">
77348
77668
  <input id="hidRefId5" name="hidRefId" type="hidden" value="" />
77349
- <svg class="is-icon-flex" style="width:18px;height:18px;"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
77669
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
77350
77670
  <input title="${util.out('Select')}" id="fileVideo1" name="fileImage" type="file" accept="video/mp4" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;">
77351
77671
  </form>
77352
77672
 
@@ -77579,7 +77899,7 @@ class Audio {
77579
77899
  <div class="audio-file-upload input-upload is-btn classic" style="position:relative;flex:none;box-shadow: 0px 3px 6px -6px rgba(0, 0, 0, 0.32);">
77580
77900
  <form class="form-upload-larger" target="frameTargetAudioUpload" method="post" action="${this.builder.opts.audioHandler}" enctype="multipart/form-data" style="position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;">
77581
77901
  <input id="hidRefAudio" name="hidRefId" type="hidden" value="" />
77582
- <svg class="is-icon-flex" style="width:18px;height:18px;"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
77902
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
77583
77903
  <input title="${util.out('Select')}" id="fileAudio1" name="fileImage" type="file" accept="audio/mp3" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;">
77584
77904
  </form>
77585
77905
 
@@ -79599,7 +79919,7 @@ class ColumnTool {
79599
79919
  <div class="image-larger3 input-upload" style="position: relative; flex: 0 0 auto;box-shadow: rgba(0, 0, 0, 0.32) 0px 3px 6px -6px;">
79600
79920
  <form class="form-upload-larger" target="frameTargetCellImageUpload" method="post" action="${this.builder.opts.largerImageHandler}" enctype="multipart/form-data" style="position:absolute;display:flex;justify-content: center;align-items: center;top:0;left:0;width:100%;height:100%;">
79601
79921
  <input id="hidRefId3" name="hidRefId" type="hidden" value="">
79602
- <svg class="is-icon-flex" style="width:18px;height:18px;"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
79922
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
79603
79923
  <input onclick="blur()" title="${util.out('Select')}" id="fileImage3" name="fileImage" type="file" accept="image/*" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;">
79604
79924
  </form>
79605
79925
 
@@ -80455,7 +80775,9 @@ class ColumnTool {
80455
80775
  <div class="image-larger4 is-btn classic" style="position: relative; flex: 0 0 auto; width: 40px; height: 38px; box-shadow: rgba(0, 0, 0, 0.32) 0px 3px 6px -6px;">
80456
80776
  <form class="form-upload-larger" target="frameTargetLinkUpload" method="post" action="${this.builder.opts.largerImageHandler}" enctype="multipart/form-data" style="position:absolute;top:0;left:0;width:100%;height:100%;border-radius:1px;display:flex;align-items: center;justify-content: center;">
80457
80777
  <input id="hidRefId4" name="hidRefId" type="hidden" value="">
80458
- <svg class="is-icon-flex" style="width:18px;height:18px;"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
80778
+
80779
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
80780
+
80459
80781
  <input onclick="blur()" title="${util.out('Select')}" id="fileImage4" name="fileImage" type="file" accept="image/*,video/mp4" style="position:absolute;top:-30px;left:0;width:100%;height:68px;opacity: 0;cursor: pointer;">
80460
80782
  </form>
80461
80783
 
@@ -94378,7 +94700,7 @@ class Rte {
94378
94700
  });
94379
94701
 
94380
94702
  const btnInsertImageOk = modalInsertImage.querySelector('.input-ok');
94381
- dom.addEventListener(btnInsertImageOk, 'click', () => {
94703
+ dom.addEventListener(btnInsertImageOk, 'click', async () => {
94382
94704
  if (!this.builder.activeCol) {
94383
94705
  util.hideModal(modalInsertImage);
94384
94706
  return;
@@ -94394,7 +94716,76 @@ class Rte {
94394
94716
  }
94395
94717
  if (val === '') return;
94396
94718
  let fileToInsert = modalInsertImage.querySelector('#imgInsertImagePreview').getAttribute('data-filename');
94397
- util.pasteHtmlAtCaret('<img data-filename="' + fileToInsert + '" src="' + val + '" alt="" />', false);
94719
+ if (this.builder.opts.onImageUpload && val.indexOf('base64') !== -1) {
94720
+ let basename, extension, newname, newfilename;
94721
+ basename = dom.baseName(fileToInsert);
94722
+ extension = fileToInsert.substr(fileToInsert.lastIndexOf('.') + 1).toLowerCase();
94723
+ if (basename.indexOf('-edit') !== -1) {
94724
+ basename = basename.substr(0, basename.indexOf('-edit'));
94725
+ }
94726
+ newname = basename + '-edit' + util.makeId();
94727
+ newname = newname.replaceAll('%20', '-'); // fix 404 error after file upload
94728
+
94729
+ newfilename = newname + '.' + extension;
94730
+ let base64 = val;
94731
+
94732
+ // await this.builder.opts.onBase64Upload(img, base64, newfilename);
94733
+ const getMimeTypeFromFilename = filename => {
94734
+ const ext = filename.split('.').pop().toLowerCase();
94735
+ const mimeTypes = {
94736
+ jpg: 'image/jpeg',
94737
+ jpeg: 'image/jpeg',
94738
+ png: 'image/png',
94739
+ gif: 'image/gif',
94740
+ webp: 'image/webp',
94741
+ svg: 'image/svg+xml',
94742
+ pdf: 'application/pdf',
94743
+ txt: 'text/plain',
94744
+ mp3: 'audio/mpeg',
94745
+ mp4: 'video/mp4',
94746
+ mov: 'video/quicktime',
94747
+ wav: 'audio/wav',
94748
+ json: 'application/json'
94749
+ // add more as needed
94750
+ };
94751
+
94752
+ return mimeTypes[ext] || 'application/octet-stream';
94753
+ };
94754
+ const base64ToFile = (base64, filename) => {
94755
+ const mimeType = getMimeTypeFromFilename(filename);
94756
+ const byteString = atob(base64.split(',')[1]);
94757
+ const byteArray = new Uint8Array(byteString.length);
94758
+ for (let i = 0; i < byteString.length; i++) {
94759
+ byteArray[i] = byteString.charCodeAt(i);
94760
+ }
94761
+ return new File([byteArray], filename, {
94762
+ type: mimeType
94763
+ });
94764
+ };
94765
+ const onUpload = file => {
94766
+ return new Promise(resolve => {
94767
+ this.builder.opts.onAssetUpload = url => {
94768
+ resolve({
94769
+ url: url,
94770
+ name: file.name,
94771
+ size: file.size
94772
+ });
94773
+ };
94774
+ const fakeEvent = {
94775
+ target: {
94776
+ files: [file]
94777
+ }
94778
+ };
94779
+ this.builder.opts.onImageUpload(fakeEvent);
94780
+ });
94781
+ };
94782
+ const file = base64ToFile(base64, newfilename);
94783
+ const result = await onUpload(file);
94784
+ val = result.url;
94785
+ }
94786
+
94787
+ // util.pasteHtmlAtCaret('<img data-filename="' + fileToInsert + '" src="' + val + '" alt="" />', false);
94788
+ util.pasteHtmlAtCaret('<img src="' + val + '" alt="" />', false);
94398
94789
  util.hideModal(modalInsertImage);
94399
94790
  let builderActive = this.builder.doc.querySelector('.builder-active');
94400
94791
  if (builderActive) this.builder.applyBehaviorOn(builderActive);
@@ -100793,7 +101184,7 @@ class SaveImages {
100793
101184
  const images = area.querySelectorAll('img');
100794
101185
  Array.prototype.forEach.call(images, img => {
100795
101186
  let src = img.getAttribute('src');
100796
- if (typeof src !== typeof undefined && src !== false) {
101187
+ if (typeof src !== typeof undefined && src !== false && img.getAttribute('data-filename')) {
100797
101188
  if (src.indexOf('base64') !== -1) {
100798
101189
  // let customcode = false;
100799
101190
  // if(dom.parentsHasAttribute(img, 'data-html')){
@@ -106506,7 +106897,7 @@ class MediaPicker {
106506
106897
  <div class="input-upload" style="position: relative; flex: 0 0 auto;box-shadow: rgba(0, 0, 0, 0.32) 0px 3px 6px -6px;">
106507
106898
  <form class="form-upload-larger" target="frameTarget${this.id}" method="post" action="${this.builder.opts.largerImageHandler}" enctype="multipart/form-data" style="position:absolute;display:flex;justify-content: center;align-items: center;top:0;left:0;width:100%;height:100%;">
106508
106899
  <input name="hidRefId" class="input-ref-id" type="hidden" value="">
106509
- <svg class="is-icon-flex" style="width:18px;height:18px;"><use xlink:href="#ion-ios-cloud-upload-outline"></use></svg>
106900
+ <svg style="width:16px;height:16px;"><use xlink:href="#icon-upload"></use></svg>
106510
106901
  <input title="${util.out('Select')}" name="fileImage" class="input-file-select" type="file" accept="image/*,video/mp4" style="position:absolute;top:-30px;left:0;width:100%;height:80px;opacity: 0;cursor: pointer;">
106511
106902
  </form>
106512
106903
  <iframe tabIndex="0" id="frameTarget${this.id}" name="frameTarget${this.id}" src="about:blank" style="width:1px;height:1px;position:absolute;top:0;right:-100000px"></iframe>
@@ -109466,6 +109857,9 @@ class Dictation {
109466
109857
  this.modalConfig = modalConfig;
109467
109858
  const btnConfig = builderStuff.querySelector('.cmd-command-config');
109468
109859
  const btnDictation = builderStuff.querySelector('.cmd-enable-dictation');
109860
+ if (!this.builder.settings.enableDictation) {
109861
+ btnDictation.style.display = 'none';
109862
+ }
109469
109863
  const btnClear = builderStuff.querySelector('.cmd-clear-command');
109470
109864
  const chkAutoSend = builderStuff.querySelector('#chkAutoSendCommand');
109471
109865
  const btnCommandList = builderStuff.querySelector('.cmd-command-list');
@@ -118475,7 +118869,7 @@ class ContentBuilder {
118475
118869
  // Option for self-hosted fonts:
118476
118870
  // fontPath: 'assets/cssfonts/', // If set, will be used
118477
118871
 
118478
- snippetModal: false,
118872
+ snippetModal: true,
118479
118873
  snippetModalLeft: false,
118480
118874
  snippetData: 'assets/minimalist-blocks/snippetlist.html',
118481
118875
  // Deprecated
@@ -118755,7 +119149,7 @@ class ContentBuilder {
118755
119149
  maxEmbedImageWidth: 1600,
118756
119150
  //set -1 for no max (use original image width)
118757
119151
  zoom: 1,
118758
- useLightbox: false,
119152
+ useLightbox: true,
118759
119153
  lightboxArrow: true,
118760
119154
  imageRenameOnEdit: true,
118761
119155
  disableAutoEmbedVideo: false,
@@ -119144,6 +119538,7 @@ class ContentBuilder {
119144
119538
  sendCommandUrl: '',
119145
119539
  // http://localhost:8081/answer
119146
119540
  // speechTranscribeUrl: 'http://192.168.1.7:8081',
119541
+ enableDictation: false,
119147
119542
  onlineDemo: false,
119148
119543
  autoSendDelay: 4000,
119149
119544
  autoEditBlock: false,
@@ -119549,6 +119944,10 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
119549
119944
  }
119550
119945
  this.settings = this.opts; // Backward compatible
119551
119946
 
119947
+ if (this.isContentBox) {
119948
+ this.settings.snippetModal = false;
119949
+ }
119950
+
119552
119951
  // freeform
119553
119952
  if (this.canvas && !this.isContentBox) {
119554
119953
  /*
@@ -121281,6 +121680,15 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
121281
121680
  applyBehaviorOn(builder) {
121282
121681
  const util = this.util;
121283
121682
 
121683
+ // upload
121684
+ if (this.opts.onImageUpload) {
121685
+ if (!builder._dragDropUploaderAttached) {
121686
+ builder._dragDropUploaderAttached = true;
121687
+ const uploader = new DragDropImageUploader(builder, this);
121688
+ uploader.attach();
121689
+ }
121690
+ }
121691
+
121284
121692
  //Make absolute
121285
121693
  if (this.opts.absolutePath) {
121286
121694
  let links = builder.querySelectorAll('a');
@@ -156153,8 +156561,26 @@ class ContentBox {
156153
156561
  designPathReplace: ['assets/', 'https://.../assets/'], // Only if needed
156154
156562
  */
156155
156563
  // New Template System: => this will replace the previous approach (designUrl1, designUrl2, designPath, designPathReplace & defaultDesignCategory)
156156
- templates: [],
156157
- // ver.5.3
156564
+ // templates: [], // ver.5.3
156565
+ templates: [{
156566
+ url: 'assets/templates-simple/templates.js',
156567
+ path: 'assets/templates-simple/',
156568
+ pathReplace: [],
156569
+ numbering: true,
156570
+ showNumberOnHover: true
156571
+ }, {
156572
+ url: 'assets/templates-quick/templates.js',
156573
+ path: 'assets/templates-quick/',
156574
+ pathReplace: [],
156575
+ numbering: true,
156576
+ showNumberOnHover: true
156577
+ }, {
156578
+ url: 'assets/templates-animated/templates.js',
156579
+ path: 'assets/templates-animated/',
156580
+ pathReplace: [],
156581
+ numbering: true,
156582
+ showNumberOnHover: true
156583
+ }],
156158
156584
 
156159
156585
  /*
156160
156586
  Example:
@@ -156348,7 +156774,7 @@ class ContentBox {
156348
156774
  blockCodeEditorHeight: '45vh',
156349
156775
  blockCodeEditorMaxWidth: '960px',
156350
156776
  assetRefresh: false,
156351
- slider: '',
156777
+ slider: 'glide',
156352
156778
  navbar: false,
156353
156779
  screenMode: 'fullview',
156354
156780
  onRender: function () {},
@@ -160232,6 +160658,10 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
160232
160658
  z-index: 2;
160233
160659
  }
160234
160660
 
160661
+ .is-wrapper .drag-over-image {
160662
+ outline: #a2f0ce 3px solid;
160663
+ }
160664
+
160235
160665
  /*
160236
160666
  .is-wrapper.is-edit {
160237
160667
  min-height: 100vh;