@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.js
CHANGED
|
@@ -3,8 +3,20 @@ import path from 'path';
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
|
|
5
5
|
// src/visualEditorProxyPlugin.ts
|
|
6
|
-
var
|
|
7
|
-
var
|
|
6
|
+
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>`;
|
|
7
|
+
var iframeAlwaysShowCssGuardScript = `<script id="__ce_force_show_guard">(function(){try{
|
|
8
|
+
function ensureForceShowStyleLast(){
|
|
9
|
+
var style=document.getElementById("__ce_force_show");
|
|
10
|
+
if(!style||!document.head)return;
|
|
11
|
+
if(document.head.lastElementChild!==style){
|
|
12
|
+
document.head.appendChild(style);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
ensureForceShowStyleLast();
|
|
16
|
+
var mo=new MutationObserver(function(){ensureForceShowStyleLast();});
|
|
17
|
+
if(document.head){mo.observe(document.head,{childList:true});}
|
|
18
|
+
window.addEventListener("beforeunload",function(){try{mo.disconnect();}catch(_){}});}
|
|
19
|
+
catch(_){}})();</script>`;
|
|
8
20
|
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.`;
|
|
9
21
|
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'});})();`;
|
|
10
22
|
function buildVvvebEditorHtml() {
|
|
@@ -140,6 +152,17 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
140
152
|
.tb-save-txt{font-size:14px;color:#00C951;white-space:nowrap}
|
|
141
153
|
.tb-save-txt::before{content:'Saved'}
|
|
142
154
|
#dirty-dot.on~.tb-save-txt::before{content:'Unsaved'}
|
|
155
|
+
|
|
156
|
+
/* \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 */
|
|
157
|
+
#ve-notification{
|
|
158
|
+
position:fixed;right:16px;top:64px;z-index:13000;max-width:360px;
|
|
159
|
+
padding:10px 12px;border-radius:8px;font-size:12px;line-height:1.35;
|
|
160
|
+
border:1px solid var(--border);background:#fff;color:var(--text);
|
|
161
|
+
box-shadow:0 10px 24px rgba(2,6,23,.18);display:none
|
|
162
|
+
}
|
|
163
|
+
#ve-notification.show{display:block}
|
|
164
|
+
#ve-notification.error{border-color:#fecaca;background:#fef2f2;color:#991b1b}
|
|
165
|
+
#ve-notification.success{border-color:#bbf7d0;background:#f0fdf4;color:#166534}
|
|
143
166
|
.tb-save-time{font-size:12px;color:#52525b;white-space:nowrap}
|
|
144
167
|
#dirty-dot.on~.tb-save-time{display:none}
|
|
145
168
|
/* Simulate + Finalize buttons */
|
|
@@ -235,7 +258,7 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
235
258
|
/* \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 */
|
|
236
259
|
.dt-tree{font-size:11px;padding:0px 0 0px 20px;user-select:none}
|
|
237
260
|
.dt-row{
|
|
238
|
-
display:flex;align-items:center;gap:2px;min-height:26px;padding:2px 8px 2px 4px;
|
|
261
|
+
width:fit-content;display:flex;align-items:center;gap:2px;min-height:26px;padding:2px 8px 2px 4px;
|
|
239
262
|
cursor:pointer;color:var(--text-2);border-radius:4px;margin:0 4px
|
|
240
263
|
}
|
|
241
264
|
.dt-row:hover{background:var(--bg-hover);color:var(--text)}
|
|
@@ -447,6 +470,49 @@ html,body{height:100%;overflow:hidden;font-family:-apple-system,BlinkMacSystemFo
|
|
|
447
470
|
/* \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 */
|
|
448
471
|
.sub-lbl{font-size:10px;text-transform:uppercase;color:var(--text-3);font-weight:700;letter-spacing:.05em;margin:8px 0 5px}
|
|
449
472
|
.sub-lbl:first-child{margin-top:0}
|
|
473
|
+
.sub-lbl-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:8px 0 5px}
|
|
474
|
+
.sub-lbl-row .sub-lbl{margin:0}
|
|
475
|
+
.css-expand-btn{
|
|
476
|
+
width:22px;height:22px;border:1px solid var(--border);border-radius:5px;background:#fff;
|
|
477
|
+
display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-3);
|
|
478
|
+
transition:all .15s
|
|
479
|
+
}
|
|
480
|
+
.css-expand-btn:hover{border-color:var(--accent);color:var(--accent-txt);background:var(--accent-bg)}
|
|
481
|
+
|
|
482
|
+
/* \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 */
|
|
483
|
+
#custom-css-modal{
|
|
484
|
+
position:fixed;inset:0;z-index:12000;background:rgba(15,23,42,.5);
|
|
485
|
+
display:none;align-items:center;justify-content:center;padding:20px
|
|
486
|
+
}
|
|
487
|
+
#custom-css-modal.open{display:flex}
|
|
488
|
+
.custom-css-dialog{
|
|
489
|
+
width:min(860px,96vw);max-height:86vh;background:#fff;border:1px solid var(--border);
|
|
490
|
+
border-radius:10px;box-shadow:0 20px 40px rgba(2,6,23,.35);display:flex;flex-direction:column;overflow:hidden
|
|
491
|
+
}
|
|
492
|
+
.custom-css-head{
|
|
493
|
+
display:flex;align-items:center;justify-content:space-between;gap:10px;
|
|
494
|
+
padding:10px 12px;border-bottom:1px solid var(--border);background:#fff
|
|
495
|
+
}
|
|
496
|
+
.custom-css-title{font-size:12px;font-weight:700;color:var(--text)}
|
|
497
|
+
.custom-css-close{
|
|
498
|
+
border:none;background:transparent;color:var(--text-3);cursor:pointer;width:24px;height:24px;border-radius:5px
|
|
499
|
+
}
|
|
500
|
+
.custom-css-close:hover{background:var(--bg-hover);color:var(--text)}
|
|
501
|
+
#custom-css-modal-textarea{
|
|
502
|
+
width:100%;min-height:360px;max-height:58vh;resize:vertical;border:none;outline:none;
|
|
503
|
+
font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
|
504
|
+
font-size:12px;line-height:1.5;padding:12px;background:#0b1220;color:#e2e8f0
|
|
505
|
+
}
|
|
506
|
+
.custom-css-actions{
|
|
507
|
+
display:flex;justify-content:flex-end;gap:8px;padding:10px 12px;border-top:1px solid var(--border);background:#fff
|
|
508
|
+
}
|
|
509
|
+
.custom-css-btn{
|
|
510
|
+
border:1px solid var(--border);background:#fff;color:var(--text-2);border-radius:6px;padding:6px 10px;cursor:pointer;
|
|
511
|
+
font-size:12px;font-weight:600;font-family:inherit
|
|
512
|
+
}
|
|
513
|
+
.custom-css-btn:hover{background:var(--bg-hover)}
|
|
514
|
+
.custom-css-btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}
|
|
515
|
+
.custom-css-btn.primary:hover{filter:brightness(.97)}
|
|
450
516
|
|
|
451
517
|
/* \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 */
|
|
452
518
|
.shadow-presets{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
|
|
@@ -618,10 +684,10 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
618
684
|
<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>
|
|
619
685
|
<button class="tb-dk-btn" title="Comments"><i class="bi bi-chat-dots"></i></button>
|
|
620
686
|
</div>
|
|
621
|
-
<!-- btn-close: hidden visually, kept for JS event listener -->
|
|
622
|
-
<button id="btn-close" style="display:none" title="Close editor"></button>
|
|
623
687
|
<button class="tb-sim-btn" id="btn-simulate" onclick="simulateExperiment()"><i class="bi bi-lightning-charge-fill"></i> Simulate</button>
|
|
624
|
-
<button class="tb-fin-btn" id="btn-save">
|
|
688
|
+
<button class="tb-fin-btn" id="btn-save">Save Changes</button>
|
|
689
|
+
<!-- btn-close: kept for JS event listener -->
|
|
690
|
+
<button class="tb-fin-btn" id="btn-close">Close</button>
|
|
625
691
|
</div>
|
|
626
692
|
|
|
627
693
|
<!-- url-bar: hidden, kept for JS compatibility -->
|
|
@@ -836,6 +902,23 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
836
902
|
|
|
837
903
|
</div><!-- #app -->
|
|
838
904
|
|
|
905
|
+
<!-- Custom CSS full-screen modal -->
|
|
906
|
+
<div id="custom-css-modal" aria-hidden="true">
|
|
907
|
+
<div class="custom-css-dialog" role="dialog" aria-modal="true" aria-labelledby="custom-css-modal-title">
|
|
908
|
+
<div class="custom-css-head">
|
|
909
|
+
<div class="custom-css-title" id="custom-css-modal-title"><i class="bi bi-code-slash"></i> Custom CSS</div>
|
|
910
|
+
<button type="button" class="custom-css-close" onclick="closeCustomCssModal()" title="Close">
|
|
911
|
+
<i class="bi bi-x-lg"></i>
|
|
912
|
+
</button>
|
|
913
|
+
</div>
|
|
914
|
+
<textarea id="custom-css-modal-textarea" spellcheck="false" placeholder="Type your css here"></textarea>
|
|
915
|
+
<div class="custom-css-actions">
|
|
916
|
+
<button type="button" class="custom-css-btn" onclick="closeCustomCssModal()">Cancel</button>
|
|
917
|
+
<button type="button" class="custom-css-btn primary" onclick="applyCustomCssModal()">Add</button>
|
|
918
|
+
</div>
|
|
919
|
+
</div>
|
|
920
|
+
</div>
|
|
921
|
+
|
|
839
922
|
<!-- CDN scripts -->
|
|
840
923
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
841
924
|
<script src="https://cdn.jsdelivr.net/gh/givanz/VvvebJs@master/libs/builder/builder.js"></script>
|
|
@@ -907,13 +990,37 @@ function send(type, payload) {
|
|
|
907
990
|
window.parent.postMessage({ channel: CHANNEL, type: type, payload: payload || {} }, '*');
|
|
908
991
|
}
|
|
909
992
|
|
|
993
|
+
var notificationTimer = null;
|
|
994
|
+
function showEditorNotification(message, kind, durationMs) {
|
|
995
|
+
var id = 've-notification';
|
|
996
|
+
var el = document.getElementById(id);
|
|
997
|
+
if (!el) {
|
|
998
|
+
el = document.createElement('div');
|
|
999
|
+
el.id = id;
|
|
1000
|
+
el.setAttribute('role', 'status');
|
|
1001
|
+
el.setAttribute('aria-live', 'polite');
|
|
1002
|
+
document.body.appendChild(el);
|
|
1003
|
+
}
|
|
1004
|
+
el.className = '';
|
|
1005
|
+
el.classList.add(kind === 'success' ? 'success' : 'error');
|
|
1006
|
+
el.classList.add('show');
|
|
1007
|
+
el.textContent = String(message || 'Something went wrong');
|
|
1008
|
+
if (notificationTimer) clearTimeout(notificationTimer);
|
|
1009
|
+
notificationTimer = setTimeout(function() {
|
|
1010
|
+
var cur = document.getElementById(id);
|
|
1011
|
+
if (!cur) return;
|
|
1012
|
+
cur.classList.remove('show');
|
|
1013
|
+
}, Math.max(1200, durationMs || 2600));
|
|
1014
|
+
}
|
|
1015
|
+
|
|
910
1016
|
function generatePreviewUrlString(args) {
|
|
911
1017
|
var baseUrl = (args && args.url) || '';
|
|
912
1018
|
var test = (args && args.test) || {};
|
|
913
1019
|
var variation = (args && args.variation) || {};
|
|
914
1020
|
if (!baseUrl) return '';
|
|
915
|
-
var testId = test.iid ||
|
|
916
|
-
var variationId = variation.iid ||
|
|
1021
|
+
var testId = test.iid || '';
|
|
1022
|
+
var variationId = variation.iid || '';
|
|
1023
|
+
if (!testId || !variationId) return '';
|
|
917
1024
|
var cId = String(testId || '') + '_' + String(variationId || '');
|
|
918
1025
|
var hasQueryParams = String(baseUrl).indexOf('?') >= 0;
|
|
919
1026
|
return (
|
|
@@ -937,6 +1044,10 @@ function simulateExperiment() {
|
|
|
937
1044
|
test.pageUrl ||
|
|
938
1045
|
(test.metadata_1 && test.metadata_1.editor_url) ||
|
|
939
1046
|
'';
|
|
1047
|
+
if (!test.iid || !activeVariation || !activeVariation.iid) {
|
|
1048
|
+
showEditorNotification('Cannot simulate: missing test.iid or variation.iid.', 'error', 3200);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
940
1051
|
var url = generatePreviewUrlString({
|
|
941
1052
|
url: targetUrl,
|
|
942
1053
|
test: test,
|
|
@@ -944,6 +1055,7 @@ function simulateExperiment() {
|
|
|
944
1055
|
});
|
|
945
1056
|
if (!url) {
|
|
946
1057
|
console.warn('[V2] simulateExperiment: missing target URL');
|
|
1058
|
+
showEditorNotification('Cannot simulate: missing target URL.', 'error', 3200);
|
|
947
1059
|
return;
|
|
948
1060
|
}
|
|
949
1061
|
try {
|
|
@@ -961,6 +1073,19 @@ window.addEventListener('message', function(e) {
|
|
|
961
1073
|
}
|
|
962
1074
|
});
|
|
963
1075
|
|
|
1076
|
+
document.addEventListener('keydown', function(e) {
|
|
1077
|
+
if (e.key !== 'Escape') return;
|
|
1078
|
+
var modal = document.getElementById('custom-css-modal');
|
|
1079
|
+
if (!modal || !modal.classList.contains('open')) return;
|
|
1080
|
+
closeCustomCssModal();
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
document.addEventListener('click', function(e) {
|
|
1084
|
+
var modal = document.getElementById('custom-css-modal');
|
|
1085
|
+
if (!modal || !modal.classList.contains('open')) return;
|
|
1086
|
+
if (e.target === modal) closeCustomCssModal();
|
|
1087
|
+
});
|
|
1088
|
+
|
|
964
1089
|
// \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
|
|
965
1090
|
var experimentData = null;
|
|
966
1091
|
var variations = [];
|
|
@@ -981,6 +1106,8 @@ var iframeContentNavGen = 0;
|
|
|
981
1106
|
var iframeContentApplyTimer = null;
|
|
982
1107
|
var iframeEarlyGranularPrimedForGen = null;
|
|
983
1108
|
var iframeEarlySyncPrimedForGen = null;
|
|
1109
|
+
var iframeEarlyDomSignature = '';
|
|
1110
|
+
var iframeEarlyDomSignatureNavGen = 0;
|
|
984
1111
|
/** insert/reorder entries are applied from early granular + full apply \u2014 skip exact duplicates per iframe nav */
|
|
985
1112
|
var appliedStructuralChangesetKeys = {};
|
|
986
1113
|
var isDirty = false;
|
|
@@ -992,6 +1119,7 @@ var selectedEl = null;
|
|
|
992
1119
|
var selectedElFingerprint = '';
|
|
993
1120
|
var selectedElRecoverMisses = 0;
|
|
994
1121
|
var MAX_SELECTED_RECOVER_MISSES = 12;
|
|
1122
|
+
var hoveredTreeEl = null;
|
|
995
1123
|
var isDeselectingSelection = false;
|
|
996
1124
|
var suppressClickUntil = 0;
|
|
997
1125
|
var dragAttachDoc = null;
|
|
@@ -1005,6 +1133,7 @@ var iframeSyncAttempts = 0;
|
|
|
1005
1133
|
var selectionScrollWin = null;
|
|
1006
1134
|
var selectionResizeBound = false;
|
|
1007
1135
|
var clickAttachDoc = null;
|
|
1136
|
+
var hoverAttachDoc = null;
|
|
1008
1137
|
var changeObserver = null;
|
|
1009
1138
|
var changeObserverDoc = null;
|
|
1010
1139
|
/** Incremented while applying changesets / selection chrome so MutationObserver does not mark dirty */
|
|
@@ -1040,14 +1169,17 @@ function endSuppressIframeMutationDirty() {
|
|
|
1040
1169
|
function commitStateChangesForActiveVariation() {
|
|
1041
1170
|
if (!activeVarId) return;
|
|
1042
1171
|
stateChangesByVarId[activeVarId] = (stateChanges || []).slice();
|
|
1172
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1043
1173
|
}
|
|
1044
1174
|
|
|
1045
1175
|
function loadStateChangesForActiveVariation() {
|
|
1046
1176
|
if (!activeVarId) {
|
|
1047
1177
|
stateChanges = [];
|
|
1178
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1048
1179
|
return;
|
|
1049
1180
|
}
|
|
1050
1181
|
stateChanges = (stateChangesByVarId[activeVarId] || []).slice();
|
|
1182
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1051
1183
|
}
|
|
1052
1184
|
|
|
1053
1185
|
function recoverSelectedElement(forceDeselectOnMiss) {
|
|
@@ -1214,6 +1346,7 @@ function setMode(mode) {
|
|
|
1214
1346
|
document.getElementById('btn-mode-editor').classList.toggle('active', mode === 'editor');
|
|
1215
1347
|
document.getElementById('btn-mode-nav').classList.toggle('active', mode === 'navigate');
|
|
1216
1348
|
if (mode === 'navigate') {
|
|
1349
|
+
clearTreeHoverHighlight();
|
|
1217
1350
|
setDragHandleActive(false);
|
|
1218
1351
|
deselectElement();
|
|
1219
1352
|
} else if (mode === 'editor') {
|
|
@@ -1527,24 +1660,26 @@ function persistActiveVariationChangesets(arr) {
|
|
|
1527
1660
|
}
|
|
1528
1661
|
}
|
|
1529
1662
|
}
|
|
1663
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1530
1664
|
}
|
|
1531
1665
|
|
|
1532
1666
|
function entrySnapshotKey(entry) {
|
|
1533
1667
|
if (!entry || !entry.selector) return '';
|
|
1668
|
+
var SEP = '__vve_sep__';
|
|
1534
1669
|
var selKey = sanitizeSelectorForMatch(entry.selector) || entry.selector;
|
|
1535
1670
|
return (
|
|
1536
1671
|
selKey +
|
|
1537
|
-
|
|
1672
|
+
SEP +
|
|
1538
1673
|
normalizeChangesetType(entry) +
|
|
1539
|
-
|
|
1674
|
+
SEP +
|
|
1540
1675
|
String(entry.property || '') +
|
|
1541
|
-
|
|
1676
|
+
SEP +
|
|
1542
1677
|
String(entry.attribute || '') +
|
|
1543
|
-
|
|
1678
|
+
SEP +
|
|
1544
1679
|
String(entry.action || '') +
|
|
1545
|
-
|
|
1680
|
+
SEP +
|
|
1546
1681
|
String(entry.html != null ? 'h' + String(entry.html).length : '') +
|
|
1547
|
-
|
|
1682
|
+
SEP +
|
|
1548
1683
|
String(entry.value != null ? 'v:' + entry.value : '')
|
|
1549
1684
|
);
|
|
1550
1685
|
}
|
|
@@ -1594,15 +1729,15 @@ function softReloadEditorIframe() {
|
|
|
1594
1729
|
var navGen = nextIframeContentNavGen();
|
|
1595
1730
|
resetIframeBindings();
|
|
1596
1731
|
setIframePageLoadingUi(true);
|
|
1597
|
-
iframe.src = '';
|
|
1598
1732
|
iframe.src = appendIframeReloadBust(src);
|
|
1599
|
-
startIframeContentApplyWatcher(navGen);
|
|
1733
|
+
startIframeContentApplyWatcher(navGen, iframe.contentDocument || null);
|
|
1600
1734
|
scheduleDomTreeRefresh();
|
|
1601
1735
|
}
|
|
1602
1736
|
|
|
1603
1737
|
/** @returns {boolean} true if a full iframe reload was started */
|
|
1604
1738
|
function revertChangesetEntryOnDom(entry) {
|
|
1605
1739
|
if (!entry) return false;
|
|
1740
|
+
var styleOnly = isStyleOnlyChangesetEntry(entry);
|
|
1606
1741
|
if (entry.selector === '__vvveb_body__') {
|
|
1607
1742
|
var iframeDoc0 = document.getElementById('iframeId').contentDocument;
|
|
1608
1743
|
if (!iframeDoc0 || !iframeDoc0.body) return false;
|
|
@@ -1623,6 +1758,11 @@ function revertChangesetEntryOnDom(entry) {
|
|
|
1623
1758
|
var snap = appliedChangesetSnapshots[k];
|
|
1624
1759
|
var el = querySelectorResolved(iframeDoc, entry.selector);
|
|
1625
1760
|
if (!snap || !el) {
|
|
1761
|
+
if (styleOnly) {
|
|
1762
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1763
|
+
delete appliedChangesetSnapshots[k];
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1626
1766
|
softReloadEditorIframe();
|
|
1627
1767
|
delete appliedChangesetSnapshots[k];
|
|
1628
1768
|
return true;
|
|
@@ -1637,6 +1777,11 @@ function revertChangesetEntryOnDom(entry) {
|
|
|
1637
1777
|
else el.setAttribute(snap.name, snap.v);
|
|
1638
1778
|
} else if (snap.kind === 'display') el.style.display = snap.v;
|
|
1639
1779
|
else {
|
|
1780
|
+
if (styleOnly) {
|
|
1781
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1782
|
+
delete appliedChangesetSnapshots[k];
|
|
1783
|
+
return false;
|
|
1784
|
+
}
|
|
1640
1785
|
softReloadEditorIframe();
|
|
1641
1786
|
delete appliedChangesetSnapshots[k];
|
|
1642
1787
|
return true;
|
|
@@ -1696,7 +1841,9 @@ function renderHistoryTab() {
|
|
|
1696
1841
|
var lab = historyEntryTypeLabel(item.entry);
|
|
1697
1842
|
var val = historyEntryValuePreview(item.entry);
|
|
1698
1843
|
html +=
|
|
1699
|
-
'<div class="state-item"
|
|
1844
|
+
'<div class="state-item" role="button" tabindex="0" title="Jump to element in iframe" onclick="focusHistoryChangeset(' +
|
|
1845
|
+
item.idx +
|
|
1846
|
+
')">' +
|
|
1700
1847
|
'<span class="state-item-idx" style="opacity:0.55;font-size:10px;min-width:2.2em;display:inline-block" title="Order in saved changesets">#' +
|
|
1701
1848
|
item.idx +
|
|
1702
1849
|
'</span>' +
|
|
@@ -1712,7 +1859,7 @@ function renderHistoryTab() {
|
|
|
1712
1859
|
item.idx +
|
|
1713
1860
|
')" onclick="removeHistoryChangeset(' +
|
|
1714
1861
|
item.idx +
|
|
1715
|
-
')">✕</button>' +
|
|
1862
|
+
', event)">✕</button>' +
|
|
1716
1863
|
'</div>';
|
|
1717
1864
|
});
|
|
1718
1865
|
html += '</div>';
|
|
@@ -1730,7 +1877,38 @@ function changesetListHasStructural(arr) {
|
|
|
1730
1877
|
return false;
|
|
1731
1878
|
}
|
|
1732
1879
|
|
|
1733
|
-
function
|
|
1880
|
+
function isStyleOnlyChangesetEntry(entry) {
|
|
1881
|
+
if (!entry) return false;
|
|
1882
|
+
var t = normalizeChangesetType(entry);
|
|
1883
|
+
if (t === 'style') return true;
|
|
1884
|
+
if (t === 'attribute' && String(entry.attribute || '').toLowerCase() === 'style') return true;
|
|
1885
|
+
return false;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
function focusHistoryChangeset(idx) {
|
|
1889
|
+
var v = getActiveVariationForHistory();
|
|
1890
|
+
if (!v) return;
|
|
1891
|
+
var arr = parseVariationChangesets(v);
|
|
1892
|
+
if (idx < 0 || idx >= arr.length) return;
|
|
1893
|
+
var entry = arr[idx];
|
|
1894
|
+
if (!entry || !entry.selector || entry.selector === '__vvveb_body__') return;
|
|
1895
|
+
try {
|
|
1896
|
+
var iframe = document.getElementById('iframeId');
|
|
1897
|
+
var iframeDoc = iframe && iframe.contentDocument;
|
|
1898
|
+
if (!iframeDoc) return;
|
|
1899
|
+
var el = querySelectorResolved(iframeDoc, entry.selector);
|
|
1900
|
+
if (!el) return;
|
|
1901
|
+
selectElement(el);
|
|
1902
|
+
try {
|
|
1903
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
1904
|
+
} catch(_) {
|
|
1905
|
+
el.scrollIntoView();
|
|
1906
|
+
}
|
|
1907
|
+
} catch(_) {}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
function removeHistoryChangeset(idx, evt) {
|
|
1911
|
+
if (evt && evt.stopPropagation) evt.stopPropagation();
|
|
1734
1912
|
var v = getActiveVariationForHistory();
|
|
1735
1913
|
if (!v) return;
|
|
1736
1914
|
var arr = parseVariationChangesets(v);
|
|
@@ -1738,6 +1916,16 @@ function removeHistoryChangeset(idx) {
|
|
|
1738
1916
|
var removed = arr[idx];
|
|
1739
1917
|
arr.splice(idx, 1);
|
|
1740
1918
|
persistActiveVariationChangesets(arr);
|
|
1919
|
+
if (isStyleOnlyChangesetEntry(removed)) {
|
|
1920
|
+
try {
|
|
1921
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1922
|
+
saveCurrentVariationHtml();
|
|
1923
|
+
} catch(_) {}
|
|
1924
|
+
if (currentMainTab === 'history') renderHistoryTab();
|
|
1925
|
+
recomputeEditorDirty();
|
|
1926
|
+
scheduleDomTreeRefresh();
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1741
1929
|
var didReload = revertChangesetEntryOnDom(removed);
|
|
1742
1930
|
try {
|
|
1743
1931
|
delete varHtmlCache[activeVarId];
|
|
@@ -1745,15 +1933,19 @@ function removeHistoryChangeset(idx) {
|
|
|
1745
1933
|
// Re-applying remaining rows on top of current DOM duplicates insert/reorder nodes; reload when any
|
|
1746
1934
|
// structural row remains or was removed (revert may already have started a reload for insert/body).
|
|
1747
1935
|
var removedType = normalizeChangesetType(removed);
|
|
1748
|
-
var
|
|
1749
|
-
|
|
1750
|
-
(removedType === 'insert' ||
|
|
1751
|
-
removedType === 'reorder' ||
|
|
1752
|
-
changesetListHasStructural(arr));
|
|
1936
|
+
var hasStructuralRemaining = changesetListHasStructural(arr);
|
|
1937
|
+
var removedIsStructural = removedType === 'insert' || removedType === 'reorder';
|
|
1753
1938
|
if (didReload) {
|
|
1754
1939
|
/* revertChangesetEntryOnDom already kicked off iframe reload */
|
|
1755
|
-
} else if (
|
|
1940
|
+
} else if (removedIsStructural) {
|
|
1756
1941
|
softReloadEditorIframe();
|
|
1942
|
+
} else if (hasStructuralRemaining) {
|
|
1943
|
+
// Keep current DOM state (already reverted for removed row) and only refresh style layer.
|
|
1944
|
+
// Avoid full reload and avoid re-applying all rows, which can duplicate structural insert/reorder entries.
|
|
1945
|
+
try {
|
|
1946
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
1947
|
+
saveCurrentVariationHtml();
|
|
1948
|
+
} catch(_) {}
|
|
1757
1949
|
} else {
|
|
1758
1950
|
try {
|
|
1759
1951
|
appliedStructuralChangesetKeys = {};
|
|
@@ -2017,6 +2209,27 @@ function bodyHasFirstPaintChild(body) {
|
|
|
2017
2209
|
return false;
|
|
2018
2210
|
}
|
|
2019
2211
|
|
|
2212
|
+
function computeIframeDomSignature(doc) {
|
|
2213
|
+
if (!doc || !doc.body) return '';
|
|
2214
|
+
var body = doc.body;
|
|
2215
|
+
var tags = [];
|
|
2216
|
+
var walker = null;
|
|
2217
|
+
try {
|
|
2218
|
+
walker = doc.createTreeWalker(body, NodeFilter.SHOW_ELEMENT, null);
|
|
2219
|
+
} catch(_) {
|
|
2220
|
+
walker = null;
|
|
2221
|
+
}
|
|
2222
|
+
var count = 0;
|
|
2223
|
+
if (walker) {
|
|
2224
|
+
while (walker.nextNode() && count < 400) {
|
|
2225
|
+
var node = walker.currentNode;
|
|
2226
|
+
tags.push((node && node.tagName) ? String(node.tagName).toLowerCase() : '');
|
|
2227
|
+
count += 1;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
return String(body.children ? body.children.length : 0) + '|' + String(count) + '|' + tags.join(',');
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2020
2233
|
/** True when at least one granular changeset selector already matches (nested content painted). */
|
|
2021
2234
|
function granularAnySelectorMatches(doc, cs) {
|
|
2022
2235
|
if (!doc || !cs || !cs.length) return false;
|
|
@@ -2121,7 +2334,7 @@ function runConsistencyReconcile() {
|
|
|
2121
2334
|
if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
|
|
2122
2335
|
var granular = filterGranularChangesetEntries(cs);
|
|
2123
2336
|
var unresolved = countUnresolvedGranularSelectors(doc, granular);
|
|
2124
|
-
if (unresolved > 0 ||
|
|
2337
|
+
if (unresolved > 0 || hasUnappliedStructuralChangesets(cs)) {
|
|
2125
2338
|
reapplyActiveVariationGranular(doc);
|
|
2126
2339
|
registerPendingGranularChangesets(cs, doc);
|
|
2127
2340
|
}
|
|
@@ -2171,6 +2384,7 @@ function resetIframeBindings() {
|
|
|
2171
2384
|
appliedStructuralChangesetKeys = {};
|
|
2172
2385
|
clickAttachDoc = null;
|
|
2173
2386
|
dragAttachDoc = null;
|
|
2387
|
+
hoverAttachDoc = null;
|
|
2174
2388
|
changeObserverDoc = null;
|
|
2175
2389
|
clearPendingGranularChangesets();
|
|
2176
2390
|
if (changeObserver) {
|
|
@@ -2199,7 +2413,7 @@ function loadPage(proxyUrl) {
|
|
|
2199
2413
|
iframe.style.display = 'block';
|
|
2200
2414
|
setIframePageLoadingUi(true);
|
|
2201
2415
|
iframe.src = appendIframeReloadBust(proxyUrl);
|
|
2202
|
-
startIframeContentApplyWatcher(navGen);
|
|
2416
|
+
startIframeContentApplyWatcher(navGen, iframe.contentDocument || null);
|
|
2203
2417
|
scheduleDomTreeRefresh();
|
|
2204
2418
|
}
|
|
2205
2419
|
|
|
@@ -2271,7 +2485,7 @@ function switchVariation(varId) {
|
|
|
2271
2485
|
iframe.src = appendIframeReloadBust(src);
|
|
2272
2486
|
// Do not sync here: the document is still the previous navigation until the
|
|
2273
2487
|
// iframe load event; an eager sync attached observers / DOM tree to the wrong document.
|
|
2274
|
-
startIframeContentApplyWatcher(navGen);
|
|
2488
|
+
startIframeContentApplyWatcher(navGen, iframe.contentDocument || null);
|
|
2275
2489
|
scheduleDomTreeRefresh();
|
|
2276
2490
|
}
|
|
2277
2491
|
} catch(_) {}
|
|
@@ -2491,24 +2705,136 @@ function flushPendingGranularChangesets() {
|
|
|
2491
2705
|
function structuralChangesetDedupKey(entry) {
|
|
2492
2706
|
var nt = normalizeChangesetType(entry);
|
|
2493
2707
|
if (!entry || (nt !== 'insert' && nt !== 'reorder')) return '';
|
|
2708
|
+
var SEP = '__vve_sep__';
|
|
2494
2709
|
var vid = activeVarId || '';
|
|
2495
2710
|
try {
|
|
2496
2711
|
return (
|
|
2497
2712
|
vid +
|
|
2498
|
-
|
|
2713
|
+
SEP +
|
|
2499
2714
|
nt +
|
|
2500
|
-
|
|
2715
|
+
SEP +
|
|
2501
2716
|
entry.selector +
|
|
2502
|
-
|
|
2717
|
+
SEP +
|
|
2503
2718
|
String(entry.action || '') +
|
|
2504
|
-
|
|
2719
|
+
SEP +
|
|
2505
2720
|
String(entry.html != null ? entry.html : '').slice(0, 240) +
|
|
2506
|
-
|
|
2721
|
+
SEP +
|
|
2507
2722
|
String(entry.targetSelector || '')
|
|
2508
2723
|
);
|
|
2509
2724
|
} catch(_) {
|
|
2510
|
-
return vid +
|
|
2725
|
+
return vid + SEP + nt + SEP + entry.selector;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
function hasUnappliedStructuralChangesets(cs) {
|
|
2730
|
+
if (!cs || !cs.length) return false;
|
|
2731
|
+
for (var i = 0; i < cs.length; i++) {
|
|
2732
|
+
var e = cs[i];
|
|
2733
|
+
var t = normalizeChangesetType(e);
|
|
2734
|
+
if (t !== 'insert' && t !== 'reorder') continue;
|
|
2735
|
+
var k = structuralChangesetDedupKey(e);
|
|
2736
|
+
if (!k) return true;
|
|
2737
|
+
if (!appliedStructuralChangesetKeys[k]) return true;
|
|
2738
|
+
}
|
|
2739
|
+
return false;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
function parseInlineStyleDeclarations(styleText) {
|
|
2743
|
+
var out = [];
|
|
2744
|
+
if (styleText == null) return out;
|
|
2745
|
+
var s = String(styleText);
|
|
2746
|
+
if (!s.trim()) return out;
|
|
2747
|
+
var parts = s.split(';');
|
|
2748
|
+
for (var i = 0; i < parts.length; i++) {
|
|
2749
|
+
var seg = parts[i];
|
|
2750
|
+
if (!seg) continue;
|
|
2751
|
+
var idx = seg.indexOf(':');
|
|
2752
|
+
if (idx <= 0) continue;
|
|
2753
|
+
var prop = seg.slice(0, idx).trim();
|
|
2754
|
+
var value = seg.slice(idx + 1).trim();
|
|
2755
|
+
if (!prop || !value) continue;
|
|
2756
|
+
out.push({ prop: prop, value: value });
|
|
2757
|
+
}
|
|
2758
|
+
return out;
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
function buildPersistentStyleRulesForActiveVariation() {
|
|
2762
|
+
if (!activeVarId) return '';
|
|
2763
|
+
var v = variations.find(function(x) { return x && x._id === activeVarId; });
|
|
2764
|
+
var parsed = parseVariationChangesets(v);
|
|
2765
|
+
var map = {};
|
|
2766
|
+
var order = [];
|
|
2767
|
+
function put(selector, prop, value) {
|
|
2768
|
+
if (!selector || !prop) return;
|
|
2769
|
+
if (value == null || value === '') return;
|
|
2770
|
+
var sel = sanitizeSelectorForMatch(String(selector)) || String(selector);
|
|
2771
|
+
var pr = String(prop).trim();
|
|
2772
|
+
var val = String(value).trim();
|
|
2773
|
+
if (!sel || !pr || !val) return;
|
|
2774
|
+
var k = sel + '__vve_sep__' + pr;
|
|
2775
|
+
if (!map[k]) order.push(k);
|
|
2776
|
+
map[k] = { selector: sel, property: pr, value: val };
|
|
2777
|
+
}
|
|
2778
|
+
for (var i = 0; i < parsed.length; i++) {
|
|
2779
|
+
var e = parsed[i];
|
|
2780
|
+
if (!e) continue;
|
|
2781
|
+
var t = normalizeChangesetType(e);
|
|
2782
|
+
if (t === 'style') {
|
|
2783
|
+
put(e.selector, e.property || e.cssProp, e.value);
|
|
2784
|
+
continue;
|
|
2785
|
+
}
|
|
2786
|
+
if (t === 'attribute' && String(e.attribute || '').toLowerCase() === 'style') {
|
|
2787
|
+
var decls = parseInlineStyleDeclarations(e.value);
|
|
2788
|
+
for (var di = 0; di < decls.length; di++) {
|
|
2789
|
+
put(e.selector, decls[di].prop, decls[di].value);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2511
2792
|
}
|
|
2793
|
+
for (var j = 0; j < stateChanges.length; j++) {
|
|
2794
|
+
var c = stateChanges[j];
|
|
2795
|
+
if (!c) continue;
|
|
2796
|
+
if (c.cssProp) {
|
|
2797
|
+
put(c.selector, c.cssProp, c.value);
|
|
2798
|
+
continue;
|
|
2799
|
+
}
|
|
2800
|
+
if (c.inputId === 'pp-css') {
|
|
2801
|
+
var liveDecls = parseInlineStyleDeclarations(c.value);
|
|
2802
|
+
for (var ldi = 0; ldi < liveDecls.length; ldi++) {
|
|
2803
|
+
put(c.selector, liveDecls[ldi].prop, liveDecls[ldi].value);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
var lines = [];
|
|
2808
|
+
for (var oi = 0; oi < order.length; oi++) {
|
|
2809
|
+
var row = map[order[oi]];
|
|
2810
|
+
lines.push(row.selector + ' { ' + row.property + ': ' + row.value + ' !important; }');
|
|
2811
|
+
}
|
|
2812
|
+
return lines.join('\\n');
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
function upsertPersistentChangesetStyleTag(iframeDoc, rulesText) {
|
|
2816
|
+
if (!iframeDoc) return;
|
|
2817
|
+
var STYLE_ID = '__vve_persist_changesets_style__';
|
|
2818
|
+
var prev = iframeDoc.getElementById(STYLE_ID);
|
|
2819
|
+
if (!rulesText) {
|
|
2820
|
+
if (prev && prev.parentNode) prev.parentNode.removeChild(prev);
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
var head = iframeDoc.head || iframeDoc.getElementsByTagName('head')[0];
|
|
2824
|
+
if (!head) return;
|
|
2825
|
+
var styleEl = prev || iframeDoc.createElement('style');
|
|
2826
|
+
styleEl.id = STYLE_ID;
|
|
2827
|
+
if (styleEl.textContent !== rulesText) styleEl.textContent = rulesText;
|
|
2828
|
+
if (!styleEl.parentNode) head.appendChild(styleEl);
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
function refreshPersistentChangesetStyleTagForActiveVariation() {
|
|
2832
|
+
try {
|
|
2833
|
+
var iframe = document.getElementById('iframeId');
|
|
2834
|
+
var iframeDoc = iframe && iframe.contentDocument;
|
|
2835
|
+
if (!iframeDoc) return;
|
|
2836
|
+
upsertPersistentChangesetStyleTag(iframeDoc, buildPersistentStyleRulesForActiveVariation());
|
|
2837
|
+
} catch(_) {}
|
|
2512
2838
|
}
|
|
2513
2839
|
|
|
2514
2840
|
/**
|
|
@@ -2545,23 +2871,11 @@ function applyChangesetEntry(entry, iframeDoc) {
|
|
|
2545
2871
|
else if (entry.value != null) el.textContent = entry.value;
|
|
2546
2872
|
break;
|
|
2547
2873
|
case 'style':
|
|
2548
|
-
|
|
2549
|
-
var propKebab = entry.property;
|
|
2550
|
-
var cam = camelize(propKebab);
|
|
2551
|
-
if (entry.value == null || entry.value === '') {
|
|
2552
|
-
try { el.style.removeProperty(propKebab); } catch(_) {}
|
|
2553
|
-
try { if (cam in el.style) el.style[cam] = ''; } catch(__) {}
|
|
2554
|
-
} else {
|
|
2555
|
-
try {
|
|
2556
|
-
el.style.setProperty(propKebab, entry.value, 'important');
|
|
2557
|
-
} catch(_) {
|
|
2558
|
-
el.style[cam] = entry.value;
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2874
|
+
// Style changes are applied via persistent stylesheet injection.
|
|
2562
2875
|
break;
|
|
2563
2876
|
case 'attribute':
|
|
2564
2877
|
if (entry.attribute && entry.value != null) {
|
|
2878
|
+
if (String(entry.attribute).toLowerCase() === 'style') break;
|
|
2565
2879
|
el.setAttribute(entry.attribute, entry.value);
|
|
2566
2880
|
}
|
|
2567
2881
|
break;
|
|
@@ -2598,6 +2912,7 @@ function applyActiveVariationHtml() {
|
|
|
2598
2912
|
|
|
2599
2913
|
var variation = variations.find(function(v) { return v._id === activeVarId; });
|
|
2600
2914
|
var cs = parseVariationChangesets(variation);
|
|
2915
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2601
2916
|
|
|
2602
2917
|
beginSuppressIframeMutationDirty();
|
|
2603
2918
|
try {
|
|
@@ -2612,6 +2927,7 @@ function applyActiveVariationHtml() {
|
|
|
2612
2927
|
for (var i = 0; i < cs.length; i++) {
|
|
2613
2928
|
applyChangesetEntry(cs[i], iframeDoc);
|
|
2614
2929
|
}
|
|
2930
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2615
2931
|
// Selectors often miss on first paint (client-rendered sections). Retry via timer + mutations.
|
|
2616
2932
|
registerPendingGranularChangesets(cs, iframeDoc);
|
|
2617
2933
|
} finally {
|
|
@@ -2660,6 +2976,7 @@ function applyVariationGranularOnly(iframeDoc) {
|
|
|
2660
2976
|
for (var i = 0; i < cs.length; i++) {
|
|
2661
2977
|
applyChangesetEntry(cs[i], iframeDoc);
|
|
2662
2978
|
}
|
|
2979
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2663
2980
|
registerPendingGranularChangesets(cs, iframeDoc);
|
|
2664
2981
|
} finally {
|
|
2665
2982
|
endSuppressIframeMutationDirty();
|
|
@@ -2678,16 +2995,19 @@ function reapplyActiveVariationGranular(iframeDoc) {
|
|
|
2678
2995
|
for (var i = 0; i < cs.length; i++) {
|
|
2679
2996
|
applyChangesetEntry(cs[i], iframeDoc);
|
|
2680
2997
|
}
|
|
2998
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
2681
2999
|
} finally {
|
|
2682
3000
|
endSuppressIframeMutationDirty();
|
|
2683
3001
|
}
|
|
2684
3002
|
}
|
|
2685
3003
|
|
|
2686
3004
|
/** Poll iframe document during navigation; apply granular edits as soon as DOM can match selectors. */
|
|
2687
|
-
function startIframeContentApplyWatcher(navGen) {
|
|
3005
|
+
function startIframeContentApplyWatcher(navGen, prevDocRef) {
|
|
2688
3006
|
stopIframeContentApplyWatcher();
|
|
2689
3007
|
iframeEarlyGranularPrimedForGen = null;
|
|
2690
3008
|
iframeEarlySyncPrimedForGen = null;
|
|
3009
|
+
iframeEarlyDomSignature = '';
|
|
3010
|
+
iframeEarlyDomSignatureNavGen = navGen;
|
|
2691
3011
|
var iframe = document.getElementById('iframeId');
|
|
2692
3012
|
iframeContentApplyTimer = setInterval(function() {
|
|
2693
3013
|
if (navGen !== iframeContentNavGen) {
|
|
@@ -2697,6 +3017,7 @@ function startIframeContentApplyWatcher(navGen) {
|
|
|
2697
3017
|
try {
|
|
2698
3018
|
var doc = iframe.contentDocument;
|
|
2699
3019
|
if (!doc || !doc.body) return;
|
|
3020
|
+
if (prevDocRef && doc === prevDocRef) return;
|
|
2700
3021
|
var docUrl = '';
|
|
2701
3022
|
try { docUrl = String(doc.URL || ''); } catch(_) {}
|
|
2702
3023
|
if (docUrl === 'about:blank') return;
|
|
@@ -2731,6 +3052,7 @@ function startIframeContentApplyWatcher(navGen) {
|
|
|
2731
3052
|
}
|
|
2732
3053
|
if (iframeEarlySyncPrimedForGen !== navGen) {
|
|
2733
3054
|
iframeEarlySyncPrimedForGen = navGen;
|
|
3055
|
+
iframeEarlyDomSignature = computeIframeDomSignature(doc);
|
|
2734
3056
|
syncIframeInteractions('iframe-early-paint');
|
|
2735
3057
|
}
|
|
2736
3058
|
}
|
|
@@ -2800,6 +3122,8 @@ function injectIframeSelectionStyles(doc) {
|
|
|
2800
3122
|
st.textContent =
|
|
2801
3123
|
'.vve-selected{outline:2px solid #6366f1!important;outline-offset:2px!important;' +
|
|
2802
3124
|
'box-shadow:0 0 0 2px rgba(99,102,241,.28),inset 0 0 0 1px rgba(99,102,241,.18)!important;}' +
|
|
3125
|
+
'.vve-tree-hover{outline:2px solid #6366f1!important;outline-offset:2px!important;' +
|
|
3126
|
+
'box-shadow:0 0 0 2px rgba(99,102,241,.22),inset 0 0 0 1px rgba(99,102,241,.12)!important;}' +
|
|
2803
3127
|
'html.vve-drag-armed .vve-selected{cursor:grab!important;}' +
|
|
2804
3128
|
'.vve-dragging{opacity:0.92!important;outline:2px dashed #f59e0b!important;' +
|
|
2805
3129
|
'outline-offset:2px!important;cursor:grabbing!important;box-shadow:none!important;}';
|
|
@@ -2811,6 +3135,47 @@ function injectIframeSelectionStyles(doc) {
|
|
|
2811
3135
|
}
|
|
2812
3136
|
}
|
|
2813
3137
|
|
|
3138
|
+
function clearTreeHoverHighlight() {
|
|
3139
|
+
if (!hoveredTreeEl) return;
|
|
3140
|
+
beginSuppressIframeMutationDirty();
|
|
3141
|
+
try {
|
|
3142
|
+
if (hoveredTreeEl.classList) hoveredTreeEl.classList.remove('vve-tree-hover');
|
|
3143
|
+
} catch(_) {
|
|
3144
|
+
} finally {
|
|
3145
|
+
endSuppressIframeMutationDirty();
|
|
3146
|
+
hoveredTreeEl = null;
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
function setTreeHoverHighlight(el) {
|
|
3151
|
+
if (!el || el.nodeType !== 1) {
|
|
3152
|
+
clearTreeHoverHighlight();
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
if (hoveredTreeEl === el) return;
|
|
3156
|
+
clearTreeHoverHighlight();
|
|
3157
|
+
beginSuppressIframeMutationDirty();
|
|
3158
|
+
try {
|
|
3159
|
+
if (el.classList) el.classList.add('vve-tree-hover');
|
|
3160
|
+
hoveredTreeEl = el;
|
|
3161
|
+
} catch(_) {
|
|
3162
|
+
hoveredTreeEl = null;
|
|
3163
|
+
} finally {
|
|
3164
|
+
endSuppressIframeMutationDirty();
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
function isTreeHoverOnlyClassMutation(mutation) {
|
|
3169
|
+
if (!mutation || mutation.type !== 'attributes' || mutation.attributeName !== 'class') return false;
|
|
3170
|
+
var oldClass = String(mutation.oldValue || '');
|
|
3171
|
+
var target = mutation.target;
|
|
3172
|
+
var nextClass = '';
|
|
3173
|
+
try {
|
|
3174
|
+
nextClass = target && typeof target.className === 'string' ? target.className : '';
|
|
3175
|
+
} catch(_) {}
|
|
3176
|
+
return oldClass.indexOf('vve-tree-hover') >= 0 || String(nextClass).indexOf('vve-tree-hover') >= 0;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
2814
3179
|
function setDragHandleActive(on) {
|
|
2815
3180
|
dragHandleActive = !!on;
|
|
2816
3181
|
var b = document.getElementById('sf-drag');
|
|
@@ -3131,6 +3496,9 @@ function renderDomTree(filterRaw) {
|
|
|
3131
3496
|
if (e.target.closest && e.target.closest('.dt-chev')) return;
|
|
3132
3497
|
selectElementFromTree(el);
|
|
3133
3498
|
};
|
|
3499
|
+
row.onmouseenter = function() {
|
|
3500
|
+
setTreeHoverHighlight(el);
|
|
3501
|
+
};
|
|
3134
3502
|
root.appendChild(row);
|
|
3135
3503
|
|
|
3136
3504
|
if (!hasKids || collapsed) return;
|
|
@@ -3149,6 +3517,9 @@ function renderDomTree(filterRaw) {
|
|
|
3149
3517
|
? '<div class="dt-muted">No elements match your search.</div>'
|
|
3150
3518
|
: '<div class="dt-muted">No visible elements yet.</div>';
|
|
3151
3519
|
}
|
|
3520
|
+
root.onmouseleave = function() {
|
|
3521
|
+
clearTreeHoverHighlight();
|
|
3522
|
+
};
|
|
3152
3523
|
}
|
|
3153
3524
|
|
|
3154
3525
|
// \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
|
|
@@ -3166,6 +3537,40 @@ function pr2(l1, i1, l2, i2) {
|
|
|
3166
3537
|
'<div class="pr2-item"><div class="pr2-lbl">'+l2+'</div>'+i2+'</div></div>';
|
|
3167
3538
|
}
|
|
3168
3539
|
function subLbl(text) { return '<div class="sub-lbl">'+text+'</div>'; }
|
|
3540
|
+
function openCustomCssModal() {
|
|
3541
|
+
var modal = document.getElementById('custom-css-modal');
|
|
3542
|
+
var ta = document.getElementById('custom-css-modal-textarea');
|
|
3543
|
+
if (!modal || !ta) return;
|
|
3544
|
+
var inp = document.getElementById('pp-css');
|
|
3545
|
+
var v = inp ? inp.value : (selectedEl && selectedEl.getAttribute ? (selectedEl.getAttribute('style') || '') : '');
|
|
3546
|
+
ta.value = v || '';
|
|
3547
|
+
modal.classList.add('open');
|
|
3548
|
+
modal.setAttribute('aria-hidden', 'false');
|
|
3549
|
+
setTimeout(function() {
|
|
3550
|
+
try { ta.focus(); ta.setSelectionRange(ta.value.length, ta.value.length); } catch(_) {}
|
|
3551
|
+
}, 0);
|
|
3552
|
+
}
|
|
3553
|
+
function closeCustomCssModal() {
|
|
3554
|
+
var modal = document.getElementById('custom-css-modal');
|
|
3555
|
+
if (!modal) return;
|
|
3556
|
+
modal.classList.remove('open');
|
|
3557
|
+
modal.setAttribute('aria-hidden', 'true');
|
|
3558
|
+
}
|
|
3559
|
+
function applyCustomCssModal() {
|
|
3560
|
+
var ta = document.getElementById('custom-css-modal-textarea');
|
|
3561
|
+
var inp = document.getElementById('pp-css');
|
|
3562
|
+
if (!ta || !inp) {
|
|
3563
|
+
closeCustomCssModal();
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
inp.value = ta.value || '';
|
|
3567
|
+
try {
|
|
3568
|
+
inp.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3569
|
+
} catch(_) {
|
|
3570
|
+
if (typeof inp.oninput === 'function') inp.oninput();
|
|
3571
|
+
}
|
|
3572
|
+
closeCustomCssModal();
|
|
3573
|
+
}
|
|
3169
3574
|
function weightOpts(cur) {
|
|
3170
3575
|
return [['100','Thin'],['200','Extra Light'],['300','Light'],['400','Normal'],['500','Medium'],
|
|
3171
3576
|
['600','Semi Bold'],['700','Bold'],['800','Extra Bold'],['900','Black']]
|
|
@@ -3446,7 +3851,12 @@ function renderRightPanel(el) {
|
|
|
3446
3851
|
// \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
|
|
3447
3852
|
document.getElementById('acc-body-css').innerHTML =
|
|
3448
3853
|
pr('Classes', '<input class="pr-inp" id="pp-cls" type="text" value="'+esc(el.className||'')+'" placeholder="class1 class2">') +
|
|
3449
|
-
|
|
3854
|
+
'<div class="sub-lbl-row">' +
|
|
3855
|
+
'<div class="sub-lbl">Custom CSS</div>' +
|
|
3856
|
+
'<button type="button" class="css-expand-btn" title="Open full-screen editor" onclick="openCustomCssModal()">' +
|
|
3857
|
+
'<i class="bi bi-fullscreen"></i>' +
|
|
3858
|
+
'</button>' +
|
|
3859
|
+
'</div>' +
|
|
3450
3860
|
'<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>';
|
|
3451
3861
|
|
|
3452
3862
|
// \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
|
|
@@ -3529,12 +3939,25 @@ function renderRightPanel(el) {
|
|
|
3529
3939
|
var sel = buildSelector(el);
|
|
3530
3940
|
bindings.forEach(function(b){
|
|
3531
3941
|
var inp = document.getElementById(b[0]);
|
|
3532
|
-
if (inp)
|
|
3942
|
+
if (inp) {
|
|
3943
|
+
var onValueChange = function() {
|
|
3533
3944
|
// Read the original value BEFORE applying the change so we can revert later
|
|
3534
3945
|
var orig = getOriginalValue(b[0], el);
|
|
3535
3946
|
b[1](inp.value);
|
|
3536
|
-
|
|
3537
|
-
|
|
3947
|
+
var valueToLog = inp.value;
|
|
3948
|
+
try {
|
|
3949
|
+
console.log('[V2] input changed', {
|
|
3950
|
+
inputId: b[0],
|
|
3951
|
+
rawValue: inp.value,
|
|
3952
|
+
appliedValue: valueToLog,
|
|
3953
|
+
selector: sel,
|
|
3954
|
+
});
|
|
3955
|
+
} catch(_) {}
|
|
3956
|
+
logChange(sel, b[0], valueToLog, el, orig);
|
|
3957
|
+
};
|
|
3958
|
+
inp.addEventListener('input', onValueChange);
|
|
3959
|
+
inp.addEventListener('change', onValueChange);
|
|
3960
|
+
}
|
|
3538
3961
|
});
|
|
3539
3962
|
}
|
|
3540
3963
|
|
|
@@ -3797,6 +4220,32 @@ function attachClickHandler() {
|
|
|
3797
4220
|
} catch(_) {}
|
|
3798
4221
|
}
|
|
3799
4222
|
|
|
4223
|
+
function attachIframeHoverHandler() {
|
|
4224
|
+
try {
|
|
4225
|
+
var iframe = document.getElementById('iframeId');
|
|
4226
|
+
var doc = iframe && iframe.contentDocument;
|
|
4227
|
+
if (!doc || !doc.body) return;
|
|
4228
|
+
if (hoverAttachDoc === doc) return;
|
|
4229
|
+
hoverAttachDoc = doc;
|
|
4230
|
+
doc.addEventListener('mousemove', function(e) {
|
|
4231
|
+
if (currentMode !== 'editor') {
|
|
4232
|
+
clearTreeHoverHighlight();
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
var target = e.target;
|
|
4236
|
+
if (!target || target === doc.body || target === doc.documentElement) {
|
|
4237
|
+
clearTreeHoverHighlight();
|
|
4238
|
+
return;
|
|
4239
|
+
}
|
|
4240
|
+
setTreeHoverHighlight(target);
|
|
4241
|
+
}, true);
|
|
4242
|
+
doc.addEventListener('mouseout', function(e) {
|
|
4243
|
+
if (e.relatedTarget) return;
|
|
4244
|
+
clearTreeHoverHighlight();
|
|
4245
|
+
}, true);
|
|
4246
|
+
} catch(_) {}
|
|
4247
|
+
}
|
|
4248
|
+
|
|
3800
4249
|
function attachChangeObserver() {
|
|
3801
4250
|
try {
|
|
3802
4251
|
var iframe = document.getElementById('iframeId');
|
|
@@ -3809,27 +4258,15 @@ function attachChangeObserver() {
|
|
|
3809
4258
|
changeObserverDoc = null;
|
|
3810
4259
|
}
|
|
3811
4260
|
changeObserver = new MutationObserver(function(mutations) {
|
|
3812
|
-
|
|
3813
|
-
var bodyReplaced = false;
|
|
4261
|
+
var hasMeaningfulMutation = false;
|
|
3814
4262
|
for (var mi = 0; mi < mutations.length; mi++) {
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
m &&
|
|
3818
|
-
m.type === 'childList' &&
|
|
3819
|
-
m.target === doc.body &&
|
|
3820
|
-
m.addedNodes &&
|
|
3821
|
-
m.removedNodes &&
|
|
3822
|
-
m.addedNodes.length > 0 &&
|
|
3823
|
-
m.removedNodes.length > 0
|
|
3824
|
-
) {
|
|
3825
|
-
bodyReplaced = true;
|
|
4263
|
+
if (!isTreeHoverOnlyClassMutation(mutations[mi])) {
|
|
4264
|
+
hasMeaningfulMutation = true;
|
|
3826
4265
|
break;
|
|
3827
4266
|
}
|
|
3828
4267
|
}
|
|
3829
|
-
if (
|
|
3830
|
-
|
|
3831
|
-
appliedStructuralChangesetKeys = {};
|
|
3832
|
-
}
|
|
4268
|
+
if (!hasMeaningfulMutation) return;
|
|
4269
|
+
// Dirty state is derived from changesets baseline + stateChanges (not raw DOM mutations).
|
|
3833
4270
|
// Host scripts can replace selected nodes every few frames (e.g. A/B tool observers).
|
|
3834
4271
|
// Keep selection sticky by re-resolving from fingerprint.
|
|
3835
4272
|
recoverSelectedElement(false);
|
|
@@ -3839,7 +4276,7 @@ function attachChangeObserver() {
|
|
|
3839
4276
|
updateSelectionToolbar();
|
|
3840
4277
|
});
|
|
3841
4278
|
changeObserver.observe(doc.body, {
|
|
3842
|
-
childList: true, subtree: true, attributes: true, characterData: true
|
|
4279
|
+
childList: true, subtree: true, attributes: true, characterData: true, attributeOldValue: true
|
|
3843
4280
|
});
|
|
3844
4281
|
changeObserverDoc = doc;
|
|
3845
4282
|
} catch(_) {}
|
|
@@ -3863,7 +4300,9 @@ function syncIframeInteractions(reason) {
|
|
|
3863
4300
|
}
|
|
3864
4301
|
showNoUrl(false);
|
|
3865
4302
|
injectIframeSelectionStyles(doc);
|
|
4303
|
+
refreshPersistentChangesetStyleTagForActiveVariation();
|
|
3866
4304
|
attachClickHandler();
|
|
4305
|
+
attachIframeHoverHandler();
|
|
3867
4306
|
attachDragReposition();
|
|
3868
4307
|
attachChangeObserver();
|
|
3869
4308
|
startConsistencyWatchdog(doc);
|
|
@@ -4222,10 +4661,9 @@ window.addEventListener('load', function() {
|
|
|
4222
4661
|
var iframe = document.getElementById('iframeId');
|
|
4223
4662
|
iframe.addEventListener('load', function() {
|
|
4224
4663
|
if (!iframe.src || iframe.src === 'about:blank' || iframe.src === window.location.href) return;
|
|
4225
|
-
// New iframe navigation: always drop bindings tied to the previous document.
|
|
4226
|
-
resetIframeBindings();
|
|
4227
4664
|
var doc = iframe.contentDocument;
|
|
4228
4665
|
if (!doc) {
|
|
4666
|
+
resetIframeBindings();
|
|
4229
4667
|
syncIframeInteractions('iframe-load-no-doc');
|
|
4230
4668
|
return;
|
|
4231
4669
|
}
|
|
@@ -4234,14 +4672,32 @@ window.addEventListener('load', function() {
|
|
|
4234
4672
|
// Stale events: src may already be the proxy URL while the document is still
|
|
4235
4673
|
// about:blank (e.g. src cleared then reset to force reload). Ask sync path to retry.
|
|
4236
4674
|
if (docUrl === 'about:blank') {
|
|
4675
|
+
resetIframeBindings();
|
|
4237
4676
|
syncIframeInteractions('iframe-load-about-blank');
|
|
4238
4677
|
return;
|
|
4239
4678
|
}
|
|
4679
|
+
// If early-paint and final load DOM signatures match, avoid a second full apply/reset
|
|
4680
|
+
// that steals focus from sidebar controls while the page is still stabilizing.
|
|
4681
|
+
var shouldRefreshOnFinalLoad = true;
|
|
4682
|
+
if (
|
|
4683
|
+
iframeEarlyDomSignatureNavGen === iframeContentNavGen &&
|
|
4684
|
+
iframeEarlySyncPrimedForGen === iframeContentNavGen &&
|
|
4685
|
+
iframeEarlyDomSignature
|
|
4686
|
+
) {
|
|
4687
|
+
var finalDomSignature = computeIframeDomSignature(doc);
|
|
4688
|
+
if (finalDomSignature && finalDomSignature === iframeEarlyDomSignature) {
|
|
4689
|
+
shouldRefreshOnFinalLoad = false;
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
if (clickAttachDoc !== doc || dragAttachDoc !== doc || changeObserverDoc !== doc || hoverAttachDoc !== doc) {
|
|
4693
|
+
resetIframeBindings();
|
|
4694
|
+
}
|
|
4240
4695
|
attachIframeLoadingUntilComplete(iframe);
|
|
4241
4696
|
if (doc.body && iframeDocMatchesNavigatedSrc(iframe, doc)) {
|
|
4242
4697
|
stopIframeContentApplyWatcher();
|
|
4243
|
-
|
|
4244
|
-
|
|
4698
|
+
if (shouldRefreshOnFinalLoad) {
|
|
4699
|
+
applyActiveVariationHtml();
|
|
4700
|
+
}
|
|
4245
4701
|
}
|
|
4246
4702
|
// Always attempt sync; it has its own readiness checks + retry loop.
|
|
4247
4703
|
syncIframeInteractions('iframe-load');
|
|
@@ -4583,9 +5039,12 @@ function createVisualEditorMiddleware(options) {
|
|
|
4583
5039
|
}
|
|
4584
5040
|
);
|
|
4585
5041
|
if (html.includes("</head>")) {
|
|
4586
|
-
html = html.replace(
|
|
4587
|
-
|
|
4588
|
-
|
|
5042
|
+
html = html.replace(
|
|
5043
|
+
"</head>",
|
|
5044
|
+
`${iframeAlwaysShowCss}
|
|
5045
|
+
${iframeAlwaysShowCssGuardScript}
|
|
5046
|
+
</head>`
|
|
5047
|
+
);
|
|
4589
5048
|
}
|
|
4590
5049
|
html = html.replace(
|
|
4591
5050
|
/<meta[^>]+http-equiv=["']?\s*(x-frame-options|content-security-policy)\s*["']?[^>]*>/gi,
|