@innovastudio/contentbox 1.6.156 → 1.6.158

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/index.d.ts CHANGED
@@ -374,6 +374,11 @@ interface ContentBoxOptions {
374
374
  exampleAudioUrl?: string;
375
375
  saveResults?: boolean;
376
376
  //---
377
+
378
+ autoSaveDelay?: number;
379
+ maxAutoSaveInterval?: number;
380
+ minAutoSaveInterval?: number;
381
+ autoSave?: () => void | Promise<void>;
377
382
  }
378
383
 
379
384
  interface OpenModalOptions {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@innovastudio/contentbox",
3
3
  "type": "module",
4
- "version": "1.6.156",
4
+ "version": "1.6.158",
5
5
  "description": "",
6
6
  "main": "public/contentbox/contentbox.esm.js",
7
7
  "types": "index.d.ts",
@@ -59,7 +59,7 @@
59
59
  "ws": "^8.13.0"
60
60
  },
61
61
  "dependencies": {
62
- "@innovastudio/contentbuilder": "^1.5.152",
62
+ "@innovastudio/contentbuilder": "^1.5.153",
63
63
  "js-beautify": "^1.14.0",
64
64
  "sortablejs": "^1.15.2"
65
65
  }
@@ -1391,10 +1391,14 @@ class SideBar {
1391
1391
  dom$l.removeClass(elm, 'active');
1392
1392
  });
1393
1393
  builderStuff.querySelectorAll('.is-sidebar-content.active').forEach(elm => {
1394
- dom$l.removeClass(elm, 'active');
1395
- elm.setAttribute('aria-hidden', true);
1394
+ dom$l.removeClass(elm, 'active'); // elm.setAttribute('aria-hidden', true);
1395
+
1396
1396
  setTimeout(() => {
1397
1397
  elm.style.display = ''; //hide
1398
+
1399
+ setTimeout(() => {
1400
+ elm.setAttribute('aria-hidden', true);
1401
+ }, 50);
1398
1402
  }, 300);
1399
1403
  }); // this.builder.animateScroll.close();
1400
1404
  }
@@ -25779,16 +25783,30 @@ class ControlPanel {
25779
25783
  this.builder.iframePanel.setScreenMode('desktop');
25780
25784
  }
25781
25785
  } else if (this.builder.iframeSrc && this.builder.controlPanel) {
25782
- const controlPanel = this.controlPanel;
25783
- const btnToggleDevice = controlPanel.querySelector('.btn-toggledevice');
25786
+ // const controlPanel = this.controlPanel;
25787
+ // const btnToggleDevice = controlPanel.querySelector('.btn-toggledevice');
25788
+ // const screenMode = this.builder.screenMode;
25789
+ // if(screenMode!=='fullview') {
25790
+ // this.builder.iframePanel.setScreenMode('fullview');
25791
+ // btnToggleDevice.classList.remove('on');
25792
+ // } else {
25793
+ // this.builder.iframePanel.setScreenMode('desktop');
25794
+ // btnToggleDevice.classList.add('on');
25795
+ // }
25784
25796
  const screenMode = this.builder.screenMode;
25785
25797
 
25786
- if (screenMode !== 'fullview') {
25787
- this.builder.iframePanel.setScreenMode('fullview');
25788
- btnToggleDevice.classList.remove('on');
25789
- } else {
25798
+ if (screenMode === 'desktop-lg') {
25790
25799
  this.builder.iframePanel.setScreenMode('desktop');
25791
- btnToggleDevice.classList.add('on');
25800
+ } else if (screenMode === 'desktop') {
25801
+ this.builder.iframePanel.setScreenMode('tablet-landscape');
25802
+ } else if (screenMode === 'tablet-landscape') {
25803
+ this.builder.iframePanel.setScreenMode('tablet');
25804
+ } else if (screenMode === 'tablet') {
25805
+ this.builder.iframePanel.setScreenMode('mobile');
25806
+ } else if (screenMode === 'mobile') {
25807
+ this.builder.iframePanel.setScreenMode('fullview');
25808
+ } else if (screenMode === 'fullview') {
25809
+ this.builder.iframePanel.setScreenMode('desktop-lg');
25792
25810
  }
25793
25811
  }
25794
25812
  }
@@ -126116,6 +126134,11 @@ Please obtain a license at: https://innovastudio.com/contentbox`);
126116
126134
  this.elmTool.hide();
126117
126135
  }
126118
126136
  typing() {
126137
+ // Lightweight: Just mark content changed for auto-save
126138
+ if (this.opts.onType) {
126139
+ this.opts.onType();
126140
+ }
126141
+
126119
126142
  // console.log('typing');
126120
126143
  if (this.timeoutId) clearTimeout(this.timeoutId);
126121
126144
  this.timeoutId = setTimeout(() => {
@@ -154490,18 +154513,16 @@ class IframePanel {
154490
154513
  tool.querySelectorAll('button').forEach(btn => btn.classList.remove('active'));
154491
154514
  const btn = tool.querySelector(`[data-command="${screenMode}"]`);
154492
154515
  if (btn) btn.classList.add('active'); // set on/off toggle device button
154493
-
154494
- if (this.builder.controlpanel) {
154495
- const btnToggleDevice = this.builder.controlpanel.controlPanel.querySelector('.btn-toggledevice');
154496
-
154497
- if (btnToggleDevice) {
154498
- if (screenMode !== 'fullview') {
154499
- btnToggleDevice.classList.add('on');
154500
- } else {
154501
- btnToggleDevice.classList.remove('on');
154502
- }
154503
- }
154504
- }
154516
+ // if(this.builder.controlpanel) {
154517
+ // const btnToggleDevice = this.builder.controlpanel.controlPanel.querySelector('.btn-toggledevice');
154518
+ // if(btnToggleDevice) {
154519
+ // if(screenMode!=='fullview') {
154520
+ // btnToggleDevice.classList.add('on');
154521
+ // } else {
154522
+ // btnToggleDevice.classList.remove('on');
154523
+ // }
154524
+ // }
154525
+ // }
154505
154526
 
154506
154527
  this.loadIframe();
154507
154528
  let btns = tool.querySelectorAll('button');
@@ -154676,19 +154697,17 @@ class IframePanel {
154676
154697
  }
154677
154698
 
154678
154699
  if (screenMode !== 'fullview') tool.querySelector(`button[data-command="${screenMode}"]`).classList.add('active'); // set on/off toggle device button
154679
-
154680
- if (this.builder.controlpanel) {
154681
- const btnToggleDevice = this.builder.controlpanel.controlPanel.querySelector('.btn-toggledevice');
154682
-
154683
- if (btnToggleDevice) {
154684
- if (screenMode !== 'fullview') {
154685
- btnToggleDevice.classList.add('on');
154686
- } else {
154687
- btnToggleDevice.classList.remove('on');
154688
- }
154689
- }
154690
- } // Refresh Part 2
154691
-
154700
+ // if(this.builder.controlpanel) {
154701
+ // const btnToggleDevice = this.builder.controlpanel.controlPanel.querySelector('.btn-toggledevice');
154702
+ // if(btnToggleDevice) {
154703
+ // if(screenMode!=='fullview') {
154704
+ // btnToggleDevice.classList.add('on');
154705
+ // } else {
154706
+ // btnToggleDevice.classList.remove('on');
154707
+ // }
154708
+ // }
154709
+ // }
154710
+ // Refresh Part 2
154692
154711
 
154693
154712
  if (hasPin) {
154694
154713
  setTimeout(() => {
@@ -161170,8 +161189,13 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
161170
161189
 
161171
161190
  let old2 = this.settings.onChange;
161172
161191
 
161173
- this.settings.onChange = () => {
161174
- old2.call(this);
161192
+ this.settings.onChange = (...args) => {
161193
+ // Accept all arguments
161194
+ // Call original onChange with all arguments
161195
+ if (old2) old2.apply(this, args); // Use apply to pass arguments
161196
+ // Trigger auto-save
161197
+
161198
+ this.markContentChanged();
161175
161199
  setTimeout(() => {
161176
161200
  // this.quickPosTool();
161177
161201
  this.editor.livePreview.previewRefresh();
@@ -161401,16 +161425,184 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
161401
161425
  }, this);
161402
161426
  } else {
161403
161427
  initialStart();
161428
+ } // ---- AUTO-SAVE ----
161429
+ // Auto-save timing configuration
161430
+
161431
+
161432
+ this.autoSaveDelay = settings.autoSaveDelay || 3000; // Wait 3s after pause before saving
161433
+
161434
+ this.maxAutoSaveInterval = settings.maxAutoSaveInterval || 30000; // Force save every 30s during continuous editing
161435
+
161436
+ this.minAutoSaveInterval = settings.minAutoSaveInterval || 5000; // Minimum 5s between saves (rate limit)
161437
+ // Auto-save callback function
161438
+
161439
+ this.autoSave = settings.autoSave; // Auto-save state
161440
+
161441
+ this.autoSaveTimeoutId = null; // Debounce timer
161442
+
161443
+ this.maxIntervalCheckerId = null; // Background checker for max interval
161444
+
161445
+ this.previousHash = null; // Last saved content hash
161446
+
161447
+ this.lastSaveTime = 0; // Timestamp of last save
161448
+
161449
+ this.pendingChanges = false; // Flag for unsaved changes
161450
+
161451
+ this.isSaving = false; // Prevent concurrent saves
161452
+ // Start auto-save if configured
161453
+
161454
+ if (this.autoSave && typeof this.autoSave === 'function') {
161455
+ this.initAutoSave();
161404
161456
  }
161405
161457
  } // constructor
161458
+ // ---- AUTO-SAVE ----
161459
+ // Initialize auto-save system
161460
+
161461
+
161462
+ initAutoSave() {
161463
+ this.startMaxIntervalChecker();
161464
+ } // Start background checker to enforce max interval
161465
+
161466
+
161467
+ startMaxIntervalChecker() {
161468
+ this.maxIntervalCheckerId = setInterval(() => {
161469
+ // Only check if there are pending changes and not currently saving
161470
+ if (this.pendingChanges && !this.isSaving) {
161471
+ const now = Date.now();
161472
+ const timeSinceLastSave = now - this.lastSaveTime; // Force save if max interval exceeded
161473
+
161474
+ if (timeSinceLastSave >= this.maxAutoSaveInterval) {
161475
+ // console.log('Max interval reached - forcing save');
161476
+ this.performAutoSave();
161477
+ }
161478
+ }
161479
+ }, 1000); // Check every second
161480
+ } // Mark content as changed and schedule auto-save
161481
+
161482
+
161483
+ markContentChanged() {
161484
+ // Skip if auto-save not configured
161485
+ if (!this.autoSave) return; // Mark that changes exist
161486
+
161487
+ this.pendingChanges = true; // Initialize lastSaveTime on first change
161488
+
161489
+ if (this.lastSaveTime === 0) {
161490
+ this.lastSaveTime = Date.now();
161491
+ }
161492
+
161493
+ const now = Date.now();
161494
+ const timeSinceLastSave = now - this.lastSaveTime; // Clear any pending scheduled save
161495
+
161496
+ if (this.autoSaveTimeoutId) {
161497
+ clearTimeout(this.autoSaveTimeoutId);
161498
+ } // Scenario 1: Max interval reached - save immediately
161499
+
161500
+
161501
+ if (timeSinceLastSave >= this.maxAutoSaveInterval && !this.isSaving) {
161502
+ this.performAutoSave();
161503
+ } // Scenario 2: Min interval passed - schedule debounced save
161504
+ else if (timeSinceLastSave >= this.minAutoSaveInterval) {
161505
+ this.autoSaveTimeoutId = setTimeout(() => {
161506
+ this.performAutoSave();
161507
+ }, this.autoSaveDelay);
161508
+ } // Scenario 3: Too soon - schedule for later
161509
+ else {
161510
+ const delayUntilNextSave = this.minAutoSaveInterval - timeSinceLastSave;
161511
+ this.autoSaveTimeoutId = setTimeout(() => {
161512
+ if (this.pendingChanges) {
161513
+ this.performAutoSave();
161514
+ }
161515
+ }, delayUntilNextSave + this.autoSaveDelay);
161516
+ }
161517
+ } // Execute auto-save with change detection
161518
+
161519
+
161520
+ async performAutoSave() {
161521
+ // Prevent concurrent saves
161522
+ if (this.isSaving) return; // Get current content hash (lightweight check)
161523
+
161524
+ const htmlLight = this.htmlCheck();
161525
+ const currentHash = this.simpleHash(htmlLight); // Only save if content actually changed
161526
+
161527
+ if (this.previousHash !== currentHash) {
161528
+ this.isSaving = true;
161529
+
161530
+ try {
161531
+ // Call the save function
161532
+ await this.autoSave(); // console.log('Content auto-saved successfully.');
161533
+ // Update tracking state
161534
+
161535
+ this.previousHash = currentHash;
161536
+ this.lastSaveTime = Date.now();
161537
+ this.pendingChanges = false;
161538
+ } catch (error) {// console.error('Auto-save failed:', error);
161539
+ // Keep pendingChanges = true to retry later
161540
+ } finally {
161541
+ this.isSaving = false;
161542
+ }
161543
+ } else {
161544
+ // No actual changes detected
161545
+ this.pendingChanges = false;
161546
+ }
161547
+ } // Generate hash for efficient change detection
161548
+
161549
+
161550
+ simpleHash(str) {
161551
+ let hash = 0;
161552
+
161553
+ for (let i = 0; i < str.length; i++) {
161554
+ const char = str.charCodeAt(i);
161555
+ hash = (hash << 5) - hash + char; // hash * 31 + char
161556
+
161557
+ hash = hash & hash; // Convert to 32-bit integer
161558
+ }
161559
+
161560
+ return hash;
161561
+ } // Manually trigger save (useful for "Save" button)
161562
+
161563
+
161564
+ async saveNow() {
161565
+ if (this.pendingChanges && !this.isSaving) {
161566
+ await this.performAutoSave();
161567
+ }
161568
+ } //----
161406
161569
 
161407
161570
 
161408
161571
  isNewV2() {
161409
161572
  return this.wrapperEl.classList.contains('box-v2');
161410
161573
  }
161411
161574
 
161575
+ loadFonts() {
161576
+ let contentStylePath = this.settings.contentStylePath;
161577
+ const wrapper = this.wrapperEl;
161578
+ const sections = wrapper.querySelectorAll('.is-section');
161579
+ sections.forEach(section => {
161580
+ section.classList.forEach(item => {
161581
+ if (item.indexOf('type-') !== -1) {
161582
+ const contentClass = item;
161583
+ const contentCss = item + '.css'; //Add css
161584
+
161585
+ let exist = false;
161586
+ let links = this.doc.getElementsByTagName('link');
161587
+
161588
+ for (let i = 0; i < links.length; i++) {
161589
+ let src = links[i].href.toLowerCase();
161590
+ if (src.indexOf(contentCss.toLowerCase()) !== -1) exist = true;
161591
+ }
161592
+
161593
+ if (!exist) {
161594
+ this.wrapperEl.insertAdjacentHTML('beforeend', '<link data-name="contentstyle" data-class="' + contentClass + '" href="' + contentStylePath + contentCss + '" rel="stylesheet">');
161595
+ }
161596
+ }
161597
+ });
161598
+ });
161599
+ }
161600
+
161412
161601
  editorSetup() {
161413
161602
  this.editor = new ContentBuilder({
161603
+ onType: () => {
161604
+ this.markContentChanged();
161605
+ },
161414
161606
  quickAddButtons: this.settings.quickAddButtons,
161415
161607
  assetManagerBasePath: this.settings.assetManagerBasePath || '',
161416
161608
  assetAbsoluteBasePath: this.settings.assetAbsoluteBasePath || '',
@@ -161535,6 +161727,7 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
161535
161727
  sectionTemplate: this.settings.sectionTemplate,
161536
161728
  formTemplate: this.settings.formTemplate,
161537
161729
  onBlockSectionAdd: () => {
161730
+ this.loadFonts();
161538
161731
  this.pageSetup();
161539
161732
  },
161540
161733
  onBlockCanvasAdd: () => {
@@ -165304,7 +165497,23 @@ Add an image for each feature.`, 'Create a new block showcasing a photo gallery
165304
165497
  }
165305
165498
 
165306
165499
  destroy() {
165307
- // let html = this.html();
165500
+ // Save any pending changes before destroying
165501
+ if (this.pendingChanges && !this.isSaving) {
165502
+ this.performAutoSave();
165503
+ } // Clear debounce timer
165504
+
165505
+
165506
+ if (this.autoSaveTimeoutId) {
165507
+ clearTimeout(this.autoSaveTimeoutId);
165508
+ this.autoSaveTimeoutId = null;
165509
+ } // Clear background checker
165510
+
165511
+
165512
+ if (this.maxIntervalCheckerId) {
165513
+ clearInterval(this.maxIntervalCheckerId);
165514
+ this.maxIntervalCheckerId = null;
165515
+ }
165516
+
165308
165517
  if (this.resizeObserver) this.resizeObserver.disconnect();
165309
165518
  const elmStyleEditPanel = document.querySelector('#_style_editpanel');
165310
165519
  if (elmStyleEditPanel) elmStyleEditPanel.remove(elmStyleEditPanel);