-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStudioLayerBridge.html
More file actions
137 lines (130 loc) · 6.04 KB
/
Copy pathStudioLayerBridge.html
File metadata and controls
137 lines (130 loc) · 6.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<script>
(function () {
'use strict';
var frame = document.getElementById('previewFrame');
var root = document.documentElement;
var MARKER = '__unscriptly_layer_sync_bridge__';
function setStatus(value) { root.setAttribute('data-html-studio-layer-bridge', value); }
function previewLayerSyncBridge() {
var SOURCE = 'unscriptly-html-studio';
var hoverSheet = null;
var fallbackTarget = null;
var fallbackStyle = null;
function post(type, payload) { parent.postMessage(Object.assign({ source: SOURCE, type: type }, payload || {}), '*'); }
function pathKey(parts) { return parts && parts.length ? parts.join('.') : 'body'; }
function structuralPath(el) {
if (!el || !document.body) return '';
if (el === document.body) return 'body';
var parts = [], cursor = el;
while (cursor && cursor !== document.body) {
var parentEl = cursor.parentElement;
if (!parentEl) return '';
var index = Array.prototype.indexOf.call(parentEl.children, cursor);
if (index < 0) return '';
parts.unshift(index); cursor = parentEl;
}
return cursor === document.body ? pathKey(parts) : '';
}
function pathArray(value) {
if (!value || value === 'body') return [];
return String(value).split('.').filter(Boolean).map(function (part) { return parseInt(part, 10); });
}
function resolvePath(value) {
var node = document.body, parts = pathArray(value);
for (var i = 0; i < parts.length; i++) {
if (!node || !node.children || !node.children[parts[i]]) return null;
node = node.children[parts[i]];
}
return node;
}
function cssEscape(value) {
if (window.CSS && typeof window.CSS.escape === 'function') return window.CSS.escape(String(value));
return String(value).replace(/([\\"'])/g, '\\$1');
}
function ensureSheet() {
if (hoverSheet) return hoverSheet;
try {
if (window.CSSStyleSheet && 'adoptedStyleSheets' in document) {
hoverSheet = new CSSStyleSheet();
hoverSheet.replaceSync('');
document.adoptedStyleSheets = Array.prototype.slice.call(document.adoptedStyleSheets || []).concat([hoverSheet]);
}
} catch (_) { hoverSheet = null; }
return hoverSheet;
}
function clearFallback() {
if (!fallbackTarget || !fallbackStyle) return;
fallbackTarget.style.outline = fallbackStyle.outline;
fallbackTarget.style.outlineOffset = fallbackStyle.outlineOffset;
fallbackTarget.style.boxShadow = fallbackStyle.boxShadow;
fallbackTarget = null; fallbackStyle = null;
}
function clearHover() {
if (hoverSheet) { try { hoverSheet.replaceSync(''); } catch (_) {} }
clearFallback();
}
function setHover(path) {
clearHover();
var target = resolvePath(path);
if (!target) return;
var editorId = target.getAttribute('data-unscriptly-editor-id');
var sheet = ensureSheet();
if (sheet && editorId) {
var selector = '[data-unscriptly-editor-id="' + cssEscape(editorId) + '"]';
sheet.replaceSync(selector + '{outline:2px dashed rgba(17,17,17,.88)!important;outline-offset:4px!important;box-shadow:0 0 0 3px rgba(255,255,255,.72),0 0 0 5px rgba(17,17,17,.18)!important;}');
return;
}
fallbackTarget = target;
fallbackStyle = { outline: target.style.outline, outlineOffset: target.style.outlineOffset, boxShadow: target.style.boxShadow };
target.style.outline = '2px dashed rgba(17,17,17,.88)';
target.style.outlineOffset = '4px';
target.style.boxShadow = '0 0 0 3px rgba(255,255,255,.72),0 0 0 5px rgba(17,17,17,.18)';
}
window.addEventListener('message', function (event) {
var msg = event.data || {};
if (msg.source !== SOURCE) return;
if (msg.type === 'NCE_LAYER_HOVER_PATH') return setHover(msg.path || '');
if (msg.type === 'NCE_LAYER_HOVER_CLEAR') return clearHover();
if (msg.type === 'NCE_LAYER_REVEAL_PATH') {
var target = resolvePath(msg.path || '');
if (target && target.scrollIntoView) target.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
}
});
document.addEventListener('click', function (event) {
var target = event.target && event.target.closest ? event.target.closest('[data-unscriptly-editor-id]') : null;
if (!target || !document.body.contains(target)) return;
var path = structuralPath(target);
if (path) post('NCE_LAYER_SELECTED_PATH', { path: path });
}, true);
window.addEventListener('blur', clearHover);
post('NCE_LAYER_BRIDGE_READY');
/* Keep synchronization preview-only. Removing the loader script prevents
the core serializer from ever copying this bridge into exported HTML. */
try {
var loader = document.currentScript;
if (loader && loader.parentNode) loader.parentNode.removeChild(loader);
} catch (_) {}
}
try {
if (!frame || !window.HTMLIFrameElement) throw new Error('Preview iframe is unavailable.');
var descriptor = Object.getOwnPropertyDescriptor(window.HTMLIFrameElement.prototype, 'srcdoc');
if (!descriptor || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') throw new Error('This browser does not expose the iframe srcdoc accessor.');
function injectBridge(html) {
var text = String(html == null ? '' : html);
if (text.indexOf(MARKER) >= 0) return text;
var payload = '<script id="' + MARKER + '">(' + previewLayerSyncBridge.toString() + ')();<\/script>';
var index = text.toLowerCase().lastIndexOf('</body>');
return index >= 0 ? text.slice(0, index) + payload + text.slice(index) : text + payload;
}
Object.defineProperty(frame, 'srcdoc', {
configurable: true, enumerable: true,
get: function () { return descriptor.get.call(frame); },
set: function (value) { descriptor.set.call(frame, injectBridge(value)); }
});
setStatus('ready');
} catch (error) {
console.error('[HTML Studio] Layer preview bridge failed:', error);
setStatus('error');
}
})();
</script>