@accelerated-agency/visual-editor 0.4.3 → 0.4.4
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/dist/vite.cjs +227 -47
- package/dist/vite.js +227 -47
- package/package.json +1 -1
package/dist/vite.cjs
CHANGED
|
@@ -86,17 +86,26 @@ function hasTrackingMarker(input, markers) {
|
|
|
86
86
|
}
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
89
|
+
function patchKnownUnsafeEditorPatterns(scriptTag) {
|
|
90
|
+
let out = String(scriptTag || "");
|
|
91
|
+
out = out.replace(
|
|
92
|
+
/dragRegion\.addEventListener\((['"])pointerdown\1,\s*onPointerDown\);?/g,
|
|
93
|
+
"if (typeof dragRegion !== 'undefined' && dragRegion) dragRegion.addEventListener($1pointerdown$1, onPointerDown);"
|
|
94
|
+
);
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
89
97
|
function stripTrackingScriptsFromScrapedHtml(html, markers) {
|
|
90
98
|
let removedCount = 0;
|
|
91
99
|
let out = html;
|
|
92
100
|
out = out.replace(/<script\b[\s\S]*?<\/script>/gi, (tag) => {
|
|
101
|
+
var patchedTag = patchKnownUnsafeEditorPatterns(tag);
|
|
93
102
|
const srcMatch = tag.match(/\bsrc\s*=\s*(["'])(.*?)\1/i);
|
|
94
103
|
const src = srcMatch?.[2] || "";
|
|
95
104
|
if (hasTrackingMarker(src, markers) || hasTrackingMarker(tag, markers)) {
|
|
96
105
|
removedCount += 1;
|
|
97
106
|
return "";
|
|
98
107
|
}
|
|
99
|
-
return
|
|
108
|
+
return patchedTag;
|
|
100
109
|
});
|
|
101
110
|
out = out.replace(/<noscript\b[\s\S]*?<\/noscript>/gi, (tag) => {
|
|
102
111
|
if (!hasTrackingMarker(tag, markers)) return tag;
|
|
@@ -860,7 +869,7 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
860
869
|
<button class="tb-dk-btn" title="More options"><i class="bi bi-three-dots"></i></button>
|
|
861
870
|
</div>
|
|
862
871
|
<button class="tb-dk-btn" id="btn-undo" title="Undo (\u2318Z)"><i class="bi bi-arrow-counterclockwise"></i></button>
|
|
863
|
-
<button class="tb-dk-btn" id="btn-redo" title="Redo (\u2318\u21E7Z)"><i class="bi bi-arrow-clockwise"></i></button>
|
|
872
|
+
<button class="tb-dk-btn" id="btn-redo" title="Redo (\u2318\u21E7Z)" style="display:none"><i class="bi bi-arrow-clockwise"></i></button>
|
|
864
873
|
</div>
|
|
865
874
|
|
|
866
875
|
<div id="iframe-loading-toolbar" class="tb-page-loading" aria-live="polite" aria-atomic="true">
|
|
@@ -931,8 +940,8 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
931
940
|
<!-- Elements -->
|
|
932
941
|
<div class="lp-sec lp-sec-no-border">
|
|
933
942
|
<div class="lp-sec-hd">
|
|
934
|
-
<span class="lp-sec-hd-left">Elements <i class="bi bi-info-circle lp-info-icon" title="Page elements"></i></span>
|
|
935
|
-
<button class="lp-add-btn" title="Add element">+ Add</button>
|
|
943
|
+
<span class="lp-sec-hd-left">Elements <i style="display:none" class="bi bi-info-circle lp-info-icon" title="Page elements"></i></span>
|
|
944
|
+
<button class="lp-add-btn" id="btn-add-element" title="Add element">+ Add</button>
|
|
936
945
|
</div>
|
|
937
946
|
|
|
938
947
|
<!-- Search (hidden, kept for JS) -->
|
|
@@ -1002,14 +1011,16 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1002
1011
|
|
|
1003
1012
|
<!-- Right panel -->
|
|
1004
1013
|
<div id="right-panel">
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
<div class="
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
<div
|
|
1012
|
-
|
|
1014
|
+
<div id="section-components-panel" style="display:none">
|
|
1015
|
+
<!-- Left-tab controls moved here -->
|
|
1016
|
+
<div class="section-components-tabs">
|
|
1017
|
+
<div class="lp-tab" onclick="switchSectionComponentsTab('components')">Components</div>
|
|
1018
|
+
<div class="lp-tab" onclick="switchSectionComponentsTab('sections')">Sections</div>
|
|
1019
|
+
</div>
|
|
1020
|
+
<div class="lp-body">
|
|
1021
|
+
<div id="tab-components" class="tab-pane"></div>
|
|
1022
|
+
<div id="tab-sections" class="tab-pane"></div>
|
|
1023
|
+
</div>
|
|
1013
1024
|
</div>
|
|
1014
1025
|
<!-- Element badge (hidden until selection) -->
|
|
1015
1026
|
<div id="el-info" style="display:none">
|
|
@@ -1666,6 +1677,19 @@ function switchSectionComponentsTab(tab) {
|
|
|
1666
1677
|
renderSidebar(inp ? inp.value : '');
|
|
1667
1678
|
}
|
|
1668
1679
|
|
|
1680
|
+
function toggleSectionComponentsPanel(forceVisible) {
|
|
1681
|
+
var panel = document.getElementById('section-components-panel');
|
|
1682
|
+
if (!panel) return;
|
|
1683
|
+
var shouldShow =
|
|
1684
|
+
typeof forceVisible === 'boolean'
|
|
1685
|
+
? forceVisible
|
|
1686
|
+
: panel.style.display === 'none';
|
|
1687
|
+
panel.style.display = shouldShow ? '' : 'none';
|
|
1688
|
+
if (shouldShow) {
|
|
1689
|
+
switchSectionComponentsTab(currentSectionComponentsTab || 'components');
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1669
1693
|
// \u2500\u2500 Accordion toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1670
1694
|
function toggleAcc(name) {
|
|
1671
1695
|
var sec = document.getElementById('acc-' + name);
|
|
@@ -1875,6 +1899,24 @@ function syncDesignInput(change) {
|
|
|
1875
1899
|
function removeStateChange(idx) {
|
|
1876
1900
|
var change = stateChanges[idx];
|
|
1877
1901
|
if (!change) return;
|
|
1902
|
+
if (change.isStructuralLive) {
|
|
1903
|
+
removeSessionStructuralRowByTimestamp(
|
|
1904
|
+
change.structuralVarId || activeVarId,
|
|
1905
|
+
change.vveTs,
|
|
1906
|
+
);
|
|
1907
|
+
stateChanges.splice(idx, 1);
|
|
1908
|
+
commitStateChangesForActiveVariation();
|
|
1909
|
+
renderStatesTab();
|
|
1910
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1911
|
+
try {
|
|
1912
|
+
delete varHtmlCache[activeVarId];
|
|
1913
|
+
} catch(_) {}
|
|
1914
|
+
appliedStructuralChangesetKeys = {};
|
|
1915
|
+
recomputeEditorDirty();
|
|
1916
|
+
scheduleDomTreeRefresh();
|
|
1917
|
+
softReloadEditorIframe();
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1878
1920
|
revertChangeOnDom(change);
|
|
1879
1921
|
syncDesignInput(change);
|
|
1880
1922
|
stateChanges.splice(idx, 1);
|
|
@@ -2231,6 +2273,14 @@ function getLatestHistoryUndoTarget() {
|
|
|
2231
2273
|
return list.length ? list[0] : null;
|
|
2232
2274
|
}
|
|
2233
2275
|
|
|
2276
|
+
function getLatestLiveUndoTarget() {
|
|
2277
|
+
var list = getUnifiedHistoryItems();
|
|
2278
|
+
for (var i = 0; i < list.length; i++) {
|
|
2279
|
+
if (list[i] && list[i].source === 'live') return list[i];
|
|
2280
|
+
}
|
|
2281
|
+
return null;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2234
2284
|
function changesetListHasStructural(arr) {
|
|
2235
2285
|
if (!arr || !arr.length) return false;
|
|
2236
2286
|
for (var i = 0; i < arr.length; i++) {
|
|
@@ -2329,8 +2379,15 @@ function removeHistoryChangeset(idx, evt) {
|
|
|
2329
2379
|
function clearAllHistoryChangesets() {
|
|
2330
2380
|
var v = getActiveVariationForHistory();
|
|
2331
2381
|
if (!v) return;
|
|
2332
|
-
|
|
2382
|
+
var hasSavedChangesets = parseVariationChangesets(v).length > 0;
|
|
2383
|
+
var hasSessionStructural =
|
|
2384
|
+
!!(activeVarId && sessionStructuralChainRowsByVarId[activeVarId] && sessionStructuralChainRowsByVarId[activeVarId].length);
|
|
2385
|
+
if (!hasSavedChangesets && !hasSessionStructural) return;
|
|
2386
|
+
|
|
2333
2387
|
persistActiveVariationChangesets([]);
|
|
2388
|
+
if (activeVarId) {
|
|
2389
|
+
sessionStructuralChainRowsByVarId[activeVarId] = [];
|
|
2390
|
+
}
|
|
2334
2391
|
appliedChangesetSnapshots = {};
|
|
2335
2392
|
appliedStructuralChangesetKeys = {};
|
|
2336
2393
|
try {
|
|
@@ -2343,6 +2400,10 @@ function clearAllHistoryChangesets() {
|
|
|
2343
2400
|
}
|
|
2344
2401
|
|
|
2345
2402
|
function clearAllUnifiedHistory() {
|
|
2403
|
+
if (activeVarId) {
|
|
2404
|
+
// Ensure structural unsaved rows are also removed by "Clear all changes".
|
|
2405
|
+
sessionStructuralChainRowsByVarId[activeVarId] = [];
|
|
2406
|
+
}
|
|
2346
2407
|
clearAllStates();
|
|
2347
2408
|
clearAllHistoryChangesets();
|
|
2348
2409
|
if (currentMainTab === 'history') renderHistoryTab();
|
|
@@ -2354,15 +2415,32 @@ var VVE_LOCAL_STORAGE_PREFIX = 'vve:';
|
|
|
2354
2415
|
|
|
2355
2416
|
function clearVisualEditorLocalStorage() {
|
|
2356
2417
|
try {
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2418
|
+
var stores = [];
|
|
2419
|
+
try { stores.push(localStorage); } catch(_) {}
|
|
2420
|
+
try { stores.push(sessionStorage); } catch(_) {}
|
|
2421
|
+
for (var si = 0; si < stores.length; si++) {
|
|
2422
|
+
var store = stores[si];
|
|
2423
|
+
if (!store) continue;
|
|
2424
|
+
for (var i = store.length - 1; i >= 0; i--) {
|
|
2425
|
+
var k = store.key(i);
|
|
2426
|
+
if (k && k.indexOf(VVE_LOCAL_STORAGE_PREFIX) === 0) {
|
|
2427
|
+
store.removeItem(k);
|
|
2428
|
+
}
|
|
2361
2429
|
}
|
|
2362
2430
|
}
|
|
2363
2431
|
} catch(_) {}
|
|
2364
2432
|
}
|
|
2365
2433
|
|
|
2434
|
+
function clearPersistedActiveVariationForData(data) {
|
|
2435
|
+
try {
|
|
2436
|
+
var sk = activeVariationStorageKeyFromPayload(data);
|
|
2437
|
+
if (sk && sk !== VVE_LOCAL_STORAGE_PREFIX + 'activeVar::') {
|
|
2438
|
+
try { localStorage.removeItem(sk); } catch(_) {}
|
|
2439
|
+
try { sessionStorage.removeItem(sk); } catch(_) {}
|
|
2440
|
+
}
|
|
2441
|
+
} catch(_) {}
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2366
2444
|
function activeVariationStorageKeyFromPayload(data) {
|
|
2367
2445
|
return (
|
|
2368
2446
|
VVE_LOCAL_STORAGE_PREFIX +
|
|
@@ -2401,14 +2479,18 @@ function writePersistedActiveVariationId(varId) {
|
|
|
2401
2479
|
* @param allowPrevMemory when true, keep in-session activeVarId if still valid (skip-reload path).
|
|
2402
2480
|
*/
|
|
2403
2481
|
function pickActiveVariationIdForLoad(data, variationsArr, prevMemoryId, allowPrevMemory) {
|
|
2404
|
-
var
|
|
2405
|
-
var fallback = (
|
|
2482
|
+
var firstNonBaseline = variationsArr.find(function(v) { return !v.baseline; });
|
|
2483
|
+
var fallback = (firstNonBaseline || variationsArr[0] || {})._id || null;
|
|
2406
2484
|
if (!variationsArr.length) return null;
|
|
2407
2485
|
if (allowPrevMemory && prevMemoryId && variationsArr.some(function(v) { return v._id === prevMemoryId; })) {
|
|
2408
2486
|
return prevMemoryId;
|
|
2409
2487
|
}
|
|
2410
2488
|
var stored = readPersistedActiveVariationId(data);
|
|
2411
2489
|
if (stored && variationsArr.some(function(v) { return v._id === stored; })) {
|
|
2490
|
+
var storedVar = variationsArr.find(function(v) { return v._id === stored; });
|
|
2491
|
+
if (storedVar && storedVar.baseline && firstNonBaseline && firstNonBaseline._id) {
|
|
2492
|
+
return firstNonBaseline._id;
|
|
2493
|
+
}
|
|
2412
2494
|
return stored;
|
|
2413
2495
|
}
|
|
2414
2496
|
return fallback;
|
|
@@ -2632,7 +2714,13 @@ function granularAnySelectorMatches(doc, cs) {
|
|
|
2632
2714
|
|
|
2633
2715
|
/** Bust bfcache / same-URL no-op reloads so the iframe actually re-parses (loading \u2192 interactive). */
|
|
2634
2716
|
function appendIframeReloadBust(url) {
|
|
2635
|
-
|
|
2717
|
+
try {
|
|
2718
|
+
var u = new URL(String(url || ''), window.location.href);
|
|
2719
|
+
u.searchParams.set('_vve_reload', String(Date.now()));
|
|
2720
|
+
return u.toString();
|
|
2721
|
+
} catch(_) {
|
|
2722
|
+
return url;
|
|
2723
|
+
}
|
|
2636
2724
|
}
|
|
2637
2725
|
|
|
2638
2726
|
// True when the iframe contentDocument belongs to the current iframe.src navigation.
|
|
@@ -3010,6 +3098,44 @@ function appendSessionStructuralChainRow(varId, row) {
|
|
|
3010
3098
|
sessionStructuralChainRowsByVarId[varId].push(row);
|
|
3011
3099
|
}
|
|
3012
3100
|
|
|
3101
|
+
function markStructuralRowApplied(row) {
|
|
3102
|
+
try {
|
|
3103
|
+
var k = structuralChangesetDedupKey(row);
|
|
3104
|
+
if (k) appliedStructuralChangesetKeys[k] = true;
|
|
3105
|
+
} catch(_) {}
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
function logStructuralStateChange(row, label, value, targetEl) {
|
|
3109
|
+
if (!row || !row.selector) return;
|
|
3110
|
+
stateChanges.push({
|
|
3111
|
+
selector: row.selector,
|
|
3112
|
+
inputId: 'vve-struct',
|
|
3113
|
+
label: label || 'Structure change',
|
|
3114
|
+
cssProp: null,
|
|
3115
|
+
value: value != null ? String(value) : String(row.type || ''),
|
|
3116
|
+
targetEl: targetEl || null,
|
|
3117
|
+
originalValue: '',
|
|
3118
|
+
vveTs: row.vveTs || nextHistoryTimestamp(),
|
|
3119
|
+
isStructuralLive: true,
|
|
3120
|
+
structuralVarId: activeVarId || null,
|
|
3121
|
+
structuralType: row.type || '',
|
|
3122
|
+
});
|
|
3123
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
3124
|
+
commitStateChangesForActiveVariation();
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
function removeSessionStructuralRowByTimestamp(varId, ts) {
|
|
3128
|
+
if (!varId || !ts) return;
|
|
3129
|
+
var arr = sessionStructuralChainRowsByVarId[varId];
|
|
3130
|
+
if (!arr || !arr.length) return;
|
|
3131
|
+
for (var i = arr.length - 1; i >= 0; i--) {
|
|
3132
|
+
if (arr[i] && arr[i].vveTs === ts) {
|
|
3133
|
+
arr.splice(i, 1);
|
|
3134
|
+
return;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3013
3139
|
/** One States-tab row -> Conversion.io chain-set shape (matches applyChangesetEntry). */
|
|
3014
3140
|
function stateChangeToChainSet(c) {
|
|
3015
3141
|
if (!c || !c.selector) return null;
|
|
@@ -3704,12 +3830,15 @@ function duplicateSelectedEl() {
|
|
|
3704
3830
|
clone.setAttribute('data-vve-instance', generateVveInstanceId());
|
|
3705
3831
|
} catch(_) {}
|
|
3706
3832
|
if (activeVarId) {
|
|
3707
|
-
|
|
3833
|
+
var dupRow = {
|
|
3708
3834
|
selector: anchorSel,
|
|
3709
3835
|
type: 'insert',
|
|
3710
3836
|
action: 'after',
|
|
3711
3837
|
html: clone.outerHTML,
|
|
3712
|
-
}
|
|
3838
|
+
};
|
|
3839
|
+
appendSessionStructuralChainRow(activeVarId, dupRow);
|
|
3840
|
+
markStructuralRowApplied(dupRow);
|
|
3841
|
+
logStructuralStateChange(dupRow, 'Duplicated via toolbar', 'Duplicate inserted', selectedEl);
|
|
3713
3842
|
}
|
|
3714
3843
|
selectedEl.parentNode.insertBefore(clone, selectedEl.nextSibling);
|
|
3715
3844
|
saveCurrentVariationHtml();
|
|
@@ -3725,23 +3854,27 @@ function toggleHideSelectedEl() {
|
|
|
3725
3854
|
selectedEl.style.visibility = '';
|
|
3726
3855
|
selectedEl.removeAttribute('data-vve-hidden');
|
|
3727
3856
|
if (activeVarId) {
|
|
3728
|
-
|
|
3857
|
+
var showRow = {
|
|
3729
3858
|
selector: hidSel,
|
|
3730
3859
|
type: 'style',
|
|
3731
3860
|
property: 'visibility',
|
|
3732
3861
|
value: '',
|
|
3733
|
-
}
|
|
3862
|
+
};
|
|
3863
|
+
appendSessionStructuralChainRow(activeVarId, showRow);
|
|
3864
|
+
logStructuralStateChange(showRow, 'Shown via toolbar', 'Visibility restored', selectedEl);
|
|
3734
3865
|
}
|
|
3735
3866
|
} else {
|
|
3736
3867
|
selectedEl.style.visibility = 'hidden';
|
|
3737
3868
|
selectedEl.setAttribute('data-vve-hidden', '1');
|
|
3738
3869
|
if (activeVarId) {
|
|
3739
|
-
|
|
3870
|
+
var hideRow = {
|
|
3740
3871
|
selector: hidSel,
|
|
3741
3872
|
type: 'style',
|
|
3742
3873
|
property: 'visibility',
|
|
3743
3874
|
value: 'hidden',
|
|
3744
|
-
}
|
|
3875
|
+
};
|
|
3876
|
+
appendSessionStructuralChainRow(activeVarId, hideRow);
|
|
3877
|
+
logStructuralStateChange(hideRow, 'Hidden via toolbar', 'Visibility set to hidden', selectedEl);
|
|
3745
3878
|
}
|
|
3746
3879
|
}
|
|
3747
3880
|
saveCurrentVariationHtml();
|
|
@@ -3752,7 +3885,11 @@ function deleteSelectedEl() {
|
|
|
3752
3885
|
if (!selectedEl || !selectedEl.parentNode) return;
|
|
3753
3886
|
var delSel = buildSelector(selectedEl);
|
|
3754
3887
|
selectedEl.remove();
|
|
3755
|
-
if (activeVarId)
|
|
3888
|
+
if (activeVarId) {
|
|
3889
|
+
var delRow = { selector: delSel, type: 'remove' };
|
|
3890
|
+
appendSessionStructuralChainRow(activeVarId, delRow);
|
|
3891
|
+
logStructuralStateChange(delRow, 'Deleted via toolbar', 'Element removed', null);
|
|
3892
|
+
}
|
|
3756
3893
|
saveCurrentVariationHtml();
|
|
3757
3894
|
recomputeEditorDirty();
|
|
3758
3895
|
deselectElement();
|
|
@@ -4653,19 +4790,25 @@ function recordReorderAfterDrag(movedEl) {
|
|
|
4653
4790
|
var prev = movedEl.previousElementSibling;
|
|
4654
4791
|
var next = movedEl.nextElementSibling;
|
|
4655
4792
|
if (prev) {
|
|
4656
|
-
|
|
4793
|
+
var reorderAfterRow = {
|
|
4657
4794
|
selector: buildSelector(movedEl),
|
|
4658
4795
|
type: 'reorder',
|
|
4659
4796
|
targetSelector: buildSelector(prev),
|
|
4660
4797
|
action: 'after',
|
|
4661
|
-
}
|
|
4798
|
+
};
|
|
4799
|
+
appendSessionStructuralChainRow(activeVarId, reorderAfterRow);
|
|
4800
|
+
markStructuralRowApplied(reorderAfterRow);
|
|
4801
|
+
logStructuralStateChange(reorderAfterRow, 'Reordered via drag', 'Moved after sibling', movedEl);
|
|
4662
4802
|
} else if (next) {
|
|
4663
|
-
|
|
4803
|
+
var reorderBeforeRow = {
|
|
4664
4804
|
selector: buildSelector(movedEl),
|
|
4665
4805
|
type: 'reorder',
|
|
4666
4806
|
targetSelector: buildSelector(next),
|
|
4667
4807
|
action: 'before',
|
|
4668
|
-
}
|
|
4808
|
+
};
|
|
4809
|
+
appendSessionStructuralChainRow(activeVarId, reorderBeforeRow);
|
|
4810
|
+
markStructuralRowApplied(reorderBeforeRow);
|
|
4811
|
+
logStructuralStateChange(reorderBeforeRow, 'Reordered via drag', 'Moved before sibling', movedEl);
|
|
4669
4812
|
}
|
|
4670
4813
|
}
|
|
4671
4814
|
|
|
@@ -4946,12 +5089,15 @@ function insertHtml(html) {
|
|
|
4946
5089
|
}
|
|
4947
5090
|
if (firstEl) selectElement(firstEl);
|
|
4948
5091
|
if (activeVarId) {
|
|
4949
|
-
|
|
5092
|
+
var insertRow = {
|
|
4950
5093
|
selector: anchorSel,
|
|
4951
5094
|
type: 'insert',
|
|
4952
5095
|
action: 'after',
|
|
4953
5096
|
html: htmlStr,
|
|
4954
|
-
}
|
|
5097
|
+
};
|
|
5098
|
+
appendSessionStructuralChainRow(activeVarId, insertRow);
|
|
5099
|
+
markStructuralRowApplied(insertRow);
|
|
5100
|
+
logStructuralStateChange(insertRow, 'Added component/section', 'Inserted new element', firstEl || selectedEl);
|
|
4955
5101
|
}
|
|
4956
5102
|
saveCurrentVariationHtml();
|
|
4957
5103
|
recomputeEditorDirty();
|
|
@@ -5041,6 +5187,14 @@ document.getElementById('comp-search').addEventListener('input', function() {
|
|
|
5041
5187
|
renderSidebar(this.value);
|
|
5042
5188
|
}
|
|
5043
5189
|
});
|
|
5190
|
+
var btnAddElement = document.getElementById('btn-add-element');
|
|
5191
|
+
if (btnAddElement) {
|
|
5192
|
+
btnAddElement.addEventListener('click', function(e) {
|
|
5193
|
+
e.preventDefault();
|
|
5194
|
+
e.stopPropagation();
|
|
5195
|
+
toggleSectionComponentsPanel();
|
|
5196
|
+
});
|
|
5197
|
+
}
|
|
5044
5198
|
|
|
5045
5199
|
// \u2500\u2500 Save / Close \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5046
5200
|
document.getElementById('btn-save').addEventListener('click', handleSave);
|
|
@@ -5081,11 +5235,21 @@ function handleSave() {
|
|
|
5081
5235
|
}
|
|
5082
5236
|
|
|
5083
5237
|
function handleClose() {
|
|
5238
|
+
clearPersistedActiveVariationForData(experimentData);
|
|
5084
5239
|
clearVisualEditorLocalStorage();
|
|
5085
5240
|
// Unsaved-changes UX lives in the parent (PlatformVisualEditorV2); avoid double confirm here.
|
|
5086
5241
|
send('close-editor', {});
|
|
5087
5242
|
}
|
|
5088
5243
|
|
|
5244
|
+
// Defensive cleanup: if parent closes/unmounts the editor shell without
|
|
5245
|
+
// invoking handleClose(), clear persisted VVE keys on unload as well.
|
|
5246
|
+
window.addEventListener('pagehide', function() {
|
|
5247
|
+
clearVisualEditorLocalStorage();
|
|
5248
|
+
});
|
|
5249
|
+
window.addEventListener('beforeunload', function() {
|
|
5250
|
+
clearVisualEditorLocalStorage();
|
|
5251
|
+
});
|
|
5252
|
+
|
|
5089
5253
|
// \u2500\u2500 Keyboard shortcuts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5090
5254
|
function isNativeEditableTarget(target) {
|
|
5091
5255
|
if (!target || target.nodeType !== 1) return false;
|
|
@@ -5107,10 +5271,7 @@ document.addEventListener('keydown', function(e) {
|
|
|
5107
5271
|
e.preventDefault();
|
|
5108
5272
|
runEditorUndo();
|
|
5109
5273
|
}
|
|
5110
|
-
|
|
5111
|
-
e.preventDefault();
|
|
5112
|
-
runEditorRedo();
|
|
5113
|
-
}
|
|
5274
|
+
// Redo is intentionally hidden/disabled in this editor flow.
|
|
5114
5275
|
if (meta && e.key === 's') { e.preventDefault(); handleSave(); }
|
|
5115
5276
|
if (e.key === 'Escape') {
|
|
5116
5277
|
var openTips = document.querySelectorAll('.ve-pl-tip.is-tip-open');
|
|
@@ -5130,11 +5291,17 @@ document.addEventListener('keydown', function(e) {
|
|
|
5130
5291
|
}
|
|
5131
5292
|
});
|
|
5132
5293
|
function runEditorUndo() {
|
|
5133
|
-
|
|
5134
|
-
if (
|
|
5135
|
-
|
|
5294
|
+
// Undo only unsaved in-session edits; do not remove persisted history rows.
|
|
5295
|
+
if (!isDirty) return;
|
|
5296
|
+
|
|
5297
|
+
// 1) Prefer live unsaved change stack (stateChanges shown in History tab).
|
|
5298
|
+
var liveTarget = getLatestLiveUndoTarget();
|
|
5299
|
+
if (liveTarget) {
|
|
5300
|
+
removeHistoryItem('live', liveTarget.idx);
|
|
5136
5301
|
return;
|
|
5137
5302
|
}
|
|
5303
|
+
|
|
5304
|
+
// 2) Fallback to Vvveb internal undo stack (e.g. structural drag/drop ops).
|
|
5138
5305
|
if (!(typeof Vvveb !== 'undefined' && Vvveb.Undo)) return;
|
|
5139
5306
|
Vvveb.Undo.undo();
|
|
5140
5307
|
saveCurrentVariationHtml();
|
|
@@ -5157,11 +5324,10 @@ document.getElementById('btn-undo').addEventListener('click', function(e) {
|
|
|
5157
5324
|
e.stopPropagation();
|
|
5158
5325
|
runEditorUndo();
|
|
5159
5326
|
});
|
|
5160
|
-
document.getElementById('btn-redo')
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
});
|
|
5327
|
+
var btnRedo = document.getElementById('btn-redo');
|
|
5328
|
+
if (btnRedo) {
|
|
5329
|
+
btnRedo.style.display = 'none';
|
|
5330
|
+
}
|
|
5165
5331
|
|
|
5166
5332
|
function layoutLoadingTooltip(host) {
|
|
5167
5333
|
var tip = host.querySelector('.ve-pl-tooltip');
|
|
@@ -5970,8 +6136,22 @@ if(window.fetch){
|
|
|
5970
6136
|
try{
|
|
5971
6137
|
var u=afterUrl?resolveUrl(afterUrl):null;
|
|
5972
6138
|
var sameOrigin=!!(u&&u.origin===TARGET_ORIGIN);
|
|
5973
|
-
var
|
|
5974
|
-
|
|
6139
|
+
var reqMethod=(init&&init.method?String(init.method):(input&&input.method?String(input.method):"GET")).toUpperCase();
|
|
6140
|
+
var isSafeMethod=reqMethod==="GET"||reqMethod==="HEAD";
|
|
6141
|
+
var path=(u&&u.pathname?u.pathname:"").toLowerCase();
|
|
6142
|
+
var qs=(u&&u.search?u.search:"").toLowerCase();
|
|
6143
|
+
var likelyBackgroundEndpoint=!!(u&&(
|
|
6144
|
+
/(^|\\/)apps?(\\/|$)|(^|\\/)a(\\/|$)/.test(path)||
|
|
6145
|
+
path==="/cart"||
|
|
6146
|
+
path==="/cart.js"||
|
|
6147
|
+
path.indexOf("/cart/")===0||
|
|
6148
|
+
path.indexOf("/recommendations/")===0||
|
|
6149
|
+
path.indexOf("/search/suggest")===0||
|
|
6150
|
+
qs.indexOf("sections=")!==-1||
|
|
6151
|
+
qs.indexOf("section_id=")!==-1
|
|
6152
|
+
));
|
|
6153
|
+
var likelyThirdPartyBg=!sameOrigin||likelyBackgroundEndpoint;
|
|
6154
|
+
if(window.__CONVERSION_EDITOR_ACTIVE__&&isSafeMethod&&likelyThirdPartyBg){
|
|
5975
6155
|
console.warn("[conversion-proxy] suppressed fetch failure",{url:afterUrl,error:err&&err.message?err.message:String(err)});
|
|
5976
6156
|
return new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}});
|
|
5977
6157
|
}
|
package/dist/vite.js
CHANGED
|
@@ -78,17 +78,26 @@ function hasTrackingMarker(input, markers) {
|
|
|
78
78
|
}
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
|
+
function patchKnownUnsafeEditorPatterns(scriptTag) {
|
|
82
|
+
let out = String(scriptTag || "");
|
|
83
|
+
out = out.replace(
|
|
84
|
+
/dragRegion\.addEventListener\((['"])pointerdown\1,\s*onPointerDown\);?/g,
|
|
85
|
+
"if (typeof dragRegion !== 'undefined' && dragRegion) dragRegion.addEventListener($1pointerdown$1, onPointerDown);"
|
|
86
|
+
);
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
81
89
|
function stripTrackingScriptsFromScrapedHtml(html, markers) {
|
|
82
90
|
let removedCount = 0;
|
|
83
91
|
let out = html;
|
|
84
92
|
out = out.replace(/<script\b[\s\S]*?<\/script>/gi, (tag) => {
|
|
93
|
+
var patchedTag = patchKnownUnsafeEditorPatterns(tag);
|
|
85
94
|
const srcMatch = tag.match(/\bsrc\s*=\s*(["'])(.*?)\1/i);
|
|
86
95
|
const src = srcMatch?.[2] || "";
|
|
87
96
|
if (hasTrackingMarker(src, markers) || hasTrackingMarker(tag, markers)) {
|
|
88
97
|
removedCount += 1;
|
|
89
98
|
return "";
|
|
90
99
|
}
|
|
91
|
-
return
|
|
100
|
+
return patchedTag;
|
|
92
101
|
});
|
|
93
102
|
out = out.replace(/<noscript\b[\s\S]*?<\/noscript>/gi, (tag) => {
|
|
94
103
|
if (!hasTrackingMarker(tag, markers)) return tag;
|
|
@@ -852,7 +861,7 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
852
861
|
<button class="tb-dk-btn" title="More options"><i class="bi bi-three-dots"></i></button>
|
|
853
862
|
</div>
|
|
854
863
|
<button class="tb-dk-btn" id="btn-undo" title="Undo (\u2318Z)"><i class="bi bi-arrow-counterclockwise"></i></button>
|
|
855
|
-
<button class="tb-dk-btn" id="btn-redo" title="Redo (\u2318\u21E7Z)"><i class="bi bi-arrow-clockwise"></i></button>
|
|
864
|
+
<button class="tb-dk-btn" id="btn-redo" title="Redo (\u2318\u21E7Z)" style="display:none"><i class="bi bi-arrow-clockwise"></i></button>
|
|
856
865
|
</div>
|
|
857
866
|
|
|
858
867
|
<div id="iframe-loading-toolbar" class="tb-page-loading" aria-live="polite" aria-atomic="true">
|
|
@@ -923,8 +932,8 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
923
932
|
<!-- Elements -->
|
|
924
933
|
<div class="lp-sec lp-sec-no-border">
|
|
925
934
|
<div class="lp-sec-hd">
|
|
926
|
-
<span class="lp-sec-hd-left">Elements <i class="bi bi-info-circle lp-info-icon" title="Page elements"></i></span>
|
|
927
|
-
<button class="lp-add-btn" title="Add element">+ Add</button>
|
|
935
|
+
<span class="lp-sec-hd-left">Elements <i style="display:none" class="bi bi-info-circle lp-info-icon" title="Page elements"></i></span>
|
|
936
|
+
<button class="lp-add-btn" id="btn-add-element" title="Add element">+ Add</button>
|
|
928
937
|
</div>
|
|
929
938
|
|
|
930
939
|
<!-- Search (hidden, kept for JS) -->
|
|
@@ -994,14 +1003,16 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
994
1003
|
|
|
995
1004
|
<!-- Right panel -->
|
|
996
1005
|
<div id="right-panel">
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
<div class="
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
<div
|
|
1004
|
-
|
|
1006
|
+
<div id="section-components-panel" style="display:none">
|
|
1007
|
+
<!-- Left-tab controls moved here -->
|
|
1008
|
+
<div class="section-components-tabs">
|
|
1009
|
+
<div class="lp-tab" onclick="switchSectionComponentsTab('components')">Components</div>
|
|
1010
|
+
<div class="lp-tab" onclick="switchSectionComponentsTab('sections')">Sections</div>
|
|
1011
|
+
</div>
|
|
1012
|
+
<div class="lp-body">
|
|
1013
|
+
<div id="tab-components" class="tab-pane"></div>
|
|
1014
|
+
<div id="tab-sections" class="tab-pane"></div>
|
|
1015
|
+
</div>
|
|
1005
1016
|
</div>
|
|
1006
1017
|
<!-- Element badge (hidden until selection) -->
|
|
1007
1018
|
<div id="el-info" style="display:none">
|
|
@@ -1658,6 +1669,19 @@ function switchSectionComponentsTab(tab) {
|
|
|
1658
1669
|
renderSidebar(inp ? inp.value : '');
|
|
1659
1670
|
}
|
|
1660
1671
|
|
|
1672
|
+
function toggleSectionComponentsPanel(forceVisible) {
|
|
1673
|
+
var panel = document.getElementById('section-components-panel');
|
|
1674
|
+
if (!panel) return;
|
|
1675
|
+
var shouldShow =
|
|
1676
|
+
typeof forceVisible === 'boolean'
|
|
1677
|
+
? forceVisible
|
|
1678
|
+
: panel.style.display === 'none';
|
|
1679
|
+
panel.style.display = shouldShow ? '' : 'none';
|
|
1680
|
+
if (shouldShow) {
|
|
1681
|
+
switchSectionComponentsTab(currentSectionComponentsTab || 'components');
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1661
1685
|
// \u2500\u2500 Accordion toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1662
1686
|
function toggleAcc(name) {
|
|
1663
1687
|
var sec = document.getElementById('acc-' + name);
|
|
@@ -1867,6 +1891,24 @@ function syncDesignInput(change) {
|
|
|
1867
1891
|
function removeStateChange(idx) {
|
|
1868
1892
|
var change = stateChanges[idx];
|
|
1869
1893
|
if (!change) return;
|
|
1894
|
+
if (change.isStructuralLive) {
|
|
1895
|
+
removeSessionStructuralRowByTimestamp(
|
|
1896
|
+
change.structuralVarId || activeVarId,
|
|
1897
|
+
change.vveTs,
|
|
1898
|
+
);
|
|
1899
|
+
stateChanges.splice(idx, 1);
|
|
1900
|
+
commitStateChangesForActiveVariation();
|
|
1901
|
+
renderStatesTab();
|
|
1902
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1903
|
+
try {
|
|
1904
|
+
delete varHtmlCache[activeVarId];
|
|
1905
|
+
} catch(_) {}
|
|
1906
|
+
appliedStructuralChangesetKeys = {};
|
|
1907
|
+
recomputeEditorDirty();
|
|
1908
|
+
scheduleDomTreeRefresh();
|
|
1909
|
+
softReloadEditorIframe();
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1870
1912
|
revertChangeOnDom(change);
|
|
1871
1913
|
syncDesignInput(change);
|
|
1872
1914
|
stateChanges.splice(idx, 1);
|
|
@@ -2223,6 +2265,14 @@ function getLatestHistoryUndoTarget() {
|
|
|
2223
2265
|
return list.length ? list[0] : null;
|
|
2224
2266
|
}
|
|
2225
2267
|
|
|
2268
|
+
function getLatestLiveUndoTarget() {
|
|
2269
|
+
var list = getUnifiedHistoryItems();
|
|
2270
|
+
for (var i = 0; i < list.length; i++) {
|
|
2271
|
+
if (list[i] && list[i].source === 'live') return list[i];
|
|
2272
|
+
}
|
|
2273
|
+
return null;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2226
2276
|
function changesetListHasStructural(arr) {
|
|
2227
2277
|
if (!arr || !arr.length) return false;
|
|
2228
2278
|
for (var i = 0; i < arr.length; i++) {
|
|
@@ -2321,8 +2371,15 @@ function removeHistoryChangeset(idx, evt) {
|
|
|
2321
2371
|
function clearAllHistoryChangesets() {
|
|
2322
2372
|
var v = getActiveVariationForHistory();
|
|
2323
2373
|
if (!v) return;
|
|
2324
|
-
|
|
2374
|
+
var hasSavedChangesets = parseVariationChangesets(v).length > 0;
|
|
2375
|
+
var hasSessionStructural =
|
|
2376
|
+
!!(activeVarId && sessionStructuralChainRowsByVarId[activeVarId] && sessionStructuralChainRowsByVarId[activeVarId].length);
|
|
2377
|
+
if (!hasSavedChangesets && !hasSessionStructural) return;
|
|
2378
|
+
|
|
2325
2379
|
persistActiveVariationChangesets([]);
|
|
2380
|
+
if (activeVarId) {
|
|
2381
|
+
sessionStructuralChainRowsByVarId[activeVarId] = [];
|
|
2382
|
+
}
|
|
2326
2383
|
appliedChangesetSnapshots = {};
|
|
2327
2384
|
appliedStructuralChangesetKeys = {};
|
|
2328
2385
|
try {
|
|
@@ -2335,6 +2392,10 @@ function clearAllHistoryChangesets() {
|
|
|
2335
2392
|
}
|
|
2336
2393
|
|
|
2337
2394
|
function clearAllUnifiedHistory() {
|
|
2395
|
+
if (activeVarId) {
|
|
2396
|
+
// Ensure structural unsaved rows are also removed by "Clear all changes".
|
|
2397
|
+
sessionStructuralChainRowsByVarId[activeVarId] = [];
|
|
2398
|
+
}
|
|
2338
2399
|
clearAllStates();
|
|
2339
2400
|
clearAllHistoryChangesets();
|
|
2340
2401
|
if (currentMainTab === 'history') renderHistoryTab();
|
|
@@ -2346,15 +2407,32 @@ var VVE_LOCAL_STORAGE_PREFIX = 'vve:';
|
|
|
2346
2407
|
|
|
2347
2408
|
function clearVisualEditorLocalStorage() {
|
|
2348
2409
|
try {
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2410
|
+
var stores = [];
|
|
2411
|
+
try { stores.push(localStorage); } catch(_) {}
|
|
2412
|
+
try { stores.push(sessionStorage); } catch(_) {}
|
|
2413
|
+
for (var si = 0; si < stores.length; si++) {
|
|
2414
|
+
var store = stores[si];
|
|
2415
|
+
if (!store) continue;
|
|
2416
|
+
for (var i = store.length - 1; i >= 0; i--) {
|
|
2417
|
+
var k = store.key(i);
|
|
2418
|
+
if (k && k.indexOf(VVE_LOCAL_STORAGE_PREFIX) === 0) {
|
|
2419
|
+
store.removeItem(k);
|
|
2420
|
+
}
|
|
2353
2421
|
}
|
|
2354
2422
|
}
|
|
2355
2423
|
} catch(_) {}
|
|
2356
2424
|
}
|
|
2357
2425
|
|
|
2426
|
+
function clearPersistedActiveVariationForData(data) {
|
|
2427
|
+
try {
|
|
2428
|
+
var sk = activeVariationStorageKeyFromPayload(data);
|
|
2429
|
+
if (sk && sk !== VVE_LOCAL_STORAGE_PREFIX + 'activeVar::') {
|
|
2430
|
+
try { localStorage.removeItem(sk); } catch(_) {}
|
|
2431
|
+
try { sessionStorage.removeItem(sk); } catch(_) {}
|
|
2432
|
+
}
|
|
2433
|
+
} catch(_) {}
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2358
2436
|
function activeVariationStorageKeyFromPayload(data) {
|
|
2359
2437
|
return (
|
|
2360
2438
|
VVE_LOCAL_STORAGE_PREFIX +
|
|
@@ -2393,14 +2471,18 @@ function writePersistedActiveVariationId(varId) {
|
|
|
2393
2471
|
* @param allowPrevMemory when true, keep in-session activeVarId if still valid (skip-reload path).
|
|
2394
2472
|
*/
|
|
2395
2473
|
function pickActiveVariationIdForLoad(data, variationsArr, prevMemoryId, allowPrevMemory) {
|
|
2396
|
-
var
|
|
2397
|
-
var fallback = (
|
|
2474
|
+
var firstNonBaseline = variationsArr.find(function(v) { return !v.baseline; });
|
|
2475
|
+
var fallback = (firstNonBaseline || variationsArr[0] || {})._id || null;
|
|
2398
2476
|
if (!variationsArr.length) return null;
|
|
2399
2477
|
if (allowPrevMemory && prevMemoryId && variationsArr.some(function(v) { return v._id === prevMemoryId; })) {
|
|
2400
2478
|
return prevMemoryId;
|
|
2401
2479
|
}
|
|
2402
2480
|
var stored = readPersistedActiveVariationId(data);
|
|
2403
2481
|
if (stored && variationsArr.some(function(v) { return v._id === stored; })) {
|
|
2482
|
+
var storedVar = variationsArr.find(function(v) { return v._id === stored; });
|
|
2483
|
+
if (storedVar && storedVar.baseline && firstNonBaseline && firstNonBaseline._id) {
|
|
2484
|
+
return firstNonBaseline._id;
|
|
2485
|
+
}
|
|
2404
2486
|
return stored;
|
|
2405
2487
|
}
|
|
2406
2488
|
return fallback;
|
|
@@ -2624,7 +2706,13 @@ function granularAnySelectorMatches(doc, cs) {
|
|
|
2624
2706
|
|
|
2625
2707
|
/** Bust bfcache / same-URL no-op reloads so the iframe actually re-parses (loading \u2192 interactive). */
|
|
2626
2708
|
function appendIframeReloadBust(url) {
|
|
2627
|
-
|
|
2709
|
+
try {
|
|
2710
|
+
var u = new URL(String(url || ''), window.location.href);
|
|
2711
|
+
u.searchParams.set('_vve_reload', String(Date.now()));
|
|
2712
|
+
return u.toString();
|
|
2713
|
+
} catch(_) {
|
|
2714
|
+
return url;
|
|
2715
|
+
}
|
|
2628
2716
|
}
|
|
2629
2717
|
|
|
2630
2718
|
// True when the iframe contentDocument belongs to the current iframe.src navigation.
|
|
@@ -3002,6 +3090,44 @@ function appendSessionStructuralChainRow(varId, row) {
|
|
|
3002
3090
|
sessionStructuralChainRowsByVarId[varId].push(row);
|
|
3003
3091
|
}
|
|
3004
3092
|
|
|
3093
|
+
function markStructuralRowApplied(row) {
|
|
3094
|
+
try {
|
|
3095
|
+
var k = structuralChangesetDedupKey(row);
|
|
3096
|
+
if (k) appliedStructuralChangesetKeys[k] = true;
|
|
3097
|
+
} catch(_) {}
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
function logStructuralStateChange(row, label, value, targetEl) {
|
|
3101
|
+
if (!row || !row.selector) return;
|
|
3102
|
+
stateChanges.push({
|
|
3103
|
+
selector: row.selector,
|
|
3104
|
+
inputId: 'vve-struct',
|
|
3105
|
+
label: label || 'Structure change',
|
|
3106
|
+
cssProp: null,
|
|
3107
|
+
value: value != null ? String(value) : String(row.type || ''),
|
|
3108
|
+
targetEl: targetEl || null,
|
|
3109
|
+
originalValue: '',
|
|
3110
|
+
vveTs: row.vveTs || nextHistoryTimestamp(),
|
|
3111
|
+
isStructuralLive: true,
|
|
3112
|
+
structuralVarId: activeVarId || null,
|
|
3113
|
+
structuralType: row.type || '',
|
|
3114
|
+
});
|
|
3115
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
3116
|
+
commitStateChangesForActiveVariation();
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
function removeSessionStructuralRowByTimestamp(varId, ts) {
|
|
3120
|
+
if (!varId || !ts) return;
|
|
3121
|
+
var arr = sessionStructuralChainRowsByVarId[varId];
|
|
3122
|
+
if (!arr || !arr.length) return;
|
|
3123
|
+
for (var i = arr.length - 1; i >= 0; i--) {
|
|
3124
|
+
if (arr[i] && arr[i].vveTs === ts) {
|
|
3125
|
+
arr.splice(i, 1);
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3005
3131
|
/** One States-tab row -> Conversion.io chain-set shape (matches applyChangesetEntry). */
|
|
3006
3132
|
function stateChangeToChainSet(c) {
|
|
3007
3133
|
if (!c || !c.selector) return null;
|
|
@@ -3696,12 +3822,15 @@ function duplicateSelectedEl() {
|
|
|
3696
3822
|
clone.setAttribute('data-vve-instance', generateVveInstanceId());
|
|
3697
3823
|
} catch(_) {}
|
|
3698
3824
|
if (activeVarId) {
|
|
3699
|
-
|
|
3825
|
+
var dupRow = {
|
|
3700
3826
|
selector: anchorSel,
|
|
3701
3827
|
type: 'insert',
|
|
3702
3828
|
action: 'after',
|
|
3703
3829
|
html: clone.outerHTML,
|
|
3704
|
-
}
|
|
3830
|
+
};
|
|
3831
|
+
appendSessionStructuralChainRow(activeVarId, dupRow);
|
|
3832
|
+
markStructuralRowApplied(dupRow);
|
|
3833
|
+
logStructuralStateChange(dupRow, 'Duplicated via toolbar', 'Duplicate inserted', selectedEl);
|
|
3705
3834
|
}
|
|
3706
3835
|
selectedEl.parentNode.insertBefore(clone, selectedEl.nextSibling);
|
|
3707
3836
|
saveCurrentVariationHtml();
|
|
@@ -3717,23 +3846,27 @@ function toggleHideSelectedEl() {
|
|
|
3717
3846
|
selectedEl.style.visibility = '';
|
|
3718
3847
|
selectedEl.removeAttribute('data-vve-hidden');
|
|
3719
3848
|
if (activeVarId) {
|
|
3720
|
-
|
|
3849
|
+
var showRow = {
|
|
3721
3850
|
selector: hidSel,
|
|
3722
3851
|
type: 'style',
|
|
3723
3852
|
property: 'visibility',
|
|
3724
3853
|
value: '',
|
|
3725
|
-
}
|
|
3854
|
+
};
|
|
3855
|
+
appendSessionStructuralChainRow(activeVarId, showRow);
|
|
3856
|
+
logStructuralStateChange(showRow, 'Shown via toolbar', 'Visibility restored', selectedEl);
|
|
3726
3857
|
}
|
|
3727
3858
|
} else {
|
|
3728
3859
|
selectedEl.style.visibility = 'hidden';
|
|
3729
3860
|
selectedEl.setAttribute('data-vve-hidden', '1');
|
|
3730
3861
|
if (activeVarId) {
|
|
3731
|
-
|
|
3862
|
+
var hideRow = {
|
|
3732
3863
|
selector: hidSel,
|
|
3733
3864
|
type: 'style',
|
|
3734
3865
|
property: 'visibility',
|
|
3735
3866
|
value: 'hidden',
|
|
3736
|
-
}
|
|
3867
|
+
};
|
|
3868
|
+
appendSessionStructuralChainRow(activeVarId, hideRow);
|
|
3869
|
+
logStructuralStateChange(hideRow, 'Hidden via toolbar', 'Visibility set to hidden', selectedEl);
|
|
3737
3870
|
}
|
|
3738
3871
|
}
|
|
3739
3872
|
saveCurrentVariationHtml();
|
|
@@ -3744,7 +3877,11 @@ function deleteSelectedEl() {
|
|
|
3744
3877
|
if (!selectedEl || !selectedEl.parentNode) return;
|
|
3745
3878
|
var delSel = buildSelector(selectedEl);
|
|
3746
3879
|
selectedEl.remove();
|
|
3747
|
-
if (activeVarId)
|
|
3880
|
+
if (activeVarId) {
|
|
3881
|
+
var delRow = { selector: delSel, type: 'remove' };
|
|
3882
|
+
appendSessionStructuralChainRow(activeVarId, delRow);
|
|
3883
|
+
logStructuralStateChange(delRow, 'Deleted via toolbar', 'Element removed', null);
|
|
3884
|
+
}
|
|
3748
3885
|
saveCurrentVariationHtml();
|
|
3749
3886
|
recomputeEditorDirty();
|
|
3750
3887
|
deselectElement();
|
|
@@ -4645,19 +4782,25 @@ function recordReorderAfterDrag(movedEl) {
|
|
|
4645
4782
|
var prev = movedEl.previousElementSibling;
|
|
4646
4783
|
var next = movedEl.nextElementSibling;
|
|
4647
4784
|
if (prev) {
|
|
4648
|
-
|
|
4785
|
+
var reorderAfterRow = {
|
|
4649
4786
|
selector: buildSelector(movedEl),
|
|
4650
4787
|
type: 'reorder',
|
|
4651
4788
|
targetSelector: buildSelector(prev),
|
|
4652
4789
|
action: 'after',
|
|
4653
|
-
}
|
|
4790
|
+
};
|
|
4791
|
+
appendSessionStructuralChainRow(activeVarId, reorderAfterRow);
|
|
4792
|
+
markStructuralRowApplied(reorderAfterRow);
|
|
4793
|
+
logStructuralStateChange(reorderAfterRow, 'Reordered via drag', 'Moved after sibling', movedEl);
|
|
4654
4794
|
} else if (next) {
|
|
4655
|
-
|
|
4795
|
+
var reorderBeforeRow = {
|
|
4656
4796
|
selector: buildSelector(movedEl),
|
|
4657
4797
|
type: 'reorder',
|
|
4658
4798
|
targetSelector: buildSelector(next),
|
|
4659
4799
|
action: 'before',
|
|
4660
|
-
}
|
|
4800
|
+
};
|
|
4801
|
+
appendSessionStructuralChainRow(activeVarId, reorderBeforeRow);
|
|
4802
|
+
markStructuralRowApplied(reorderBeforeRow);
|
|
4803
|
+
logStructuralStateChange(reorderBeforeRow, 'Reordered via drag', 'Moved before sibling', movedEl);
|
|
4661
4804
|
}
|
|
4662
4805
|
}
|
|
4663
4806
|
|
|
@@ -4938,12 +5081,15 @@ function insertHtml(html) {
|
|
|
4938
5081
|
}
|
|
4939
5082
|
if (firstEl) selectElement(firstEl);
|
|
4940
5083
|
if (activeVarId) {
|
|
4941
|
-
|
|
5084
|
+
var insertRow = {
|
|
4942
5085
|
selector: anchorSel,
|
|
4943
5086
|
type: 'insert',
|
|
4944
5087
|
action: 'after',
|
|
4945
5088
|
html: htmlStr,
|
|
4946
|
-
}
|
|
5089
|
+
};
|
|
5090
|
+
appendSessionStructuralChainRow(activeVarId, insertRow);
|
|
5091
|
+
markStructuralRowApplied(insertRow);
|
|
5092
|
+
logStructuralStateChange(insertRow, 'Added component/section', 'Inserted new element', firstEl || selectedEl);
|
|
4947
5093
|
}
|
|
4948
5094
|
saveCurrentVariationHtml();
|
|
4949
5095
|
recomputeEditorDirty();
|
|
@@ -5033,6 +5179,14 @@ document.getElementById('comp-search').addEventListener('input', function() {
|
|
|
5033
5179
|
renderSidebar(this.value);
|
|
5034
5180
|
}
|
|
5035
5181
|
});
|
|
5182
|
+
var btnAddElement = document.getElementById('btn-add-element');
|
|
5183
|
+
if (btnAddElement) {
|
|
5184
|
+
btnAddElement.addEventListener('click', function(e) {
|
|
5185
|
+
e.preventDefault();
|
|
5186
|
+
e.stopPropagation();
|
|
5187
|
+
toggleSectionComponentsPanel();
|
|
5188
|
+
});
|
|
5189
|
+
}
|
|
5036
5190
|
|
|
5037
5191
|
// \u2500\u2500 Save / Close \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5038
5192
|
document.getElementById('btn-save').addEventListener('click', handleSave);
|
|
@@ -5073,11 +5227,21 @@ function handleSave() {
|
|
|
5073
5227
|
}
|
|
5074
5228
|
|
|
5075
5229
|
function handleClose() {
|
|
5230
|
+
clearPersistedActiveVariationForData(experimentData);
|
|
5076
5231
|
clearVisualEditorLocalStorage();
|
|
5077
5232
|
// Unsaved-changes UX lives in the parent (PlatformVisualEditorV2); avoid double confirm here.
|
|
5078
5233
|
send('close-editor', {});
|
|
5079
5234
|
}
|
|
5080
5235
|
|
|
5236
|
+
// Defensive cleanup: if parent closes/unmounts the editor shell without
|
|
5237
|
+
// invoking handleClose(), clear persisted VVE keys on unload as well.
|
|
5238
|
+
window.addEventListener('pagehide', function() {
|
|
5239
|
+
clearVisualEditorLocalStorage();
|
|
5240
|
+
});
|
|
5241
|
+
window.addEventListener('beforeunload', function() {
|
|
5242
|
+
clearVisualEditorLocalStorage();
|
|
5243
|
+
});
|
|
5244
|
+
|
|
5081
5245
|
// \u2500\u2500 Keyboard shortcuts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
5082
5246
|
function isNativeEditableTarget(target) {
|
|
5083
5247
|
if (!target || target.nodeType !== 1) return false;
|
|
@@ -5099,10 +5263,7 @@ document.addEventListener('keydown', function(e) {
|
|
|
5099
5263
|
e.preventDefault();
|
|
5100
5264
|
runEditorUndo();
|
|
5101
5265
|
}
|
|
5102
|
-
|
|
5103
|
-
e.preventDefault();
|
|
5104
|
-
runEditorRedo();
|
|
5105
|
-
}
|
|
5266
|
+
// Redo is intentionally hidden/disabled in this editor flow.
|
|
5106
5267
|
if (meta && e.key === 's') { e.preventDefault(); handleSave(); }
|
|
5107
5268
|
if (e.key === 'Escape') {
|
|
5108
5269
|
var openTips = document.querySelectorAll('.ve-pl-tip.is-tip-open');
|
|
@@ -5122,11 +5283,17 @@ document.addEventListener('keydown', function(e) {
|
|
|
5122
5283
|
}
|
|
5123
5284
|
});
|
|
5124
5285
|
function runEditorUndo() {
|
|
5125
|
-
|
|
5126
|
-
if (
|
|
5127
|
-
|
|
5286
|
+
// Undo only unsaved in-session edits; do not remove persisted history rows.
|
|
5287
|
+
if (!isDirty) return;
|
|
5288
|
+
|
|
5289
|
+
// 1) Prefer live unsaved change stack (stateChanges shown in History tab).
|
|
5290
|
+
var liveTarget = getLatestLiveUndoTarget();
|
|
5291
|
+
if (liveTarget) {
|
|
5292
|
+
removeHistoryItem('live', liveTarget.idx);
|
|
5128
5293
|
return;
|
|
5129
5294
|
}
|
|
5295
|
+
|
|
5296
|
+
// 2) Fallback to Vvveb internal undo stack (e.g. structural drag/drop ops).
|
|
5130
5297
|
if (!(typeof Vvveb !== 'undefined' && Vvveb.Undo)) return;
|
|
5131
5298
|
Vvveb.Undo.undo();
|
|
5132
5299
|
saveCurrentVariationHtml();
|
|
@@ -5149,11 +5316,10 @@ document.getElementById('btn-undo').addEventListener('click', function(e) {
|
|
|
5149
5316
|
e.stopPropagation();
|
|
5150
5317
|
runEditorUndo();
|
|
5151
5318
|
});
|
|
5152
|
-
document.getElementById('btn-redo')
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
});
|
|
5319
|
+
var btnRedo = document.getElementById('btn-redo');
|
|
5320
|
+
if (btnRedo) {
|
|
5321
|
+
btnRedo.style.display = 'none';
|
|
5322
|
+
}
|
|
5157
5323
|
|
|
5158
5324
|
function layoutLoadingTooltip(host) {
|
|
5159
5325
|
var tip = host.querySelector('.ve-pl-tooltip');
|
|
@@ -5962,8 +6128,22 @@ if(window.fetch){
|
|
|
5962
6128
|
try{
|
|
5963
6129
|
var u=afterUrl?resolveUrl(afterUrl):null;
|
|
5964
6130
|
var sameOrigin=!!(u&&u.origin===TARGET_ORIGIN);
|
|
5965
|
-
var
|
|
5966
|
-
|
|
6131
|
+
var reqMethod=(init&&init.method?String(init.method):(input&&input.method?String(input.method):"GET")).toUpperCase();
|
|
6132
|
+
var isSafeMethod=reqMethod==="GET"||reqMethod==="HEAD";
|
|
6133
|
+
var path=(u&&u.pathname?u.pathname:"").toLowerCase();
|
|
6134
|
+
var qs=(u&&u.search?u.search:"").toLowerCase();
|
|
6135
|
+
var likelyBackgroundEndpoint=!!(u&&(
|
|
6136
|
+
/(^|\\/)apps?(\\/|$)|(^|\\/)a(\\/|$)/.test(path)||
|
|
6137
|
+
path==="/cart"||
|
|
6138
|
+
path==="/cart.js"||
|
|
6139
|
+
path.indexOf("/cart/")===0||
|
|
6140
|
+
path.indexOf("/recommendations/")===0||
|
|
6141
|
+
path.indexOf("/search/suggest")===0||
|
|
6142
|
+
qs.indexOf("sections=")!==-1||
|
|
6143
|
+
qs.indexOf("section_id=")!==-1
|
|
6144
|
+
));
|
|
6145
|
+
var likelyThirdPartyBg=!sameOrigin||likelyBackgroundEndpoint;
|
|
6146
|
+
if(window.__CONVERSION_EDITOR_ACTIVE__&&isSafeMethod&&likelyThirdPartyBg){
|
|
5967
6147
|
console.warn("[conversion-proxy] suppressed fetch failure",{url:afterUrl,error:err&&err.message?err.message:String(err)});
|
|
5968
6148
|
return new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}});
|
|
5969
6149
|
}
|