{"version":3,"file":"main.CMDv9P.js","sources":["theme-52/virtual:theme","theme-52/discourse/components/notification-banner.gjs","theme-52/discourse/components/notification-banners.gjs","theme-52/discourse/api-initializers/notification-banners.gjs","theme-52/virtual:entrypoint:main"],"sourcesContent":["import { getObjectForTheme } from \"discourse/lib/theme-settings-store\";\n\nexport const settings = getObjectForTheme(52);\n\nexport function themePrefix(key) {\n  return `theme_translations.52.${key}`;\n}\n","import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { action } from \"@ember/object\";\nimport { htmlSafe } from \"@ember/template\";\nimport CookText from \"discourse/components/cook-text\";\nimport DButton from \"discourse/components/d-button\";\n\nexport default class NotificationBanner extends Component {\n  @tracked\n  dismissed = this.args.banner.dismissible\n    ? localStorage.getItem(this.args.banner.id)\n    : false;\n\n  @action\n  dismiss() {\n    if (!this.args.banner.dismissible) {\n      return;\n    }\n    this.dismissed = true;\n    return localStorage.setItem(this.args.banner.id, true);\n  }\n\n  get showBanner() {\n    return !this.dismissed;\n  }\n\n  <template>\n    {{#if this.showBanner}}\n      <div\n        id={{@banner.id}}\n        class=\"notification-banner\"\n        style={{htmlSafe @banner.styles}}\n      >\n        <div class=\"notification-banner__wrapper wrap\">\n          {{#if @banner.dismissible}}\n            <div class=\"notification-banner__close\">\n              <DButton\n                @icon=\"xmark\"\n                @action={{this.dismiss}}\n                @title=\"banner.close\"\n                class=\"btn-transparent close\"\n              />\n            </div>\n          {{/if}}\n          <div class=\"notification-banner__content\">\n            {{#if @banner.title}}\n              <h2 class=\"notification-banner__header\">{{@banner.title}}</h2>\n            {{/if}}\n            <CookText @rawText={{@banner.message}} />\n          </div>\n        </div>\n      </div>\n    {{/if}}\n  </template>\n}\n","import Component from \"@glimmer/component\";\nimport { cached, tracked } from \"@glimmer/tracking\";\nimport { action } from \"@ember/object\";\nimport { service } from \"@ember/service\";\nimport NotificationBanner from \"./notification-banner\";\n\nconst TL_GROUPS = [10, 11, 12, 13, 14];\n\nexport default class NotificationBanners extends Component {\n  @service currentUser;\n  @service router;\n\n  @tracked enabledCarouselBanners = [];\n  @tracked enabledSoloBanners = [];\n\n  constructor() {\n    super(...arguments);\n    this.setBanners();\n    this.router.on(\"routeDidChange\", this.setBanners);\n  }\n\n  willDestroy() {\n    super.willDestroy(...arguments);\n    this.router.off(\"routeDidChange\", this.setBanners);\n  }\n\n  #filterBanners(banner) {\n    const currentRoute = this.router.currentRoute;\n    const now = Date.now();\n\n    return (\n      !this.#adminRoute(currentRoute) &&\n      this.#matchedCategory(banner, currentRoute) &&\n      this.#matchedAudience(banner) &&\n      this.#withinDateRange(banner, now)\n    );\n  }\n\n  #adminRoute(currentRoute) {\n    return currentRoute.name?.startsWith(\"admin\");\n  }\n\n  #matchedCategory(banner, currentRoute) {\n    if (\n      !(\"selected_categories\" in banner) ||\n      banner.selected_categories?.length === 0\n    ) {\n      return true;\n    }\n\n    const categoryId = currentRoute.attributes?.category?.id;\n\n    return (\n      currentRoute.name === \"discovery.category\" &&\n      banner.selected_categories?.includes(categoryId)\n    );\n  }\n\n  #matchedAudience(banner) {\n    // If no groups are specified, allow the banner\n    if (banner.enabled_groups?.length === 1 && banner.enabled_groups[0] === 0) {\n      return true;\n    }\n\n    // If groups are specified, check if user has any of them\n    const userGroupsSet = this.currentUserGroupsSet;\n    return banner.enabled_groups\n      .filter((group) => group !== 0)\n      .some((group) => userGroupsSet.has(group));\n  }\n\n  #withinDateRange(banner, now) {\n    const startDate = banner.date_after ? Date.parse(banner.date_after) : null;\n    const endDate = banner.date_before ? Date.parse(banner.date_before) : null;\n\n    if (startDate && now < startDate) {\n      return false;\n    }\n    if (endDate && now > endDate) {\n      return false;\n    }\n\n    return true;\n  }\n\n  get carouselBanners() {\n    if (!this.args.carouselBanners) {\n      return [];\n    }\n    return this.args.carouselBanners.filter(this.#filterBanners.bind(this));\n  }\n\n  get soloBanners() {\n    if (!this.args.soloBanners) {\n      return [];\n    }\n    return this.args.soloBanners.filter(this.#filterBanners.bind(this));\n  }\n\n  @cached\n  get currentUserGroups() {\n    if (!this.currentUser) {\n      return [0];\n    }\n\n    const allGroups = this.currentUser.groups.map((group) => group.id);\n    const tlGroups = allGroups.filter((g) => TL_GROUPS.includes(g));\n    const highestTl = tlGroups.length > 0 ? [Math.max(...tlGroups)] : [];\n    const nonTlGroups = allGroups.filter((group) => !tlGroups.includes(group));\n\n    return [...highestTl, ...nonTlGroups];\n  }\n\n  @cached\n  get currentUserGroupsSet() {\n    return new Set(this.currentUserGroups);\n  }\n\n  @action\n  setBanners() {\n    if (this.carouselBanners.length < 2) {\n      this.enabledSoloBanners = [...this.soloBanners, ...this.carouselBanners];\n    } else {\n      this.enabledCarouselBanners = this.carouselBanners;\n      this.enabledSoloBanners = this.soloBanners;\n    }\n  }\n\n  <template>\n    {{#if this.enabledCarouselBanners}}\n      <section\n        class=\"splide notification-banners--{{@outlet}}\"\n        aria-label=\"Notification banners\"\n        aria-roledescription=\"carousel\"\n        role=\"group\"\n        data-splide={{@splideOptions}}\n      >\n        <div class=\"splide__track\">\n          <ul class=\"splide__list\">\n            {{#each this.enabledCarouselBanners as |banner|}}\n              <li class=\"splide__slide\">\n                <NotificationBanner @banner={{banner}} />\n              </li>\n            {{/each}}\n          </ul>\n        </div>\n      </section>\n    {{/if}}\n\n    {{#if this.enabledSoloBanners}}\n      <section class=\"notification-banners--{{@outlet}}\">\n        {{#each this.enabledSoloBanners as |banner|}}\n          <NotificationBanner @banner={{banner}} />\n        {{/each}}\n      </section>\n    {{/if}}\n  </template>\n}\n","import { apiInitializer } from \"discourse/lib/api\";\nimport loadScript from \"discourse/lib/load-script\";\nimport NotificationBanners from \"../components/notification-banners\";\n\n// Cache for color calculations to avoid redundant computations\nconst colorStyleCache = new Map();\n\nfunction loadSplideCSS() {\n  if (document.getElementById(\"splide-css\")) {\n    return;\n  }\n\n  const link = document.createElement(\"link\");\n  Object.assign(link, {\n    rel: \"stylesheet\",\n    type: \"text/css\",\n    id: \"splide-css\",\n    href: settings.theme_uploads.splide_css,\n  });\n  document.head.appendChild(link);\n}\n\nfunction bannerStyles(background_color) {\n  // Check cache first\n  if (colorStyleCache.has(background_color)) {\n    return colorStyleCache.get(background_color);\n  }\n\n  let foregroundColor = \"var(--primary)\";\n  let backgroundColor = \"var(--tertiary-low)\";\n\n  if (background_color) {\n    const r = parseInt(background_color.substring(0, 2), 16);\n    const g = parseInt(background_color.substring(2, 4), 16);\n    const b = parseInt(background_color.substring(4, 6), 16);\n\n    const srgb = [r, g, b].map((i) => {\n      const normalized = i / 255;\n      return normalized <= 0.04045\n        ? normalized / 12.92\n        : Math.pow((normalized + 0.055) / 1.055, 2.4);\n    });\n\n    const L = 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];\n    foregroundColor = L > 0.179 ? \"#000000\" : \"#FFFFFF\";\n    backgroundColor = `#${background_color}`;\n  }\n\n  const result = `background-color: ${backgroundColor}; color: ${foregroundColor};`;\n\n  // Cache the result\n  colorStyleCache.set(background_color, result);\n\n  return result;\n}\n\n// Utility function to transform outlet name for settings lookup\nfunction normalizeName(outlet) {\n  return outlet.replaceAll(\"-\", \"_\");\n}\n\nfunction slugify(str) {\n  str = str\n    .trim() // trim leading/trailing white space\n    .replace(/[^a-zA-Z0-9 -]/g, \"\") // remove any non-alphanumeric characters\n    .replace(/\\s+/g, \"-\") // replace spaces with hyphens\n    .replace(/-+/g, \"-\") // remove consecutive hyphens\n    .padEnd(6, \"0\");\n  return str;\n}\n\nexport default apiInitializer((api) => {\n  loadSplideCSS();\n\n  const bannerConfigVersion = settings.banner_config_version;\n\n  const banners = [...settings.banners].reduce((acc, banner) => {\n    const outlet = banner.plugin_outlet;\n    const type = banner.carousel ? \"carousel\" : \"solo\";\n\n    // Create new object instead of mutating\n    const processedBanner = {\n      ...banner,\n      id: `notification-banner--${slugify(banner.id)}--${bannerConfigVersion}`,\n      styles: bannerStyles(banner.background_color),\n    };\n\n    // Initialize outlet if it doesn't exist\n    if (!acc[outlet]) {\n      acc[outlet] = {\n        carousel: [],\n        solo: [],\n      };\n    }\n\n    // Add banner to appropriate array\n    acc[outlet][type].push(processedBanner);\n\n    return acc;\n  }, {});\n\n  Object.keys(banners).forEach((outlet) => {\n    const carouselBanners = banners[outlet].carousel;\n    const soloBanners = banners[outlet].solo;\n    const splideOptions = settings[`splide_options__${normalizeName(outlet)}`];\n\n    api.renderInOutlet(\n      outlet,\n      <template>\n        <NotificationBanners\n          @outlet={{outlet}}\n          @carouselBanners={{carouselBanners}}\n          @soloBanners={{soloBanners}}\n          @splideOptions={{splideOptions}}\n        />\n      </template>\n    );\n  });\n\n  loadScript(settings.theme_uploads.splide_js).then(() => {\n    const el = document.querySelectorAll(\n      \".splide.notification-banners--above-site-header, .splide.notification-banners--below-site-header, .splide.notification-banners--top-notices\"\n    );\n    el.forEach((carousel) => {\n      // eslint-disable-next-line no-undef\n      new Splide(carousel).mount();\n    });\n  });\n});\n","import * as Mod1 from \"./discourse/api-initializers/notification-banners\";\nimport * as Mod2 from \"./discourse/components/notification-banner\";\nimport * as Mod3 from \"./discourse/components/notification-banners\";\nconst compatModules = {\n  \"discourse/api-initializers/notification-banners\": Mod1,\n  \"discourse/components/notification-banner\": Mod2,\n  \"discourse/components/notification-banners\": Mod3,\n};\nexport { compatModules };\nexport default compatModules;\n"],"names":["settings","getObjectForTheme","NotificationBanner","Component","dt7948","g","this","prototype","tracked","args","banner","dismissible","localStorage","getItem","id","dismissed","i","dismiss","setItem","n","action","showBanner","setComponentTemplate","createTemplateFactory","block","moduleName","scope","htmlSafe","DButton","CookText","isStrictMode","TL_GROUPS","NotificationBanners","service","currentUser","router","enabledCarouselBanners","enabledSoloBanners","constructor","super","arguments","setBanners","on","willDestroy","off","filterBanners","currentRoute","now","Date","adminRoute","matchedCategory","matchedAudience","withinDateRange","name","startsWith","selected_categories","length","categoryId","attributes","category","includes","enabled_groups","userGroupsSet","currentUserGroupsSet","filter","group","some","has","startDate","date_after","parse","endDate","date_before","carouselBanners","bind","soloBanners","currentUserGroups","allGroups","groups","map","tlGroups","Math","max","cached","Set","colorStyleCache","Map","bannerStyles","background_color","get","foregroundColor","backgroundColor","srgb","parseInt","substring","normalized","pow","result","set","notificationBanners","apiInitializer","api","document","getElementById","link","createElement","Object","assign","rel","type","href","theme_uploads","splide_css","head","appendChild","loadSplideCSS","bannerConfigVersion","banner_config_version","banners","reduce","acc","outlet","plugin_outlet","carousel","processedBanner","str","trim","replace","padEnd","styles","solo","push","keys","forEach","splideOptions","replaceAll","normalizeName","renderInOutlet","templateOnly","loadScript","splide_js","then","querySelectorAll","Splide","mount","compatModules","Mod2","Mod3"],"mappings":"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"}