{"version":3,"file":"index.mjs","sources":[""],"sourcesContent":["import { bubble } from \"@web3r/flowerkit/evt\";\r\nimport { getExternalScript } from \"@web3r/flowerkit/net\";\r\nimport { getWindow, getDocument } from \"ssr-window\";\r\nimport { constants } from \"../../constants\";\r\nimport { getCfg } from \"../../utils/getCfg\";\r\nimport { Collection } from \"../collection\";\r\nimport type { TCollectionElement, TCollectionItemDefaultCfg } from \"../collection\";\r\nimport { Dispatcher } from \"../dispatcher\";\r\n\r\n\r\n/**\r\n * DOM-элемент экземпляра captcha в коллекции.\r\n */\r\nexport type TCaptchaElement = TCollectionElement;\r\n\r\n\r\n/**\r\n * Состояние жизненного цикла виджета captcha.\r\n */\r\nexport type TCaptchaState = {\r\n  render: boolean;\r\n  ready: boolean;\r\n  expired: boolean;\r\n  error: boolean;\r\n  verify: boolean;\r\n};\r\n\r\n/**\r\n * Класс для управления Google Captcha.\r\n */\r\nclass Captcha {\r\n\r\n  stateClasses = {\r\n    rendered: \"isRendered\",\r\n    expired: \"isExpired\",\r\n    error: \"isError\",\r\n    verify: \"isVerify\",\r\n  };\r\n\r\n  #defaultCfg = {\r\n    \"sitekey\": getWindow()?.App?.googleCaptchaKey ?? \"0\",\r\n    \"theme\": \"dark\",\r\n    \"callback\": (response: string) => this.#handleVerify(response),\r\n    \"size\": \"normal\",\r\n    \"error-callback\": this.#handleError,\r\n    \"expired-callback\": this.#handleExpired,\r\n  };\r\n\r\n  instance: TCaptchaElement;\r\n\r\n  state: TCaptchaState;\r\n\r\n  cfg: TCollectionItemDefaultCfg;\r\n\r\n  constructor(instance: TCaptchaElement, _params: TCollectionItemDefaultCfg = {}) {\r\n    this.instance = instance;\r\n    this.state = {\r\n      render: false,\r\n      ready: false,\r\n      expired: false,\r\n      error: false,\r\n      verify: false,\r\n    };\r\n    if (CaptchaCollection.state.isAPIReady) {\r\n      if (!this.state.render) {\r\n        this.cfg = getCfg(this.instance as HTMLElement, CaptchaCollection.selector, this.#defaultCfg);\r\n        this.bindEvents();\r\n      } else if (getWindow()?.App?.isDebug) {\r\n        console.debug(\"[Captcha] Object are not defined or already rendered: \", instance);\r\n      }\r\n    } else {\r\n      CaptchaCollection.loadAPI();\r\n    }\r\n  }\r\n\r\n  #handleError() {\r\n    this.instance.classList.add(this.stateClasses.error);\r\n    this.instance.classList.remove(this.stateClasses.verify, constants.stateClasses.valid);\r\n    this.state = {\r\n      ...this.state,\r\n      error: true,\r\n      ready: false,\r\n      verify: false,\r\n    };\r\n    bubble(getDocument(), CaptchaCollection.bubbles.error, this.instance);\r\n  }\r\n\r\n  #handleExpired() {\r\n    this.instance.classList.add(this.stateClasses.expired);\r\n    this.instance.classList.remove(this.stateClasses.verify, constants.stateClasses.valid);\r\n    this.state = {\r\n      ...this.state,\r\n      ready: false,\r\n      verify: false,\r\n      expired: true,\r\n    };\r\n    bubble(getDocument(), CaptchaCollection.bubbles.expire, this.instance);\r\n  }\r\n\r\n  #handleVerify(response: string) {\r\n    this.instance.classList.add(this.stateClasses.verify, constants.stateClasses.valid);\r\n    this.instance.classList.remove(constants.stateClasses.invalid);\r\n    this.state.verify = true;\r\n    bubble(getDocument(), CaptchaCollection.bubbles.verify, { el: this.instance, response, states: this.state });\r\n  }\r\n\r\n  render() {\r\n    getWindow()?.grecaptcha.render(this.instance, this.cfg);\r\n    this.instance.classList.add(this.stateClasses.rendered);\r\n    this.state.render = true;\r\n    getWindow()?.grecaptcha.ready(() => {\r\n      bubble(getDocument(), CaptchaCollection.bubbles.render, { el: this.instance, states: this.state });\r\n    });\r\n  }\r\n\r\n  getResponse() {\r\n    return getWindow()?.grecaptcha.getResponse(this.instance);\r\n  }\r\n\r\n  reset() {\r\n    try {\r\n      getWindow()?.grecaptcha.reset(this.instance);\r\n    } catch (e: unknown) {\r\n      console.error(e);\r\n    }\r\n    this.state.verify = false;\r\n    this.instance.classList.remove(this.stateClasses.verify);\r\n    bubble(getDocument(), CaptchaCollection.bubbles.reset, this.instance);\r\n  }\r\n\r\n  #handleReady() {\r\n    this.state.ready = true;\r\n    if (this.instance.children.length) {\r\n      this.reset();\r\n    } else {\r\n      this.render();\r\n    }\r\n  }\r\n\r\n  #handleValidation(_event?: Event) {\r\n    // FormValidation.setValidClassToEl(this.instance, detail.isValid)\r\n  }\r\n\r\n  bindEvents() {\r\n    getWindow()?.grecaptcha.ready(() => this.#handleReady());\r\n    [ constants.formBubbles.inputValid, constants.formBubbles.inputInvalid ].forEach((eventName: string) => this.instance.addEventListener(eventName, (e: Event) => this.#handleValidation(e)));\r\n  }\r\n\r\n}\r\n\r\n/**\r\n * Класс, управляющий загрузкой API\r\n * @private\r\n */\r\nclass CaptchaApiManager {\n\n  /**\n   * URL подключения внешнего скрипта Google reCAPTCHA.\n   * @type {string}\n   */\n  static apiUrl = `https://www.google.com/recaptcha/api.js?render=explicit&onload=bubbleCaptchaAPIReady`;\n\n  /**\n   * Singleton-экземпляр менеджера загрузки API.\n   * @type {CaptchaApiManager|null}\n   */\n  static instance: CaptchaApiManager | null = null;\n\r\n  constructor() {\r\n    if (CaptchaApiManager.instance) {\r\n      return CaptchaApiManager.instance;\r\n    }\r\n    getWindow().bubbleCaptchaAPIReady = () => CaptchaApiManager.handleScriptReady();\r\n    CaptchaApiManager.instance = this;\r\n  }\r\n\r\n  static handleScriptReady() {\r\n    CaptchaCollection.state.isAPIReady = true;\r\n    bubble(getDocument(), CaptchaCollection.bubbles.ready);\r\n    if (getWindow()?.App?.isDebug) {\r\n      console.debug(\"[Captcha] API are ready\");\r\n    }\r\n  }\r\n\r\n  static async load(): Promise<unknown | void> {\r\n    if (CaptchaCollection.state.isScriptLoaded) {\r\n      return CaptchaApiManager.handleScriptReady();\r\n    } else if (!CaptchaCollection.state.isScriptLoading) {\r\n      CaptchaCollection.state.isScriptLoading = true;\r\n      return await getExternalScript({\r\n        isDefer: true,\r\n        src: CaptchaApiManager.apiUrl,\r\n      });\r\n    }\r\n  }\r\n\r\n  static handleScriptError(script: unknown) {\n    CaptchaCollection.state = {\n      ...CaptchaCollection.state,\n      isScriptLoaded: false,\n      isScriptLoading: false,\n      isAPIReady: false,\n    };\n    console.error(\"[Captcha] API are not loaded: \", script);\n  }\n\r\n  static handleScriptLoad(_script?: unknown) {\r\n    CaptchaCollection.state = {\r\n      ...CaptchaCollection.state,\r\n      isScriptLoaded: true,\r\n      isScriptLoading: false,\r\n    };\r\n  }\r\n\r\n}\r\n\r\n/**\r\n * Плагин, представляющий коллекцию Google Captcha.\r\n * Автоматически загружает API v2. Является коллекцией.\r\n * @module CaptchaCollection\r\n * @memberof Collection\r\n * @example\r\n * // <div class=\"gre\" data-js-google-captcha></div>\r\n */\r\nclass CaptchaCollection extends Collection<Captcha> {\n\n  /**\n   * CSS-селектор элементов captcha для автоматической инициализации коллекции.\n   * @type {string}\n   */\n  static selector = \"[data-js-google-captcha]\";\n\n  /**\n   * Набор всплывающих событий жизненного цикла captcha.\n   * @type {{mount: string, unmount: string, ready: string, render: string, reset: string, verify: string, expire: string, error: string}}\n   */\n  static bubbles = {\n    mount: \"collection::mount\",\n    unmount: \"collection::unmount\",\n    ready: \"googleCaptchaReady\",\r\n    render: \"googleCaptchaRender\",\r\n    reset: \"googleCaptchaReset\",\r\n    verify: \"googleCaptchaVerify\",\r\n    expire: \"googleCaptchaExpire\",\r\n    error: \"googleCaptchaError\",\n  };\n\n  /**\n   * Глобальное состояние загрузки скрипта и готовности reCAPTCHA API.\n   * @type {{isScriptLoaded: boolean, isScriptLoading: boolean, isAPIReady: boolean}}\n   */\n  static state = {\n    isScriptLoaded: false,\n    isScriptLoading: false,\n    isAPIReady: false,\r\n  };\r\n\r\n  constructor() {\r\n    new CaptchaApiManager();\r\n    super(CaptchaCollection.selector, Captcha);\r\n  }\r\n\r\n  getByDOMElement(DOMElement: Element): Captcha | null {\r\n    return this.collection.find((item) => item.instance === DOMElement && CaptchaCollection.state.isAPIReady) || null;\r\n  }\r\n\r\n  static loadAPI() {\r\n    if (!CaptchaCollection.state.isScriptLoading) {\r\n      CaptchaApiManager.load().then((script: unknown) => {\r\n        return CaptchaApiManager.handleScriptLoad(script);\r\n      }, (script: unknown) => {\r\n        CaptchaApiManager.handleScriptError(script);\r\n      });\r\n    }\r\n  }\r\n\r\n  init(context: Document | HTMLElement = getDocument()) {\r\n    const items = context.querySelectorAll(CaptchaCollection.selector);\r\n    if (items.length) {\r\n      if (CaptchaCollection.state.isAPIReady) {\r\n        items.forEach((el) => {\r\n          const instance = new Captcha(el as TCaptchaElement);\r\n          this.addToCollection(instance);\r\n        });\r\n      } else {\r\n        CaptchaCollection.loadAPI();\r\n      }\r\n    }\r\n  }\r\n\r\n  bindEvents() {\r\n    getDocument().addEventListener(CaptchaCollection.bubbles.ready, () => this.init());\r\n    Dispatcher.initiator = {\r\n      selector: CaptchaCollection.selector,\r\n      initiator: (context: Document | HTMLElement) => this.init(context),\r\n      getCollection: () => this.collection,\r\n    };\r\n  }\r\n\r\n}\r\n\r\nexport {\r\n  Captcha,\r\n  CaptchaCollection,\r\n};\r\n\r\n"],"names":["Captcha","stateClasses","rendered","expired","error","verify","defaultCfg","sitekey","getWindow","App","googleCaptchaKey","theme","callback","response","this","handleVerify","size","handleError","handleExpired","instance","state","cfg","constructor","_params","render","ready","CaptchaCollection","isAPIReady","getCfg","selector","bindEvents","isDebug","console","debug","loadAPI","classList","add","remove","constants","valid","bubble","getDocument","bubbles","expire","invalid","el","states","grecaptcha","getResponse","reset","e","handleReady","children","length","handleValidation","_event","formBubbles","inputValid","inputInvalid","forEach","eventName","addEventListener","CaptchaApiManager","static","bubbleCaptchaAPIReady","handleScriptReady","load","isScriptLoaded","isScriptLoading","getExternalScript","isDefer","src","apiUrl","handleScriptError","script","handleScriptLoad","_script","Collection","mount","unmount","super","getByDOMElement","DOMElement","collection","find","item","then","init","context","items","querySelectorAll","addToCollection","Dispatcher","initiator","getCollection"],"mappings":"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"}