@accelerated-agency/visual-editor 0.4.2 → 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/index.js +23 -3
- package/dist/vite.cjs +278 -58
- package/dist/vite.js +278 -58
- package/package.json +3 -2
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">
|
|
@@ -1140,6 +1151,8 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1140
1151
|
</div>
|
|
1141
1152
|
|
|
1142
1153
|
<!-- CDN scripts -->
|
|
1154
|
+
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
|
1155
|
+
<script src="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.13.3/jquery-ui.min.js"></script>
|
|
1143
1156
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
1144
1157
|
<script>
|
|
1145
1158
|
window.__veLoadFallback = function(el, candidates){
|
|
@@ -1167,10 +1180,6 @@ window.__veLoadFallback = function(el, candidates){
|
|
|
1167
1180
|
onerror="window.__veLoadFallback(this,'https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/inputs.js||https://unpkg.com/vvvebjs@latest/libs/builder/inputs.js')"
|
|
1168
1181
|
></script>
|
|
1169
1182
|
<!-- components.js removed (frequently missing on npm CDN); safety stubs below prevent bootstrap/widgets loader errors -->
|
|
1170
|
-
// <script
|
|
1171
|
-
// src="https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-widgets.js"
|
|
1172
|
-
// onerror="window.__veLoadFallback(this,'https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-widgets.js||https://unpkg.com/vvvebjs@latest/libs/builder/components-widgets.js')"
|
|
1173
|
-
// ></script>
|
|
1174
1183
|
<script>
|
|
1175
1184
|
/* Safety stub: if components.js didn't define these, create empty arrays so
|
|
1176
1185
|
components-bootstrap5.js doesn't throw ReferenceError on load. */
|
|
@@ -1199,7 +1208,7 @@ if (window.Vvveb && window.Vvveb.Components) {
|
|
|
1199
1208
|
</script>
|
|
1200
1209
|
<script
|
|
1201
1210
|
src="https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-bootstrap4.js"
|
|
1202
|
-
onerror="window.__veLoadFallback(this,'https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-bootstrap4.js||https://unpkg.com/vvvebjs@latest/libs/builder/components-bootstrap4.js')"
|
|
1211
|
+
onerror="window.__veLoadFallback(this,'https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-bootstrap5.js||https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-bootstrap4.js||https://unpkg.com/vvvebjs@latest/libs/builder/components-bootstrap5.js||https://unpkg.com/vvvebjs@latest/libs/builder/components-bootstrap4.js')"
|
|
1203
1212
|
></script>
|
|
1204
1213
|
<script
|
|
1205
1214
|
src="https://cdn.jsdelivr.net/npm/vvvebjs@latest/libs/builder/components-widgets.js"
|
|
@@ -1668,6 +1677,19 @@ function switchSectionComponentsTab(tab) {
|
|
|
1668
1677
|
renderSidebar(inp ? inp.value : '');
|
|
1669
1678
|
}
|
|
1670
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
|
+
|
|
1671
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
|
|
1672
1694
|
function toggleAcc(name) {
|
|
1673
1695
|
var sec = document.getElementById('acc-' + name);
|
|
@@ -1877,6 +1899,24 @@ function syncDesignInput(change) {
|
|
|
1877
1899
|
function removeStateChange(idx) {
|
|
1878
1900
|
var change = stateChanges[idx];
|
|
1879
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
|
+
}
|
|
1880
1920
|
revertChangeOnDom(change);
|
|
1881
1921
|
syncDesignInput(change);
|
|
1882
1922
|
stateChanges.splice(idx, 1);
|
|
@@ -2233,6 +2273,14 @@ function getLatestHistoryUndoTarget() {
|
|
|
2233
2273
|
return list.length ? list[0] : null;
|
|
2234
2274
|
}
|
|
2235
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
|
+
|
|
2236
2284
|
function changesetListHasStructural(arr) {
|
|
2237
2285
|
if (!arr || !arr.length) return false;
|
|
2238
2286
|
for (var i = 0; i < arr.length; i++) {
|
|
@@ -2331,8 +2379,15 @@ function removeHistoryChangeset(idx, evt) {
|
|
|
2331
2379
|
function clearAllHistoryChangesets() {
|
|
2332
2380
|
var v = getActiveVariationForHistory();
|
|
2333
2381
|
if (!v) return;
|
|
2334
|
-
|
|
2382
|
+
var hasSavedChangesets = parseVariationChangesets(v).length > 0;
|
|
2383
|
+
var hasSessionStructural =
|
|
2384
|
+
!!(activeVarId && sessionStructuralChainRowsByVarId[activeVarId] && sessionStructuralChainRowsByVarId[activeVarId].length);
|
|
2385
|
+
if (!hasSavedChangesets && !hasSessionStructural) return;
|
|
2386
|
+
|
|
2335
2387
|
persistActiveVariationChangesets([]);
|
|
2388
|
+
if (activeVarId) {
|
|
2389
|
+
sessionStructuralChainRowsByVarId[activeVarId] = [];
|
|
2390
|
+
}
|
|
2336
2391
|
appliedChangesetSnapshots = {};
|
|
2337
2392
|
appliedStructuralChangesetKeys = {};
|
|
2338
2393
|
try {
|
|
@@ -2345,6 +2400,10 @@ function clearAllHistoryChangesets() {
|
|
|
2345
2400
|
}
|
|
2346
2401
|
|
|
2347
2402
|
function clearAllUnifiedHistory() {
|
|
2403
|
+
if (activeVarId) {
|
|
2404
|
+
// Ensure structural unsaved rows are also removed by "Clear all changes".
|
|
2405
|
+
sessionStructuralChainRowsByVarId[activeVarId] = [];
|
|
2406
|
+
}
|
|
2348
2407
|
clearAllStates();
|
|
2349
2408
|
clearAllHistoryChangesets();
|
|
2350
2409
|
if (currentMainTab === 'history') renderHistoryTab();
|
|
@@ -2356,15 +2415,32 @@ var VVE_LOCAL_STORAGE_PREFIX = 'vve:';
|
|
|
2356
2415
|
|
|
2357
2416
|
function clearVisualEditorLocalStorage() {
|
|
2358
2417
|
try {
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
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
|
+
}
|
|
2363
2429
|
}
|
|
2364
2430
|
}
|
|
2365
2431
|
} catch(_) {}
|
|
2366
2432
|
}
|
|
2367
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
|
+
|
|
2368
2444
|
function activeVariationStorageKeyFromPayload(data) {
|
|
2369
2445
|
return (
|
|
2370
2446
|
VVE_LOCAL_STORAGE_PREFIX +
|
|
@@ -2403,14 +2479,18 @@ function writePersistedActiveVariationId(varId) {
|
|
|
2403
2479
|
* @param allowPrevMemory when true, keep in-session activeVarId if still valid (skip-reload path).
|
|
2404
2480
|
*/
|
|
2405
2481
|
function pickActiveVariationIdForLoad(data, variationsArr, prevMemoryId, allowPrevMemory) {
|
|
2406
|
-
var
|
|
2407
|
-
var fallback = (
|
|
2482
|
+
var firstNonBaseline = variationsArr.find(function(v) { return !v.baseline; });
|
|
2483
|
+
var fallback = (firstNonBaseline || variationsArr[0] || {})._id || null;
|
|
2408
2484
|
if (!variationsArr.length) return null;
|
|
2409
2485
|
if (allowPrevMemory && prevMemoryId && variationsArr.some(function(v) { return v._id === prevMemoryId; })) {
|
|
2410
2486
|
return prevMemoryId;
|
|
2411
2487
|
}
|
|
2412
2488
|
var stored = readPersistedActiveVariationId(data);
|
|
2413
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
|
+
}
|
|
2414
2494
|
return stored;
|
|
2415
2495
|
}
|
|
2416
2496
|
return fallback;
|
|
@@ -2432,12 +2512,24 @@ function handleLoadExperiment(data) {
|
|
|
2432
2512
|
var extraTrackingMarkers = Array.isArray(data && data.trackingMarkers)
|
|
2433
2513
|
? data.trackingMarkers.filter(function(m){return typeof m === 'string' && m.trim().length > 0;})
|
|
2434
2514
|
: [];
|
|
2515
|
+
var conversionProxyBaseUrl = (typeof (data && data.conversionProxyBaseUrl) === 'string'
|
|
2516
|
+
? data.conversionProxyBaseUrl
|
|
2517
|
+
: '').trim().replace(/\\/+$/, '');
|
|
2518
|
+
var proxyPath = '/api/conversion-proxy';
|
|
2519
|
+
// Keep iframe navigation same-origin for DOM access (selection/editing).
|
|
2520
|
+
// If conversionProxyBaseUrl is provided, middleware delegates upstream fetches
|
|
2521
|
+
// server-side while browser URLs stay local.
|
|
2522
|
+
var proxyRoot = proxyPath;
|
|
2435
2523
|
var trackingMarkersParam = extraTrackingMarkers.length
|
|
2436
2524
|
? '&trackingMarkers=' + encodeURIComponent(JSON.stringify(extraTrackingMarkers))
|
|
2437
2525
|
: '';
|
|
2438
|
-
var
|
|
2526
|
+
var conversionProxyBaseUrlParam = conversionProxyBaseUrl
|
|
2527
|
+
? '&conversionProxyBaseUrl=' + encodeURIComponent(conversionProxyBaseUrl)
|
|
2528
|
+
: '';
|
|
2529
|
+
var proxyUrl = proxyRoot + '?password=' + encodeURIComponent(data.editorPassword || '') +
|
|
2439
2530
|
'&url=' + encodeURIComponent(pageUrl) +
|
|
2440
2531
|
'&strictObserverFreeze=' + encodeURIComponent(data && data.strictObserverFreeze ? '1' : '0') +
|
|
2532
|
+
conversionProxyBaseUrlParam +
|
|
2441
2533
|
trackingMarkersParam;
|
|
2442
2534
|
|
|
2443
2535
|
// Parent often re-posts load-experiment when React re-renders (new object identity) or
|
|
@@ -2622,7 +2714,13 @@ function granularAnySelectorMatches(doc, cs) {
|
|
|
2622
2714
|
|
|
2623
2715
|
/** Bust bfcache / same-URL no-op reloads so the iframe actually re-parses (loading \u2192 interactive). */
|
|
2624
2716
|
function appendIframeReloadBust(url) {
|
|
2625
|
-
|
|
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
|
+
}
|
|
2626
2724
|
}
|
|
2627
2725
|
|
|
2628
2726
|
// True when the iframe contentDocument belongs to the current iframe.src navigation.
|
|
@@ -3000,6 +3098,44 @@ function appendSessionStructuralChainRow(varId, row) {
|
|
|
3000
3098
|
sessionStructuralChainRowsByVarId[varId].push(row);
|
|
3001
3099
|
}
|
|
3002
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
|
+
|
|
3003
3139
|
/** One States-tab row -> Conversion.io chain-set shape (matches applyChangesetEntry). */
|
|
3004
3140
|
function stateChangeToChainSet(c) {
|
|
3005
3141
|
if (!c || !c.selector) return null;
|
|
@@ -3694,12 +3830,15 @@ function duplicateSelectedEl() {
|
|
|
3694
3830
|
clone.setAttribute('data-vve-instance', generateVveInstanceId());
|
|
3695
3831
|
} catch(_) {}
|
|
3696
3832
|
if (activeVarId) {
|
|
3697
|
-
|
|
3833
|
+
var dupRow = {
|
|
3698
3834
|
selector: anchorSel,
|
|
3699
3835
|
type: 'insert',
|
|
3700
3836
|
action: 'after',
|
|
3701
3837
|
html: clone.outerHTML,
|
|
3702
|
-
}
|
|
3838
|
+
};
|
|
3839
|
+
appendSessionStructuralChainRow(activeVarId, dupRow);
|
|
3840
|
+
markStructuralRowApplied(dupRow);
|
|
3841
|
+
logStructuralStateChange(dupRow, 'Duplicated via toolbar', 'Duplicate inserted', selectedEl);
|
|
3703
3842
|
}
|
|
3704
3843
|
selectedEl.parentNode.insertBefore(clone, selectedEl.nextSibling);
|
|
3705
3844
|
saveCurrentVariationHtml();
|
|
@@ -3715,23 +3854,27 @@ function toggleHideSelectedEl() {
|
|
|
3715
3854
|
selectedEl.style.visibility = '';
|
|
3716
3855
|
selectedEl.removeAttribute('data-vve-hidden');
|
|
3717
3856
|
if (activeVarId) {
|
|
3718
|
-
|
|
3857
|
+
var showRow = {
|
|
3719
3858
|
selector: hidSel,
|
|
3720
3859
|
type: 'style',
|
|
3721
3860
|
property: 'visibility',
|
|
3722
3861
|
value: '',
|
|
3723
|
-
}
|
|
3862
|
+
};
|
|
3863
|
+
appendSessionStructuralChainRow(activeVarId, showRow);
|
|
3864
|
+
logStructuralStateChange(showRow, 'Shown via toolbar', 'Visibility restored', selectedEl);
|
|
3724
3865
|
}
|
|
3725
3866
|
} else {
|
|
3726
3867
|
selectedEl.style.visibility = 'hidden';
|
|
3727
3868
|
selectedEl.setAttribute('data-vve-hidden', '1');
|
|
3728
3869
|
if (activeVarId) {
|
|
3729
|
-
|
|
3870
|
+
var hideRow = {
|
|
3730
3871
|
selector: hidSel,
|
|
3731
3872
|
type: 'style',
|
|
3732
3873
|
property: 'visibility',
|
|
3733
3874
|
value: 'hidden',
|
|
3734
|
-
}
|
|
3875
|
+
};
|
|
3876
|
+
appendSessionStructuralChainRow(activeVarId, hideRow);
|
|
3877
|
+
logStructuralStateChange(hideRow, 'Hidden via toolbar', 'Visibility set to hidden', selectedEl);
|
|
3735
3878
|
}
|
|
3736
3879
|
}
|
|
3737
3880
|
saveCurrentVariationHtml();
|
|
@@ -3742,7 +3885,11 @@ function deleteSelectedEl() {
|
|
|
3742
3885
|
if (!selectedEl || !selectedEl.parentNode) return;
|
|
3743
3886
|
var delSel = buildSelector(selectedEl);
|
|
3744
3887
|
selectedEl.remove();
|
|
3745
|
-
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
|
+
}
|
|
3746
3893
|
saveCurrentVariationHtml();
|
|
3747
3894
|
recomputeEditorDirty();
|
|
3748
3895
|
deselectElement();
|
|
@@ -4643,19 +4790,25 @@ function recordReorderAfterDrag(movedEl) {
|
|
|
4643
4790
|
var prev = movedEl.previousElementSibling;
|
|
4644
4791
|
var next = movedEl.nextElementSibling;
|
|
4645
4792
|
if (prev) {
|
|
4646
|
-
|
|
4793
|
+
var reorderAfterRow = {
|
|
4647
4794
|
selector: buildSelector(movedEl),
|
|
4648
4795
|
type: 'reorder',
|
|
4649
4796
|
targetSelector: buildSelector(prev),
|
|
4650
4797
|
action: 'after',
|
|
4651
|
-
}
|
|
4798
|
+
};
|
|
4799
|
+
appendSessionStructuralChainRow(activeVarId, reorderAfterRow);
|
|
4800
|
+
markStructuralRowApplied(reorderAfterRow);
|
|
4801
|
+
logStructuralStateChange(reorderAfterRow, 'Reordered via drag', 'Moved after sibling', movedEl);
|
|
4652
4802
|
} else if (next) {
|
|
4653
|
-
|
|
4803
|
+
var reorderBeforeRow = {
|
|
4654
4804
|
selector: buildSelector(movedEl),
|
|
4655
4805
|
type: 'reorder',
|
|
4656
4806
|
targetSelector: buildSelector(next),
|
|
4657
4807
|
action: 'before',
|
|
4658
|
-
}
|
|
4808
|
+
};
|
|
4809
|
+
appendSessionStructuralChainRow(activeVarId, reorderBeforeRow);
|
|
4810
|
+
markStructuralRowApplied(reorderBeforeRow);
|
|
4811
|
+
logStructuralStateChange(reorderBeforeRow, 'Reordered via drag', 'Moved before sibling', movedEl);
|
|
4659
4812
|
}
|
|
4660
4813
|
}
|
|
4661
4814
|
|
|
@@ -4936,12 +5089,15 @@ function insertHtml(html) {
|
|
|
4936
5089
|
}
|
|
4937
5090
|
if (firstEl) selectElement(firstEl);
|
|
4938
5091
|
if (activeVarId) {
|
|
4939
|
-
|
|
5092
|
+
var insertRow = {
|
|
4940
5093
|
selector: anchorSel,
|
|
4941
5094
|
type: 'insert',
|
|
4942
5095
|
action: 'after',
|
|
4943
5096
|
html: htmlStr,
|
|
4944
|
-
}
|
|
5097
|
+
};
|
|
5098
|
+
appendSessionStructuralChainRow(activeVarId, insertRow);
|
|
5099
|
+
markStructuralRowApplied(insertRow);
|
|
5100
|
+
logStructuralStateChange(insertRow, 'Added component/section', 'Inserted new element', firstEl || selectedEl);
|
|
4945
5101
|
}
|
|
4946
5102
|
saveCurrentVariationHtml();
|
|
4947
5103
|
recomputeEditorDirty();
|
|
@@ -5031,6 +5187,14 @@ document.getElementById('comp-search').addEventListener('input', function() {
|
|
|
5031
5187
|
renderSidebar(this.value);
|
|
5032
5188
|
}
|
|
5033
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
|
+
}
|
|
5034
5198
|
|
|
5035
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
|
|
5036
5200
|
document.getElementById('btn-save').addEventListener('click', handleSave);
|
|
@@ -5071,11 +5235,21 @@ function handleSave() {
|
|
|
5071
5235
|
}
|
|
5072
5236
|
|
|
5073
5237
|
function handleClose() {
|
|
5238
|
+
clearPersistedActiveVariationForData(experimentData);
|
|
5074
5239
|
clearVisualEditorLocalStorage();
|
|
5075
5240
|
// Unsaved-changes UX lives in the parent (PlatformVisualEditorV2); avoid double confirm here.
|
|
5076
5241
|
send('close-editor', {});
|
|
5077
5242
|
}
|
|
5078
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
|
+
|
|
5079
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
|
|
5080
5254
|
function isNativeEditableTarget(target) {
|
|
5081
5255
|
if (!target || target.nodeType !== 1) return false;
|
|
@@ -5097,10 +5271,7 @@ document.addEventListener('keydown', function(e) {
|
|
|
5097
5271
|
e.preventDefault();
|
|
5098
5272
|
runEditorUndo();
|
|
5099
5273
|
}
|
|
5100
|
-
|
|
5101
|
-
e.preventDefault();
|
|
5102
|
-
runEditorRedo();
|
|
5103
|
-
}
|
|
5274
|
+
// Redo is intentionally hidden/disabled in this editor flow.
|
|
5104
5275
|
if (meta && e.key === 's') { e.preventDefault(); handleSave(); }
|
|
5105
5276
|
if (e.key === 'Escape') {
|
|
5106
5277
|
var openTips = document.querySelectorAll('.ve-pl-tip.is-tip-open');
|
|
@@ -5120,11 +5291,17 @@ document.addEventListener('keydown', function(e) {
|
|
|
5120
5291
|
}
|
|
5121
5292
|
});
|
|
5122
5293
|
function runEditorUndo() {
|
|
5123
|
-
|
|
5124
|
-
if (
|
|
5125
|
-
|
|
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);
|
|
5126
5301
|
return;
|
|
5127
5302
|
}
|
|
5303
|
+
|
|
5304
|
+
// 2) Fallback to Vvveb internal undo stack (e.g. structural drag/drop ops).
|
|
5128
5305
|
if (!(typeof Vvveb !== 'undefined' && Vvveb.Undo)) return;
|
|
5129
5306
|
Vvveb.Undo.undo();
|
|
5130
5307
|
saveCurrentVariationHtml();
|
|
@@ -5147,11 +5324,10 @@ document.getElementById('btn-undo').addEventListener('click', function(e) {
|
|
|
5147
5324
|
e.stopPropagation();
|
|
5148
5325
|
runEditorUndo();
|
|
5149
5326
|
});
|
|
5150
|
-
document.getElementById('btn-redo')
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
});
|
|
5327
|
+
var btnRedo = document.getElementById('btn-redo');
|
|
5328
|
+
if (btnRedo) {
|
|
5329
|
+
btnRedo.style.display = 'none';
|
|
5330
|
+
}
|
|
5155
5331
|
|
|
5156
5332
|
function layoutLoadingTooltip(host) {
|
|
5157
5333
|
var tip = host.querySelector('.ve-pl-tooltip');
|
|
@@ -5523,6 +5699,8 @@ function createVisualEditorMiddleware(options) {
|
|
|
5523
5699
|
const url = new URL(req.url || "", "http://localhost");
|
|
5524
5700
|
const targetUrl = url.searchParams.get("url");
|
|
5525
5701
|
const password = url.searchParams.get("password") || "";
|
|
5702
|
+
const conversionProxyBaseUrlForRequest = (url.searchParams.get("conversionProxyBaseUrl") || "").trim().replace(/\/+$/, "");
|
|
5703
|
+
const proxyRootForRequest = "/api/conversion-proxy";
|
|
5526
5704
|
const extraTrackingMarkersForRequest = parseTrackingMarkersParam(
|
|
5527
5705
|
url.searchParams.get("trackingMarkers")
|
|
5528
5706
|
);
|
|
@@ -5575,7 +5753,34 @@ function createVisualEditorMiddleware(options) {
|
|
|
5575
5753
|
return false;
|
|
5576
5754
|
}
|
|
5577
5755
|
};
|
|
5756
|
+
const workerRawFetch = (input, init = {}) => {
|
|
5757
|
+
const workerUrl = new URL("/api/conversion-proxy", conversionProxyBaseUrlForRequest);
|
|
5758
|
+
workerUrl.searchParams.set("url", input);
|
|
5759
|
+
workerUrl.searchParams.set("raw", "1");
|
|
5760
|
+
const method2 = (init?.method || "GET").toUpperCase();
|
|
5761
|
+
const nextHeaders = {};
|
|
5762
|
+
const h = init?.headers;
|
|
5763
|
+
if (h && typeof h.forEach === "function") {
|
|
5764
|
+
h.forEach((v, k) => {
|
|
5765
|
+
if (!v) return;
|
|
5766
|
+
nextHeaders[k] = String(v);
|
|
5767
|
+
});
|
|
5768
|
+
} else {
|
|
5769
|
+
Object.entries(h || {}).forEach(([k, v]) => {
|
|
5770
|
+
if (!v) return;
|
|
5771
|
+
nextHeaders[k] = Array.isArray(v) ? v.join(",") : String(v);
|
|
5772
|
+
});
|
|
5773
|
+
}
|
|
5774
|
+
return fetch(workerUrl.toString(), {
|
|
5775
|
+
...init,
|
|
5776
|
+
method: method2,
|
|
5777
|
+
headers: nextHeaders
|
|
5778
|
+
});
|
|
5779
|
+
};
|
|
5578
5780
|
const upstreamFetch = async (input, init = {}) => {
|
|
5781
|
+
if (conversionProxyBaseUrlForRequest) {
|
|
5782
|
+
return workerRawFetch(input, init);
|
|
5783
|
+
}
|
|
5579
5784
|
const primary = await scraperFetch(input, init);
|
|
5580
5785
|
if (!await shouldFallbackFromScraper(primary)) {
|
|
5581
5786
|
return primary;
|
|
@@ -5606,7 +5811,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
5606
5811
|
)}`,
|
|
5607
5812
|
redirect: "manual"
|
|
5608
5813
|
});
|
|
5609
|
-
const setCookies = passResp.headers.getSetCookie?.() || [];
|
|
5814
|
+
const setCookies = passResp.headers.getSetCookie?.() || (passResp.headers.get("set-cookie") ? [String(passResp.headers.get("set-cookie"))] : []);
|
|
5610
5815
|
cookieHeader = setCookies.map((c) => c.split(";")[0]).join("; ");
|
|
5611
5816
|
}
|
|
5612
5817
|
const fetchHeaders = { ...headers };
|
|
@@ -5704,7 +5909,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
5704
5909
|
html = stripTrackingScriptsFromScrapedHtml(html, trackingMarkersForRequest);
|
|
5705
5910
|
html = html.replace(/(href|src|action)="\/(?!\/)/g, `$1="${origin}/`);
|
|
5706
5911
|
const escapedOrigin = origin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5707
|
-
const proxyBase =
|
|
5912
|
+
const proxyBase = `${proxyRootForRequest}?password=${encodeURIComponent(password)}&url=`;
|
|
5708
5913
|
html = html.replace(
|
|
5709
5914
|
new RegExp(`(href|action)="${escapedOrigin}(/[^"]*)"`, "g"),
|
|
5710
5915
|
(match, attr, urlPath) => {
|
|
@@ -5717,7 +5922,7 @@ function createVisualEditorMiddleware(options) {
|
|
|
5717
5922
|
);
|
|
5718
5923
|
html = html.replace(
|
|
5719
5924
|
/data-link="\/(?!\/)([^"]*)"/g,
|
|
5720
|
-
(_match, pathPart) => `data-link="
|
|
5925
|
+
(_match, pathPart) => `data-link="${proxyRootForRequest}?password=${encodeURIComponent(password)}&url=${encodeURIComponent(
|
|
5721
5926
|
`${origin}/${pathPart}`
|
|
5722
5927
|
)}"`
|
|
5723
5928
|
);
|
|
@@ -5741,6 +5946,7 @@ ${iframeAlwaysShowCssGuardScript}
|
|
|
5741
5946
|
var TARGET_ORIGIN=${JSON.stringify(origin)};
|
|
5742
5947
|
var TARGET_PAGE_URL=${JSON.stringify(targetUrl)};
|
|
5743
5948
|
var PROXY_PASSWORD=${JSON.stringify(password)};
|
|
5949
|
+
var PROXY_BASE_URL="";
|
|
5744
5950
|
var STRICT_OBSERVER_FREEZE=${JSON.stringify(strictObserverFreezeForRequest)};
|
|
5745
5951
|
var PARENT_URL_CHANNEL="vvveb-proxy-url";
|
|
5746
5952
|
window.__CONVERSION_EDITOR_ACTIVE__=true;
|
|
@@ -5791,7 +5997,7 @@ function shouldBlockEditorTracking(rawUrl){
|
|
|
5791
5997
|
var looksLikeHeartbeatPayload=hasViewportPing&&hasSessionIds;
|
|
5792
5998
|
var isCrossOriginCollector=host!==TARGET_HOSTNAME&&hasMarker(host,TRACKING_HOST_MARKERS)&&hasMarker(path,TRACKING_PATH_MARKERS);
|
|
5793
5999
|
var isSnowplowStylePath=path==="/i"||path.indexOf("/com.snowplowanalytics.snowplow/")!==-1||path.indexOf("/tp2")!==-1;
|
|
5794
|
-
var isTaboolaUnip=host==="trc-events.taboola.com"
|
|
6000
|
+
var isTaboolaUnip=host==="trc-events.taboola.com"&&/\\/log\\/\\d+\\/unip(?:\\/|$)/.test(path);
|
|
5795
6001
|
var isCollectApiPath=path==="/api/collect"||path.indexOf("/api/collect/")===0;
|
|
5796
6002
|
var isNbCollectorPath=path==="/nb-collector"||path.indexOf("/nb-collector/")===0;
|
|
5797
6003
|
return isCrossOriginCollector||(looksLikeHeartbeatPayload&&isSnowplowStylePath)||isCollectApiPath||isTaboolaUnip||isNbCollectorPath;
|
|
@@ -5861,7 +6067,7 @@ try{window.addEventListener("popstate",notifyEditorUrlChanged,true);}catch(_){}
|
|
|
5861
6067
|
try{window.addEventListener("hashchange",notifyEditorUrlChanged,true);}catch(_){}
|
|
5862
6068
|
function isSkippable(raw){if(!raw||typeof raw!=="string")return true;return raw.startsWith("data:")||raw.startsWith("blob:")||raw.startsWith("javascript:")||raw.startsWith("#");}
|
|
5863
6069
|
function toAbsolute(raw){if(isSkippable(raw))return raw;try{var base=raw.startsWith("/")||raw.startsWith("//")?TARGET_ORIGIN:TARGET_PAGE_URL;return new URL(raw,base).toString();}catch(_){return raw;}}
|
|
5864
|
-
function toProxy(raw){if(isSkippable(raw))return null;var abs=toAbsolute(raw);if(!abs||typeof abs!=="string")return null;try{var parsed=new URL(abs);if(parsed.origin!==TARGET_ORIGIN)return null;
|
|
6070
|
+
function toProxy(raw){if(isSkippable(raw))return null;var abs=toAbsolute(raw);if(!abs||typeof abs!=="string")return null;try{var parsed=new URL(abs);if(parsed.origin!==TARGET_ORIGIN)return null;var root="/api/conversion-proxy";return root+"?password="+encodeURIComponent(PROXY_PASSWORD||"")+"&url="+encodeURIComponent(parsed.toString());}catch(_){return null;}}
|
|
5865
6071
|
var nativeAssign=window.location.assign?window.location.assign.bind(window.location):null;
|
|
5866
6072
|
var nativeReplace=window.location.replace?window.location.replace.bind(window.location):null;
|
|
5867
6073
|
function safeNavigate(raw,mode){var abs=toAbsolute(raw);var prox=toProxy(raw);if(!prox){try{console.warn("[conversion-proxy] redirect blocked",{mode:mode,requested:raw,resolved:abs,origin:TARGET_ORIGIN});}catch(_){}return false;}try{console.info("[conversion-proxy] redirect intercepted",{mode:mode,requested:raw,resolved:abs,proxied:prox});if(mode==="replace"&&nativeReplace){nativeReplace(prox);return true;}if(nativeAssign){nativeAssign(prox);return true;}window.location.href=prox;return true;}catch(err){try{console.warn("[conversion-proxy] redirect interception failed",{mode:mode,requested:raw,resolved:abs,proxied:prox,error:err&&err.message?err.message:String(err)});}catch(_){}return false;}}
|
|
@@ -5930,8 +6136,22 @@ if(window.fetch){
|
|
|
5930
6136
|
try{
|
|
5931
6137
|
var u=afterUrl?resolveUrl(afterUrl):null;
|
|
5932
6138
|
var sameOrigin=!!(u&&u.origin===TARGET_ORIGIN);
|
|
5933
|
-
var
|
|
5934
|
-
|
|
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){
|
|
5935
6155
|
console.warn("[conversion-proxy] suppressed fetch failure",{url:afterUrl,error:err&&err.message?err.message:String(err)});
|
|
5936
6156
|
return new Response("{}",{status:200,headers:{"Content-Type":"application/json; charset=utf-8"}});
|
|
5937
6157
|
}
|