@viamedici-spc/configurator-framer-host
Version:
Provides a Web Component for host applications to embed a Framer based Viamedici SPC configurator.
181 lines (180 loc) • 8.38 kB
JavaScript
function v(s) {
var e = [];
if (s.length === 0)
return "";
if (typeof s[0] != "string")
throw new TypeError("Url must be a string. Received " + s[0]);
if (s[0].match(/^[^/:]+:\/*$/) && s.length > 1) {
var o = s.shift();
s[0] = o + s[0];
}
s[0].match(/^file:\/\/\//) ? s[0] = s[0].replace(/^([^/:]+):\/*/, "$1:///") : s[0] = s[0].replace(/^([^/:]+):\/*/, "$1://");
for (var t = 0; t < s.length; t++) {
var r = s[t];
if (typeof r != "string")
throw new TypeError("Url must be a string. Received " + r);
r !== "" && (t > 0 && (r = r.replace(/^[\/]+/, "")), t < s.length - 1 ? r = r.replace(/[\/]+$/, "") : r = r.replace(/[\/]+$/, "/"), e.push(r));
}
var n = e.join("/");
n = n.replace(/\/(\?|&|#[^!])/g, "$1");
var a = n.split("?");
return n = a.shift() + (a.length > 0 ? "?" : "") + a.join("&"), n;
}
function x() {
var s;
return typeof arguments[0] == "object" ? s = arguments[0] : s = [].slice.call(arguments), v(s);
}
const f = class l extends HTMLElement {
constructor() {
super(), this.src = null, this.proxyBaseUrl = l.proxyBaseUrl, this.hasConnected = !1;
}
static get observedAttributes() {
return l.attributeNames;
}
connectedCallback() {
this.hasConnected = !0, this.src && (console.log("[Configurator] Web Component connected -> reinitialize the configurator app."), this.bootstrapApp());
}
attributeChangedCallback(e, o, t) {
e === "src" && t !== o ? (this.src = t, this.hasConnected && this.isConnected && this.src && (console.log("[Configurator] The URL was changed -> reinitialize the configurator app."), this.bootstrapApp())) : e === "proxy-base-url" && t !== o && (this.proxyBaseUrl = t ?? l.proxyBaseUrl);
}
async bootstrapApp() {
try {
let e;
try {
const i = x(this.proxyBaseUrl, `?url=${this.src}`);
e = await fetch(i);
} catch (i) {
console.error("[Configurator] Failed to bootstrap configurator app: Failed to load index.html.", i);
return;
}
for (; this.firstChild; )
this.removeChild(this.firstChild);
const o = await e.text(), t = new DOMParser().parseFromString(o, "text/html"), r = t.querySelector("style[data-framer-css-ssr-minified]") || t.querySelector("style[data-framer-css-ssr]"), n = t.querySelector("div#main"), a = t.querySelector('script[data-framer-bundle="main"]'), c = t.querySelector('script[type="importmap"][data-framer-importmap]');
if (!r || !r.textContent) {
console.error("[Configurator] Failed to bootstrap configurator app: Could not find the style element.");
return;
}
if (!n) {
console.error("[Configurator] Failed to bootstrap configurator app: Could not find the main div.");
return;
}
if (!a || !(a instanceof HTMLScriptElement)) {
console.error("[Configurator] Failed to bootstrap configurator app: Could not find the main entry script.");
return;
}
const y = r.textContent.split("}").filter((i) => {
const d = i.trim();
return !// Remove some rules that may interfere too much with the host
(d.startsWith("html,body,#main") || d.startsWith("h1,h2,h3,h4,h5,h6,p,figure") || d.startsWith("body,input,textarea,select,button"));
}).map((i) => i.trim()).join(`}
`), h = document.createElement("style");
for (const i of r.attributes)
h.setAttribute(i.name, i.value);
if (h.textContent = y, this.appendChild(h), this.appendChild(n.cloneNode()), c && c.textContent) {
const i = document.createElement("script");
i.type = "importmap", i.textContent = c.textContent, this.appendChild(i);
}
if (a.src) {
const i = document.createElement("script");
i.type = "module", i.src = a.src, i.setAttribute("async", ""), this.appendChild(i);
} else if (a.textContent) {
const i = document.createElement("script");
i.type = "module", i.textContent = a.textContent, this.appendChild(i);
} else
console.error("[Configurator] Script tag found, but no src or content available.");
} catch (e) {
console.error("[Configurator] Failed to bootstrap configurator app", e);
}
}
};
f.proxyBaseUrl = "https://spc.cloud.ceventis.de/framer/host-proxy";
f.attributeNames = ["src", "proxy-base-url"];
let p = f;
const m = class g extends HTMLElement {
constructor() {
super(), this.src = null, this.hasConnected = !1, this.iframe = null, this.autoHeightHandler = null, this.allowedOrigin = null;
}
static get observedAttributes() {
return g.attributeNames;
}
connectedCallback() {
this.hasConnected = !0, this.src && (console.log("[Configurator] Web Component connected -> reinitialize the configurator app."), this.bootstrapApp());
}
disconnectedCallback() {
this.removeAutoHeightSync();
}
attributeChangedCallback(e, o, t) {
e === "src" && t !== o && (this.src = t, this.hasConnected && this.isConnected && this.src && (console.log("[Configurator] The src was changed -> reinitialize the configurator app."), this.iframe ? this.iframe.src = this.src : this.bootstrapApp()));
}
bootstrapApp() {
if (!this.src) {
console.error("[Configurator] Cannot bootstrap iframe: src is missing.");
return;
}
for (this.removeAutoHeightSync(); this.firstChild; )
this.removeChild(this.firstChild);
this.iframe = document.createElement("iframe"), this.iframe.src = this.src;
try {
this.allowedOrigin = new URL(this.src).origin;
} catch {
console.warn("[Configurator] Invalid src URL provided, cannot derive allowed origin. Height synchronisation is disabled."), this.allowedOrigin = null;
}
this.iframe.width = "100%", this.iframe.height = "100%", this.iframe.style.border = "none", this.appendChild(this.iframe), this.getAttribute("no-auto-height") != null && this.getAttribute("no-auto-height") != "false" || this.useAutoHeightSync();
}
useAutoHeightSync() {
this.autoHeightHandler = (e) => {
var o;
this.allowedOrigin && e.origin !== this.allowedOrigin || ((o = e.data) == null ? void 0 : o.type) !== "spc.configurator.height" || typeof e.data.height != "number" || (console.log("[Configurator] Received new height from iFrame -> Apply", e.data.height), this.iframe.style.height = `${e.data.height + 10}px`);
}, window.addEventListener("message", this.autoHeightHandler);
}
removeAutoHeightSync() {
this.autoHeightHandler && (window.removeEventListener("message", this.autoHeightHandler), this.autoHeightHandler = null);
}
};
m.attributeNames = ["src", "no-auto-height"];
let b = m;
const C = class u extends HTMLElement {
constructor() {
super(), this.child = null, this.isIsolated = !1;
}
static get observedAttributes() {
return ["isolated", ...u.childAttributes];
}
connectedCallback() {
this.initialize();
}
disconnectedCallback() {
this.child && this.contains(this.child) && this.removeChild(this.child), this.child = null;
}
attributeChangedCallback(e, o, t) {
if (e === "isolated") {
const r = t !== null && t !== "false";
if (r !== this.isIsolated) {
console.log("[Configurator] The isolation mode was changed -> reinitialize the configurator app."), this.isIsolated = r, this.initialize();
return;
}
}
this.child && o !== t && (t === null ? this.child.removeAttribute(e) : this.child.setAttribute(e, t));
}
initialize() {
const e = this.getAttribute("isolated");
this.isIsolated = e !== null && e !== "false";
const o = this.isIsolated ? "spc-embedded-configurator-iframe" : "spc-embedded-configurator-native";
if (this.child && this.child.tagName.toLowerCase() === o)
return;
this.child && this.removeChild(this.child);
const t = document.createElement(o);
for (const { name: r, value: n } of Array.from(this.attributes))
u.childAttributes.some((a) => a === r) && n !== null && t.setAttribute(r, n);
this.appendChild(t), this.child = t;
}
};
C.childAttributes = [...p.attributeNames, ...b.attributeNames];
let E = C;
customElements.define("spc-embedded-configurator", E);
customElements.define("spc-embedded-configurator-native", p);
customElements.define("spc-embedded-configurator-iframe", b);
export {
p as EmbeddedConfiguratorNative,
E as EmbeddedConfiguratorStrategy
};