145 lines
4.0 KiB
JavaScript
145 lines
4.0 KiB
JavaScript
/**
|
|
* Vibn preview element picker — load inside the previewed app (same or cross-origin).
|
|
* Parent verifies event.origin; iframe may postMessage with targetOrigin '*'.
|
|
*/
|
|
(function () {
|
|
var NS = "vibn-preview";
|
|
var enabled = false;
|
|
var overlay = null;
|
|
var lastEl = null;
|
|
|
|
function esc(s) {
|
|
if (typeof CSS !== "undefined" && CSS.escape) return CSS.escape(s);
|
|
return String(s).replace(/[^a-zA-Z0-9_-]/g, "\\$&");
|
|
}
|
|
|
|
function shortSelector(el) {
|
|
if (!el || el.nodeType !== 1) return "(unknown)";
|
|
if (el.id) return "#" + esc(el.id);
|
|
var parts = [];
|
|
var cur = el;
|
|
var depth = 0;
|
|
while (cur && cur.nodeType === 1 && depth < 6) {
|
|
var tag = cur.tagName.toLowerCase();
|
|
var cls = cur.className && typeof cur.className === "string"
|
|
? cur.className.trim().split(/\s+/).slice(0, 2).join(".")
|
|
: "";
|
|
parts.unshift(cls ? tag + "." + cls.split(".").map(esc).join(".") : tag);
|
|
cur = cur.parentElement;
|
|
depth++;
|
|
}
|
|
return parts.join(" > ");
|
|
}
|
|
|
|
function snippetText(el, max) {
|
|
max = max || 120;
|
|
var t = (el.innerText || "").trim().replace(/\s+/g, " ");
|
|
return t.length > max ? t.slice(0, max - 1) + "…" : t;
|
|
}
|
|
|
|
function outerSnip(el, max) {
|
|
max = max || 400;
|
|
try {
|
|
var html = el.outerHTML || "";
|
|
return html.length > max ? html.slice(0, max - 1) + "…" : html;
|
|
} catch (e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function ensureOverlay() {
|
|
if (overlay) return overlay;
|
|
overlay = document.createElement("div");
|
|
overlay.setAttribute("data-vibn-overlay", "1");
|
|
overlay.style.cssText =
|
|
"pointer-events:none;position:fixed;z-index:2147483646;box-sizing:border-box;border:2px solid #4f46e5;border-radius:6px;background:rgba(79,70,229,0.12);display:none;transition:top .05s,left .05s,width .05s,height .05s;";
|
|
document.documentElement.appendChild(overlay);
|
|
return overlay;
|
|
}
|
|
|
|
function hideOverlay() {
|
|
if (overlay) overlay.style.display = "none";
|
|
lastEl = null;
|
|
}
|
|
|
|
function moveOverlay(el) {
|
|
if (!el || el.nodeType !== 1 || el === overlay || el.contains(overlay)) {
|
|
hideOverlay();
|
|
return;
|
|
}
|
|
lastEl = el;
|
|
var r = el.getBoundingClientRect();
|
|
var o = ensureOverlay();
|
|
o.style.display = "block";
|
|
o.style.top = r.top + "px";
|
|
o.style.left = r.left + "px";
|
|
o.style.width = r.width + "px";
|
|
o.style.height = r.height + "px";
|
|
}
|
|
|
|
window.addEventListener(
|
|
"message",
|
|
function (ev) {
|
|
var d = ev.data;
|
|
if (!d || d.source !== NS) return;
|
|
if (d.type === "CMD") {
|
|
enabled = d.cmd === "enable-select";
|
|
document.body.style.cursor = enabled ? "crosshair" : "";
|
|
if (!enabled) hideOverlay();
|
|
}
|
|
},
|
|
false,
|
|
);
|
|
|
|
document.addEventListener(
|
|
"mousemove",
|
|
function (e) {
|
|
if (!enabled) return;
|
|
var el = document.elementFromPoint(e.clientX, e.clientY);
|
|
if (!el || el === overlay || (overlay && overlay.contains(el))) return;
|
|
moveOverlay(el);
|
|
},
|
|
true,
|
|
);
|
|
|
|
document.addEventListener(
|
|
"click",
|
|
function (e) {
|
|
if (!enabled) return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
|
|
var el = lastEl || document.elementFromPoint(e.clientX, e.clientY);
|
|
if (!el || el === overlay) return;
|
|
var payload = {
|
|
selector: shortSelector(el),
|
|
tagName: el.tagName.toLowerCase(),
|
|
textSnippet: snippetText(el, 200),
|
|
outerHtmlSnippet: outerSnip(el, 500),
|
|
};
|
|
enabled = false;
|
|
document.body.style.cursor = "";
|
|
hideOverlay();
|
|
window.parent.postMessage({ source: NS, type: "PICK", payload: payload }, "*");
|
|
},
|
|
true,
|
|
);
|
|
|
|
window.addEventListener(
|
|
"scroll",
|
|
function () {
|
|
if (enabled && lastEl) moveOverlay(lastEl);
|
|
},
|
|
true,
|
|
);
|
|
|
|
try {
|
|
window.parent.postMessage(
|
|
{ source: NS, type: "BRIDGE_READY", payload: { origin: window.location.origin } },
|
|
"*",
|
|
);
|
|
} catch (e) {
|
|
/* ignore */
|
|
}
|
|
})();
|