@phun-ky/frameport 2.0.30 → 2.0.31

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.
@@ -2,7 +2,7 @@
2
2
  * @phun-ky/frameport
3
3
  * A zero dependency package to effortlessly create responsive component previews in documentation using real media queries. Frameport dynamically generates iframes containing your HTML/CSS/JS, mimicking natural device viewports to test responsiveness, without any build steps or dependencies.
4
4
  * @author Alexander Vassbotn Røyne-Helgesen <alexander+frameport@phun-ky.net>
5
- * @version 2.0.30
5
+ * @version 2.0.31
6
6
  * @license
7
7
  * Copyright (c) 2023 Alexander Vassbotn Røyne-Helgesen
8
8
  *
@@ -24,5 +24,5 @@
24
24
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
25
  * SOFTWARE.
26
26
  */
27
- const t=t=>{let{style:e,css:r,code:a,javascript:o}=t;const{html:n,headers:s=[]}=t;return r=(t=>t&&""!==t?`<link rel="stylesheet" type="text/css" href="${window.location.protocol}//${window.location.host}${t.trim()}" />`:"")(r),o=(t=>t&&""!==t?`<script src="${window.location.protocol}//${window.location.host}${t.trim()}"><\/script>`:"")(o),a=(t=>t&&""!==t?`<script>${t.trim()}<\/script>`:"")(a),e=(t=>t&&""!==t?`<style type="text/css">${t}</style>`:"")(e),`<!DOCTYPE html>\n<html>\n<head>\n${s.join("\n")}\n${e}\n${r}\n</head>\n<body>\n${n||""}\n${o}\n${a}\n</body>\n</html>`},e=e=>((t,e)=>{const r=new Blob([t],{type:e});return URL.createObjectURL(r)})(t(e),"text/html"),r=async(t,e)=>{!t||!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e||Array.isArray(e)&&0===e.length||0===Object.keys(e).length&&e.constructor===Object||(await new Promise(requestAnimationFrame),Array.isArray(e)?e.forEach((e=>t.style[e.key]=e.value)):Object.keys(e).forEach((r=>t.style[r]=e[r])))},a=t=>{const{className:a,height:o,width:n}=t,s=e(t),i=document.createElement("iframe"),c={};return i.src=s,i.setAttribute("data-rde-iframe",""),a&&""!==a?i.classList.add(a):c.border="none",c.width=`${n}px`,o&&(c.height=`${o}px`),r(i,c),i},o={clip:"rect(1px, 1px, 1px, 1px)",height:"1px",margin:"0",overflow:"hidden",position:"absolute",width:"1px"},n=['<meta charset="utf-8" />','<meta name="robots" content="none" />','<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />','<meta name="viewport" content="width=device-width, initial-scale=1" />'],s=(t,e)=>{if(!t||!e||e&&0===Object.keys(e).length)return;const{html:n,viewports:s,templateElement:i}=e;n&&""!==n&&(r(i,o),s?((t,e)=>{const{viewports:r}=e;if(!r||""===r)return;let o=[];r.includes(",")?o=[...o,...r.split(",")]:o.push(r);for(const r of o){const o=r.split("x"),[n,s]=o,i=a({...e,height:s,width:n});t.insertAdjacentElement("afterend",i)}})(t,e):((t,e)=>{const{width:r}=e;if(!r)return;const o=a(e);t.append(o)})(t,e))},i=t=>{let e=[...n];return t&&(Array.isArray(t)?e=[...e,...t].map((t=>t.trim())):t.includes(",")?e=[...e,...t.split(",")].map((t=>t.trim())):""!==t&&e.push(t.trim())),e},c=t=>{"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>{t()})):t()},l=()=>{const t=new IntersectionObserver(((t,e)=>{t.forEach((t=>{if(t.intersectionRatio>0){const{dataset:{frameportTemplate:r,frameportVh:a,frameportVw:o,frameportCss:n,frameportStyle:c,frameportCode:l,frameportJs:m,frameportClass:p,frameportHeaders:d,frameportViewports:h}}=t.target;let f=t.target.innerHTML,u=t.target;if(r){const t=document.querySelector(r);t&&(f=t.innerHTML,u=t)}const y={templateSelector:r,templateElement:u,height:a,width:o,html:f,css:n,style:c,code:l,javascript:m,className:p,headers:i(d),viewports:h};s(t.target,y),e.unobserve(t.target)}}))}));document.querySelectorAll("[data-frameport]").forEach((e=>{t.observe(e)}))},m=t=>{window.frameport=t},p=t=>{const e=document.currentScript;if(e){const r=e.getAttribute("src");r&&r.includes("frameport.js")&&(e.hasAttribute("data-manual")?m(t):e.hasAttribute("data-instant")?t():e.hasAttribute("data-dom")?c(t):e.hasAttribute("data-lazy")?l():c(t))}},d={domReady:c,lazy:l,manual:m,activate:p},h=()=>{document.querySelectorAll("[data-frameport-iframe]").forEach((t=>t.remove()));document.querySelectorAll("[data-frameport]").forEach((t=>{const{dataset:{frameportTemplate:e,frameportVh:r,frameportVw:a,frameportCss:o,frameportStyle:n,frameportCode:c,frameportJs:l,frameportClass:m,frameportHeaders:p,frameportViewports:d}}=t;let h=t.innerHTML,f=t;if(e){const t=document.querySelector(e);t&&(h=t.innerHTML,f=t)}const u={templateSelector:e,templateElement:f,height:r,width:a,html:h,css:o,style:n,code:c,javascript:l,className:m,headers:i(p),viewports:d};s(t,u)}))};p(h);export{h as default,d as modes};
27
+ const t=t=>{let{style:e,css:r,code:a,javascript:o}=t;const{html:n,headers:s=[]}=t;return r=(t=>t&&""!==t?`<link rel="stylesheet" type="text/css" href="${window.location.protocol}//${window.location.host}${t.trim()}" />`:"")(r),o=(t=>t&&""!==t?`<script src="${window.location.protocol}//${window.location.host}${t.trim()}"><\/script>`:"")(o),a=(t=>t&&""!==t?`<script>${t.trim()}<\/script>`:"")(a),e=(t=>t&&""!==t?`<style type="text/css">${t}</style>`:"")(e),`<!DOCTYPE html>\n<html>\n<head>\n${s.join("\n")}\n${e}\n${r}\n</head>\n<body>\n${n||""}\n${o}\n${a}\n</body>\n</html>`},e=e=>((t,e)=>{const r=new Blob([t],{type:e});return URL.createObjectURL(r)})(t(e),"text/html"),r=async(t,e)=>{!t||!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e||Array.isArray(e)&&0===e.length||0===Object.keys(e).length&&e.constructor===Object||(await new Promise(requestAnimationFrame),Array.isArray(e)?e.forEach(e=>t.style[e.key]=e.value):Object.keys(e).forEach(r=>t.style[r]=e[r]))},a=t=>{const{className:a,height:o,width:n}=t,s=e(t),i=document.createElement("iframe"),c={};return i.src=s,i.setAttribute("data-rde-iframe",""),a&&""!==a?i.classList.add(a):c.border="none",c.width=`${n}px`,o&&(c.height=`${o}px`),r(i,c),i},o={clip:"rect(1px, 1px, 1px, 1px)",height:"1px",margin:"0",overflow:"hidden",position:"absolute",width:"1px"},n=['<meta charset="utf-8" />','<meta name="robots" content="none" />','<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />','<meta name="viewport" content="width=device-width, initial-scale=1" />'],s=(t,e)=>{if(!t||!e||e&&0===Object.keys(e).length)return;const{html:n,viewports:s,templateElement:i}=e;n&&""!==n&&(r(i,o),s?((t,e)=>{const{viewports:r}=e;if(!r||""===r)return;let o=[];r.includes(",")?o=[...o,...r.split(",")]:o.push(r);for(const r of o){const o=r.split("x"),[n,s]=o,i=a({...e,height:s,width:n});t.insertAdjacentElement("afterend",i)}})(t,e):((t,e)=>{const{width:r}=e;if(!r)return;const o=a(e);t.append(o)})(t,e))},i=t=>{let e=[...n];return t&&(Array.isArray(t)?e=[...e,...t].map(t=>t.trim()):t.includes(",")?e=[...e,...t.split(",")].map(t=>t.trim()):""!==t&&e.push(t.trim())),e},c=t=>{"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{t()}):t()},l=()=>{const t=new IntersectionObserver((t,e)=>{t.forEach(t=>{if(t.intersectionRatio>0){const{dataset:{frameportTemplate:r,frameportVh:a,frameportVw:o,frameportCss:n,frameportStyle:c,frameportCode:l,frameportJs:m,frameportClass:p,frameportHeaders:d,frameportViewports:h}}=t.target;let f=t.target.innerHTML,u=t.target;if(r){const t=document.querySelector(r);t&&(f=t.innerHTML,u=t)}const y={templateSelector:r,templateElement:u,height:a,width:o,html:f,css:n,style:c,code:l,javascript:m,className:p,headers:i(d),viewports:h};s(t.target,y),e.unobserve(t.target)}})});document.querySelectorAll("[data-frameport]").forEach(e=>{t.observe(e)})},m=t=>{window.frameport=t},p=t=>{const e=document.currentScript;if(e){const r=e.getAttribute("src");r&&r.includes("frameport.js")&&(e.hasAttribute("data-manual")?m(t):e.hasAttribute("data-instant")?t():e.hasAttribute("data-dom")?c(t):e.hasAttribute("data-lazy")?l():c(t))}},d={domReady:c,lazy:l,manual:m,activate:p},h=()=>{document.querySelectorAll("[data-frameport-iframe]").forEach(t=>t.remove());document.querySelectorAll("[data-frameport]").forEach(t=>{const{dataset:{frameportTemplate:e,frameportVh:r,frameportVw:a,frameportCss:o,frameportStyle:n,frameportCode:c,frameportJs:l,frameportClass:m,frameportHeaders:p,frameportViewports:d}}=t;let h=t.innerHTML,f=t;if(e){const t=document.querySelector(e);t&&(h=t.innerHTML,f=t)}const u={templateSelector:e,templateElement:f,height:r,width:a,html:h,css:o,style:n,code:c,javascript:l,className:m,headers:i(p),viewports:d};s(t,u)})};p(h);export{h as default,d as modes};
28
28
  //# sourceMappingURL=frameport.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"frameport.esm.js","sources":["../src/utils/iframe.ts","../src/utils/source.ts","../src/utils/css.ts","../src/utils/js.ts","../src/utils/code.ts","../src/utils/style.ts","../src/utils/page.ts","../src/utils/blob.ts","../src/utils/styles.ts","../src/utils/wait.ts","../src/utils/create.ts","../src/constants/index.ts","../src/features/dom.ts","../src/config/generate-viewports.ts","../src/config/generate.ts","../src/utils/headers.ts","../src/config/browser.ts","../src/main.ts"],"sourcesContent":["/**\n * Create and return an iframe element.\n *\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const createIframe = (): HTMLIFrameElement =>\n document.createElement('iframe');\n","import { type FrameportOptionsInterface } from '../types';\n\nimport { getCode } from './code';\nimport { getCSS } from './css';\nimport { getJavaScript } from './js';\nimport { getStyle } from './style';\n\n/**\n * Generate the source code for an HTML page based on the provided options.\n *\n * @param {FrameportOptionsInterface} options - The options for generating the HTML page source.\n * @returns {string} - The generated HTML source code as a string.\n */\nexport const getSource = (options: FrameportOptionsInterface): string => {\n let { style, css, code, javascript } = options;\n\n const { html, headers = [] } = options;\n\n css = getCSS(css);\n javascript = getJavaScript(javascript);\n code = getCode(code);\n style = getStyle(style);\n\n return `<!DOCTYPE html>\n<html>\n<head>\n${headers.join('\\n')}\n${style}\n${css}\n</head>\n<body>\n${html || ''}\n${javascript}\n${code}\n</body>\n</html>`;\n};\n","/**\n * Generate a CSS link tag based on the provided URL.\n *\n * @param {string | undefined} css - The URL of the CSS file.\n * @returns {string} - A CSS link tag.\n */\nexport const getCSS = (css: string | undefined): string => {\n if (css && css !== '') {\n return `<link rel=\"stylesheet\" type=\"text/css\" href=\"${window.location.protocol}//${window.location.host}${css.trim()}\" />`;\n }\n\n return '';\n};\n","/**\n * Generate a script tag for the specified JavaScript file.\n *\n * @param {string | undefined} javascript - The path to the JavaScript file.\n * @returns {string} - The generated script tag or an empty string if no JavaScript path is provided.\n */\nexport const getJavaScript = (javascript: string | undefined): string => {\n if (javascript && javascript !== '') {\n return `<script src=\"${window.location.protocol}//${window.location.host}${javascript.trim()}\"></script>`;\n }\n\n return '';\n};\n","/**\n * Get a script element containing the provided code if available.\n *\n * @param {string | undefined} code - The code to include in the script element.\n * @returns {string} - The script element or an empty string if code is not available.\n */\nexport const getCode = (code: string | undefined): string => {\n if (code && code !== '') {\n return `<script>${code.trim()}</script>`;\n }\n\n return '';\n};\n","/**\n * Generate a style element based on the provided CSS styles.\n *\n * @param {string | undefined} style - The CSS styles to include in the style element.\n * @returns {string} - The style element as a string or an empty string if no styles are provided.\n */\nexport const getStyle = (style: string | undefined): string => {\n if (style && style !== '') {\n return `<style type=\"text/css\">${style}</style>`;\n }\n\n return '';\n};\n","import { getBlobURL } from './blob';\nimport { getSource } from './source';\n\n/**\n * Get the URL of a generated HTML page based on the provided options.\n *\n * @param {object} options - The options for generating the HTML page.\n * @returns {string} - The URL of the generated HTML page as a Blob URL.\n */\nexport const getGeneratedPageURL = (options) => {\n const source = getSource(options);\n\n return getBlobURL(source, 'text/html');\n};\n","/**\n * Generates a Blob URL from HTML content with the specified MIME type.\n *\n * @param {string} html - The HTML content to create a Blob from.\n * @param {string} type - The MIME type of the Blob (e.g., 'text/html', 'image/jpeg').\n * @returns {string} - The generated Blob URL.\n */\nexport const getBlobURL = (html: string, type: string): string => {\n const blob = new Blob([html], { type });\n\n return URL.createObjectURL(blob);\n};\n","/* eslint no-console:0 */\nimport { waitForFrame } from './wait';\n\n/**\n * Adds CSS styles to an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to apply styles to.\n * @param {object | Array<{ key: string; value: string }>} styles - An object or an array of objects containing CSS styles to apply.\n * @returns {Promise<void>} - A Promise that resolves after styles are applied.\n *\n * @example\n * ```ts\n * // Apply styles as an object\n * const element = document.getElementById('my-element');\n * await add(element, { color: 'red', fontSize: '16px' });\n *\n * // Apply styles as an array of objects\n * const styles = [\n * { key: 'color', value: 'blue' },\n * { key: 'backgroundColor', value: 'yellow' }\n * ];\n * await add(element, styles);\n * ```\n */\nexport const add = async (\n el: HTMLElement,\n styles: object | { key: string; value: string }[]\n): Promise<void> => {\n if (\n !el ||\n !styles ||\n typeof styles === 'string' ||\n typeof styles === 'number' ||\n typeof styles === 'boolean' ||\n (Array.isArray(styles) && styles.length === 0) ||\n (Object.keys(styles).length === 0 && styles.constructor === Object)\n ) {\n return;\n }\n\n await waitForFrame();\n\n if (Array.isArray(styles)) {\n styles.forEach(\n (style: { key: string; value: string }) =>\n (el.style[style.key] = style.value)\n );\n } else {\n Object.keys(styles).forEach((key) => (el.style[key] = styles[key]));\n }\n};\n\n/**\n * Gets the computed CSS styles of an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to get computed styles from.\n * @returns {Promise<CSSStyleDeclaration>} - A Promise that resolves with the computed CSS styles.\n *\n * @example\n * ```ts\n * // Get computed styles of an element\n * const element = document.getElementById('my-element');\n * const computedStyles = await get(element);\n * console.log(computedStyles.color); // Logs the color property value\n * ```\n */\nexport const get = async (el: HTMLElement): Promise<CSSStyleDeclaration> => {\n await waitForFrame();\n\n return getComputedStyle(el, null);\n};\n","/**\n * Waits for the next animation frame using requestAnimationFrame.\n *\n * @returns {Promise<number>} - A Promise that resolves with the timestamp of the next animation frame.\n *\n * @example\n * ```ts\n * // Wait for the next animation frame and get the rect\n * await waitForFrame();\n * const rect = el.getBoundingClientRect();\n * // Wait for the next animation frame and get the timestamp\n * const timestamp = await waitForFrame();\n * ```\n */\nexport const waitForFrame = (): Promise<number> =>\n new Promise<number>(requestAnimationFrame);\n","import { type FrameportOptionsInterface } from '../types';\nimport { createIframe } from '../utils/iframe';\nimport { getGeneratedPageURL } from '../utils/page';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Create an iframe element with specified options and styles.\n *\n * @param {FrameportOptionsInterface} options - The options for creating the iframe.\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const create = (\n options: FrameportOptionsInterface\n): HTMLIFrameElement => {\n const { className, height, width } = options;\n const url = getGeneratedPageURL(options);\n const iframeElement = createIframe();\n const iframeStyle = {};\n\n iframeElement.src = url;\n iframeElement.setAttribute('data-rde-iframe', '');\n\n if (!className || className === '') {\n iframeStyle['border'] = 'none';\n } else {\n iframeElement.classList.add(className);\n }\n\n iframeStyle['width'] = `${width}px`;\n\n if (height) {\n iframeStyle['height'] = `${height}px`;\n }\n\n addStyles(iframeElement, iframeStyle);\n\n return iframeElement;\n};\n","/**\n * @constant\n * @description\n * Inline style used to visually hide an element from layout and screen readers,\n * often used to hide `<template>` elements or accessibility-hidden content.\n *\n * Mimics techniques commonly recommended for a11y-invisible content without removing from DOM.\n *\n * @type {{ clip: string, height: string, margin: string, overflow: string, position: string, width: string }}\n *\n * @example\n * ```ts\n * element.style = { ...HIDE_TEMPLATE_STYLE };\n * ```\n */\nexport const HIDE_TEMPLATE_STYLE: {\n clip: string;\n height: string;\n margin: string;\n overflow: string;\n position: string;\n width: string;\n} = {\n clip: 'rect(1px, 1px, 1px, 1px)',\n height: '1px',\n margin: '0',\n overflow: 'hidden',\n position: 'absolute',\n width: '1px'\n};\n\n/**\n * @constant\n * @description\n * Default `<meta>` tags to inject into a dynamically created HTML document.\n * Ensures consistent encoding, responsive layout, and no indexing by robots.\n *\n * Useful when rendering sandboxed `<iframe>` content or initializing virtual DOMs.\n *\n * @type {string[]}\n *\n * @example\n * ```ts\n * const headHtml = DEFAULT_HEADERS.join('\\n');\n * iframeDoc.head.innerHTML = headHtml;\n * ```\n */\nexport const DEFAULT_HEADERS: string[] = [\n '<meta charset=\"utf-8\" />',\n '<meta name=\"robots\" content=\"none\" />',\n '<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />'\n];\n","import { generate } from '../config/generate';\nimport { generateViewports } from '../config/generate-viewports';\nimport { HIDE_TEMPLATE_STYLE } from '../constants';\nimport { type FrameportOptionsInterface } from '../types';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Generate iframes into the DOM, possibly generating viewports.\n *\n * @param {HTMLElement} targetElement - The target HTML element.\n * @param {FrameportOptionsInterface} options - The options for the iframe.\n * @returns {void}\n */\nconst dom = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n if (\n !targetElement ||\n !options ||\n (options && Object.keys(options).length === 0)\n )\n return;\n\n const { html, viewports, templateElement } = options;\n\n if (!html || html === '') return;\n\n addStyles(templateElement as HTMLElement, HIDE_TEMPLATE_STYLE);\n\n if (viewports) {\n generateViewports(targetElement, options);\n } else {\n generate(targetElement, options);\n }\n};\n\nexport default dom;\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate multiple iframe elements for different viewports and append them to a target element.\n *\n * @param {HTMLElement} target - The target HTML element to insert iframes after.\n * @param {FrameportOptionsInterface} options - The options for generating the iframes.\n * @returns {void}\n */\nexport const generateViewports = (\n target: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { viewports } = options;\n\n if (!viewports || viewports === '') return;\n\n let screens: string[] = [];\n\n if (viewports.includes(',')) screens = [...screens, ...viewports.split(',')];\n else screens.push(viewports);\n\n for (const viewPort of screens) {\n const values = viewPort.split('x');\n const [width, height] = values;\n const iframeElement = create({ ...options, height, width });\n\n target.insertAdjacentElement('afterend', iframeElement);\n }\n};\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate an iframe with the given options and append it to a target element.\n *\n * @param {HTMLElement} targetElement - The target HTML element to append the iframe to.\n * @param {FrameportOptionsInterface} options - The options for generating the iframe.\n * @returns {void}\n */\nexport const generate = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { width } = options;\n\n if (!width) return;\n\n const iframeElement = create(options);\n\n targetElement.append(iframeElement);\n};\n","import { DEFAULT_HEADERS } from '../constants';\n\n/**\n * Get headers for the iframe generated\n *\n * @param {string|string[]|null|undefined} rdeHeaders - The custom headers to include.\n * @returns {string[]} - An array of headers, including default and custom headers.\n */\nexport const getHeaders = (\n rdeHeaders: string | string[] | null | undefined\n): string[] => {\n let headers: string[] = [...DEFAULT_HEADERS];\n\n if (rdeHeaders) {\n if (Array.isArray(rdeHeaders)) {\n headers = [...headers, ...rdeHeaders].map((h) => h.trim());\n } else if (rdeHeaders.includes(',')) {\n headers = [...headers, ...rdeHeaders.split(',')].map((h) => h.trim());\n } else if (rdeHeaders !== '') {\n headers.push(rdeHeaders.trim());\n }\n }\n\n return headers;\n};\n","/* eslint no-console:0 */\nimport dom from '../features/dom';\nimport { type FrameportFunctionType } from '../types';\nimport { getHeaders } from '../utils/headers';\n\n/**\n * A function to initialize frameport when the DOM is ready.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // domReady(myRDE);\n * ```\n */\nexport const domReady = (frameport: FrameportFunctionType): void => {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n frameport();\n });\n } else {\n // `DOMContentLoaded` already fired\n frameport();\n }\n};\n\n/**\n * A function to initialize lazy frameport functionality.\n *\n * @example\n * ```ts\n * // Usage example:\n * // lazy();\n * ```\n */\nexport const lazy = (): void => {\n const frameportObserverTarget = new IntersectionObserver((els, observer) => {\n els.forEach((el: IntersectionObserverEntry) => {\n if (el.intersectionRatio > 0) {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = el.target as HTMLElement;\n\n let html = el.target.innerHTML;\n let templateElementToUse = el.target as HTMLElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(el.target as HTMLElement, options);\n observer.unobserve(el.target);\n }\n });\n });\n\n document.querySelectorAll('[data-frameport]').forEach((el) => {\n frameportObserverTarget.observe(el);\n });\n};\n\n/**\n * A function to manually activate frameport.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // manual(myRDE);\n * ```\n */\nexport const manual = (frameport: FrameportFunctionType): void => {\n window.frameport = frameport;\n};\n\n/**\n * A function to activate frameport based on script attributes.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // activate(myRDE);\n * ```\n */\nexport const activate = (frameport: FrameportFunctionType): void => {\n const script = document.currentScript;\n\n if (script) {\n const frameportScriptSrc = script.getAttribute('src');\n\n if (frameportScriptSrc && frameportScriptSrc.includes('frameport.js')) {\n if (script.hasAttribute('data-manual')) {\n manual(frameport);\n } else if (script.hasAttribute('data-instant')) {\n frameport();\n } else if (script.hasAttribute('data-dom')) {\n domReady(frameport);\n } else if (script.hasAttribute('data-lazy')) {\n lazy();\n } else {\n domReady(frameport);\n }\n }\n }\n};\n","/* eslint-disable import/no-unused-modules */\nimport { domReady, lazy, manual, activate } from './config/browser';\nimport dom from './features/dom';\nimport { getHeaders } from './utils/headers';\n\n/**\n * Available initialization modes for frameport usage.\n * Can be triggered manually or via browser lifecycle hooks.\n */\nexport const modes = {\n domReady,\n lazy,\n manual,\n activate\n};\n\n/**\n * Transforms all DOM elements marked with `[data-frameport]` into embedded iframes\n * with sandboxed content based on HTML templates and associated metadata.\n *\n * Also removes any existing `[data-frameport-iframe]` elements from the document,\n * ensuring re-renders are clean.\n *\n * Reads attributes such as:\n * - `data-frameport-template`\n * - `data-frameport-css`\n * - `data-frameport-style`\n * - `data-frameport-code`\n * - `data-frameport-js`\n * - `data-frameport-class`\n * - `data-frameport-headers`\n * - `data-frameport-viewports`\n * - `data-frameport-vh`\n * - `data-frameport-vw`\n *\n * @example\n * ```html\n * <div\n * data-frameport\n * data-frameport-template=\"#my-template\"\n * data-frameport-vh=\"100\"\n * data-frameport-vw=\"100\"\n * data-frameport-css=\"styles.css\"\n * data-frameport-js=\"script.js\"\n * ></div>\n *\n * <template id=\"my-template\">\n * <h1>Hello, sandbox!</h1>\n * </template>\n * ```\n *\n * @function frameport\n * @returns void\n */\nconst frameport = () => {\n // Remove all generated iframes first\n document\n .querySelectorAll('[data-frameport-iframe]')\n .forEach((iframe) => iframe.remove());\n\n // Find all frameport targets\n const elsToBeTransformedTemplate =\n document.querySelectorAll('[data-frameport]');\n\n elsToBeTransformedTemplate.forEach((targetElement: HTMLElement) => {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = targetElement;\n\n let html = targetElement.innerHTML;\n let templateElementToUse = targetElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(targetElement, options);\n });\n};\n\nexport default frameport;\n\n// Automatically activate frameport logic\nactivate(frameport);\n"],"names":["getSource","options","style","css","code","javascript","html","headers","window","location","protocol","host","trim","getCSS","getJavaScript","getCode","getStyle","join","getGeneratedPageURL","type","blob","Blob","URL","createObjectURL","getBlobURL","add","async","el","styles","Array","isArray","length","Object","keys","constructor","Promise","requestAnimationFrame","forEach","key","value","create","className","height","width","url","iframeElement","document","createElement","iframeStyle","src","setAttribute","classList","addStyles","HIDE_TEMPLATE_STYLE","clip","margin","overflow","position","DEFAULT_HEADERS","dom","targetElement","viewports","templateElement","target","screens","includes","split","push","viewPort","values","insertAdjacentElement","generateViewports","append","generate","getHeaders","rdeHeaders","map","h","domReady","frameport","readyState","addEventListener","lazy","frameportObserverTarget","IntersectionObserver","els","observer","intersectionRatio","dataset","frameportTemplate","templateSelector","frameportVh","frameportVw","frameportCss","frameportStyle","frameportCode","frameportJs","frameportClass","frameportHeaders","frameportViewports","innerHTML","templateElementToUse","querySelector","unobserve","querySelectorAll","observe","manual","activate","script","currentScript","frameportScriptSrc","getAttribute","hasAttribute","modes","iframe","remove"],"mappings":"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"}
1
+ {"version":3,"file":"frameport.esm.js","sources":["../src/utils/iframe.ts","../src/utils/source.ts","../src/utils/css.ts","../src/utils/js.ts","../src/utils/code.ts","../src/utils/style.ts","../src/utils/page.ts","../src/utils/blob.ts","../src/utils/styles.ts","../src/utils/wait.ts","../src/utils/create.ts","../src/constants/index.ts","../src/features/dom.ts","../src/config/generate-viewports.ts","../src/config/generate.ts","../src/utils/headers.ts","../src/config/browser.ts","../src/main.ts"],"sourcesContent":["/**\n * Create and return an iframe element.\n *\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const createIframe = (): HTMLIFrameElement =>\n document.createElement('iframe');\n","import { type FrameportOptionsInterface } from '../types';\n\nimport { getCode } from './code';\nimport { getCSS } from './css';\nimport { getJavaScript } from './js';\nimport { getStyle } from './style';\n\n/**\n * Generate the source code for an HTML page based on the provided options.\n *\n * @param {FrameportOptionsInterface} options - The options for generating the HTML page source.\n * @returns {string} - The generated HTML source code as a string.\n */\nexport const getSource = (options: FrameportOptionsInterface): string => {\n let { style, css, code, javascript } = options;\n\n const { html, headers = [] } = options;\n\n css = getCSS(css);\n javascript = getJavaScript(javascript);\n code = getCode(code);\n style = getStyle(style);\n\n return `<!DOCTYPE html>\n<html>\n<head>\n${headers.join('\\n')}\n${style}\n${css}\n</head>\n<body>\n${html || ''}\n${javascript}\n${code}\n</body>\n</html>`;\n};\n","/**\n * Generate a CSS link tag based on the provided URL.\n *\n * @param {string | undefined} css - The URL of the CSS file.\n * @returns {string} - A CSS link tag.\n */\nexport const getCSS = (css: string | undefined): string => {\n if (css && css !== '') {\n return `<link rel=\"stylesheet\" type=\"text/css\" href=\"${window.location.protocol}//${window.location.host}${css.trim()}\" />`;\n }\n\n return '';\n};\n","/**\n * Generate a script tag for the specified JavaScript file.\n *\n * @param {string | undefined} javascript - The path to the JavaScript file.\n * @returns {string} - The generated script tag or an empty string if no JavaScript path is provided.\n */\nexport const getJavaScript = (javascript: string | undefined): string => {\n if (javascript && javascript !== '') {\n return `<script src=\"${window.location.protocol}//${window.location.host}${javascript.trim()}\"></script>`;\n }\n\n return '';\n};\n","/**\n * Get a script element containing the provided code if available.\n *\n * @param {string | undefined} code - The code to include in the script element.\n * @returns {string} - The script element or an empty string if code is not available.\n */\nexport const getCode = (code: string | undefined): string => {\n if (code && code !== '') {\n return `<script>${code.trim()}</script>`;\n }\n\n return '';\n};\n","/**\n * Generate a style element based on the provided CSS styles.\n *\n * @param {string | undefined} style - The CSS styles to include in the style element.\n * @returns {string} - The style element as a string or an empty string if no styles are provided.\n */\nexport const getStyle = (style: string | undefined): string => {\n if (style && style !== '') {\n return `<style type=\"text/css\">${style}</style>`;\n }\n\n return '';\n};\n","import { getBlobURL } from './blob';\nimport { getSource } from './source';\n\n/**\n * Get the URL of a generated HTML page based on the provided options.\n *\n * @param {object} options - The options for generating the HTML page.\n * @returns {string} - The URL of the generated HTML page as a Blob URL.\n */\nexport const getGeneratedPageURL = (options) => {\n const source = getSource(options);\n\n return getBlobURL(source, 'text/html');\n};\n","/**\n * Generates a Blob URL from HTML content with the specified MIME type.\n *\n * @param {string} html - The HTML content to create a Blob from.\n * @param {string} type - The MIME type of the Blob (e.g., 'text/html', 'image/jpeg').\n * @returns {string} - The generated Blob URL.\n */\nexport const getBlobURL = (html: string, type: string): string => {\n const blob = new Blob([html], { type });\n\n return URL.createObjectURL(blob);\n};\n","/* eslint no-console:0 */\nimport { waitForFrame } from './wait';\n\n/**\n * Adds CSS styles to an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to apply styles to.\n * @param {object | Array<{ key: string; value: string }>} styles - An object or an array of objects containing CSS styles to apply.\n * @returns {Promise<void>} - A Promise that resolves after styles are applied.\n *\n * @example\n * ```ts\n * // Apply styles as an object\n * const element = document.getElementById('my-element');\n * await add(element, { color: 'red', fontSize: '16px' });\n *\n * // Apply styles as an array of objects\n * const styles = [\n * { key: 'color', value: 'blue' },\n * { key: 'backgroundColor', value: 'yellow' }\n * ];\n * await add(element, styles);\n * ```\n */\nexport const add = async (\n el: HTMLElement,\n styles: object | { key: string; value: string }[]\n): Promise<void> => {\n if (\n !el ||\n !styles ||\n typeof styles === 'string' ||\n typeof styles === 'number' ||\n typeof styles === 'boolean' ||\n (Array.isArray(styles) && styles.length === 0) ||\n (Object.keys(styles).length === 0 && styles.constructor === Object)\n ) {\n return;\n }\n\n await waitForFrame();\n\n if (Array.isArray(styles)) {\n styles.forEach(\n (style: { key: string; value: string }) =>\n (el.style[style.key] = style.value)\n );\n } else {\n Object.keys(styles).forEach((key) => (el.style[key] = styles[key]));\n }\n};\n\n/**\n * Gets the computed CSS styles of an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to get computed styles from.\n * @returns {Promise<CSSStyleDeclaration>} - A Promise that resolves with the computed CSS styles.\n *\n * @example\n * ```ts\n * // Get computed styles of an element\n * const element = document.getElementById('my-element');\n * const computedStyles = await get(element);\n * console.log(computedStyles.color); // Logs the color property value\n * ```\n */\nexport const get = async (el: HTMLElement): Promise<CSSStyleDeclaration> => {\n await waitForFrame();\n\n return getComputedStyle(el, null);\n};\n","/**\n * Waits for the next animation frame using requestAnimationFrame.\n *\n * @returns {Promise<number>} - A Promise that resolves with the timestamp of the next animation frame.\n *\n * @example\n * ```ts\n * // Wait for the next animation frame and get the rect\n * await waitForFrame();\n * const rect = el.getBoundingClientRect();\n * // Wait for the next animation frame and get the timestamp\n * const timestamp = await waitForFrame();\n * ```\n */\nexport const waitForFrame = (): Promise<number> =>\n new Promise<number>(requestAnimationFrame);\n","import { type FrameportOptionsInterface } from '../types';\nimport { createIframe } from '../utils/iframe';\nimport { getGeneratedPageURL } from '../utils/page';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Create an iframe element with specified options and styles.\n *\n * @param {FrameportOptionsInterface} options - The options for creating the iframe.\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const create = (\n options: FrameportOptionsInterface\n): HTMLIFrameElement => {\n const { className, height, width } = options;\n const url = getGeneratedPageURL(options);\n const iframeElement = createIframe();\n const iframeStyle = {};\n\n iframeElement.src = url;\n iframeElement.setAttribute('data-rde-iframe', '');\n\n if (!className || className === '') {\n iframeStyle['border'] = 'none';\n } else {\n iframeElement.classList.add(className);\n }\n\n iframeStyle['width'] = `${width}px`;\n\n if (height) {\n iframeStyle['height'] = `${height}px`;\n }\n\n addStyles(iframeElement, iframeStyle);\n\n return iframeElement;\n};\n","/**\n * @constant\n * @description\n * Inline style used to visually hide an element from layout and screen readers,\n * often used to hide `<template>` elements or accessibility-hidden content.\n *\n * Mimics techniques commonly recommended for a11y-invisible content without removing from DOM.\n *\n * @type {{ clip: string, height: string, margin: string, overflow: string, position: string, width: string }}\n *\n * @example\n * ```ts\n * element.style = { ...HIDE_TEMPLATE_STYLE };\n * ```\n */\nexport const HIDE_TEMPLATE_STYLE: {\n clip: string;\n height: string;\n margin: string;\n overflow: string;\n position: string;\n width: string;\n} = {\n clip: 'rect(1px, 1px, 1px, 1px)',\n height: '1px',\n margin: '0',\n overflow: 'hidden',\n position: 'absolute',\n width: '1px'\n};\n\n/**\n * @constant\n * @description\n * Default `<meta>` tags to inject into a dynamically created HTML document.\n * Ensures consistent encoding, responsive layout, and no indexing by robots.\n *\n * Useful when rendering sandboxed `<iframe>` content or initializing virtual DOMs.\n *\n * @type {string[]}\n *\n * @example\n * ```ts\n * const headHtml = DEFAULT_HEADERS.join('\\n');\n * iframeDoc.head.innerHTML = headHtml;\n * ```\n */\nexport const DEFAULT_HEADERS: string[] = [\n '<meta charset=\"utf-8\" />',\n '<meta name=\"robots\" content=\"none\" />',\n '<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />'\n];\n","import { generate } from '../config/generate';\nimport { generateViewports } from '../config/generate-viewports';\nimport { HIDE_TEMPLATE_STYLE } from '../constants';\nimport { type FrameportOptionsInterface } from '../types';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Generate iframes into the DOM, possibly generating viewports.\n *\n * @param {HTMLElement} targetElement - The target HTML element.\n * @param {FrameportOptionsInterface} options - The options for the iframe.\n * @returns {void}\n */\nconst dom = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n if (\n !targetElement ||\n !options ||\n (options && Object.keys(options).length === 0)\n )\n return;\n\n const { html, viewports, templateElement } = options;\n\n if (!html || html === '') return;\n\n addStyles(templateElement as HTMLElement, HIDE_TEMPLATE_STYLE);\n\n if (viewports) {\n generateViewports(targetElement, options);\n } else {\n generate(targetElement, options);\n }\n};\n\nexport default dom;\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate multiple iframe elements for different viewports and append them to a target element.\n *\n * @param {HTMLElement} target - The target HTML element to insert iframes after.\n * @param {FrameportOptionsInterface} options - The options for generating the iframes.\n * @returns {void}\n */\nexport const generateViewports = (\n target: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { viewports } = options;\n\n if (!viewports || viewports === '') return;\n\n let screens: string[] = [];\n\n if (viewports.includes(',')) screens = [...screens, ...viewports.split(',')];\n else screens.push(viewports);\n\n for (const viewPort of screens) {\n const values = viewPort.split('x');\n const [width, height] = values;\n const iframeElement = create({ ...options, height, width });\n\n target.insertAdjacentElement('afterend', iframeElement);\n }\n};\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate an iframe with the given options and append it to a target element.\n *\n * @param {HTMLElement} targetElement - The target HTML element to append the iframe to.\n * @param {FrameportOptionsInterface} options - The options for generating the iframe.\n * @returns {void}\n */\nexport const generate = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { width } = options;\n\n if (!width) return;\n\n const iframeElement = create(options);\n\n targetElement.append(iframeElement);\n};\n","import { DEFAULT_HEADERS } from '../constants';\n\n/**\n * Get headers for the iframe generated\n *\n * @param {string|string[]|null|undefined} rdeHeaders - The custom headers to include.\n * @returns {string[]} - An array of headers, including default and custom headers.\n */\nexport const getHeaders = (\n rdeHeaders: string | string[] | null | undefined\n): string[] => {\n let headers: string[] = [...DEFAULT_HEADERS];\n\n if (rdeHeaders) {\n if (Array.isArray(rdeHeaders)) {\n headers = [...headers, ...rdeHeaders].map((h) => h.trim());\n } else if (rdeHeaders.includes(',')) {\n headers = [...headers, ...rdeHeaders.split(',')].map((h) => h.trim());\n } else if (rdeHeaders !== '') {\n headers.push(rdeHeaders.trim());\n }\n }\n\n return headers;\n};\n","/* eslint no-console:0 */\nimport dom from '../features/dom';\nimport { type FrameportFunctionType } from '../types';\nimport { getHeaders } from '../utils/headers';\n\n/**\n * A function to initialize frameport when the DOM is ready.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // domReady(myRDE);\n * ```\n */\nexport const domReady = (frameport: FrameportFunctionType): void => {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n frameport();\n });\n } else {\n // `DOMContentLoaded` already fired\n frameport();\n }\n};\n\n/**\n * A function to initialize lazy frameport functionality.\n *\n * @example\n * ```ts\n * // Usage example:\n * // lazy();\n * ```\n */\nexport const lazy = (): void => {\n const frameportObserverTarget = new IntersectionObserver((els, observer) => {\n els.forEach((el: IntersectionObserverEntry) => {\n if (el.intersectionRatio > 0) {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = el.target as HTMLElement;\n\n let html = el.target.innerHTML;\n let templateElementToUse = el.target as HTMLElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(el.target as HTMLElement, options);\n observer.unobserve(el.target);\n }\n });\n });\n\n document.querySelectorAll('[data-frameport]').forEach((el) => {\n frameportObserverTarget.observe(el);\n });\n};\n\n/**\n * A function to manually activate frameport.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // manual(myRDE);\n * ```\n */\nexport const manual = (frameport: FrameportFunctionType): void => {\n window.frameport = frameport;\n};\n\n/**\n * A function to activate frameport based on script attributes.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // activate(myRDE);\n * ```\n */\nexport const activate = (frameport: FrameportFunctionType): void => {\n const script = document.currentScript;\n\n if (script) {\n const frameportScriptSrc = script.getAttribute('src');\n\n if (frameportScriptSrc && frameportScriptSrc.includes('frameport.js')) {\n if (script.hasAttribute('data-manual')) {\n manual(frameport);\n } else if (script.hasAttribute('data-instant')) {\n frameport();\n } else if (script.hasAttribute('data-dom')) {\n domReady(frameport);\n } else if (script.hasAttribute('data-lazy')) {\n lazy();\n } else {\n domReady(frameport);\n }\n }\n }\n};\n","/* eslint-disable import/no-unused-modules */\nimport { domReady, lazy, manual, activate } from './config/browser';\nimport dom from './features/dom';\nimport { getHeaders } from './utils/headers';\n\n/**\n * Available initialization modes for frameport usage.\n * Can be triggered manually or via browser lifecycle hooks.\n */\nexport const modes = {\n domReady,\n lazy,\n manual,\n activate\n};\n\n/**\n * Transforms all DOM elements marked with `[data-frameport]` into embedded iframes\n * with sandboxed content based on HTML templates and associated metadata.\n *\n * Also removes any existing `[data-frameport-iframe]` elements from the document,\n * ensuring re-renders are clean.\n *\n * Reads attributes such as:\n * - `data-frameport-template`\n * - `data-frameport-css`\n * - `data-frameport-style`\n * - `data-frameport-code`\n * - `data-frameport-js`\n * - `data-frameport-class`\n * - `data-frameport-headers`\n * - `data-frameport-viewports`\n * - `data-frameport-vh`\n * - `data-frameport-vw`\n *\n * @example\n * ```html\n * <div\n * data-frameport\n * data-frameport-template=\"#my-template\"\n * data-frameport-vh=\"100\"\n * data-frameport-vw=\"100\"\n * data-frameport-css=\"styles.css\"\n * data-frameport-js=\"script.js\"\n * ></div>\n *\n * <template id=\"my-template\">\n * <h1>Hello, sandbox!</h1>\n * </template>\n * ```\n *\n * @function frameport\n * @returns void\n */\nconst frameport = () => {\n // Remove all generated iframes first\n document\n .querySelectorAll('[data-frameport-iframe]')\n .forEach((iframe) => iframe.remove());\n\n // Find all frameport targets\n const elsToBeTransformedTemplate =\n document.querySelectorAll('[data-frameport]');\n\n elsToBeTransformedTemplate.forEach((targetElement: HTMLElement) => {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = targetElement;\n\n let html = targetElement.innerHTML;\n let templateElementToUse = targetElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(targetElement, options);\n });\n};\n\nexport default frameport;\n\n// Automatically activate frameport logic\nactivate(frameport);\n"],"names":["getSource","options","style","css","code","javascript","html","headers","window","location","protocol","host","trim","getCSS","getJavaScript","getCode","getStyle","join","getGeneratedPageURL","type","blob","Blob","URL","createObjectURL","getBlobURL","add","async","el","styles","Array","isArray","length","Object","keys","constructor","Promise","requestAnimationFrame","forEach","key","value","create","className","height","width","url","iframeElement","document","createElement","iframeStyle","src","setAttribute","classList","addStyles","HIDE_TEMPLATE_STYLE","clip","margin","overflow","position","DEFAULT_HEADERS","dom","targetElement","viewports","templateElement","target","screens","includes","split","push","viewPort","values","insertAdjacentElement","generateViewports","append","generate","getHeaders","rdeHeaders","map","h","domReady","frameport","readyState","addEventListener","lazy","frameportObserverTarget","IntersectionObserver","els","observer","intersectionRatio","dataset","frameportTemplate","templateSelector","frameportVh","frameportVw","frameportCss","frameportStyle","frameportCode","frameportJs","frameportClass","frameportHeaders","frameportViewports","innerHTML","templateElementToUse","querySelector","unobserve","querySelectorAll","observe","manual","activate","script","currentScript","frameportScriptSrc","getAttribute","hasAttribute","modes","iframe","remove"],"mappings":"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"}
package/dist/frameport.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * @phun-ky/frameport
3
3
  * A zero dependency package to effortlessly create responsive component previews in documentation using real media queries. Frameport dynamically generates iframes containing your HTML/CSS/JS, mimicking natural device viewports to test responsiveness, without any build steps or dependencies.
4
4
  * @author Alexander Vassbotn Røyne-Helgesen <alexander+frameport@phun-ky.net>
5
- * @version 2.0.30
5
+ * @version 2.0.31
6
6
  * @license
7
7
  * Copyright (c) 2023 Alexander Vassbotn Røyne-Helgesen
8
8
  *
@@ -24,5 +24,5 @@
24
24
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
25
  * SOFTWARE.
26
26
  */
27
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).frameport={})}(this,(function(t){"use strict";const e=t=>{let{style:e,css:r,code:o,javascript:a}=t;const{html:n,headers:s=[]}=t;return r=(t=>t&&""!==t?`<link rel="stylesheet" type="text/css" href="${window.location.protocol}//${window.location.host}${t.trim()}" />`:"")(r),a=(t=>t&&""!==t?`<script src="${window.location.protocol}//${window.location.host}${t.trim()}"><\/script>`:"")(a),o=(t=>t&&""!==t?`<script>${t.trim()}<\/script>`:"")(o),e=(t=>t&&""!==t?`<style type="text/css">${t}</style>`:"")(e),`<!DOCTYPE html>\n<html>\n<head>\n${s.join("\n")}\n${e}\n${r}\n</head>\n<body>\n${n||""}\n${a}\n${o}\n</body>\n</html>`},r=t=>((t,e)=>{const r=new Blob([t],{type:e});return URL.createObjectURL(r)})(e(t),"text/html"),o=async(t,e)=>{!t||!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e||Array.isArray(e)&&0===e.length||0===Object.keys(e).length&&e.constructor===Object||(await new Promise(requestAnimationFrame),Array.isArray(e)?e.forEach((e=>t.style[e.key]=e.value)):Object.keys(e).forEach((r=>t.style[r]=e[r])))},a=t=>{const{className:e,height:a,width:n}=t,s=r(t),i=document.createElement("iframe"),c={};return i.src=s,i.setAttribute("data-rde-iframe",""),e&&""!==e?i.classList.add(e):c.border="none",c.width=`${n}px`,a&&(c.height=`${a}px`),o(i,c),i},n={clip:"rect(1px, 1px, 1px, 1px)",height:"1px",margin:"0",overflow:"hidden",position:"absolute",width:"1px"},s=['<meta charset="utf-8" />','<meta name="robots" content="none" />','<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />','<meta name="viewport" content="width=device-width, initial-scale=1" />'],i=(t,e)=>{if(!t||!e||e&&0===Object.keys(e).length)return;const{html:r,viewports:s,templateElement:i}=e;r&&""!==r&&(o(i,n),s?((t,e)=>{const{viewports:r}=e;if(!r||""===r)return;let o=[];r.includes(",")?o=[...o,...r.split(",")]:o.push(r);for(const r of o){const o=r.split("x"),[n,s]=o,i=a({...e,height:s,width:n});t.insertAdjacentElement("afterend",i)}})(t,e):((t,e)=>{const{width:r}=e;if(!r)return;const o=a(e);t.append(o)})(t,e))},c=t=>{let e=[...s];return t&&(Array.isArray(t)?e=[...e,...t].map((t=>t.trim())):t.includes(",")?e=[...e,...t.split(",")].map((t=>t.trim())):""!==t&&e.push(t.trim())),e},l=t=>{"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>{t()})):t()},m=()=>{const t=new IntersectionObserver(((t,e)=>{t.forEach((t=>{if(t.intersectionRatio>0){const{dataset:{frameportTemplate:r,frameportVh:o,frameportVw:a,frameportCss:n,frameportStyle:s,frameportCode:l,frameportJs:m,frameportClass:p,frameportHeaders:d,frameportViewports:f}}=t.target;let h=t.target.innerHTML,u=t.target;if(r){const t=document.querySelector(r);t&&(h=t.innerHTML,u=t)}const y={templateSelector:r,templateElement:u,height:o,width:a,html:h,css:n,style:s,code:l,javascript:m,className:p,headers:c(d),viewports:f};i(t.target,y),e.unobserve(t.target)}}))}));document.querySelectorAll("[data-frameport]").forEach((e=>{t.observe(e)}))},p=t=>{window.frameport=t},d=t=>{const e=document.currentScript;if(e){const r=e.getAttribute("src");r&&r.includes("frameport.js")&&(e.hasAttribute("data-manual")?p(t):e.hasAttribute("data-instant")?t():e.hasAttribute("data-dom")?l(t):e.hasAttribute("data-lazy")?m():l(t))}},f={domReady:l,lazy:m,manual:p,activate:d},h=()=>{document.querySelectorAll("[data-frameport-iframe]").forEach((t=>t.remove()));document.querySelectorAll("[data-frameport]").forEach((t=>{const{dataset:{frameportTemplate:e,frameportVh:r,frameportVw:o,frameportCss:a,frameportStyle:n,frameportCode:s,frameportJs:l,frameportClass:m,frameportHeaders:p,frameportViewports:d}}=t;let f=t.innerHTML,h=t;if(e){const t=document.querySelector(e);t&&(f=t.innerHTML,h=t)}const u={templateSelector:e,templateElement:h,height:r,width:o,html:f,css:a,style:n,code:s,javascript:l,className:m,headers:c(p),viewports:d};i(t,u)}))};d(h),t.default=h,t.modes=f,Object.defineProperty(t,"__esModule",{value:!0})}));
27
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).frameport={})}(this,function(t){"use strict";const e=t=>{let{style:e,css:r,code:o,javascript:a}=t;const{html:n,headers:s=[]}=t;return r=(t=>t&&""!==t?`<link rel="stylesheet" type="text/css" href="${window.location.protocol}//${window.location.host}${t.trim()}" />`:"")(r),a=(t=>t&&""!==t?`<script src="${window.location.protocol}//${window.location.host}${t.trim()}"><\/script>`:"")(a),o=(t=>t&&""!==t?`<script>${t.trim()}<\/script>`:"")(o),e=(t=>t&&""!==t?`<style type="text/css">${t}</style>`:"")(e),`<!DOCTYPE html>\n<html>\n<head>\n${s.join("\n")}\n${e}\n${r}\n</head>\n<body>\n${n||""}\n${a}\n${o}\n</body>\n</html>`},r=t=>((t,e)=>{const r=new Blob([t],{type:e});return URL.createObjectURL(r)})(e(t),"text/html"),o=async(t,e)=>{!t||!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e||Array.isArray(e)&&0===e.length||0===Object.keys(e).length&&e.constructor===Object||(await new Promise(requestAnimationFrame),Array.isArray(e)?e.forEach(e=>t.style[e.key]=e.value):Object.keys(e).forEach(r=>t.style[r]=e[r]))},a=t=>{const{className:e,height:a,width:n}=t,s=r(t),i=document.createElement("iframe"),c={};return i.src=s,i.setAttribute("data-rde-iframe",""),e&&""!==e?i.classList.add(e):c.border="none",c.width=`${n}px`,a&&(c.height=`${a}px`),o(i,c),i},n={clip:"rect(1px, 1px, 1px, 1px)",height:"1px",margin:"0",overflow:"hidden",position:"absolute",width:"1px"},s=['<meta charset="utf-8" />','<meta name="robots" content="none" />','<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />','<meta name="viewport" content="width=device-width, initial-scale=1" />'],i=(t,e)=>{if(!t||!e||e&&0===Object.keys(e).length)return;const{html:r,viewports:s,templateElement:i}=e;r&&""!==r&&(o(i,n),s?((t,e)=>{const{viewports:r}=e;if(!r||""===r)return;let o=[];r.includes(",")?o=[...o,...r.split(",")]:o.push(r);for(const r of o){const o=r.split("x"),[n,s]=o,i=a({...e,height:s,width:n});t.insertAdjacentElement("afterend",i)}})(t,e):((t,e)=>{const{width:r}=e;if(!r)return;const o=a(e);t.append(o)})(t,e))},c=t=>{let e=[...s];return t&&(Array.isArray(t)?e=[...e,...t].map(t=>t.trim()):t.includes(",")?e=[...e,...t.split(",")].map(t=>t.trim()):""!==t&&e.push(t.trim())),e},l=t=>{"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{t()}):t()},m=()=>{const t=new IntersectionObserver((t,e)=>{t.forEach(t=>{if(t.intersectionRatio>0){const{dataset:{frameportTemplate:r,frameportVh:o,frameportVw:a,frameportCss:n,frameportStyle:s,frameportCode:l,frameportJs:m,frameportClass:p,frameportHeaders:d,frameportViewports:f}}=t.target;let h=t.target.innerHTML,u=t.target;if(r){const t=document.querySelector(r);t&&(h=t.innerHTML,u=t)}const y={templateSelector:r,templateElement:u,height:o,width:a,html:h,css:n,style:s,code:l,javascript:m,className:p,headers:c(d),viewports:f};i(t.target,y),e.unobserve(t.target)}})});document.querySelectorAll("[data-frameport]").forEach(e=>{t.observe(e)})},p=t=>{window.frameport=t},d=t=>{const e=document.currentScript;if(e){const r=e.getAttribute("src");r&&r.includes("frameport.js")&&(e.hasAttribute("data-manual")?p(t):e.hasAttribute("data-instant")?t():e.hasAttribute("data-dom")?l(t):e.hasAttribute("data-lazy")?m():l(t))}},f={domReady:l,lazy:m,manual:p,activate:d},h=()=>{document.querySelectorAll("[data-frameport-iframe]").forEach(t=>t.remove());document.querySelectorAll("[data-frameport]").forEach(t=>{const{dataset:{frameportTemplate:e,frameportVh:r,frameportVw:o,frameportCss:a,frameportStyle:n,frameportCode:s,frameportJs:l,frameportClass:m,frameportHeaders:p,frameportViewports:d}}=t;let f=t.innerHTML,h=t;if(e){const t=document.querySelector(e);t&&(f=t.innerHTML,h=t)}const u={templateSelector:e,templateElement:h,height:r,width:o,html:f,css:a,style:n,code:s,javascript:l,className:m,headers:c(p),viewports:d};i(t,u)})};d(h),t.default=h,t.modes=f,Object.defineProperty(t,"__esModule",{value:!0})});
28
28
  //# sourceMappingURL=frameport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"frameport.js","sources":["../src/utils/iframe.ts","../src/utils/source.ts","../src/utils/css.ts","../src/utils/js.ts","../src/utils/code.ts","../src/utils/style.ts","../src/utils/page.ts","../src/utils/blob.ts","../src/utils/styles.ts","../src/utils/wait.ts","../src/utils/create.ts","../src/constants/index.ts","../src/features/dom.ts","../src/config/generate-viewports.ts","../src/config/generate.ts","../src/utils/headers.ts","../src/config/browser.ts","../src/main.ts"],"sourcesContent":["/**\n * Create and return an iframe element.\n *\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const createIframe = (): HTMLIFrameElement =>\n document.createElement('iframe');\n","import { type FrameportOptionsInterface } from '../types';\n\nimport { getCode } from './code';\nimport { getCSS } from './css';\nimport { getJavaScript } from './js';\nimport { getStyle } from './style';\n\n/**\n * Generate the source code for an HTML page based on the provided options.\n *\n * @param {FrameportOptionsInterface} options - The options for generating the HTML page source.\n * @returns {string} - The generated HTML source code as a string.\n */\nexport const getSource = (options: FrameportOptionsInterface): string => {\n let { style, css, code, javascript } = options;\n\n const { html, headers = [] } = options;\n\n css = getCSS(css);\n javascript = getJavaScript(javascript);\n code = getCode(code);\n style = getStyle(style);\n\n return `<!DOCTYPE html>\n<html>\n<head>\n${headers.join('\\n')}\n${style}\n${css}\n</head>\n<body>\n${html || ''}\n${javascript}\n${code}\n</body>\n</html>`;\n};\n","/**\n * Generate a CSS link tag based on the provided URL.\n *\n * @param {string | undefined} css - The URL of the CSS file.\n * @returns {string} - A CSS link tag.\n */\nexport const getCSS = (css: string | undefined): string => {\n if (css && css !== '') {\n return `<link rel=\"stylesheet\" type=\"text/css\" href=\"${window.location.protocol}//${window.location.host}${css.trim()}\" />`;\n }\n\n return '';\n};\n","/**\n * Generate a script tag for the specified JavaScript file.\n *\n * @param {string | undefined} javascript - The path to the JavaScript file.\n * @returns {string} - The generated script tag or an empty string if no JavaScript path is provided.\n */\nexport const getJavaScript = (javascript: string | undefined): string => {\n if (javascript && javascript !== '') {\n return `<script src=\"${window.location.protocol}//${window.location.host}${javascript.trim()}\"></script>`;\n }\n\n return '';\n};\n","/**\n * Get a script element containing the provided code if available.\n *\n * @param {string | undefined} code - The code to include in the script element.\n * @returns {string} - The script element or an empty string if code is not available.\n */\nexport const getCode = (code: string | undefined): string => {\n if (code && code !== '') {\n return `<script>${code.trim()}</script>`;\n }\n\n return '';\n};\n","/**\n * Generate a style element based on the provided CSS styles.\n *\n * @param {string | undefined} style - The CSS styles to include in the style element.\n * @returns {string} - The style element as a string or an empty string if no styles are provided.\n */\nexport const getStyle = (style: string | undefined): string => {\n if (style && style !== '') {\n return `<style type=\"text/css\">${style}</style>`;\n }\n\n return '';\n};\n","import { getBlobURL } from './blob';\nimport { getSource } from './source';\n\n/**\n * Get the URL of a generated HTML page based on the provided options.\n *\n * @param {object} options - The options for generating the HTML page.\n * @returns {string} - The URL of the generated HTML page as a Blob URL.\n */\nexport const getGeneratedPageURL = (options) => {\n const source = getSource(options);\n\n return getBlobURL(source, 'text/html');\n};\n","/**\n * Generates a Blob URL from HTML content with the specified MIME type.\n *\n * @param {string} html - The HTML content to create a Blob from.\n * @param {string} type - The MIME type of the Blob (e.g., 'text/html', 'image/jpeg').\n * @returns {string} - The generated Blob URL.\n */\nexport const getBlobURL = (html: string, type: string): string => {\n const blob = new Blob([html], { type });\n\n return URL.createObjectURL(blob);\n};\n","/* eslint no-console:0 */\nimport { waitForFrame } from './wait';\n\n/**\n * Adds CSS styles to an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to apply styles to.\n * @param {object | Array<{ key: string; value: string }>} styles - An object or an array of objects containing CSS styles to apply.\n * @returns {Promise<void>} - A Promise that resolves after styles are applied.\n *\n * @example\n * ```ts\n * // Apply styles as an object\n * const element = document.getElementById('my-element');\n * await add(element, { color: 'red', fontSize: '16px' });\n *\n * // Apply styles as an array of objects\n * const styles = [\n * { key: 'color', value: 'blue' },\n * { key: 'backgroundColor', value: 'yellow' }\n * ];\n * await add(element, styles);\n * ```\n */\nexport const add = async (\n el: HTMLElement,\n styles: object | { key: string; value: string }[]\n): Promise<void> => {\n if (\n !el ||\n !styles ||\n typeof styles === 'string' ||\n typeof styles === 'number' ||\n typeof styles === 'boolean' ||\n (Array.isArray(styles) && styles.length === 0) ||\n (Object.keys(styles).length === 0 && styles.constructor === Object)\n ) {\n return;\n }\n\n await waitForFrame();\n\n if (Array.isArray(styles)) {\n styles.forEach(\n (style: { key: string; value: string }) =>\n (el.style[style.key] = style.value)\n );\n } else {\n Object.keys(styles).forEach((key) => (el.style[key] = styles[key]));\n }\n};\n\n/**\n * Gets the computed CSS styles of an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to get computed styles from.\n * @returns {Promise<CSSStyleDeclaration>} - A Promise that resolves with the computed CSS styles.\n *\n * @example\n * ```ts\n * // Get computed styles of an element\n * const element = document.getElementById('my-element');\n * const computedStyles = await get(element);\n * console.log(computedStyles.color); // Logs the color property value\n * ```\n */\nexport const get = async (el: HTMLElement): Promise<CSSStyleDeclaration> => {\n await waitForFrame();\n\n return getComputedStyle(el, null);\n};\n","/**\n * Waits for the next animation frame using requestAnimationFrame.\n *\n * @returns {Promise<number>} - A Promise that resolves with the timestamp of the next animation frame.\n *\n * @example\n * ```ts\n * // Wait for the next animation frame and get the rect\n * await waitForFrame();\n * const rect = el.getBoundingClientRect();\n * // Wait for the next animation frame and get the timestamp\n * const timestamp = await waitForFrame();\n * ```\n */\nexport const waitForFrame = (): Promise<number> =>\n new Promise<number>(requestAnimationFrame);\n","import { type FrameportOptionsInterface } from '../types';\nimport { createIframe } from '../utils/iframe';\nimport { getGeneratedPageURL } from '../utils/page';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Create an iframe element with specified options and styles.\n *\n * @param {FrameportOptionsInterface} options - The options for creating the iframe.\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const create = (\n options: FrameportOptionsInterface\n): HTMLIFrameElement => {\n const { className, height, width } = options;\n const url = getGeneratedPageURL(options);\n const iframeElement = createIframe();\n const iframeStyle = {};\n\n iframeElement.src = url;\n iframeElement.setAttribute('data-rde-iframe', '');\n\n if (!className || className === '') {\n iframeStyle['border'] = 'none';\n } else {\n iframeElement.classList.add(className);\n }\n\n iframeStyle['width'] = `${width}px`;\n\n if (height) {\n iframeStyle['height'] = `${height}px`;\n }\n\n addStyles(iframeElement, iframeStyle);\n\n return iframeElement;\n};\n","/**\n * @constant\n * @description\n * Inline style used to visually hide an element from layout and screen readers,\n * often used to hide `<template>` elements or accessibility-hidden content.\n *\n * Mimics techniques commonly recommended for a11y-invisible content without removing from DOM.\n *\n * @type {{ clip: string, height: string, margin: string, overflow: string, position: string, width: string }}\n *\n * @example\n * ```ts\n * element.style = { ...HIDE_TEMPLATE_STYLE };\n * ```\n */\nexport const HIDE_TEMPLATE_STYLE: {\n clip: string;\n height: string;\n margin: string;\n overflow: string;\n position: string;\n width: string;\n} = {\n clip: 'rect(1px, 1px, 1px, 1px)',\n height: '1px',\n margin: '0',\n overflow: 'hidden',\n position: 'absolute',\n width: '1px'\n};\n\n/**\n * @constant\n * @description\n * Default `<meta>` tags to inject into a dynamically created HTML document.\n * Ensures consistent encoding, responsive layout, and no indexing by robots.\n *\n * Useful when rendering sandboxed `<iframe>` content or initializing virtual DOMs.\n *\n * @type {string[]}\n *\n * @example\n * ```ts\n * const headHtml = DEFAULT_HEADERS.join('\\n');\n * iframeDoc.head.innerHTML = headHtml;\n * ```\n */\nexport const DEFAULT_HEADERS: string[] = [\n '<meta charset=\"utf-8\" />',\n '<meta name=\"robots\" content=\"none\" />',\n '<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />'\n];\n","import { generate } from '../config/generate';\nimport { generateViewports } from '../config/generate-viewports';\nimport { HIDE_TEMPLATE_STYLE } from '../constants';\nimport { type FrameportOptionsInterface } from '../types';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Generate iframes into the DOM, possibly generating viewports.\n *\n * @param {HTMLElement} targetElement - The target HTML element.\n * @param {FrameportOptionsInterface} options - The options for the iframe.\n * @returns {void}\n */\nconst dom = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n if (\n !targetElement ||\n !options ||\n (options && Object.keys(options).length === 0)\n )\n return;\n\n const { html, viewports, templateElement } = options;\n\n if (!html || html === '') return;\n\n addStyles(templateElement as HTMLElement, HIDE_TEMPLATE_STYLE);\n\n if (viewports) {\n generateViewports(targetElement, options);\n } else {\n generate(targetElement, options);\n }\n};\n\nexport default dom;\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate multiple iframe elements for different viewports and append them to a target element.\n *\n * @param {HTMLElement} target - The target HTML element to insert iframes after.\n * @param {FrameportOptionsInterface} options - The options for generating the iframes.\n * @returns {void}\n */\nexport const generateViewports = (\n target: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { viewports } = options;\n\n if (!viewports || viewports === '') return;\n\n let screens: string[] = [];\n\n if (viewports.includes(',')) screens = [...screens, ...viewports.split(',')];\n else screens.push(viewports);\n\n for (const viewPort of screens) {\n const values = viewPort.split('x');\n const [width, height] = values;\n const iframeElement = create({ ...options, height, width });\n\n target.insertAdjacentElement('afterend', iframeElement);\n }\n};\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate an iframe with the given options and append it to a target element.\n *\n * @param {HTMLElement} targetElement - The target HTML element to append the iframe to.\n * @param {FrameportOptionsInterface} options - The options for generating the iframe.\n * @returns {void}\n */\nexport const generate = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { width } = options;\n\n if (!width) return;\n\n const iframeElement = create(options);\n\n targetElement.append(iframeElement);\n};\n","import { DEFAULT_HEADERS } from '../constants';\n\n/**\n * Get headers for the iframe generated\n *\n * @param {string|string[]|null|undefined} rdeHeaders - The custom headers to include.\n * @returns {string[]} - An array of headers, including default and custom headers.\n */\nexport const getHeaders = (\n rdeHeaders: string | string[] | null | undefined\n): string[] => {\n let headers: string[] = [...DEFAULT_HEADERS];\n\n if (rdeHeaders) {\n if (Array.isArray(rdeHeaders)) {\n headers = [...headers, ...rdeHeaders].map((h) => h.trim());\n } else if (rdeHeaders.includes(',')) {\n headers = [...headers, ...rdeHeaders.split(',')].map((h) => h.trim());\n } else if (rdeHeaders !== '') {\n headers.push(rdeHeaders.trim());\n }\n }\n\n return headers;\n};\n","/* eslint no-console:0 */\nimport dom from '../features/dom';\nimport { type FrameportFunctionType } from '../types';\nimport { getHeaders } from '../utils/headers';\n\n/**\n * A function to initialize frameport when the DOM is ready.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // domReady(myRDE);\n * ```\n */\nexport const domReady = (frameport: FrameportFunctionType): void => {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n frameport();\n });\n } else {\n // `DOMContentLoaded` already fired\n frameport();\n }\n};\n\n/**\n * A function to initialize lazy frameport functionality.\n *\n * @example\n * ```ts\n * // Usage example:\n * // lazy();\n * ```\n */\nexport const lazy = (): void => {\n const frameportObserverTarget = new IntersectionObserver((els, observer) => {\n els.forEach((el: IntersectionObserverEntry) => {\n if (el.intersectionRatio > 0) {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = el.target as HTMLElement;\n\n let html = el.target.innerHTML;\n let templateElementToUse = el.target as HTMLElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(el.target as HTMLElement, options);\n observer.unobserve(el.target);\n }\n });\n });\n\n document.querySelectorAll('[data-frameport]').forEach((el) => {\n frameportObserverTarget.observe(el);\n });\n};\n\n/**\n * A function to manually activate frameport.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // manual(myRDE);\n * ```\n */\nexport const manual = (frameport: FrameportFunctionType): void => {\n window.frameport = frameport;\n};\n\n/**\n * A function to activate frameport based on script attributes.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // activate(myRDE);\n * ```\n */\nexport const activate = (frameport: FrameportFunctionType): void => {\n const script = document.currentScript;\n\n if (script) {\n const frameportScriptSrc = script.getAttribute('src');\n\n if (frameportScriptSrc && frameportScriptSrc.includes('frameport.js')) {\n if (script.hasAttribute('data-manual')) {\n manual(frameport);\n } else if (script.hasAttribute('data-instant')) {\n frameport();\n } else if (script.hasAttribute('data-dom')) {\n domReady(frameport);\n } else if (script.hasAttribute('data-lazy')) {\n lazy();\n } else {\n domReady(frameport);\n }\n }\n }\n};\n","/* eslint-disable import/no-unused-modules */\nimport { domReady, lazy, manual, activate } from './config/browser';\nimport dom from './features/dom';\nimport { getHeaders } from './utils/headers';\n\n/**\n * Available initialization modes for frameport usage.\n * Can be triggered manually or via browser lifecycle hooks.\n */\nexport const modes = {\n domReady,\n lazy,\n manual,\n activate\n};\n\n/**\n * Transforms all DOM elements marked with `[data-frameport]` into embedded iframes\n * with sandboxed content based on HTML templates and associated metadata.\n *\n * Also removes any existing `[data-frameport-iframe]` elements from the document,\n * ensuring re-renders are clean.\n *\n * Reads attributes such as:\n * - `data-frameport-template`\n * - `data-frameport-css`\n * - `data-frameport-style`\n * - `data-frameport-code`\n * - `data-frameport-js`\n * - `data-frameport-class`\n * - `data-frameport-headers`\n * - `data-frameport-viewports`\n * - `data-frameport-vh`\n * - `data-frameport-vw`\n *\n * @example\n * ```html\n * <div\n * data-frameport\n * data-frameport-template=\"#my-template\"\n * data-frameport-vh=\"100\"\n * data-frameport-vw=\"100\"\n * data-frameport-css=\"styles.css\"\n * data-frameport-js=\"script.js\"\n * ></div>\n *\n * <template id=\"my-template\">\n * <h1>Hello, sandbox!</h1>\n * </template>\n * ```\n *\n * @function frameport\n * @returns void\n */\nconst frameport = () => {\n // Remove all generated iframes first\n document\n .querySelectorAll('[data-frameport-iframe]')\n .forEach((iframe) => iframe.remove());\n\n // Find all frameport targets\n const elsToBeTransformedTemplate =\n document.querySelectorAll('[data-frameport]');\n\n elsToBeTransformedTemplate.forEach((targetElement: HTMLElement) => {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = targetElement;\n\n let html = targetElement.innerHTML;\n let templateElementToUse = targetElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(targetElement, options);\n });\n};\n\nexport default frameport;\n\n// Automatically activate frameport logic\nactivate(frameport);\n"],"names":["getSource","options","style","css","code","javascript","html","headers","window","location","protocol","host","trim","getCSS","getJavaScript","getCode","getStyle","join","getGeneratedPageURL","type","blob","Blob","URL","createObjectURL","getBlobURL","add","async","el","styles","Array","isArray","length","Object","keys","constructor","Promise","requestAnimationFrame","forEach","key","value","create","className","height","width","url","iframeElement","document","createElement","iframeStyle","src","setAttribute","classList","addStyles","HIDE_TEMPLATE_STYLE","clip","margin","overflow","position","DEFAULT_HEADERS","dom","targetElement","viewports","templateElement","target","screens","includes","split","push","viewPort","values","insertAdjacentElement","generateViewports","append","generate","getHeaders","rdeHeaders","map","h","domReady","frameport","readyState","addEventListener","lazy","frameportObserverTarget","IntersectionObserver","els","observer","intersectionRatio","dataset","frameportTemplate","templateSelector","frameportVh","frameportVw","frameportCss","frameportStyle","frameportCode","frameportJs","frameportClass","frameportHeaders","frameportViewports","innerHTML","templateElementToUse","querySelector","unobserve","querySelectorAll","observe","manual","activate","script","currentScript","frameportScriptSrc","getAttribute","hasAttribute","modes","iframe","remove"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;iPAKO,MCQMA,EAAaC,IACxB,IAAIC,MAAEA,EAAKC,IAAEA,EAAGC,KAAEA,EAAIC,WAAEA,GAAeJ,EAEvC,MAAMK,KAAEA,EAAIC,QAAEA,EAAU,IAAON,EAO/B,OALAE,ECZoB,CAACA,GACjBA,GAAe,KAARA,EACF,gDAAgDK,OAAOC,SAASC,aAAaF,OAAOC,SAASE,OAAOR,EAAIS,aAG1G,GDODC,CAAOV,GACbE,EEb2B,CAACA,GACxBA,GAA6B,KAAfA,EACT,gBAAgBG,OAAOC,SAASC,aAAaF,OAAOC,SAASE,OAAON,EAAWO,qBAGjF,GFQME,CAAcT,GAC3BD,EGdqB,CAACA,GAClBA,GAAiB,KAATA,EACH,WAAWA,EAAKQ,mBAGlB,GHSAG,CAAQX,GACfF,EIfsB,CAACA,GACnBA,GAAmB,KAAVA,EACJ,0BAA0BA,YAG5B,GJUCc,CAASd,GAEV,oCAGPK,EAAQU,KAAK,UACbf,MACAC,uBAGAG,GAAQ,OACRD,MACAD,qBAEM,EK1BKc,EAAuBjB,GCFV,EAACK,EAAca,KACvC,MAAMC,EAAO,IAAIC,KAAK,CAACf,GAAO,CAAEa,SAEhC,OAAOG,IAAIC,gBAAgBH,EAAK,EDEzBI,CAFQxB,EAAUC,GAEC,aEYfwB,EAAMC,MACjBC,EACAC,MAGGD,IACAC,GACiB,iBAAXA,GACW,iBAAXA,GACW,kBAAXA,GACNC,MAAMC,QAAQF,IAA6B,IAAlBA,EAAOG,QACD,IAA/BC,OAAOC,KAAKL,GAAQG,QAAgBH,EAAOM,cAAgBF,eCpB9D,IAAIG,QAAgBC,uBD2BhBP,MAAMC,QAAQF,GAChBA,EAAOS,SACJnC,GACEyB,EAAGzB,MAAMA,EAAMoC,KAAOpC,EAAMqC,QAGjCP,OAAOC,KAAKL,GAAQS,SAASC,GAASX,EAAGzB,MAAMoC,GAAOV,EAAOU,KAC/D,EEtCWE,EACXvC,IAEA,MAAMwC,UAAEA,EAASC,OAAEA,EAAMC,MAAEA,GAAU1C,EAC/B2C,EAAM1B,EAAoBjB,GAC1B4C,EVVNC,SAASC,cAAc,UUWjBC,EAAc,CAAA,EAmBpB,OAjBAH,EAAcI,IAAML,EACpBC,EAAcK,aAAa,kBAAmB,IAEzCT,GAA2B,KAAdA,EAGhBI,EAAcM,UAAU1B,IAAIgB,GAF5BO,EAAoB,OAAI,OAK1BA,EAAmB,MAAI,GAAGL,MAEtBD,IACFM,EAAoB,OAAI,GAAGN,OAG7BU,EAAUP,EAAeG,GAElBH,CAAa,ECrBTQ,EAOT,CACFC,KAAM,2BACNZ,OAAQ,MACRa,OAAQ,IACRC,SAAU,SACVC,SAAU,WACVd,MAAO,OAmBIe,EAA4B,CACvC,2BACA,wCACA,mEACA,0ECtCIC,EAAM,CACVC,EACA3D,KAEA,IACG2D,IACA3D,GACAA,GAA2C,IAAhC+B,OAAOC,KAAKhC,GAAS8B,OAEjC,OAEF,MAAMzB,KAAEA,EAAIuD,UAAEA,EAASC,gBAAEA,GAAoB7D,EAExCK,GAAiB,KAATA,IAEb8C,EAAUU,EAAgCT,GAEtCQ,ECpB2B,EAC/BE,EACA9D,KAEA,MAAM4D,UAAEA,GAAc5D,EAEtB,IAAK4D,GAA2B,KAAdA,EAAkB,OAEpC,IAAIG,EAAoB,GAEpBH,EAAUI,SAAS,KAAMD,EAAU,IAAIA,KAAYH,EAAUK,MAAM,MAClEF,EAAQG,KAAKN,GAElB,IAAK,MAAMO,KAAYJ,EAAS,CAC9B,MAAMK,EAASD,EAASF,MAAM,MACvBvB,EAAOD,GAAU2B,EAClBxB,EAAgBL,EAAO,IAAKvC,EAASyC,SAAQC,UAEnDoB,EAAOO,sBAAsB,WAAYzB,EAC3C,GDEE0B,CAAkBX,EAAe3D,GErBb,EACtB2D,EACA3D,KAEA,MAAM0C,MAAEA,GAAU1C,EAElB,IAAK0C,EAAO,OAEZ,MAAME,EAAgBL,EAAOvC,GAE7B2D,EAAcY,OAAO3B,EAAc,EFajC4B,CAASb,EAAe3D,GAC1B,EG1BWyE,EACXC,IAEA,IAAIpE,EAAoB,IAAImD,GAY5B,OAVIiB,IACE9C,MAAMC,QAAQ6C,GAChBpE,EAAU,IAAIA,KAAYoE,GAAYC,KAAKC,GAAMA,EAAEjE,SAC1C+D,EAAWV,SAAS,KAC7B1D,EAAU,IAAIA,KAAYoE,EAAWT,MAAM,MAAMU,KAAKC,GAAMA,EAAEjE,SACtC,KAAf+D,GACTpE,EAAQ4D,KAAKQ,EAAW/D,SAIrBL,CAAO,ECPHuE,EAAYC,IACK,YAAxBjC,SAASkC,WACXlC,SAASmC,iBAAiB,oBAAoB,KAC5CF,GAAW,IAIbA,GACF,EAYWG,EAAO,KAClB,MAAMC,EAA0B,IAAIC,sBAAqB,CAACC,EAAKC,KAC7DD,EAAIhD,SAASV,IACX,GAAIA,EAAG4D,kBAAoB,EAAG,CAC5B,MACEC,SACEC,kBAAmBC,EACnBC,YAAajD,EACbkD,YAAajD,EACbkD,aAAc1F,EACd2F,eAAgB5F,EAChB6F,cAAe3F,EACf4F,YAAa3F,EACb4F,eAAgBxD,EAChByD,iBAAkB3F,EAClB4F,mBAAoBtC,IAEpBlC,EAAGoC,OAEP,IAAIzD,EAAOqB,EAAGoC,OAAOqC,UACjBC,EAAuB1E,EAAGoC,OAE9B,GAAI2B,EAAkB,CACpB,MAAM5B,EAAkBhB,SAASwD,cAAcZ,GAE3C5B,IACFxD,EAAOwD,EAAgBsC,UACvBC,EAAuBvC,EAE3B,CAEA,MAAM7D,EAAU,CACdyF,mBACA5B,gBAAiBuC,EACjB3D,SACAC,QACArC,OACAH,MACAD,QACAE,OACAC,aACAoC,YACAlC,QAASmE,EAAWnE,GACpBsD,aAGFF,EAAIhC,EAAGoC,OAAuB9D,GAC9BqF,EAASiB,UAAU5E,EAAGoC,OACxB,IACA,IAGJjB,SAAS0D,iBAAiB,oBAAoBnE,SAASV,IACrDwD,EAAwBsB,QAAQ9E,EAAG,GACnC,EAcS+E,EAAU3B,IACrBvE,OAAOuE,UAAYA,CAAS,EAcjB4B,EAAY5B,IACvB,MAAM6B,EAAS9D,SAAS+D,cAExB,GAAID,EAAQ,CACV,MAAME,EAAqBF,EAAOG,aAAa,OAE3CD,GAAsBA,EAAmB7C,SAAS,kBAChD2C,EAAOI,aAAa,eACtBN,EAAO3B,GACE6B,EAAOI,aAAa,gBAC7BjC,IACS6B,EAAOI,aAAa,YAC7BlC,EAASC,GACA6B,EAAOI,aAAa,aAC7B9B,IAEAJ,EAASC,GAGf,GCjIWkC,EAAQ,CACnBnC,WACAI,OACAwB,SACAC,YAyCI5B,EAAY,KAEhBjC,SACG0D,iBAAiB,2BACjBnE,SAAS6E,GAAWA,EAAOC,WAI5BrE,SAAS0D,iBAAiB,oBAEDnE,SAASuB,IAClC,MACE4B,SACEC,kBAAmBC,EACnBC,YAAajD,EACbkD,YAAajD,EACbkD,aAAc1F,EACd2F,eAAgB5F,EAChB6F,cAAe3F,EACf4F,YAAa3F,EACb4F,eAAgBxD,EAChByD,iBAAkB3F,EAClB4F,mBAAoBtC,IAEpBD,EAEJ,IAAItD,EAAOsD,EAAcwC,UACrBC,EAAuBzC,EAE3B,GAAI8B,EAAkB,CACpB,MAAM5B,EAAkBhB,SAASwD,cAAcZ,GAE3C5B,IACFxD,EAAOwD,EAAgBsC,UACvBC,EAAuBvC,EAE3B,CAEA,MAAM7D,EAAU,CACdyF,mBACA5B,gBAAiBuC,EACjB3D,SACAC,QACArC,OACAH,MACAD,QACAE,OACAC,aACAoC,YACAlC,QAASmE,EAAWnE,GACpBsD,aAGFF,EAAIC,EAAe3D,EAAQ,GAC3B,EAMJ0G,EAAS5B"}
1
+ {"version":3,"file":"frameport.js","sources":["../src/utils/iframe.ts","../src/utils/source.ts","../src/utils/css.ts","../src/utils/js.ts","../src/utils/code.ts","../src/utils/style.ts","../src/utils/page.ts","../src/utils/blob.ts","../src/utils/styles.ts","../src/utils/wait.ts","../src/utils/create.ts","../src/constants/index.ts","../src/features/dom.ts","../src/config/generate-viewports.ts","../src/config/generate.ts","../src/utils/headers.ts","../src/config/browser.ts","../src/main.ts"],"sourcesContent":["/**\n * Create and return an iframe element.\n *\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const createIframe = (): HTMLIFrameElement =>\n document.createElement('iframe');\n","import { type FrameportOptionsInterface } from '../types';\n\nimport { getCode } from './code';\nimport { getCSS } from './css';\nimport { getJavaScript } from './js';\nimport { getStyle } from './style';\n\n/**\n * Generate the source code for an HTML page based on the provided options.\n *\n * @param {FrameportOptionsInterface} options - The options for generating the HTML page source.\n * @returns {string} - The generated HTML source code as a string.\n */\nexport const getSource = (options: FrameportOptionsInterface): string => {\n let { style, css, code, javascript } = options;\n\n const { html, headers = [] } = options;\n\n css = getCSS(css);\n javascript = getJavaScript(javascript);\n code = getCode(code);\n style = getStyle(style);\n\n return `<!DOCTYPE html>\n<html>\n<head>\n${headers.join('\\n')}\n${style}\n${css}\n</head>\n<body>\n${html || ''}\n${javascript}\n${code}\n</body>\n</html>`;\n};\n","/**\n * Generate a CSS link tag based on the provided URL.\n *\n * @param {string | undefined} css - The URL of the CSS file.\n * @returns {string} - A CSS link tag.\n */\nexport const getCSS = (css: string | undefined): string => {\n if (css && css !== '') {\n return `<link rel=\"stylesheet\" type=\"text/css\" href=\"${window.location.protocol}//${window.location.host}${css.trim()}\" />`;\n }\n\n return '';\n};\n","/**\n * Generate a script tag for the specified JavaScript file.\n *\n * @param {string | undefined} javascript - The path to the JavaScript file.\n * @returns {string} - The generated script tag or an empty string if no JavaScript path is provided.\n */\nexport const getJavaScript = (javascript: string | undefined): string => {\n if (javascript && javascript !== '') {\n return `<script src=\"${window.location.protocol}//${window.location.host}${javascript.trim()}\"></script>`;\n }\n\n return '';\n};\n","/**\n * Get a script element containing the provided code if available.\n *\n * @param {string | undefined} code - The code to include in the script element.\n * @returns {string} - The script element or an empty string if code is not available.\n */\nexport const getCode = (code: string | undefined): string => {\n if (code && code !== '') {\n return `<script>${code.trim()}</script>`;\n }\n\n return '';\n};\n","/**\n * Generate a style element based on the provided CSS styles.\n *\n * @param {string | undefined} style - The CSS styles to include in the style element.\n * @returns {string} - The style element as a string or an empty string if no styles are provided.\n */\nexport const getStyle = (style: string | undefined): string => {\n if (style && style !== '') {\n return `<style type=\"text/css\">${style}</style>`;\n }\n\n return '';\n};\n","import { getBlobURL } from './blob';\nimport { getSource } from './source';\n\n/**\n * Get the URL of a generated HTML page based on the provided options.\n *\n * @param {object} options - The options for generating the HTML page.\n * @returns {string} - The URL of the generated HTML page as a Blob URL.\n */\nexport const getGeneratedPageURL = (options) => {\n const source = getSource(options);\n\n return getBlobURL(source, 'text/html');\n};\n","/**\n * Generates a Blob URL from HTML content with the specified MIME type.\n *\n * @param {string} html - The HTML content to create a Blob from.\n * @param {string} type - The MIME type of the Blob (e.g., 'text/html', 'image/jpeg').\n * @returns {string} - The generated Blob URL.\n */\nexport const getBlobURL = (html: string, type: string): string => {\n const blob = new Blob([html], { type });\n\n return URL.createObjectURL(blob);\n};\n","/* eslint no-console:0 */\nimport { waitForFrame } from './wait';\n\n/**\n * Adds CSS styles to an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to apply styles to.\n * @param {object | Array<{ key: string; value: string }>} styles - An object or an array of objects containing CSS styles to apply.\n * @returns {Promise<void>} - A Promise that resolves after styles are applied.\n *\n * @example\n * ```ts\n * // Apply styles as an object\n * const element = document.getElementById('my-element');\n * await add(element, { color: 'red', fontSize: '16px' });\n *\n * // Apply styles as an array of objects\n * const styles = [\n * { key: 'color', value: 'blue' },\n * { key: 'backgroundColor', value: 'yellow' }\n * ];\n * await add(element, styles);\n * ```\n */\nexport const add = async (\n el: HTMLElement,\n styles: object | { key: string; value: string }[]\n): Promise<void> => {\n if (\n !el ||\n !styles ||\n typeof styles === 'string' ||\n typeof styles === 'number' ||\n typeof styles === 'boolean' ||\n (Array.isArray(styles) && styles.length === 0) ||\n (Object.keys(styles).length === 0 && styles.constructor === Object)\n ) {\n return;\n }\n\n await waitForFrame();\n\n if (Array.isArray(styles)) {\n styles.forEach(\n (style: { key: string; value: string }) =>\n (el.style[style.key] = style.value)\n );\n } else {\n Object.keys(styles).forEach((key) => (el.style[key] = styles[key]));\n }\n};\n\n/**\n * Gets the computed CSS styles of an HTMLElement.\n *\n * @param {HTMLElement} el - The HTMLElement to get computed styles from.\n * @returns {Promise<CSSStyleDeclaration>} - A Promise that resolves with the computed CSS styles.\n *\n * @example\n * ```ts\n * // Get computed styles of an element\n * const element = document.getElementById('my-element');\n * const computedStyles = await get(element);\n * console.log(computedStyles.color); // Logs the color property value\n * ```\n */\nexport const get = async (el: HTMLElement): Promise<CSSStyleDeclaration> => {\n await waitForFrame();\n\n return getComputedStyle(el, null);\n};\n","/**\n * Waits for the next animation frame using requestAnimationFrame.\n *\n * @returns {Promise<number>} - A Promise that resolves with the timestamp of the next animation frame.\n *\n * @example\n * ```ts\n * // Wait for the next animation frame and get the rect\n * await waitForFrame();\n * const rect = el.getBoundingClientRect();\n * // Wait for the next animation frame and get the timestamp\n * const timestamp = await waitForFrame();\n * ```\n */\nexport const waitForFrame = (): Promise<number> =>\n new Promise<number>(requestAnimationFrame);\n","import { type FrameportOptionsInterface } from '../types';\nimport { createIframe } from '../utils/iframe';\nimport { getGeneratedPageURL } from '../utils/page';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Create an iframe element with specified options and styles.\n *\n * @param {FrameportOptionsInterface} options - The options for creating the iframe.\n * @returns {HTMLIFrameElement} - The created iframe element.\n */\nexport const create = (\n options: FrameportOptionsInterface\n): HTMLIFrameElement => {\n const { className, height, width } = options;\n const url = getGeneratedPageURL(options);\n const iframeElement = createIframe();\n const iframeStyle = {};\n\n iframeElement.src = url;\n iframeElement.setAttribute('data-rde-iframe', '');\n\n if (!className || className === '') {\n iframeStyle['border'] = 'none';\n } else {\n iframeElement.classList.add(className);\n }\n\n iframeStyle['width'] = `${width}px`;\n\n if (height) {\n iframeStyle['height'] = `${height}px`;\n }\n\n addStyles(iframeElement, iframeStyle);\n\n return iframeElement;\n};\n","/**\n * @constant\n * @description\n * Inline style used to visually hide an element from layout and screen readers,\n * often used to hide `<template>` elements or accessibility-hidden content.\n *\n * Mimics techniques commonly recommended for a11y-invisible content without removing from DOM.\n *\n * @type {{ clip: string, height: string, margin: string, overflow: string, position: string, width: string }}\n *\n * @example\n * ```ts\n * element.style = { ...HIDE_TEMPLATE_STYLE };\n * ```\n */\nexport const HIDE_TEMPLATE_STYLE: {\n clip: string;\n height: string;\n margin: string;\n overflow: string;\n position: string;\n width: string;\n} = {\n clip: 'rect(1px, 1px, 1px, 1px)',\n height: '1px',\n margin: '0',\n overflow: 'hidden',\n position: 'absolute',\n width: '1px'\n};\n\n/**\n * @constant\n * @description\n * Default `<meta>` tags to inject into a dynamically created HTML document.\n * Ensures consistent encoding, responsive layout, and no indexing by robots.\n *\n * Useful when rendering sandboxed `<iframe>` content or initializing virtual DOMs.\n *\n * @type {string[]}\n *\n * @example\n * ```ts\n * const headHtml = DEFAULT_HEADERS.join('\\n');\n * iframeDoc.head.innerHTML = headHtml;\n * ```\n */\nexport const DEFAULT_HEADERS: string[] = [\n '<meta charset=\"utf-8\" />',\n '<meta name=\"robots\" content=\"none\" />',\n '<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />'\n];\n","import { generate } from '../config/generate';\nimport { generateViewports } from '../config/generate-viewports';\nimport { HIDE_TEMPLATE_STYLE } from '../constants';\nimport { type FrameportOptionsInterface } from '../types';\nimport { add as addStyles } from '../utils/styles';\n\n/**\n * Generate iframes into the DOM, possibly generating viewports.\n *\n * @param {HTMLElement} targetElement - The target HTML element.\n * @param {FrameportOptionsInterface} options - The options for the iframe.\n * @returns {void}\n */\nconst dom = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n if (\n !targetElement ||\n !options ||\n (options && Object.keys(options).length === 0)\n )\n return;\n\n const { html, viewports, templateElement } = options;\n\n if (!html || html === '') return;\n\n addStyles(templateElement as HTMLElement, HIDE_TEMPLATE_STYLE);\n\n if (viewports) {\n generateViewports(targetElement, options);\n } else {\n generate(targetElement, options);\n }\n};\n\nexport default dom;\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate multiple iframe elements for different viewports and append them to a target element.\n *\n * @param {HTMLElement} target - The target HTML element to insert iframes after.\n * @param {FrameportOptionsInterface} options - The options for generating the iframes.\n * @returns {void}\n */\nexport const generateViewports = (\n target: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { viewports } = options;\n\n if (!viewports || viewports === '') return;\n\n let screens: string[] = [];\n\n if (viewports.includes(',')) screens = [...screens, ...viewports.split(',')];\n else screens.push(viewports);\n\n for (const viewPort of screens) {\n const values = viewPort.split('x');\n const [width, height] = values;\n const iframeElement = create({ ...options, height, width });\n\n target.insertAdjacentElement('afterend', iframeElement);\n }\n};\n","import { type FrameportOptionsInterface } from '../types';\nimport { create } from '../utils/create';\n\n/**\n * Generate an iframe with the given options and append it to a target element.\n *\n * @param {HTMLElement} targetElement - The target HTML element to append the iframe to.\n * @param {FrameportOptionsInterface} options - The options for generating the iframe.\n * @returns {void}\n */\nexport const generate = (\n targetElement: HTMLElement,\n options: FrameportOptionsInterface\n): void => {\n const { width } = options;\n\n if (!width) return;\n\n const iframeElement = create(options);\n\n targetElement.append(iframeElement);\n};\n","import { DEFAULT_HEADERS } from '../constants';\n\n/**\n * Get headers for the iframe generated\n *\n * @param {string|string[]|null|undefined} rdeHeaders - The custom headers to include.\n * @returns {string[]} - An array of headers, including default and custom headers.\n */\nexport const getHeaders = (\n rdeHeaders: string | string[] | null | undefined\n): string[] => {\n let headers: string[] = [...DEFAULT_HEADERS];\n\n if (rdeHeaders) {\n if (Array.isArray(rdeHeaders)) {\n headers = [...headers, ...rdeHeaders].map((h) => h.trim());\n } else if (rdeHeaders.includes(',')) {\n headers = [...headers, ...rdeHeaders.split(',')].map((h) => h.trim());\n } else if (rdeHeaders !== '') {\n headers.push(rdeHeaders.trim());\n }\n }\n\n return headers;\n};\n","/* eslint no-console:0 */\nimport dom from '../features/dom';\nimport { type FrameportFunctionType } from '../types';\nimport { getHeaders } from '../utils/headers';\n\n/**\n * A function to initialize frameport when the DOM is ready.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // domReady(myRDE);\n * ```\n */\nexport const domReady = (frameport: FrameportFunctionType): void => {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n frameport();\n });\n } else {\n // `DOMContentLoaded` already fired\n frameport();\n }\n};\n\n/**\n * A function to initialize lazy frameport functionality.\n *\n * @example\n * ```ts\n * // Usage example:\n * // lazy();\n * ```\n */\nexport const lazy = (): void => {\n const frameportObserverTarget = new IntersectionObserver((els, observer) => {\n els.forEach((el: IntersectionObserverEntry) => {\n if (el.intersectionRatio > 0) {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = el.target as HTMLElement;\n\n let html = el.target.innerHTML;\n let templateElementToUse = el.target as HTMLElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(el.target as HTMLElement, options);\n observer.unobserve(el.target);\n }\n });\n });\n\n document.querySelectorAll('[data-frameport]').forEach((el) => {\n frameportObserverTarget.observe(el);\n });\n};\n\n/**\n * A function to manually activate frameport.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // manual(myRDE);\n * ```\n */\nexport const manual = (frameport: FrameportFunctionType): void => {\n window.frameport = frameport;\n};\n\n/**\n * A function to activate frameport based on script attributes.\n *\n * @param {FrameportFunctionType} frameport - The frameport function to execute.\n *\n * @example\n * ```ts\n * // Usage example:\n * // activate(myRDE);\n * ```\n */\nexport const activate = (frameport: FrameportFunctionType): void => {\n const script = document.currentScript;\n\n if (script) {\n const frameportScriptSrc = script.getAttribute('src');\n\n if (frameportScriptSrc && frameportScriptSrc.includes('frameport.js')) {\n if (script.hasAttribute('data-manual')) {\n manual(frameport);\n } else if (script.hasAttribute('data-instant')) {\n frameport();\n } else if (script.hasAttribute('data-dom')) {\n domReady(frameport);\n } else if (script.hasAttribute('data-lazy')) {\n lazy();\n } else {\n domReady(frameport);\n }\n }\n }\n};\n","/* eslint-disable import/no-unused-modules */\nimport { domReady, lazy, manual, activate } from './config/browser';\nimport dom from './features/dom';\nimport { getHeaders } from './utils/headers';\n\n/**\n * Available initialization modes for frameport usage.\n * Can be triggered manually or via browser lifecycle hooks.\n */\nexport const modes = {\n domReady,\n lazy,\n manual,\n activate\n};\n\n/**\n * Transforms all DOM elements marked with `[data-frameport]` into embedded iframes\n * with sandboxed content based on HTML templates and associated metadata.\n *\n * Also removes any existing `[data-frameport-iframe]` elements from the document,\n * ensuring re-renders are clean.\n *\n * Reads attributes such as:\n * - `data-frameport-template`\n * - `data-frameport-css`\n * - `data-frameport-style`\n * - `data-frameport-code`\n * - `data-frameport-js`\n * - `data-frameport-class`\n * - `data-frameport-headers`\n * - `data-frameport-viewports`\n * - `data-frameport-vh`\n * - `data-frameport-vw`\n *\n * @example\n * ```html\n * <div\n * data-frameport\n * data-frameport-template=\"#my-template\"\n * data-frameport-vh=\"100\"\n * data-frameport-vw=\"100\"\n * data-frameport-css=\"styles.css\"\n * data-frameport-js=\"script.js\"\n * ></div>\n *\n * <template id=\"my-template\">\n * <h1>Hello, sandbox!</h1>\n * </template>\n * ```\n *\n * @function frameport\n * @returns void\n */\nconst frameport = () => {\n // Remove all generated iframes first\n document\n .querySelectorAll('[data-frameport-iframe]')\n .forEach((iframe) => iframe.remove());\n\n // Find all frameport targets\n const elsToBeTransformedTemplate =\n document.querySelectorAll('[data-frameport]');\n\n elsToBeTransformedTemplate.forEach((targetElement: HTMLElement) => {\n const {\n dataset: {\n frameportTemplate: templateSelector,\n frameportVh: height,\n frameportVw: width,\n frameportCss: css,\n frameportStyle: style,\n frameportCode: code,\n frameportJs: javascript,\n frameportClass: className,\n frameportHeaders: headers,\n frameportViewports: viewports\n }\n } = targetElement;\n\n let html = targetElement.innerHTML;\n let templateElementToUse = targetElement;\n\n if (templateSelector) {\n const templateElement = document.querySelector(templateSelector);\n\n if (templateElement) {\n html = templateElement.innerHTML;\n templateElementToUse = templateElement as HTMLElement;\n }\n }\n\n const options = {\n templateSelector,\n templateElement: templateElementToUse,\n height,\n width,\n html,\n css,\n style,\n code,\n javascript,\n className,\n headers: getHeaders(headers),\n viewports\n };\n\n dom(targetElement, options);\n });\n};\n\nexport default frameport;\n\n// Automatically activate frameport logic\nactivate(frameport);\n"],"names":["getSource","options","style","css","code","javascript","html","headers","window","location","protocol","host","trim","getCSS","getJavaScript","getCode","getStyle","join","getGeneratedPageURL","type","blob","Blob","URL","createObjectURL","getBlobURL","add","async","el","styles","Array","isArray","length","Object","keys","constructor","Promise","requestAnimationFrame","forEach","key","value","create","className","height","width","url","iframeElement","document","createElement","iframeStyle","src","setAttribute","classList","addStyles","HIDE_TEMPLATE_STYLE","clip","margin","overflow","position","DEFAULT_HEADERS","dom","targetElement","viewports","templateElement","target","screens","includes","split","push","viewPort","values","insertAdjacentElement","generateViewports","append","generate","getHeaders","rdeHeaders","map","h","domReady","frameport","readyState","addEventListener","lazy","frameportObserverTarget","IntersectionObserver","els","observer","intersectionRatio","dataset","frameportTemplate","templateSelector","frameportVh","frameportVw","frameportCss","frameportStyle","frameportCode","frameportJs","frameportClass","frameportHeaders","frameportViewports","innerHTML","templateElementToUse","querySelector","unobserve","querySelectorAll","observe","manual","activate","script","currentScript","frameportScriptSrc","getAttribute","hasAttribute","modes","iframe","remove"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phun-ky/frameport",
3
- "version": "2.0.30",
3
+ "version": "2.0.31",
4
4
  "description": "A zero dependency package to effortlessly create responsive component previews in documentation using real media queries. Frameport dynamically generates iframes containing your HTML/CSS/JS, mimicking natural device viewports to test responsiveness, without any build steps or dependencies.",
5
5
  "keywords": [
6
6
  "documentation",
@@ -105,12 +105,13 @@
105
105
  "rollup-plugin-typescript2": "^0.36.0",
106
106
  "stylus": "^0.64.0",
107
107
  "tslib": "^2.3.1",
108
- "tsx": "^4.20.3",
108
+ "tsx": "^4.7.1",
109
+ "typedoc": "^0.28.2",
109
110
  "typedoc-plugin-frontmatter": "^1.0.0",
110
- "typedoc-plugin-markdown": "^4.7.1",
111
- "typedoc-plugin-mdn-links": "^5.0.5",
112
- "typedoc-plugin-no-inherit": "^1.5.0",
113
- "typedoc-plugin-remark": "^2.0.1",
111
+ "typedoc-plugin-markdown": "^4.2.3",
112
+ "typedoc-plugin-mdn-links": "^5.0.1",
113
+ "typedoc-plugin-no-inherit": "^1.4.0",
114
+ "typedoc-plugin-remark": "^2.0.0",
114
115
  "typedoc-plugin-rename-defaults": "^0.7.1",
115
116
  "typescript": "^5.7.3",
116
117
  "unified-prettier": "^2.0.1"