@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.cjs
CHANGED
|
@@ -415,7 +415,7 @@ html,body{height:100%;overflow:hidden;font-family:var(--font-sans);font-size:13p
|
|
|
415
415
|
overflow:visible
|
|
416
416
|
}
|
|
417
417
|
/* Toolbar layout sections */
|
|
418
|
-
.tb-left{display:flex;align-items:center;gap:0;flex-
|
|
418
|
+
.tb-left{display:flex;align-items:center;gap:0;flex:1;min-width:0;max-width:min(560px,46vw);margin-right:8px}
|
|
419
419
|
.tb-center{display:flex;align-items:center;gap:4px;flex:1;justify-content:center}
|
|
420
420
|
.tb-right{display:flex;align-items:center;gap:6px;flex-shrink:0;margin-left:auto}
|
|
421
421
|
/* Logo */
|
|
@@ -437,7 +437,7 @@ html,body{height:100%;overflow:hidden;font-family:var(--font-sans);font-size:13p
|
|
|
437
437
|
/* Device toggle buttons */
|
|
438
438
|
.tb-dev-wrap{position:relative;display:flex;align-items:center}
|
|
439
439
|
.tb-dev-btns{display:flex;align-items:center;gap:1px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width: 116px;}
|
|
440
|
-
.tb-dev-3btns{display:flex;align-items:center;gap:2px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width:
|
|
440
|
+
.tb-dev-3btns{display:flex;align-items:center;gap:2px;padding:8px 4px;background:#F0F0F0;border-radius:7px; height: 30px;width: auto;}
|
|
441
441
|
.tb-dev-menu{
|
|
442
442
|
position:absolute;top:calc(100% + 8px);right:0;z-index:12040;display:none;
|
|
443
443
|
width:264px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:4px 4px;
|
|
@@ -541,7 +541,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
|
|
|
541
541
|
}
|
|
542
542
|
.tb-dev-menu .vp-preset-btn:hover,.tb-dev-menu .vp-preset-btn.active{background:#f4f4f5}
|
|
543
543
|
/* Dark icon buttons */
|
|
544
|
-
.tb-dk-btn{width:28px;height:
|
|
544
|
+
.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}
|
|
545
545
|
.tb-dk-btn:hover{color:#e4e4e7;background:rgba(255,255,255,.07)}
|
|
546
546
|
.tb-dk-btn.active{background:#FFFFFF}
|
|
547
547
|
/* Dark separator */
|
|
@@ -708,6 +708,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
|
|
|
708
708
|
background: var(--bg-primary-default, #262626)!important;
|
|
709
709
|
color: var(--text-primary-default, #FFF)!important;
|
|
710
710
|
}
|
|
711
|
+
#dom-tree-root .dt-row[draggable="true"]{cursor:grab}
|
|
712
|
+
#dom-tree-root .dt-row.dt-dragging{opacity:.55;cursor:grabbing}
|
|
713
|
+
#dom-tree-root .dt-row.dt-drop-before::after,
|
|
714
|
+
#dom-tree-root .dt-row.dt-drop-after::after{
|
|
715
|
+
content:'';position:absolute;left:8px;right:8px;height:2px;background:#6366f1;pointer-events:none;z-index:1
|
|
716
|
+
}
|
|
717
|
+
#dom-tree-root .dt-row.dt-drop-before::after{top:0}
|
|
718
|
+
#dom-tree-root .dt-row.dt-drop-after::after{bottom:0}
|
|
719
|
+
#dom-tree-root .dt-row.dt-drop-invalid{cursor:not-allowed;opacity:.45}
|
|
711
720
|
.dt-chev{
|
|
712
721
|
width:16px;height:16px;flex-shrink:0;border:none;background:transparent;
|
|
713
722
|
cursor:pointer;color:var(--text-3);display:flex;align-items:center;justify-content:center;
|
|
@@ -756,6 +765,21 @@ flex: 1;
|
|
|
756
765
|
}
|
|
757
766
|
#no-url .nu-icon{font-size:48px;opacity:.3}
|
|
758
767
|
|
|
768
|
+
/* \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 */
|
|
769
|
+
#iframe-load-error{
|
|
770
|
+
display:none;position:absolute;inset:0;z-index:50;flex-direction:column;
|
|
771
|
+
align-items:center;justify-content:center;gap:12px;text-align:center;padding:40px;
|
|
772
|
+
background:rgba(232,236,240,.94);backdrop-filter:blur(4px)
|
|
773
|
+
}
|
|
774
|
+
#iframe-load-error .nu-icon{font-size:48px;color:#94a3b8}
|
|
775
|
+
#iframe-load-error .ile-title{font-weight:600;font-size:15px;color:var(--text-2)}
|
|
776
|
+
#iframe-load-error .ile-msg{font-size:12px;color:var(--text-3);max-width:360px;line-height:1.5;word-break:break-word}
|
|
777
|
+
#iframe-load-error .ile-reload-btn{
|
|
778
|
+
margin-top:4px;display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:6px;
|
|
779
|
+
border:1px solid var(--border);background:#fff;color:var(--text);font-size:13px;font-weight:500;cursor:pointer
|
|
780
|
+
}
|
|
781
|
+
#iframe-load-error .ile-reload-btn:hover{background:var(--bg-hover);border-color:#cbd5e1}
|
|
782
|
+
|
|
759
783
|
/* \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 */
|
|
760
784
|
#device-frame{
|
|
761
785
|
width: 100%;
|
|
@@ -810,12 +834,32 @@ flex: 1;
|
|
|
810
834
|
|
|
811
835
|
/* \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 */
|
|
812
836
|
#url-bar{
|
|
813
|
-
|
|
814
|
-
|
|
837
|
+
flex: 1;
|
|
838
|
+
max-width: 100%;
|
|
839
|
+
width: 100%;
|
|
840
|
+
border: 1px solid var(--border);
|
|
841
|
+
border-radius: 4px;
|
|
842
|
+
color: var(--text-2);
|
|
843
|
+
font-size: 12px;
|
|
844
|
+
padding: 8px 8px;
|
|
845
|
+
outline: none;
|
|
846
|
+
font-family: inherit;
|
|
847
|
+
overflow: hidden;
|
|
848
|
+
text-overflow: ellipsis;
|
|
815
849
|
}
|
|
850
|
+
#url-bar::placeholder{color:var(--text-3)}
|
|
851
|
+
#url-bar:focus{border-color:#94a3b8;color:var(--text);background:#fff}
|
|
852
|
+
#url-bar:disabled{opacity:.55;cursor:not-allowed}
|
|
816
853
|
|
|
817
854
|
/* \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 */
|
|
818
|
-
.lp-sec{border-bottom:1px solid var(--border);
|
|
855
|
+
.lp-sec{ border-bottom: 1px solid var(--border);
|
|
856
|
+
flex-shrink: 0;
|
|
857
|
+
padding: 18px 16px;}
|
|
858
|
+
.lp-sec-hd-url{
|
|
859
|
+
border-bottom: 1px solid var(--border);
|
|
860
|
+
flex-shrink: 0;
|
|
861
|
+
padding: 5px 16px;
|
|
862
|
+
}
|
|
819
863
|
.lp-sec-no-border{border-bottom:none!important}
|
|
820
864
|
.lp-sec-hd{
|
|
821
865
|
margin-bottom: 12px;
|
|
@@ -1435,23 +1479,102 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1435
1479
|
max-width:420px;
|
|
1436
1480
|
line-height:1.5;
|
|
1437
1481
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
display:flex;
|
|
1445
|
-
}
|
|
1482
|
+
html.ve-editor-gated #app,
|
|
1483
|
+
html.ve-editor-gated #custom-css-modal{
|
|
1484
|
+
display:none!important;
|
|
1485
|
+
}
|
|
1486
|
+
html.ve-editor-gated #ve-min-screen-notice{
|
|
1487
|
+
display:flex;
|
|
1446
1488
|
}
|
|
1447
1489
|
</style>
|
|
1448
1490
|
</head>
|
|
1449
1491
|
<body class="mode-editor">
|
|
1450
1492
|
<div id="ve-min-screen-notice" aria-live="polite">
|
|
1451
1493
|
<div class="ve-min-screen-icon"><i class="bi bi-display"></i></div>
|
|
1452
|
-
<div class="ve-min-screen-title">Please switch to
|
|
1494
|
+
<div class="ve-min-screen-title">Please switch to a larger screen to use the Visual Editor</div>
|
|
1453
1495
|
<div class="ve-min-screen-desc">The Visual Editor requires a screen width of at least 1100px.</div>
|
|
1454
1496
|
</div>
|
|
1497
|
+
<script>(function(){
|
|
1498
|
+
var MIN_EDITOR_WIDTH=1100;
|
|
1499
|
+
var ZOOM_IN_THRESHOLD=1.05;
|
|
1500
|
+
var ZOOM_MATCH_TOLERANCE=0.06;
|
|
1501
|
+
var STANDARD_BROWSER_ZOOMS=[1.25,1.33,1.5,1.67,1.75,2,2.5,3,4];
|
|
1502
|
+
function markUserZoomInteraction(){
|
|
1503
|
+
try{sessionStorage.setItem('ve-user-zoomed','1');}catch(_){}
|
|
1504
|
+
}
|
|
1505
|
+
function hasUserZoomInteraction(){
|
|
1506
|
+
try{return sessionStorage.getItem('ve-user-zoomed')==='1';}catch(_){return false;}
|
|
1507
|
+
}
|
|
1508
|
+
function matchesStandardBrowserZoom(iw,screenW){
|
|
1509
|
+
if(!iw||!screenW)return false;
|
|
1510
|
+
for(var i=0;i<STANDARD_BROWSER_ZOOMS.length;i++){
|
|
1511
|
+
var z=STANDARD_BROWSER_ZOOMS[i];
|
|
1512
|
+
var impliedFull=iw*z;
|
|
1513
|
+
if(Math.abs(impliedFull-screenW)/screenW<=ZOOM_MATCH_TOLERANCE)return true;
|
|
1514
|
+
}
|
|
1515
|
+
return false;
|
|
1516
|
+
}
|
|
1517
|
+
function isBrowserZoomedIn(iw,screenW){
|
|
1518
|
+
try{
|
|
1519
|
+
if(window.visualViewport&&window.visualViewport.scale>ZOOM_IN_THRESHOLD)return true;
|
|
1520
|
+
}catch(_){}
|
|
1521
|
+
if(!screenW||screenW<MIN_EDITOR_WIDTH||iw>=MIN_EDITOR_WIDTH)return false;
|
|
1522
|
+
if(hasUserZoomInteraction())return true;
|
|
1523
|
+
return matchesStandardBrowserZoom(iw,screenW);
|
|
1524
|
+
}
|
|
1525
|
+
function updateMinScreenGate(){
|
|
1526
|
+
var iw=window.innerWidth||0;
|
|
1527
|
+
var gated=iw<MIN_EDITOR_WIDTH;
|
|
1528
|
+
document.documentElement.classList.toggle('ve-editor-gated',gated);
|
|
1529
|
+
if(!gated){
|
|
1530
|
+
try{sessionStorage.removeItem('ve-user-zoomed');}catch(_){}
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
var notice=document.getElementById('ve-min-screen-notice');
|
|
1534
|
+
if(!notice)return;
|
|
1535
|
+
var titleEl=notice.querySelector('.ve-min-screen-title');
|
|
1536
|
+
var descEl=notice.querySelector('.ve-min-screen-desc');
|
|
1537
|
+
var iconEl=notice.querySelector('.ve-min-screen-icon i');
|
|
1538
|
+
if(!titleEl||!descEl||!iconEl)return;
|
|
1539
|
+
var screenW=(window.screen&&(window.screen.availWidth||window.screen.width))||0;
|
|
1540
|
+
var smallScreen=screenW>0&&screenW<MIN_EDITOR_WIDTH;
|
|
1541
|
+
var zoomedIn=isBrowserZoomedIn(iw,screenW);
|
|
1542
|
+
if(smallScreen){
|
|
1543
|
+
iconEl.className='bi bi-display';
|
|
1544
|
+
titleEl.textContent='Please switch to a larger screen to use the Visual Editor';
|
|
1545
|
+
descEl.textContent='The Visual Editor requires a screen width of at least '+MIN_EDITOR_WIDTH+'px.';
|
|
1546
|
+
}else if(zoomedIn){
|
|
1547
|
+
iconEl.className='bi bi-zoom-out';
|
|
1548
|
+
titleEl.textContent='You have zoomed in too much';
|
|
1549
|
+
descEl.textContent='Zoom out to use the Visual Editor \u2014 press Cmd + - on Mac or Ctrl + - on Windows.';
|
|
1550
|
+
}else{
|
|
1551
|
+
iconEl.className='bi bi-arrows-angle-expand';
|
|
1552
|
+
titleEl.textContent='Widen your browser window';
|
|
1553
|
+
descEl.textContent='The Visual Editor needs at least '+MIN_EDITOR_WIDTH+'px of width. Maximize this window or resize it wider.';
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
function onZoomShortcut(e){
|
|
1557
|
+
if(!(e.ctrlKey||e.metaKey))return;
|
|
1558
|
+
var k=e.key||'';
|
|
1559
|
+
if(k==='+'||k==='-'||k==='='||k==='0'||k==='_'||e.code==='Equal'||e.code==='Minus'||e.code==='NumpadAdd'||e.code==='NumpadSubtract'){
|
|
1560
|
+
markUserZoomInteraction();
|
|
1561
|
+
setTimeout(updateMinScreenGate,0);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
function onZoomWheel(e){
|
|
1565
|
+
if(!e.ctrlKey)return;
|
|
1566
|
+
markUserZoomInteraction();
|
|
1567
|
+
setTimeout(updateMinScreenGate,0);
|
|
1568
|
+
}
|
|
1569
|
+
document.addEventListener('keydown',onZoomShortcut,true);
|
|
1570
|
+
window.addEventListener('wheel',onZoomWheel,{passive:true,capture:true});
|
|
1571
|
+
updateMinScreenGate();
|
|
1572
|
+
window.addEventListener('resize',updateMinScreenGate);
|
|
1573
|
+
if(window.visualViewport){
|
|
1574
|
+
window.visualViewport.addEventListener('resize',updateMinScreenGate);
|
|
1575
|
+
window.visualViewport.addEventListener('scroll',updateMinScreenGate);
|
|
1576
|
+
}
|
|
1577
|
+
})();</script>
|
|
1455
1578
|
<div id="app">
|
|
1456
1579
|
<!-- \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 -->
|
|
1457
1580
|
<div id="toolbar">
|
|
@@ -1548,16 +1671,13 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1548
1671
|
<div class="tb-dev-3btns">
|
|
1549
1672
|
<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>
|
|
1550
1673
|
<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>
|
|
1551
|
-
<button class="tb-dk-btn" title="Comments"><i class="bi bi-chat-dots"></i></button>
|
|
1674
|
+
<button class="tb-dk-btn" style="display:none" title="Comments"><i class="bi bi-chat-dots"></i></button>
|
|
1552
1675
|
</div>
|
|
1553
1676
|
<button class="tb-sim-btn" id="btn-simulate" onclick="simulateExperiment()">See Preview</button>
|
|
1554
1677
|
|
|
1555
1678
|
<!-- btn-close: kept for JS event listener -->
|
|
1556
1679
|
<button class="tb-fin-btn" id="btn-close">Exit Editor</button>
|
|
1557
1680
|
</div>
|
|
1558
|
-
|
|
1559
|
-
<!-- url-bar: hidden, kept for JS compatibility -->
|
|
1560
|
-
<div id="url-bar" style="display:none" title="">No page loaded</div>
|
|
1561
1681
|
</div>
|
|
1562
1682
|
|
|
1563
1683
|
<!-- \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 -->
|
|
@@ -1565,7 +1685,10 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1565
1685
|
|
|
1566
1686
|
<!-- Left panel -->
|
|
1567
1687
|
<div id="left-panel">
|
|
1568
|
-
|
|
1688
|
+
<div class="lp-sec-hd-url">
|
|
1689
|
+
<!-- Page URL -->
|
|
1690
|
+
<input type="text" id="url-bar" placeholder="Enter URL and press Enter" spellcheck="false" autocomplete="off" aria-label="Page URL" title="" />
|
|
1691
|
+
</div>
|
|
1569
1692
|
<!-- Variations -->
|
|
1570
1693
|
<div class="lp-sec">
|
|
1571
1694
|
<div class="lp-sec-hd">
|
|
@@ -1608,7 +1731,7 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1608
1731
|
<!-- Tabs (hidden, kept for JS) -->
|
|
1609
1732
|
<div class="lp-tabs-container">
|
|
1610
1733
|
<div class="lp-tabs">
|
|
1611
|
-
<div class="lp-tab active" onclick="switchLeftTab('elements')">
|
|
1734
|
+
<div class="lp-tab active" onclick="switchLeftTab('elements')">Added Nodes</div>
|
|
1612
1735
|
<div class="lp-tab" onclick="switchLeftTab('dom-tree')">DOM Tree</div>
|
|
1613
1736
|
</div>
|
|
1614
1737
|
<button class="lp-add-btn" id="btn-add-element" title="Add element">+ Add</button>
|
|
@@ -1660,6 +1783,16 @@ select.pr-inp{cursor:pointer;background:#fff}
|
|
|
1660
1783
|
<div style="font-size:12px;color:var(--text-3)">Waiting for experiment data…</div>
|
|
1661
1784
|
</div>
|
|
1662
1785
|
|
|
1786
|
+
<!-- Load error overlay (bad URL, fetch failure, refused to connect) -->
|
|
1787
|
+
<div id="iframe-load-error" aria-live="polite">
|
|
1788
|
+
<div class="nu-icon"><i class="bi bi-wifi-off"></i></div>
|
|
1789
|
+
<div class="ile-title">Page failed to load</div>
|
|
1790
|
+
<div class="ile-msg" id="iframe-load-error-msg">Check the URL and try again.</div>
|
|
1791
|
+
<button type="button" class="ile-reload-btn" id="iframe-load-error-reload">
|
|
1792
|
+
<i class="bi bi-arrow-clockwise"></i> Reload page
|
|
1793
|
+
</button>
|
|
1794
|
+
</div>
|
|
1795
|
+
|
|
1663
1796
|
<!-- Device frame containing the editing iframe -->
|
|
1664
1797
|
<div id="device-frame" class="desktop">
|
|
1665
1798
|
<iframe id="iframeId" name="iframeId" allowfullscreen></iframe>
|
|
@@ -1909,12 +2042,17 @@ function renderCroSectionThumb(sec) {
|
|
|
1909
2042
|
}
|
|
1910
2043
|
|
|
1911
2044
|
var BASE_COMPONENTS = [
|
|
1912
|
-
{ name:'Heading',
|
|
2045
|
+
{ 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>' },
|
|
2046
|
+
{ 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>' },
|
|
2047
|
+
{ 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>' },
|
|
2048
|
+
{ 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>' },
|
|
2049
|
+
{ 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>' },
|
|
2050
|
+
{ 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>' },
|
|
1913
2051
|
{ 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>' },
|
|
1914
2052
|
{ 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">' },
|
|
1915
2053
|
{ 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>' },
|
|
1916
2054
|
{ 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>' },
|
|
1917
|
-
{ name:'Divider', icon:'bi-dash-lg', defaultAccSection:'
|
|
2055
|
+
{ name:'Divider', icon:'bi-dash-lg', defaultAccSection:'border', html:'<hr style="margin:16px 0;border:none;border-top:1px solid #e2e8f0">' },
|
|
1918
2056
|
{ 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>' },
|
|
1919
2057
|
{ 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>' },
|
|
1920
2058
|
{ 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>' },
|
|
@@ -2161,6 +2299,8 @@ var currentSectionComponentsTab = 'components';
|
|
|
2161
2299
|
var dragHandleActive = false;
|
|
2162
2300
|
var domTreeCollapsed = {};
|
|
2163
2301
|
var domTreeRefreshTimer = null;
|
|
2302
|
+
var domTreeDragState = null;
|
|
2303
|
+
var iframeLoadErrorCheckTimer = null;
|
|
2164
2304
|
var iframeSyncRetryTimer = null;
|
|
2165
2305
|
var iframeSyncAttempts = 0;
|
|
2166
2306
|
var selectionScrollWin = null;
|
|
@@ -2646,6 +2786,103 @@ function bindViewportControls() {
|
|
|
2646
2786
|
applyViewportFrame();
|
|
2647
2787
|
}
|
|
2648
2788
|
|
|
2789
|
+
function setUrlBarValue(pageUrl) {
|
|
2790
|
+
var urlBar = document.getElementById('url-bar');
|
|
2791
|
+
if (!urlBar) return;
|
|
2792
|
+
var val = pageUrl ? String(pageUrl) : '';
|
|
2793
|
+
urlBar.value = val;
|
|
2794
|
+
urlBar.title = val || 'Enter a page URL and press Enter';
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
function normalizeUserPageUrl(raw) {
|
|
2798
|
+
var s = String(raw || '').trim();
|
|
2799
|
+
if (!s) return '';
|
|
2800
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s)) s = 'https://' + s;
|
|
2801
|
+
try {
|
|
2802
|
+
return new URL(s).toString();
|
|
2803
|
+
} catch(_) {
|
|
2804
|
+
return '';
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
function buildProxyUrlForPageUrl(pageUrl, data) {
|
|
2809
|
+
data = data || experimentData || {};
|
|
2810
|
+
var extraTrackingMarkers = Array.isArray(data.trackingMarkers)
|
|
2811
|
+
? data.trackingMarkers.filter(function(m) { return typeof m === 'string' && m.trim().length > 0; })
|
|
2812
|
+
: [];
|
|
2813
|
+
var conversionProxyBaseUrl = (typeof data.conversionProxyBaseUrl === 'string'
|
|
2814
|
+
? data.conversionProxyBaseUrl
|
|
2815
|
+
: '').trim().replace(/\\/+$/, '');
|
|
2816
|
+
var proxyPath = '/api/conversion-proxy';
|
|
2817
|
+
var shellOrigin = '';
|
|
2818
|
+
try { shellOrigin = window.location.origin || ''; } catch(_) {}
|
|
2819
|
+
var onWorkerOrigin = !!(conversionProxyBaseUrl && shellOrigin === conversionProxyBaseUrl);
|
|
2820
|
+
var proxyRoot = proxyPath;
|
|
2821
|
+
var trackingMarkersParam = extraTrackingMarkers.length
|
|
2822
|
+
? '&trackingMarkers=' + encodeURIComponent(JSON.stringify(extraTrackingMarkers))
|
|
2823
|
+
: '';
|
|
2824
|
+
var conversionProxyBaseUrlParam = (!onWorkerOrigin && conversionProxyBaseUrl)
|
|
2825
|
+
? '&conversionProxyBaseUrl=' + encodeURIComponent(conversionProxyBaseUrl)
|
|
2826
|
+
: '';
|
|
2827
|
+
return proxyRoot + '?password=' + encodeURIComponent(data.editorPassword || '') +
|
|
2828
|
+
'&url=' + encodeURIComponent(pageUrl) +
|
|
2829
|
+
'&strictObserverFreeze=' + encodeURIComponent(data && data.strictObserverFreeze ? '1' : '0') +
|
|
2830
|
+
conversionProxyBaseUrlParam +
|
|
2831
|
+
trackingMarkersParam;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
function navigateToUserPageUrl(rawUrl) {
|
|
2835
|
+
var pageUrl = normalizeUserPageUrl(rawUrl);
|
|
2836
|
+
if (!pageUrl) {
|
|
2837
|
+
showEditorNotification('Enter a valid URL.', 'error', 2600);
|
|
2838
|
+
setUrlBarValue(experimentData && experimentData.pageUrl);
|
|
2839
|
+
return false;
|
|
2840
|
+
}
|
|
2841
|
+
var prevPageUrl = experimentData && experimentData.pageUrl ? String(experimentData.pageUrl) : '';
|
|
2842
|
+
if (prevPageUrl === pageUrl && lastLoadedProxyUrl) {
|
|
2843
|
+
setUrlBarValue(pageUrl);
|
|
2844
|
+
return true;
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
commitStateChangesForActiveVariation();
|
|
2848
|
+
clearPendingGranularChangesets();
|
|
2849
|
+
varHtmlCache = {};
|
|
2850
|
+
sessionStructuralChainRowsByVarId = {};
|
|
2851
|
+
appliedChangesetSnapshots = {};
|
|
2852
|
+
appliedStructuralChangesetKeys = {};
|
|
2853
|
+
deselectElement();
|
|
2854
|
+
|
|
2855
|
+
experimentData = experimentData
|
|
2856
|
+
? Object.assign({}, experimentData, { pageUrl: pageUrl })
|
|
2857
|
+
: { pageUrl: pageUrl };
|
|
2858
|
+
|
|
2859
|
+
setUrlBarValue(pageUrl);
|
|
2860
|
+
showNoUrl(false);
|
|
2861
|
+
loadPage(buildProxyUrlForPageUrl(pageUrl, experimentData));
|
|
2862
|
+
return true;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
function bindUrlBar() {
|
|
2866
|
+
var urlBar = document.getElementById('url-bar');
|
|
2867
|
+
if (!urlBar || urlBar._urlBarBound) return;
|
|
2868
|
+
urlBar._urlBarBound = true;
|
|
2869
|
+
urlBar.addEventListener('keydown', function(e) {
|
|
2870
|
+
if (e.key === 'Enter') {
|
|
2871
|
+
e.preventDefault();
|
|
2872
|
+
navigateToUserPageUrl(urlBar.value);
|
|
2873
|
+
urlBar.blur();
|
|
2874
|
+
} else if (e.key === 'Escape') {
|
|
2875
|
+
e.preventDefault();
|
|
2876
|
+
setUrlBarValue(experimentData && experimentData.pageUrl);
|
|
2877
|
+
urlBar.blur();
|
|
2878
|
+
}
|
|
2879
|
+
});
|
|
2880
|
+
urlBar.addEventListener('blur', function() {
|
|
2881
|
+
var canonical = experimentData && experimentData.pageUrl ? String(experimentData.pageUrl) : '';
|
|
2882
|
+
if (canonical && urlBar.value.trim() !== canonical) urlBar.value = canonical;
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2649
2886
|
// \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
|
|
2650
2887
|
function switchLeftTab(tab) {
|
|
2651
2888
|
if (tab !== 'elements' && tab !== 'dom-tree') return;
|
|
@@ -3880,24 +4117,11 @@ function handleLoadExperiment(data) {
|
|
|
3880
4117
|
var pageUrl = data.pageUrl || '';
|
|
3881
4118
|
if (!pageUrl) {
|
|
3882
4119
|
showNoUrl(true);
|
|
4120
|
+
setUrlBarValue('');
|
|
3883
4121
|
lastLoadedProxyUrl = '';
|
|
3884
4122
|
return;
|
|
3885
4123
|
}
|
|
3886
|
-
var
|
|
3887
|
-
? data.trackingMarkers.filter(function(m){return typeof m === 'string' && m.trim().length > 0;})
|
|
3888
|
-
: [];
|
|
3889
|
-
var conversionProxyBaseUrl = (typeof (data && data.conversionProxyBaseUrl) === 'string'
|
|
3890
|
-
? data.conversionProxyBaseUrl
|
|
3891
|
-
: '').trim().replace(/\\/+$/, '');
|
|
3892
|
-
var proxyPath = '/api/conversion-proxy';
|
|
3893
|
-
var proxyRoot = conversionProxyBaseUrl ? (conversionProxyBaseUrl + proxyPath) : proxyPath;
|
|
3894
|
-
var trackingMarkersParam = extraTrackingMarkers.length
|
|
3895
|
-
? '&trackingMarkers=' + encodeURIComponent(JSON.stringify(extraTrackingMarkers))
|
|
3896
|
-
: '';
|
|
3897
|
-
var proxyUrl = proxyRoot + '?password=' + encodeURIComponent(data.editorPassword || '') +
|
|
3898
|
-
'&url=' + encodeURIComponent(pageUrl) +
|
|
3899
|
-
'&strictObserverFreeze=' + encodeURIComponent(data && data.strictObserverFreeze ? '1' : '0') +
|
|
3900
|
-
trackingMarkersParam;
|
|
4124
|
+
var proxyUrl = buildProxyUrlForPageUrl(pageUrl, data);
|
|
3901
4125
|
|
|
3902
4126
|
// Parent often re-posts load-experiment when React re-renders (new object identity) or
|
|
3903
4127
|
// after mutations-changed. Reloading the iframe again wipes variant changesets mid-session.
|
|
@@ -3915,9 +4139,7 @@ function handleLoadExperiment(data) {
|
|
|
3915
4139
|
loadStateChangesForActiveVariation();
|
|
3916
4140
|
writePersistedActiveVariationId(activeVarId);
|
|
3917
4141
|
renderVariationTabs();
|
|
3918
|
-
|
|
3919
|
-
urlBarSkip.textContent = pageUrl;
|
|
3920
|
-
urlBarSkip.title = pageUrl;
|
|
4142
|
+
setUrlBarValue(pageUrl);
|
|
3921
4143
|
try {
|
|
3922
4144
|
applyActiveVariationHtml();
|
|
3923
4145
|
syncIframeInteractions('load-experiment-skip-url');
|
|
@@ -3948,9 +4170,7 @@ function handleLoadExperiment(data) {
|
|
|
3948
4170
|
writePersistedActiveVariationId(activeVarId);
|
|
3949
4171
|
renderVariationTabs();
|
|
3950
4172
|
|
|
3951
|
-
|
|
3952
|
-
urlBar.textContent = pageUrl;
|
|
3953
|
-
urlBar.title = pageUrl;
|
|
4173
|
+
setUrlBarValue(pageUrl);
|
|
3954
4174
|
if (currentMainTab === 'history') renderHistoryTab();
|
|
3955
4175
|
captureBaselineFromVariations(variations);
|
|
3956
4176
|
recomputeEditorDirty();
|
|
@@ -3959,12 +4179,128 @@ function handleLoadExperiment(data) {
|
|
|
3959
4179
|
|
|
3960
4180
|
function showNoUrl(show) {
|
|
3961
4181
|
document.getElementById('no-url').style.display = show ? 'flex' : 'none';
|
|
4182
|
+
var urlBar = document.getElementById('url-bar');
|
|
4183
|
+
if (urlBar) urlBar.disabled = !!show;
|
|
3962
4184
|
if (show) {
|
|
4185
|
+
hideIframeLoadError();
|
|
3963
4186
|
detachIframeLoadingListeners();
|
|
3964
4187
|
setIframePageLoadingUi(false);
|
|
3965
4188
|
}
|
|
3966
4189
|
}
|
|
3967
4190
|
|
|
4191
|
+
function hideIframeLoadError() {
|
|
4192
|
+
if (iframeLoadErrorCheckTimer) {
|
|
4193
|
+
clearTimeout(iframeLoadErrorCheckTimer);
|
|
4194
|
+
iframeLoadErrorCheckTimer = null;
|
|
4195
|
+
}
|
|
4196
|
+
var el = document.getElementById('iframe-load-error');
|
|
4197
|
+
if (el) el.style.display = 'none';
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
function showIframeLoadError(message) {
|
|
4201
|
+
if (iframeLoadErrorCheckTimer) {
|
|
4202
|
+
clearTimeout(iframeLoadErrorCheckTimer);
|
|
4203
|
+
iframeLoadErrorCheckTimer = null;
|
|
4204
|
+
}
|
|
4205
|
+
var el = document.getElementById('iframe-load-error');
|
|
4206
|
+
var msgEl = document.getElementById('iframe-load-error-msg');
|
|
4207
|
+
if (msgEl) msgEl.textContent = message || 'Check the URL and try again.';
|
|
4208
|
+
if (el) el.style.display = 'flex';
|
|
4209
|
+
setIframePageLoadingUi(false);
|
|
4210
|
+
detachIframeLoadingListeners();
|
|
4211
|
+
deselectElement({ preserveMainTab: true });
|
|
4212
|
+
}
|
|
4213
|
+
|
|
4214
|
+
function detectIframeLoadFailure(iframe, doc) {
|
|
4215
|
+
if (!iframe || !iframe.src || iframe.src === 'about:blank') return null;
|
|
4216
|
+
|
|
4217
|
+
if (!doc) {
|
|
4218
|
+
return 'Unable to load this page. The connection was refused or blocked.';
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
var docUrl = '';
|
|
4222
|
+
try { docUrl = String(doc.URL || ''); } catch(_) {}
|
|
4223
|
+
if (docUrl === 'about:blank') {
|
|
4224
|
+
return 'This page could not be loaded. Check the URL and try again.';
|
|
4225
|
+
}
|
|
4226
|
+
if (
|
|
4227
|
+
docUrl.indexOf('chrome-error://') === 0 ||
|
|
4228
|
+
docUrl.indexOf('about:neterror') === 0
|
|
4229
|
+
) {
|
|
4230
|
+
return 'This page could not be loaded. Check the URL and try again.';
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
var bodyText = '';
|
|
4234
|
+
try {
|
|
4235
|
+
bodyText = doc.body && doc.body.innerText ? String(doc.body.innerText).trim() : '';
|
|
4236
|
+
} catch(_) {}
|
|
4237
|
+
|
|
4238
|
+
var bodyLower = bodyText.toLowerCase();
|
|
4239
|
+
if (
|
|
4240
|
+
bodyLower.indexOf('refused to connect') >= 0 ||
|
|
4241
|
+
bodyLower.indexOf('err_connection_refused') >= 0 ||
|
|
4242
|
+
bodyLower.indexOf('err_name_not_resolved') >= 0 ||
|
|
4243
|
+
bodyLower.indexOf('err_ssl') >= 0 ||
|
|
4244
|
+
bodyLower.indexOf('err_connection_timed_out') >= 0 ||
|
|
4245
|
+
bodyLower.indexOf("this site can't be reached") >= 0 ||
|
|
4246
|
+
bodyLower.indexOf('this site cant be reached') >= 0 ||
|
|
4247
|
+
bodyLower.indexOf('unable to connect') >= 0 ||
|
|
4248
|
+
bodyLower.indexOf("page isn't working") >= 0 ||
|
|
4249
|
+
bodyLower.indexOf('404 not found') >= 0 && bodyLower.length < 280
|
|
4250
|
+
) {
|
|
4251
|
+
return 'This page could not be loaded. Check the URL and try again.';
|
|
4252
|
+
}
|
|
4253
|
+
|
|
4254
|
+
if (bodyText.charAt(0) === '{') {
|
|
4255
|
+
try {
|
|
4256
|
+
var parsed = JSON.parse(bodyText);
|
|
4257
|
+
if (parsed && parsed.error) return String(parsed.error);
|
|
4258
|
+
} catch(_) {}
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4261
|
+
try {
|
|
4262
|
+
var ct = doc.contentType || '';
|
|
4263
|
+
if (String(ct).indexOf('application/json') >= 0 && bodyText.charAt(0) === '{') {
|
|
4264
|
+
var parsedCt = JSON.parse(bodyText);
|
|
4265
|
+
if (parsedCt && parsedCt.error) return String(parsedCt.error);
|
|
4266
|
+
}
|
|
4267
|
+
} catch(_) {}
|
|
4268
|
+
|
|
4269
|
+
return null;
|
|
4270
|
+
}
|
|
4271
|
+
|
|
4272
|
+
function scheduleIframeLoadErrorCheck(iframe) {
|
|
4273
|
+
if (iframeLoadErrorCheckTimer) clearTimeout(iframeLoadErrorCheckTimer);
|
|
4274
|
+
iframeLoadErrorCheckTimer = setTimeout(function() {
|
|
4275
|
+
iframeLoadErrorCheckTimer = null;
|
|
4276
|
+
if (!iframe || !iframe.src || iframe.src === 'about:blank') return;
|
|
4277
|
+
var doc = null;
|
|
4278
|
+
try { doc = iframe.contentDocument; } catch(_) {}
|
|
4279
|
+
var failMsg = detectIframeLoadFailure(iframe, doc);
|
|
4280
|
+
if (failMsg) showIframeLoadError(failMsg);
|
|
4281
|
+
}, 450);
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
function reloadCurrentPage() {
|
|
4285
|
+
hideIframeLoadError();
|
|
4286
|
+
if (lastLoadedProxyUrl) {
|
|
4287
|
+
loadPage(lastLoadedProxyUrl);
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
var pageUrl = experimentData && experimentData.pageUrl;
|
|
4291
|
+
if (pageUrl) navigateToUserPageUrl(pageUrl);
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
function bindIframeLoadErrorControls() {
|
|
4295
|
+
var btn = document.getElementById('iframe-load-error-reload');
|
|
4296
|
+
if (!btn || btn._iframeLoadErrorBound) return;
|
|
4297
|
+
btn._iframeLoadErrorBound = true;
|
|
4298
|
+
btn.addEventListener('click', function(e) {
|
|
4299
|
+
e.preventDefault();
|
|
4300
|
+
reloadCurrentPage();
|
|
4301
|
+
});
|
|
4302
|
+
}
|
|
4303
|
+
|
|
3968
4304
|
function detachIframeLoadingListeners() {
|
|
3969
4305
|
if (!iframeDocLoadingListeners) return;
|
|
3970
4306
|
try {
|
|
@@ -4355,6 +4691,7 @@ function emitEditorUrlChanged(rawUrl) {
|
|
|
4355
4691
|
}
|
|
4356
4692
|
|
|
4357
4693
|
function loadPage(proxyUrl) {
|
|
4694
|
+
hideIframeLoadError();
|
|
4358
4695
|
showNoUrl(false);
|
|
4359
4696
|
lastLoadedProxyUrl = proxyUrl;
|
|
4360
4697
|
var navGen = nextIframeContentNavGen();
|
|
@@ -5519,6 +5856,149 @@ function setDomTreeStatus(mode) {
|
|
|
5519
5856
|
}
|
|
5520
5857
|
}
|
|
5521
5858
|
|
|
5859
|
+
function canDomTreeMoveElement(el) {
|
|
5860
|
+
if (!el || el.nodeType !== 1 || !el.parentElement) return false;
|
|
5861
|
+
var tag = (el.tagName || '').toLowerCase();
|
|
5862
|
+
if (tag === 'html' || tag === 'body') return false;
|
|
5863
|
+
try {
|
|
5864
|
+
var doc = el.ownerDocument;
|
|
5865
|
+
if (doc && (el === doc.documentElement || el === doc.body)) return false;
|
|
5866
|
+
} catch(_) {}
|
|
5867
|
+
return true;
|
|
5868
|
+
}
|
|
5869
|
+
|
|
5870
|
+
function canDomTreeDropRelativeTo(dragEl, targetEl) {
|
|
5871
|
+
if (!canDomTreeMoveElement(dragEl) || !targetEl || targetEl.nodeType !== 1) return false;
|
|
5872
|
+
if (dragEl === targetEl) return false;
|
|
5873
|
+
try {
|
|
5874
|
+
if (dragEl.contains(targetEl) || targetEl.contains(dragEl)) return false;
|
|
5875
|
+
} catch(_) {
|
|
5876
|
+
return false;
|
|
5877
|
+
}
|
|
5878
|
+
var parent = targetEl.parentElement;
|
|
5879
|
+
if (!parent) return false;
|
|
5880
|
+
if (isDomTreeSkippableTagName(parent.tagName)) return false;
|
|
5881
|
+
return true;
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
function moveDomTreeElementRelativeTo(dragEl, targetEl, insertBefore) {
|
|
5885
|
+
if (!canDomTreeDropRelativeTo(dragEl, targetEl)) return false;
|
|
5886
|
+
var parent = targetEl.parentElement;
|
|
5887
|
+
if (!parent) return false;
|
|
5888
|
+
var ref = insertBefore ? targetEl : targetEl.nextSibling;
|
|
5889
|
+
if (dragEl.parentElement === parent && dragEl === ref) return false;
|
|
5890
|
+
if (!insertBefore && dragEl.parentElement === parent && dragEl === targetEl.nextSibling) return false;
|
|
5891
|
+
try {
|
|
5892
|
+
parent.insertBefore(dragEl, ref);
|
|
5893
|
+
return true;
|
|
5894
|
+
} catch(_) {
|
|
5895
|
+
return false;
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
|
|
5899
|
+
function clearDomTreeDropIndicators() {
|
|
5900
|
+
var root = document.getElementById('dom-tree-root');
|
|
5901
|
+
if (!root) return;
|
|
5902
|
+
var rows = root.querySelectorAll('.dt-row');
|
|
5903
|
+
for (var i = 0; i < rows.length; i++) {
|
|
5904
|
+
rows[i].classList.remove('dt-drop-before', 'dt-drop-after', 'dt-drop-invalid', 'dt-dragging');
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
|
|
5908
|
+
function cleanupDomTreeDrag() {
|
|
5909
|
+
clearDomTreeDropIndicators();
|
|
5910
|
+
domTreeDragState = null;
|
|
5911
|
+
}
|
|
5912
|
+
|
|
5913
|
+
function attachDomTreeDragHandlers() {
|
|
5914
|
+
var root = document.getElementById('dom-tree-root');
|
|
5915
|
+
if (!root || root._domTreeDragBound) return;
|
|
5916
|
+
root._domTreeDragBound = true;
|
|
5917
|
+
|
|
5918
|
+
root.addEventListener('dragstart', function(e) {
|
|
5919
|
+
if (currentMode !== 'editor') {
|
|
5920
|
+
e.preventDefault();
|
|
5921
|
+
return;
|
|
5922
|
+
}
|
|
5923
|
+
var row = e.target && e.target.closest ? e.target.closest('.dt-row') : null;
|
|
5924
|
+
if (!row || !row._dtEl || (e.target.closest && e.target.closest('.dt-chev'))) {
|
|
5925
|
+
e.preventDefault();
|
|
5926
|
+
return;
|
|
5927
|
+
}
|
|
5928
|
+
if (!canDomTreeMoveElement(row._dtEl)) {
|
|
5929
|
+
e.preventDefault();
|
|
5930
|
+
return;
|
|
5931
|
+
}
|
|
5932
|
+
domTreeDragState = { el: row._dtEl, row: row };
|
|
5933
|
+
row.classList.add('dt-dragging');
|
|
5934
|
+
try {
|
|
5935
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
5936
|
+
e.dataTransfer.setData('text/plain', 'dom-tree');
|
|
5937
|
+
} catch(_) {}
|
|
5938
|
+
suppressClickUntil = Date.now() + 300;
|
|
5939
|
+
});
|
|
5940
|
+
|
|
5941
|
+
root.addEventListener('dragover', function(e) {
|
|
5942
|
+
if (!domTreeDragState) return;
|
|
5943
|
+
e.preventDefault();
|
|
5944
|
+
clearDomTreeDropIndicators();
|
|
5945
|
+
if (domTreeDragState.row) domTreeDragState.row.classList.add('dt-dragging');
|
|
5946
|
+
|
|
5947
|
+
var row = e.target && e.target.closest ? e.target.closest('.dt-row') : null;
|
|
5948
|
+
if (!row || !row._dtEl) {
|
|
5949
|
+
try { e.dataTransfer.dropEffect = 'none'; } catch(_) {}
|
|
5950
|
+
return;
|
|
5951
|
+
}
|
|
5952
|
+
|
|
5953
|
+
var dragEl = domTreeDragState.el;
|
|
5954
|
+
var targetEl = row._dtEl;
|
|
5955
|
+
if (!canDomTreeDropRelativeTo(dragEl, targetEl)) {
|
|
5956
|
+
row.classList.add('dt-drop-invalid');
|
|
5957
|
+
try { e.dataTransfer.dropEffect = 'none'; } catch(_) {}
|
|
5958
|
+
return;
|
|
5959
|
+
}
|
|
5960
|
+
|
|
5961
|
+
var rect = row.getBoundingClientRect();
|
|
5962
|
+
var insertBefore = e.clientY < rect.top + rect.height / 2;
|
|
5963
|
+
row.classList.add(insertBefore ? 'dt-drop-before' : 'dt-drop-after');
|
|
5964
|
+
domTreeDragState.dropRow = row;
|
|
5965
|
+
domTreeDragState.insertBefore = insertBefore;
|
|
5966
|
+
try { e.dataTransfer.dropEffect = 'move'; } catch(_) {}
|
|
5967
|
+
});
|
|
5968
|
+
|
|
5969
|
+
root.addEventListener('drop', function(e) {
|
|
5970
|
+
e.preventDefault();
|
|
5971
|
+
if (!domTreeDragState) return;
|
|
5972
|
+
|
|
5973
|
+
var dragEl = domTreeDragState.el;
|
|
5974
|
+
var row = domTreeDragState.dropRow || (e.target && e.target.closest ? e.target.closest('.dt-row') : null);
|
|
5975
|
+
var moved = false;
|
|
5976
|
+
if (row && row._dtEl && canDomTreeDropRelativeTo(dragEl, row._dtEl)) {
|
|
5977
|
+
var insertBefore = domTreeDragState.insertBefore;
|
|
5978
|
+
if (insertBefore === undefined) {
|
|
5979
|
+
var rect = row.getBoundingClientRect();
|
|
5980
|
+
insertBefore = e.clientY < rect.top + rect.height / 2;
|
|
5981
|
+
}
|
|
5982
|
+
if (moveDomTreeElementRelativeTo(dragEl, row._dtEl, insertBefore)) {
|
|
5983
|
+
moved = true;
|
|
5984
|
+
if (activeVarId) recordReorderAfterDrag(dragEl);
|
|
5985
|
+
saveCurrentVariationHtml();
|
|
5986
|
+
recomputeEditorDirty();
|
|
5987
|
+
selectElement(dragEl);
|
|
5988
|
+
scrollIframeElementIntoView(dragEl);
|
|
5989
|
+
updateSelectionToolbar();
|
|
5990
|
+
scheduleDomTreeRefresh();
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
cleanupDomTreeDrag();
|
|
5994
|
+
if (moved) suppressClickUntil = Date.now() + 300;
|
|
5995
|
+
});
|
|
5996
|
+
|
|
5997
|
+
root.addEventListener('dragend', function() {
|
|
5998
|
+
cleanupDomTreeDrag();
|
|
5999
|
+
});
|
|
6000
|
+
}
|
|
6001
|
+
|
|
5522
6002
|
function domTreePathSegment(el) {
|
|
5523
6003
|
var tag = el.tagName.toLowerCase();
|
|
5524
6004
|
var idx = 1;
|
|
@@ -5897,6 +6377,7 @@ function renderDomTree(filterRaw) {
|
|
|
5897
6377
|
row.onmouseenter = function() {
|
|
5898
6378
|
setTreeHoverHighlight(el);
|
|
5899
6379
|
};
|
|
6380
|
+
if (canDomTreeMoveElement(el)) row.draggable = true;
|
|
5900
6381
|
root.appendChild(row);
|
|
5901
6382
|
|
|
5902
6383
|
if (!hasKids || collapsed) return;
|
|
@@ -5918,6 +6399,7 @@ function renderDomTree(filterRaw) {
|
|
|
5918
6399
|
root.onmouseleave = function() {
|
|
5919
6400
|
clearTreeHoverHighlight();
|
|
5920
6401
|
};
|
|
6402
|
+
attachDomTreeDragHandlers();
|
|
5921
6403
|
}
|
|
5922
6404
|
|
|
5923
6405
|
// \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
|
|
@@ -6346,7 +6828,7 @@ function renderVideoSection(el) {
|
|
|
6346
6828
|
}
|
|
6347
6829
|
|
|
6348
6830
|
html += pr('Src', '<input class="pr-inp" id="pp-video-src" type="url" value="'+esc(src)+'" placeholder="https://example.com/video.mp4">');
|
|
6349
|
-
html += pr('
|
|
6831
|
+
html += pr('Thumbnail', '<input class="pr-inp" id="pp-video-poster" type="url" value="'+esc(el.getAttribute('poster')||'')+'" placeholder="https://\u2026">');
|
|
6350
6832
|
|
|
6351
6833
|
document.getElementById('acc-body-video').innerHTML = html;
|
|
6352
6834
|
|
|
@@ -6613,11 +7095,6 @@ function renderRightPanel(el, options) {
|
|
|
6613
7095
|
// \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
|
|
6614
7096
|
var contentHtml = '';
|
|
6615
7097
|
if (isLinkElement(el)) contentHtml += buildAnchorContentFieldsHtml(el);
|
|
6616
|
-
if (isFormControlElement(el)) {
|
|
6617
|
-
contentHtml +=
|
|
6618
|
-
subLbl('Value') +
|
|
6619
|
-
'<textarea class="pr-inp" id="pp-value" style="width:100%;min-height:44px;margin-bottom:8px">'+esc(getFormControlValue(el))+'</textarea>';
|
|
6620
|
-
}
|
|
6621
7098
|
if (tag==='input' || tag==='textarea') {
|
|
6622
7099
|
contentHtml +=
|
|
6623
7100
|
subLbl('Placeholder') +
|
|
@@ -6730,17 +7207,13 @@ function renderRightPanel(el, options) {
|
|
|
6730
7207
|
function wireContentFieldSync(el, sel) {
|
|
6731
7208
|
var textInp = document.getElementById('pp-text');
|
|
6732
7209
|
var htmlInp = document.getElementById('pp-html');
|
|
6733
|
-
var valueInp = document.getElementById('pp-value');
|
|
6734
7210
|
var syncing = false;
|
|
6735
7211
|
function applyContentChange(changedId) {
|
|
6736
7212
|
if (syncing) return;
|
|
6737
7213
|
syncing = true;
|
|
6738
7214
|
try {
|
|
6739
7215
|
var orig = getOriginalValue(changedId, el);
|
|
6740
|
-
if (changedId === 'pp-
|
|
6741
|
-
el.value = valueInp.value;
|
|
6742
|
-
logChange(sel, 'pp-value', valueInp.value, el, orig);
|
|
6743
|
-
} else if (changedId === 'pp-text') {
|
|
7216
|
+
if (changedId === 'pp-text') {
|
|
6744
7217
|
el.innerText = textInp.value;
|
|
6745
7218
|
if (htmlInp) htmlInp.value = el.innerHTML;
|
|
6746
7219
|
logChange(sel, 'pp-text', textInp.value, el, orig);
|
|
@@ -6753,10 +7226,6 @@ function wireContentFieldSync(el, sel) {
|
|
|
6753
7226
|
syncing = false;
|
|
6754
7227
|
}
|
|
6755
7228
|
}
|
|
6756
|
-
if (valueInp && isFormControlElement(el)) {
|
|
6757
|
-
valueInp.addEventListener('input', function() { applyContentChange('pp-value'); });
|
|
6758
|
-
valueInp.addEventListener('change', function() { applyContentChange('pp-value'); });
|
|
6759
|
-
}
|
|
6760
7229
|
if (textInp) {
|
|
6761
7230
|
textInp.addEventListener('input', function() { applyContentChange('pp-text'); });
|
|
6762
7231
|
textInp.addEventListener('change', function() { applyContentChange('pp-text'); });
|
|
@@ -7717,6 +8186,8 @@ function registerCROSections() {
|
|
|
7717
8186
|
window.addEventListener('load', function() {
|
|
7718
8187
|
registerCROSections();
|
|
7719
8188
|
bindViewportControls();
|
|
8189
|
+
bindUrlBar();
|
|
8190
|
+
bindIframeLoadErrorControls();
|
|
7720
8191
|
// switchSectionComponentsTab(currentSectionComponentsTab);
|
|
7721
8192
|
renderElementsTree(document.getElementById('comp-search').value);
|
|
7722
8193
|
vvvebReady = true;
|
|
@@ -7750,17 +8221,24 @@ window.addEventListener('load', function() {
|
|
|
7750
8221
|
var doc = iframe.contentDocument;
|
|
7751
8222
|
if (!doc) {
|
|
7752
8223
|
resetIframeBindings();
|
|
7753
|
-
|
|
8224
|
+
showIframeLoadError('Unable to load this page. The connection was refused or blocked.');
|
|
7754
8225
|
return;
|
|
7755
8226
|
}
|
|
7756
8227
|
var docUrl = '';
|
|
7757
8228
|
try { docUrl = String(doc.URL || ''); } catch(_) {}
|
|
8229
|
+
var immediateFail = detectIframeLoadFailure(iframe, doc);
|
|
8230
|
+
if (immediateFail) {
|
|
8231
|
+
showIframeLoadError(immediateFail);
|
|
8232
|
+
return;
|
|
8233
|
+
}
|
|
8234
|
+
hideIframeLoadError();
|
|
7758
8235
|
emitEditorUrlChanged(iframe.src || docUrl);
|
|
7759
8236
|
// Stale events: src may already be the proxy URL while the document is still
|
|
7760
8237
|
// about:blank (e.g. src cleared then reset to force reload). Ask sync path to retry.
|
|
7761
8238
|
if (docUrl === 'about:blank') {
|
|
7762
8239
|
resetIframeBindings();
|
|
7763
8240
|
syncIframeInteractions('iframe-load-about-blank');
|
|
8241
|
+
scheduleIframeLoadErrorCheck(iframe);
|
|
7764
8242
|
return;
|
|
7765
8243
|
}
|
|
7766
8244
|
// If early-paint and final load DOM signatures match, avoid a second full apply/reset
|
|
@@ -7788,6 +8266,7 @@ window.addEventListener('load', function() {
|
|
|
7788
8266
|
}
|
|
7789
8267
|
// Always attempt sync; it has its own readiness checks + retry loop.
|
|
7790
8268
|
syncIframeInteractions('iframe-load');
|
|
8269
|
+
scheduleIframeLoadErrorCheck(iframe);
|
|
7791
8270
|
});
|
|
7792
8271
|
|
|
7793
8272
|
var sfAdd = document.getElementById('sf-add');
|
|
@@ -8762,5 +9241,6 @@ function visualEditorProxyPlugin(options) {
|
|
|
8762
9241
|
};
|
|
8763
9242
|
}
|
|
8764
9243
|
|
|
9244
|
+
exports.buildVvvebEditorHtml = buildVvvebEditorHtml;
|
|
8765
9245
|
exports.createVisualEditorMiddleware = createVisualEditorMiddleware;
|
|
8766
9246
|
exports.visualEditorProxyPlugin = visualEditorProxyPlugin;
|