{"version":3,"file":"client.mjs","sources":["../app/constants.ts","../app/utils.ts","../app/teaser_internet_blitzdeals.client.ts"],"sourcesContent":["const PSS_URL_PROD = 'https://internet.check24.de/';\nconst PSS_URL_TEST = 'https://internet.check24-test.de/';\n\nfunction hostname(): string {\n    return globalThis.location?.hostname ?? '';\n}\n\n/**\n * Product start page origin. The widget is hosted on www.check24.de, so the\n * teaser link must go to the PSS — never the holding host or the local preview origin.\n */\nexport function getPssUrl(): string {\n    // check24-test.de must be checked before returning PROD (test hostnames also end with check24.de).\n    if (hostname().endsWith('check24-test.de')) {\n        return PSS_URL_TEST;\n    }\n    return PSS_URL_PROD;\n}\n\nfunction getApiUrl(path: string): string {\n    const host = hostname();\n    if (host === 'localhost' || host === '127.0.0.1') {\n        return `${globalThis.location?.origin ?? ''}/api/${path}`;\n    }\n    return `${getPssUrl()}api/${path}`;\n}\n\n/** Public endpoint, used for visitors who are not logged in. */\nexport function getMaxCashbackApiUrl(): string {\n    return getApiUrl('max-cashback');\n}\n\n/** Requires a login session (401 otherwise), used for logged-in users. */\nexport function getPromotionApiUrl(): string {\n    return getApiUrl('promotion');\n}\n\nexport const CASHBACK_FALLBACK = 415;\n\nexport const TID3_VALUE = 'home_teaser';\nexport const SOURCE_VALUE = 'home';\n\nexport const labels = {\n    linkLabel: 'Zu den Internet Blitz Deals',\n    headline: 'Internet',\n    cashbackLabelTop: 'Bis zu',\n    /** Default and fallback label, also used for visitors who are not logged in. */\n    cashbackLabelBottom: 'Cashback',\n    /** Only shown with the amount from the promotion API (logged-in users). */\n    discountLabelBottom: 'Rabatt',\n} as const;\n\nexport type TeaserTagline = readonly [prefix: string, highlight: string];\n\nexport const TAGLINE_DEFAULT: TeaserTagline = ['Nur bis', '27.10.'];\n\n/** Campaign runs until 27.10.2026 23:59 (Europe/Berlin, CET), i.e. ends at 28.10.2026 00:00. */\nexport const CAMPAIGN_END = new Date('2026-10-28T00:00:00+01:00');\n\n/** From this much remaining time the tagline counts down live in hours/minutes. */\nexport const LIVE_COUNTDOWN_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Day-based countdown steps (Europe/Berlin). Each text applies from its `from` timestamp\n * until the next step starts. The last 24h before `CAMPAIGN_END` are handled live in utils.\n * DST ends on 25.10.2026 03:00, so later steps use +01:00.\n */\nexport const TAGLINE_STEPS: ReadonlyArray<{ from: Date; text: TeaserTagline }> = [\n    { from: new Date('2026-10-23T16:00:00+02:00'), text: ['Nur noch', '4 Tage'] },\n    { from: new Date('2026-10-24T16:00:00+02:00'), text: ['Nur noch', '3 Tage'] },\n    { from: new Date('2026-10-25T16:00:00+01:00'), text: ['Nur noch', '2 Tage'] },\n    { from: new Date('2026-10-26T16:00:00+01:00'), text: ['Nur noch', 'bis morgen'] },\n];\n","import {\n    CAMPAIGN_END,\n    CASHBACK_FALLBACK,\n    getMaxCashbackApiUrl,\n    getPromotionApiUrl,\n    getPssUrl,\n    labels,\n    LIVE_COUNTDOWN_MS,\n    SOURCE_VALUE,\n    TAGLINE_DEFAULT,\n    TAGLINE_STEPS,\n    type TeaserTagline,\n    TID3_VALUE,\n} from './constants';\n\nconst DESKTOP_MIN_VIEWPORT_PX = 1024;\nconst HERO_SLOT_MIN_WIDTH_PX = 400;\nconst HERO_SLOT_MAX_WIDTH_PX = 559;\n\n/**\n * Hero layout only when viewport >= 1024 px AND the available slot is 400–559 px.\n * Below 1024 px, layout is always Tile regardless of slot width.\n */\nexport function isHeroSlot(slotWidth: number, viewportWidth: number = globalThis.innerWidth ?? 0): boolean {\n    if (viewportWidth < DESKTOP_MIN_VIEWPORT_PX) return false;\n    return slotWidth >= HERO_SLOT_MIN_WIDTH_PX && slotWidth <= HERO_SLOT_MAX_WIDTH_PX;\n}\n\nfunction parseReferenceTimestamp(raw: string | null | undefined): Date | null {\n    if (!raw) return null;\n\n    const parsed = new Date(raw);\n    return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\n/** Host `reference-timestamp` as a Date (falls back to the client clock). */\nexport function resolveReferenceTime(referenceTimestamp?: string | null): Date {\n    return parseReferenceTimestamp(referenceTimestamp) ?? new Date();\n}\n\nconst MINUTE_MS = 60_000;\nconst HOUR_MS = 60 * MINUTE_MS;\n/** setTimeout overflows above ~24.8 days; cap well below. */\nconst MAX_TICK_MS = 6 * HOUR_MS;\n\nfunction plural(count: number, singular: string, pluralForm: string): string {\n    return `${count} ${count === 1 ? singular : pluralForm}`;\n}\n\n/**\n * Countdown text at `now`. Day steps until 24h before the campaign end, then live\n * hours (>= 1h remaining, rounded down) and minutes (< 1h remaining, rounded up, max 59).\n * Returns `null` once the campaign is over.\n */\nexport function getTeaserTagline(now: Date): TeaserTagline | null {\n    const remaining = CAMPAIGN_END.getTime() - now.getTime();\n    if (remaining <= 0) return null;\n\n    if (remaining <= LIVE_COUNTDOWN_MS) {\n        if (remaining < HOUR_MS) {\n            const minutes = Math.min(59, Math.ceil(remaining / MINUTE_MS));\n            return ['Nur noch', plural(minutes, 'Minute', 'Minuten')];\n        }\n        return ['Nur noch', plural(Math.floor(remaining / HOUR_MS), 'Stunde', 'Stunden')];\n    }\n\n    let tagline: TeaserTagline = TAGLINE_DEFAULT;\n    for (const { from, text } of TAGLINE_STEPS) {\n        if (now < from) break;\n        tagline = text;\n    }\n    return tagline;\n}\n\n/**\n * Milliseconds until the tagline text can change next, or `null` when the campaign is over.\n */\nexport function getMsUntilNextTaglineChange(now: Date): number | null {\n    const remaining = CAMPAIGN_END.getTime() - now.getTime();\n    if (remaining <= 0) return null;\n\n    let delay: number;\n    if (remaining <= LIVE_COUNTDOWN_MS) {\n        if (remaining < HOUR_MS) {\n            // minutes (rounded up): text changes once remaining drops to the next full minute\n            delay = remaining - (Math.ceil(remaining / MINUTE_MS) - 1) * MINUTE_MS;\n        } else {\n            // hours (rounded down): text changes 1 ms after remaining drops below the current full hour\n            delay = remaining - Math.floor(remaining / HOUR_MS) * HOUR_MS + 1;\n        }\n    } else {\n        const nextStart = Math.min(\n            ...[...TAGLINE_STEPS.map(({ from }) => from.getTime()), CAMPAIGN_END.getTime() - LIVE_COUNTDOWN_MS].filter(\n                (time) => time > now.getTime(),\n            ),\n        );\n        delay = nextStart - now.getTime();\n    }\n\n    return Math.min(Math.max(delay, 1), MAX_TICK_MS);\n}\n\nexport function formatTeaserTaglineHtml([prefix, highlight]: TeaserTagline): string {\n    return `${prefix} <span class=\"mw-tagline-highlight\">${highlight}</span>`;\n}\n\nexport function formatCashback(value: number): string {\n    return `${value}€`;\n}\n\nexport function buildTeaserUrl(): string {\n    const url = new URL(getPssUrl());\n\n    new URLSearchParams(globalThis.location?.search ?? '').forEach((value, key) => {\n        if (key !== 'tid3' && key !== 'customerType') {\n            url.searchParams.set(key, value);\n        }\n    });\n\n    url.searchParams.set('tid3', TID3_VALUE);\n    url.searchParams.set('source', SOURCE_VALUE);\n\n    return url.toString();\n}\n\ntype MaxCashbackResponse = {\n    readonly value?: unknown;\n};\n\ntype PromotionResponse = {\n    readonly total?: unknown;\n};\n\nexport type CashbackBadge = {\n    readonly amount: number;\n    readonly label: string;\n};\n\n/**\n * Fail fast when the API is slow or unreachable (e.g. Factory layout check on localhost).\n * The badge is hidden until the call settles, so the wait stays short.\n */\nconst FETCH_TIMEOUT_MS = 300;\n\n/** Parsed JSON body, or `null` on network errors, timeouts, non-2xx responses and invalid JSON. */\nasync function fetchJson(url: string, init: RequestInit = {}): Promise<unknown> {\n    try {\n        const response = await fetch(url, { ...init, signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n        return response.ok ? await response.json() : null;\n    } catch {\n        return null;\n    }\n}\n\n/** Public max cashback in whole euros (visitors who are not logged in). */\nexport async function fetchMaxCashback(): Promise<number> {\n    const data = (await fetchJson(getMaxCashbackApiUrl())) as MaxCashbackResponse | null;\n    return typeof data?.value === 'number' ? data.value : CASHBACK_FALLBACK;\n}\n\n/**\n * Max discount for logged-in users in whole euros, or `null` when unavailable.\n * `total` is delivered in whole euros.\n */\nexport async function fetchPromotionDiscount(): Promise<number | null> {\n    const data = (await fetchJson(getPromotionApiUrl(), { credentials: 'include' })) as PromotionResponse | null;\n    const total = data?.total;\n    if (typeof total !== 'number' || !Number.isFinite(total)) return null;\n\n    return total > 0 ? total : null;\n}\n\n/**\n * Badge content per login state. Logged-in users get the promotion discount (\"Rabatt\");\n * everything else, including a failed promotion call, shows \"Cashback\" (fallback amount on errors).\n */\nexport async function fetchCashbackBadge(isLoggedIn: boolean): Promise<CashbackBadge> {\n    if (isLoggedIn) {\n        const discount = await fetchPromotionDiscount();\n        if (discount !== null) return { amount: discount, label: labels.discountLabelBottom };\n        return { amount: CASHBACK_FALLBACK, label: labels.cashbackLabelBottom };\n    }\n\n    return { amount: await fetchMaxCashback(), label: labels.cashbackLabelBottom };\n}\n","/// <reference types=\"../.check24-home/types/client\" />\n\nimport type { Language } from '../.check24-home/types/check24';\nimport type { HomeClientComponentElement, HomeClientComponentProps } from '../.check24-home/types/client';\nimport { CASHBACK_FALLBACK, labels } from './constants';\nimport {\n    buildTeaserUrl,\n    fetchCashbackBadge,\n    formatCashback,\n    formatTeaserTaglineHtml,\n    getMsUntilNextTaglineChange,\n    getTeaserTagline,\n    isHeroSlot,\n    resolveReferenceTime,\n} from './utils';\nimport clientStyles from './assets/tile_blitzdeals_client.css?raw';\nimport blitzDealsSvg from './assets/blitzdeals-headline.svg?raw';\n\nexport default class InternetBlitzdealsTileClient extends HTMLElement implements HomeClientComponentElement {\n    deviceOutput: 'desktop' | 'mobile' | 'tabletapp' | 'app' = 'desktop';\n    language: Language = 'de-DE';\n    platform: 'app' | 'web' = 'web';\n    referenceTimestamp: string | null = null;\n    /** Logged in when the host passes an `account_id` (or a `user`) attribute. */\n    isLoggedIn = false;\n\n    private shadow: ShadowRoot | null = null;\n    private cashbackRequestId = 0;\n    private resizeObserver: ResizeObserver | null = null;\n    private breakpointMedia: MediaQueryList | null = null;\n    private layoutListenersBound = false;\n    private clockOffsetMs = 0;\n    private clockAnchoredTo: string | null | undefined;\n    private taglineTimer: ReturnType<typeof setTimeout> | null = null;\n\n    static get observedAttributes(): string[] {\n        return ['data-teaser-active', 'deviceoutput', 'platform', 'reference-timestamp', 'account_id', 'user'];\n    }\n\n    attributeChangedCallback(name: string, oldValue: string, newValue: string): void {\n        if (oldValue === newValue) return;\n        const wasLoggedIn = this.isLoggedIn;\n        this.applyAttributes();\n        this.updatePromotionAttribute();\n        if (name === 'reference-timestamp') {\n            this.updateTagline();\n            this.scheduleTaglineTick();\n        }\n        // Before the first render connectedCallback loads the badge itself.\n        if (this.shadow && this.isLoggedIn !== wasLoggedIn) {\n            void this.loadCashbackAmount();\n        }\n    }\n\n    connectedCallback(): void {\n        if (!this.shadow) {\n            this.shadow = this.attachShadow({ mode: 'closed' });\n        }\n\n        this.applyAttributes();\n        this.updatePromotionAttribute();\n        this.render();\n        this.bindLayoutListeners();\n        this.updateTagline();\n        this.scheduleTaglineTick();\n        void this.loadCashbackAmount();\n    }\n\n    disconnectedCallback(): void {\n        this.clearTaglineTimer();\n        window.removeEventListener('resize', this.boundUpdatePromotionAttribute);\n        this.breakpointMedia?.removeEventListener('change', this.boundUpdatePromotionAttribute);\n        this.breakpointMedia = null;\n        this.resizeObserver?.disconnect();\n        this.resizeObserver = null;\n        this.layoutListenersBound = false;\n    }\n\n    private boundUpdatePromotionAttribute = (): void => {\n        this.updatePromotionAttribute();\n    };\n\n    private bindLayoutListeners(): void {\n        if (this.layoutListenersBound) return;\n        this.layoutListenersBound = true;\n\n        window.addEventListener('resize', this.boundUpdatePromotionAttribute);\n        this.breakpointMedia = window.matchMedia('(min-width: 1024px)');\n        this.breakpointMedia.addEventListener('change', this.boundUpdatePromotionAttribute);\n        if (typeof ResizeObserver !== 'undefined') {\n            this.resizeObserver = new ResizeObserver(this.boundUpdatePromotionAttribute);\n            const slotTarget = this.assignedSlot?.parentElement ?? this.parentElement ?? this;\n            this.resizeObserver.observe(slotTarget);\n        }\n    }\n\n    private applyAttributes(): void {\n        this.referenceTimestamp = this.getAttribute('reference-timestamp') || null;\n        // Anchor the host clock once per reference timestamp, so unrelated attribute changes or\n        // re-connects don't snap the countdown back to the original timestamp.\n        if (this.clockAnchoredTo !== this.referenceTimestamp) {\n            this.clockAnchoredTo = this.referenceTimestamp;\n            this.clockOffsetMs = resolveReferenceTime(this.referenceTimestamp).getTime() - Date.now();\n        }\n\n        // The host passes `account_id` (not part of the validated props) for logged-in users.\n        const hasAccountId = Boolean(this.getAttribute('account_id'));\n        const validator = (globalThis as Record<string, unknown>).C24Home as typeof C24Home | undefined;\n\n        if (typeof validator?.validateAttributes === 'function') {\n            const validated = validator.validateAttributes<HomeClientComponentProps>(this.attributes);\n            this.deviceOutput = validated.deviceOutput ?? this.deviceOutput;\n            this.platform = validated.platform ?? this.platform;\n            this.language = validated.language ?? this.language;\n            this.isLoggedIn = hasAccountId || Boolean(validated.user);\n            return;\n        }\n\n        this.isLoggedIn = hasAccountId || Boolean(this.getAttribute('user'));\n        this.deviceOutput =\n            (this.getAttribute('deviceoutput') as InternetBlitzdealsTileClient['deviceOutput']) ||\n            (this.getAttribute('device-output') as InternetBlitzdealsTileClient['deviceOutput']) ||\n            'desktop';\n        this.platform = (this.getAttribute('platform') as InternetBlitzdealsTileClient['platform']) || 'web';\n        this.language = (this.getAttribute('language') as Language) || 'de-DE';\n    }\n\n    private getAvailableSlotWidth(): number {\n        const slotParent = this.assignedSlot?.parentElement;\n        if (slotParent) return slotParent.clientWidth;\n        if (this.parentElement) return this.parentElement.clientWidth;\n        return this.clientWidth;\n    }\n\n    private updatePromotionAttribute(): void {\n        const isHero = isHeroSlot(this.getAvailableSlotWidth(), window.innerWidth);\n        if (isHero) {\n            this.setAttribute('data-promotion', 'top');\n            return;\n        }\n\n        this.removeAttribute('data-promotion');\n    }\n\n    private getTaglineHtml(now: Date = this.getNow()): string {\n        const tagline = getTeaserTagline(now);\n        return tagline ? formatTeaserTaglineHtml(tagline) : '';\n    }\n\n    private getNow(): Date {\n        return new Date(Date.now() + this.clockOffsetMs);\n    }\n\n    private clearTaglineTimer(): void {\n        if (this.taglineTimer !== null) {\n            clearTimeout(this.taglineTimer);\n            this.taglineTimer = null;\n        }\n    }\n\n    /** Re-renders the tagline exactly when its text changes (live hours/minutes countdown). */\n    private scheduleTaglineTick(now: Date = this.getNow()): void {\n        this.clearTaglineTimer();\n        if (!this.isConnected) return;\n\n        const delay = getMsUntilNextTaglineChange(now);\n        if (delay === null) return;\n\n        this.taglineTimer = setTimeout(() => {\n            this.taglineTimer = null;\n            // Render and reschedule from the same instant, otherwise a tick landing right at a\n            // boundary can leave the old text on screen (e.g. the final tick at the campaign end).\n            const tickNow = this.getNow();\n            this.updateTagline(tickNow);\n            this.scheduleTaglineTick(tickNow);\n        }, delay);\n    }\n\n    private updateTagline(now: Date = this.getNow()): void {\n        const tagline = this.shadow?.querySelector('.mw-tagline');\n        if (tagline) {\n            tagline.innerHTML = this.getTaglineHtml(now);\n        }\n    }\n\n    private async loadCashbackAmount(): Promise<void> {\n        const requestId = ++this.cashbackRequestId;\n        const badge = await fetchCashbackBadge(this.isLoggedIn);\n        // Ignore stale answers, e.g. when the login state changed while a request was in flight.\n        if (!this.isConnected || requestId !== this.cashbackRequestId) return;\n\n        const amount = this.shadow?.querySelector('.mw-cashback-amount');\n        if (amount) {\n            amount.textContent = formatCashback(badge.amount);\n        }\n        const kind = this.shadow?.querySelector('.mw-cashback-kind');\n        if (kind) {\n            kind.textContent = badge.label;\n        }\n        // Reveal (with pop animation) once the call has settled, with API value or fallback.\n        this.shadow?.querySelector('.mw-cashback')?.setAttribute('data-ready', '');\n    }\n\n    private render(): void {\n        if (!this.shadow) return;\n\n        if (!this.shadow.querySelector('.mw-root')) {\n            this.shadow.innerHTML = `\n                <style>${clientStyles}</style>\n                ${this.getContent()}\n            `;\n        }\n    }\n\n    private getContent(): string {\n        return `\n            <a class=\"mw-root\" href=\"${buildTeaserUrl()}\" aria-label=\"${labels.linkLabel}\">\n                <h2 class=\"mw-headline\">${labels.headline}</h2>\n                <div class=\"mw-blitz\">${blitzDealsSvg}</div>\n                <div class=\"mw-cashback\">\n                    <span class=\"mw-cashback-label\">${labels.cashbackLabelTop}</span>\n                    <span class=\"mw-cashback-amount\">${formatCashback(CASHBACK_FALLBACK)}</span>\n                    <span class=\"mw-cashback-label mw-cashback-kind\">${labels.cashbackLabelBottom}</span>\n                </div>\n                <p class=\"mw-tagline\">${this.getTaglineHtml()}</p>\n            </a>\n        `;\n    }\n}\n\n// SlugTagMatchStep: tag must match manifest slug (dsl-internetblitzdealstile)\ncustomElements.define('dsl-internetblitzdealstile', InternetBlitzdealsTileClient);\n"],"names":["hostname","_a","_b","globalThis","location","getPssUrl","endsWith","getApiUrl","path","host","origin","labels","TAGLINE_DEFAULT","CAMPAIGN_END","Date","LIVE_COUNTDOWN_MS","TAGLINE_STEPS","from","text","resolveReferenceTime","referenceTimestamp","raw","parsed","Number","isNaN","getTime","parseReferenceTimestamp","MINUTE_MS","HOUR_MS","plural","count","singular","pluralForm","formatCashback","value","fetchJson","_0","__async","this","arguments","url","init","response","fetch","__spreadValues","signal","AbortSignal","timeout","ok","json","e","fetchMaxCashback","data","fetchPromotionDiscount","credentials","total","isFinite","InternetBlitzdealsTileClient","HTMLElement","constructor","super","__publicField","updatePromotionAttribute","observedAttributes","attributeChangedCallback","name","oldValue","newValue","wasLoggedIn","isLoggedIn","applyAttributes","updateTagline","scheduleTaglineTick","shadow","loadCashbackAmount","connectedCallback","attachShadow","mode","render","bindLayoutListeners","disconnectedCallback","clearTaglineTimer","window","removeEventListener","boundUpdatePromotionAttribute","breakpointMedia","resizeObserver","disconnect","layoutListenersBound","_c","addEventListener","matchMedia","ResizeObserver","slotTarget","assignedSlot","parentElement","observe","getAttribute","clockAnchoredTo","clockOffsetMs","now","hasAccountId","Boolean","validator","C24Home","validateAttributes","validated","attributes","deviceOutput","platform","language","user","getAvailableSlotWidth","slotParent","clientWidth","slotWidth","viewportWidth","innerWidth","isHeroSlot","removeAttribute","setAttribute","getTaglineHtml","getNow","tagline","remaining","Math","min","ceil","floor","getTeaserTagline","prefix","highlight","formatTeaserTaglineHtml","taglineTimer","clearTimeout","isConnected","delay","map","filter","time","max","getMsUntilNextTaglineChange","setTimeout","tickNow","querySelector","innerHTML","_d","requestId","cashbackRequestId","badge","discount","amount","label","fetchCashbackBadge","textContent","kind","getContent","URL","URLSearchParams","search","forEach","key","searchParams","set","toString","buildTeaserUrl","customElements","define"],"mappings":"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"}