@accelerated-agency/visual-editor 0.5.6 → 0.5.8
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 +3 -1
- package/dist/vite.cjs +540 -60
- package/dist/vite.js +540 -61
- package/package.json +7 -2
package/dist/vite.js
CHANGED
|
@@ -407,7 +407,7 @@ html,body{height:100%;overflow:hidden;font-family:var(--font-sans);font-size:13p
|
|
|
407
407
|
overflow:visible
|
|
408
408
|
}
|
|
409
409
|
/* Toolbar layout sections */
|
|
410
|
-
.tb-left{display:flex;align-items:center;gap:0;flex-
|
|
410
|
+
.tb-left{display:flex;align-items:center;gap:0;flex:1;min-width:0;max-width:min(560px,46vw);margin-right:8px}
|
|
411
411
|
.tb-center{display:flex;align-items:center;gap:4px;flex:1;justify-content:center}
|
|
412
412
|
.tb-right{display:flex;align-items:center;gap:6px;flex-shrink:0;margin-left:auto}
|
|
413
413
|
/* Logo */
|
|
@@ -429,7 +429,7 @@ html,body{height:100%;overflow:hidden;font-family:var(--font-sans);font-size:13p
|
|
|
429
429
|
/* Device toggle buttons */
|
|
430
430
|
.tb-dev-wrap{position:relative;display:flex;align-items:center}
|
|
431
431
|
.tb-dev-btns{display:flex;align-items:center;gap:1px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width: 116px;}
|
|
432
|
-
.tb-dev-3btns{display:flex;align-items:center;gap:2px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width:
|
|
432
|
+
.tb-dev-3btns{display:flex;align-items:center;gap:2px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width: auto;}
|
|
433
433
|
.tb-dev-menu{
|
|
434
434
|
position:absolute;top:calc(100% + 8px);right:0;z-index:12040;display:none;
|
|
435
435
|
width:264px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:4px 4px;
|
|
@@ -533,7 +533,7 @@ box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.20), 0 1px 2px 0 rgba(0, 0, 0, 0.05), 0 1p
|
|
|
533
533
|
}
|
|
534
534
|
.tb-dev-menu .vp-preset-btn:hover,.tb-dev-menu .vp-preset-btn.active{background:#f4f4f5}
|
|
535
535
|
/* Dark icon buttons */
|
|
536
|
-
.tb-dk-btn{width:28px;height:
|
|
536
|
+
.tb-dk-btn{width:28px;height:25px;background:transparent;border:none;border-radius:5px;cursor:pointer;color:#71717a;display:flex;align-items:center;justify-content:center;font-size:13px;transition:all .12s;flex-shrink:0}
|
|
537
537
|
.tb-dk-btn:hover{color:#e4e4e7;background:rgba(255,255,255,.07)}
|
|
538
538
|
.tb-dk-btn.active{background:#FFFFFF}
|
|
539
539
|
/* Dark separator */
|
|
@@ -700,6 +700,15 @@ box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.20), 0 1px 2px 0 rgba(0, 0, 0, 0.05), 0 1p
|
|
|
700
700
|
background: var(--bg-primary-default, #262626)!important;
|
|
701
701
|
color: var(--text-primary-default, #FFF)!important;
|
|
702
702
|
}
|
|
703
|
+
#dom-tree-root .dt-row[draggable="true"]{cursor:grab}
|
|
704
|
+
#dom-tree-root .dt-row.dt-dragging{opacity:.55;cursor:grabbing}
|
|
705
|
+
#dom-tree-root .dt-row.dt-drop-before::after,
|
|
706
|
+
#dom-tree-root .dt-row.dt-drop-after::after{
|
|
707
|
+
content:'';position:absolute;left:8px;right:8px;height:2px;background:#6366f1;pointer-events:none;z-index:1
|
|
708
|
+
}
|
|
709
|
+
#dom-tree-root .dt-row.dt-drop-before::after{top:0}
|
|
710
|
+
#dom-tree-root .dt-row.dt-drop-after::after{bottom:0}
|
|
711
|
+
#dom-tree-root .dt-row.dt-drop-invalid{cursor:not-allowed;opacity:.45}
|
|
703
712
|
.dt-chev{
|
|
704
713
|
width:16px;height:16px;flex-shrink:0;border:none;background:transparent;
|
|
705
714
|
cursor:pointer;color:var(--text-3);display:flex;align-items:center;justify-content:center;
|
|
@@ -748,6 +757,21 @@ flex: 1;
|
|
|
748
757
|
}
|
|
749
758
|
#no-url .nu-icon{font-size:48px;opacity:.3}
|
|
750
759
|
|
|
760
|
+
/* \u2500\u2500 Iframe load error overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
761
|
+
#iframe-load-error{
|
|
762
|
+
display:none;position:absolute;inset:0;z-index:50;flex-direction:column;
|
|
763
|
+
align-items:center;justify-content:center;gap:12px;text-align:center;padding:40px;
|
|
764
|
+
background:rgba(232,236,240,.94);backdrop-filter:blur(4px)
|
|
765
|
+
}
|
|
766
|
+
#iframe-load-error .nu-icon{font-size:48px;color:#94a3b8}
|
|
767
|
+
#iframe-load-error .ile-title{font-weight:600;font-size:15px;color:var(--text-2)}
|
|
768
|
+
#iframe-load-error .ile-msg{font-size:12px;color:var(--text-3);max-width:360px;line-height:1.5;word-break:break-word}
|
|
769
|
+
#iframe-load-error .ile-reload-btn{
|
|
770
|
+
margin-top:4px;display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:6px;
|
|
771
|
+
border:1px solid var(--border);background:#fff;color:var(--text);font-size:13px;font-weight:500;cursor:pointer
|
|
772
|
+
}
|
|
773
|
+
#iframe-load-error .ile-reload-btn:hover{background:var(--bg-hover);border-color:#cbd5e1}
|
|
774
|
+
|
|
751
775
|
/* \u2500\u2500 Device frame \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
752
776
|
#device-frame{
|
|
753
777
|
width: 100%;
|
|
@@ -802,12 +826,32 @@ flex: 1;
|
|
|
802
826
|
|
|
803
827
|
/* \u2500\u2500 URL bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
804
828
|
#url-bar{
|
|
805
|
-
|
|
806
|
-
|
|
829
|
+
flex: 1;
|
|
830
|
+
max-width: 100%;
|
|
831
|
+
width: 100%;
|
|
832
|
+
border: 1px solid var(--border);
|
|
833
|
+
border-radius: 4px;
|
|
834
|
+
color: var(--text-2);
|
|
835
|
+
font-size: 12px;
|
|
836
|
+
padding: 8px 8px;
|
|
837
|
+
outline: none;
|
|
838
|
+
font-family: inherit;
|
|
839
|
+
overflow: hidden;
|
|
840
|
+
text-overflow: ellipsis;
|
|
807
841
|
}
|
|
842
|
+
#url-bar::placeholder{color:var(--text-3)}
|
|
843
|
+
#url-bar:focus{border-color:#94a3b8;color:var(--text);background:#fff}
|
|
844
|
+
#url-bar:disabled{opacity:.55;cursor:not-allowed}
|
|
808
845
|
|
|
809
846
|
/* \u2500\u2500 Left panel sections \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
810
|
-
.lp-sec{border-bottom:1px solid var(--border);
|
|
847
|
+
.lp-sec{ border-bottom: 1px solid var(--border);
|
|
848
|
+
flex-shrink: 0;
|
|
849
|
+
padding: 18px 16px;}
|
|
850
|
+
.lp-sec-hd-url{
|
|
851
|
+
border-bottom: 1px solid var(--border);
|
|
852
|
+
flex-shrink: 0;
|
|
853
|
+
padding: 5px 16px;
|
|
854
|
+
}
|
|
811
855
|
.lp-sec-no-border{border-bottom:none!important}
|
|
812
856
|
.lp-sec-hd{
|
|
813
857
|
margin-bottom: 12px;
|
|
@@ -1427,23 +1471,102 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1427
1471
|
max-width:420px;
|
|
1428
1472
|
line-height:1.5;
|
|
1429
1473
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
display:flex;
|
|
1437
|
-
}
|
|
1474
|
+
html.ve-editor-gated #app,
|
|
1475
|
+
html.ve-editor-gated #custom-css-modal{
|
|
1476
|
+
display:none!important;
|
|
1477
|
+
}
|
|
1478
|
+
html.ve-editor-gated #ve-min-screen-notice{
|
|
1479
|
+
display:flex;
|
|
1438
1480
|
}
|
|
1439
1481
|
</style>
|
|
1440
1482
|
</head>
|
|
1441
1483
|
<body class="mode-editor">
|
|
1442
1484
|
<div id="ve-min-screen-notice" aria-live="polite">
|
|
1443
1485
|
<div class="ve-min-screen-icon"><i class="bi bi-display"></i></div>
|
|
1444
|
-
<div class="ve-min-screen-title">Please switch to
|
|
1486
|
+
<div class="ve-min-screen-title">Please switch to a larger screen to use the Visual Editor</div>
|
|
1445
1487
|
<div class="ve-min-screen-desc">The Visual Editor requires a screen width of at least 1100px.</div>
|
|
1446
1488
|
</div>
|
|
1489
|
+
<script>(function(){
|
|
1490
|
+
var MIN_EDITOR_WIDTH=1100;
|
|
1491
|
+
var ZOOM_IN_THRESHOLD=1.05;
|
|
1492
|
+
var ZOOM_MATCH_TOLERANCE=0.06;
|
|
1493
|
+
var STANDARD_BROWSER_ZOOMS=[1.25,1.33,1.5,1.67,1.75,2,2.5,3,4];
|
|
1494
|
+
function markUserZoomInteraction(){
|
|
1495
|
+
try{sessionStorage.setItem('ve-user-zoomed','1');}catch(_){}
|
|
1496
|
+
}
|
|
1497
|
+
function hasUserZoomInteraction(){
|
|
1498
|
+
try{return sessionStorage.getItem('ve-user-zoomed')==='1';}catch(_){return false;}
|
|
1499
|
+
}
|
|
1500
|
+
function matchesStandardBrowserZoom(iw,screenW){
|
|
1501
|
+
if(!iw||!screenW)return false;
|
|
1502
|
+
for(var i=0;i<STANDARD_BROWSER_ZOOMS.length;i++){
|
|
1503
|
+
var z=STANDARD_BROWSER_ZOOMS[i];
|
|
1504
|
+
var impliedFull=iw*z;
|
|
1505
|
+
if(Math.abs(impliedFull-screenW)/screenW<=ZOOM_MATCH_TOLERANCE)return true;
|
|
1506
|
+
}
|
|
1507
|
+
return false;
|
|
1508
|
+
}
|
|
1509
|
+
function isBrowserZoomedIn(iw,screenW){
|
|
1510
|
+
try{
|
|
1511
|
+
if(window.visualViewport&&window.visualViewport.scale>ZOOM_IN_THRESHOLD)return true;
|
|
1512
|
+
}catch(_){}
|
|
1513
|
+
if(!screenW||screenW<MIN_EDITOR_WIDTH||iw>=MIN_EDITOR_WIDTH)return false;
|
|
1514
|
+
if(hasUserZoomInteraction())return true;
|
|
1515
|
+
return matchesStandardBrowserZoom(iw,screenW);
|
|
1516
|
+
}
|
|
1517
|
+
function updateMinScreenGate(){
|
|
1518
|
+
var iw=window.innerWidth||0;
|
|
1519
|
+
var gated=iw<MIN_EDITOR_WIDTH;
|
|
1520
|
+
document.documentElement.classList.toggle('ve-editor-gated',gated);
|
|
1521
|
+
if(!gated){
|
|
1522
|
+
try{sessionStorage.removeItem('ve-user-zoomed');}catch(_){}
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
var notice=document.getElementById('ve-min-screen-notice');
|
|
1526
|
+
if(!notice)return;
|
|
1527
|
+
var titleEl=notice.querySelector('.ve-min-screen-title');
|
|
1528
|
+
var descEl=notice.querySelector('.ve-min-screen-desc');
|
|
1529
|
+
var iconEl=notice.querySelector('.ve-min-screen-icon i');
|
|
1530
|
+
if(!titleEl||!descEl||!iconEl)return;
|
|
1531
|
+
var screenW=(window.screen&&(window.screen.availWidth||window.screen.width))||0;
|
|
1532
|
+
var smallScreen=screenW>0&&screenW<MIN_EDITOR_WIDTH;
|
|
1533
|
+
var zoomedIn=isBrowserZoomedIn(iw,screenW);
|
|
1534
|
+
if(smallScreen){
|
|
1535
|
+
iconEl.className='bi bi-display';
|
|
1536
|
+
titleEl.textContent='Please switch to a larger screen to use the Visual Editor';
|
|
1537
|
+
descEl.textContent='The Visual Editor requires a screen width of at least '+MIN_EDITOR_WIDTH+'px.';
|
|
1538
|
+
}else if(zoomedIn){
|
|
1539
|
+
iconEl.className='bi bi-zoom-out';
|
|
1540
|
+
titleEl.textContent='You have zoomed in too much';
|
|
1541
|
+
descEl.textContent='Zoom out to use the Visual Editor \u2014 press Cmd + - on Mac or Ctrl + - on Windows.';
|
|
1542
|
+
}else{
|
|
1543
|
+
iconEl.className='bi bi-arrows-angle-expand';
|
|
1544
|
+
titleEl.textContent='Widen your browser window';
|
|
1545
|
+
descEl.textContent='The Visual Editor needs at least '+MIN_EDITOR_WIDTH+'px of width. Maximize this window or resize it wider.';
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function onZoomShortcut(e){
|
|
1549
|
+
if(!(e.ctrlKey||e.metaKey))return;
|
|
1550
|
+
var k=e.key||'';
|
|
1551
|
+
if(k==='+'||k==='-'||k==='='||k==='0'||k==='_'||e.code==='Equal'||e.code==='Minus'||e.code==='NumpadAdd'||e.code==='NumpadSubtract'){
|
|
1552
|
+
markUserZoomInteraction();
|
|
1553
|
+
setTimeout(updateMinScreenGate,0);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
function onZoomWheel(e){
|
|
1557
|
+
if(!e.ctrlKey)return;
|
|
1558
|
+
markUserZoomInteraction();
|
|
1559
|
+
setTimeout(updateMinScreenGate,0);
|
|
1560
|
+
}
|
|
1561
|
+
document.addEventListener('keydown',onZoomShortcut,true);
|
|
1562
|
+
window.addEventListener('wheel',onZoomWheel,{passive:true,capture:true});
|
|
1563
|
+
updateMinScreenGate();
|
|
1564
|
+
window.addEventListener('resize',updateMinScreenGate);
|
|
1565
|
+
if(window.visualViewport){
|
|
1566
|
+
window.visualViewport.addEventListener('resize',updateMinScreenGate);
|
|
1567
|
+
window.visualViewport.addEventListener('scroll',updateMinScreenGate);
|
|
1568
|
+
}
|
|
1569
|
+
})();</script>
|
|
1447
1570
|
<div id="app">
|
|
1448
1571
|
<!-- \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->
|
|
1449
1572
|
<div id="toolbar">
|
|
@@ -1540,16 +1663,13 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1540
1663
|
<div class="tb-dev-3btns">
|
|
1541
1664
|
<button class="tb-dk-btn active" id="btn-mode-editor" onclick="setMode('editor')" title="Edit mode \u2014 click elements to edit"><i class="bi bi-pencil"></i></button>
|
|
1542
1665
|
<button class="tb-dk-btn" id="btn-mode-nav" onclick="setMode('navigate')" title="Navigate mode \u2014 interact with page normally"><i class="bi bi-cursor"></i></button>
|
|
1543
|
-
<button class="tb-dk-btn" title="Comments"><i class="bi bi-chat-dots"></i></button>
|
|
1666
|
+
<button class="tb-dk-btn" style="display:none" title="Comments"><i class="bi bi-chat-dots"></i></button>
|
|
1544
1667
|
</div>
|
|
1545
1668
|
<button class="tb-sim-btn" id="btn-simulate" onclick="simulateExperiment()">See Preview</button>
|
|
1546
1669
|
|
|
1547
1670
|
<!-- btn-close: kept for JS event listener -->
|
|
1548
1671
|
<button class="tb-fin-btn" id="btn-close">Exit Editor</button>
|
|
1549
1672
|
</div>
|
|
1550
|
-
|
|
1551
|
-
<!-- url-bar: hidden, kept for JS compatibility -->
|
|
1552
|
-
<div id="url-bar" style="display:none" title="">No page loaded</div>
|
|
1553
1673
|
</div>
|
|
1554
1674
|
|
|
1555
1675
|
<!-- \u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->
|
|
@@ -1557,7 +1677,10 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1557
1677
|
|
|
1558
1678
|
<!-- Left panel -->
|
|
1559
1679
|
<div id="left-panel">
|
|
1560
|
-
|
|
1680
|
+
<div class="lp-sec-hd-url">
|
|
1681
|
+
<!-- Page URL -->
|
|
1682
|
+
<input type="text" id="url-bar" placeholder="Enter URL and press Enter" spellcheck="false" autocomplete="off" aria-label="Page URL" title="" />
|
|
1683
|
+
</div>
|
|
1561
1684
|
<!-- Variations -->
|
|
1562
1685
|
<div class="lp-sec">
|
|
1563
1686
|
<div class="lp-sec-hd">
|
|
@@ -1600,7 +1723,7 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1600
1723
|
<!-- Tabs (hidden, kept for JS) -->
|
|
1601
1724
|
<div class="lp-tabs-container">
|
|
1602
1725
|
<div class="lp-tabs">
|
|
1603
|
-
<div class="lp-tab active" onclick="switchLeftTab('elements')">
|
|
1726
|
+
<div class="lp-tab active" onclick="switchLeftTab('elements')">Added Nodes</div>
|
|
1604
1727
|
<div class="lp-tab" onclick="switchLeftTab('dom-tree')">DOM Tree</div>
|
|
1605
1728
|
</div>
|
|
1606
1729
|
<button class="lp-add-btn" id="btn-add-element" title="Add element">+ Add</button>
|
|
@@ -1652,6 +1775,16 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1652
1775
|
<div style="font-size:12px;color:var(--text-3)">Waiting for experiment data…</div>
|
|
1653
1776
|
</div>
|
|
1654
1777
|
|
|
1778
|
+
<!-- Load error overlay (bad URL, fetch failure, refused to connect) -->
|
|
1779
|
+
<div id="iframe-load-error" aria-live="polite">
|
|
1780
|
+
<div class="nu-icon"><i class="bi bi-wifi-off"></i></div>
|
|
1781
|
+
<div class="ile-title">Page failed to load</div>
|
|
1782
|
+
<div class="ile-msg" id="iframe-load-error-msg">Check the URL and try again.</div>
|
|
1783
|
+
<button type="button" class="ile-reload-btn" id="iframe-load-error-reload">
|
|
1784
|
+
<i class="bi bi-arrow-clockwise"></i> Reload page
|
|
1785
|
+
</button>
|
|
1786
|
+
</div>
|
|
1787
|
+
|
|
1655
1788
|
<!-- Device frame containing the editing iframe -->
|
|
1656
1789
|
<div id="device-frame" class="desktop">
|
|
1657
1790
|
<iframe id="iframeId" name="iframeId" allowfullscreen></iframe>
|
|
@@ -1901,12 +2034,17 @@ function renderCroSectionThumb(sec) {
|
|
|
1901
2034
|
}
|
|
1902
2035
|
|
|
1903
2036
|
var BASE_COMPONENTS = [
|
|
1904
|
-
{ name:'Heading',
|
|
2037
|
+
{ name:'Heading', icon:'bi-type-h1', defaultAccSection:'content', html:'<h1 style="margin:0 0 12px;font-size:28px;font-weight:700;line-height:1.2;color:#0f172a">Your Heading</h1>' },
|
|
2038
|
+
{ name:'Heading', icon:'bi-type-h2', defaultAccSection:'content', html:'<h2 style="margin:0 0 12px;font-size:24px;font-weight:700;line-height:1.25;color:#0f172a">Your Heading</h2>' },
|
|
2039
|
+
{ name:'Heading', icon:'bi-type-h3', defaultAccSection:'content', html:'<h3 style="margin:0 0 12px;font-size:20px;font-weight:700;line-height:1.3;color:#0f172a">Your Heading</h3>' },
|
|
2040
|
+
{ name:'Heading', icon:'bi-type-h4', defaultAccSection:'content', html:'<h4 style="margin:0 0 12px;font-size:18px;font-weight:600;line-height:1.35;color:#0f172a">Your Heading</h4>' },
|
|
2041
|
+
{ name:'Heading', icon:'bi-type-h5', defaultAccSection:'content', html:'<h5 style="margin:0 0 12px;font-size:16px;font-weight:600;line-height:1.4;color:#0f172a">Your Heading</h5>' },
|
|
2042
|
+
{ name:'Heading', icon:'bi-type-h6', defaultAccSection:'content', html:'<h6 style="margin:0 0 12px;font-size:14px;font-weight:600;line-height:1.45;color:#0f172a">Your Heading</h6>' },
|
|
1905
2043
|
{ name:'Paragraph', icon:'bi-paragraph', defaultAccSection:'content', html:'<p style="margin:0 0 12px;font-size:16px;line-height:1.6;color:#334155">Your text here. Click to edit.</p>' },
|
|
1906
2044
|
{ name:'Image', icon:'bi-image', defaultAccSection:'image', html:'<img src="https://placehold.co/600x300/1a1a2e/a78bfa?text=Image" alt="Image" style="display:block;max-width:100%;height:auto;border-radius:8px">' },
|
|
1907
2045
|
{ name:'Button', icon:'bi-hand-index', defaultAccSection:'content', html:'<button type="button" style="display:inline-block;padding:10px 20px;font-size:14px;font-weight:600;line-height:1.2;color:#fff;background:#2563eb;border:none;border-radius:6px;cursor:pointer">Click me</button>' },
|
|
1908
2046
|
{ name:'Link', icon:'bi-link-45deg', defaultAccSection:'content', html:'<a href="#" style="font-size:16px;font-weight:500;color:#2563eb;text-decoration:underline">Link text</a>' },
|
|
1909
|
-
{ name:'Divider', icon:'bi-dash-lg', defaultAccSection:'
|
|
2047
|
+
{ name:'Divider', icon:'bi-dash-lg', defaultAccSection:'border', html:'<hr style="margin:16px 0;border:none;border-top:1px solid #e2e8f0">' },
|
|
1910
2048
|
{ name:'List', icon:'bi-list-ul', defaultAccSection:'content', html:'<ul style="margin:0 0 12px;padding-left:20px;font-size:16px;line-height:1.6;color:#334155"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>' },
|
|
1911
2049
|
{ name:'Video', icon:'bi-play-circle', defaultAccSection:'video', html:'<video controls style="display:block;width:100%;max-width:100%;border-radius:8px;background:#000"><source src="" type="video/mp4"></video>' },
|
|
1912
2050
|
{ name:'Youtube Video', icon:'bi-youtube', defaultAccSection:'youtube', html:'<iframe data-youtube-id="" src="about:blank" title="YouTube video" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="display:block;width:100%;max-width:100%;aspect-ratio:16/9;border:none;border-radius:8px;background:#000"></iframe>' },
|
|
@@ -2153,6 +2291,8 @@ var currentSectionComponentsTab = 'components';
|
|
|
2153
2291
|
var dragHandleActive = false;
|
|
2154
2292
|
var domTreeCollapsed = {};
|
|
2155
2293
|
var domTreeRefreshTimer = null;
|
|
2294
|
+
var domTreeDragState = null;
|
|
2295
|
+
var iframeLoadErrorCheckTimer = null;
|
|
2156
2296
|
var iframeSyncRetryTimer = null;
|
|
2157
2297
|
var iframeSyncAttempts = 0;
|
|
2158
2298
|
var selectionScrollWin = null;
|
|
@@ -2638,6 +2778,103 @@ function bindViewportControls() {
|
|
|
2638
2778
|
applyViewportFrame();
|
|
2639
2779
|
}
|
|
2640
2780
|
|
|
2781
|
+
function setUrlBarValue(pageUrl) {
|
|
2782
|
+
var urlBar = document.getElementById('url-bar');
|
|
2783
|
+
if (!urlBar) return;
|
|
2784
|
+
var val = pageUrl ? String(pageUrl) : '';
|
|
2785
|
+
urlBar.value = val;
|
|
2786
|
+
urlBar.title = val || 'Enter a page URL and press Enter';
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
function normalizeUserPageUrl(raw) {
|
|
2790
|
+
var s = String(raw || '').trim();
|
|
2791
|
+
if (!s) return '';
|
|
2792
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s)) s = 'https://' + s;
|
|
2793
|
+
try {
|
|
2794
|
+
return new URL(s).toString();
|
|
2795
|
+
} catch(_) {
|
|
2796
|
+
return '';
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
function buildProxyUrlForPageUrl(pageUrl, data) {
|
|
2801
|
+
data = data || experimentData || {};
|
|
2802
|
+
var extraTrackingMarkers = Array.isArray(data.trackingMarkers)
|
|
2803
|
+
? data.trackingMarkers.filter(function(m) { return typeof m === 'string' && m.trim().length > 0; })
|
|
2804
|
+
: [];
|
|
2805
|
+
var conversionProxyBaseUrl = (typeof data.conversionProxyBaseUrl === 'string'
|
|
2806
|
+
? data.conversionProxyBaseUrl
|
|
2807
|
+
: '').trim().replace(/\\/+$/, '');
|
|
2808
|
+
var proxyPath = '/api/conversion-proxy';
|
|
2809
|
+
var shellOrigin = '';
|
|
2810
|
+
try { shellOrigin = window.location.origin || ''; } catch(_) {}
|
|
2811
|
+
var onWorkerOrigin = !!(conversionProxyBaseUrl && shellOrigin === conversionProxyBaseUrl);
|
|
2812
|
+
var proxyRoot = proxyPath;
|
|
2813
|
+
var trackingMarkersParam = extraTrackingMarkers.length
|
|
2814
|
+
? '&trackingMarkers=' + encodeURIComponent(JSON.stringify(extraTrackingMarkers))
|
|
2815
|
+
: '';
|
|
2816
|
+
var conversionProxyBaseUrlParam = (!onWorkerOrigin && conversionProxyBaseUrl)
|
|
2817
|
+
? '&conversionProxyBaseUrl=' + encodeURIComponent(conversionProxyBaseUrl)
|
|
2818
|
+
: '';
|
|
2819
|
+
return proxyRoot + '?password=' + encodeURIComponent(data.editorPassword || '') +
|
|
2820
|
+
'&url=' + encodeURIComponent(pageUrl) +
|
|
2821
|
+
'&strictObserverFreeze=' + encodeURIComponent(data && data.strictObserverFreeze ? '1' : '0') +
|
|
2822
|
+
conversionProxyBaseUrlParam +
|
|
2823
|
+
trackingMarkersParam;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
function navigateToUserPageUrl(rawUrl) {
|
|
2827
|
+
var pageUrl = normalizeUserPageUrl(rawUrl);
|
|
2828
|
+
if (!pageUrl) {
|
|
2829
|
+
showEditorNotification('Enter a valid URL.', 'error', 2600);
|
|
2830
|
+
setUrlBarValue(experimentData && experimentData.pageUrl);
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2833
|
+
var prevPageUrl = experimentData && experimentData.pageUrl ? String(experimentData.pageUrl) : '';
|
|
2834
|
+
if (prevPageUrl === pageUrl && lastLoadedProxyUrl) {
|
|
2835
|
+
setUrlBarValue(pageUrl);
|
|
2836
|
+
return true;
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
commitStateChangesForActiveVariation();
|
|
2840
|
+
clearPendingGranularChangesets();
|
|
2841
|
+
varHtmlCache = {};
|
|
2842
|
+
sessionStructuralChainRowsByVarId = {};
|
|
2843
|
+
appliedChangesetSnapshots = {};
|
|
2844
|
+
appliedStructuralChangesetKeys = {};
|
|
2845
|
+
deselectElement();
|
|
2846
|
+
|
|
2847
|
+
experimentData = experimentData
|
|
2848
|
+
? Object.assign({}, experimentData, { pageUrl: pageUrl })
|
|
2849
|
+
: { pageUrl: pageUrl };
|
|
2850
|
+
|
|
2851
|
+
setUrlBarValue(pageUrl);
|
|
2852
|
+
showNoUrl(false);
|
|
2853
|
+
loadPage(buildProxyUrlForPageUrl(pageUrl, experimentData));
|
|
2854
|
+
return true;
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
function bindUrlBar() {
|
|
2858
|
+
var urlBar = document.getElementById('url-bar');
|
|
2859
|
+
if (!urlBar || urlBar._urlBarBound) return;
|
|
2860
|
+
urlBar._urlBarBound = true;
|
|
2861
|
+
urlBar.addEventListener('keydown', function(e) {
|
|
2862
|
+
if (e.key === 'Enter') {
|
|
2863
|
+
e.preventDefault();
|
|
2864
|
+
navigateToUserPageUrl(urlBar.value);
|
|
2865
|
+
urlBar.blur();
|
|
2866
|
+
} else if (e.key === 'Escape') {
|
|
2867
|
+
e.preventDefault();
|
|
2868
|
+
setUrlBarValue(experimentData && experimentData.pageUrl);
|
|
2869
|
+
urlBar.blur();
|
|
2870
|
+
}
|
|
2871
|
+
});
|
|
2872
|
+
urlBar.addEventListener('blur', function() {
|
|
2873
|
+
var canonical = experimentData && experimentData.pageUrl ? String(experimentData.pageUrl) : '';
|
|
2874
|
+
if (canonical && urlBar.value.trim() !== canonical) urlBar.value = canonical;
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2641
2878
|
// \u2500\u2500 Left panel tab switch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2642
2879
|
function switchLeftTab(tab) {
|
|
2643
2880
|
if (tab !== 'elements' && tab !== 'dom-tree') return;
|
|
@@ -3872,24 +4109,11 @@ function handleLoadExperiment(data) {
|
|
|
3872
4109
|
var pageUrl = data.pageUrl || '';
|
|
3873
4110
|
if (!pageUrl) {
|
|
3874
4111
|
showNoUrl(true);
|
|
4112
|
+
setUrlBarValue('');
|
|
3875
4113
|
lastLoadedProxyUrl = '';
|
|
3876
4114
|
return;
|
|
3877
4115
|
}
|
|
3878
|
-
var
|
|
3879
|
-
? data.trackingMarkers.filter(function(m){return typeof m === 'string' && m.trim().length > 0;})
|
|
3880
|
-
: [];
|
|
3881
|
-
var conversionProxyBaseUrl = (typeof (data && data.conversionProxyBaseUrl) === 'string'
|
|
3882
|
-
? data.conversionProxyBaseUrl
|
|
3883
|
-
: '').trim().replace(/\\/+$/, '');
|
|
3884
|
-
var proxyPath = '/api/conversion-proxy';
|
|
3885
|
-
var proxyRoot = conversionProxyBaseUrl ? (conversionProxyBaseUrl + proxyPath) : proxyPath;
|
|
3886
|
-
var trackingMarkersParam = extraTrackingMarkers.length
|
|
3887
|
-
? '&trackingMarkers=' + encodeURIComponent(JSON.stringify(extraTrackingMarkers))
|
|
3888
|
-
: '';
|
|
3889
|
-
var proxyUrl = proxyRoot + '?password=' + encodeURIComponent(data.editorPassword || '') +
|
|
3890
|
-
'&url=' + encodeURIComponent(pageUrl) +
|
|
3891
|
-
'&strictObserverFreeze=' + encodeURIComponent(data && data.strictObserverFreeze ? '1' : '0') +
|
|
3892
|
-
trackingMarkersParam;
|
|
4116
|
+
var proxyUrl = buildProxyUrlForPageUrl(pageUrl, data);
|
|
3893
4117
|
|
|
3894
4118
|
// Parent often re-posts load-experiment when React re-renders (new object identity) or
|
|
3895
4119
|
// after mutations-changed. Reloading the iframe again wipes variant changesets mid-session.
|
|
@@ -3907,9 +4131,7 @@ function handleLoadExperiment(data) {
|
|
|
3907
4131
|
loadStateChangesForActiveVariation();
|
|
3908
4132
|
writePersistedActiveVariationId(activeVarId);
|
|
3909
4133
|
renderVariationTabs();
|
|
3910
|
-
|
|
3911
|
-
urlBarSkip.textContent = pageUrl;
|
|
3912
|
-
urlBarSkip.title = pageUrl;
|
|
4134
|
+
setUrlBarValue(pageUrl);
|
|
3913
4135
|
try {
|
|
3914
4136
|
applyActiveVariationHtml();
|
|
3915
4137
|
syncIframeInteractions('load-experiment-skip-url');
|
|
@@ -3940,9 +4162,7 @@ function handleLoadExperiment(data) {
|
|
|
3940
4162
|
writePersistedActiveVariationId(activeVarId);
|
|
3941
4163
|
renderVariationTabs();
|
|
3942
4164
|
|
|
3943
|
-
|
|
3944
|
-
urlBar.textContent = pageUrl;
|
|
3945
|
-
urlBar.title = pageUrl;
|
|
4165
|
+
setUrlBarValue(pageUrl);
|
|
3946
4166
|
if (currentMainTab === 'history') renderHistoryTab();
|
|
3947
4167
|
captureBaselineFromVariations(variations);
|
|
3948
4168
|
recomputeEditorDirty();
|
|
@@ -3951,12 +4171,128 @@ function handleLoadExperiment(data) {
|
|
|
3951
4171
|
|
|
3952
4172
|
function showNoUrl(show) {
|
|
3953
4173
|
document.getElementById('no-url').style.display = show ? 'flex' : 'none';
|
|
4174
|
+
var urlBar = document.getElementById('url-bar');
|
|
4175
|
+
if (urlBar) urlBar.disabled = !!show;
|
|
3954
4176
|
if (show) {
|
|
4177
|
+
hideIframeLoadError();
|
|
3955
4178
|
detachIframeLoadingListeners();
|
|
3956
4179
|
setIframePageLoadingUi(false);
|
|
3957
4180
|
}
|
|
3958
4181
|
}
|
|
3959
4182
|
|
|
4183
|
+
function hideIframeLoadError() {
|
|
4184
|
+
if (iframeLoadErrorCheckTimer) {
|
|
4185
|
+
clearTimeout(iframeLoadErrorCheckTimer);
|
|
4186
|
+
iframeLoadErrorCheckTimer = null;
|
|
4187
|
+
}
|
|
4188
|
+
var el = document.getElementById('iframe-load-error');
|
|
4189
|
+
if (el) el.style.display = 'none';
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
function showIframeLoadError(message) {
|
|
4193
|
+
if (iframeLoadErrorCheckTimer) {
|
|
4194
|
+
clearTimeout(iframeLoadErrorCheckTimer);
|
|
4195
|
+
iframeLoadErrorCheckTimer = null;
|
|
4196
|
+
}
|
|
4197
|
+
var el = document.getElementById('iframe-load-error');
|
|
4198
|
+
var msgEl = document.getElementById('iframe-load-error-msg');
|
|
4199
|
+
if (msgEl) msgEl.textContent = message || 'Check the URL and try again.';
|
|
4200
|
+
if (el) el.style.display = 'flex';
|
|
4201
|
+
setIframePageLoadingUi(false);
|
|
4202
|
+
detachIframeLoadingListeners();
|
|
4203
|
+
deselectElement({ preserveMainTab: true });
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
function detectIframeLoadFailure(iframe, doc) {
|
|
4207
|
+
if (!iframe || !iframe.src || iframe.src === 'about:blank') return null;
|
|
4208
|
+
|
|
4209
|
+
if (!doc) {
|
|
4210
|
+
return 'Unable to load this page. The connection was refused or blocked.';
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
var docUrl = '';
|
|
4214
|
+
try { docUrl = String(doc.URL || ''); } catch(_) {}
|
|
4215
|
+
if (docUrl === 'about:blank') {
|
|
4216
|
+
return 'This page could not be loaded. Check the URL and try again.';
|
|
4217
|
+
}
|
|
4218
|
+
if (
|
|
4219
|
+
docUrl.indexOf('chrome-error://') === 0 ||
|
|
4220
|
+
docUrl.indexOf('about:neterror') === 0
|
|
4221
|
+
) {
|
|
4222
|
+
return 'This page could not be loaded. Check the URL and try again.';
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
var bodyText = '';
|
|
4226
|
+
try {
|
|
4227
|
+
bodyText = doc.body && doc.body.innerText ? String(doc.body.innerText).trim() : '';
|
|
4228
|
+
} catch(_) {}
|
|
4229
|
+
|
|
4230
|
+
var bodyLower = bodyText.toLowerCase();
|
|
4231
|
+
if (
|
|
4232
|
+
bodyLower.indexOf('refused to connect') >= 0 ||
|
|
4233
|
+
bodyLower.indexOf('err_connection_refused') >= 0 ||
|
|
4234
|
+
bodyLower.indexOf('err_name_not_resolved') >= 0 ||
|
|
4235
|
+
bodyLower.indexOf('err_ssl') >= 0 ||
|
|
4236
|
+
bodyLower.indexOf('err_connection_timed_out') >= 0 ||
|
|
4237
|
+
bodyLower.indexOf("this site can't be reached") >= 0 ||
|
|
4238
|
+
bodyLower.indexOf('this site cant be reached') >= 0 ||
|
|
4239
|
+
bodyLower.indexOf('unable to connect') >= 0 ||
|
|
4240
|
+
bodyLower.indexOf("page isn't working") >= 0 ||
|
|
4241
|
+
bodyLower.indexOf('404 not found') >= 0 && bodyLower.length < 280
|
|
4242
|
+
) {
|
|
4243
|
+
return 'This page could not be loaded. Check the URL and try again.';
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
if (bodyText.charAt(0) === '{') {
|
|
4247
|
+
try {
|
|
4248
|
+
var parsed = JSON.parse(bodyText);
|
|
4249
|
+
if (parsed && parsed.error) return String(parsed.error);
|
|
4250
|
+
} catch(_) {}
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
try {
|
|
4254
|
+
var ct = doc.contentType || '';
|
|
4255
|
+
if (String(ct).indexOf('application/json') >= 0 && bodyText.charAt(0) === '{') {
|
|
4256
|
+
var parsedCt = JSON.parse(bodyText);
|
|
4257
|
+
if (parsedCt && parsedCt.error) return String(parsedCt.error);
|
|
4258
|
+
}
|
|
4259
|
+
} catch(_) {}
|
|
4260
|
+
|
|
4261
|
+
return null;
|
|
4262
|
+
}
|
|
4263
|
+
|
|
4264
|
+
function scheduleIframeLoadErrorCheck(iframe) {
|
|
4265
|
+
if (iframeLoadErrorCheckTimer) clearTimeout(iframeLoadErrorCheckTimer);
|
|
4266
|
+
iframeLoadErrorCheckTimer = setTimeout(function() {
|
|
4267
|
+
iframeLoadErrorCheckTimer = null;
|
|
4268
|
+
if (!iframe || !iframe.src || iframe.src === 'about:blank') return;
|
|
4269
|
+
var doc = null;
|
|
4270
|
+
try { doc = iframe.contentDocument; } catch(_) {}
|
|
4271
|
+
var failMsg = detectIframeLoadFailure(iframe, doc);
|
|
4272
|
+
if (failMsg) showIframeLoadError(failMsg);
|
|
4273
|
+
}, 450);
|
|
4274
|
+
}
|
|
4275
|
+
|
|
4276
|
+
function reloadCurrentPage() {
|
|
4277
|
+
hideIframeLoadError();
|
|
4278
|
+
if (lastLoadedProxyUrl) {
|
|
4279
|
+
loadPage(lastLoadedProxyUrl);
|
|
4280
|
+
return;
|
|
4281
|
+
}
|
|
4282
|
+
var pageUrl = experimentData && experimentData.pageUrl;
|
|
4283
|
+
if (pageUrl) navigateToUserPageUrl(pageUrl);
|
|
4284
|
+
}
|
|
4285
|
+
|
|
4286
|
+
function bindIframeLoadErrorControls() {
|
|
4287
|
+
var btn = document.getElementById('iframe-load-error-reload');
|
|
4288
|
+
if (!btn || btn._iframeLoadErrorBound) return;
|
|
4289
|
+
btn._iframeLoadErrorBound = true;
|
|
4290
|
+
btn.addEventListener('click', function(e) {
|
|
4291
|
+
e.preventDefault();
|
|
4292
|
+
reloadCurrentPage();
|
|
4293
|
+
});
|
|
4294
|
+
}
|
|
4295
|
+
|
|
3960
4296
|
function detachIframeLoadingListeners() {
|
|
3961
4297
|
if (!iframeDocLoadingListeners) return;
|
|
3962
4298
|
try {
|
|
@@ -4347,6 +4683,7 @@ function emitEditorUrlChanged(rawUrl) {
|
|
|
4347
4683
|
}
|
|
4348
4684
|
|
|
4349
4685
|
function loadPage(proxyUrl) {
|
|
4686
|
+
hideIframeLoadError();
|
|
4350
4687
|
showNoUrl(false);
|
|
4351
4688
|
lastLoadedProxyUrl = proxyUrl;
|
|
4352
4689
|
var navGen = nextIframeContentNavGen();
|
|
@@ -5511,6 +5848,149 @@ function setDomTreeStatus(mode) {
|
|
|
5511
5848
|
}
|
|
5512
5849
|
}
|
|
5513
5850
|
|
|
5851
|
+
function canDomTreeMoveElement(el) {
|
|
5852
|
+
if (!el || el.nodeType !== 1 || !el.parentElement) return false;
|
|
5853
|
+
var tag = (el.tagName || '').toLowerCase();
|
|
5854
|
+
if (tag === 'html' || tag === 'body') return false;
|
|
5855
|
+
try {
|
|
5856
|
+
var doc = el.ownerDocument;
|
|
5857
|
+
if (doc && (el === doc.documentElement || el === doc.body)) return false;
|
|
5858
|
+
} catch(_) {}
|
|
5859
|
+
return true;
|
|
5860
|
+
}
|
|
5861
|
+
|
|
5862
|
+
function canDomTreeDropRelativeTo(dragEl, targetEl) {
|
|
5863
|
+
if (!canDomTreeMoveElement(dragEl) || !targetEl || targetEl.nodeType !== 1) return false;
|
|
5864
|
+
if (dragEl === targetEl) return false;
|
|
5865
|
+
try {
|
|
5866
|
+
if (dragEl.contains(targetEl) || targetEl.contains(dragEl)) return false;
|
|
5867
|
+
} catch(_) {
|
|
5868
|
+
return false;
|
|
5869
|
+
}
|
|
5870
|
+
var parent = targetEl.parentElement;
|
|
5871
|
+
if (!parent) return false;
|
|
5872
|
+
if (isDomTreeSkippableTagName(parent.tagName)) return false;
|
|
5873
|
+
return true;
|
|
5874
|
+
}
|
|
5875
|
+
|
|
5876
|
+
function moveDomTreeElementRelativeTo(dragEl, targetEl, insertBefore) {
|
|
5877
|
+
if (!canDomTreeDropRelativeTo(dragEl, targetEl)) return false;
|
|
5878
|
+
var parent = targetEl.parentElement;
|
|
5879
|
+
if (!parent) return false;
|
|
5880
|
+
var ref = insertBefore ? targetEl : targetEl.nextSibling;
|
|
5881
|
+
if (dragEl.parentElement === parent && dragEl === ref) return false;
|
|
5882
|
+
if (!insertBefore && dragEl.parentElement === parent && dragEl === targetEl.nextSibling) return false;
|
|
5883
|
+
try {
|
|
5884
|
+
parent.insertBefore(dragEl, ref);
|
|
5885
|
+
return true;
|
|
5886
|
+
} catch(_) {
|
|
5887
|
+
return false;
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
|
|
5891
|
+
function clearDomTreeDropIndicators() {
|
|
5892
|
+
var root = document.getElementById('dom-tree-root');
|
|
5893
|
+
if (!root) return;
|
|
5894
|
+
var rows = root.querySelectorAll('.dt-row');
|
|
5895
|
+
for (var i = 0; i < rows.length; i++) {
|
|
5896
|
+
rows[i].classList.remove('dt-drop-before', 'dt-drop-after', 'dt-drop-invalid', 'dt-dragging');
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
|
|
5900
|
+
function cleanupDomTreeDrag() {
|
|
5901
|
+
clearDomTreeDropIndicators();
|
|
5902
|
+
domTreeDragState = null;
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5905
|
+
function attachDomTreeDragHandlers() {
|
|
5906
|
+
var root = document.getElementById('dom-tree-root');
|
|
5907
|
+
if (!root || root._domTreeDragBound) return;
|
|
5908
|
+
root._domTreeDragBound = true;
|
|
5909
|
+
|
|
5910
|
+
root.addEventListener('dragstart', function(e) {
|
|
5911
|
+
if (currentMode !== 'editor') {
|
|
5912
|
+
e.preventDefault();
|
|
5913
|
+
return;
|
|
5914
|
+
}
|
|
5915
|
+
var row = e.target && e.target.closest ? e.target.closest('.dt-row') : null;
|
|
5916
|
+
if (!row || !row._dtEl || (e.target.closest && e.target.closest('.dt-chev'))) {
|
|
5917
|
+
e.preventDefault();
|
|
5918
|
+
return;
|
|
5919
|
+
}
|
|
5920
|
+
if (!canDomTreeMoveElement(row._dtEl)) {
|
|
5921
|
+
e.preventDefault();
|
|
5922
|
+
return;
|
|
5923
|
+
}
|
|
5924
|
+
domTreeDragState = { el: row._dtEl, row: row };
|
|
5925
|
+
row.classList.add('dt-dragging');
|
|
5926
|
+
try {
|
|
5927
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
5928
|
+
e.dataTransfer.setData('text/plain', 'dom-tree');
|
|
5929
|
+
} catch(_) {}
|
|
5930
|
+
suppressClickUntil = Date.now() + 300;
|
|
5931
|
+
});
|
|
5932
|
+
|
|
5933
|
+
root.addEventListener('dragover', function(e) {
|
|
5934
|
+
if (!domTreeDragState) return;
|
|
5935
|
+
e.preventDefault();
|
|
5936
|
+
clearDomTreeDropIndicators();
|
|
5937
|
+
if (domTreeDragState.row) domTreeDragState.row.classList.add('dt-dragging');
|
|
5938
|
+
|
|
5939
|
+
var row = e.target && e.target.closest ? e.target.closest('.dt-row') : null;
|
|
5940
|
+
if (!row || !row._dtEl) {
|
|
5941
|
+
try { e.dataTransfer.dropEffect = 'none'; } catch(_) {}
|
|
5942
|
+
return;
|
|
5943
|
+
}
|
|
5944
|
+
|
|
5945
|
+
var dragEl = domTreeDragState.el;
|
|
5946
|
+
var targetEl = row._dtEl;
|
|
5947
|
+
if (!canDomTreeDropRelativeTo(dragEl, targetEl)) {
|
|
5948
|
+
row.classList.add('dt-drop-invalid');
|
|
5949
|
+
try { e.dataTransfer.dropEffect = 'none'; } catch(_) {}
|
|
5950
|
+
return;
|
|
5951
|
+
}
|
|
5952
|
+
|
|
5953
|
+
var rect = row.getBoundingClientRect();
|
|
5954
|
+
var insertBefore = e.clientY < rect.top + rect.height / 2;
|
|
5955
|
+
row.classList.add(insertBefore ? 'dt-drop-before' : 'dt-drop-after');
|
|
5956
|
+
domTreeDragState.dropRow = row;
|
|
5957
|
+
domTreeDragState.insertBefore = insertBefore;
|
|
5958
|
+
try { e.dataTransfer.dropEffect = 'move'; } catch(_) {}
|
|
5959
|
+
});
|
|
5960
|
+
|
|
5961
|
+
root.addEventListener('drop', function(e) {
|
|
5962
|
+
e.preventDefault();
|
|
5963
|
+
if (!domTreeDragState) return;
|
|
5964
|
+
|
|
5965
|
+
var dragEl = domTreeDragState.el;
|
|
5966
|
+
var row = domTreeDragState.dropRow || (e.target && e.target.closest ? e.target.closest('.dt-row') : null);
|
|
5967
|
+
var moved = false;
|
|
5968
|
+
if (row && row._dtEl && canDomTreeDropRelativeTo(dragEl, row._dtEl)) {
|
|
5969
|
+
var insertBefore = domTreeDragState.insertBefore;
|
|
5970
|
+
if (insertBefore === undefined) {
|
|
5971
|
+
var rect = row.getBoundingClientRect();
|
|
5972
|
+
insertBefore = e.clientY < rect.top + rect.height / 2;
|
|
5973
|
+
}
|
|
5974
|
+
if (moveDomTreeElementRelativeTo(dragEl, row._dtEl, insertBefore)) {
|
|
5975
|
+
moved = true;
|
|
5976
|
+
if (activeVarId) recordReorderAfterDrag(dragEl);
|
|
5977
|
+
saveCurrentVariationHtml();
|
|
5978
|
+
recomputeEditorDirty();
|
|
5979
|
+
selectElement(dragEl);
|
|
5980
|
+
scrollIframeElementIntoView(dragEl);
|
|
5981
|
+
updateSelectionToolbar();
|
|
5982
|
+
scheduleDomTreeRefresh();
|
|
5983
|
+
}
|
|
5984
|
+
}
|
|
5985
|
+
cleanupDomTreeDrag();
|
|
5986
|
+
if (moved) suppressClickUntil = Date.now() + 300;
|
|
5987
|
+
});
|
|
5988
|
+
|
|
5989
|
+
root.addEventListener('dragend', function() {
|
|
5990
|
+
cleanupDomTreeDrag();
|
|
5991
|
+
});
|
|
5992
|
+
}
|
|
5993
|
+
|
|
5514
5994
|
function domTreePathSegment(el) {
|
|
5515
5995
|
var tag = el.tagName.toLowerCase();
|
|
5516
5996
|
var idx = 1;
|
|
@@ -5889,6 +6369,7 @@ function renderDomTree(filterRaw) {
|
|
|
5889
6369
|
row.onmouseenter = function() {
|
|
5890
6370
|
setTreeHoverHighlight(el);
|
|
5891
6371
|
};
|
|
6372
|
+
if (canDomTreeMoveElement(el)) row.draggable = true;
|
|
5892
6373
|
root.appendChild(row);
|
|
5893
6374
|
|
|
5894
6375
|
if (!hasKids || collapsed) return;
|
|
@@ -5910,6 +6391,7 @@ function renderDomTree(filterRaw) {
|
|
|
5910
6391
|
root.onmouseleave = function() {
|
|
5911
6392
|
clearTreeHoverHighlight();
|
|
5912
6393
|
};
|
|
6394
|
+
attachDomTreeDragHandlers();
|
|
5913
6395
|
}
|
|
5914
6396
|
|
|
5915
6397
|
// \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
|
|
@@ -6338,7 +6820,7 @@ function renderVideoSection(el) {
|
|
|
6338
6820
|
}
|
|
6339
6821
|
|
|
6340
6822
|
html += pr('Src', '<input class="pr-inp" id="pp-video-src" type="url" value="'+esc(src)+'" placeholder="https://example.com/video.mp4">');
|
|
6341
|
-
html += pr('
|
|
6823
|
+
html += pr('Thumbnail', '<input class="pr-inp" id="pp-video-poster" type="url" value="'+esc(el.getAttribute('poster')||'')+'" placeholder="https://\u2026">');
|
|
6342
6824
|
|
|
6343
6825
|
document.getElementById('acc-body-video').innerHTML = html;
|
|
6344
6826
|
|
|
@@ -6605,11 +7087,6 @@ function renderRightPanel(el, options) {
|
|
|
6605
7087
|
// \u2500\u2500 HTML Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6606
7088
|
var contentHtml = '';
|
|
6607
7089
|
if (isLinkElement(el)) contentHtml += buildAnchorContentFieldsHtml(el);
|
|
6608
|
-
if (isFormControlElement(el)) {
|
|
6609
|
-
contentHtml +=
|
|
6610
|
-
subLbl('Value') +
|
|
6611
|
-
'<textarea class="pr-inp" id="pp-value" style="width:100%;min-height:44px;margin-bottom:8px">'+esc(getFormControlValue(el))+'</textarea>';
|
|
6612
|
-
}
|
|
6613
7090
|
if (tag==='input' || tag==='textarea') {
|
|
6614
7091
|
contentHtml +=
|
|
6615
7092
|
subLbl('Placeholder') +
|
|
@@ -6722,17 +7199,13 @@ function renderRightPanel(el, options) {
|
|
|
6722
7199
|
function wireContentFieldSync(el, sel) {
|
|
6723
7200
|
var textInp = document.getElementById('pp-text');
|
|
6724
7201
|
var htmlInp = document.getElementById('pp-html');
|
|
6725
|
-
var valueInp = document.getElementById('pp-value');
|
|
6726
7202
|
var syncing = false;
|
|
6727
7203
|
function applyContentChange(changedId) {
|
|
6728
7204
|
if (syncing) return;
|
|
6729
7205
|
syncing = true;
|
|
6730
7206
|
try {
|
|
6731
7207
|
var orig = getOriginalValue(changedId, el);
|
|
6732
|
-
if (changedId === 'pp-
|
|
6733
|
-
el.value = valueInp.value;
|
|
6734
|
-
logChange(sel, 'pp-value', valueInp.value, el, orig);
|
|
6735
|
-
} else if (changedId === 'pp-text') {
|
|
7208
|
+
if (changedId === 'pp-text') {
|
|
6736
7209
|
el.innerText = textInp.value;
|
|
6737
7210
|
if (htmlInp) htmlInp.value = el.innerHTML;
|
|
6738
7211
|
logChange(sel, 'pp-text', textInp.value, el, orig);
|
|
@@ -6745,10 +7218,6 @@ function wireContentFieldSync(el, sel) {
|
|
|
6745
7218
|
syncing = false;
|
|
6746
7219
|
}
|
|
6747
7220
|
}
|
|
6748
|
-
if (valueInp && isFormControlElement(el)) {
|
|
6749
|
-
valueInp.addEventListener('input', function() { applyContentChange('pp-value'); });
|
|
6750
|
-
valueInp.addEventListener('change', function() { applyContentChange('pp-value'); });
|
|
6751
|
-
}
|
|
6752
7221
|
if (textInp) {
|
|
6753
7222
|
textInp.addEventListener('input', function() { applyContentChange('pp-text'); });
|
|
6754
7223
|
textInp.addEventListener('change', function() { applyContentChange('pp-text'); });
|
|
@@ -7709,6 +8178,8 @@ function registerCROSections() {
|
|
|
7709
8178
|
window.addEventListener('load', function() {
|
|
7710
8179
|
registerCROSections();
|
|
7711
8180
|
bindViewportControls();
|
|
8181
|
+
bindUrlBar();
|
|
8182
|
+
bindIframeLoadErrorControls();
|
|
7712
8183
|
// switchSectionComponentsTab(currentSectionComponentsTab);
|
|
7713
8184
|
renderElementsTree(document.getElementById('comp-search').value);
|
|
7714
8185
|
vvvebReady = true;
|
|
@@ -7742,17 +8213,24 @@ window.addEventListener('load', function() {
|
|
|
7742
8213
|
var doc = iframe.contentDocument;
|
|
7743
8214
|
if (!doc) {
|
|
7744
8215
|
resetIframeBindings();
|
|
7745
|
-
|
|
8216
|
+
showIframeLoadError('Unable to load this page. The connection was refused or blocked.');
|
|
7746
8217
|
return;
|
|
7747
8218
|
}
|
|
7748
8219
|
var docUrl = '';
|
|
7749
8220
|
try { docUrl = String(doc.URL || ''); } catch(_) {}
|
|
8221
|
+
var immediateFail = detectIframeLoadFailure(iframe, doc);
|
|
8222
|
+
if (immediateFail) {
|
|
8223
|
+
showIframeLoadError(immediateFail);
|
|
8224
|
+
return;
|
|
8225
|
+
}
|
|
8226
|
+
hideIframeLoadError();
|
|
7750
8227
|
emitEditorUrlChanged(iframe.src || docUrl);
|
|
7751
8228
|
// Stale events: src may already be the proxy URL while the document is still
|
|
7752
8229
|
// about:blank (e.g. src cleared then reset to force reload). Ask sync path to retry.
|
|
7753
8230
|
if (docUrl === 'about:blank') {
|
|
7754
8231
|
resetIframeBindings();
|
|
7755
8232
|
syncIframeInteractions('iframe-load-about-blank');
|
|
8233
|
+
scheduleIframeLoadErrorCheck(iframe);
|
|
7756
8234
|
return;
|
|
7757
8235
|
}
|
|
7758
8236
|
// If early-paint and final load DOM signatures match, avoid a second full apply/reset
|
|
@@ -7780,6 +8258,7 @@ window.addEventListener('load', function() {
|
|
|
7780
8258
|
}
|
|
7781
8259
|
// Always attempt sync; it has its own readiness checks + retry loop.
|
|
7782
8260
|
syncIframeInteractions('iframe-load');
|
|
8261
|
+
scheduleIframeLoadErrorCheck(iframe);
|
|
7783
8262
|
});
|
|
7784
8263
|
|
|
7785
8264
|
var sfAdd = document.getElementById('sf-add');
|
|
@@ -8754,4 +9233,4 @@ function visualEditorProxyPlugin(options) {
|
|
|
8754
9233
|
};
|
|
8755
9234
|
}
|
|
8756
9235
|
|
|
8757
|
-
export { createVisualEditorMiddleware, visualEditorProxyPlugin };
|
|
9236
|
+
export { buildVvvebEditorHtml, createVisualEditorMiddleware, visualEditorProxyPlugin };
|