@open-iframe-resizer/vue
Version:
Open-source modern iframe resizer component
300 lines (299 loc) • 9.29 kB
JavaScript
import { defineComponent as D, ref as A, onMounted as H, onBeforeUnmount as _, createElementBlock as T, openBlock as W, mergeProps as x } from "vue";
const E = () => typeof window < "u", C = () => {
try {
return window.self !== window.top;
} catch {
return !0;
}
}, p = (e) => e instanceof HTMLIFrameElement, z = (e) => {
window.document.readyState === "complete" ? e() : window.addEventListener("load", e, { once: !0 });
}, F = (e, t) => {
t(), e.addEventListener("load", t, { once: !0 });
}, $ = (e, t) => {
const n = e.contentWindow?.document.readyState === "complete";
e.src !== "about:blank" && e.contentWindow?.location.href !== "about:blank" && n && t();
}, N = () => ({ offsetSize: 0, checkOrigin: !0, enableLegacyLibSupport: !1 });
async function U(e) {
try {
return e.contentDocument?.URL === "about:blank" ? new Promise((t) => {
e.addEventListener("load", () => t(e.contentDocument !== null), { once: !0 });
}) : e.contentDocument !== null;
} catch {
return !1;
}
}
const j = (e) => {
try {
const t = new URL(e.src).origin;
if (t !== "about:blank")
return t;
} catch {
}
return null;
}, q = (e) => (Object.keys(e).forEach((t) => {
e[t] === void 0 && delete e[t];
}), e), S = (e) => {
const { height: t, width: n } = e.getBoundingClientRect();
return { height: Math.ceil(t), width: Math.ceil(n) };
}, f = (e, t) => e ? t ? e.querySelector(t) : e.documentElement : null, L = (e, t) => {
e && (t.bodyPadding && (e.body.style.padding = t.bodyPadding), t.bodyMargin && (e.body.style.margin = t.bodyMargin));
}, h = (e) => e <= 100 ? 100 : e <= 120 ? 1e3 : 1e4, G = () => "[iFrameSizer]ID:0:false:false:32:true:true::auto:::0:false:child:auto:true:::true:::false";
function J(e) {
if (typeof e.data != "string" || !e.data.startsWith("[iFrameSizer]"))
return null;
const t = e.data.endsWith("mutationObserver") || e.data.endsWith("resizeObserver"), n = e.data.search(/:init(:[\d.]+)?/g) >= 0;
if (!t && !n)
return null;
const [r, i] = e.data.split(":"), o = +i;
return o > 0 ? o : null;
}
const v = te();
let m = [];
const K = async (e, t) => {
if (!E())
return [];
const n = { ...N(), ...q(e ?? {}) }, r = Q(t), i = V(n, r);
return Promise.all(
r.map(async (o) => {
const s = {
iframe: o,
settings: n,
interactionState: { isHovered: !1 },
initContext: { isInitialized: !1, retryAttempts: 0 }
}, { unsubscribe: a, resize: u } = await X(s, i);
return m.push(s), {
unsubscribe: () => {
a(), m = m.filter((c) => c.iframe !== o);
},
resize: u
};
})
);
};
function Q(e) {
return typeof e == "string" ? Array.from(document.querySelectorAll(e)).filter(p) : e ? p(e) ? [e] : [] : Array.from(document.getElementsByTagName("iframe"));
}
function V(e, t) {
if (Array.isArray(e.checkOrigin))
return e.checkOrigin;
if (!e.checkOrigin)
return [];
const n = [];
for (const r of t) {
const i = j(r);
i && n.push(i);
}
return n;
}
async function X(e, t) {
const n = await U(e.iframe), { unsubscribe: r, resize: i } = n ? Z(e) : Y(e, t), o = ee(e);
return {
unsubscribe: () => {
r(), o();
},
resize: i
};
}
function Y(e, t) {
const {
iframe: n,
initContext: r,
settings: { checkOrigin: i, enableLegacyLibSupport: o, targetElementSelector: s, bodyPadding: a, bodyMargin: u }
} = e, c = (d) => {
const B = d.origin === "null", P = !i || B || t.includes(d.origin);
if (!(n.contentWindow !== d.source || !P)) {
if (d.data?.type === "iframe-resized") {
const { height: l } = d.data;
l && b({ newHeight: l, registeredElement: e });
return;
}
if (o) {
const l = J(d);
l !== null && b({ newHeight: l, registeredElement: e });
return;
}
}
};
window.addEventListener("message", c);
const O = o ? G() : { type: "iframe-child-init", targetElementSelector: s, bodyPadding: a, bodyMargin: u }, g = () => {
F(n, () => n.contentWindow?.postMessage(O, "*")), r.retryAttempts++, r.retryTimeoutId = window.setTimeout(g, h(r.retryAttempts));
}, w = () => {
r.isInitialized && (r.isInitialized = !1, r.retryAttempts = 0, g());
};
return n.addEventListener("load", w), g(), {
unsubscribe: () => {
window.removeEventListener("message", c), n.removeEventListener("load", w);
},
resize: () => {
const d = { type: "iframe-get-child-dimensions" };
n.contentWindow?.postMessage(d, "*");
}
};
}
function Z(e) {
const { iframe: t, settings: n } = e, { targetElementSelector: r } = n;
let i = 0;
const o = () => {
const s = f(t.contentDocument, r);
if (!t.contentDocument || !s)
return i++, setTimeout(o, h(i));
L(t.contentDocument, n), v().observe(s);
};
return $(t, o), t.addEventListener("load", o), {
unsubscribe: () => {
const s = f(t.contentDocument, r);
s && v().unobserve(s), t.removeEventListener("load", o);
},
resize: () => R(e)
};
}
function ee({ iframe: e, interactionState: t, settings: n }) {
if (!n.onBeforeIframeResize && !n.onIframeResize)
return () => {
};
const r = () => {
t.isHovered = !0;
}, i = () => {
t.isHovered = !1;
};
return e.addEventListener("mouseenter", r), e.addEventListener("mouseleave", i), () => {
e.removeEventListener("mouseenter", r), e.removeEventListener("mouseleave", i);
};
}
function te() {
let e = null;
return () => {
if (!e) {
const t = ({ target: n }) => {
const r = m.find(({ iframe: i }) => i.contentDocument === n.ownerDocument);
r && R(r);
};
e = new ResizeObserver((n) => n.forEach(t));
}
return e;
};
}
function R(e) {
const { iframe: t, settings: n } = e, r = f(t.contentDocument, n.targetElementSelector);
if (!r)
return;
const { height: i } = S(r);
i && b({ newHeight: i, registeredElement: e });
}
function b({ registeredElement: e, newHeight: t }) {
const { iframe: n, settings: r, interactionState: i, initContext: o } = e;
if (o.isInitialized || (o.isInitialized = !0, clearTimeout(o.retryTimeoutId)), r.onBeforeIframeResize?.({ iframe: n, interactionState: { ...i }, settings: { ...r }, observedHeight: t }) === !1)
return;
const s = n.getBoundingClientRect(), a = t + r.offsetSize;
if (n.style.height = `${a}px`, !r.onIframeResize)
return;
const u = {
iframe: n,
settings: { ...r },
interactionState: { ...i },
previousRenderState: { rect: s },
nextRenderState: { rect: n.getBoundingClientRect() }
};
r.onIframeResize(u);
}
const ne = oe();
let y = !1, I;
re();
function re() {
!E() || !C() || window.addEventListener("message", (e) => {
if (e.data?.type === "iframe-child-init")
return z(() => k(e));
if (e.data?.type === "iframe-get-child-dimensions")
return z(() => ie(e));
});
}
function k(e, t = 0) {
const { targetElementSelector: n, bodyPadding: r, bodyMargin: i } = e.data, o = f(document, n);
if (y || window.parent !== e.source)
return;
if (!o)
return setTimeout(() => k(e, t + 1), h(t));
L(document, { bodyMargin: i, bodyPadding: r }), I = n;
const s = ne();
s.disconnect(), s.observe(o), y = !0;
}
function ie(e) {
const t = f(document, I);
!y || window.parent !== e.source || !t || M(t);
}
function oe() {
let e = null;
return () => (e || (e = new ResizeObserver((t) => {
t[0].target && M(t[0].target);
})), e);
}
const M = (e) => {
const { width: t, height: n } = S(e), r = {
type: "iframe-resized",
width: t,
height: n
};
window.parent.postMessage(r, "*");
}, se = D({
name: "ResizableIframe",
props: {
offsetSize: [String, Number, Object],
checkOrigin: [Boolean, Array],
onIframeResize: Function,
onBeforeIframeResize: Function,
targetElementSelector: String,
bodyMargin: [String, Number],
bodyPadding: [String, Number],
enableLegacyLibSupport: Boolean
},
setup(e, { attrs: t }) {
const n = A(null);
let r = [], i = !1;
const o = async (s) => {
const a = {
offsetSize: e.offsetSize,
enableLegacyLibSupport: e.enableLegacyLibSupport,
checkOrigin: e.checkOrigin,
onIframeResize: e.onIframeResize,
onBeforeIframeResize: e.onBeforeIframeResize,
targetElementSelector: e.targetElementSelector,
bodyMargin: e.bodyMargin,
bodyPadding: e.bodyPadding
};
Object.keys(a).forEach((c) => {
a[c] === void 0 && delete a[c];
});
const u = await K(a, s);
i ? u.forEach((c) => {
c.unsubscribe();
}) : r = u;
};
return H(() => {
n.value && o(n.value);
}), _(() => {
i = !0, r.forEach((s) => {
s.unsubscribe();
}), r = [];
}), {
iframeRef: n,
attrs: t
};
}
}), ae = (e, t) => {
const n = e.__vccOpts || e;
for (const [r, i] of t)
n[r] = i;
return n;
};
function ce(e, t, n, r, i, o) {
return W(), T("iframe", x({ ref: "iframeRef" }, e.$attrs), null, 16);
}
const ue = /* @__PURE__ */ ae(se, [["render", ce]]), le = {
install(e) {
e.component("IframeResizer", ue);
}
};
export {
ue as IframeResizer,
le as IframeResizerPlugin
};