import { isInsideHtmlEditor } from "../core/HtmlEditor.js"; import { timelinePolyfillInstalled } from "../polyfills/polyfills.js"; import { AnimationInstaller } from "./AnimationInstaller.js"; import { installSvgClipPath } from "./installSvgClipPath.js"; import { NodeListWalker } from "./NodeListWalker.js"; import RevealAttributes from "./RevealAttributes.js"; import RevealObserver from "./RevealObserver.js"; const prefix = "styler-"; const onDomReady = (resolve) => { if (document.readyState !== "loading") { resolve(); return; } const d = () => { document.removeEventListener("DOMContentLoaded", d); resolve(); }; document.addEventListener("DOMContentLoaded", d); }; const timelineAttributes = [ 'styler-on-scroll', 'styler-on-scroll-effect-1', 'styler-on-scroll-effect-2', 'styler-on-scroll-above', 'styler-on-scroll-above-effect-1', 'styler-on-scroll-above-effect-2', 'styler-on-scroll-top-half', 'styler-on-scroll-top-half-effect-1', 'styler-on-scroll-top-half-effect-2', ]; const keyFrameAttributes = [ 'styler-on-enter', 'styler-on-enter-effect-1', 'styler-on-enter-effect-2', 'styler-on-hover', 'styler-on-hover-effect-1', 'styler-on-hover-effect-2', 'styler-on-leave', 'styler-on-leave-effect-1', 'styler-on-leave-effect-2', ... timelineAttributes ]; const maskAnimations = [ "styler-mask-on-enter" ]; const css = new CSSStyleSheet({ }); const scrollerCss = new CSSStyleSheet({}); const stylerCss = new CSSStyleSheet({ }); const stylerMap = new Set(); const updateStylerMap = (key, value, av = value) => { if(!value) { return; } try { switch(key) { case "styler-clip-path-url": // install on svg... av = `url("${installSvgClipPath(value)}")`; break; case "styler-background-image-url": av = `url("${value}")`; break; } const selector = `[${key}=${CSS.escape(value)}]`; const match = `${key}=${value},${av}` if (stylerMap.has(match)) { return; } stylerMap.add(match); stylerCss.insertRule(`${selector} { --${key}: ${av}; }`); } catch (e) { console.error(e); } }; document.adoptedStyleSheets = document.adoptedStyleSheets?.length ? [... document.adoptedStyleSheets, css, stylerCss, scrollerCss] : [css, stylerCss, scrollerCss]; document.addEventListener("scroll", ()=> { scrollerCss.replaceSync(`:root { --window-scroll-y: ${window.scrollY}px; --body-scroll-top: ${document.body.scrollTop}px; }`); }, { passive: true }); const updateMaskAnimationAttribute = (e: HTMLElement, a, remove = false) => { const url = e.getAttribute(a); const img = document.createElement("img"); img.setAttribute("src", url); updateStylerMap(a, url, `url("${img.src}")`); }; const updateKeyFrameAttribute = (e: HTMLElement, a, remove = false) => { const style = e.style; if (!style) { return; } const animationDefJson = e.getAttribute(a); if (!animationDefJson) { return; } const animationObj = AnimationInstaller.installAnimation(animationDefJson, stylerCss); if (animationObj === void 0) { const isValidAnimationName = /[\-\_a-z][\-\_a-z0-9]{0,200}/i.test(animationDefJson); if (isValidAnimationName) { updateStylerMap(a, animationDefJson); if (timelinePolyfillInstalled) { if (timelineAttributes.includes(a)) { css.insertRule(`[${a}=${CSS.escape(animationDefJson)}] { animation-duration: 1ms; animation-play-state: paused; --${animationDefJson}-animation-timeline: var(--${a}-timeline); --${animationDefJson}-animation-range: var(--${a}-range); }`); } } } return; } const { id } = animationObj; if (timelinePolyfillInstalled) { if (timelineAttributes.includes(a)) { css.insertRule(`[${a}=${CSS.escape(animationDefJson)}] { animation-duration: 1ms; animation-play-state: paused; --${id}-animation-timeline: var(--${a}-animation-timeline); --${id}-animation-range: var(--${a}-animation-range); }`); } } updateStylerMap(a, animationDefJson, id); }; onDomReady(() => { if(typeof (document as any).stylerExpansion === "undefined") { if (!document.body.hasAttribute("styler-expansion-enabled")) { return; } } const updateStyles = isInsideHtmlEditor(document.body) ? (element: HTMLElement) => { if (!element.getAttributeNames) { return; } for(const a of element.getAttributeNames()) { if (!a.startsWith(prefix)) continue; if (keyFrameAttributes.includes(a)) { updateKeyFrameAttribute(element, a); continue; } const v = element.getAttribute(a); if (v !== "custom") { updateStylerMap(a, v); } } } : (element: HTMLElement) => { if (!element.getAttributeNames) { return; } for(const a of element.getAttributeNames()) { if (!a.startsWith(prefix)) continue; if (a === "styler-on-enter") { RevealObserver.observe(element); } if (a === "styler-mask-on-enter") { RevealObserver.observe(element); RevealAttributes.install(element); } if (maskAnimations.includes(a)) { updateMaskAnimationAttribute(element, a); continue; } if (keyFrameAttributes.includes(a)) { updateKeyFrameAttribute(element, a); continue; } const v = element.getAttribute(a); if (v !== "custom") { updateStylerMap(a, v); } } }; const unobserve = (e: HTMLElement) => e.getAttributeNames && RevealObserver.unobserve(e); // const prefix = document.body.hasAttribute("editing") // ? "styler-on-" // : "styler-"; // set all attributes... const all = document.body.querySelectorAll("*"); all.forEach(updateStyles); // we will only watch for changes here. const ao = new MutationObserver((mutations) => { for (const m of mutations) { const { type } = m; if (type === "childList") { const { addedNodes, removedNodes } = m; for (const a of NodeListWalker.walk(addedNodes)) { updateStyles(a); } for(const a of NodeListWalker.walk(removedNodes)) { unobserve(a); } // addedNodes.forEach(updateStyles); // removedNodes.forEach(unobserve); continue; } if (type === "attributes") { const { target, attributeName, oldValue } = m; if (!attributeName.startsWith(prefix)) { continue; } const element = target as HTMLElement; if (attributeName === "styler-on-enter") { RevealObserver.observe(element); } if (keyFrameAttributes.includes(attributeName)) { updateKeyFrameAttribute(element, attributeName); continue; } const v = element.getAttribute(attributeName); if (oldValue === v && oldValue !== null) { // this duplication occurs for no reason... continue; } if (v && v !== "custom") { updateStylerMap(attributeName, v); } continue; } } }); ao.observe(document.body, { subtree: true, childList: true, attributes: true }); });