/*
Provides a standard orchestration logic for layout-global scripts across all Warpgogol projects.
- Do not handle project-specific feature logic directly.
- RFC-1175: initBackgroundVideos for video[data-bg-video] — capability-gated background autoplay (always-on step; the marker replaces the native autoplay attribute in media.astro).
- RFC-1175: close Lighthouse gaps — directive-allowlist robots (Schemamap retired, ROBOTS-SURFACE-01), capability-gated video (preload=none, data-bg-video, saveData/reduced-motion), single fetchpriority=high per page (IMG-DELIVERY-05), inlined shared texture token, 24px QR touch targets; RFC-1177 security-headers draft
- RFC-1175 AC-5: single-chunk orchestrator — all self-gating feature modules statically imported (scheduler, gsap-*, inline-number, live-photos, video-player, background-video, external-link-qr), so the entry script's one dynamic import resolves the whole dispatch graph; heavy deps stay lazy inside each init.
- warpgogol-m000170: initLenis moved behind scheduleTask — it was the only awaited feature init, pulling the lenis chunk into the critical request chain Lighthouse reported.
- RFC-1198: cookie-free referral attribution — /r/ partner landings + voluntary referrer field
Replace the RFC-1191 wg_ref cookie capture with URL+form attribution:
static /r/ and //r/ partner landings (noindex, zero JS,
consent-gated via publicNameConsentRef), ?ref prefill on the existing
referrerField rail, server-side slug resolution against the bundled
partners.yaml, referral_partner_slug/name on the Lagebild ingress contract,
legacy /?ref -> /r/ 302 in the generated worker, and full removal of
the cookie capture module (wg_ref, referral.attribution entitlement,
payload.attribution).
RFC-1097
*/
// @ai-invariant: This is a high-risk module. Preserve its core logic and minimize external side effects during modification.
// RFC-1178: the werkstatt Trusted Types policy is a static side-effect
// import — it must evaluate before any feature module so the policy exists
// before the first sink write; never move it behind a dynamic import.
import "./trusted-types.ts";
// RFC-1175: self-gating modules are statically imported so the orchestrator
// is emitted as a single chunk — the only dynamic import after the entry
// script. Heavy third-party deps (gsap, plyr, hls.js, @lordicon/element,
// qrcode, lenis) stay lazy: each init loads them only when its trigger exists.
import { applyExternalLinkBehavior } from "./external-links.ts";
import { initLordIconOnDemand } from "./lordicon.ts";
import { initLenis } from "./lenis.ts";
import { initScrollSpy } from "./scroll-spy.ts";
import { initBackgroundVideos } from "./background-video.ts";
import { initExternalLinkQr } from "./external-link-qr.ts";
import { initGsapCounter } from "./gsap-counter.ts";
import { initGsapParallax } from "./gsap-parallax.ts";
import { initGsapReveal } from "./gsap-reveal.ts";
import { initGsapStagger } from "./gsap-stagger.ts";
import { initInlineNumberAnimation } from "./inline-number-animation.ts";
import { initLivePhotos } from "./live-photos.ts";
import { initVideoPlayers } from "./video-player.ts";
import { initCookieConsent } from "./cookie-consent.ts";
import { scheduleTask } from "./scheduler.ts";
export interface OrchestrationOptions {
headerOffset?: number;
/** RFC-0040: opt in to GSAP stat counter animation. Default false. */
counters?: boolean;
/** RFC-0041: opt in to GSAP inline number animation for prose sections. Default false. */
inlineNumbers?: boolean;
/** RFC-0106: opt in to GSAP scroll-triggered reveal animations. Default false. */
reveal?: boolean;
/** RFC-0106: opt in to GSAP parallax for [data-parallax-speed] elements. Default false. */
parallax?: boolean;
/** RFC-0106: opt in to GSAP stagger for [data-motion-stagger] parents. Default false. */
stagger?: boolean;
/** RFC-0202: opt in to the living-photos runtime for [data-live-photo] elements. Default false. */
livePhotos?: boolean;
/** RFC-0210: opt in to the lazy feature-video player (Plyr + hls.js) for [data-video-player]. Default false. */
videoPlayers?: boolean;
/** RFC-0205: opt in to Lenis smooth scroll. Default false — avoids loading the 17 KB bundle when not needed. */
smoothScroll?: boolean;
/** RFC-0932: entitlement state for external-link-qr module. Passed from layout. */
externalLinkQrEntitled?: boolean;
/** RFC-1192: entitled COOKIE_MODULES ids for this site — non-empty activates the consent surface. */
cookieModules?: string[];
}
const has = (selector: string) => document.querySelector(selector) instanceof Element;
/**
* Runs the standard set of browser initializations required for all Warpgogol platform sites.
*/
export async function runStandardLayoutOrchestration(
options: OrchestrationOptions = {},
): Promise {
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// 1. External links security (mandatory)
if (has("a[href]")) {
if (document.readyState === "loading") {
document.addEventListener(
"DOMContentLoaded",
() => applyExternalLinkBehavior({ externalLinkQrEntitled: options.externalLinkQrEntitled }),
{
once: true,
},
);
} else {
applyExternalLinkBehavior({ externalLinkQrEntitled: options.externalLinkQrEntitled });
}
}
// 2. LordIcon hydration (deferred to load event to avoid blocking LCP)
if (has("lord-icon")) {
if (document.readyState === "complete") {
initLordIconOnDemand();
} else {
window.addEventListener("load", () => initLordIconOnDemand(), { once: true });
}
}
// 3. Lenis smooth scroll (opt-in via smoothScroll: true)
// m000170: scheduled at idle like the other feature inits — awaiting it here
// put the lenis chunk fetch inside the critical request chain.
if (options.smoothScroll) {
scheduleTask(async () => {
await initLenis({
prefersReducedMotion,
headerOffset: options.headerOffset ?? 80,
});
});
}
// 4. GSAP stat counter (RFC-0040 — opt-in via counters: true)
// Note: has() check removed; initGsapCounter internally guards against empty sections.
if (options.counters) {
scheduleTask(async () => {
await initGsapCounter({ prefersReducedMotion });
});
}
// 5. GSAP inline number animation (RFC-0041 — opt-in via inlineNumbers: true)
if (options.inlineNumbers && has(".js-inline-number")) {
scheduleTask(async () => {
await initInlineNumberAnimation({ prefersReducedMotion });
});
}
// 6. RFC-0106: reveal animations (opt-in via reveal: true).
if (options.reveal && has("[data-motion-reveal]")) {
scheduleTask(async () => {
await initGsapReveal({ prefersReducedMotion });
});
}
// 7. RFC-0106: parallax animations (opt-in via parallax: true).
if (options.parallax && has("[data-parallax-speed]")) {
scheduleTask(async () => {
await initGsapParallax({ prefersReducedMotion });
});
}
// 8. RFC-0106: stagger animations (opt-in via stagger: true).
if (options.stagger && has("[data-motion-stagger]")) {
scheduleTask(async () => {
await initGsapStagger({ prefersReducedMotion });
});
}
// 9. RFC-0202: living photos runtime (opt-in via livePhotos: true).
// Only the in-viewport/tap triggers carry [data-trigger]; the autoplay path is native.
if (options.livePhotos && has("[data-live-photo][data-trigger]")) {
scheduleTask(async () => {
await initLivePhotos({ prefersReducedMotion });
});
}
// 10. RFC-0210: feature-video player (opt-in via videoPlayers: true).
// Lazy Plyr + hls.js, gated on [data-video-player]; nothing loads without a feature video.
if (options.videoPlayers && has("video[data-video-player]")) {
scheduleTask(async () => {
await initVideoPlayers({ prefersReducedMotion });
});
}
// 10b. RFC-1175: background