@accelerated-agency/visual-editor 0.3.1 → 0.3.3
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 +15 -54
- package/dist/vite.cjs +537 -78
- package/dist/vite.js +537 -78
- package/package.json +2 -1
package/dist/vite.cjs
CHANGED
|
@@ -11,8 +11,20 @@ var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
|
11
11
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
12
|
|
|
13
13
|
// src/visualEditorProxyPlugin.ts
|
|
14
|
-
var
|
|
15
|
-
var
|
|
14
|
+
var iframeAlwaysShowCss = `<style id="__ce_force_show">#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#onetrust-consent-sdk,#onetrust-banner-sdk,.cc-window,.cc-banner,.cc-overlay,#cookie-notice,#cookie-banner,#cookie-consent,.cookie-notice,.cookie-banner,.cookie-consent,.cookie-popup,.cookie-bar,.cookie-message,.cookie-alert,.gdpr-banner,.gdpr-consent,.gdpr-popup,.gdpr-overlay,#gdpr-consent,#gdpr-banner,.consent-banner,.consent-popup,.consent-overlay,#consent-banner,#consent-popup,[class*="cookie-consent"],[class*="cookie-banner"],[class*="cookie-notice"],[class*="CookieConsent"],[class*="CookieBanner"],[id*="cookie-consent"],[id*="cookie-banner"],[id*="cookie-notice"],[aria-label*="cookie" i],[aria-label*="consent" i],.klaro,.klaro .cookie-modal,#usercentrics-root,.trustarc-banner,#truste-consent-track,#hs-eu-cookie-confirmation,.osano-cm-window,.osano-cm-dialog,.evidon-banner,#_evidon_banner,.js-cookie-consent,.cookie-disclaimer,.shopify-section-cookies,#shopify-section-cookies,#shopify-pc__banner,#shopify-pc__modal,.privacy-banner,.privacy-popup,[data-testid="cookie-banner"],[data-testid="consent-banner"],.amgdprcookie-bar-container,[data-amcookie-js="bar"],.amgdprjs-bar-template,.amgdprcookie-modal-container,.amgdprcookie-modal-overlay,#cmplz-cookiebanner-container,.cmplz-cookiebanner,#iubenda-cs-banner,.iubenda-cs-container,#qc-cmp2-container,.qc-cmp2-consent-info,#didomi-host,.didomi-popup-container,.didomi-notice,#termly-code-snippet-support,[class*="termly"],[class*="gdprcookie"],[class*="amgdpr"],[id*="gdpr-cookie"],[class*="cookie-modal"],[id*="cookie-modal"],[class*="cookieConsent"],[id*="cookieConsent"]{display:revert!important;visibility:visible!important;opacity:1!important;pointer-events:auto!important;height:auto!important;max-height:none!important;overflow:visible!important;}</style>`;
|
|
15
|
+
var iframeAlwaysShowCssGuardScript = `<script id="__ce_force_show_guard">(function(){try{
|
|
16
|
+
function ensureForceShowStyleLast(){
|
|
17
|
+
var style=document.getElementById("__ce_force_show");
|
|
18
|
+
if(!style||!document.head)return;
|
|
19
|
+
if(document.head.lastElementChild!==style){
|
|
20
|
+
document.head.appendChild(style);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
ensureForceShowStyleLast();
|
|
24
|
+
var mo=new MutationObserver(function(){ensureForceShowStyleLast();});
|
|
25
|
+
if(document.head){mo.observe(document.head,{childList:true});}
|
|
26
|
+
window.addEventListener("beforeunload",function(){try{mo.disconnect();}catch(_){}});}
|
|
27
|
+
catch(_){}})();</script>`;
|
|
16
28
|
var AI_SYSTEM_PROMPT = `You are a CRO expert. Return JSON only with: hypothesis, mutations, summary. Keep 2-6 precise mutations and only valid selectors from snapshot.`;
|
|
17
29
|
var BRIDGE_SCRIPT = `(function(){if(window.__CONVERSION_BRIDGE_LOADED__)return;window.__CONVERSION_BRIDGE_LOADED__=true;var CHANNEL='conversion-editor';var highlightEl=null;function send(payload){window.parent.postMessage({channel:CHANNEL,payload:payload},'*');}function qs(selector){try{return document.querySelector(selector);}catch(_){return null;}}function getSelector(el){if(!el||el.nodeType!==1)return'';if(el.id)return'#'+CSS.escape(el.id);var parts=[];var current=el;var depth=0;while(current&¤t.nodeType===1&&depth<5){var part=current.tagName.toLowerCase();if(current.classList&¤t.classList.length){part+='.'+Array.from(current.classList).slice(0,2).map(function(c){return CSS.escape(c);}).join('.');}var parent=current.parentElement;if(parent){var siblings=Array.from(parent.children).filter(function(s){return s.tagName===current.tagName;});if(siblings.length>1)part+=':nth-of-type('+(siblings.indexOf(current)+1)+')';}parts.unshift(part);current=parent;depth+=1;}return parts.join(' > ');}function payloadOf(el){var rect=el.getBoundingClientRect();var style=window.getComputedStyle(el);return{selector:getSelector(el),tagName:el.tagName.toLowerCase(),textContent:(el.textContent||'').trim().slice(0,500),computedStyles:{color:style.color,backgroundColor:style.backgroundColor,fontSize:style.fontSize,fontWeight:style.fontWeight,lineHeight:style.lineHeight,display:style.display},rect:{top:rect.top,left:rect.left,width:rect.width,height:rect.height}};}function applyMutation(m){var el=qs(m.selector);if(!el)return;switch(m.action){case'setStyle':if(m.property)el.style[m.property]=m.value;break;case'setText':el.textContent=m.value;break;case'setHTML':el.innerHTML=m.value;break;case'setAttribute':if(m.property)el.setAttribute(m.property,m.value);break;case'hide':el.style.display='none';break;case'show':el.style.display='';break;case'insertHTML':if(m.position)el.insertAdjacentHTML(m.position,m.value||'');break;case'insertSection':if(m.position)el.insertAdjacentHTML(m.position,m.sectionHtml||m.value||'');break;case'reorderElement':if(!m.targetSelector)break;var target=qs(m.targetSelector);if(!target||!target.parentNode||!el.parentNode)break;if(m.insertPosition==='before')target.parentNode.insertBefore(el,target);else target.parentNode.insertBefore(el,target.nextSibling);break;default:break;}}function revertMutation(m){var el=qs(m.selector);if(!el)return;switch(m.action){case'setStyle':if(m.property)el.style[m.property]=m.previous||'';break;case'setText':el.textContent=m.previous||'';break;case'setHTML':el.innerHTML=m.previous||'';break;case'setAttribute':if(m.property){if(m.previous!=null)el.setAttribute(m.property,m.previous);else el.removeAttribute(m.property);}break;case'hide':case'show':el.style.display=m.previous||'';break;default:break;}}function captureSnapshot(){var elements=Array.from(document.querySelectorAll('h1,h2,h3,p,button,a,input,select,img,section,div')).slice(0,700).map(function(el){var rect=el.getBoundingClientRect();var cs=window.getComputedStyle(el);return{selector:getSelector(el),parentSelector:el.parentElement?getSelector(el.parentElement):null,tagName:el.tagName.toLowerCase(),textContent:(el.textContent||'').trim().slice(0,300),attributes:Array.from(el.attributes||[]).reduce(function(acc,a){acc[a.name]=a.value;return acc;},{}),computedStyles:{color:cs.color,backgroundColor:cs.backgroundColor,fontSize:cs.fontSize,fontWeight:cs.fontWeight,display:cs.display},childrenCount:el.children?el.children.length:0,aboveFold:rect.top<window.innerHeight,depth:(function(){var d=0,p=el.parentElement;while(p&&d<25){d++;p=p.parentElement;}return d;})()};});send({type:'snapshotCaptured',snapshot:{url:window.location.href,title:document.title,viewport:{width:window.innerWidth,height:window.innerHeight},elements:elements}});}function captureTree(){function toNode(el,depth){var rect=el.getBoundingClientRect();var tag=el.tagName.toLowerCase();var type='generic';if(tag==='section')type='section';else if(tag==='img')type='image';else if(tag==='a')type='link';else if(tag==='button')type='button';else if(tag==='form')type='form';else if(['p','h1','h2','h3','h4','h5','h6','span'].indexOf(tag)>=0)type='text';else if(['div','main','article','aside','header','footer','nav'].indexOf(tag)>=0)type='container';var children=Array.from(el.children||[]).slice(0,50).map(function(c){return toNode(c,depth+1);});return{id:getSelector(el),selector:getSelector(el),tagName:tag,label:(el.getAttribute('aria-label')||el.getAttribute('id')||el.className||tag).toString().slice(0,80),type:type,children:children,depth:depth,isAboveFold:rect.top<window.innerHeight,rect:{top:rect.top,left:rect.left,width:rect.width,height:rect.height}};}send({type:'pageTreeCaptured',tree:[toNode(document.body,0)]});}window.addEventListener('message',function(e){var msg=e.data;if(!msg||msg.channel!==CHANNEL||!msg.payload)return;var p=msg.payload;switch(p.type){case'ping':send({type:'pong'});break;case'applyMutation':applyMutation(p.mutation);break;case'applyMutationBatch':(p.mutations||[]).forEach(applyMutation);break;case'revert':revertMutation(p.mutation);break;case'clearAllMutations':window.location.reload();break;case'captureSnapshot':captureSnapshot();break;case'validateSelectors':send({type:'selectorsValidated',results:(p.selectors||[]).map(function(s){var el=qs(s);return{selector:s,found:!!el,tagName:el?el.tagName.toLowerCase():null};})});break;case'scrollToElement':case'selectElement':var target=qs(p.selector);if(target){target.scrollIntoView({behavior:'smooth',block:'center'});send({type:'elementSelected',element:payloadOf(target)});}break;case'hoverElement':var h=qs(p.selector);if(h){if(highlightEl)highlightEl.style.outline='';h.style.outline='2px solid #3b82f6';highlightEl=h;}break;case'capturePageTree':captureTree();break;default:break;}});document.addEventListener('click',function(e){var el=e.target;if(!(el instanceof Element))return;send({type:'elementSelected',element:payloadOf(el)});},true);send({type:'bridgeReady'});})();`;
|
|
18
30
|
function buildVvvebEditorHtml() {
|
|
@@ -148,6 +160,17 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
148
160
|
.tb-save-txt{font-size:14px;color:#00C951;white-space:nowrap}
|
|
149
161
|
.tb-save-txt::before{content:'Saved'}
|
|
150
162
|
#dirty-dot.on~.tb-save-txt::before{content:'Unsaved'}
|
|
163
|
+
|
|
164
|
+
/* \u2500\u2500 In-editor toast notification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
165
|
+
#ve-notification{
|
|
166
|
+
position:fixed;right:16px;top:64px;z-index:13000;max-width:360px;
|
|
167
|
+
padding:10px 12px;border-radius:8px;font-size:12px;line-height:1.35;
|
|
168
|
+
border:1px solid var(--border);background:#fff;color:var(--text);
|
|
169
|
+
box-shadow:0 10px 24px rgba(2,6,23,.18);display:none
|
|
170
|
+
}
|
|
171
|
+
#ve-notification.show{display:block}
|
|
172
|
+
#ve-notification.error{border-color:#fecaca;background:#fef2f2;color:#991b1b}
|
|
173
|
+
#ve-notification.success{border-color:#bbf7d0;background:#f0fdf4;color:#166534}
|
|
151
174
|
.tb-save-time{font-size:12px;color:#52525b;white-space:nowrap}
|
|
152
175
|
#dirty-dot.on~.tb-save-time{display:none}
|
|
153
176
|
/* Simulate + Finalize buttons */
|
|
@@ -243,7 +266,7 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
243
266
|
/* \u2500\u2500 DOM tree (Elements tab) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
244
267
|
.dt-tree{font-size:11px;padding:0px 0 0px 20px;user-select:none}
|
|
245
268
|
.dt-row{
|
|
246
|
-
display:flex;align-items:center;gap:2px;min-height:26px;padding:2px 8px 2px 4px;
|
|
269
|
+
width:fit-content;display:flex;align-items:center;gap:2px;min-height:26px;padding:2px 8px 2px 4px;
|
|
247
270
|
cursor:pointer;color:var(--text-2);border-radius:4px;margin:0 4px
|
|
248
271
|
}
|
|
249
272
|
.dt-row:hover{background:var(--bg-hover);color:var(--text)}
|
|
@@ -455,6 +478,49 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
455
478
|
/* \u2500\u2500 Subgroup label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
456
479
|
.sub-lbl{font-size:10px;text-transform:uppercase;color:var(--text-3);font-weight:700;letter-spacing:.05em;margin:8px 0 5px}
|
|
457
480
|
.sub-lbl:first-child{margin-top:0}
|
|
481
|
+
.sub-lbl-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:8px 0 5px}
|
|
482
|
+
.sub-lbl-row .sub-lbl{margin:0}
|
|
483
|
+
.css-expand-btn{
|
|
484
|
+
width:22px;height:22px;border:1px solid var(--border);border-radius:5px;background:#fff;
|
|
485
|
+
display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-3);
|
|
486
|
+
transition:all .15s
|
|
487
|
+
}
|
|
488
|
+
.css-expand-btn:hover{border-color:var(--accent);color:var(--accent-txt);background:var(--accent-bg)}
|
|
489
|
+
|
|
490
|
+
/* \u2500\u2500 Custom CSS modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
491
|
+
#custom-css-modal{
|
|
492
|
+
position:fixed;inset:0;z-index:12000;background:rgba(15,23,42,.5);
|
|
493
|
+
display:none;align-items:center;justify-content:center;padding:20px
|
|
494
|
+
}
|
|
495
|
+
#custom-css-modal.open{display:flex}
|
|
496
|
+
.custom-css-dialog{
|
|
497
|
+
width:min(860px,96vw);max-height:86vh;background:#fff;border:1px solid var(--border);
|
|
498
|
+
border-radius:10px;box-shadow:0 20px 40px rgba(2,6,23,.35);display:flex;flex-direction:column;overflow:hidden
|
|
499
|
+
}
|
|
500
|
+
.custom-css-head{
|
|
501
|
+
display:flex;align-items:center;justify-content:space-between;gap:10px;
|
|
502
|
+
padding:10px 12px;border-bottom:1px solid var(--border);background:#fff
|
|
503
|
+
}
|
|
504
|
+
.custom-css-title{font-size:12px;font-weight:700;color:var(--text)}
|
|
505
|
+
.custom-css-close{
|
|
506
|
+
border:none;background:transparent;color:var(--text-3);cursor:pointer;width:24px;height:24px;border-radius:5px
|
|
507
|
+
}
|
|
508
|
+
.custom-css-close:hover{background:var(--bg-hover);color:var(--text)}
|
|
509
|
+
#custom-css-modal-textarea{
|
|
510
|
+
width:100%;min-height:360px;max-height:58vh;resize:vertical;border:none;outline:none;
|
|
511
|
+
font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
|
512
|
+
font-size:12px;line-height:1.5;padding:12px;background:#0b1220;color:#e2e8f0
|
|
513
|
+
}
|
|
514
|
+
.custom-css-actions{
|
|
515
|
+
display:flex;justify-content:flex-end;gap:8px;padding:10px 12px;border-top:1px solid var(--border);background:#fff
|
|
516
|
+
}
|
|
517
|
+
.custom-css-btn{
|
|
518
|
+
border:1px solid var(--border);background:#fff;color:var(--text-2);border-radius:6px;padding:6px 10px;cursor:pointer;
|
|
519
|
+
font-size:12px;font-weight:600;font-family:inherit
|
|
520
|
+
}
|
|
521
|
+
.custom-css-btn:hover{background:var(--bg-hover)}
|
|
522
|
+
.custom-css-btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}
|
|
523
|
+
.custom-css-btn.primary:hover{filter:brightness(.97)}
|
|
458
524
|
|
|
459
525
|
/* \u2500\u2500 Shadow presets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
460
526
|
.shadow-presets{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
|
|
@@ -626,10 +692,10 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
626
692
|
<button class="tb-dk-btn" id="btn-mode-nav" onclick="setMode('navigate')" title="Navigate mode \u2014 interact with page normally"><i class="bi bi-magic"></i></button>
|
|
627
693
|
<button class="tb-dk-btn" title="Comments"><i class="bi bi-chat-dots"></i></button>
|
|
628
694
|
</div>
|
|
629
|
-
<!-- btn-close: hidden visually, kept for JS event listener -->
|
|
630
|
-
<button id="btn-close" style="display:none" title="Close editor"></button>
|
|
631
695
|
<button class="tb-sim-btn" id="btn-simulate" onclick="simulateExperiment()"><i class="bi bi-lightning-charge-fill"></i> Simulate</button>
|
|
632
|
-
<button class="tb-fin-btn" id="btn-save">
|
|
696
|
+
<button class="tb-fin-btn" id="btn-save">Save Changes</button>
|
|
697
|
+
<!-- btn-close: kept for JS event listener -->
|
|
698
|
+
<button class="tb-fin-btn" id="btn-close">Close</button>
|
|
633
699
|
</div>
|
|
634
700
|
|
|
635
701
|
<!-- url-bar: hidden, kept for JS compatibility -->
|
|
@@ -844,6 +910,23 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
844
910
|
|
|
845
911
|
</div><!-- #app -->
|
|
846
912
|
|
|
913
|
+
<!-- Custom CSS full-screen modal -->
|
|
914
|
+
<div id="custom-css-modal" aria-hidden="true">
|
|
915
|
+
<div class="custom-css-dialog" role="dialog" aria-modal="true" aria-labelledby="custom-css-modal-title">
|
|
916
|
+
<div class="custom-css-head">
|
|
917
|
+
<div class="custom-css-title" id="custom-css-modal-title"><i class="bi bi-code-slash"></i> Custom CSS</div>
|
|
918
|
+
<button type="button" class="custom-css-close" onclick="closeCustomCssModal()" title="Close">
|
|
919
|
+
<i class="bi bi-x-lg"></i>
|
|
920
|
+
</button>
|
|
921
|
+
</div>
|
|
922
|
+
<textarea id="custom-css-modal-textarea" spellcheck="false" placeholder="Type your css here"></textarea>
|
|
923
|
+
<div class="custom-css-actions">
|
|
924
|
+
<button type="button" class="custom-css-btn" onclick="closeCustomCssModal()">Cancel</button>
|
|
925
|
+
<button type="button" class="custom-css-btn primary" onclick="applyCustomCssModal()">Add</button>
|
|
926
|
+
</div>
|
|
927
|
+
</div>
|
|
928
|
+
</div>
|
|
929
|
+
|
|
847
930
|
<!-- CDN scripts -->
|
|
848
931
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
849
932
|
<script src="https://cdn.jsdelivr.net/gh/givanz/VvvebJs@master/libs/builder/builder.js"></script>
|
|
@@ -915,13 +998,37 @@ function send(type, payload) {
|
|
|
915
998
|
window.parent.postMessage({ channel: CHANNEL, type: type, payload: payload || {} }, '*');
|
|
916
999
|
}
|
|
917
1000
|
|
|
1001
|
+
var notificationTimer = null;
|
|
1002
|
+
function showEditorNotification(message, kind, durationMs) {
|
|
1003
|
+
var id = 've-notification';
|
|
1004
|
+
var el = document.getElementById(id);
|
|
1005
|
+
if (!el) {
|
|
1006
|
+
el = document.createElement('div');
|
|
1007
|
+
el.id = id;
|
|
1008
|
+
el.setAttribute('role', 'status');
|
|
1009
|
+
el.setAttribute('aria-live', 'polite');
|
|
1010
|
+
document.body.appendChild(el);
|
|
1011
|
+
}
|
|
1012
|
+
el.className = '';
|
|
1013
|
+
el.classList.add(kind === 'success' ? 'success' : 'error');
|
|
1014
|
+
el.classList.add('show');
|
|
1015
|
+
el.textContent = String(message || 'Something went wrong');
|
|
1016
|
+
if (notificationTimer) clearTimeout(notificationTimer);
|
|
1017
|
+
notificationTimer = setTimeout(function() {
|
|
1018
|
+
var cur = document.getElementById(id);
|
|
1019
|
+
if (!cur) return;
|
|
1020
|
+
cur.classList.remove('show');
|
|
1021
|
+
}, Math.max(1200, durationMs || 2600));
|
|
1022
|
+
}
|
|
1023
|
+
|
|
918
1024
|
function generatePreviewUrlString(args) {
|
|
919
1025
|
var baseUrl = (args && args.url) || '';
|
|
920
1026
|
var test = (args && args.test) || {};
|
|
921
1027
|
var variation = (args && args.variation) || {};
|
|
922
1028
|
if (!baseUrl) return '';
|
|
923
|
-
var testId = test.iid ||
|
|
924
|
-
var variationId = variation.iid ||
|
|
1029
|
+
var testId = test.iid || '';
|
|
1030
|
+
var variationId = variation.iid || '';
|
|
1031
|
+
if (!testId || !variationId) return '';
|
|
925
1032
|
var cId = String(testId || '') + '_' + String(variationId || '');
|
|
926
1033
|
var hasQueryParams = String(baseUrl).indexOf('?') >= 0;
|
|
927
1034
|
return (
|
|
@@ -945,6 +1052,10 @@ function simulateExperiment() {
|
|
|
945
1052
|
test.pageUrl ||
|
|
946
1053
|
(test.metadata_1 && test.metadata_1.editor_url) ||
|
|
947
1054
|
'';
|
|
1055
|
+
if (!test.iid || !activeVariation || !activeVariation.iid) {
|
|
1056
|
+
showEditorNotification('Cannot simulate: missing test.iid or variation.iid.', 'error', 3200);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
948
1059
|
var url = generatePreviewUrlString({
|
|
949
1060
|
url: targetUrl,
|
|
950
1061
|
test: test,
|
|
@@ -952,6 +1063,7 @@ function simulateExperiment() {
|
|
|
952
1063
|
});
|
|
953
1064
|
if (!url) {
|
|
954
1065
|
console.warn('[V2] simulateExperiment: missing target URL');
|
|
1066
|
+
showEditorNotification('Cannot simulate: missing target URL.', 'error', 3200);
|
|
955
1067
|
return;
|
|
956
1068
|
}
|
|
957
1069
|
try {
|
|
@@ -969,6 +1081,19 @@ window.addEventListener('message', function(e) {
|
|
|
969
1081
|
}
|
|
970
1082
|
});
|
|
971
1083
|
|
|
1084
|
+
document.addEventListener('keydown', function(e) {
|
|
1085
|
+
if (e.key !== 'Escape') return;
|
|
1086
|
+
var modal = document.getElementById('custom-css-modal');
|
|
1087
|
+
if (!modal || !modal.classList.contains('open')) return;
|
|
1088
|
+
closeCustomCssModal();
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
document.addEventListener('click', function(e) {
|
|
1092
|
+
var modal = document.getElementById('custom-css-modal');
|
|
1093
|
+
if (!modal || !modal.classList.contains('open')) return;
|
|
1094
|
+
if (e.target === modal) closeCustomCssModal();
|
|
1095
|
+
});
|
|
1096
|
+
|
|
972
1097
|
// \u2500\u2500 State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
973
1098
|
var experimentData = null;
|
|
974
1099
|
var variations = [];
|
|
@@ -989,6 +1114,8 @@ var iframeContentNavGen = 0;
|
|
|
989
1114
|
var iframeContentApplyTimer = null;
|
|
990
1115
|
var iframeEarlyGranularPrimedForGen = null;
|
|
991
1116
|
var iframeEarlySyncPrimedForGen = null;
|
|
1117
|
+
var iframeEarlyDomSignature = '';
|
|
1118
|
+
var iframeEarlyDomSignatureNavGen = 0;
|
|
992
1119
|
/** insert/reorder entries are applied from early granular + full apply \u2014 skip exact duplicates per iframe nav */
|
|
993
1120
|
var appliedStructuralChangesetKeys = {};
|
|
994
1121
|
var isDirty = false;
|
|
@@ -1000,6 +1127,7 @@ var selectedEl = null;
|
|
|
1000
1127
|
var selectedElFingerprint = '';
|
|
1001
1128
|
var selectedElRecoverMisses = 0;
|
|
1002
1129
|
var MAX_SELECTED_RECOVER_MISSES = 12;
|
|
1130
|
+
var hoveredTreeEl = null;
|
|
1003
1131
|
var isDeselectingSelection = false;
|
|
1004
1132
|
var suppressClickUntil = 0;
|
|
1005
1133
|
var dragAttachDoc = null;
|
|
@@ -1013,6 +1141,7 @@ var iframeSyncAttempts = 0;
|
|
|
1013
1141
|
var selectionScrollWin = null;
|
|
1014
1142
|
var selectionResizeBound = false;
|
|
1015
1143
|
var clickAttachDoc = null;
|
|
1144
|
+
var hoverAttachDoc = null;
|
|
1016
1145
|
var changeObserver = null;
|
|
1017
1146
|
var changeObserverDoc = null;
|
|
1018
1147
|
/** Incremented while applying changesets / selection chrome so MutationObserver does not mark dirty */
|
|
@@ -1048,14 +1177,17 @@ function endSuppressIframeMutationDirty() {
|
|
|
1048
1177
|
function commitStateChangesForActiveVariation() {
|
|
1049
1178
|
if (!activeVarId) return;
|
|
1050
1179
|
stateChangesByVarId[activeVarId] = (stateChanges || []).slice();
|
|
1180
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1051
1181
|
}
|
|
1052
1182
|
|
|
1053
1183
|
function loadStateChangesForActiveVariation() {
|
|
1054
1184
|
if (!activeVarId) {
|
|
1055
1185
|
stateChanges = [];
|
|
1186
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1056
1187
|
return;
|
|
1057
1188
|
}
|
|
1058
1189
|
stateChanges = (stateChangesByVarId[activeVarId] || []).slice();
|
|
1190
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1059
1191
|
}
|
|
1060
1192
|
|
|
1061
1193
|
function recoverSelectedElement(forceDeselectOnMiss) {
|
|
@@ -1222,6 +1354,7 @@ function setMode(mode) {
|
|
|
1222
1354
|
document.getElementById('btn-mode-editor').classList.toggle('active', mode === 'editor');
|
|
1223
1355
|
document.getElementById('btn-mode-nav').classList.toggle('active', mode === 'navigate');
|
|
1224
1356
|
if (mode === 'navigate') {
|
|
1357
|
+
clearTreeHoverHighlight();
|
|
1225
1358
|
setDragHandleActive(false);
|
|
1226
1359
|
deselectElement();
|
|
1227
1360
|
} else if (mode === 'editor') {
|
|
@@ -1535,24 +1668,26 @@ function persistActiveVariationChangesets(arr) {
|
|
|
1535
1668
|
}
|
|
1536
1669
|
}
|
|
1537
1670
|
}
|
|
1671
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1538
1672
|
}
|
|
1539
1673
|
|
|
1540
1674
|
function entrySnapshotKey(entry) {
|
|
1541
1675
|
if (!entry || !entry.selector) return '';
|
|
1676
|
+
var SEP = '__vve_sep__';
|
|
1542
1677
|
var selKey = sanitizeSelectorForMatch(entry.selector) || entry.selector;
|
|
1543
1678
|
return (
|
|
1544
1679
|
selKey +
|
|
1545
|
-
|
|
1680
|
+
SEP +
|
|
1546
1681
|
normalizeChangesetType(entry) +
|
|
1547
|
-
|
|
1682
|
+
SEP +
|
|
1548
1683
|
String(entry.property || '') +
|
|
1549
|
-
|
|
1684
|
+
SEP +
|
|
1550
1685
|
String(entry.attribute || '') +
|
|
1551
|
-
|
|
1686
|
+
SEP +
|
|
1552
1687
|
String(entry.action || '') +
|
|
1553
|
-
|
|
1688
|
+
SEP +
|
|
1554
1689
|
String(entry.html != null ? 'h' + String(entry.html).length : '') +
|
|
1555
|
-
|
|
1690
|
+
SEP +
|
|
1556
1691
|
String(entry.value != null ? 'v:' + entry.value : '')
|
|
1557
1692
|
);
|
|
1558
1693
|
}
|
|
@@ -1602,15 +1737,15 @@ function softReloadEditorIframe() {
|
|
|
1602
1737
|
var navGen = nextIframeContentNavGen();
|
|
1603
1738
|
resetIframeBindings();
|
|
1604
1739
|
setIframePageLoadingUi(true);
|
|
1605
|
-
iframe.src = '';
|
|
1606
1740
|
iframe.src = appendIframeReloadBust(src);
|
|
1607
|
-
startIframeContentApplyWatcher(navGen);
|
|
1741
|
+
startIframeContentApplyWatcher(navGen, iframe.contentDocument || null);
|
|
1608
1742
|
scheduleDomTreeRefresh();
|
|
1609
1743
|
}
|
|
1610
1744
|
|
|
1611
1745
|
/** @returns {boolean} true if a full iframe reload was started */
|
|
1612
1746
|
function revertChangesetEntryOnDom(entry) {
|
|
1613
1747
|
if (!entry) return false;
|
|
1748
|
+
var styleOnly = isStyleOnlyChangesetEntry(entry);
|
|
1614
1749
|
if (entry.selector === '__vvveb_body__') {
|
|
1615
1750
|
var iframeDoc0 = document.getElementById('iframeId').contentDocument;
|
|
1616
1751
|
if (!iframeDoc0 || !iframeDoc0.body) return false;
|
|
@@ -1631,6 +1766,11 @@ function revertChangesetEntryOnDom(entry) {
|
|
|
1631
1766
|
var snap = appliedChangesetSnapshots[k];
|
|
1632
1767
|
var el = querySelectorResolved(iframeDoc, entry.selector);
|
|
1633
1768
|
if (!snap || !el) {
|
|
1769
|
+
if (styleOnly) {
|
|
1770
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1771
|
+
delete appliedChangesetSnapshots[k];
|
|
1772
|
+
return false;
|
|
1773
|
+
}
|
|
1634
1774
|
softReloadEditorIframe();
|
|
1635
1775
|
delete appliedChangesetSnapshots[k];
|
|
1636
1776
|
return true;
|
|
@@ -1645,6 +1785,11 @@ function revertChangesetEntryOnDom(entry) {
|
|
|
1645
1785
|
else el.setAttribute(snap.name, snap.v);
|
|
1646
1786
|
} else if (snap.kind === 'display') el.style.display = snap.v;
|
|
1647
1787
|
else {
|
|
1788
|
+
if (styleOnly) {
|
|
1789
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1790
|
+
delete appliedChangesetSnapshots[k];
|
|
1791
|
+
return false;
|
|
1792
|
+
}
|
|
1648
1793
|
softReloadEditorIframe();
|
|
1649
1794
|
delete appliedChangesetSnapshots[k];
|
|
1650
1795
|
return true;
|
|
@@ -1704,7 +1849,9 @@ function renderHistoryTab() {
|
|
|
1704
1849
|
var lab = historyEntryTypeLabel(item.entry);
|
|
1705
1850
|
var val = historyEntryValuePreview(item.entry);
|
|
1706
1851
|
html +=
|
|
1707
|
-
'<div class="state-item"
|
|
1852
|
+
'<div class="state-item" role="button" tabindex="0" title="Jump to element in iframe" onclick="focusHistoryChangeset(' +
|
|
1853
|
+
item.idx +
|
|
1854
|
+
')">' +
|
|
1708
1855
|
'<span class="state-item-idx" style="opacity:0.55;font-size:10px;min-width:2.2em;display:inline-block" title="Order in saved changesets">#' +
|
|
1709
1856
|
item.idx +
|
|
1710
1857
|
'</span>' +
|
|
@@ -1720,7 +1867,7 @@ function renderHistoryTab() {
|
|
|
1720
1867
|
item.idx +
|
|
1721
1868
|
')" onclick="removeHistoryChangeset(' +
|
|
1722
1869
|
item.idx +
|
|
1723
|
-
')">✕</button>' +
|
|
1870
|
+
', event)">✕</button>' +
|
|
1724
1871
|
'</div>';
|
|
1725
1872
|
});
|
|
1726
1873
|
html += '</div>';
|
|
@@ -1738,7 +1885,38 @@ function changesetListHasStructural(arr) {
|
|
|
1738
1885
|
return false;
|
|
1739
1886
|
}
|
|
1740
1887
|
|
|
1741
|
-
function
|
|
1888
|
+
function isStyleOnlyChangesetEntry(entry) {
|
|
1889
|
+
if (!entry) return false;
|
|
1890
|
+
var t = normalizeChangesetType(entry);
|
|
1891
|
+
if (t === 'style') return true;
|
|
1892
|
+
if (t === 'attribute' && String(entry.attribute || '').toLowerCase() === 'style') return true;
|
|
1893
|
+
return false;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
function focusHistoryChangeset(idx) {
|
|
1897
|
+
var v = getActiveVariationForHistory();
|
|
1898
|
+
if (!v) return;
|
|
1899
|
+
var arr = parseVariationChangesets(v);
|
|
1900
|
+
if (idx < 0 || idx >= arr.length) return;
|
|
1901
|
+
var entry = arr[idx];
|
|
1902
|
+
if (!entry || !entry.selector || entry.selector === '__vvveb_body__') return;
|
|
1903
|
+
try {
|
|
1904
|
+
var iframe = document.getElementById('iframeId');
|
|
1905
|
+
var iframeDoc = iframe && iframe.contentDocument;
|
|
1906
|
+
if (!iframeDoc) return;
|
|
1907
|
+
var el = querySelectorResolved(iframeDoc, entry.selector);
|
|
1908
|
+
if (!el) return;
|
|
1909
|
+
selectElement(el);
|
|
1910
|
+
try {
|
|
1911
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
1912
|
+
} catch(_) {
|
|
1913
|
+
el.scrollIntoView();
|
|
1914
|
+
}
|
|
1915
|
+
} catch(_) {}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
function removeHistoryChangeset(idx, evt) {
|
|
1919
|
+
if (evt && evt.stopPropagation) evt.stopPropagation();
|
|
1742
1920
|
var v = getActiveVariationForHistory();
|
|
1743
1921
|
if (!v) return;
|
|
1744
1922
|
var arr = parseVariationChangesets(v);
|
|
@@ -1746,6 +1924,16 @@ function removeHistoryChangeset(idx) {
|
|
|
1746
1924
|
var removed = arr[idx];
|
|
1747
1925
|
arr.splice(idx, 1);
|
|
1748
1926
|
persistActiveVariationChangesets(arr);
|
|
1927
|
+
if (isStyleOnlyChangesetEntry(removed)) {
|
|
1928
|
+
try {
|
|
1929
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1930
|
+
saveCurrentVariationHtml();
|
|
1931
|
+
} catch(_) {}
|
|
1932
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1933
|
+
recomputeEditorDirty();
|
|
1934
|
+
scheduleDomTreeRefresh();
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1749
1937
|
var didReload = revertChangesetEntryOnDom(removed);
|
|
1750
1938
|
try {
|
|
1751
1939
|
delete varHtmlCache[activeVarId];
|
|
@@ -1753,15 +1941,19 @@ function removeHistoryChangeset(idx) {
|
|
|
1753
1941
|
// Re-applying remaining rows on top of current DOM duplicates insert/reorder nodes; reload when any
|
|
1754
1942
|
// structural row remains or was removed (revert may already have started a reload for insert/body).
|
|
1755
1943
|
var removedType = normalizeChangesetType(removed);
|
|
1756
|
-
var
|
|
1757
|
-
|
|
1758
|
-
(removedType === 'insert' ||
|
|
1759
|
-
removedType === 'reorder' ||
|
|
1760
|
-
changesetListHasStructural(arr));
|
|
1944
|
+
var hasStructuralRemaining = changesetListHasStructural(arr);
|
|
1945
|
+
var removedIsStructural = removedType === 'insert' || removedType === 'reorder';
|
|
1761
1946
|
if (didReload) {
|
|
1762
1947
|
/* revertChangesetEntryOnDom already kicked off iframe reload */
|
|
1763
|
-
} else if (
|
|
1948
|
+
} else if (removedIsStructural) {
|
|
1764
1949
|
softReloadEditorIframe();
|
|
1950
|
+
} else if (hasStructuralRemaining) {
|
|
1951
|
+
// Keep current DOM state (already reverted for removed row) and only refresh style layer.
|
|
1952
|
+
// Avoid full reload and avoid re-applying all rows, which can duplicate structural insert/reorder entries.
|
|
1953
|
+
try {
|
|
1954
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1955
|
+
saveCurrentVariationHtml();
|
|
1956
|
+
} catch(_) {}
|
|
1765
1957
|
} else {
|
|
1766
1958
|
try {
|
|
1767
1959
|
appliedStructuralChangesetKeys = {};
|
|
@@ -2025,6 +2217,27 @@ function bodyHasFirstPaintChild(body) {
|
|
|
2025
2217
|
return false;
|
|
2026
2218
|
}
|
|
2027
2219
|
|
|
2220
|
+
function computeIframeDomSignature(doc) {
|
|
2221
|
+
if (!doc || !doc.body) return '';
|
|
2222
|
+
var body = doc.body;
|
|
2223
|
+
var tags = [];
|
|
2224
|
+
var walker = null;
|
|
2225
|
+
try {
|
|
2226
|
+
walker = doc.createTreeWalker(body, NodeFilter.SHOW_ELEMENT, null);
|
|
2227
|
+
} catch(_) {
|
|
2228
|
+
walker = null;
|
|
2229
|
+
}
|
|
2230
|
+
var count = 0;
|
|
2231
|
+
if (walker) {
|
|
2232
|
+
while (walker.nextNode() && count < 400) {
|
|
2233
|
+
var node = walker.currentNode;
|
|
2234
|
+
tags.push((node && node.tagName) ? String(node.tagName).toLowerCase() : '');
|
|
2235
|
+
count += 1;
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
return String(body.children ? body.children.length : 0) + '|' + String(count) + '|' + tags.join(',');
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2028
2241
|
/** True when at least one granular changeset selector already matches (nested content painted). */
|
|
2029
2242
|
function granularAnySelectorMatches(doc, cs) {
|
|
2030
2243
|
if (!doc || !cs || !cs.length) return false;
|
|
@@ -2129,7 +2342,7 @@ function runConsistencyReconcile() {
|
|
|
2129
2342
|
if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
|
|
2130
2343
|
var granular = filterGranularChangesetEntries(cs);
|
|
2131
2344
|
var unresolved = countUnresolvedGranularSelectors(doc, granular);
|
|
2132
|
-
if (unresolved > 0 ||
|
|
2345
|
+
if (unresolved > 0 || hasUnappliedStructuralChangesets(cs)) {
|
|
2133
2346
|
reapplyActiveVariationGranular(doc);
|
|
2134
2347
|
registerPendingGranularChangesets(cs, doc);
|
|
2135
2348
|
}
|
|
@@ -2179,6 +2392,7 @@ function resetIframeBindings() {
|
|
|
2179
2392
|
appliedStructuralChangesetKeys = {};
|
|
2180
2393
|
clickAttachDoc = null;
|
|
2181
2394
|
dragAttachDoc = null;
|
|
2395
|
+
hoverAttachDoc = null;
|
|
2182
2396
|
changeObserverDoc = null;
|
|
2183
2397
|
clearPendingGranularChangesets();
|
|
2184
2398
|
if (changeObserver) {
|
|
@@ -2207,7 +2421,7 @@ function loadPage(proxyUrl) {
|
|
|
2207
2421
|
iframe.style.display = 'block';
|
|
2208
2422
|
setIframePageLoadingUi(true);
|
|
2209
2423
|
iframe.src = appendIframeReloadBust(proxyUrl);
|
|
2210
|
-
startIframeContentApplyWatcher(navGen);
|
|
2424
|
+
startIframeContentApplyWatcher(navGen, iframe.contentDocument || null);
|
|
2211
2425
|
scheduleDomTreeRefresh();
|
|
2212
2426
|
}
|
|
2213
2427
|
|
|
@@ -2279,7 +2493,7 @@ function switchVariation(varId) {
|
|
|
2279
2493
|
iframe.src = appendIframeReloadBust(src);
|
|
2280
2494
|
// Do not sync here: the document is still the previous navigation until the
|
|
2281
2495
|
// iframe load event; an eager sync attached observers / DOM tree to the wrong document.
|
|
2282
|
-
startIframeContentApplyWatcher(navGen);
|
|
2496
|
+
startIframeContentApplyWatcher(navGen, iframe.contentDocument || null);
|
|
2283
2497
|
scheduleDomTreeRefresh();
|
|
2284
2498
|
}
|
|
2285
2499
|
} catch(_) {}
|
|
@@ -2499,24 +2713,136 @@ function flushPendingGranularChangesets() {
|
|
|
2499
2713
|
function structuralChangesetDedupKey(entry) {
|
|
2500
2714
|
var nt = normalizeChangesetType(entry);
|
|
2501
2715
|
if (!entry || (nt !== 'insert' && nt !== 'reorder')) return '';
|
|
2716
|
+
var SEP = '__vve_sep__';
|
|
2502
2717
|
var vid = activeVarId || '';
|
|
2503
2718
|
try {
|
|
2504
2719
|
return (
|
|
2505
2720
|
vid +
|
|
2506
|
-
|
|
2721
|
+
SEP +
|
|
2507
2722
|
nt +
|
|
2508
|
-
|
|
2723
|
+
SEP +
|
|
2509
2724
|
entry.selector +
|
|
2510
|
-
|
|
2725
|
+
SEP +
|
|
2511
2726
|
String(entry.action || '') +
|
|
2512
|
-
|
|
2727
|
+
SEP +
|
|
2513
2728
|
String(entry.html != null ? entry.html : '').slice(0, 240) +
|
|
2514
|
-
|
|
2729
|
+
SEP +
|
|
2515
2730
|
String(entry.targetSelector || '')
|
|
2516
2731
|
);
|
|
2517
2732
|
} catch(_) {
|
|
2518
|
-
return vid +
|
|
2733
|
+
return vid + SEP + nt + SEP + entry.selector;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
function hasUnappliedStructuralChangesets(cs) {
|
|
2738
|
+
if (!cs || !cs.length) return false;
|
|
2739
|
+
for (var i = 0; i < cs.length; i++) {
|
|
2740
|
+
var e = cs[i];
|
|
2741
|
+
var t = normalizeChangesetType(e);
|
|
2742
|
+
if (t !== 'insert' && t !== 'reorder') continue;
|
|
2743
|
+
var k = structuralChangesetDedupKey(e);
|
|
2744
|
+
if (!k) return true;
|
|
2745
|
+
if (!appliedStructuralChangesetKeys[k]) return true;
|
|
2746
|
+
}
|
|
2747
|
+
return false;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
function parseInlineStyleDeclarations(styleText) {
|
|
2751
|
+
var out = [];
|
|
2752
|
+
if (styleText == null) return out;
|
|
2753
|
+
var s = String(styleText);
|
|
2754
|
+
if (!s.trim()) return out;
|
|
2755
|
+
var parts = s.split(';');
|
|
2756
|
+
for (var i = 0; i < parts.length; i++) {
|
|
2757
|
+
var seg = parts[i];
|
|
2758
|
+
if (!seg) continue;
|
|
2759
|
+
var idx = seg.indexOf(':');
|
|
2760
|
+
if (idx <= 0) continue;
|
|
2761
|
+
var prop = seg.slice(0, idx).trim();
|
|
2762
|
+
var value = seg.slice(idx + 1).trim();
|
|
2763
|
+
if (!prop || !value) continue;
|
|
2764
|
+
out.push({ prop: prop, value: value });
|
|
2765
|
+
}
|
|
2766
|
+
return out;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
function buildPersistentStyleRulesForActiveVariation() {
|
|
2770
|
+
if (!activeVarId) return '';
|
|
2771
|
+
var v = variations.find(function(x) { return x && x._id === activeVarId; });
|
|
2772
|
+
var parsed = parseVariationChangesets(v);
|
|
2773
|
+
var map = {};
|
|
2774
|
+
var order = [];
|
|
2775
|
+
function put(selector, prop, value) {
|
|
2776
|
+
if (!selector || !prop) return;
|
|
2777
|
+
if (value == null || value === '') return;
|
|
2778
|
+
var sel = sanitizeSelectorForMatch(String(selector)) || String(selector);
|
|
2779
|
+
var pr = String(prop).trim();
|
|
2780
|
+
var val = String(value).trim();
|
|
2781
|
+
if (!sel || !pr || !val) return;
|
|
2782
|
+
var k = sel + '__vve_sep__' + pr;
|
|
2783
|
+
if (!map[k]) order.push(k);
|
|
2784
|
+
map[k] = { selector: sel, property: pr, value: val };
|
|
2785
|
+
}
|
|
2786
|
+
for (var i = 0; i < parsed.length; i++) {
|
|
2787
|
+
var e = parsed[i];
|
|
2788
|
+
if (!e) continue;
|
|
2789
|
+
var t = normalizeChangesetType(e);
|
|
2790
|
+
if (t === 'style') {
|
|
2791
|
+
put(e.selector, e.property || e.cssProp, e.value);
|
|
2792
|
+
continue;
|
|
2793
|
+
}
|
|
2794
|
+
if (t === 'attribute' && String(e.attribute || '').toLowerCase() === 'style') {
|
|
2795
|
+
var decls = parseInlineStyleDeclarations(e.value);
|
|
2796
|
+
for (var di = 0; di < decls.length; di++) {
|
|
2797
|
+
put(e.selector, decls[di].prop, decls[di].value);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2519
2800
|
}
|
|
2801
|
+
for (var j = 0; j < stateChanges.length; j++) {
|
|
2802
|
+
var c = stateChanges[j];
|
|
2803
|
+
if (!c) continue;
|
|
2804
|
+
if (c.cssProp) {
|
|
2805
|
+
put(c.selector, c.cssProp, c.value);
|
|
2806
|
+
continue;
|
|
2807
|
+
}
|
|
2808
|
+
if (c.inputId === 'pp-css') {
|
|
2809
|
+
var liveDecls = parseInlineStyleDeclarations(c.value);
|
|
2810
|
+
for (var ldi = 0; ldi < liveDecls.length; ldi++) {
|
|
2811
|
+
put(c.selector, liveDecls[ldi].prop, liveDecls[ldi].value);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
var lines = [];
|
|
2816
|
+
for (var oi = 0; oi < order.length; oi++) {
|
|
2817
|
+
var row = map[order[oi]];
|
|
2818
|
+
lines.push(row.selector + ' { ' + row.property + ': ' + row.value + ' !important; }');
|
|
2819
|
+
}
|
|
2820
|
+
return lines.join('\\n');
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
function upsertPersistentChangesetStyleTag(iframeDoc, rulesText) {
|
|
2824
|
+
if (!iframeDoc) return;
|
|
2825
|
+
var STYLE_ID = '__vve_persist_changesets_style__';
|
|
2826
|
+
var prev = iframeDoc.getElementById(STYLE_ID);
|
|
2827
|
+
if (!rulesText) {
|
|
2828
|
+
if (prev && prev.parentNode) prev.parentNode.removeChild(prev);
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
var head = iframeDoc.head || iframeDoc.getElementsByTagName('head')[0];
|
|
2832
|
+
if (!head) return;
|
|
2833
|
+
var styleEl = prev || iframeDoc.createElement('style');
|
|
2834
|
+
styleEl.id = STYLE_ID;
|
|
2835
|
+
if (styleEl.textContent !== rulesText) styleEl.textContent = rulesText;
|
|
2836
|
+
if (!styleEl.parentNode) head.appendChild(styleEl);
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
function refreshPersistentChangesetStyleTagForActiveVariation() {
|
|
2840
|
+
try {
|
|
2841
|
+
var iframe = document.getElementById('iframeId');
|
|
2842
|
+
var iframeDoc = iframe && iframe.contentDocument;
|
|
2843
|
+
if (!iframeDoc) return;
|
|
2844
|
+
upsertPersistentChangesetStyleTag(iframeDoc, buildPersistentStyleRulesForActiveVariation());
|
|
2845
|
+
} catch(_) {}
|
|
2520
2846
|
}
|
|
2521
2847
|
|
|
2522
2848
|
/**
|
|
@@ -2553,23 +2879,11 @@ function applyChangesetEntry(entry, iframeDoc) {
|
|
|
2553
2879
|
else if (entry.value != null) el.textContent = entry.value;
|
|
2554
2880
|
break;
|
|
2555
2881
|
case 'style':
|
|
2556
|
-
|
|
2557
|
-
var propKebab = entry.property;
|
|
2558
|
-
var cam = camelize(propKebab);
|
|
2559
|
-
if (entry.value == null || entry.value === '') {
|
|
2560
|
-
try { el.style.removeProperty(propKebab); } catch(_) {}
|
|
2561
|
-
try { if (cam in el.style) el.style[cam] = ''; } catch(__) {}
|
|
2562
|
-
} else {
|
|
2563
|
-
try {
|
|
2564
|
-
el.style.setProperty(propKebab, entry.value, 'important');
|
|
2565
|
-
} catch(_) {
|
|
2566
|
-
el.style[cam] = entry.value;
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2882
|
+
// Style changes are applied via persistent stylesheet injection.
|
|
2570
2883
|
break;
|
|
2571
2884
|
case 'attribute':
|
|
2572
2885
|
if (entry.attribute && entry.value != null) {
|
|
2886
|
+
if (String(entry.attribute).toLowerCase() === 'style') break;
|
|
2573
2887
|
el.setAttribute(entry.attribute, entry.value);
|
|
2574
2888
|
}
|
|
2575
2889
|
break;
|
|
@@ -2606,6 +2920,7 @@ function applyActiveVariationHtml() {
|
|
|
2606
2920
|
|
|
2607
2921
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
2608
2922
|
var cs = parseVariationChangesets(variation);
|
|
2923
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2609
2924
|
|
|
2610
2925
|
beginSuppressIframeMutationDirty();
|
|
2611
2926
|
try {
|
|
@@ -2620,6 +2935,7 @@ function applyActiveVariationHtml() {
|
|
|
2620
2935
|
for (var i = 0; i < cs.length; i++) {
|
|
2621
2936
|
applyChangesetEntry(cs[i], iframeDoc);
|
|
2622
2937
|
}
|
|
2938
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2623
2939
|
// Selectors often miss on first paint (client-rendered sections). Retry via timer + mutations.
|
|
2624
2940
|
registerPendingGranularChangesets(cs, iframeDoc);
|
|
2625
2941
|
} finally {
|
|
@@ -2668,6 +2984,7 @@ function applyVariationGranularOnly(iframeDoc) {
|
|
|
2668
2984
|
for (var i = 0; i < cs.length; i++) {
|
|
2669
2985
|
applyChangesetEntry(cs[i], iframeDoc);
|
|
2670
2986
|
}
|
|
2987
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2671
2988
|
registerPendingGranularChangesets(cs, iframeDoc);
|
|
2672
2989
|
} finally {
|
|
2673
2990
|
endSuppressIframeMutationDirty();
|
|
@@ -2686,16 +3003,19 @@ function reapplyActiveVariationGranular(iframeDoc) {
|
|
|
2686
3003
|
for (var i = 0; i < cs.length; i++) {
|
|
2687
3004
|
applyChangesetEntry(cs[i], iframeDoc);
|
|
2688
3005
|
}
|
|
3006
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2689
3007
|
} finally {
|
|
2690
3008
|
endSuppressIframeMutationDirty();
|
|
2691
3009
|
}
|
|
2692
3010
|
}
|
|
2693
3011
|
|
|
2694
3012
|
/** Poll iframe document during navigation; apply granular edits as soon as DOM can match selectors. */
|
|
2695
|
-
function startIframeContentApplyWatcher(navGen) {
|
|
3013
|
+
function startIframeContentApplyWatcher(navGen, prevDocRef) {
|
|
2696
3014
|
stopIframeContentApplyWatcher();
|
|
2697
3015
|
iframeEarlyGranularPrimedForGen = null;
|
|
2698
3016
|
iframeEarlySyncPrimedForGen = null;
|
|
3017
|
+
iframeEarlyDomSignature = '';
|
|
3018
|
+
iframeEarlyDomSignatureNavGen = navGen;
|
|
2699
3019
|
var iframe = document.getElementById('iframeId');
|
|
2700
3020
|
iframeContentApplyTimer = setInterval(function() {
|
|
2701
3021
|
if (navGen !== iframeContentNavGen) {
|
|
@@ -2705,6 +3025,7 @@ function startIframeContentApplyWatcher(navGen) {
|
|
|
2705
3025
|
try {
|
|
2706
3026
|
var doc = iframe.contentDocument;
|
|
2707
3027
|
if (!doc || !doc.body) return;
|
|
3028
|
+
if (prevDocRef && doc === prevDocRef) return;
|
|
2708
3029
|
var docUrl = '';
|
|
2709
3030
|
try { docUrl = String(doc.URL || ''); } catch(_) {}
|
|
2710
3031
|
if (docUrl === 'about:blank') return;
|
|
@@ -2739,6 +3060,7 @@ function startIframeContentApplyWatcher(navGen) {
|
|
|
2739
3060
|
}
|
|
2740
3061
|
if (iframeEarlySyncPrimedForGen !== navGen) {
|
|
2741
3062
|
iframeEarlySyncPrimedForGen = navGen;
|
|
3063
|
+
iframeEarlyDomSignature = computeIframeDomSignature(doc);
|
|
2742
3064
|
syncIframeInteractions('iframe-early-paint');
|
|
2743
3065
|
}
|
|
2744
3066
|
}
|
|
@@ -2808,6 +3130,8 @@ function injectIframeSelectionStyles(doc) {
|
|
|
2808
3130
|
st.textContent =
|
|
2809
3131
|
'.vve-selected{outline:2px solid #6366f1!important;outline-offset:2px!important;' +
|
|
2810
3132
|
'box-shadow:0 0 0 2px rgba(99,102,241,.28),inset 0 0 0 1px rgba(99,102,241,.18)!important;}' +
|
|
3133
|
+
'.vve-tree-hover{outline:2px solid #6366f1!important;outline-offset:2px!important;' +
|
|
3134
|
+
'box-shadow:0 0 0 2px rgba(99,102,241,.22),inset 0 0 0 1px rgba(99,102,241,.12)!important;}' +
|
|
2811
3135
|
'html.vve-drag-armed .vve-selected{cursor:grab!important;}' +
|
|
2812
3136
|
'.vve-dragging{opacity:0.92!important;outline:2px dashed #f59e0b!important;' +
|
|
2813
3137
|
'outline-offset:2px!important;cursor:grabbing!important;box-shadow:none!important;}';
|
|
@@ -2819,6 +3143,47 @@ function injectIframeSelectionStyles(doc) {
|
|
|
2819
3143
|
}
|
|
2820
3144
|
}
|
|
2821
3145
|
|
|
3146
|
+
function clearTreeHoverHighlight() {
|
|
3147
|
+
if (!hoveredTreeEl) return;
|
|
3148
|
+
beginSuppressIframeMutationDirty();
|
|
3149
|
+
try {
|
|
3150
|
+
if (hoveredTreeEl.classList) hoveredTreeEl.classList.remove('vve-tree-hover');
|
|
3151
|
+
} catch(_) {
|
|
3152
|
+
} finally {
|
|
3153
|
+
endSuppressIframeMutationDirty();
|
|
3154
|
+
hoveredTreeEl = null;
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
function setTreeHoverHighlight(el) {
|
|
3159
|
+
if (!el || el.nodeType !== 1) {
|
|
3160
|
+
clearTreeHoverHighlight();
|
|
3161
|
+
return;
|
|
3162
|
+
}
|
|
3163
|
+
if (hoveredTreeEl === el) return;
|
|
3164
|
+
clearTreeHoverHighlight();
|
|
3165
|
+
beginSuppressIframeMutationDirty();
|
|
3166
|
+
try {
|
|
3167
|
+
if (el.classList) el.classList.add('vve-tree-hover');
|
|
3168
|
+
hoveredTreeEl = el;
|
|
3169
|
+
} catch(_) {
|
|
3170
|
+
hoveredTreeEl = null;
|
|
3171
|
+
} finally {
|
|
3172
|
+
endSuppressIframeMutationDirty();
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
function isTreeHoverOnlyClassMutation(mutation) {
|
|
3177
|
+
if (!mutation || mutation.type !== 'attributes' || mutation.attributeName !== 'class') return false;
|
|
3178
|
+
var oldClass = String(mutation.oldValue || '');
|
|
3179
|
+
var target = mutation.target;
|
|
3180
|
+
var nextClass = '';
|
|
3181
|
+
try {
|
|
3182
|
+
nextClass = target && typeof target.className === 'string' ? target.className : '';
|
|
3183
|
+
} catch(_) {}
|
|
3184
|
+
return oldClass.indexOf('vve-tree-hover') >= 0 || String(nextClass).indexOf('vve-tree-hover') >= 0;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
2822
3187
|
function setDragHandleActive(on) {
|
|
2823
3188
|
dragHandleActive = !!on;
|
|
2824
3189
|
var b = document.getElementById('sf-drag');
|
|
@@ -3139,6 +3504,9 @@ function renderDomTree(filterRaw) {
|
|
|
3139
3504
|
if (e.target.closest && e.target.closest('.dt-chev')) return;
|
|
3140
3505
|
selectElementFromTree(el);
|
|
3141
3506
|
};
|
|
3507
|
+
row.onmouseenter = function() {
|
|
3508
|
+
setTreeHoverHighlight(el);
|
|
3509
|
+
};
|
|
3142
3510
|
root.appendChild(row);
|
|
3143
3511
|
|
|
3144
3512
|
if (!hasKids || collapsed) return;
|
|
@@ -3157,6 +3525,9 @@ function renderDomTree(filterRaw) {
|
|
|
3157
3525
|
? '<div class="dt-muted">No elements match your search.</div>'
|
|
3158
3526
|
: '<div class="dt-muted">No visible elements yet.</div>';
|
|
3159
3527
|
}
|
|
3528
|
+
root.onmouseleave = function() {
|
|
3529
|
+
clearTreeHoverHighlight();
|
|
3530
|
+
};
|
|
3160
3531
|
}
|
|
3161
3532
|
|
|
3162
3533
|
// \u2500\u2500 Utility helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -3174,6 +3545,40 @@ function pr2(l1, i1, l2, i2) {
|
|
|
3174
3545
|
'<div class="pr2-item"><div class="pr2-lbl">'+l2+'</div>'+i2+'</div></div>';
|
|
3175
3546
|
}
|
|
3176
3547
|
function subLbl(text) { return '<div class="sub-lbl">'+text+'</div>'; }
|
|
3548
|
+
function openCustomCssModal() {
|
|
3549
|
+
var modal = document.getElementById('custom-css-modal');
|
|
3550
|
+
var ta = document.getElementById('custom-css-modal-textarea');
|
|
3551
|
+
if (!modal || !ta) return;
|
|
3552
|
+
var inp = document.getElementById('pp-css');
|
|
3553
|
+
var v = inp ? inp.value : (selectedEl && selectedEl.getAttribute ? (selectedEl.getAttribute('style') || '') : '');
|
|
3554
|
+
ta.value = v || '';
|
|
3555
|
+
modal.classList.add('open');
|
|
3556
|
+
modal.setAttribute('aria-hidden', 'false');
|
|
3557
|
+
setTimeout(function() {
|
|
3558
|
+
try { ta.focus(); ta.setSelectionRange(ta.value.length, ta.value.length); } catch(_) {}
|
|
3559
|
+
}, 0);
|
|
3560
|
+
}
|
|
3561
|
+
function closeCustomCssModal() {
|
|
3562
|
+
var modal = document.getElementById('custom-css-modal');
|
|
3563
|
+
if (!modal) return;
|
|
3564
|
+
modal.classList.remove('open');
|
|
3565
|
+
modal.setAttribute('aria-hidden', 'true');
|
|
3566
|
+
}
|
|
3567
|
+
function applyCustomCssModal() {
|
|
3568
|
+
var ta = document.getElementById('custom-css-modal-textarea');
|
|
3569
|
+
var inp = document.getElementById('pp-css');
|
|
3570
|
+
if (!ta || !inp) {
|
|
3571
|
+
closeCustomCssModal();
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
inp.value = ta.value || '';
|
|
3575
|
+
try {
|
|
3576
|
+
inp.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3577
|
+
} catch(_) {
|
|
3578
|
+
if (typeof inp.oninput === 'function') inp.oninput();
|
|
3579
|
+
}
|
|
3580
|
+
closeCustomCssModal();
|
|
3581
|
+
}
|
|
3177
3582
|
function weightOpts(cur) {
|
|
3178
3583
|
return [['100','Thin'],['200','Extra Light'],['300','Light'],['400','Normal'],['500','Medium'],
|
|
3179
3584
|
['600','Semi Bold'],['700','Bold'],['800','Extra Bold'],['900','Black']]
|
|
@@ -3454,7 +3859,12 @@ function renderRightPanel(el) {
|
|
|
3454
3859
|
// \u2500\u2500 CSS and Classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3455
3860
|
document.getElementById('acc-body-css').innerHTML =
|
|
3456
3861
|
pr('Classes', '<input class="pr-inp" id="pp-cls" type="text" value="'+esc(el.className||'')+'" placeholder="class1 class2">') +
|
|
3457
|
-
|
|
3862
|
+
'<div class="sub-lbl-row">' +
|
|
3863
|
+
'<div class="sub-lbl">Custom CSS</div>' +
|
|
3864
|
+
'<button type="button" class="css-expand-btn" title="Open full-screen editor" onclick="openCustomCssModal()">' +
|
|
3865
|
+
'<i class="bi bi-fullscreen"></i>' +
|
|
3866
|
+
'</button>' +
|
|
3867
|
+
'</div>' +
|
|
3458
3868
|
'<textarea class="pr-inp" id="pp-css" style="width:100%;min-height:80px;font-family:monospace;font-size:11px" placeholder="color: red; font-size: 16px;">'+esc(el.getAttribute('style')||'')+'</textarea>';
|
|
3459
3869
|
|
|
3460
3870
|
// \u2500\u2500 Attributes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -3537,12 +3947,25 @@ function renderRightPanel(el) {
|
|
|
3537
3947
|
var sel = buildSelector(el);
|
|
3538
3948
|
bindings.forEach(function(b){
|
|
3539
3949
|
var inp = document.getElementById(b[0]);
|
|
3540
|
-
if (inp)
|
|
3950
|
+
if (inp) {
|
|
3951
|
+
var onValueChange = function() {
|
|
3541
3952
|
// Read the original value BEFORE applying the change so we can revert later
|
|
3542
3953
|
var orig = getOriginalValue(b[0], el);
|
|
3543
3954
|
b[1](inp.value);
|
|
3544
|
-
|
|
3545
|
-
|
|
3955
|
+
var valueToLog = inp.value;
|
|
3956
|
+
try {
|
|
3957
|
+
console.log('[V2] input changed', {
|
|
3958
|
+
inputId: b[0],
|
|
3959
|
+
rawValue: inp.value,
|
|
3960
|
+
appliedValue: valueToLog,
|
|
3961
|
+
selector: sel,
|
|
3962
|
+
});
|
|
3963
|
+
} catch(_) {}
|
|
3964
|
+
logChange(sel, b[0], valueToLog, el, orig);
|
|
3965
|
+
};
|
|
3966
|
+
inp.addEventListener('input', onValueChange);
|
|
3967
|
+
inp.addEventListener('change', onValueChange);
|
|
3968
|
+
}
|
|
3546
3969
|
});
|
|
3547
3970
|
}
|
|
3548
3971
|
|
|
@@ -3805,6 +4228,32 @@ function attachClickHandler() {
|
|
|
3805
4228
|
} catch(_) {}
|
|
3806
4229
|
}
|
|
3807
4230
|
|
|
4231
|
+
function attachIframeHoverHandler() {
|
|
4232
|
+
try {
|
|
4233
|
+
var iframe = document.getElementById('iframeId');
|
|
4234
|
+
var doc = iframe && iframe.contentDocument;
|
|
4235
|
+
if (!doc || !doc.body) return;
|
|
4236
|
+
if (hoverAttachDoc === doc) return;
|
|
4237
|
+
hoverAttachDoc = doc;
|
|
4238
|
+
doc.addEventListener('mousemove', function(e) {
|
|
4239
|
+
if (currentMode !== 'editor') {
|
|
4240
|
+
clearTreeHoverHighlight();
|
|
4241
|
+
return;
|
|
4242
|
+
}
|
|
4243
|
+
var target = e.target;
|
|
4244
|
+
if (!target || target === doc.body || target === doc.documentElement) {
|
|
4245
|
+
clearTreeHoverHighlight();
|
|
4246
|
+
return;
|
|
4247
|
+
}
|
|
4248
|
+
setTreeHoverHighlight(target);
|
|
4249
|
+
}, true);
|
|
4250
|
+
doc.addEventListener('mouseout', function(e) {
|
|
4251
|
+
if (e.relatedTarget) return;
|
|
4252
|
+
clearTreeHoverHighlight();
|
|
4253
|
+
}, true);
|
|
4254
|
+
} catch(_) {}
|
|
4255
|
+
}
|
|
4256
|
+
|
|
3808
4257
|
function attachChangeObserver() {
|
|
3809
4258
|
try {
|
|
3810
4259
|
var iframe = document.getElementById('iframeId');
|
|
@@ -3817,27 +4266,15 @@ function attachChangeObserver() {
|
|
|
3817
4266
|
changeObserverDoc = null;
|
|
3818
4267
|
}
|
|
3819
4268
|
changeObserver = new MutationObserver(function(mutations) {
|
|
3820
|
-
|
|
3821
|
-
var bodyReplaced = false;
|
|
4269
|
+
var hasMeaningfulMutation = false;
|
|
3822
4270
|
for (var mi = 0; mi < mutations.length; mi++) {
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
m &&
|
|
3826
|
-
m.type === 'childList' &&
|
|
3827
|
-
m.target === doc.body &&
|
|
3828
|
-
m.addedNodes &&
|
|
3829
|
-
m.removedNodes &&
|
|
3830
|
-
m.addedNodes.length > 0 &&
|
|
3831
|
-
m.removedNodes.length > 0
|
|
3832
|
-
) {
|
|
3833
|
-
bodyReplaced = true;
|
|
4271
|
+
if (!isTreeHoverOnlyClassMutation(mutations[mi])) {
|
|
4272
|
+
hasMeaningfulMutation = true;
|
|
3834
4273
|
break;
|
|
3835
4274
|
}
|
|
3836
4275
|
}
|
|
3837
|
-
if (
|
|
3838
|
-
|
|
3839
|
-
appliedStructuralChangesetKeys = {};
|
|
3840
|
-
}
|
|
4276
|
+
if (!hasMeaningfulMutation) return;
|
|
4277
|
+
// Dirty state is derived from changesets baseline + stateChanges (not raw DOM mutations).
|
|
3841
4278
|
// Host scripts can replace selected nodes every few frames (e.g. A/B tool observers).
|
|
3842
4279
|
// Keep selection sticky by re-resolving from fingerprint.
|
|
3843
4280
|
recoverSelectedElement(false);
|
|
@@ -3847,7 +4284,7 @@ function attachChangeObserver() {
|
|
|
3847
4284
|
updateSelectionToolbar();
|
|
3848
4285
|
});
|
|
3849
4286
|
changeObserver.observe(doc.body, {
|
|
3850
|
-
childList: true, subtree: true, attributes: true, characterData: true
|
|
4287
|
+
childList: true, subtree: true, attributes: true, characterData: true, attributeOldValue: true
|
|
3851
4288
|
});
|
|
3852
4289
|
changeObserverDoc = doc;
|
|
3853
4290
|
} catch(_) {}
|
|
@@ -3871,7 +4308,9 @@ function syncIframeInteractions(reason) {
|
|
|
3871
4308
|
}
|
|
3872
4309
|
showNoUrl(false);
|
|
3873
4310
|
injectIframeSelectionStyles(doc);
|
|
4311
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
3874
4312
|
attachClickHandler();
|
|
4313
|
+
attachIframeHoverHandler();
|
|
3875
4314
|
attachDragReposition();
|
|
3876
4315
|
attachChangeObserver();
|
|
3877
4316
|
startConsistencyWatchdog(doc);
|
|
@@ -4230,10 +4669,9 @@ window.addEventListener('load', function() {
|
|
|
4230
4669
|
var iframe = document.getElementById('iframeId');
|
|
4231
4670
|
iframe.addEventListener('load', function() {
|
|
4232
4671
|
if (!iframe.src || iframe.src === 'about:blank' || iframe.src === window.location.href) return;
|
|
4233
|
-
// New iframe navigation: always drop bindings tied to the previous document.
|
|
4234
|
-
resetIframeBindings();
|
|
4235
4672
|
var doc = iframe.contentDocument;
|
|
4236
4673
|
if (!doc) {
|
|
4674
|
+
resetIframeBindings();
|
|
4237
4675
|
syncIframeInteractions('iframe-load-no-doc');
|
|
4238
4676
|
return;
|
|
4239
4677
|
}
|
|
@@ -4242,14 +4680,32 @@ window.addEventListener('load', function() {
|
|
|
4242
4680
|
// Stale events: src may already be the proxy URL while the document is still
|
|
4243
4681
|
// about:blank (e.g. src cleared then reset to force reload). Ask sync path to retry.
|
|
4244
4682
|
if (docUrl === 'about:blank') {
|
|
4683
|
+
resetIframeBindings();
|
|
4245
4684
|
syncIframeInteractions('iframe-load-about-blank');
|
|
4246
4685
|
return;
|
|
4247
4686
|
}
|
|
4687
|
+
// If early-paint and final load DOM signatures match, avoid a second full apply/reset
|
|
4688
|
+
// that steals focus from sidebar controls while the page is still stabilizing.
|
|
4689
|
+
var shouldRefreshOnFinalLoad = true;
|
|
4690
|
+
if (
|
|
4691
|
+
iframeEarlyDomSignatureNavGen === iframeContentNavGen &&
|
|
4692
|
+
iframeEarlySyncPrimedForGen === iframeContentNavGen &&
|
|
4693
|
+
iframeEarlyDomSignature
|
|
4694
|
+
) {
|
|
4695
|
+
var finalDomSignature = computeIframeDomSignature(doc);
|
|
4696
|
+
if (finalDomSignature && finalDomSignature === iframeEarlyDomSignature) {
|
|
4697
|
+
shouldRefreshOnFinalLoad = false;
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
if (clickAttachDoc !== doc || dragAttachDoc !== doc || changeObserverDoc !== doc || hoverAttachDoc !== doc) {
|
|
4701
|
+
resetIframeBindings();
|
|
4702
|
+
}
|
|
4248
4703
|
attachIframeLoadingUntilComplete(iframe);
|
|
4249
4704
|
if (doc.body && iframeDocMatchesNavigatedSrc(iframe, doc)) {
|
|
4250
4705
|
stopIframeContentApplyWatcher();
|
|
4251
|
-
|
|
4252
|
-
|
|
4706
|
+
if (shouldRefreshOnFinalLoad) {
|
|
4707
|
+
applyActiveVariationHtml();
|
|
4708
|
+
}
|
|
4253
4709
|
}
|
|
4254
4710
|
// Always attempt sync; it has its own readiness checks + retry loop.
|
|
4255
4711
|
syncIframeInteractions('iframe-load');
|
|
@@ -4591,9 +5047,12 @@ function createVisualEditorMiddleware(options) {
|
|
|
4591
5047
|
}
|
|
4592
5048
|
);
|
|
4593
5049
|
if (html.includes("</head>")) {
|
|
4594
|
-
html = html.replace(
|
|
4595
|
-
|
|
4596
|
-
|
|
5050
|
+
html = html.replace(
|
|
5051
|
+
"</head>",
|
|
5052
|
+
`${iframeAlwaysShowCss}
|
|
5053
|
+
${iframeAlwaysShowCssGuardScript}
|
|
5054
|
+
</head>`
|
|
5055
|
+
);
|
|
4597
5056
|
}
|
|
4598
5057
|
html = html.replace(
|
|
4599
5058
|
/<meta[^>]+http-equiv=["']?\s*(x-frame-options|content-security-policy)\s*["']?[^>]*>/gi,
|