@hortonstudio/main 1.9.31 → 1.9.33
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.
- package/dist/assets/{hs-animations-B2hXpM0Z.js → hs-animations-DSOzyBn9.js} +3 -3
- package/dist/assets/hs-animations-DSOzyBn9.js.br +0 -0
- package/dist/assets/hs-animations-DSOzyBn9.js.gz +0 -0
- package/dist/assets/{hs-animations-B2hXpM0Z.js.map → hs-animations-DSOzyBn9.js.map} +1 -1
- package/dist/assets/{hs-counter-DxT32FUj.js → hs-counter-J3XivMD4.js} +2 -2
- package/dist/assets/hs-counter-J3XivMD4.js.br +0 -0
- package/dist/assets/hs-counter-J3XivMD4.js.gz +0 -0
- package/dist/assets/{hs-counter-DxT32FUj.js.map → hs-counter-J3XivMD4.js.map} +1 -1
- package/dist/assets/hs-default-J2cvzRiu.js +2 -0
- package/dist/assets/hs-default-J2cvzRiu.js.br +0 -0
- package/dist/assets/hs-default-J2cvzRiu.js.gz +0 -0
- package/dist/assets/hs-default-J2cvzRiu.js.map +1 -0
- package/dist/assets/{hs-marquee-SNfwk82r.js → hs-marquee-Bq_EGDiW.js} +2 -2
- package/dist/assets/hs-marquee-Bq_EGDiW.js.br +0 -0
- package/dist/assets/hs-marquee-Bq_EGDiW.js.gz +0 -0
- package/dist/assets/{hs-marquee-SNfwk82r.js.map → hs-marquee-Bq_EGDiW.js.map} +1 -1
- package/dist/assets/{hs-normalize-B-mB80zY.js → hs-normalize-CW1mxe_R.js} +2 -2
- package/dist/assets/hs-normalize-CW1mxe_R.js.br +0 -0
- package/dist/assets/hs-normalize-CW1mxe_R.js.gz +0 -0
- package/dist/assets/{hs-normalize-B-mB80zY.js.map → hs-normalize-CW1mxe_R.js.map} +1 -1
- package/dist/assets/hs-utils-BfSJf3ld.js +2 -0
- package/dist/assets/hs-utils-BfSJf3ld.js.br +0 -0
- package/dist/assets/hs-utils-BfSJf3ld.js.gz +0 -0
- package/dist/assets/hs-utils-BfSJf3ld.js.map +1 -0
- package/dist/main.js +2 -2
- package/dist/main.js.br +0 -0
- package/dist/main.js.gz +0 -0
- package/dist/main.js.map +1 -1
- package/package.json +4 -1
- package/dist/assets/hs-animations-B2hXpM0Z.js.br +0 -0
- package/dist/assets/hs-animations-B2hXpM0Z.js.gz +0 -0
- package/dist/assets/hs-counter-DxT32FUj.js.br +0 -0
- package/dist/assets/hs-counter-DxT32FUj.js.gz +0 -0
- package/dist/assets/hs-default-BVsQjN21.js +0 -2
- package/dist/assets/hs-default-BVsQjN21.js.br +0 -0
- package/dist/assets/hs-default-BVsQjN21.js.gz +0 -0
- package/dist/assets/hs-default-BVsQjN21.js.map +0 -1
- package/dist/assets/hs-marquee-SNfwk82r.js.br +0 -0
- package/dist/assets/hs-marquee-SNfwk82r.js.gz +0 -0
- package/dist/assets/hs-normalize-B-mB80zY.js.br +0 -0
- package/dist/assets/hs-normalize-B-mB80zY.js.gz +0 -0
- package/dist/assets/hs-utils-BDoLvY4b.js +0 -2
- package/dist/assets/hs-utils-BDoLvY4b.js.br +0 -0
- package/dist/assets/hs-utils-BDoLvY4b.js.gz +0 -0
- package/dist/assets/hs-utils-BDoLvY4b.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{p as e,d as r,j as t,l as s,m as a,g as i,n,r as l}from"./hs-utils-
|
|
2
|
-
//# sourceMappingURL=hs-marquee-
|
|
1
|
+
import{p as e,d as r,j as t,l as s,m as a,g as i,n,r as l}from"./hs-utils-BfSJf3ld.js";const o='[data-hs-marquee="wrapper"]',c="data-hs-marquee-direction",u="data-hs-marquee-duration",g="data-hs-marquee-scroll",d="data-hs-marquee-type",m=25,h=0;async function p(){if(e())return{result:"marquee skipped - prefers-reduced-motion enabled"};const p=r("marquee");if(!p)return{result:"marquee skipped - GSAP not loaded"};const{gsap:f,ScrollTrigger:q}=p;t("marquee",["ScrollTrigger"],"error"),q&&f.registerPlugin(q),s(),a();const k=Array.from(document.querySelectorAll(o)),A={timelines:[],scrollTriggers:[],tickers:[]};return k.forEach(e=>{const r=e.getAttribute(c),t=Array.from(e.children);if(0===t.length)return void console.warn("[marquee] No children found in marquee wrapper:",e);const s=e.getAttribute(u),a=s?parseFloat(s):m,o=e.getAttribute(g),p=o?parseFloat(o):h,q=e.getAttribute(d);s||e.setAttribute(u,String(m)),o||e.setAttribute(g,String(h)),e.classList.add(i.classes.gsap);const k=f.timeline({repeat:-1});if(t.forEach(e=>{"left"===r?k.to(e,{xPercent:-100,duration:a,ease:"none"},0):"right"===r&&(f.set(e,{xPercent:-100}),k.to(e,{xPercent:0,duration:a,ease:"none"},0))}),A.timelines.push(k),p>0){let e=1;const r=()=>{const r=l(),t=n();let s=1;if("reverse"===q)if(1===t)s=1+r*p;else if(-1===t){const e=r*p;s=e>.5?-e:1}else s=1;else{s=1+r*p}Math.abs(s-e)>.001&&(k.timeScale(s),e=s)};f.ticker.add(r),A.tickers.push({tickerFunc:r,gsap:f})}}),{result:`marquee initialized (${k.length} instances)`,destroy:()=>{A.timelines.forEach(e=>{e&&e.kill()}),A.scrollTriggers.forEach(e=>{e&&e.kill()}),A.tickers.forEach(({tickerFunc:e,gsap:r})=>{r.ticker.remove(e)}),k.forEach(e=>{e.classList.remove(i.classes.gsap)}),A.timelines.length=0,A.scrollTriggers.length=0,A.tickers.length=0}}}export{p as init};
|
|
2
|
+
//# sourceMappingURL=hs-marquee-Bq_EGDiW.js.map
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hs-marquee-
|
|
1
|
+
{"version":3,"file":"hs-marquee-Bq_EGDiW.js","sources":["../../src/animations/functions/marquee.ts"],"sourcesContent":["/**\n * Marquee Module\n *\n * GSAP-powered infinite scrolling marquee with scroll-based interactions.\n * Works with normalize/dupe module for content duplication.\n *\n * Usage:\n * <div data-hs-marquee=\"wrapper\" data-hs-marquee-direction=\"left\">\n * <div data-hs-dupe=\"child\" data-hs-dupe-count=\"2\">\n * <!-- Content here -->\n * </div>\n * </div>\n *\n * Scroll Behavior:\n * - Default: Speeds up when scrolling (magnitude only)\n * - Type \"reverse\": Scroll down speeds up, scroll up reverses animation\n * - Smooth velocity interpolation for buttery transitions\n */\n\nimport {\n globalConfig,\n getGsap,\n prefersReducedMotion,\n warnMissingDependencies,\n initScrollVelocityTracking,\n initScrollTriggerRefresh,\n getScrollVelocity,\n getScrollDirection,\n type GsapTimeline,\n} from '@utils';\n\ninterface GsapInstance {\n timeline: (vars?: Record<string, unknown>) => GsapTimeline;\n set: (target: unknown, vars: Record<string, unknown>) => void;\n registerPlugin: (...plugins: unknown[]) => void;\n ticker: {\n add: (callback: () => void) => void;\n remove: (callback: () => void) => void;\n };\n}\n\n// Selectors and Attributes\nconst SELECTORS = {\n wrapper: '[data-hs-marquee=\"wrapper\"]',\n};\n\nconst ATTRIBUTES = {\n direction: 'data-hs-marquee-direction',\n duration: 'data-hs-marquee-duration',\n scroll: 'data-hs-marquee-scroll',\n type: 'data-hs-marquee-type',\n};\n\nconst DEFAULTS = {\n duration: 25,\n scrollMultiplier: 0,\n};\n\nexport async function init() {\n // Skip if user prefers reduced motion\n if (prefersReducedMotion()) {\n return { result: 'marquee skipped - prefers-reduced-motion enabled' };\n }\n\n const gsapLib = getGsap('marquee');\n if (!gsapLib) {\n return { result: 'marquee skipped - GSAP not loaded' };\n }\n\n const { gsap, ScrollTrigger } = gsapLib;\n\n // Warn about missing required dependencies (marquee needs ScrollTrigger for scroll effects)\n warnMissingDependencies('marquee', ['ScrollTrigger'], 'error');\n\n if (ScrollTrigger) {\n gsap.registerPlugin(ScrollTrigger);\n }\n\n // Initialize global scroll utilities (only run once)\n initScrollVelocityTracking();\n initScrollTriggerRefresh();\n\n const marquees = Array.from(document.querySelectorAll(SELECTORS.wrapper));\n const cleanup = {\n timelines: [] as GsapTimeline[],\n scrollTriggers: [] as any[],\n tickers: [] as Array<{ tickerFunc: () => void; gsap: GsapInstance }>,\n };\n\n marquees.forEach((marquee) => {\n const direction = marquee.getAttribute(ATTRIBUTES.direction);\n const children = Array.from(marquee.children);\n\n if (children.length === 0) {\n console.warn('[marquee] No children found in marquee wrapper:', marquee);\n return;\n }\n\n // Get duration - check attribute first, then use default\n const durationAttr = marquee.getAttribute(ATTRIBUTES.duration);\n const duration = durationAttr ? parseFloat(durationAttr) : DEFAULTS.duration;\n\n // Get scroll multiplier - check attribute first, then use default\n const scrollAttr = marquee.getAttribute(ATTRIBUTES.scroll);\n const scrollMultiplier = scrollAttr ? parseFloat(scrollAttr) : DEFAULTS.scrollMultiplier;\n\n // Get marquee type (reverse or default)\n const marqueeType = marquee.getAttribute(ATTRIBUTES.type);\n\n // Set attributes with defaults if not present (for visibility in DOM)\n if (!durationAttr) {\n marquee.setAttribute(ATTRIBUTES.duration, String(DEFAULTS.duration));\n }\n if (!scrollAttr) {\n marquee.setAttribute(ATTRIBUTES.scroll, String(DEFAULTS.scrollMultiplier));\n }\n\n // Add class to indicate GSAP is controlling animation\n marquee.classList.add(globalConfig.classes.gsap);\n\n // Create GSAP timeline\n const tl = gsap.timeline({ repeat: -1 });\n\n // Animate all children together at the same time\n children.forEach((child) => {\n if (direction === 'left') {\n // Left: 0% → -100%\n tl.to(\n child,\n {\n xPercent: -100,\n duration: duration,\n ease: 'none',\n },\n 0\n );\n } else if (direction === 'right') {\n // Right: -100% → 0%\n gsap.set(child, { xPercent: -100 }); // Start from -100%\n tl.to(\n child,\n {\n xPercent: 0,\n duration: duration,\n ease: 'none',\n },\n 0\n );\n }\n });\n\n cleanup.timelines.push(tl);\n\n // Add scroll-based speed boost if scroll multiplier is set\n if (scrollMultiplier > 0) {\n let lastTimeScale = 1;\n\n const tickerFunc = () => {\n // Get global scroll velocity and direction\n const scrollSpeed = getScrollVelocity();\n const scrollDir = getScrollDirection();\n let newTimeScale = 1;\n\n if (marqueeType === 'reverse') {\n // Reverse type: scroll down speeds up, scroll up reverses\n if (scrollDir === 1) {\n // Scrolling down: speed up in normal direction\n newTimeScale = 1 + scrollSpeed * scrollMultiplier;\n } else if (scrollDir === -1) {\n // Scrolling up: reverse the animation\n // Use absolute minimum to prevent getting stuck at 0\n const reverseScale = scrollSpeed * scrollMultiplier;\n newTimeScale = reverseScale > 0.5 ? -reverseScale : 1;\n } else {\n // Not scrolling: return to base speed\n newTimeScale = 1;\n }\n } else {\n // Default type: just speed up based on velocity (no direction)\n const speedBoost = scrollSpeed * scrollMultiplier;\n newTimeScale = 1 + speedBoost;\n }\n\n // Only update if changed (avoid unnecessary updates)\n if (Math.abs(newTimeScale - lastTimeScale) > 0.001) {\n tl.timeScale(newTimeScale);\n lastTimeScale = newTimeScale;\n }\n };\n\n // Add ticker function to GSAP's ticker\n gsap.ticker.add(tickerFunc);\n\n // Store ticker function and gsap reference for cleanup\n cleanup.tickers.push({ tickerFunc, gsap: gsap as GsapInstance });\n }\n });\n\n return {\n result: `marquee initialized (${marquees.length} instances)`,\n destroy: () => {\n // Kill all timelines\n cleanup.timelines.forEach((tl) => {\n if (tl) tl.kill();\n });\n\n // Kill all scroll triggers\n cleanup.scrollTriggers.forEach((st) => {\n if (st) (st as any).kill();\n });\n\n // Remove all ticker functions\n cleanup.tickers.forEach(({ tickerFunc, gsap: gsapInstance }) => {\n gsapInstance.ticker.remove(tickerFunc);\n });\n\n // Remove GSAP class from all marquees\n marquees.forEach((marquee) => {\n marquee.classList.remove(globalConfig.classes.gsap);\n });\n\n // Clear arrays\n cleanup.timelines.length = 0;\n cleanup.scrollTriggers.length = 0;\n cleanup.tickers.length = 0;\n },\n };\n}\n"],"names":["SELECTORS","ATTRIBUTES","DEFAULTS","async","init","prefersReducedMotion","result","gsapLib","getGsap","gsap","ScrollTrigger","warnMissingDependencies","registerPlugin","initScrollVelocityTracking","initScrollTriggerRefresh","marquees","Array","from","document","querySelectorAll","cleanup","timelines","scrollTriggers","tickers","forEach","marquee","direction","getAttribute","children","length","console","warn","durationAttr","duration","parseFloat","scrollAttr","scrollMultiplier","marqueeType","setAttribute","String","classList","add","globalConfig","classes","tl","timeline","repeat","child","to","xPercent","ease","set","push","lastTimeScale","tickerFunc","scrollSpeed","getScrollVelocity","scrollDir","getScrollDirection","newTimeScale","reverseScale","Math","abs","timeScale","ticker","destroy","kill","st","gsapInstance","remove"],"mappings":"uFA0CA,MAAMA,EACK,8BAGLC,EACO,4BADPA,EAEM,2BAFNA,EAGI,yBAHJA,EAIE,uBAGFC,EACM,GADNA,EAEc,EAGpBC,eAAsBC,IAEpB,GAAIC,IACF,MAAO,CAAEC,OAAQ,oDAGnB,MAAMC,EAAUC,EAAQ,WACxB,IAAKD,EACH,MAAO,CAAED,OAAQ,qCAGnB,MAAMG,KAAEA,EAAAC,cAAMA,GAAkBH,EAGhCI,EAAwB,UAAW,CAAC,iBAAkB,SAElDD,GACFD,EAAKG,eAAeF,GAItBG,IACAC,IAEA,MAAMC,EAAWC,MAAMC,KAAKC,SAASC,iBAAiBnB,IAChDoB,EAAU,CACdC,UAAW,GACXC,eAAgB,GAChBC,QAAS,IAgHX,OA7GAR,EAASS,QAASC,IAChB,MAAMC,EAAYD,EAAQE,aAAa1B,GACjC2B,EAAWZ,MAAMC,KAAKQ,EAAQG,UAEpC,GAAwB,IAApBA,EAASC,OAEX,YADAC,QAAQC,KAAK,kDAAmDN,GAKlE,MAAMO,EAAeP,EAAQE,aAAa1B,GACpCgC,EAAWD,EAAeE,WAAWF,GAAgB9B,EAGrDiC,EAAaV,EAAQE,aAAa1B,GAClCmC,EAAmBD,EAAaD,WAAWC,GAAcjC,EAGzDmC,EAAcZ,EAAQE,aAAa1B,GAGpC+B,GACHP,EAAQa,aAAarC,EAAqBsC,OAAOrC,IAE9CiC,GACHV,EAAQa,aAAarC,EAAmBsC,OAAOrC,IAIjDuB,EAAQe,UAAUC,IAAIC,EAAaC,QAAQlC,MAG3C,MAAMmC,EAAKnC,EAAKoC,SAAS,CAAEC,YAiC3B,GA9BAlB,EAASJ,QAASuB,IACE,SAAdrB,EAEFkB,EAAGI,GACDD,EACA,CACEE,UAAU,IACVhB,WACAiB,KAAM,QAER,GAEqB,UAAdxB,IAETjB,EAAK0C,IAAIJ,EAAO,CAAEE,gBAClBL,EAAGI,GACDD,EACA,CACEE,SAAU,EACVhB,WACAiB,KAAM,QAER,MAKN9B,EAAQC,UAAU+B,KAAKR,GAGnBR,EAAmB,EAAG,CACxB,IAAIiB,EAAgB,EAEpB,MAAMC,EAAa,KAEjB,MAAMC,EAAcC,IACdC,EAAYC,IAClB,IAAIC,EAAe,EAEnB,GAAoB,YAAhBtB,EAEF,GAAkB,IAAdoB,EAEFE,EAAe,EAAIJ,EAAcnB,OACnC,IAAyB,IAAdqB,EAAkB,CAG3B,MAAMG,EAAeL,EAAcnB,EACnCuB,EAAeC,EAAe,IAAOA,EAAe,CACtD,MAEED,EAAe,MAEZ,CAGLA,EAAe,EADIJ,EAAcnB,CAEnC,CAGIyB,KAAKC,IAAIH,EAAeN,GAAiB,OAC3CT,EAAGmB,UAAUJ,GACbN,EAAgBM,IAKpBlD,EAAKuD,OAAOvB,IAAIa,GAGhBlC,EAAQG,QAAQ6B,KAAK,CAAEE,aAAY7C,QACrC,IAGK,CACLH,OAAQ,wBAAwBS,EAASc,oBACzCoC,QAAS,KAEP7C,EAAQC,UAAUG,QAASoB,IACrBA,KAAOsB,SAIb9C,EAAQE,eAAeE,QAAS2C,IAC1BA,KAAgBD,SAItB9C,EAAQG,QAAQC,QAAQ,EAAG8B,aAAY7C,KAAM2D,MAC3CA,EAAaJ,OAAOK,OAAOf,KAI7BvC,EAASS,QAASC,IAChBA,EAAQe,UAAU6B,OAAO3B,EAAaC,QAAQlC,QAIhDW,EAAQC,UAAUQ,OAAS,EAC3BT,EAAQE,eAAeO,OAAS,EAChCT,EAAQG,QAAQM,OAAS,GAG/B"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{q as e,a as t,g as n,b as r,c as o}from"./hs-utils-BDoLvY4b.js";function s(){return function(){const e=window.location.origin,t=e=>{const t=document.querySelector(`[data-hs-replace="${e}"]`);return t?.textContent?.trim()||""},n=t("company"),r=t("phone"),o=t("email"),s=t("city"),i=t("state"),a=t("zipcode"),c={"@context":"https://schema.org","@type":"LocalBusiness",name:n,url:e,telephone:r,email:o},l=document.querySelector('[data-hs-replace="business-logo"] img');l&&l.src&&(c.image=l.src);const d={"@type":"PostalAddress",addressLocality:s,addressRegion:i,postalCode:a,addressCountry:"US"},u=t("street-address");u&&(d.streetAddress=u),c.address=d;const m=t("business-latitude"),p=t("business-longitude");m&&p&&(c.geo={"@type":"GeoCoordinates",latitude:m,longitude:p});const h=["facebook","instagram","linkedin","pinterest","tiktok","x","youtube"].map(e=>(e=>{const t=document.querySelector(`[data-hs-replace="${e}"]`);return t?.href||""})(e)).filter(e=>e&&""!==e.trim()&&!e.includes("placeholder"));h.length>0&&(c.sameAs=h);const f=document.querySelector('[data-hs-schema="services"]');if(f){const e=f.querySelectorAll('[data-hs-schema-list="item"]'),t=Array.from(e).map(e=>e.textContent?.trim()).filter(e=>e&&""!==e);t.length>0&&(c.serviceType=t)}const y=document.querySelector('[data-hs-schema="areas"]');if(y){const e=y.querySelectorAll('[data-hs-schema-list="item"]'),t=Array.from(e).map(e=>({"@type":"City",name:e.textContent?.trim()||""})).filter(e=>e.name&&""!==e.name);t.length>0&&(c.areaServed=t)}const g=document.createElement("script");g.type="application/ld+json",g.text=JSON.stringify(c),document.head.appendChild(g);const b=document.querySelector('[data-hs-schema="breadcrumb"]')?.textContent?.trim()||"",A=window.location.pathname.split("/").filter(e=>""!==e);if(A.length>0){const t={"@context":"https://schema.org","@type":"BreadcrumbList",itemListElement:[{"@type":"ListItem",position:1,name:"Home",item:e}]},n=A[0],r=n.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ");t.itemListElement.push({"@type":"ListItem",position:2,name:r,item:e+"/"+n}),b&&t.itemListElement.push({"@type":"ListItem",position:3,name:b,item:window.location.href});const o=document.createElement("script");o.type="application/ld+json",o.text=JSON.stringify(t),document.head.appendChild(o)}if(document.querySelector('[data-hs-accordion-type="faq"]')){const e=[];if(document.querySelectorAll('[data-hs-accordion="list"]').forEach(t=>{t.querySelectorAll('[data-hs-accordion-type="faq"]').forEach(t=>{const n=t.querySelector('[data-hs-accordion="toggle"]'),r=t.querySelector('[data-hs-accordion="content"]');if(n&&r){const t=n.textContent?.trim(),o=r.innerHTML.trim();e.push({"@type":"Question",name:t,acceptedAnswer:{"@type":"Answer",text:o}})}})}),e.length>0){const t={"@context":"https://schema.org","@type":"FAQPage",mainEntity:e},n=document.createElement("script");n.type="application/ld+json",n.text=JSON.stringify(t),document.head.appendChild(n)}}const x=document.querySelector('[data-hs-schema-service="name"]'),S=document.querySelector('[data-hs-schema-service="description"]');if(x&&S){const e=x.textContent?.trim()||"",t=S.textContent?.trim()||"";if(e&&t){const n={"@context":"https://schema.org","@type":"Service",name:e,description:t},r=document.createElement("script");r.type="application/ld+json",r.text=JSON.stringify(n),document.head.appendChild(r)}}else(x||S)&&console.warn('[schema] Service page schema requires both "name" and "description" elements');const q=document.querySelector('[data-hs-schema-faq="question"]'),C=document.querySelector('[data-hs-schema-faq="answer"]'),E=document.querySelector('[data-hs-schema-faq="published"]'),w=document.querySelector('[data-hs-schema-faq="modified"]');if(q&&C&&E&&w){const e=q.textContent?.trim()||"",t=C.innerHTML.trim(),n=E.textContent?.trim()||"",r=w.textContent?.trim()||"";if(e&&t&&n&&r){const o={"@context":"https://schema.org","@type":"QAPage",mainEntity:{"@type":"Question",name:e,text:e,answerCount:1,datePublished:n,dateModified:r,acceptedAnswer:{"@type":"Answer",text:t}}},s=document.createElement("script");s.type="application/ld+json",s.text=JSON.stringify(o),document.head.appendChild(s)}}else if(q||C||E||w){const e=[];q||e.push("question"),C||e.push("answer"),E||e.push("published"),w||e.push("modified"),console.warn(`[schema] FAQ page schema missing required fields: ${e.join(", ")}`)}const v=document.querySelector('[data-hs-schema-blog="name"]'),N=document.querySelector('[data-hs-schema-blog="published"]'),$=document.querySelector('[data-hs-schema-blog="modified"]'),F=document.querySelector('[data-hs-schema-blog="summary"]'),L=document.querySelector('[data-hs-schema-blog="author-name"]'),k=document.querySelector('[data-hs-schema-blog="author-type"]');if(v&&N&&$&&F&&L&&k){const e=v.textContent?.trim()||"",n=N.textContent?.trim()||"",r=$.textContent?.trim()||"",o=F.textContent?.trim()||"",s=L.textContent?.trim()||"",i=k.textContent?.trim()||"Organization",a=document.querySelector('[data-hs-schema-blog="author-role"]'),c=document.querySelector('[data-hs-schema-blog="author-image"]'),l=a?.textContent?.trim()||"",d=c?.src||"",u=t("company"),m=document.querySelector('[data-hs-replace="business-logo"] img'),p=m?m.src:"";let h;"Person"===i?(h={"@type":"Person",name:s},l&&(h.jobTitle=l),d&&(h.image=d)):(h={"@type":"Organization",name:s},p&&(h.logo=p));const f={"@context":"https://schema.org","@type":"BlogPosting",headline:e,description:o,datePublished:n,dateModified:r,author:h,publisher:{"@type":"Organization",name:u||s,logo:{"@type":"ImageObject",url:p}},mainEntityOfPage:{"@type":"WebPage","@id":window.location.href}},y=document.createElement("script");y.type="application/ld+json",y.text=JSON.stringify(f),document.head.appendChild(y)}else if(v||N||$||F||L||k){const e=[];v||e.push("name"),N||e.push("published"),$||e.push("modified"),F||e.push("summary"),L||e.push("author-name"),k||e.push("author-type"),console.warn(`[schema] Blog page schema missing required fields: ${e.join(", ")}`)}}(),{result:"schema initialized",destroy:()=>{}}}function i(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null);let r;for(;r=n.nextNode();)r.nodeValue?.trim()&&t.push(r);return t}function a(e){const t=[];return e.hasAttribute("href")&&t.push(e),t.push(...Array.from(e.querySelectorAll("[href]"))),t}function c(e){if(e.hasAttribute("src")){if(e.getAttribute("src"))return e}const t=e.querySelector("[src]");if(t){if(t.getAttribute("src"))return t}return null}function l(e,t,n){if(function(e,t){const n=t.attributes.properties.syncField;return null!==e.querySelector(`[${n}]`)}(t,n))return void function(e,t,n){const r=n.attributes.properties.syncField;e.querySelectorAll(`[${r}]`).forEach(e=>{const n=e.getAttribute(r),o=t.querySelector(`[${r}="${n}"]`);if(!o)return;const s=c(e),a=c(o);if(s&&a){const e=s.getAttribute("src");e&&a.setAttribute("src",e)}else if(e.hasAttribute("href")&&o.hasAttribute("href")){const t=e.getAttribute("href");t&&o.setAttribute("href",t)}else{const t=i(e),n=t.length>0?t[0].nodeValue:e.textContent.trim();if(n){const e=i(o);e.length>0?e[0].nodeValue=n:o.textContent=n}}})}(e,t,n);const r=i(e),o=r.length>0?r[0].nodeValue:"",s=a(e),l=s.length>0?s[0].getAttribute("href"):"";if(o){i(t).forEach(e=>{e.nodeValue=o})}if(l){a(t).forEach(e=>{e.setAttribute("href",l)})}}const d="hs-dupe-no-transition";let u=!1;async function m(t){!function(){if(u)return;const e=document.createElement("style");e.textContent=`\n .${d},\n .${d} * {\n transition: none !important;\n animation: none !important;\n }\n `,document.head.appendChild(e),u=!0}();const n={processedElements:[]};return e(t,"wrapper").forEach(e=>{const t=e.getAttribute("data-hs-dupe");if(!t||"child"!==t&&"self"!==t)return void console.warn('[dupe] Invalid mode. Use "child" or "self":',e);const r=e.getAttribute("data-hs-dupe-count");let o=r?parseInt(r,10):1;(isNaN(o)||o<1)&&(console.warn("[dupe] Invalid count, using default of 1:",e),o=1),o>20&&(console.warn(`[dupe] Count of ${o} exceeds maximum of 20. Capping at 20.`),o=20);const s={hidden:e.hasAttribute("data-hs-dupe-hidden"),noSelect:e.hasAttribute("data-hs-dupe-no-select")};"child"===t?function(e,t,n,r){const o=e.children[0];if(!o)return;const s=[];for(let i=0;i<t;i++){const t=o.cloneNode(!0);t.classList.add(d),p(t,n),e.appendChild(t),s.push(t),t.offsetHeight,requestAnimationFrame(()=>{t.classList.remove(d)})}r.processedElements.push({element:e,mode:"child",clones:s})}(e,o,s,n):"self"===t&&function(e,t,n,r){const o=e.parentNode;if(!o)return void console.warn("[dupe] Element has no parent, cannot duplicate self:",e);const s=[];let i=e;for(let a=0;a<t;a++){const t=e.cloneNode(!0);t.classList.add(d),t.removeAttribute("data-hs-dupe"),t.removeAttribute("data-hs-dupe-count"),t.removeAttribute("data-hs-dupe-hidden"),t.removeAttribute("data-hs-dupe-no-select"),p(t,n),i.nextSibling?o.insertBefore(t,i.nextSibling):o.appendChild(t),s.push(t),i=t,t.offsetHeight,requestAnimationFrame(()=>{t.classList.remove(d)})}r.processedElements.push({element:e,mode:"self",clones:s})}(e,o,s,n)}),{result:`dupe processed ${n.processedElements.length} elements`,destroy:()=>{}}}function p(e,t){if(t.hidden&&e.setAttribute("aria-hidden","true"),t.noSelect){const t=e;t.style.userSelect="none",t.style.webkitUserSelect="none",t.style.msUserSelect="none"}}async function h(){const i={destroyFunctions:[]},a=o.normalize;try{const d=s();d&&"destroy"in d&&"function"==typeof d.destroy&&i.destroyFunctions.push(d.destroy);const u=await async function(){const t={processedWrappers:[]};return e(n.clickable,"wrapper").forEach(e=>{const n=e.querySelector(":scope > button"),r=e.querySelector(":scope > a");if(!n||!r)return;const o=r.getAttribute("href");let s,i;o&&""!==o&&"#"!==o?(s=r,i=n):(s=n,i=r);const a="button"===s.getAttribute("data-hs-clickable");a||s.setAttribute("data-hs-clickable","button"),i.remove(),t.processedWrappers.push({wrapper:e,keptElement:s,wasLink:s===r,addedAttribute:!a})}),{result:`clickable processed ${t.processedWrappers.length} wrappers`,destroy:()=>{}}}();u&&"destroy"in u&&"function"==typeof u.destroy&&i.destroyFunctions.push(u.destroy);const p=(c=o.normalize?.replace,function(){const e=new Map,t=r(c,"element");document.querySelectorAll(t).forEach(t=>{const n=t.cloneNode(!0),o=r(c,"hide");n.querySelectorAll(o).forEach(e=>{const t=c.attributes.elements.hide.primary.match(/data-hs-replace-hide/)[0],n=e.getAttribute(t);n&&n.split(" ").filter(e=>e.trim()).some(t=>e.classList.contains(t))&&e.remove()});const s=c.attributes.elements.element.primary.match(/data-hs-replace/)[0],i=t.getAttribute(s),a=n.textContent?.trim()||null,l=t.getAttribute("href");if(i&&(a||l)){const t={text:a,href:l};if(e.has(i)){const n=e.get(i);Array.isArray(n)?n.push(t):e.set(i,[n,t])}else e.set(i,t)}});const n=(new Date).getFullYear().toString(),o=["January","February","March","April","May","June","July","August","September","October","November","December"][(new Date).getMonth()];if(e.set("year",{text:n,href:null}),e.set("month",{text:o,href:null}),0===e.size)return;const s=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT,{acceptNode:t=>{const n=t.textContent||"";for(const r of e.keys())if(n.includes(`{{${r}}}`))return NodeFilter.FILTER_ACCEPT;return NodeFilter.FILTER_SKIP}}),i=[];let a;for(;a=s.nextNode();)i.push(a);i.forEach(t=>{let n=t.textContent||"",r=!1;for(const[o,s]of e.entries()){const e=`{{${o}}}`;if(n.includes(e)){let t=null;t=Array.isArray(s)?s.map(e=>e.text||e.href).filter(Boolean).join(", "):s.text||s.href,t&&(n=n.replace(new RegExp(e,"g"),t),r=!0)}}r&&(t.textContent=n)}),document.querySelectorAll("a[href]").forEach(t=>{let n=t.getAttribute("href")||"",r=!1;for(const[o,s]of e.entries()){const e=`{{${o}}}`;if(n.includes(e)){let t=null;if(Array.isArray(s)){const e=s[0];t=e.href||e.text}else{if(s.href){t=s.href,n=s.href,r=!0;break}s.text&&(t=s.text)}t&&!r&&(n=n.replace(new RegExp(e,"g"),t),r=!0)}}r&&t.setAttribute("href",n)});const l=document.querySelector("[data-hs-replace-test]");if(l){const t={};for(const[r,o]of e.entries()){const e=`{{${r}}}`;if(Array.isArray(o)){const n=o.map(e=>e.text||e.href).filter(Boolean).join(", ");t[r]={placeholder:e,value:n,type:`Array (${o.length} items)`,items:o.map(e=>e.text||e.href||"(empty)")}}else{const n=o.text||o.href||"(empty)";t[r]={placeholder:e,value:n,...o.text&&{text:o.text},...o.href&&{href:o.href}}}}const n=document.createElement("pre");n.textContent=JSON.stringify(t,null,2),l.innerHTML="",l.appendChild(document.createTextNode(`Replace Module Debug (${e.size} replacements):`)),l.appendChild(n)}}(),{result:"replace initialized",destroy:()=>{}});p&&"destroy"in p&&"function"==typeof p.destroy&&i.destroyFunctions.push(p.destroy);const h=await async function(n){const r=e(n,"source"),o=new Map,s=new Map;r.forEach(e=>{const t=e.getAttribute(n.attributes.properties.syncId),r=e.getAttribute(n.attributes.properties.syncField);t&&o.set(t,e),r&&s.set(r,e)});const i=e(n,"target");let a=0,c=0;return i.forEach(r=>{const i=r.getAttribute(n.attributes.properties.syncId),d=r.getAttribute(n.attributes.properties.syncField),u=r.getAttribute(n.attributes.properties.syncValue);let m;if(d&&u){if(m=s.get(d),!m)return void c++}else{if(!i)return void c++;if(m=o.get(i),!m)return void c++}const p=t(n,"item",r);if(!p){const e=d||i||"unknown";return void console.warn(`[sync] Target list "${e}" missing item template`)}const h="child"===p.getAttribute(n.attributes.properties.syncMode),f=t(n,"ignore",r);let y,g;if(u){const t=e(n,"item",m),r=Array.from(t).find(e=>e.getAttribute(n.attributes.properties.syncValue)===u);if(!r)return void console.warn(`[sync] No source item found with value "${u}" in field "${d}"`);y=[r]}else if(y=Array.from(e(n,"item",m)),0===y.length)return void console.warn(`[sync] Source list "${i}" has no items`);if(h){const e=p.firstElementChild;if(!e)return void console.warn(`[sync] Target list "${i}" has sync-mode="child" but item wrapper has no children`);g=e.cloneNode(!0)}else g=p.cloneNode(!0),g.removeAttribute(n.attributes.elements.item.primary.split("=")[0]);let b=!1;if(f){let e=f.nextElementSibling;for(;e;){if(e===p){b=!0;break}e=e.nextElementSibling}}if(h){for(;p.firstChild;)p.removeChild(p.firstChild);y.forEach(e=>{const t=g.cloneNode(!0);l(e,t,n),p.appendChild(t)}),p.removeAttribute(n.attributes.elements.item.primary.split("=")[0])}else{Array.from(r.children).forEach(e=>{e!==f&&e.remove()});const e=Array.from(y).map(e=>{const t=g.cloneNode(!0);return l(e,t,n),t});f?b?e.forEach(e=>{r.appendChild(e)}):e.forEach(e=>{r.insertBefore(e,f)}):e.forEach(e=>{r.appendChild(e)})}a++}),{result:`sync initialized (${a} synced, ${c} skipped)`,destroy:()=>{}}}(a.sync);h&&"destroy"in h&&"function"==typeof h.destroy&&i.destroyFunctions.push(h.destroy);const f=await m(a.dupe);return f&&"destroy"in f&&"function"==typeof f.destroy&&i.destroyFunctions.push(f.destroy),{result:"normalize initialized",destroy:()=>{i.destroyFunctions.reverse().forEach(e=>{try{e()}catch(t){console.error("[normalize] Error during cleanup:",t)}}),i.destroyFunctions.length=0}}}catch(d){throw console.error("[normalize] Initialization failed:",d),i.destroyFunctions.reverse().forEach(e=>{try{e()}catch(t){console.error("[normalize] Error during error cleanup:",t)}}),d}var c}export{h as init};
|
|
2
|
-
//# sourceMappingURL=hs-normalize-
|
|
1
|
+
import{q as e,a as t,g as n,b as r,c as o}from"./hs-utils-BfSJf3ld.js";function s(){return function(){const e=window.location.origin,t=e=>{const t=document.querySelector(`[data-hs-replace="${e}"]`);return t?.textContent?.trim()||""},n=t("company"),r=t("phone"),o=t("email"),s=t("city"),i=t("state"),a=t("zipcode"),c={"@context":"https://schema.org","@type":"LocalBusiness",name:n,url:e,telephone:r,email:o},l=document.querySelector('[data-hs-replace="business-logo"] img');l&&l.src&&(c.image=l.src);const d={"@type":"PostalAddress",addressLocality:s,addressRegion:i,postalCode:a,addressCountry:"US"},u=t("street-address");u&&(d.streetAddress=u),c.address=d;const m=t("business-latitude"),p=t("business-longitude");m&&p&&(c.geo={"@type":"GeoCoordinates",latitude:m,longitude:p});const h=["facebook","instagram","linkedin","pinterest","tiktok","x","youtube"].map(e=>(e=>{const t=document.querySelector(`[data-hs-replace="${e}"]`);return t?.href||""})(e)).filter(e=>e&&""!==e.trim()&&!e.includes("placeholder"));h.length>0&&(c.sameAs=h);const f=document.querySelector('[data-hs-schema="services"]');if(f){const e=f.querySelectorAll('[data-hs-schema-list="item"]'),t=Array.from(e).map(e=>e.textContent?.trim()).filter(e=>e&&""!==e);t.length>0&&(c.serviceType=t)}const y=document.querySelector('[data-hs-schema="areas"]');if(y){const e=y.querySelectorAll('[data-hs-schema-list="item"]'),t=Array.from(e).map(e=>({"@type":"City",name:e.textContent?.trim()||""})).filter(e=>e.name&&""!==e.name);t.length>0&&(c.areaServed=t)}const g=document.createElement("script");g.type="application/ld+json",g.text=JSON.stringify(c),document.head.appendChild(g);const b=document.querySelector('[data-hs-schema="breadcrumb"]')?.textContent?.trim()||"",A=window.location.pathname.split("/").filter(e=>""!==e);if(A.length>0){const t={"@context":"https://schema.org","@type":"BreadcrumbList",itemListElement:[{"@type":"ListItem",position:1,name:"Home",item:e}]},n=A[0],r=n.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ");t.itemListElement.push({"@type":"ListItem",position:2,name:r,item:e+"/"+n}),b&&t.itemListElement.push({"@type":"ListItem",position:3,name:b,item:window.location.href});const o=document.createElement("script");o.type="application/ld+json",o.text=JSON.stringify(t),document.head.appendChild(o)}if(document.querySelector('[data-hs-accordion-type="faq"]')){const e=[];if(document.querySelectorAll('[data-hs-accordion="list"]').forEach(t=>{t.querySelectorAll('[data-hs-accordion-type="faq"]').forEach(t=>{const n=t.querySelector('[data-hs-accordion="toggle"]'),r=t.querySelector('[data-hs-accordion="content"]');if(n&&r){const t=n.textContent?.trim(),o=r.innerHTML.trim();e.push({"@type":"Question",name:t,acceptedAnswer:{"@type":"Answer",text:o}})}})}),e.length>0){const t={"@context":"https://schema.org","@type":"FAQPage",mainEntity:e},n=document.createElement("script");n.type="application/ld+json",n.text=JSON.stringify(t),document.head.appendChild(n)}}const x=document.querySelector('[data-hs-schema-service="name"]'),S=document.querySelector('[data-hs-schema-service="description"]');if(x&&S){const e=x.textContent?.trim()||"",t=S.textContent?.trim()||"";if(e&&t){const n={"@context":"https://schema.org","@type":"Service",name:e,description:t},r=document.createElement("script");r.type="application/ld+json",r.text=JSON.stringify(n),document.head.appendChild(r)}}else(x||S)&&console.warn('[schema] Service page schema requires both "name" and "description" elements');const q=document.querySelector('[data-hs-schema-faq="question"]'),C=document.querySelector('[data-hs-schema-faq="answer"]'),E=document.querySelector('[data-hs-schema-faq="published"]'),w=document.querySelector('[data-hs-schema-faq="modified"]');if(q&&C&&E&&w){const e=q.textContent?.trim()||"",t=C.innerHTML.trim(),n=E.textContent?.trim()||"",r=w.textContent?.trim()||"";if(e&&t&&n&&r){const o={"@context":"https://schema.org","@type":"QAPage",mainEntity:{"@type":"Question",name:e,text:e,answerCount:1,datePublished:n,dateModified:r,acceptedAnswer:{"@type":"Answer",text:t}}},s=document.createElement("script");s.type="application/ld+json",s.text=JSON.stringify(o),document.head.appendChild(s)}}else if(q||C||E||w){const e=[];q||e.push("question"),C||e.push("answer"),E||e.push("published"),w||e.push("modified"),console.warn(`[schema] FAQ page schema missing required fields: ${e.join(", ")}`)}const v=document.querySelector('[data-hs-schema-blog="name"]'),N=document.querySelector('[data-hs-schema-blog="published"]'),$=document.querySelector('[data-hs-schema-blog="modified"]'),F=document.querySelector('[data-hs-schema-blog="summary"]'),L=document.querySelector('[data-hs-schema-blog="author-name"]'),k=document.querySelector('[data-hs-schema-blog="author-type"]');if(v&&N&&$&&F&&L&&k){const e=v.textContent?.trim()||"",n=N.textContent?.trim()||"",r=$.textContent?.trim()||"",o=F.textContent?.trim()||"",s=L.textContent?.trim()||"",i=k.textContent?.trim()||"Organization",a=document.querySelector('[data-hs-schema-blog="author-role"]'),c=document.querySelector('[data-hs-schema-blog="author-image"]'),l=a?.textContent?.trim()||"",d=c?.src||"",u=t("company"),m=document.querySelector('[data-hs-replace="business-logo"] img'),p=m?m.src:"";let h;"Person"===i?(h={"@type":"Person",name:s},l&&(h.jobTitle=l),d&&(h.image=d)):(h={"@type":"Organization",name:s},p&&(h.logo=p));const f={"@context":"https://schema.org","@type":"BlogPosting",headline:e,description:o,datePublished:n,dateModified:r,author:h,publisher:{"@type":"Organization",name:u||s,logo:{"@type":"ImageObject",url:p}},mainEntityOfPage:{"@type":"WebPage","@id":window.location.href}},y=document.createElement("script");y.type="application/ld+json",y.text=JSON.stringify(f),document.head.appendChild(y)}else if(v||N||$||F||L||k){const e=[];v||e.push("name"),N||e.push("published"),$||e.push("modified"),F||e.push("summary"),L||e.push("author-name"),k||e.push("author-type"),console.warn(`[schema] Blog page schema missing required fields: ${e.join(", ")}`)}}(),{result:"schema initialized",destroy:()=>{}}}function i(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null);let r;for(;r=n.nextNode();)r.nodeValue?.trim()&&t.push(r);return t}function a(e){const t=[];return e.hasAttribute("href")&&t.push(e),t.push(...Array.from(e.querySelectorAll("[href]"))),t}function c(e){if(e.hasAttribute("src")){if(e.getAttribute("src"))return e}const t=e.querySelector("[src]");if(t){if(t.getAttribute("src"))return t}return null}function l(e,t,n){if(function(e,t){const n=t.attributes.properties.syncField;return null!==e.querySelector(`[${n}]`)}(t,n))return void function(e,t,n){const r=n.attributes.properties.syncField;e.querySelectorAll(`[${r}]`).forEach(e=>{const n=e.getAttribute(r),o=t.querySelector(`[${r}="${n}"]`);if(!o)return;const s=c(e),a=c(o);if(s&&a){const e=s.getAttribute("src");e&&a.setAttribute("src",e)}else if(e.hasAttribute("href")&&o.hasAttribute("href")){const t=e.getAttribute("href");t&&o.setAttribute("href",t)}else{const t=i(e),n=t.length>0?t[0].nodeValue:e.textContent.trim();if(n){const e=i(o);e.length>0?e[0].nodeValue=n:o.textContent=n}}})}(e,t,n);const r=i(e),o=r.length>0?r[0].nodeValue:"",s=a(e),l=s.length>0?s[0].getAttribute("href"):"";if(o){i(t).forEach(e=>{e.nodeValue=o})}if(l){a(t).forEach(e=>{e.setAttribute("href",l)})}}const d="hs-dupe-no-transition";let u=!1;async function m(t){!function(){if(u)return;const e=document.createElement("style");e.textContent=`\n .${d},\n .${d} * {\n transition: none !important;\n animation: none !important;\n }\n `,document.head.appendChild(e),u=!0}();const n={processedElements:[]};return e(t,"wrapper").forEach(e=>{const t=e.getAttribute("data-hs-dupe");if(!t||"child"!==t&&"self"!==t)return void console.warn('[dupe] Invalid mode. Use "child" or "self":',e);const r=e.getAttribute("data-hs-dupe-count");let o=r?parseInt(r,10):1;(isNaN(o)||o<1)&&(console.warn("[dupe] Invalid count, using default of 1:",e),o=1),o>20&&(console.warn(`[dupe] Count of ${o} exceeds maximum of 20. Capping at 20.`),o=20);const s={hidden:e.hasAttribute("data-hs-dupe-hidden"),noSelect:e.hasAttribute("data-hs-dupe-no-select")};"child"===t?function(e,t,n,r){const o=e.children[0];if(!o)return;const s=[];for(let i=0;i<t;i++){const t=o.cloneNode(!0);t.classList.add(d),p(t,n),e.appendChild(t),s.push(t),t.offsetHeight,requestAnimationFrame(()=>{t.classList.remove(d)})}r.processedElements.push({element:e,mode:"child",clones:s})}(e,o,s,n):"self"===t&&function(e,t,n,r){const o=e.parentNode;if(!o)return void console.warn("[dupe] Element has no parent, cannot duplicate self:",e);const s=[];let i=e;for(let a=0;a<t;a++){const t=e.cloneNode(!0);t.classList.add(d),t.removeAttribute("data-hs-dupe"),t.removeAttribute("data-hs-dupe-count"),t.removeAttribute("data-hs-dupe-hidden"),t.removeAttribute("data-hs-dupe-no-select"),p(t,n),i.nextSibling?o.insertBefore(t,i.nextSibling):o.appendChild(t),s.push(t),i=t,t.offsetHeight,requestAnimationFrame(()=>{t.classList.remove(d)})}r.processedElements.push({element:e,mode:"self",clones:s})}(e,o,s,n)}),{result:`dupe processed ${n.processedElements.length} elements`,destroy:()=>{}}}function p(e,t){if(t.hidden&&e.setAttribute("aria-hidden","true"),t.noSelect){const t=e;t.style.userSelect="none",t.style.webkitUserSelect="none",t.style.msUserSelect="none"}}async function h(){const i={destroyFunctions:[]},a=o.normalize;try{const d=s();d&&"destroy"in d&&"function"==typeof d.destroy&&i.destroyFunctions.push(d.destroy);const u=await async function(){const t={processedWrappers:[]};return e(n.clickable,"wrapper").forEach(e=>{const n=e.querySelector(":scope > button"),r=e.querySelector(":scope > a");if(!n||!r)return;const o=r.getAttribute("href");let s,i;o&&""!==o&&"#"!==o?(s=r,i=n):(s=n,i=r);const a="button"===s.getAttribute("data-hs-clickable");a||s.setAttribute("data-hs-clickable","button"),i.remove(),t.processedWrappers.push({wrapper:e,keptElement:s,wasLink:s===r,addedAttribute:!a})}),{result:`clickable processed ${t.processedWrappers.length} wrappers`,destroy:()=>{}}}();u&&"destroy"in u&&"function"==typeof u.destroy&&i.destroyFunctions.push(u.destroy);const p=(c=o.normalize?.replace,function(){const e=new Map,t=r(c,"element");document.querySelectorAll(t).forEach(t=>{const n=t.cloneNode(!0),o=r(c,"hide");n.querySelectorAll(o).forEach(e=>{const t=c.attributes.elements.hide.primary.match(/data-hs-replace-hide/)[0],n=e.getAttribute(t);n&&n.split(" ").filter(e=>e.trim()).some(t=>e.classList.contains(t))&&e.remove()});const s=c.attributes.elements.element.primary.match(/data-hs-replace/)[0],i=t.getAttribute(s),a=n.textContent?.trim()||null,l=t.getAttribute("href");if(i&&(a||l)){const t={text:a,href:l};if(e.has(i)){const n=e.get(i);Array.isArray(n)?n.push(t):e.set(i,[n,t])}else e.set(i,t)}});const n=(new Date).getFullYear().toString(),o=["January","February","March","April","May","June","July","August","September","October","November","December"][(new Date).getMonth()];if(e.set("year",{text:n,href:null}),e.set("month",{text:o,href:null}),0===e.size)return;const s=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT,{acceptNode:t=>{const n=t.textContent||"";for(const r of e.keys())if(n.includes(`{{${r}}}`))return NodeFilter.FILTER_ACCEPT;return NodeFilter.FILTER_SKIP}}),i=[];let a;for(;a=s.nextNode();)i.push(a);i.forEach(t=>{let n=t.textContent||"",r=!1;for(const[o,s]of e.entries()){const e=`{{${o}}}`;if(n.includes(e)){let t=null;t=Array.isArray(s)?s.map(e=>e.text||e.href).filter(Boolean).join(", "):s.text||s.href,t&&(n=n.replace(new RegExp(e,"g"),t),r=!0)}}r&&(t.textContent=n)}),document.querySelectorAll("a[href]").forEach(t=>{let n=t.getAttribute("href")||"",r=!1;for(const[o,s]of e.entries()){const e=`{{${o}}}`;if(n.includes(e)){let t=null;if(Array.isArray(s)){const e=s[0];t=e.href||e.text}else{if(s.href){t=s.href,n=s.href,r=!0;break}s.text&&(t=s.text)}t&&!r&&(n=n.replace(new RegExp(e,"g"),t),r=!0)}}r&&t.setAttribute("href",n)});const l=document.querySelector("[data-hs-replace-test]");if(l){const t={};for(const[r,o]of e.entries()){const e=`{{${r}}}`;if(Array.isArray(o)){const n=o.map(e=>e.text||e.href).filter(Boolean).join(", ");t[r]={placeholder:e,value:n,type:`Array (${o.length} items)`,items:o.map(e=>e.text||e.href||"(empty)")}}else{const n=o.text||o.href||"(empty)";t[r]={placeholder:e,value:n,...o.text&&{text:o.text},...o.href&&{href:o.href}}}}const n=document.createElement("pre");n.textContent=JSON.stringify(t,null,2),l.innerHTML="",l.appendChild(document.createTextNode(`Replace Module Debug (${e.size} replacements):`)),l.appendChild(n)}}(),{result:"replace initialized",destroy:()=>{}});p&&"destroy"in p&&"function"==typeof p.destroy&&i.destroyFunctions.push(p.destroy);const h=await async function(n){const r=e(n,"source"),o=new Map,s=new Map;r.forEach(e=>{const t=e.getAttribute(n.attributes.properties.syncId),r=e.getAttribute(n.attributes.properties.syncField);t&&o.set(t,e),r&&s.set(r,e)});const i=e(n,"target");let a=0,c=0;return i.forEach(r=>{const i=r.getAttribute(n.attributes.properties.syncId),d=r.getAttribute(n.attributes.properties.syncField),u=r.getAttribute(n.attributes.properties.syncValue);let m;if(d&&u){if(m=s.get(d),!m)return void c++}else{if(!i)return void c++;if(m=o.get(i),!m)return void c++}const p=t(n,"item",r);if(!p){const e=d||i||"unknown";return void console.warn(`[sync] Target list "${e}" missing item template`)}const h="child"===p.getAttribute(n.attributes.properties.syncMode),f=t(n,"ignore",r);let y,g;if(u){const t=e(n,"item",m),r=Array.from(t).find(e=>e.getAttribute(n.attributes.properties.syncValue)===u);if(!r)return void console.warn(`[sync] No source item found with value "${u}" in field "${d}"`);y=[r]}else if(y=Array.from(e(n,"item",m)),0===y.length)return void console.warn(`[sync] Source list "${i}" has no items`);if(h){const e=p.firstElementChild;if(!e)return void console.warn(`[sync] Target list "${i}" has sync-mode="child" but item wrapper has no children`);g=e.cloneNode(!0)}else g=p.cloneNode(!0),g.removeAttribute(n.attributes.elements.item.primary.split("=")[0]);let b=!1;if(f){let e=f.nextElementSibling;for(;e;){if(e===p){b=!0;break}e=e.nextElementSibling}}if(h){for(;p.firstChild;)p.removeChild(p.firstChild);y.forEach(e=>{const t=g.cloneNode(!0);l(e,t,n),p.appendChild(t)}),p.removeAttribute(n.attributes.elements.item.primary.split("=")[0])}else{Array.from(r.children).forEach(e=>{e!==f&&e.remove()});const e=Array.from(y).map(e=>{const t=g.cloneNode(!0);return l(e,t,n),t});f?b?e.forEach(e=>{r.appendChild(e)}):e.forEach(e=>{r.insertBefore(e,f)}):e.forEach(e=>{r.appendChild(e)})}a++}),{result:`sync initialized (${a} synced, ${c} skipped)`,destroy:()=>{}}}(a.sync);h&&"destroy"in h&&"function"==typeof h.destroy&&i.destroyFunctions.push(h.destroy);const f=await m(a.dupe);return f&&"destroy"in f&&"function"==typeof f.destroy&&i.destroyFunctions.push(f.destroy),{result:"normalize initialized",destroy:()=>{i.destroyFunctions.reverse().forEach(e=>{try{e()}catch(t){console.error("[normalize] Error during cleanup:",t)}}),i.destroyFunctions.length=0}}}catch(d){throw console.error("[normalize] Initialization failed:",d),i.destroyFunctions.reverse().forEach(e=>{try{e()}catch(t){console.error("[normalize] Error during error cleanup:",t)}}),d}var c}export{h as init};
|
|
2
|
+
//# sourceMappingURL=hs-normalize-CW1mxe_R.js.map
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hs-normalize-B-mB80zY.js","sources":["../../src/modules/normalize/functions/schema.ts","../../src/modules/normalize/functions/sync.ts","../../src/modules/normalize/functions/dupe.ts","../../src/modules/normalize/normalize.ts","../../src/modules/normalize/functions/clickable.ts","../../src/modules/normalize/functions/replace.ts"],"sourcesContent":["/**\n * Schema Generator\n *\n * Generates JSON-LD structured data for SEO (LocalBusiness, BreadcrumbList, FAQPage).\n * Reads from data-hs-replace elements and data-hs-schema markers.\n *\n * Features:\n * - LocalBusiness schema with company info, address, geo, social links\n * - Optional services and areas served (only if elements exist)\n * - BreadcrumbList schema (auto-generated from URL path)\n * - FAQPage schema (from accordion elements with data-hs-accordion-type=\"faq\")\n * - Filters out empty/placeholder values\n * - Injects as <script type=\"application/ld+json\"> in <head>\n *\n * Structure:\n * <!-- Business info (uses replace data) -->\n * <span data-hs-replace=\"company\">Business Name</span>\n * <span data-hs-replace=\"phone\">(555) 123-4567</span>\n *\n * <!-- Services wrapper -->\n * <div data-hs-schema=\"services\">\n * <div data-hs-schema-list=\"item\">Service 1</div>\n * <div data-hs-schema-list=\"item\">Service 2</div>\n * </div>\n *\n * <!-- Areas wrapper -->\n * <div data-hs-schema=\"areas\">\n * <div data-hs-schema-list=\"item\">City 1</div>\n * </div>\n *\n * <!-- Breadcrumb marker -->\n * <span data-hs-schema=\"breadcrumb\">Page Title</span>\n *\n * <!-- FAQ accordions -->\n * <div data-hs-accordion=\"wrapper\">\n * <div data-hs-accordion-type=\"faq\">\n * <div data-hs-accordion=\"toggle\">Question?</div>\n * <div data-hs-accordion=\"content\">Answer</div>\n * </div>\n * </div>\n */\n\nexport function init() {\n function setupSchema() {\n const baseURL = window.location.origin;\n\n // Helper to get text content from data-hs-replace element\n const getReplaceText = (name: string): string => {\n const element = document.querySelector(`[data-hs-replace=\"${name}\"]`);\n return element?.textContent?.trim() || '';\n };\n\n // Helper to get href from data-hs-replace element\n const getReplaceHref = (name: string): string => {\n const element = document.querySelector(`[data-hs-replace=\"${name}\"]`);\n return (element as HTMLAnchorElement)?.href || '';\n };\n\n // 1. Build LocalBusiness Schema\n const businessName = getReplaceText('company');\n const phone = getReplaceText('phone');\n const email = getReplaceText('email');\n const city = getReplaceText('city');\n const state = getReplaceText('state');\n const zipCode = getReplaceText('zipcode');\n\n const schema: any = {\n '@context': 'https://schema.org',\n '@type': 'LocalBusiness',\n name: businessName,\n url: baseURL,\n telephone: phone,\n email: email,\n };\n\n // Add logo if it exists\n const logoElement = document.querySelector('[data-hs-replace=\"business-logo\"] img');\n if (logoElement && (logoElement as HTMLImageElement).src) {\n schema.image = (logoElement as HTMLImageElement).src;\n }\n\n // Add address\n const address: any = {\n '@type': 'PostalAddress',\n addressLocality: city,\n addressRegion: state,\n postalCode: zipCode,\n addressCountry: 'US',\n };\n\n const streetAddress = getReplaceText('street-address');\n if (streetAddress) {\n address.streetAddress = streetAddress;\n }\n\n schema.address = address;\n\n // Add geo coordinates if they exist\n const latitude = getReplaceText('business-latitude');\n const longitude = getReplaceText('business-longitude');\n if (latitude && longitude) {\n schema.geo = {\n '@type': 'GeoCoordinates',\n latitude: latitude,\n longitude: longitude,\n };\n }\n\n // Add social links (filter out empty/placeholder)\n const socialNames = [\n 'facebook',\n 'instagram',\n 'linkedin',\n 'pinterest',\n 'tiktok',\n 'x',\n 'youtube',\n ];\n const socialLinks = socialNames\n .map((name) => getReplaceHref(name))\n .filter((link) => link && link.trim() !== '' && !link.includes('placeholder'));\n\n if (socialLinks.length > 0) {\n schema.sameAs = socialLinks;\n }\n\n // Add services if wrapper exists\n const servicesWrapper = document.querySelector('[data-hs-schema=\"services\"]');\n if (servicesWrapper) {\n const serviceItems = servicesWrapper.querySelectorAll('[data-hs-schema-list=\"item\"]');\n const services = Array.from(serviceItems)\n .map((el) => el.textContent?.trim())\n .filter((service) => service && service !== '');\n\n if (services.length > 0) {\n schema.serviceType = services;\n }\n }\n\n // Add areas served if wrapper exists\n const areasWrapper = document.querySelector('[data-hs-schema=\"areas\"]');\n if (areasWrapper) {\n const areaItems = areasWrapper.querySelectorAll('[data-hs-schema-list=\"item\"]');\n const areas = Array.from(areaItems)\n .map((el) => ({\n '@type': 'City',\n name: el.textContent?.trim() || '',\n }))\n .filter((area) => area.name && area.name !== '');\n\n if (areas.length > 0) {\n schema.areaServed = areas;\n }\n }\n\n // Inject LocalBusiness schema\n const businessScript = document.createElement('script');\n businessScript.type = 'application/ld+json';\n businessScript.text = JSON.stringify(schema);\n document.head.appendChild(businessScript);\n\n // 2. Build BreadcrumbList Schema (if page has path)\n const breadcrumbText =\n document.querySelector('[data-hs-schema=\"breadcrumb\"]')?.textContent?.trim() || '';\n const pathname = window.location.pathname;\n const pathParts = pathname.split('/').filter((part) => part !== '');\n\n if (pathParts.length > 0) {\n const breadcrumbList: any = {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: [\n {\n '@type': 'ListItem',\n position: 1,\n name: 'Home',\n item: baseURL,\n },\n ],\n };\n\n // Add middle breadcrumb (first slug capitalized)\n const firstSlug = pathParts[0];\n const middleBreadcrumb = firstSlug\n .split('-')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n\n breadcrumbList.itemListElement.push({\n '@type': 'ListItem',\n position: 2,\n name: middleBreadcrumb,\n item: baseURL + '/' + firstSlug,\n });\n\n // Add final breadcrumb if text exists\n if (breadcrumbText) {\n breadcrumbList.itemListElement.push({\n '@type': 'ListItem',\n position: 3,\n name: breadcrumbText,\n item: window.location.href,\n });\n }\n\n // Inject breadcrumb schema\n const breadcrumbScript = document.createElement('script');\n breadcrumbScript.type = 'application/ld+json';\n breadcrumbScript.text = JSON.stringify(breadcrumbList);\n document.head.appendChild(breadcrumbScript);\n }\n\n // 3. Build FAQPage Schema (if FAQ accordions exist)\n const hasFAQs = document.querySelector('[data-hs-accordion-type=\"faq\"]');\n if (hasFAQs) {\n const faqArray: any[] = [];\n const faqLists = document.querySelectorAll('[data-hs-accordion=\"list\"]');\n\n faqLists.forEach((faqList) => {\n const faqAccordions = faqList.querySelectorAll('[data-hs-accordion-type=\"faq\"]');\n\n faqAccordions.forEach((faqAccordion) => {\n const toggleElement = faqAccordion.querySelector('[data-hs-accordion=\"toggle\"]');\n const contentElement = faqAccordion.querySelector('[data-hs-accordion=\"content\"]');\n\n if (toggleElement && contentElement) {\n const questionText = toggleElement.textContent?.trim();\n const answerHTML = contentElement.innerHTML.trim();\n\n faqArray.push({\n '@type': 'Question',\n name: questionText,\n acceptedAnswer: {\n '@type': 'Answer',\n text: answerHTML,\n },\n });\n }\n });\n });\n\n // Only create schema if we have FAQs\n if (faqArray.length > 0) {\n const faqSchema = {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: faqArray,\n };\n\n // Inject FAQ schema\n const faqScript = document.createElement('script');\n faqScript.type = 'application/ld+json';\n faqScript.text = JSON.stringify(faqSchema);\n document.head.appendChild(faqScript);\n }\n }\n\n // 4. Build Service Page Schema (if service elements exist)\n const nameElement = document.querySelector('[data-hs-schema-service=\"name\"]');\n const descElement = document.querySelector('[data-hs-schema-service=\"description\"]');\n\n // Require BOTH name and description\n if (nameElement && descElement) {\n const serviceName = nameElement.textContent?.trim() || '';\n // Convert description to plain text (in case it's rich text)\n const serviceDescription = descElement.textContent?.trim() || '';\n\n if (serviceName && serviceDescription) {\n const serviceSchema = {\n '@context': 'https://schema.org',\n '@type': 'Service',\n name: serviceName,\n description: serviceDescription,\n };\n\n const serviceScript = document.createElement('script');\n serviceScript.type = 'application/ld+json';\n serviceScript.text = JSON.stringify(serviceSchema);\n document.head.appendChild(serviceScript);\n }\n } else if (nameElement || descElement) {\n console.warn('[schema] Service page schema requires both \"name\" and \"description\" elements');\n }\n\n // 5. Build FAQ Page Schema (QAPage - single FAQ page)\n const questionElement = document.querySelector('[data-hs-schema-faq=\"question\"]');\n const answerElement = document.querySelector('[data-hs-schema-faq=\"answer\"]');\n const publishedElement = document.querySelector('[data-hs-schema-faq=\"published\"]');\n const modifiedElement = document.querySelector('[data-hs-schema-faq=\"modified\"]');\n\n // Require ALL 4 fields\n if (questionElement && answerElement && publishedElement && modifiedElement) {\n // Question: convert to plain text\n const questionText = questionElement.textContent?.trim() || '';\n // Answer: keep as HTML\n const answerHTML = answerElement.innerHTML.trim();\n const publishedDate = publishedElement.textContent?.trim() || '';\n const modifiedDate = modifiedElement.textContent?.trim() || '';\n\n if (questionText && answerHTML && publishedDate && modifiedDate) {\n const qaPageSchema = {\n '@context': 'https://schema.org',\n '@type': 'QAPage',\n mainEntity: {\n '@type': 'Question',\n name: questionText,\n text: questionText,\n answerCount: 1,\n datePublished: publishedDate,\n dateModified: modifiedDate,\n acceptedAnswer: {\n '@type': 'Answer',\n text: answerHTML,\n },\n },\n };\n\n const qaPageScript = document.createElement('script');\n qaPageScript.type = 'application/ld+json';\n qaPageScript.text = JSON.stringify(qaPageSchema);\n document.head.appendChild(qaPageScript);\n }\n } else if (questionElement || answerElement || publishedElement || modifiedElement) {\n const missing: string[] = [];\n if (!questionElement) missing.push('question');\n if (!answerElement) missing.push('answer');\n if (!publishedElement) missing.push('published');\n if (!modifiedElement) missing.push('modified');\n console.warn(`[schema] FAQ page schema missing required fields: ${missing.join(', ')}`);\n }\n\n // 6. Build Blog Page Schema\n const blogNameElement = document.querySelector('[data-hs-schema-blog=\"name\"]');\n const blogPublishedElement = document.querySelector('[data-hs-schema-blog=\"published\"]');\n const blogModifiedElement = document.querySelector('[data-hs-schema-blog=\"modified\"]');\n const summaryElement = document.querySelector('[data-hs-schema-blog=\"summary\"]');\n const authorNameElement = document.querySelector('[data-hs-schema-blog=\"author-name\"]');\n const authorTypeElement = document.querySelector('[data-hs-schema-blog=\"author-type\"]');\n\n // Require: name, published, modified, summary, author-name, author-type\n if (\n blogNameElement &&\n blogPublishedElement &&\n blogModifiedElement &&\n summaryElement &&\n authorNameElement &&\n authorTypeElement\n ) {\n const blogName = blogNameElement.textContent?.trim() || '';\n const publishedDate = blogPublishedElement.textContent?.trim() || '';\n const modifiedDate = blogModifiedElement.textContent?.trim() || '';\n // Summary: convert to plain text\n const summaryText = summaryElement.textContent?.trim() || '';\n const authorName = authorNameElement.textContent?.trim() || '';\n const authorType = authorTypeElement.textContent?.trim() || 'Organization';\n\n // Optional fields\n const authorRoleElement = document.querySelector('[data-hs-schema-blog=\"author-role\"]');\n const authorImageElement = document.querySelector(\n '[data-hs-schema-blog=\"author-image\"]'\n ) as HTMLImageElement;\n\n const authorRole = authorRoleElement?.textContent?.trim() || '';\n const authorImage = authorImageElement?.src || '';\n\n // Get company info for publisher\n const companyName = getReplaceText('company');\n const logoElement = document.querySelector('[data-hs-replace=\"business-logo\"] img');\n const logoURL = logoElement ? (logoElement as HTMLImageElement).src : '';\n\n // Build author schema\n let authorSchema: any;\n if (authorType === 'Person') {\n authorSchema = {\n '@type': 'Person',\n name: authorName,\n };\n if (authorRole) authorSchema.jobTitle = authorRole;\n if (authorImage) authorSchema.image = authorImage;\n } else {\n authorSchema = {\n '@type': 'Organization',\n name: authorName,\n };\n if (logoURL) authorSchema.logo = logoURL;\n }\n\n const blogSchema: any = {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: blogName,\n description: summaryText,\n datePublished: publishedDate,\n dateModified: modifiedDate,\n author: authorSchema,\n publisher: {\n '@type': 'Organization',\n name: companyName || authorName,\n logo: {\n '@type': 'ImageObject',\n url: logoURL,\n },\n },\n mainEntityOfPage: {\n '@type': 'WebPage',\n '@id': window.location.href,\n },\n };\n\n const blogScript = document.createElement('script');\n blogScript.type = 'application/ld+json';\n blogScript.text = JSON.stringify(blogSchema);\n document.head.appendChild(blogScript);\n } else if (\n blogNameElement ||\n blogPublishedElement ||\n blogModifiedElement ||\n summaryElement ||\n authorNameElement ||\n authorTypeElement\n ) {\n const missing: string[] = [];\n if (!blogNameElement) missing.push('name');\n if (!blogPublishedElement) missing.push('published');\n if (!blogModifiedElement) missing.push('modified');\n if (!summaryElement) missing.push('summary');\n if (!authorNameElement) missing.push('author-name');\n if (!authorTypeElement) missing.push('author-type');\n console.warn(`[schema] Blog page schema missing required fields: ${missing.join(', ')}`);\n }\n }\n\n setupSchema();\n\n return {\n result: 'schema initialized',\n destroy: () => {},\n };\n}\n","/**\n * List Sync\n *\n * Syncs collection list data to static lists by sync-id.\n *\n * Simple mode (text/href):\n * <div data-hs-sync=\"source\" data-hs-sync-id=\"services\">\n * <a data-hs-sync=\"item\" href=\"/service-1\">Service 1</a>\n * </div>\n * <div data-hs-sync=\"target\" data-hs-sync-id=\"services\">\n * <a data-hs-sync=\"item\" href=\"#\">Placeholder</a>\n * </div>\n *\n * Field-based mode (named fields):\n * <div data-hs-sync=\"source\" data-hs-sync-id=\"projects\">\n * <div data-hs-sync=\"item\">\n * <img data-hs-sync-field=\"before\" src=\"/before.jpg\">\n * <img data-hs-sync-field=\"after\" src=\"/after.jpg\">\n * </div>\n * </div>\n * <div data-hs-sync=\"target\" data-hs-sync-id=\"projects\">\n * <div data-hs-sync=\"item\">\n * <img data-hs-sync-field=\"before\" src=\"/placeholder.jpg\">\n * <img data-hs-sync-field=\"after\" src=\"/placeholder.jpg\">\n * </div>\n * </div>\n *\n * Value-based matching (match by value):\n * <div data-hs-sync=\"source\" data-hs-sync-field=\"area-type\">\n * <div data-hs-sync=\"item\" data-hs-sync-value=\"Residential\">\n * <svg>...</svg>\n * </div>\n * <div data-hs-sync=\"item\" data-hs-sync-value=\"Commercial\">\n * <svg>...</svg>\n * </div>\n * </div>\n * <div data-hs-sync=\"target\" data-hs-sync-field=\"area-type\" data-hs-sync-value=\"Residential\">\n * <div data-hs-sync=\"item\"><!-- Matched SVG injected here --></div>\n * </div>\n *\n * Child mode (wrapper templates):\n * <div data-hs-sync=\"target\" data-hs-sync-id=\"marquee\">\n * <div data-hs-sync=\"item\" data-hs-sync-mode=\"child\">\n * <div class=\"marquee_list\">\n * <img data-hs-sync-field=\"image\" src=\"/placeholder.jpg\">\n * </div>\n * </div>\n * </div>\n *\n * Supports data-hs-sync=\"ignore\" to preserve elements during sync.\n */\n\nimport { querySelectorAll, querySelector } from '@utils';\n\n/**\n * Get all text nodes within an element\n */\nfunction getTextNodes(element: Element): Text[] {\n const textNodes: Text[] = [];\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);\n\n let node: Node | null;\n while ((node = walker.nextNode())) {\n // Skip empty/whitespace-only nodes\n if (node.nodeValue?.trim()) {\n textNodes.push(node as Text);\n }\n }\n\n return textNodes;\n}\n\n/**\n * Get all elements with href attribute (including the element itself)\n */\nfunction getHrefElements(element: Element): Element[] {\n const hrefs: Element[] = [];\n\n // Check if element itself has href\n if (element.hasAttribute('href')) {\n hrefs.push(element);\n }\n\n // Check descendants\n hrefs.push(...Array.from(element.querySelectorAll('[href]')));\n\n return hrefs;\n}\n\n/**\n * Check if item uses field-based syncing\n */\nfunction usesFieldBasedSync(\n item: Element,\n config: { attributes: { properties: { syncField: string } } }\n): boolean {\n const fieldAttr = config.attributes.properties.syncField;\n return item.querySelector(`[${fieldAttr}]`) !== null;\n}\n\n/**\n * Find element with src attribute (check element itself, then descendants)\n */\nfunction findElementWithSrc(element: Element): Element | null {\n // Check element itself\n if (element.hasAttribute('src')) {\n const src = element.getAttribute('src');\n if (src) return element;\n }\n\n // Check descendants\n const descendant = element.querySelector('[src]');\n if (descendant) {\n const src = descendant.getAttribute('src');\n if (src) return descendant;\n }\n\n return null;\n}\n\n/**\n * Sync data using field-based mapping\n * Maps fields by name between source and target\n */\nfunction syncFieldBasedData(\n sourceItem: Element,\n targetClone: Element,\n config: { attributes: { properties: { syncField: string } } }\n): void {\n const fieldAttr = config.attributes.properties.syncField;\n\n // Get all fields in source\n const sourceFields = sourceItem.querySelectorAll(`[${fieldAttr}]`);\n\n sourceFields.forEach((sourceField: Element) => {\n const fieldName = sourceField.getAttribute(fieldAttr);\n\n // Find matching field in target\n const targetField = targetClone.querySelector(`[${fieldAttr}=\"${fieldName}\"]`);\n if (!targetField) return;\n\n // Try to find elements with src (for images)\n const sourceImgElement = findElementWithSrc(sourceField);\n const targetImgElement = findElementWithSrc(targetField);\n\n if (sourceImgElement && targetImgElement) {\n // Sync src attribute\n const src = sourceImgElement.getAttribute('src');\n if (src) {\n targetImgElement.setAttribute('src', src);\n }\n }\n // Link elements: sync href\n else if (sourceField.hasAttribute('href') && targetField.hasAttribute('href')) {\n const href = sourceField.getAttribute('href');\n if (href) {\n targetField.setAttribute('href', href);\n }\n }\n // Text elements: sync text content\n else {\n const sourceTextNodes = getTextNodes(sourceField);\n const sourceText =\n sourceTextNodes.length > 0 ? sourceTextNodes[0].nodeValue : sourceField.textContent.trim();\n\n if (sourceText) {\n const targetTextNodes = getTextNodes(targetField);\n if (targetTextNodes.length > 0) {\n targetTextNodes[0].nodeValue = sourceText;\n } else {\n targetField.textContent = sourceText;\n }\n }\n }\n });\n}\n\n/**\n * Sync data from source item to target clone\n * Extracts text and href from source, applies to ALL matching elements in target\n */\nfunction syncItemData(\n sourceItem: Element,\n targetClone: Element,\n config: { attributes: { properties: { syncField: string } } }\n): void {\n // Check if using field-based syncing\n if (usesFieldBasedSync(targetClone, config)) {\n syncFieldBasedData(sourceItem, targetClone, config);\n return;\n }\n\n // Fallback to simple syncing (existing behavior)\n // Get text content from source (first non-empty text node)\n const sourceTextNodes = getTextNodes(sourceItem);\n const sourceText = sourceTextNodes.length > 0 ? sourceTextNodes[0].nodeValue : '';\n\n // Get href from source (first href element)\n const sourceHrefs = getHrefElements(sourceItem);\n const sourceHref = sourceHrefs.length > 0 ? sourceHrefs[0].getAttribute('href') : '';\n\n // Apply source text to ALL text nodes in target\n if (sourceText) {\n const targetTextNodes = getTextNodes(targetClone);\n targetTextNodes.forEach((node) => {\n node.nodeValue = sourceText;\n });\n }\n\n // Apply source href to ALL href elements in target\n if (sourceHref) {\n const targetHrefs = getHrefElements(targetClone);\n targetHrefs.forEach((el) => {\n el.setAttribute('href', sourceHref);\n });\n }\n}\n\ninterface SyncConfig {\n attributes: {\n elements: {\n item: { primary: string };\n };\n properties: {\n syncId: string;\n syncField: string;\n syncMode: string;\n syncValue: string;\n };\n };\n}\n\nexport async function init(config: SyncConfig): Promise<{ result: string; destroy: () => void }> {\n // Find all source lists and build maps by sync-id and sync-field\n const sourceLists = querySelectorAll(config, 'source');\n const sourceMapById = new Map<string, Element>();\n const sourceMapByField = new Map<string, Element>();\n\n sourceLists.forEach((source) => {\n const syncId = source.getAttribute(config.attributes.properties.syncId);\n const syncField = source.getAttribute(config.attributes.properties.syncField);\n\n if (syncId) {\n sourceMapById.set(syncId, source);\n }\n if (syncField) {\n sourceMapByField.set(syncField, source);\n }\n });\n\n // Find all target lists\n const targetLists = querySelectorAll(config, 'target');\n let syncedCount = 0;\n let skippedCount = 0;\n\n targetLists.forEach((target) => {\n const syncId = target.getAttribute(config.attributes.properties.syncId);\n const syncField = target.getAttribute(config.attributes.properties.syncField);\n const syncValue = target.getAttribute(config.attributes.properties.syncValue);\n\n // Determine which matching mode to use\n let source: Element | undefined;\n\n // Value-based matching (sync-field + sync-value)\n if (syncField && syncValue) {\n source = sourceMapByField.get(syncField);\n if (!source) {\n skippedCount++;\n return;\n }\n }\n // ID-based matching (sync-id)\n else if (syncId) {\n source = sourceMapById.get(syncId);\n if (!source) {\n skippedCount++;\n return;\n }\n }\n // No valid matching criteria\n else {\n skippedCount++;\n return;\n }\n\n // Find item template in target (element to clone)\n const targetTemplateWrapper = querySelector(config, 'item', target);\n if (!targetTemplateWrapper) {\n const identifier = syncField || syncId || 'unknown';\n console.warn(`[sync] Target list \"${identifier}\" missing item template`);\n return;\n }\n\n // Check sync mode - determines which element to use as template\n const syncMode = targetTemplateWrapper.getAttribute(config.attributes.properties.syncMode);\n const isChildMode = syncMode === 'child';\n\n // Find ignore element in target (if exists)\n const ignoreElement = querySelector(config, 'ignore', target);\n\n // Find source items - filtered by value if value-based matching\n let sourceItems: Element[];\n\n if (syncValue) {\n // Value-based matching: find single item with matching sync-value\n const allSourceItems = querySelectorAll(config, 'item', source);\n const matchedItem = Array.from(allSourceItems).find((item) => {\n const itemValue = item.getAttribute(config.attributes.properties.syncValue);\n return itemValue === syncValue;\n });\n\n if (!matchedItem) {\n console.warn(\n `[sync] No source item found with value \"${syncValue}\" in field \"${syncField}\"`\n );\n return;\n }\n\n sourceItems = [matchedItem];\n } else {\n // ID-based matching: use all source items\n sourceItems = Array.from(querySelectorAll(config, 'item', source));\n if (sourceItems.length === 0) {\n console.warn(`[sync] Source list \"${syncId}\" has no items`);\n return;\n }\n }\n\n // Determine template based on mode\n let template: Element;\n if (isChildMode) {\n // Child mode: clone the wrapper, we'll replace its child content\n const firstChild = targetTemplateWrapper.firstElementChild;\n if (!firstChild) {\n console.warn(\n `[sync] Target list \"${syncId}\" has sync-mode=\"child\" but item wrapper has no children`\n );\n return;\n }\n // Clone the child element to use for syncing data\n template = firstChild.cloneNode(true) as Element;\n } else {\n // Default mode: clone the item element itself\n template = targetTemplateWrapper.cloneNode(true) as Element;\n template.removeAttribute(config.attributes.elements.item.primary.split('=')[0]);\n }\n\n // Determine insertion strategy if ignore exists\n let insertAfterIgnore = false;\n if (ignoreElement) {\n // Check if template wrapper comes after ignore element\n let currentElement = ignoreElement.nextElementSibling;\n while (currentElement) {\n if (currentElement === targetTemplateWrapper) {\n insertAfterIgnore = true;\n break;\n }\n currentElement = currentElement.nextElementSibling;\n }\n }\n\n if (isChildMode) {\n // CHILD MODE: Keep wrapper as single element, duplicate children inside it\n // Clear the wrapper's children\n while (targetTemplateWrapper.firstChild) {\n targetTemplateWrapper.removeChild(targetTemplateWrapper.firstChild);\n }\n\n // Create synced children and append to the wrapper\n sourceItems.forEach((sourceItem) => {\n const childClone = template.cloneNode(true) as Element;\n syncItemData(sourceItem, childClone, config);\n targetTemplateWrapper.appendChild(childClone);\n });\n\n // Remove data-hs-sync=\"item\" attribute from wrapper\n targetTemplateWrapper.removeAttribute(config.attributes.elements.item.primary.split('=')[0]);\n } else {\n // DEFAULT MODE: Duplicate the item element itself\n // Clear target list, but preserve ignore element\n const children = Array.from(target.children);\n children.forEach((child) => {\n if (child !== ignoreElement) {\n child.remove();\n }\n });\n\n // Create synced items\n const syncedItems = Array.from(sourceItems).map((sourceItem) => {\n const clone = template.cloneNode(true) as Element;\n syncItemData(sourceItem, clone, config);\n return clone;\n });\n\n // Insert synced items based on strategy\n if (ignoreElement) {\n if (insertAfterIgnore) {\n // Insert all items after ignore\n syncedItems.forEach((item) => {\n target.appendChild(item);\n });\n } else {\n // Insert all items before ignore\n syncedItems.forEach((item) => {\n target.insertBefore(item, ignoreElement);\n });\n }\n } else {\n // No ignore element, just append all\n syncedItems.forEach((item) => {\n target.appendChild(item);\n });\n }\n }\n\n syncedCount++;\n });\n\n return {\n result: `sync initialized (${syncedCount} synced, ${skippedCount} skipped)`,\n destroy: () => {},\n };\n}\n","/**\n * Element Duplication\n *\n * Duplicates elements for marquee effects and visual consistency.\n *\n * Child mode (duplicate children):\n * <div data-hs-dupe=\"child\" data-hs-dupe-count=\"2\">\n * <div class=\"marquee_item\">Content</div>\n * </div>\n *\n * Self mode (duplicate wrapper):\n * <div data-hs-dupe=\"self\" data-hs-dupe-count=\"3\">Content</div>\n *\n * Modifiers: data-hs-dupe-hidden (aria-hidden), data-hs-dupe-no-select (disable selection).\n */\nimport { querySelectorAll } from '@utils';\n\nconst MAX_DUPE_COUNT = 20;\nconst TRANSITION_CLASS = 'hs-dupe-no-transition';\n\n// Inject CSS once\nlet cssInjected = false;\nfunction injectCSS(): void {\n if (cssInjected) return;\n\n const style = document.createElement('style');\n style.textContent = `\n .${TRANSITION_CLASS},\n .${TRANSITION_CLASS} * {\n transition: none !important;\n animation: none !important;\n }\n `;\n document.head.appendChild(style);\n cssInjected = true;\n}\n\ninterface DupeConfig {\n attributes: {\n elements: {\n [key: string]: {\n primary: string;\n aliases?: string[];\n };\n };\n };\n}\n\ninterface CleanupRecord {\n element: Element;\n mode: string;\n clones: Element[];\n}\n\ninterface DupeOptions {\n hidden: boolean;\n noSelect: boolean;\n}\n\nexport async function init(config: DupeConfig): Promise<{ result: string; destroy: () => void }> {\n // Inject CSS on first init\n injectCSS();\n\n const cleanup: { processedElements: CleanupRecord[] } = {\n processedElements: [],\n };\n\n // Find all elements with dupe attribute\n const dupeElements = querySelectorAll(config, 'wrapper');\n\n dupeElements.forEach((element) => {\n // Get dupe mode (child or self)\n const mode = element.getAttribute('data-hs-dupe');\n if (!mode || (mode !== 'child' && mode !== 'self')) {\n console.warn('[dupe] Invalid mode. Use \"child\" or \"self\":', element);\n return;\n }\n\n // Get dupe count (default: 1)\n const countAttr = element.getAttribute('data-hs-dupe-count');\n let count = countAttr ? parseInt(countAttr, 10) : 1;\n\n // Validate and cap count\n if (isNaN(count) || count < 1) {\n console.warn('[dupe] Invalid count, using default of 1:', element);\n count = 1;\n }\n\n if (count > MAX_DUPE_COUNT) {\n console.warn(\n `[dupe] Count of ${count} exceeds maximum of ${MAX_DUPE_COUNT}. Capping at ${MAX_DUPE_COUNT}.`\n );\n count = MAX_DUPE_COUNT;\n }\n\n // Get modifier options\n const options = {\n hidden: element.hasAttribute('data-hs-dupe-hidden'),\n noSelect: element.hasAttribute('data-hs-dupe-no-select'),\n };\n\n // Execute duplication based on mode\n if (mode === 'child') {\n dupeChild(element, count, options, cleanup);\n } else if (mode === 'self') {\n dupeSelf(element, count, options, cleanup);\n }\n });\n\n return {\n result: `dupe processed ${cleanup.processedElements.length} elements`,\n destroy: () => {},\n };\n}\n\nfunction dupeChild(\n element: Element,\n count: number,\n options: DupeOptions,\n cleanup: { processedElements: CleanupRecord[] }\n): void {\n // Get first direct child\n const firstChild = element.children[0];\n\n // Silently skip if no child exists (e.g., empty slot waiting for CMS content)\n if (!firstChild) {\n return;\n }\n\n const clones: Element[] = [];\n\n // Create clones and append as siblings\n for (let i = 0; i < count; i++) {\n const clone = firstChild.cloneNode(true) as Element;\n\n // Disable transitions during insertion\n clone.classList.add(TRANSITION_CLASS);\n\n // Apply modifiers to clone\n applyModifiers(clone, options);\n\n // Append to DOM\n element.appendChild(clone);\n clones.push(clone);\n\n // Force reflow\n (clone as HTMLElement).offsetHeight;\n\n // Re-enable transitions on next frame\n requestAnimationFrame(() => {\n clone.classList.remove(TRANSITION_CLASS);\n });\n }\n\n // Track for cleanup\n cleanup.processedElements.push({\n element,\n mode: 'child',\n clones,\n });\n}\n\nfunction dupeSelf(\n element: Element,\n count: number,\n options: DupeOptions,\n cleanup: { processedElements: CleanupRecord[] }\n): void {\n const parent = element.parentNode;\n\n if (!parent) {\n console.warn('[dupe] Element has no parent, cannot duplicate self:', element);\n return;\n }\n\n const clones: Element[] = [];\n\n // Create clones and insert as next siblings\n let referenceNode: Element | Node = element;\n for (let i = 0; i < count; i++) {\n const clone = element.cloneNode(true) as Element;\n\n // Disable transitions during insertion\n clone.classList.add(TRANSITION_CLASS);\n\n // Remove dupe attributes from clone\n clone.removeAttribute('data-hs-dupe');\n clone.removeAttribute('data-hs-dupe-count');\n clone.removeAttribute('data-hs-dupe-hidden');\n clone.removeAttribute('data-hs-dupe-no-select');\n\n // Apply modifiers to clone\n applyModifiers(clone, options);\n\n // Insert after reference node\n if (referenceNode.nextSibling) {\n parent.insertBefore(clone, referenceNode.nextSibling);\n } else {\n parent.appendChild(clone);\n }\n\n clones.push(clone);\n referenceNode = clone; // Next clone goes after this one\n\n // Force reflow\n (clone as HTMLElement).offsetHeight;\n\n // Re-enable transitions on next frame\n requestAnimationFrame(() => {\n clone.classList.remove(TRANSITION_CLASS);\n });\n }\n\n // Track for cleanup\n cleanup.processedElements.push({\n element,\n mode: 'self',\n clones,\n });\n}\n\nfunction applyModifiers(clone: Element, options: DupeOptions): void {\n // Add aria-hidden if requested\n if (options.hidden) {\n clone.setAttribute('aria-hidden', 'true');\n }\n\n // Add user-select: none if requested\n if (options.noSelect) {\n const htmlClone = clone as HTMLElement;\n htmlClone.style.userSelect = 'none';\n (htmlClone.style as unknown as { webkitUserSelect: string }).webkitUserSelect = 'none';\n (htmlClone.style as unknown as { msUserSelect: string }).msUserSelect = 'none';\n }\n}\n","/**\n * Normalize Orchestrator\n *\n * Sequential DOM normalization: Schema → Clickable → Replace → Sync → Dupe.\n * Critical first phase that prepares DOM structure for all other modules.\n */\nimport config from '@config';\nimport { init as schemaInit } from './functions/schema.ts';\nimport { init as syncInit } from './functions/sync.ts';\nimport { init as clickableInit } from './functions/clickable.ts';\nimport { init as dupeInit } from './functions/dupe.ts';\nimport { init as replaceInit } from './functions/replace.ts';\n\nconst CONFIG_ROOT = 'normalize';\n\nexport async function init(): Promise<{ result: string; destroy: () => void }> {\n const cleanup: { destroyFunctions: (() => void)[] } = { destroyFunctions: [] };\n const moduleConfig = config[CONFIG_ROOT];\n\n try {\n // Phase 1a: Schema (generates JSON-LD structured data for SEO)\n const schemaResult = schemaInit();\n if (schemaResult && 'destroy' in schemaResult && typeof schemaResult.destroy === 'function') {\n cleanup.destroyFunctions.push(schemaResult.destroy as () => void);\n }\n\n // Phase 1b: Clickable (normalizes button/link structure)\n const clickableResult = await clickableInit();\n if (\n clickableResult &&\n 'destroy' in clickableResult &&\n typeof clickableResult.destroy === 'function'\n ) {\n cleanup.destroyFunctions.push(clickableResult.destroy as () => void);\n }\n\n // Phase 1c: Replace (replaces {{placeholder}} tokens including year/month)\n const replaceResult = replaceInit(config.normalize?.replace);\n if (\n replaceResult &&\n 'destroy' in replaceResult &&\n typeof replaceResult.destroy === 'function'\n ) {\n cleanup.destroyFunctions.push(replaceResult.destroy as () => void);\n }\n\n // Phase 1d: Sync (populates lists with collection data)\n const syncResult = await syncInit(\n moduleConfig.sync as unknown as {\n attributes: {\n elements: { item: { primary: string } };\n properties: { syncId: string; syncField: string; syncMode: string; syncValue: string };\n };\n }\n );\n if (syncResult && 'destroy' in syncResult && typeof syncResult.destroy === 'function') {\n cleanup.destroyFunctions.push(syncResult.destroy as () => void);\n }\n\n // Phase 1e: Dupe (duplicates normalized elements)\n const dupeResult = await dupeInit(\n moduleConfig.dupe as unknown as {\n attributes: { elements: { [key: string]: { primary: string; aliases?: string[] } } };\n }\n );\n if (dupeResult && 'destroy' in dupeResult && typeof dupeResult.destroy === 'function') {\n cleanup.destroyFunctions.push(dupeResult.destroy as () => void);\n }\n\n return {\n result: 'normalize initialized',\n destroy: () => {\n // Call all destroy functions in reverse order\n cleanup.destroyFunctions.reverse().forEach((destroyFn) => {\n try {\n destroyFn();\n } catch (error) {\n console.error('[normalize] Error during cleanup:', error);\n }\n });\n cleanup.destroyFunctions.length = 0;\n },\n };\n } catch (error) {\n console.error('[normalize] Initialization failed:', error);\n // Cleanup any partial initialization\n cleanup.destroyFunctions.reverse().forEach((fn) => {\n try {\n fn();\n } catch (cleanupError) {\n console.error('[normalize] Error during error cleanup:', cleanupError);\n }\n });\n throw error;\n }\n}\n","/**\n * Clickable Normalization\n *\n * Normalizes clickable wrappers by keeping either button or link based on href validity.\n * Removes the unused element and ensures the kept element has data-hs-clickable=\"button\".\n *\n * Structure:\n * <div data-hs-clickable=\"wrapper\">\n * <button data-hs-clickable=\"button\">Click</button>\n * <a data-hs-clickable=\"button\" href=\"/page\">Click</a>\n * </div>\n *\n * Logic: Keep link if href is valid, otherwise keep button.\n */\nimport { globalConfig, querySelectorAll } from '@utils';\n\ninterface ProcessedWrapper {\n wrapper: Element;\n keptElement: Element;\n wasLink: boolean;\n addedAttribute: boolean;\n}\n\nexport async function init(): Promise<{ result: string; destroy: () => void }> {\n const cleanup: { processedWrappers: ProcessedWrapper[] } = {\n processedWrappers: [],\n };\n\n // Find all clickable wrappers using global clickable config\n const wrappers = querySelectorAll(\n globalConfig.clickable as {\n attributes: { elements: { [key: string]: { primary: string; aliases?: string[] } } };\n },\n 'wrapper'\n );\n\n wrappers.forEach((wrapper) => {\n // Find direct button and link children\n const button = wrapper.querySelector(':scope > button');\n const link = wrapper.querySelector(':scope > a');\n\n // Only process if both exist\n if (!button || !link) return;\n\n // Check if link has valid href\n const href = link.getAttribute('href');\n const hasValidHref = href && href !== '' && href !== '#';\n\n let keptElement: Element;\n let removedElement: Element;\n\n if (hasValidHref) {\n // Keep link, remove button\n keptElement = link;\n removedElement = button;\n } else {\n // Keep button, remove link\n keptElement = button;\n removedElement = link;\n }\n\n // Check if kept element already has data-hs-clickable=\"button\"\n const hadAttribute = keptElement.getAttribute('data-hs-clickable') === 'button';\n\n // Ensure kept element has data-hs-clickable=\"button\"\n if (!hadAttribute) {\n keptElement.setAttribute('data-hs-clickable', 'button');\n }\n\n // Remove the other element\n removedElement.remove();\n\n // Track for cleanup\n cleanup.processedWrappers.push({\n wrapper,\n keptElement,\n wasLink: keptElement === link,\n addedAttribute: !hadAttribute,\n });\n });\n\n return {\n result: `clickable processed ${cleanup.processedWrappers.length} wrappers`,\n destroy: () => {},\n };\n}\n","/**\n * Replace\n *\n * Collects values from elements and replaces {{placeholder}} text throughout the page.\n * Supports text content, href attributes, and automatic year/month injection.\n *\n * Features:\n * - Collects values from any element with data-hs-replace attribute\n * - Replaces {{name}} placeholders in text content and link hrefs\n * - Complete href replacement for links (prevents Webflow \"https://\" prefix issues)\n * - Supports multiple elements with same name (creates ordered array, joined with \", \")\n * - Conditional hide feature to exclude elements based on class names\n * - Automatic {{year}} and {{month}} replacement\n * - One-time replacement on page load (no ongoing observers)\n *\n * Structure:\n * <a data-hs-replace=\"email\" href=\"mailto:info@example.com\">info@example.com</a>\n * <span data-hs-replace=\"phone\">(555) 123-4567</span>\n * <div data-hs-replace=\"service\">Service 1</div>\n * <div data-hs-replace=\"service\">Service 2</div>\n *\n * Usage:\n * Text: \"Contact us at {{email}}\" → \"Contact us at info@example.com\"\n * Link: <a href=\"{{email}}\">Email</a> → <a href=\"mailto:info@example.com\">Email</a>\n * Array: \"Services: {{service}}\" → \"Services: Service 1, Service 2\"\n * Dynamic: \"Copyright {{year}}\" → \"Copyright 2024\"\n */\n\nimport { getSelector } from '@utils';\n\nexport function init(config: any) {\n function setupReplacements() {\n // Map to store replacements (supports arrays for duplicate names)\n const replacements = new Map<\n string,\n | { text: string | null; href: string | null }\n | Array<{ text: string | null; href: string | null }>\n >();\n\n // 1. Collect from data-hs-replace elements\n const elementSelector = getSelector(config, 'element');\n const elements = document.querySelectorAll(elementSelector);\n\n elements.forEach((element) => {\n // Clone the element to avoid modifying the original DOM\n const clonedElement = element.cloneNode(true) as Element;\n\n // Find and remove elements with data-hs-replace-hide that match their classes\n const hideSelector = getSelector(config, 'hide');\n const hideElements = clonedElement.querySelectorAll(hideSelector);\n\n hideElements.forEach((hideElement) => {\n const hideAttr = config.attributes.elements.hide.primary.match(/data-hs-replace-hide/)[0];\n const hideValue = hideElement.getAttribute(hideAttr);\n\n if (hideValue) {\n // Split by spaces to get all class names to check\n const classesToHide = hideValue.split(' ').filter((c: string) => c.trim());\n\n // Check if element has any of the classes\n const hasMatchingClass = classesToHide.some((className: string) =>\n hideElement.classList.contains(className)\n );\n\n // Remove element if it has a matching class\n if (hasMatchingClass) {\n hideElement.remove();\n }\n }\n });\n\n const elementAttr = config.attributes.elements.element.primary.match(/data-hs-replace/)[0];\n const name = element.getAttribute(elementAttr);\n const text = clonedElement.textContent?.trim() || null;\n const href = element.getAttribute('href');\n\n // Store if name exists and either text or href exists\n if (name && (text || href)) {\n const value = { text, href };\n\n // If name already exists, convert to array or append to array\n if (replacements.has(name)) {\n const existing = replacements.get(name)!;\n if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n replacements.set(name, [existing, value]);\n }\n } else {\n replacements.set(name, value);\n }\n }\n });\n\n // 2. Add dynamic year/month replacements\n const currentYear = new Date().getFullYear().toString();\n const monthNames = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n ];\n const currentMonth = monthNames[new Date().getMonth()];\n\n replacements.set('year', { text: currentYear, href: null });\n replacements.set('month', { text: currentMonth, href: null });\n\n // If no replacements found, exit early\n if (replacements.size === 0) {\n return;\n }\n\n // 3. Replace text content efficiently using TreeWalker\n const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {\n acceptNode: (node) => {\n // Check if any replacement names exist in the text content\n const text = node.textContent || '';\n for (const name of replacements.keys()) {\n if (text.includes(`{{${name}}}`)) {\n return NodeFilter.FILTER_ACCEPT;\n }\n }\n return NodeFilter.FILTER_SKIP;\n },\n });\n\n const textNodes: Node[] = [];\n let node: Node | null;\n while ((node = walker.nextNode())) {\n textNodes.push(node);\n }\n\n // Replace text in collected nodes\n textNodes.forEach((textNode) => {\n let newText = textNode.textContent || '';\n let hasChanges = false;\n\n for (const [name, value] of replacements.entries()) {\n const placeholder = `{{${name}}}`;\n if (newText.includes(placeholder)) {\n let replacementValue: string | null = null;\n\n // Handle array values (join with \", \")\n if (Array.isArray(value)) {\n replacementValue = value\n .map((v) => v.text || v.href)\n .filter(Boolean)\n .join(', ');\n } else {\n // Use text value if available, otherwise use href value\n replacementValue = value.text || value.href;\n }\n\n if (replacementValue) {\n newText = newText.replace(new RegExp(placeholder, 'g'), replacementValue);\n hasChanges = true;\n }\n }\n }\n\n if (hasChanges) {\n textNode.textContent = newText;\n }\n });\n\n // 4. Replace link hrefs\n const links = document.querySelectorAll('a[href]');\n links.forEach((link) => {\n let href = link.getAttribute('href') || '';\n let hasChanges = false;\n\n for (const [name, value] of replacements.entries()) {\n const placeholder = `{{${name}}}`;\n if (href.includes(placeholder)) {\n let replacementValue: string | null = null;\n\n // Handle array values - use first item's href or text\n if (Array.isArray(value)) {\n const firstValue = value[0];\n replacementValue = firstValue.href || firstValue.text;\n } else {\n // If value has a dedicated href, completely replace the entire href\n // (prevents Webflow's default \"https://\" from breaking tel:, mailto:, etc.)\n if (value.href) {\n replacementValue = value.href;\n href = value.href; // Complete replacement, not substitution\n hasChanges = true;\n break; // Only one replacement per link\n }\n // Otherwise, do normal placeholder replacement with text value\n else if (value.text) {\n replacementValue = value.text;\n }\n }\n\n if (replacementValue && !hasChanges) {\n href = href.replace(new RegExp(placeholder, 'g'), replacementValue);\n hasChanges = true;\n }\n }\n }\n\n if (hasChanges) {\n link.setAttribute('href', href);\n }\n });\n\n // 5. Optional debug output (if data-hs-replace-test element exists)\n const testElement = document.querySelector('[data-hs-replace-test]');\n if (testElement) {\n const debugData: Record<\n string,\n {\n placeholder: string;\n value: string;\n text?: string | null;\n href?: string | null;\n type?: string;\n items?: string[];\n }\n > = {};\n\n for (const [name, value] of replacements.entries()) {\n const placeholder = `{{${name}}}`;\n\n if (Array.isArray(value)) {\n const displayValue = value\n .map((v) => v.text || v.href)\n .filter(Boolean)\n .join(', ');\n\n debugData[name] = {\n placeholder,\n value: displayValue,\n type: `Array (${value.length} items)`,\n items: value.map((v) => v.text || v.href || '(empty)'),\n };\n } else {\n const displayValue = value.text || value.href || '(empty)';\n debugData[name] = {\n placeholder,\n value: displayValue,\n ...(value.text && { text: value.text }),\n ...(value.href && { href: value.href }),\n };\n }\n }\n\n const pre = document.createElement('pre');\n pre.textContent = JSON.stringify(debugData, null, 2);\n\n testElement.innerHTML = '';\n testElement.appendChild(\n document.createTextNode(`Replace Module Debug (${replacements.size} replacements):`)\n );\n testElement.appendChild(pre);\n }\n }\n\n setupReplacements();\n\n return {\n result: 'replace initialized',\n destroy: () => {},\n };\n}\n"],"names":["init","baseURL","window","location","origin","getReplaceText","name","element","document","querySelector","textContent","trim","businessName","phone","email","city","state","zipCode","schema","url","telephone","logoElement","src","image","address","addressLocality","addressRegion","postalCode","addressCountry","streetAddress","latitude","longitude","geo","socialLinks","map","href","getReplaceHref","filter","link","includes","length","sameAs","servicesWrapper","serviceItems","querySelectorAll","services","Array","from","el","service","serviceType","areasWrapper","areaItems","areas","area","areaServed","businessScript","createElement","type","text","JSON","stringify","head","appendChild","breadcrumbText","pathParts","pathname","split","part","breadcrumbList","itemListElement","position","item","firstSlug","middleBreadcrumb","word","charAt","toUpperCase","slice","join","push","breadcrumbScript","faqArray","forEach","faqList","faqAccordion","toggleElement","contentElement","questionText","answerHTML","innerHTML","acceptedAnswer","faqSchema","mainEntity","faqScript","nameElement","descElement","serviceName","serviceDescription","serviceSchema","description","serviceScript","console","warn","questionElement","answerElement","publishedElement","modifiedElement","publishedDate","modifiedDate","qaPageSchema","answerCount","datePublished","dateModified","qaPageScript","missing","blogNameElement","blogPublishedElement","blogModifiedElement","summaryElement","authorNameElement","authorTypeElement","blogName","summaryText","authorName","authorType","authorRoleElement","authorImageElement","authorRole","authorImage","companyName","logoURL","authorSchema","jobTitle","logo","blogSchema","headline","author","publisher","mainEntityOfPage","blogScript","setupSchema","result","destroy","getTextNodes","textNodes","walker","createTreeWalker","NodeFilter","SHOW_TEXT","node","nextNode","nodeValue","getHrefElements","hrefs","hasAttribute","findElementWithSrc","getAttribute","descendant","syncItemData","sourceItem","targetClone","config","fieldAttr","attributes","properties","syncField","usesFieldBasedSync","sourceField","fieldName","targetField","sourceImgElement","targetImgElement","setAttribute","sourceTextNodes","sourceText","targetTextNodes","syncFieldBasedData","sourceHrefs","sourceHref","TRANSITION_CLASS","cssInjected","async","style","injectCSS","cleanup","processedElements","mode","countAttr","count","parseInt","isNaN","options","hidden","noSelect","firstChild","children","clones","i","clone","cloneNode","classList","add","applyModifiers","offsetHeight","requestAnimationFrame","remove","dupeChild","parent","parentNode","referenceNode","removeAttribute","nextSibling","insertBefore","dupeSelf","htmlClone","userSelect","webkitUserSelect","msUserSelect","destroyFunctions","moduleConfig","schemaResult","schemaInit","clickableResult","processedWrappers","globalConfig","clickable","wrapper","button","keptElement","removedElement","hadAttribute","wasLink","addedAttribute","clickableInit","replaceResult","normalize","replace","replacements","Map","elementSelector","getSelector","clonedElement","hideSelector","hideElement","hideAttr","elements","hide","primary","match","hideValue","c","some","className","contains","elementAttr","value","has","existing","get","isArray","set","currentYear","Date","getFullYear","toString","currentMonth","getMonth","size","body","acceptNode","keys","FILTER_ACCEPT","FILTER_SKIP","textNode","newText","hasChanges","entries","placeholder","replacementValue","v","Boolean","RegExp","firstValue","testElement","debugData","displayValue","items","pre","createTextNode","setupReplacements","syncResult","sourceLists","sourceMapById","sourceMapByField","source","syncId","targetLists","syncedCount","skippedCount","target","syncValue","targetTemplateWrapper","identifier","isChildMode","syncMode","ignoreElement","sourceItems","template","allSourceItems","matchedItem","find","firstElementChild","insertAfterIgnore","currentElement","nextElementSibling","removeChild","childClone","child","syncedItems","syncInit","sync","dupeResult","dupeInit","dupe","reverse","destroyFn","error","fn","cleanupError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hs-normalize-CW1mxe_R.js","sources":["../../src/modules/normalize/functions/schema.ts","../../src/modules/normalize/functions/sync.ts","../../src/modules/normalize/functions/dupe.ts","../../src/modules/normalize/normalize.ts","../../src/modules/normalize/functions/clickable.ts","../../src/modules/normalize/functions/replace.ts"],"sourcesContent":["/**\n * Schema Generator\n *\n * Generates JSON-LD structured data for SEO (LocalBusiness, BreadcrumbList, FAQPage).\n * Reads from data-hs-replace elements and data-hs-schema markers.\n *\n * Features:\n * - LocalBusiness schema with company info, address, geo, social links\n * - Optional services and areas served (only if elements exist)\n * - BreadcrumbList schema (auto-generated from URL path)\n * - FAQPage schema (from accordion elements with data-hs-accordion-type=\"faq\")\n * - Filters out empty/placeholder values\n * - Injects as <script type=\"application/ld+json\"> in <head>\n *\n * Structure:\n * <!-- Business info (uses replace data) -->\n * <span data-hs-replace=\"company\">Business Name</span>\n * <span data-hs-replace=\"phone\">(555) 123-4567</span>\n *\n * <!-- Services wrapper -->\n * <div data-hs-schema=\"services\">\n * <div data-hs-schema-list=\"item\">Service 1</div>\n * <div data-hs-schema-list=\"item\">Service 2</div>\n * </div>\n *\n * <!-- Areas wrapper -->\n * <div data-hs-schema=\"areas\">\n * <div data-hs-schema-list=\"item\">City 1</div>\n * </div>\n *\n * <!-- Breadcrumb marker -->\n * <span data-hs-schema=\"breadcrumb\">Page Title</span>\n *\n * <!-- FAQ accordions -->\n * <div data-hs-accordion=\"wrapper\">\n * <div data-hs-accordion-type=\"faq\">\n * <div data-hs-accordion=\"toggle\">Question?</div>\n * <div data-hs-accordion=\"content\">Answer</div>\n * </div>\n * </div>\n */\n\nexport function init() {\n function setupSchema() {\n const baseURL = window.location.origin;\n\n // Helper to get text content from data-hs-replace element\n const getReplaceText = (name: string): string => {\n const element = document.querySelector(`[data-hs-replace=\"${name}\"]`);\n return element?.textContent?.trim() || '';\n };\n\n // Helper to get href from data-hs-replace element\n const getReplaceHref = (name: string): string => {\n const element = document.querySelector(`[data-hs-replace=\"${name}\"]`);\n return (element as HTMLAnchorElement)?.href || '';\n };\n\n // 1. Build LocalBusiness Schema\n const businessName = getReplaceText('company');\n const phone = getReplaceText('phone');\n const email = getReplaceText('email');\n const city = getReplaceText('city');\n const state = getReplaceText('state');\n const zipCode = getReplaceText('zipcode');\n\n const schema: any = {\n '@context': 'https://schema.org',\n '@type': 'LocalBusiness',\n name: businessName,\n url: baseURL,\n telephone: phone,\n email: email,\n };\n\n // Add logo if it exists\n const logoElement = document.querySelector('[data-hs-replace=\"business-logo\"] img');\n if (logoElement && (logoElement as HTMLImageElement).src) {\n schema.image = (logoElement as HTMLImageElement).src;\n }\n\n // Add address\n const address: any = {\n '@type': 'PostalAddress',\n addressLocality: city,\n addressRegion: state,\n postalCode: zipCode,\n addressCountry: 'US',\n };\n\n const streetAddress = getReplaceText('street-address');\n if (streetAddress) {\n address.streetAddress = streetAddress;\n }\n\n schema.address = address;\n\n // Add geo coordinates if they exist\n const latitude = getReplaceText('business-latitude');\n const longitude = getReplaceText('business-longitude');\n if (latitude && longitude) {\n schema.geo = {\n '@type': 'GeoCoordinates',\n latitude: latitude,\n longitude: longitude,\n };\n }\n\n // Add social links (filter out empty/placeholder)\n const socialNames = [\n 'facebook',\n 'instagram',\n 'linkedin',\n 'pinterest',\n 'tiktok',\n 'x',\n 'youtube',\n ];\n const socialLinks = socialNames\n .map((name) => getReplaceHref(name))\n .filter((link) => link && link.trim() !== '' && !link.includes('placeholder'));\n\n if (socialLinks.length > 0) {\n schema.sameAs = socialLinks;\n }\n\n // Add services if wrapper exists\n const servicesWrapper = document.querySelector('[data-hs-schema=\"services\"]');\n if (servicesWrapper) {\n const serviceItems = servicesWrapper.querySelectorAll('[data-hs-schema-list=\"item\"]');\n const services = Array.from(serviceItems)\n .map((el) => el.textContent?.trim())\n .filter((service) => service && service !== '');\n\n if (services.length > 0) {\n schema.serviceType = services;\n }\n }\n\n // Add areas served if wrapper exists\n const areasWrapper = document.querySelector('[data-hs-schema=\"areas\"]');\n if (areasWrapper) {\n const areaItems = areasWrapper.querySelectorAll('[data-hs-schema-list=\"item\"]');\n const areas = Array.from(areaItems)\n .map((el) => ({\n '@type': 'City',\n name: el.textContent?.trim() || '',\n }))\n .filter((area) => area.name && area.name !== '');\n\n if (areas.length > 0) {\n schema.areaServed = areas;\n }\n }\n\n // Inject LocalBusiness schema\n const businessScript = document.createElement('script');\n businessScript.type = 'application/ld+json';\n businessScript.text = JSON.stringify(schema);\n document.head.appendChild(businessScript);\n\n // 2. Build BreadcrumbList Schema (if page has path)\n const breadcrumbText =\n document.querySelector('[data-hs-schema=\"breadcrumb\"]')?.textContent?.trim() || '';\n const pathname = window.location.pathname;\n const pathParts = pathname.split('/').filter((part) => part !== '');\n\n if (pathParts.length > 0) {\n const breadcrumbList: any = {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: [\n {\n '@type': 'ListItem',\n position: 1,\n name: 'Home',\n item: baseURL,\n },\n ],\n };\n\n // Add middle breadcrumb (first slug capitalized)\n const firstSlug = pathParts[0];\n const middleBreadcrumb = firstSlug\n .split('-')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n\n breadcrumbList.itemListElement.push({\n '@type': 'ListItem',\n position: 2,\n name: middleBreadcrumb,\n item: baseURL + '/' + firstSlug,\n });\n\n // Add final breadcrumb if text exists\n if (breadcrumbText) {\n breadcrumbList.itemListElement.push({\n '@type': 'ListItem',\n position: 3,\n name: breadcrumbText,\n item: window.location.href,\n });\n }\n\n // Inject breadcrumb schema\n const breadcrumbScript = document.createElement('script');\n breadcrumbScript.type = 'application/ld+json';\n breadcrumbScript.text = JSON.stringify(breadcrumbList);\n document.head.appendChild(breadcrumbScript);\n }\n\n // 3. Build FAQPage Schema (if FAQ accordions exist)\n const hasFAQs = document.querySelector('[data-hs-accordion-type=\"faq\"]');\n if (hasFAQs) {\n const faqArray: any[] = [];\n const faqLists = document.querySelectorAll('[data-hs-accordion=\"list\"]');\n\n faqLists.forEach((faqList) => {\n const faqAccordions = faqList.querySelectorAll('[data-hs-accordion-type=\"faq\"]');\n\n faqAccordions.forEach((faqAccordion) => {\n const toggleElement = faqAccordion.querySelector('[data-hs-accordion=\"toggle\"]');\n const contentElement = faqAccordion.querySelector('[data-hs-accordion=\"content\"]');\n\n if (toggleElement && contentElement) {\n const questionText = toggleElement.textContent?.trim();\n const answerHTML = contentElement.innerHTML.trim();\n\n faqArray.push({\n '@type': 'Question',\n name: questionText,\n acceptedAnswer: {\n '@type': 'Answer',\n text: answerHTML,\n },\n });\n }\n });\n });\n\n // Only create schema if we have FAQs\n if (faqArray.length > 0) {\n const faqSchema = {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: faqArray,\n };\n\n // Inject FAQ schema\n const faqScript = document.createElement('script');\n faqScript.type = 'application/ld+json';\n faqScript.text = JSON.stringify(faqSchema);\n document.head.appendChild(faqScript);\n }\n }\n\n // 4. Build Service Page Schema (if service elements exist)\n const nameElement = document.querySelector('[data-hs-schema-service=\"name\"]');\n const descElement = document.querySelector('[data-hs-schema-service=\"description\"]');\n\n // Require BOTH name and description\n if (nameElement && descElement) {\n const serviceName = nameElement.textContent?.trim() || '';\n // Convert description to plain text (in case it's rich text)\n const serviceDescription = descElement.textContent?.trim() || '';\n\n if (serviceName && serviceDescription) {\n const serviceSchema = {\n '@context': 'https://schema.org',\n '@type': 'Service',\n name: serviceName,\n description: serviceDescription,\n };\n\n const serviceScript = document.createElement('script');\n serviceScript.type = 'application/ld+json';\n serviceScript.text = JSON.stringify(serviceSchema);\n document.head.appendChild(serviceScript);\n }\n } else if (nameElement || descElement) {\n console.warn('[schema] Service page schema requires both \"name\" and \"description\" elements');\n }\n\n // 5. Build FAQ Page Schema (QAPage - single FAQ page)\n const questionElement = document.querySelector('[data-hs-schema-faq=\"question\"]');\n const answerElement = document.querySelector('[data-hs-schema-faq=\"answer\"]');\n const publishedElement = document.querySelector('[data-hs-schema-faq=\"published\"]');\n const modifiedElement = document.querySelector('[data-hs-schema-faq=\"modified\"]');\n\n // Require ALL 4 fields\n if (questionElement && answerElement && publishedElement && modifiedElement) {\n // Question: convert to plain text\n const questionText = questionElement.textContent?.trim() || '';\n // Answer: keep as HTML\n const answerHTML = answerElement.innerHTML.trim();\n const publishedDate = publishedElement.textContent?.trim() || '';\n const modifiedDate = modifiedElement.textContent?.trim() || '';\n\n if (questionText && answerHTML && publishedDate && modifiedDate) {\n const qaPageSchema = {\n '@context': 'https://schema.org',\n '@type': 'QAPage',\n mainEntity: {\n '@type': 'Question',\n name: questionText,\n text: questionText,\n answerCount: 1,\n datePublished: publishedDate,\n dateModified: modifiedDate,\n acceptedAnswer: {\n '@type': 'Answer',\n text: answerHTML,\n },\n },\n };\n\n const qaPageScript = document.createElement('script');\n qaPageScript.type = 'application/ld+json';\n qaPageScript.text = JSON.stringify(qaPageSchema);\n document.head.appendChild(qaPageScript);\n }\n } else if (questionElement || answerElement || publishedElement || modifiedElement) {\n const missing: string[] = [];\n if (!questionElement) missing.push('question');\n if (!answerElement) missing.push('answer');\n if (!publishedElement) missing.push('published');\n if (!modifiedElement) missing.push('modified');\n console.warn(`[schema] FAQ page schema missing required fields: ${missing.join(', ')}`);\n }\n\n // 6. Build Blog Page Schema\n const blogNameElement = document.querySelector('[data-hs-schema-blog=\"name\"]');\n const blogPublishedElement = document.querySelector('[data-hs-schema-blog=\"published\"]');\n const blogModifiedElement = document.querySelector('[data-hs-schema-blog=\"modified\"]');\n const summaryElement = document.querySelector('[data-hs-schema-blog=\"summary\"]');\n const authorNameElement = document.querySelector('[data-hs-schema-blog=\"author-name\"]');\n const authorTypeElement = document.querySelector('[data-hs-schema-blog=\"author-type\"]');\n\n // Require: name, published, modified, summary, author-name, author-type\n if (\n blogNameElement &&\n blogPublishedElement &&\n blogModifiedElement &&\n summaryElement &&\n authorNameElement &&\n authorTypeElement\n ) {\n const blogName = blogNameElement.textContent?.trim() || '';\n const publishedDate = blogPublishedElement.textContent?.trim() || '';\n const modifiedDate = blogModifiedElement.textContent?.trim() || '';\n // Summary: convert to plain text\n const summaryText = summaryElement.textContent?.trim() || '';\n const authorName = authorNameElement.textContent?.trim() || '';\n const authorType = authorTypeElement.textContent?.trim() || 'Organization';\n\n // Optional fields\n const authorRoleElement = document.querySelector('[data-hs-schema-blog=\"author-role\"]');\n const authorImageElement = document.querySelector(\n '[data-hs-schema-blog=\"author-image\"]'\n ) as HTMLImageElement;\n\n const authorRole = authorRoleElement?.textContent?.trim() || '';\n const authorImage = authorImageElement?.src || '';\n\n // Get company info for publisher\n const companyName = getReplaceText('company');\n const logoElement = document.querySelector('[data-hs-replace=\"business-logo\"] img');\n const logoURL = logoElement ? (logoElement as HTMLImageElement).src : '';\n\n // Build author schema\n let authorSchema: any;\n if (authorType === 'Person') {\n authorSchema = {\n '@type': 'Person',\n name: authorName,\n };\n if (authorRole) authorSchema.jobTitle = authorRole;\n if (authorImage) authorSchema.image = authorImage;\n } else {\n authorSchema = {\n '@type': 'Organization',\n name: authorName,\n };\n if (logoURL) authorSchema.logo = logoURL;\n }\n\n const blogSchema: any = {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: blogName,\n description: summaryText,\n datePublished: publishedDate,\n dateModified: modifiedDate,\n author: authorSchema,\n publisher: {\n '@type': 'Organization',\n name: companyName || authorName,\n logo: {\n '@type': 'ImageObject',\n url: logoURL,\n },\n },\n mainEntityOfPage: {\n '@type': 'WebPage',\n '@id': window.location.href,\n },\n };\n\n const blogScript = document.createElement('script');\n blogScript.type = 'application/ld+json';\n blogScript.text = JSON.stringify(blogSchema);\n document.head.appendChild(blogScript);\n } else if (\n blogNameElement ||\n blogPublishedElement ||\n blogModifiedElement ||\n summaryElement ||\n authorNameElement ||\n authorTypeElement\n ) {\n const missing: string[] = [];\n if (!blogNameElement) missing.push('name');\n if (!blogPublishedElement) missing.push('published');\n if (!blogModifiedElement) missing.push('modified');\n if (!summaryElement) missing.push('summary');\n if (!authorNameElement) missing.push('author-name');\n if (!authorTypeElement) missing.push('author-type');\n console.warn(`[schema] Blog page schema missing required fields: ${missing.join(', ')}`);\n }\n }\n\n setupSchema();\n\n return {\n result: 'schema initialized',\n destroy: () => {},\n };\n}\n","/**\n * List Sync\n *\n * Syncs collection list data to static lists by sync-id.\n *\n * Simple mode (text/href):\n * <div data-hs-sync=\"source\" data-hs-sync-id=\"services\">\n * <a data-hs-sync=\"item\" href=\"/service-1\">Service 1</a>\n * </div>\n * <div data-hs-sync=\"target\" data-hs-sync-id=\"services\">\n * <a data-hs-sync=\"item\" href=\"#\">Placeholder</a>\n * </div>\n *\n * Field-based mode (named fields):\n * <div data-hs-sync=\"source\" data-hs-sync-id=\"projects\">\n * <div data-hs-sync=\"item\">\n * <img data-hs-sync-field=\"before\" src=\"/before.jpg\">\n * <img data-hs-sync-field=\"after\" src=\"/after.jpg\">\n * </div>\n * </div>\n * <div data-hs-sync=\"target\" data-hs-sync-id=\"projects\">\n * <div data-hs-sync=\"item\">\n * <img data-hs-sync-field=\"before\" src=\"/placeholder.jpg\">\n * <img data-hs-sync-field=\"after\" src=\"/placeholder.jpg\">\n * </div>\n * </div>\n *\n * Value-based matching (match by value):\n * <div data-hs-sync=\"source\" data-hs-sync-field=\"area-type\">\n * <div data-hs-sync=\"item\" data-hs-sync-value=\"Residential\">\n * <svg>...</svg>\n * </div>\n * <div data-hs-sync=\"item\" data-hs-sync-value=\"Commercial\">\n * <svg>...</svg>\n * </div>\n * </div>\n * <div data-hs-sync=\"target\" data-hs-sync-field=\"area-type\" data-hs-sync-value=\"Residential\">\n * <div data-hs-sync=\"item\"><!-- Matched SVG injected here --></div>\n * </div>\n *\n * Child mode (wrapper templates):\n * <div data-hs-sync=\"target\" data-hs-sync-id=\"marquee\">\n * <div data-hs-sync=\"item\" data-hs-sync-mode=\"child\">\n * <div class=\"marquee_list\">\n * <img data-hs-sync-field=\"image\" src=\"/placeholder.jpg\">\n * </div>\n * </div>\n * </div>\n *\n * Supports data-hs-sync=\"ignore\" to preserve elements during sync.\n */\n\nimport { querySelectorAll, querySelector } from '@utils';\n\n/**\n * Get all text nodes within an element\n */\nfunction getTextNodes(element: Element): Text[] {\n const textNodes: Text[] = [];\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);\n\n let node: Node | null;\n while ((node = walker.nextNode())) {\n // Skip empty/whitespace-only nodes\n if (node.nodeValue?.trim()) {\n textNodes.push(node as Text);\n }\n }\n\n return textNodes;\n}\n\n/**\n * Get all elements with href attribute (including the element itself)\n */\nfunction getHrefElements(element: Element): Element[] {\n const hrefs: Element[] = [];\n\n // Check if element itself has href\n if (element.hasAttribute('href')) {\n hrefs.push(element);\n }\n\n // Check descendants\n hrefs.push(...Array.from(element.querySelectorAll('[href]')));\n\n return hrefs;\n}\n\n/**\n * Check if item uses field-based syncing\n */\nfunction usesFieldBasedSync(\n item: Element,\n config: { attributes: { properties: { syncField: string } } }\n): boolean {\n const fieldAttr = config.attributes.properties.syncField;\n return item.querySelector(`[${fieldAttr}]`) !== null;\n}\n\n/**\n * Find element with src attribute (check element itself, then descendants)\n */\nfunction findElementWithSrc(element: Element): Element | null {\n // Check element itself\n if (element.hasAttribute('src')) {\n const src = element.getAttribute('src');\n if (src) return element;\n }\n\n // Check descendants\n const descendant = element.querySelector('[src]');\n if (descendant) {\n const src = descendant.getAttribute('src');\n if (src) return descendant;\n }\n\n return null;\n}\n\n/**\n * Sync data using field-based mapping\n * Maps fields by name between source and target\n */\nfunction syncFieldBasedData(\n sourceItem: Element,\n targetClone: Element,\n config: { attributes: { properties: { syncField: string } } }\n): void {\n const fieldAttr = config.attributes.properties.syncField;\n\n // Get all fields in source\n const sourceFields = sourceItem.querySelectorAll(`[${fieldAttr}]`);\n\n sourceFields.forEach((sourceField: Element) => {\n const fieldName = sourceField.getAttribute(fieldAttr);\n\n // Find matching field in target\n const targetField = targetClone.querySelector(`[${fieldAttr}=\"${fieldName}\"]`);\n if (!targetField) return;\n\n // Try to find elements with src (for images)\n const sourceImgElement = findElementWithSrc(sourceField);\n const targetImgElement = findElementWithSrc(targetField);\n\n if (sourceImgElement && targetImgElement) {\n // Sync src attribute\n const src = sourceImgElement.getAttribute('src');\n if (src) {\n targetImgElement.setAttribute('src', src);\n }\n }\n // Link elements: sync href\n else if (sourceField.hasAttribute('href') && targetField.hasAttribute('href')) {\n const href = sourceField.getAttribute('href');\n if (href) {\n targetField.setAttribute('href', href);\n }\n }\n // Text elements: sync text content\n else {\n const sourceTextNodes = getTextNodes(sourceField);\n const sourceText =\n sourceTextNodes.length > 0 ? sourceTextNodes[0].nodeValue : sourceField.textContent.trim();\n\n if (sourceText) {\n const targetTextNodes = getTextNodes(targetField);\n if (targetTextNodes.length > 0) {\n targetTextNodes[0].nodeValue = sourceText;\n } else {\n targetField.textContent = sourceText;\n }\n }\n }\n });\n}\n\n/**\n * Sync data from source item to target clone\n * Extracts text and href from source, applies to ALL matching elements in target\n */\nfunction syncItemData(\n sourceItem: Element,\n targetClone: Element,\n config: { attributes: { properties: { syncField: string } } }\n): void {\n // Check if using field-based syncing\n if (usesFieldBasedSync(targetClone, config)) {\n syncFieldBasedData(sourceItem, targetClone, config);\n return;\n }\n\n // Fallback to simple syncing (existing behavior)\n // Get text content from source (first non-empty text node)\n const sourceTextNodes = getTextNodes(sourceItem);\n const sourceText = sourceTextNodes.length > 0 ? sourceTextNodes[0].nodeValue : '';\n\n // Get href from source (first href element)\n const sourceHrefs = getHrefElements(sourceItem);\n const sourceHref = sourceHrefs.length > 0 ? sourceHrefs[0].getAttribute('href') : '';\n\n // Apply source text to ALL text nodes in target\n if (sourceText) {\n const targetTextNodes = getTextNodes(targetClone);\n targetTextNodes.forEach((node) => {\n node.nodeValue = sourceText;\n });\n }\n\n // Apply source href to ALL href elements in target\n if (sourceHref) {\n const targetHrefs = getHrefElements(targetClone);\n targetHrefs.forEach((el) => {\n el.setAttribute('href', sourceHref);\n });\n }\n}\n\ninterface SyncConfig {\n attributes: {\n elements: {\n item: { primary: string };\n };\n properties: {\n syncId: string;\n syncField: string;\n syncMode: string;\n syncValue: string;\n };\n };\n}\n\nexport async function init(config: SyncConfig): Promise<{ result: string; destroy: () => void }> {\n // Find all source lists and build maps by sync-id and sync-field\n const sourceLists = querySelectorAll(config, 'source');\n const sourceMapById = new Map<string, Element>();\n const sourceMapByField = new Map<string, Element>();\n\n sourceLists.forEach((source) => {\n const syncId = source.getAttribute(config.attributes.properties.syncId);\n const syncField = source.getAttribute(config.attributes.properties.syncField);\n\n if (syncId) {\n sourceMapById.set(syncId, source);\n }\n if (syncField) {\n sourceMapByField.set(syncField, source);\n }\n });\n\n // Find all target lists\n const targetLists = querySelectorAll(config, 'target');\n let syncedCount = 0;\n let skippedCount = 0;\n\n targetLists.forEach((target) => {\n const syncId = target.getAttribute(config.attributes.properties.syncId);\n const syncField = target.getAttribute(config.attributes.properties.syncField);\n const syncValue = target.getAttribute(config.attributes.properties.syncValue);\n\n // Determine which matching mode to use\n let source: Element | undefined;\n\n // Value-based matching (sync-field + sync-value)\n if (syncField && syncValue) {\n source = sourceMapByField.get(syncField);\n if (!source) {\n skippedCount++;\n return;\n }\n }\n // ID-based matching (sync-id)\n else if (syncId) {\n source = sourceMapById.get(syncId);\n if (!source) {\n skippedCount++;\n return;\n }\n }\n // No valid matching criteria\n else {\n skippedCount++;\n return;\n }\n\n // Find item template in target (element to clone)\n const targetTemplateWrapper = querySelector(config, 'item', target);\n if (!targetTemplateWrapper) {\n const identifier = syncField || syncId || 'unknown';\n console.warn(`[sync] Target list \"${identifier}\" missing item template`);\n return;\n }\n\n // Check sync mode - determines which element to use as template\n const syncMode = targetTemplateWrapper.getAttribute(config.attributes.properties.syncMode);\n const isChildMode = syncMode === 'child';\n\n // Find ignore element in target (if exists)\n const ignoreElement = querySelector(config, 'ignore', target);\n\n // Find source items - filtered by value if value-based matching\n let sourceItems: Element[];\n\n if (syncValue) {\n // Value-based matching: find single item with matching sync-value\n const allSourceItems = querySelectorAll(config, 'item', source);\n const matchedItem = Array.from(allSourceItems).find((item) => {\n const itemValue = item.getAttribute(config.attributes.properties.syncValue);\n return itemValue === syncValue;\n });\n\n if (!matchedItem) {\n console.warn(\n `[sync] No source item found with value \"${syncValue}\" in field \"${syncField}\"`\n );\n return;\n }\n\n sourceItems = [matchedItem];\n } else {\n // ID-based matching: use all source items\n sourceItems = Array.from(querySelectorAll(config, 'item', source));\n if (sourceItems.length === 0) {\n console.warn(`[sync] Source list \"${syncId}\" has no items`);\n return;\n }\n }\n\n // Determine template based on mode\n let template: Element;\n if (isChildMode) {\n // Child mode: clone the wrapper, we'll replace its child content\n const firstChild = targetTemplateWrapper.firstElementChild;\n if (!firstChild) {\n console.warn(\n `[sync] Target list \"${syncId}\" has sync-mode=\"child\" but item wrapper has no children`\n );\n return;\n }\n // Clone the child element to use for syncing data\n template = firstChild.cloneNode(true) as Element;\n } else {\n // Default mode: clone the item element itself\n template = targetTemplateWrapper.cloneNode(true) as Element;\n template.removeAttribute(config.attributes.elements.item.primary.split('=')[0]);\n }\n\n // Determine insertion strategy if ignore exists\n let insertAfterIgnore = false;\n if (ignoreElement) {\n // Check if template wrapper comes after ignore element\n let currentElement = ignoreElement.nextElementSibling;\n while (currentElement) {\n if (currentElement === targetTemplateWrapper) {\n insertAfterIgnore = true;\n break;\n }\n currentElement = currentElement.nextElementSibling;\n }\n }\n\n if (isChildMode) {\n // CHILD MODE: Keep wrapper as single element, duplicate children inside it\n // Clear the wrapper's children\n while (targetTemplateWrapper.firstChild) {\n targetTemplateWrapper.removeChild(targetTemplateWrapper.firstChild);\n }\n\n // Create synced children and append to the wrapper\n sourceItems.forEach((sourceItem) => {\n const childClone = template.cloneNode(true) as Element;\n syncItemData(sourceItem, childClone, config);\n targetTemplateWrapper.appendChild(childClone);\n });\n\n // Remove data-hs-sync=\"item\" attribute from wrapper\n targetTemplateWrapper.removeAttribute(config.attributes.elements.item.primary.split('=')[0]);\n } else {\n // DEFAULT MODE: Duplicate the item element itself\n // Clear target list, but preserve ignore element\n const children = Array.from(target.children);\n children.forEach((child) => {\n if (child !== ignoreElement) {\n child.remove();\n }\n });\n\n // Create synced items\n const syncedItems = Array.from(sourceItems).map((sourceItem) => {\n const clone = template.cloneNode(true) as Element;\n syncItemData(sourceItem, clone, config);\n return clone;\n });\n\n // Insert synced items based on strategy\n if (ignoreElement) {\n if (insertAfterIgnore) {\n // Insert all items after ignore\n syncedItems.forEach((item) => {\n target.appendChild(item);\n });\n } else {\n // Insert all items before ignore\n syncedItems.forEach((item) => {\n target.insertBefore(item, ignoreElement);\n });\n }\n } else {\n // No ignore element, just append all\n syncedItems.forEach((item) => {\n target.appendChild(item);\n });\n }\n }\n\n syncedCount++;\n });\n\n return {\n result: `sync initialized (${syncedCount} synced, ${skippedCount} skipped)`,\n destroy: () => {},\n };\n}\n","/**\n * Element Duplication\n *\n * Duplicates elements for marquee effects and visual consistency.\n *\n * Child mode (duplicate children):\n * <div data-hs-dupe=\"child\" data-hs-dupe-count=\"2\">\n * <div class=\"marquee_item\">Content</div>\n * </div>\n *\n * Self mode (duplicate wrapper):\n * <div data-hs-dupe=\"self\" data-hs-dupe-count=\"3\">Content</div>\n *\n * Modifiers: data-hs-dupe-hidden (aria-hidden), data-hs-dupe-no-select (disable selection).\n */\nimport { querySelectorAll } from '@utils';\n\nconst MAX_DUPE_COUNT = 20;\nconst TRANSITION_CLASS = 'hs-dupe-no-transition';\n\n// Inject CSS once\nlet cssInjected = false;\nfunction injectCSS(): void {\n if (cssInjected) return;\n\n const style = document.createElement('style');\n style.textContent = `\n .${TRANSITION_CLASS},\n .${TRANSITION_CLASS} * {\n transition: none !important;\n animation: none !important;\n }\n `;\n document.head.appendChild(style);\n cssInjected = true;\n}\n\ninterface DupeConfig {\n attributes: {\n elements: {\n [key: string]: {\n primary: string;\n aliases?: string[];\n };\n };\n };\n}\n\ninterface CleanupRecord {\n element: Element;\n mode: string;\n clones: Element[];\n}\n\ninterface DupeOptions {\n hidden: boolean;\n noSelect: boolean;\n}\n\nexport async function init(config: DupeConfig): Promise<{ result: string; destroy: () => void }> {\n // Inject CSS on first init\n injectCSS();\n\n const cleanup: { processedElements: CleanupRecord[] } = {\n processedElements: [],\n };\n\n // Find all elements with dupe attribute\n const dupeElements = querySelectorAll(config, 'wrapper');\n\n dupeElements.forEach((element) => {\n // Get dupe mode (child or self)\n const mode = element.getAttribute('data-hs-dupe');\n if (!mode || (mode !== 'child' && mode !== 'self')) {\n console.warn('[dupe] Invalid mode. Use \"child\" or \"self\":', element);\n return;\n }\n\n // Get dupe count (default: 1)\n const countAttr = element.getAttribute('data-hs-dupe-count');\n let count = countAttr ? parseInt(countAttr, 10) : 1;\n\n // Validate and cap count\n if (isNaN(count) || count < 1) {\n console.warn('[dupe] Invalid count, using default of 1:', element);\n count = 1;\n }\n\n if (count > MAX_DUPE_COUNT) {\n console.warn(\n `[dupe] Count of ${count} exceeds maximum of ${MAX_DUPE_COUNT}. Capping at ${MAX_DUPE_COUNT}.`\n );\n count = MAX_DUPE_COUNT;\n }\n\n // Get modifier options\n const options = {\n hidden: element.hasAttribute('data-hs-dupe-hidden'),\n noSelect: element.hasAttribute('data-hs-dupe-no-select'),\n };\n\n // Execute duplication based on mode\n if (mode === 'child') {\n dupeChild(element, count, options, cleanup);\n } else if (mode === 'self') {\n dupeSelf(element, count, options, cleanup);\n }\n });\n\n return {\n result: `dupe processed ${cleanup.processedElements.length} elements`,\n destroy: () => {},\n };\n}\n\nfunction dupeChild(\n element: Element,\n count: number,\n options: DupeOptions,\n cleanup: { processedElements: CleanupRecord[] }\n): void {\n // Get first direct child\n const firstChild = element.children[0];\n\n // Silently skip if no child exists (e.g., empty slot waiting for CMS content)\n if (!firstChild) {\n return;\n }\n\n const clones: Element[] = [];\n\n // Create clones and append as siblings\n for (let i = 0; i < count; i++) {\n const clone = firstChild.cloneNode(true) as Element;\n\n // Disable transitions during insertion\n clone.classList.add(TRANSITION_CLASS);\n\n // Apply modifiers to clone\n applyModifiers(clone, options);\n\n // Append to DOM\n element.appendChild(clone);\n clones.push(clone);\n\n // Force reflow\n (clone as HTMLElement).offsetHeight;\n\n // Re-enable transitions on next frame\n requestAnimationFrame(() => {\n clone.classList.remove(TRANSITION_CLASS);\n });\n }\n\n // Track for cleanup\n cleanup.processedElements.push({\n element,\n mode: 'child',\n clones,\n });\n}\n\nfunction dupeSelf(\n element: Element,\n count: number,\n options: DupeOptions,\n cleanup: { processedElements: CleanupRecord[] }\n): void {\n const parent = element.parentNode;\n\n if (!parent) {\n console.warn('[dupe] Element has no parent, cannot duplicate self:', element);\n return;\n }\n\n const clones: Element[] = [];\n\n // Create clones and insert as next siblings\n let referenceNode: Element | Node = element;\n for (let i = 0; i < count; i++) {\n const clone = element.cloneNode(true) as Element;\n\n // Disable transitions during insertion\n clone.classList.add(TRANSITION_CLASS);\n\n // Remove dupe attributes from clone\n clone.removeAttribute('data-hs-dupe');\n clone.removeAttribute('data-hs-dupe-count');\n clone.removeAttribute('data-hs-dupe-hidden');\n clone.removeAttribute('data-hs-dupe-no-select');\n\n // Apply modifiers to clone\n applyModifiers(clone, options);\n\n // Insert after reference node\n if (referenceNode.nextSibling) {\n parent.insertBefore(clone, referenceNode.nextSibling);\n } else {\n parent.appendChild(clone);\n }\n\n clones.push(clone);\n referenceNode = clone; // Next clone goes after this one\n\n // Force reflow\n (clone as HTMLElement).offsetHeight;\n\n // Re-enable transitions on next frame\n requestAnimationFrame(() => {\n clone.classList.remove(TRANSITION_CLASS);\n });\n }\n\n // Track for cleanup\n cleanup.processedElements.push({\n element,\n mode: 'self',\n clones,\n });\n}\n\nfunction applyModifiers(clone: Element, options: DupeOptions): void {\n // Add aria-hidden if requested\n if (options.hidden) {\n clone.setAttribute('aria-hidden', 'true');\n }\n\n // Add user-select: none if requested\n if (options.noSelect) {\n const htmlClone = clone as HTMLElement;\n htmlClone.style.userSelect = 'none';\n (htmlClone.style as unknown as { webkitUserSelect: string }).webkitUserSelect = 'none';\n (htmlClone.style as unknown as { msUserSelect: string }).msUserSelect = 'none';\n }\n}\n","/**\n * Normalize Orchestrator\n *\n * Sequential DOM normalization: Schema → Clickable → Replace → Sync → Dupe.\n * Critical first phase that prepares DOM structure for all other modules.\n */\nimport config from '@config';\nimport { init as schemaInit } from './functions/schema.ts';\nimport { init as syncInit } from './functions/sync.ts';\nimport { init as clickableInit } from './functions/clickable.ts';\nimport { init as dupeInit } from './functions/dupe.ts';\nimport { init as replaceInit } from './functions/replace.ts';\n\nconst CONFIG_ROOT = 'normalize';\n\nexport async function init(): Promise<{ result: string; destroy: () => void }> {\n const cleanup: { destroyFunctions: (() => void)[] } = { destroyFunctions: [] };\n const moduleConfig = config[CONFIG_ROOT];\n\n try {\n // Phase 1a: Schema (generates JSON-LD structured data for SEO)\n const schemaResult = schemaInit();\n if (schemaResult && 'destroy' in schemaResult && typeof schemaResult.destroy === 'function') {\n cleanup.destroyFunctions.push(schemaResult.destroy as () => void);\n }\n\n // Phase 1b: Clickable (normalizes button/link structure)\n const clickableResult = await clickableInit();\n if (\n clickableResult &&\n 'destroy' in clickableResult &&\n typeof clickableResult.destroy === 'function'\n ) {\n cleanup.destroyFunctions.push(clickableResult.destroy as () => void);\n }\n\n // Phase 1c: Replace (replaces {{placeholder}} tokens including year/month)\n const replaceResult = replaceInit(config.normalize?.replace);\n if (\n replaceResult &&\n 'destroy' in replaceResult &&\n typeof replaceResult.destroy === 'function'\n ) {\n cleanup.destroyFunctions.push(replaceResult.destroy as () => void);\n }\n\n // Phase 1d: Sync (populates lists with collection data)\n const syncResult = await syncInit(\n moduleConfig.sync as unknown as {\n attributes: {\n elements: { item: { primary: string } };\n properties: { syncId: string; syncField: string; syncMode: string; syncValue: string };\n };\n }\n );\n if (syncResult && 'destroy' in syncResult && typeof syncResult.destroy === 'function') {\n cleanup.destroyFunctions.push(syncResult.destroy as () => void);\n }\n\n // Phase 1e: Dupe (duplicates normalized elements)\n const dupeResult = await dupeInit(\n moduleConfig.dupe as unknown as {\n attributes: { elements: { [key: string]: { primary: string; aliases?: string[] } } };\n }\n );\n if (dupeResult && 'destroy' in dupeResult && typeof dupeResult.destroy === 'function') {\n cleanup.destroyFunctions.push(dupeResult.destroy as () => void);\n }\n\n return {\n result: 'normalize initialized',\n destroy: () => {\n // Call all destroy functions in reverse order\n cleanup.destroyFunctions.reverse().forEach((destroyFn) => {\n try {\n destroyFn();\n } catch (error) {\n console.error('[normalize] Error during cleanup:', error);\n }\n });\n cleanup.destroyFunctions.length = 0;\n },\n };\n } catch (error) {\n console.error('[normalize] Initialization failed:', error);\n // Cleanup any partial initialization\n cleanup.destroyFunctions.reverse().forEach((fn) => {\n try {\n fn();\n } catch (cleanupError) {\n console.error('[normalize] Error during error cleanup:', cleanupError);\n }\n });\n throw error;\n }\n}\n","/**\n * Clickable Normalization\n *\n * Normalizes clickable wrappers by keeping either button or link based on href validity.\n * Removes the unused element and ensures the kept element has data-hs-clickable=\"button\".\n *\n * Structure:\n * <div data-hs-clickable=\"wrapper\">\n * <button data-hs-clickable=\"button\">Click</button>\n * <a data-hs-clickable=\"button\" href=\"/page\">Click</a>\n * </div>\n *\n * Logic: Keep link if href is valid, otherwise keep button.\n */\nimport { globalConfig, querySelectorAll } from '@utils';\n\ninterface ProcessedWrapper {\n wrapper: Element;\n keptElement: Element;\n wasLink: boolean;\n addedAttribute: boolean;\n}\n\nexport async function init(): Promise<{ result: string; destroy: () => void }> {\n const cleanup: { processedWrappers: ProcessedWrapper[] } = {\n processedWrappers: [],\n };\n\n // Find all clickable wrappers using global clickable config\n const wrappers = querySelectorAll(\n globalConfig.clickable as {\n attributes: { elements: { [key: string]: { primary: string; aliases?: string[] } } };\n },\n 'wrapper'\n );\n\n wrappers.forEach((wrapper) => {\n // Find direct button and link children\n const button = wrapper.querySelector(':scope > button');\n const link = wrapper.querySelector(':scope > a');\n\n // Only process if both exist\n if (!button || !link) return;\n\n // Check if link has valid href\n const href = link.getAttribute('href');\n const hasValidHref = href && href !== '' && href !== '#';\n\n let keptElement: Element;\n let removedElement: Element;\n\n if (hasValidHref) {\n // Keep link, remove button\n keptElement = link;\n removedElement = button;\n } else {\n // Keep button, remove link\n keptElement = button;\n removedElement = link;\n }\n\n // Check if kept element already has data-hs-clickable=\"button\"\n const hadAttribute = keptElement.getAttribute('data-hs-clickable') === 'button';\n\n // Ensure kept element has data-hs-clickable=\"button\"\n if (!hadAttribute) {\n keptElement.setAttribute('data-hs-clickable', 'button');\n }\n\n // Remove the other element\n removedElement.remove();\n\n // Track for cleanup\n cleanup.processedWrappers.push({\n wrapper,\n keptElement,\n wasLink: keptElement === link,\n addedAttribute: !hadAttribute,\n });\n });\n\n return {\n result: `clickable processed ${cleanup.processedWrappers.length} wrappers`,\n destroy: () => {},\n };\n}\n","/**\n * Replace\n *\n * Collects values from elements and replaces {{placeholder}} text throughout the page.\n * Supports text content, href attributes, and automatic year/month injection.\n *\n * Features:\n * - Collects values from any element with data-hs-replace attribute\n * - Replaces {{name}} placeholders in text content and link hrefs\n * - Complete href replacement for links (prevents Webflow \"https://\" prefix issues)\n * - Supports multiple elements with same name (creates ordered array, joined with \", \")\n * - Conditional hide feature to exclude elements based on class names\n * - Automatic {{year}} and {{month}} replacement\n * - One-time replacement on page load (no ongoing observers)\n *\n * Structure:\n * <a data-hs-replace=\"email\" href=\"mailto:info@example.com\">info@example.com</a>\n * <span data-hs-replace=\"phone\">(555) 123-4567</span>\n * <div data-hs-replace=\"service\">Service 1</div>\n * <div data-hs-replace=\"service\">Service 2</div>\n *\n * Usage:\n * Text: \"Contact us at {{email}}\" → \"Contact us at info@example.com\"\n * Link: <a href=\"{{email}}\">Email</a> → <a href=\"mailto:info@example.com\">Email</a>\n * Array: \"Services: {{service}}\" → \"Services: Service 1, Service 2\"\n * Dynamic: \"Copyright {{year}}\" → \"Copyright 2024\"\n */\n\nimport { getSelector } from '@utils';\n\nexport function init(config: any) {\n function setupReplacements() {\n // Map to store replacements (supports arrays for duplicate names)\n const replacements = new Map<\n string,\n | { text: string | null; href: string | null }\n | Array<{ text: string | null; href: string | null }>\n >();\n\n // 1. Collect from data-hs-replace elements\n const elementSelector = getSelector(config, 'element');\n const elements = document.querySelectorAll(elementSelector);\n\n elements.forEach((element) => {\n // Clone the element to avoid modifying the original DOM\n const clonedElement = element.cloneNode(true) as Element;\n\n // Find and remove elements with data-hs-replace-hide that match their classes\n const hideSelector = getSelector(config, 'hide');\n const hideElements = clonedElement.querySelectorAll(hideSelector);\n\n hideElements.forEach((hideElement) => {\n const hideAttr = config.attributes.elements.hide.primary.match(/data-hs-replace-hide/)[0];\n const hideValue = hideElement.getAttribute(hideAttr);\n\n if (hideValue) {\n // Split by spaces to get all class names to check\n const classesToHide = hideValue.split(' ').filter((c: string) => c.trim());\n\n // Check if element has any of the classes\n const hasMatchingClass = classesToHide.some((className: string) =>\n hideElement.classList.contains(className)\n );\n\n // Remove element if it has a matching class\n if (hasMatchingClass) {\n hideElement.remove();\n }\n }\n });\n\n const elementAttr = config.attributes.elements.element.primary.match(/data-hs-replace/)[0];\n const name = element.getAttribute(elementAttr);\n const text = clonedElement.textContent?.trim() || null;\n const href = element.getAttribute('href');\n\n // Store if name exists and either text or href exists\n if (name && (text || href)) {\n const value = { text, href };\n\n // If name already exists, convert to array or append to array\n if (replacements.has(name)) {\n const existing = replacements.get(name)!;\n if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n replacements.set(name, [existing, value]);\n }\n } else {\n replacements.set(name, value);\n }\n }\n });\n\n // 2. Add dynamic year/month replacements\n const currentYear = new Date().getFullYear().toString();\n const monthNames = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n ];\n const currentMonth = monthNames[new Date().getMonth()];\n\n replacements.set('year', { text: currentYear, href: null });\n replacements.set('month', { text: currentMonth, href: null });\n\n // If no replacements found, exit early\n if (replacements.size === 0) {\n return;\n }\n\n // 3. Replace text content efficiently using TreeWalker\n const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {\n acceptNode: (node) => {\n // Check if any replacement names exist in the text content\n const text = node.textContent || '';\n for (const name of replacements.keys()) {\n if (text.includes(`{{${name}}}`)) {\n return NodeFilter.FILTER_ACCEPT;\n }\n }\n return NodeFilter.FILTER_SKIP;\n },\n });\n\n const textNodes: Node[] = [];\n let node: Node | null;\n while ((node = walker.nextNode())) {\n textNodes.push(node);\n }\n\n // Replace text in collected nodes\n textNodes.forEach((textNode) => {\n let newText = textNode.textContent || '';\n let hasChanges = false;\n\n for (const [name, value] of replacements.entries()) {\n const placeholder = `{{${name}}}`;\n if (newText.includes(placeholder)) {\n let replacementValue: string | null = null;\n\n // Handle array values (join with \", \")\n if (Array.isArray(value)) {\n replacementValue = value\n .map((v) => v.text || v.href)\n .filter(Boolean)\n .join(', ');\n } else {\n // Use text value if available, otherwise use href value\n replacementValue = value.text || value.href;\n }\n\n if (replacementValue) {\n newText = newText.replace(new RegExp(placeholder, 'g'), replacementValue);\n hasChanges = true;\n }\n }\n }\n\n if (hasChanges) {\n textNode.textContent = newText;\n }\n });\n\n // 4. Replace link hrefs\n const links = document.querySelectorAll('a[href]');\n links.forEach((link) => {\n let href = link.getAttribute('href') || '';\n let hasChanges = false;\n\n for (const [name, value] of replacements.entries()) {\n const placeholder = `{{${name}}}`;\n if (href.includes(placeholder)) {\n let replacementValue: string | null = null;\n\n // Handle array values - use first item's href or text\n if (Array.isArray(value)) {\n const firstValue = value[0];\n replacementValue = firstValue.href || firstValue.text;\n } else {\n // If value has a dedicated href, completely replace the entire href\n // (prevents Webflow's default \"https://\" from breaking tel:, mailto:, etc.)\n if (value.href) {\n replacementValue = value.href;\n href = value.href; // Complete replacement, not substitution\n hasChanges = true;\n break; // Only one replacement per link\n }\n // Otherwise, do normal placeholder replacement with text value\n else if (value.text) {\n replacementValue = value.text;\n }\n }\n\n if (replacementValue && !hasChanges) {\n href = href.replace(new RegExp(placeholder, 'g'), replacementValue);\n hasChanges = true;\n }\n }\n }\n\n if (hasChanges) {\n link.setAttribute('href', href);\n }\n });\n\n // 5. Optional debug output (if data-hs-replace-test element exists)\n const testElement = document.querySelector('[data-hs-replace-test]');\n if (testElement) {\n const debugData: Record<\n string,\n {\n placeholder: string;\n value: string;\n text?: string | null;\n href?: string | null;\n type?: string;\n items?: string[];\n }\n > = {};\n\n for (const [name, value] of replacements.entries()) {\n const placeholder = `{{${name}}}`;\n\n if (Array.isArray(value)) {\n const displayValue = value\n .map((v) => v.text || v.href)\n .filter(Boolean)\n .join(', ');\n\n debugData[name] = {\n placeholder,\n value: displayValue,\n type: `Array (${value.length} items)`,\n items: value.map((v) => v.text || v.href || '(empty)'),\n };\n } else {\n const displayValue = value.text || value.href || '(empty)';\n debugData[name] = {\n placeholder,\n value: displayValue,\n ...(value.text && { text: value.text }),\n ...(value.href && { href: value.href }),\n };\n }\n }\n\n const pre = document.createElement('pre');\n pre.textContent = JSON.stringify(debugData, null, 2);\n\n testElement.innerHTML = '';\n testElement.appendChild(\n document.createTextNode(`Replace Module Debug (${replacements.size} replacements):`)\n );\n testElement.appendChild(pre);\n }\n }\n\n setupReplacements();\n\n return {\n result: 'replace initialized',\n destroy: () => {},\n };\n}\n"],"names":["init","baseURL","window","location","origin","getReplaceText","name","element","document","querySelector","textContent","trim","businessName","phone","email","city","state","zipCode","schema","url","telephone","logoElement","src","image","address","addressLocality","addressRegion","postalCode","addressCountry","streetAddress","latitude","longitude","geo","socialLinks","map","href","getReplaceHref","filter","link","includes","length","sameAs","servicesWrapper","serviceItems","querySelectorAll","services","Array","from","el","service","serviceType","areasWrapper","areaItems","areas","area","areaServed","businessScript","createElement","type","text","JSON","stringify","head","appendChild","breadcrumbText","pathParts","pathname","split","part","breadcrumbList","itemListElement","position","item","firstSlug","middleBreadcrumb","word","charAt","toUpperCase","slice","join","push","breadcrumbScript","faqArray","forEach","faqList","faqAccordion","toggleElement","contentElement","questionText","answerHTML","innerHTML","acceptedAnswer","faqSchema","mainEntity","faqScript","nameElement","descElement","serviceName","serviceDescription","serviceSchema","description","serviceScript","console","warn","questionElement","answerElement","publishedElement","modifiedElement","publishedDate","modifiedDate","qaPageSchema","answerCount","datePublished","dateModified","qaPageScript","missing","blogNameElement","blogPublishedElement","blogModifiedElement","summaryElement","authorNameElement","authorTypeElement","blogName","summaryText","authorName","authorType","authorRoleElement","authorImageElement","authorRole","authorImage","companyName","logoURL","authorSchema","jobTitle","logo","blogSchema","headline","author","publisher","mainEntityOfPage","blogScript","setupSchema","result","destroy","getTextNodes","textNodes","walker","createTreeWalker","NodeFilter","SHOW_TEXT","node","nextNode","nodeValue","getHrefElements","hrefs","hasAttribute","findElementWithSrc","getAttribute","descendant","syncItemData","sourceItem","targetClone","config","fieldAttr","attributes","properties","syncField","usesFieldBasedSync","sourceField","fieldName","targetField","sourceImgElement","targetImgElement","setAttribute","sourceTextNodes","sourceText","targetTextNodes","syncFieldBasedData","sourceHrefs","sourceHref","TRANSITION_CLASS","cssInjected","async","style","injectCSS","cleanup","processedElements","mode","countAttr","count","parseInt","isNaN","options","hidden","noSelect","firstChild","children","clones","i","clone","cloneNode","classList","add","applyModifiers","offsetHeight","requestAnimationFrame","remove","dupeChild","parent","parentNode","referenceNode","removeAttribute","nextSibling","insertBefore","dupeSelf","htmlClone","userSelect","webkitUserSelect","msUserSelect","destroyFunctions","moduleConfig","schemaResult","schemaInit","clickableResult","processedWrappers","globalConfig","clickable","wrapper","button","keptElement","removedElement","hadAttribute","wasLink","addedAttribute","clickableInit","replaceResult","normalize","replace","replacements","Map","elementSelector","getSelector","clonedElement","hideSelector","hideElement","hideAttr","elements","hide","primary","match","hideValue","c","some","className","contains","elementAttr","value","has","existing","get","isArray","set","currentYear","Date","getFullYear","toString","currentMonth","getMonth","size","body","acceptNode","keys","FILTER_ACCEPT","FILTER_SKIP","textNode","newText","hasChanges","entries","placeholder","replacementValue","v","Boolean","RegExp","firstValue","testElement","debugData","displayValue","items","pre","createTextNode","setupReplacements","syncResult","sourceLists","sourceMapById","sourceMapByField","source","syncId","targetLists","syncedCount","skippedCount","target","syncValue","targetTemplateWrapper","identifier","isChildMode","syncMode","ignoreElement","sourceItems","template","allSourceItems","matchedItem","find","firstElementChild","insertAfterIgnore","currentElement","nextElementSibling","removeChild","childClone","child","syncedItems","syncInit","sync","dupeResult","dupeInit","dupe","reverse","destroyFn","error","fn","cleanupError"],"mappings":"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"}
|