{"version":3,"file":"virtual_main.js","sources":["theme-9/virtual:init-settings","theme-9/virtual:theme","theme-9/discourse/api-initializers/theme-initializer.gjs","theme-9/virtual:main"],"sourcesContent":["import { registerSettings } from \"discourse/lib/theme-settings-store\";\n\nregisterSettings(9, {});\n","import { getObjectForTheme } from \"discourse/lib/theme-settings-store\";\n\nexport const settings = getObjectForTheme(9);\n\nexport function themePrefix(key) {\n  return `theme_translations.9.${key}`;\n}\n","import { apiInitializer } from \"discourse/lib/api\";\n\nexport default apiInitializer((api) => {\n    // -------------------------\n    // Constants\n    // -------------------------\n\n    const EXCLUDED_GROUPS = [\"SlowtwitchPlus\"];\n    const DEFAULT_THREAD_LONG_AD_CLASS = \"ads-long-thread-default\";\n    const DEFAULT_LONG_AD_CLASS = \"ads-long-default\";\n    const DEFAULT_SMALL_AD_CLASS = \"ads-small-default\";\n    const AD_CATEGORY_INJECTION_INTERVAL = 10;\n    const AD_THREAD_INJECTION_INTERVAL = 5;\n    const SCROLL_THROTTLE_DELAY = 800;\n    const DESKTOP_BREAKPOINT = 1024;\n    const POLL_IFRAME_URL = \"https://slowtwitch.com/polls/\";\n\n    const CATEGORY_AD_CONFIG = {\n        \"gravel cycling\": {\n            long: \"ads-long-gravel\",\n            small: \"ads-small-gravel\",\n        },\n        \"politics is life\": {\n            long: \"ads-long-politics\",\n            small: \"ads-small-politics\",\n        },\n        running: {\n            long: \"ads-long-running\",\n            small: \"ads-small-running\",\n        },\n        \"triathlon forum\": {\n            long: \"ads-long-triathlon\",\n            small: \"ads-small-triathlon\",\n        },\n    };\n\n    const THREAD_AD_CONFIG = {\n        1284017: \".ads-long-canyon\",\n        785701: \".ads-long-profile__design\",\n        781575: \".ads-long-quintana__roo\",\n        825621: \".ads-long-lionel__sanders\",\n        1295670: \".ads-long-seasons__long__cycling\",\n    };\n\n    // -------------------------\n    // State\n    // -------------------------\n\n    let scrollAttached = false;\n    let firstInjectionDone = false;\n\n    // -------------------------\n    // Auth Check\n    // -------------------------\n\n    const user = api.getCurrentUser();\n    if (user?.groups?.some((g) => EXCLUDED_GROUPS.includes(g.name))) {\n        return;\n    }\n\n    // -------------------------\n    // Utility Functions\n    // -------------------------\n\n    function isDesktop() {\n        return window.matchMedia(`(min-width: ${DESKTOP_BREAKPOINT}px)`).matches;\n    }\n\n    function isOnThread() {\n        return window.location.pathname.startsWith(\"/t/\");\n    }\n\n    function throttle(fn, delay) {\n        let wait = false;\n        return (...args) => {\n            if (wait) return;\n            fn.apply(this, args);\n            wait = true;\n            setTimeout(() => {\n                wait = false;\n            }, delay);\n        };\n    }\n\n    function sanitizeClassName(className) {\n        return className.replace(/[^a-zA-Z0-9_-]/g, \"\");\n    }\n\n    // -------------------------\n    // Route Detection\n    // -------------------------\n\n    function getCurrentThread() {\n        const match = window.location.pathname.match(/^\\/t\\/[^/]+\\/(\\d+)/);\n        return match?.[1] || null;\n    }\n\n    function getCurrentCategory() {\n        const match = window.location.pathname.match(/^\\/c\\/([^/]+)/);\n        if (!match) return null;\n\n        try {\n            return decodeURIComponent(match[1].replace(/-/g, \" \")).toLowerCase();\n        } catch (e) {\n            return null;\n        }\n    }\n\n    // -------------------------\n    // Ad Class Resolution \n    // -------------------------\n\n    function getAdClass() {\n        if (isOnThread()) {\n            const threadId = getCurrentThread();\n            return THREAD_AD_CONFIG[threadId] || DEFAULT_THREAD_LONG_AD_CLASS;\n        }\n\n        const category = getCurrentCategory();\n        return CATEGORY_AD_CONFIG[category]?.long || DEFAULT_LONG_AD_CLASS;\n    }\n\n    function getSmallAdClass() {\n        const category = getCurrentCategory();\n        return CATEGORY_AD_CONFIG[category]?.small || DEFAULT_SMALL_AD_CLASS;\n    }\n\n    // -------------------------\n    // DOM Manipulation Helpers\n    // -------------------------\n\n    function removeExistingAds(container, selector = '[class*=\"ads-\"]') {\n        if (!container) return;\n        const existingAds = container.querySelectorAll(selector);\n        existingAds.forEach((ad) => ad.remove());\n    }\n\n    function createAdContainer(className, styles = {}) {\n        const div = document.createElement(\"div\");\n        div.className = sanitizeClassName(className);\n        Object.assign(div.style, styles);\n        return div;\n    }\n\n    // -------------------------\n    // Category Ad Injectors\n    // -------------------------\n\n    function injectCategoryHeaderAd() {\n        const targetContainer = document.querySelector(\"div.container.list-container.--topic-list, div.container.list-container.--categories\");\n\n        if (!targetContainer) return;\n\n        removeExistingAds(targetContainer);\n\n        const adClass = getAdClass();\n        const adContainer = createAdContainer(adClass);\n        targetContainer.insertBefore(adContainer, targetContainer.firstChild);\n    }\n\n    function injectCategoryTableAds() {\n        const tables = document.querySelectorAll(\"table\");\n        const adClass = getAdClass();\n        const isNonDesktop = !isDesktop();\n\n        tables.forEach((table) => {\n            // Skip calendar tables\n            if (table.parentElement?.classList.contains(\"fc-view\")) return;\n\n            const rows = Array.from(table.querySelectorAll(\"tr\"));\n            const maxCols = Math.max(...rows.map((r) => r.querySelectorAll(\"td, th\").length));\n\n            // Filter out rows that are already ad rows\n            const contentRows = rows.filter((row) => !row.querySelector('[class*=\"ads-\"], .st-polls'));\n\n            contentRows.forEach((row, index) => {\n                // Skip if already processed\n                if (row.dataset.adChecked === \"true\") return;\n\n                // Mark as processed\n                row.dataset.adChecked = \"true\";\n\n                // Inject ad after every 10th content row\n                if ((index + 1) % AD_CATEGORY_INJECTION_INTERVAL !== 0) return;\n\n                const nextRow = row.nextElementSibling;\n\n                // Skip if ad already exists\n                if (nextRow?.querySelector('[class*=\"ads-\"], .st-polls')) return;\n\n                const tr = document.createElement(\"tr\");\n                const td = document.createElement(\"td\");\n                td.colSpan = maxCols;\n\n                let container;\n\n                // Special poll injection for first non-desktop ad\n                if (isNonDesktop && !firstInjectionDone) {\n                    container = document.createElement(\"iframe\");\n                    container.className = \"st-polls\";\n                    container.src = POLL_IFRAME_URL;\n                    Object.assign(container.style, {\n                        border: \"none\",\n                        opacity: \"0\",\n                        transition: \"opacity 0.2s\",\n                    });\n                    container.width = \"500\";\n                    container.height = \"350\";\n                    firstInjectionDone = true;\n                } else {\n                    container = createAdContainer(adClass);\n                }\n\n                td.appendChild(container);\n                tr.appendChild(td);\n                row.parentNode.insertBefore(tr, row.nextSibling);\n            });\n        });\n    }\n\n    // -------------------------\n    // Thread Ad Injectors\n    // -------------------------\n\n    function injectThreadHeaderAd() {\n        const firstPost = document.querySelector(\".post-stream .topic-post\");\n        if (!firstPost) return;\n\n        const adClass = getAdClass();\n        const prevElement = firstPost.previousElementSibling;\n\n        // Remove existing ad if present\n        if (prevElement?.className.includes(\"ads-\")) {\n            prevElement.remove();\n        }\n\n        const styles = {\n            marginBottom: \"1em\",\n        };\n\n        if (isDesktop()) {\n            styles.paddingLeft = \"5em\";\n        }\n\n        const adContainer = createAdContainer(adClass, styles);\n\n        firstPost.parentNode.insertBefore(adContainer, firstPost);\n    }\n\n    function injectThreadPostAds() {\n        const posts = document.querySelectorAll(\".topic-post\");\n        const adClass = getAdClass();\n\n        posts.forEach((post, index) => {\n            if (post.dataset.adProcessed === \"true\") return;\n\n            post.dataset.adProcessed = \"true\";\n\n            if ((index + 1) % AD_THREAD_INJECTION_INTERVAL !== 0) return;\n\n            const nextElement = post.nextElementSibling;\n            if (nextElement?.className.includes(\"ads-\")) return;\n\n            const styles = {};\n            if (isDesktop()) {\n                styles.paddingLeft = \"5em\";\n            }\n\n            const adContainer = createAdContainer(adClass, styles);\n\n            post.parentNode.insertBefore(adContainer, post.nextSibling);\n        });\n    }\n\n    // -------------------------\n    // Sidebar Ad Injector\n    // -------------------------\n\n    function injectSidebarAd() {\n        const sidebar = document.querySelector(\".forum-sidebar-ads\");\n        if (!sidebar) return;\n    \n        const existingAdClasses = Array.from(sidebar.classList).filter((cls) => cls.startsWith(\"ads-\"));\n        existingAdClasses.forEach((cls) => sidebar.classList.remove(cls));\n    \n        const adClass = getSmallAdClass();\n    \n        const newSidebar = document.createElement(\"div\");\n        newSidebar.className = sidebar.className;\n        newSidebar.classList.add(adClass);\n    \n        sidebar.parentNode.replaceChild(newSidebar, sidebar);\n    }\n\n    // -------------------------\n    // Scroll Handler\n    // -------------------------\n\n    const handleScroll = throttle(() => {\n        if (!isOnThread()) {\n            injectCategoryTableAds();\n        } else if (isOnThread()) {\n            injectThreadPostAds();\n        }\n    }, SCROLL_THROTTLE_DELAY);\n\n    // -------------------------\n    // Main Injector Runner\n    // -------------------------\n\n    function runInjectors() {\n        if (!isOnThread()) {\n            injectCategoryHeaderAd();\n            injectCategoryTableAds();\n            injectSidebarAd();\n        } else if (isOnThread()) {\n            injectThreadHeaderAd();\n            injectThreadPostAds();\n        }\n\n        // Attach scroll listener once\n        if (!scrollAttached) {\n            window.addEventListener(\"scroll\", handleScroll, { passive: true });\n            scrollAttached = true;\n        }\n    }\n\n    function cleanup() {\n        if (scrollAttached) {\n            window.removeEventListener(\"scroll\", handleScroll);\n            scrollAttached = false;\n        }\n        firstInjectionDone = false;\n    }\n\n    // -------------------------\n    // Event Hooks\n    // -------------------------\n\n    api.onAppEvent(\"page:loaded\", () => {\n        firstInjectionDone = false;\n        runInjectors();\n    });\n\n    api.onPageChange(() => {\n        cleanup();\n        runInjectors();\n    });\n});\n","import \"virtual:init-settings\";\n\nconst themeCompatModules = {};\nimport * as Mod1 from \"./discourse/api-initializers/theme-initializer\";\nthemeCompatModules[\"discourse/api-initializers/theme-initializer\"] = Mod1;\n\nexport default themeCompatModules;\n"],"names":["registerSettings","getObjectForTheme","themeInitializer","apiInitializer","api","EXCLUDED_GROUPS","CATEGORY_AD_CONFIG","long","small","running","THREAD_AD_CONFIG","scrollAttached","firstInjectionDone","user","getCurrentUser","groups","some","g","includes","name","isDesktop","window","matchMedia","matches","isOnThread","location","pathname","startsWith","getCurrentCategory","match","decodeURIComponent","replace","toLowerCase","e","getAdClass","threadId","getCurrentThread","category","createAdContainer","className","styles","div","document","createElement","sanitizeClassName","Object","assign","style","injectCategoryTableAds","tables","querySelectorAll","adClass","isNonDesktop","forEach","table","parentElement","classList","contains","rows","Array","from","maxCols","Math","max","map","r","length","filter","row","querySelector","index","dataset","adChecked","nextRow","nextElementSibling","tr","td","container","colSpan","src","border","opacity","transition","width","height","appendChild","parentNode","insertBefore","nextSibling","injectThreadPostAds","posts","post","adProcessed","nextElement","paddingLeft","adContainer","injectSidebarAd","sidebar","cls","remove","getSmallAdClass","newSidebar","add","replaceChild","handleScroll","fn","delay","wait","args","apply","this","setTimeout","throttle","runInjectors","firstPost","prevElement","previousElementSibling","marginBottom","injectThreadHeaderAd","targetContainer","selector","ad","removeExistingAds","firstChild","injectCategoryHeaderAd","addEventListener","passive","onAppEvent","onPageChange","removeEventListener","themeCompatModules","Mod1"],"mappings":"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"}