{"version":3,"file":"index.mjs","sources":[""],"sourcesContent":["import { removeChildNodes } from \"@web3r/flowerkit/dom\";\r\nimport { onDOMReady } from \"@web3r/flowerkit/evt\";\r\nimport type { TGetFromServerArgs } from \"@web3r/flowerkit/net\";\r\nimport { getFromServer } from \"@web3r/flowerkit/net\";\r\nimport { getDocument } from \"ssr-window\";\r\nimport { getSanitizedHTML } from \"../../utils/getSanitizedHTML\";\r\n\r\ntype TGetSpriteUrlParams = {\r\n  fileName?: string;\r\n  svgSpriteRevision?: number;\r\n  revision?: number;\r\n  filePath?: string;\r\n  env?: string;\r\n};\r\n\r\n/**\r\n * Singleton. Плагин отвечает за загрузку SVG-спрайта и встраивание иконок на страницу. Принимает в себя объект с параметрами.\r\n * В случае если SVG спрайт успешно загрузился на клиенте, то плагин помещает содержимое файла в локальное хранилище с ключом, указанным в `storageInlineSVGDataName`. Временная метка `storageInlineSVGDataName` служит для валидации кеша и должна задаваться с backend как один из ключей глобальных параметров приложения — `svgSpriteRevision` в формате числа, конвертируемого в дату.\r\n * Предупреждение `[IconSprite] Sprite was found in cache without stamp. Please, provide prop 'svgSpriteRevision' to plugins config` возникает, когда глобальная временная метка не была присвоена, но содержимое спрайта было добавлено в кеш. По факту это делает кеш иконок бесконечным.\r\n * @module IconSprite\r\n * @param cfg{Object=} = объект конфигурации\r\n * @param cfg.filePath{String=} путь к файлу спрайта (до папки /build/)\r\n * @param cfg.fileName{String=} название файла со спрайтом (по умолчанию icons.svg)\r\n * @param cfg.requestCfg{Object=} параметры запроса к спрайту\r\n * @param cfg.svgSpriteRevision{Number=} временная метка даты последнего изменения файла\r\n * @param cfg.getSpriteUrl{Function=} функция получения полного адреса спрайта\r\n * @param cfg.idSprite{String=} уникальный ID генерируемого блока со спрайтом\r\n * @param cfg.env{String=} название переменной текущего окружения (process.env.NODE_ENV)\r\n * @param cfg.storageInlineSVGDataName{String=} название ключа в localStorage для содержимого спрайта\r\n * @param cfg.storageInlineSVGRevisionName{String=} название ключа в localStorage для временной метки спрайта\r\n *\r\n */\r\nexport class IconSprite {\r\n\r\n  static #instance: IconSprite | null = null;\r\n\r\n  static #iconExcludeAttr = \"data-js-IconSpriteExclude\";\r\n\r\n  static #iconIdAttr = \"data-js-IconSpriteSvgId\";\r\n\r\n  filePath: string;\r\n\r\n  fileName: string;\r\n\r\n  requestCfg: Record<string, unknown>;\r\n\r\n  timeStamp: number;\r\n\r\n  svgSpriteRevision: number;\r\n\r\n  getSpriteUrl: (params: TGetSpriteUrlParams) => string;\r\n\r\n  env: string;\r\n\r\n  idSprite: string;\r\n\r\n  data: string | null;\r\n\r\n  storageInlineSVGDataName: string;\r\n\r\n  storageInlineSVGRevisionName: string;\r\n\r\n  isCached: boolean;\r\n\r\n  /**\r\n   * Стандартная конфигурация плагина иконок.\r\n   * Используется как базовый набор параметров при инициализации.\r\n   * @type {{\r\n   *   filePath: string,\r\n   *   fileName: string,\r\n   *   idSprite: string,\r\n   *   svgSpriteRevision: number,\r\n   *   env: string | undefined,\r\n   *   storageInlineSVGDataName: string,\r\n   *   storageInlineSVGRevisionName: string,\r\n   *   getSpriteUrl: (params: {filePath?: string, env?: string, fileName?: string, svgSpriteRevision?: number, revision?: number}) => string,\r\n   *   requestCfg: {type: string, mode: string, isBubble: boolean}\r\n   * }}\r\n   */\r\n  static defaultCfg = {\r\n    filePath: \"/build/\",\r\n    fileName: \"icons.svg\",\r\n    idSprite: \"svgSprite\",\r\n    svgSpriteRevision: 0,\r\n    env: process.env.NODE_ENV,\r\n    storageInlineSVGDataName: \"inlineSVGData\",\r\n    storageInlineSVGRevisionName: \"inlineSVGRev\",\r\n    /**\r\n     * Получает ссылку на спрайт\r\n     * @param filePath{String=} путь к спрайту\r\n     * @param fileName{String=} название файла спрайта\r\n     * @param svgSpriteRevision{Number=} дата последнего обновления спрайта\r\n     * @param revision{Number=} устаревший алиас даты последнего обновления спрайта\r\n     * @param env{String=} название переменной текущего окружения (process.env.NODE_ENV)\r\n     * @returns {string}\r\n     */\r\n    getSpriteUrl: ({\r\n      filePath = \"\",\r\n      env = process.env.NODE_ENV,\r\n      fileName = \"icons.svg\",\r\n      svgSpriteRevision,\r\n      revision,\r\n    }: TGetSpriteUrlParams) => {\r\n      let resolvedRevision = new Date().getTime();\r\n      if (typeof revision === \"number\") {\r\n        resolvedRevision = revision;\r\n      }\r\n      if (typeof svgSpriteRevision === \"number\") {\r\n        resolvedRevision = svgSpriteRevision;\r\n      }\r\n      return env === \"development\" ? `./${fileName}` : `${filePath}/${fileName}?revision=${resolvedRevision.toString()}`;\r\n    },\r\n    requestCfg: {\r\n      type: \"text\",\r\n      mode: \"same-origin\",\r\n      isBubble: false,\r\n    },\r\n  };\r\n\r\n  constructor(cfg: Partial<{\r\n    filePath: string;\r\n    fileName: string;\r\n    requestCfg: TGetFromServerArgs<string>;\r\n    svgSpriteRevision: number;\r\n    getSpriteUrl: (params: TGetSpriteUrlParams) => string;\r\n    idSprite: string;\r\n    env: string;\r\n    storageInlineSVGDataName: string;\r\n    storageInlineSVGRevisionName: string;\r\n  }> = {}) {\r\n    if (IconSprite.#instance) {\r\n      return IconSprite.#instance;\r\n    }\r\n    const {\r\n      filePath,\r\n      fileName,\r\n      requestCfg,\r\n      svgSpriteRevision,\r\n      getSpriteUrl,\r\n      idSprite,\r\n      env,\r\n      storageInlineSVGDataName,\r\n      storageInlineSVGRevisionName,\r\n    } = { ...IconSprite.defaultCfg, ...cfg };\r\n    this.filePath = filePath;\r\n    this.fileName = fileName;\r\n    this.requestCfg = requestCfg;\r\n    this.timeStamp = new Date().getTime();\r\n    this.svgSpriteRevision = svgSpriteRevision || this.timeStamp;\r\n    this.getSpriteUrl = getSpriteUrl.bind(this);\r\n    this.env = env || \"\";\r\n    this.idSprite = idSprite;\r\n    this.data = null;\r\n    this.storageInlineSVGDataName = storageInlineSVGDataName;\r\n    this.storageInlineSVGRevisionName = storageInlineSVGRevisionName;\r\n    this.isCached = typeof localStorage !== \"undefined\" && !!localStorage.getItem(storageInlineSVGDataName) && !this.isExpired();\r\n    this.init();\r\n    IconSprite.#instance = this;\r\n  }\r\n\r\n\r\n  /**\r\n   * Создание спрайта\r\n   * @private\r\n   */\r\n  #createSprite() {\r\n    onDOMReady(() => {\r\n      const currentNode = getDocument().getElementById(this.idSprite);\r\n      if (currentNode) {\r\n        console.error(`[IconSprite] Element with id '${this.idSprite}' is already exist: `, currentNode);\r\n      } else {\r\n        const sprite = getDocument().createElement(\"div\");\r\n        const html = getSanitizedHTML(this.data || \"\");\r\n        sprite.id = this.idSprite;\r\n        sprite.style.cssText = `position: absolute !important; clip: rect(1px, 1px, 1px, 1px); padding: 0 !important; border: 0 !important; height: 1px !important; width: 1px !important; overflow: hidden; left: -1px;`;\r\n        sprite.insertAdjacentHTML(\"afterbegin\", html);\r\n        getDocument().body.appendChild(sprite);\r\n      }\r\n      this.data = null;\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Callback-функция загрузки спрайта\r\n   * @param sprite{String} содержимое файла спрайта\r\n   */\r\n  #onSpriteLoaded(sprite = \"\") {\r\n    this.data = sprite;\r\n    if (typeof localStorage !== \"undefined\") {\r\n      localStorage.setItem(this.storageInlineSVGDataName, this.data.toString());\r\n      localStorage.setItem(this.storageInlineSVGRevisionName, this.svgSpriteRevision.toString());\r\n    }\r\n    this.#createSprite();\r\n  }\r\n\r\n  /**\r\n   * Получение спрайта из кеша\r\n   */\r\n  #getFromCache() {\r\n    this.data = (typeof localStorage !== \"undefined\" && localStorage.getItem(this.storageInlineSVGDataName)) || null;\r\n    this.#createSprite();\r\n  }\r\n\r\n  /**\r\n   * Загружает спрайт с указанного URL\r\n   * @param url{String} адрес спрайта\r\n   * @returns {Promise<String>} промис с содержимым спрайта\r\n   */\r\n  async getSprite(url: string) {\r\n    return await getFromServer<string>({\r\n      url,\r\n      ...this.requestCfg,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Проверяет, устарел ли спрайт в кеше\r\n   * @returns {Boolean} true, если спрайт устарел или отсутствует\r\n   */\r\n  isExpired() {\r\n    if (typeof localStorage === \"undefined\") {\r\n      return true;\r\n    } else {\r\n      const cachedData = localStorage.getItem(this.storageInlineSVGRevisionName);\r\n      switch (true) {\r\n        case this.svgSpriteRevision === this.timeStamp: {\r\n          if (this.env === \"production\") {\r\n            console.error(`[IconSprite] Sprite was found in cache without stamp. Please, provide prop 'svgSpriteRevision' to plugin's config for proper caching!\"`);\r\n          }\r\n          return true;\r\n        }\r\n        case !!cachedData: {\r\n          const serverStamp = new Date(this.svgSpriteRevision);\r\n          const clientStamp = new Date(Number(cachedData));\r\n          const isExpired = serverStamp > clientStamp;\r\n          const details = `(server stamp: ${serverStamp}, client stamp: ${clientStamp})`;\r\n          console.debug(`[IconSprite] Sprite was found in cache with ${isExpired ? \"expired\" : \"actual\"} stamp ${details}`);\r\n          return isExpired;\r\n        }\r\n        default: {\r\n          return true;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Проверяет, валидная ли иконка\r\n   * @param src{String} адрес иконки\r\n   * @param svg{SVGElement} иконка\r\n   * @param use{SVGUseElement} элемент use svg\r\n   * @returns {boolean}\r\n   */\r\n  static #isIconValid(src: string, svg: SVGElement, use: SVGUseElement) {\r\n    if (svg.hasAttribute(IconSprite.#iconExcludeAttr)) {\r\n      removeChildNodes(svg);\r\n      svg.appendChild(use);\r\n      return false;\r\n    } else {\r\n      const originID = src.replace(\"#\", \"\");\r\n      svg.setAttribute(IconSprite.#iconIdAttr, originID);\r\n      svg.classList.add(originID);\r\n      return true;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Инициализирует плагин, загружая спрайт из кеша или с сервера\r\n   */\r\n  init() {\r\n    if (this.isCached) {\r\n      this.#getFromCache();\r\n    } else {\r\n      const url = this.getSpriteUrl({\r\n        fileName: this.fileName,\r\n        svgSpriteRevision: this.svgSpriteRevision,\r\n        filePath: this.filePath,\r\n        env: this.env,\r\n      });\r\n      this.getSprite(url).then((sprite) => {\r\n        if (sprite && sprite?.startsWith(\"<svg\")) {\r\n          console.debug(`[IconSprite] Sprite was loaded from server: \"${url}\"`);\r\n          this.#onSpriteLoaded(sprite);\r\n        } else {\r\n          console.error(`[IconSprite] Sprite was loaded from server but it contains invalid response: \"${url}\"`);\r\n        }\r\n        return Promise.resolve();\r\n      }, (status) => console.error(`[IconSprite] Sprite loading failed: ${status}`));\r\n    }\r\n  }\r\n\r\n}\r\n"],"names":["IconSprite","static","filePath","fileName","requestCfg","timeStamp","svgSpriteRevision","getSpriteUrl","env","idSprite","data","storageInlineSVGDataName","storageInlineSVGRevisionName","isCached","process","NODE_ENV","revision","resolvedRevision","Date","getTime","toString","type","mode","isBubble","constructor","cfg","instance","defaultCfg","this","bind","localStorage","getItem","isExpired","init","createSprite","onDOMReady","currentNode","getDocument","getElementById","console","error","sprite","createElement","html","getSanitizedHTML","id","style","cssText","insertAdjacentHTML","body","appendChild","onSpriteLoaded","setItem","getFromCache","getSprite","url","getFromServer","cachedData","serverStamp","clientStamp","Number","details","debug","isIconValid","src","svg","use","hasAttribute","iconExcludeAttr","removeChildNodes","originID","replace","setAttribute","iconIdAttr","classList","add","then","startsWith","Promise","resolve","status"],"mappings":"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"}