Captcha.v2 =
  lifetime: 2 * $.MINUTE

  init: ->
    return if d.cookie.indexOf('pass_enabled=1') >= 0
    return if not (@isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript') or !$.id('postForm'))

    if (@noscript = Conf['Force Noscript Captcha'] or not Main.jsEnabled)
      $.addClass QR.nodes.el, 'noscript-captcha'

    Captcha.cache.init()
    $.on d, 'CaptchaCount', @count.bind(@)

    root = $.el 'div', className: 'captcha-root'
    $.extend root, `<%= html(
      '<div class="captcha-counter"><a href="javascript:;"></a></div>'
    ) %>`
    counter   = $ '.captcha-counter > a', root
    @nodes = {root, counter}
    @count()
    $.addClass QR.nodes.el, 'has-captcha', 'captcha-v2'
    $.after QR.nodes.com.parentNode, root

    $.on counter, 'click', @toggle.bind @
    $.on counter, 'keydown', (e) =>
      return unless Keybinds.keyCode(e) is 'Space'
      @toggle()
      e.preventDefault()
      e.stopPropagation()
    $.on window, 'captcha:success', =>
      # XXX Greasemonkey 1.x workaround to gain access to GM_* functions.
      $.queueTask => @save false

  timeouts: {}
  prevNeeded: 0

  noscriptURL: ->
    url = 'https://www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>'
    if (lang = Conf['captchaLanguage'].trim())
      url += "&hl=#{encodeURIComponent lang}"
    url

  moreNeeded: ->
    # Post count temporarily off by 1 when called from QR.post.rm, QR.close, or QR.submit
    $.queueTask =>
      needed = Captcha.cache.needed()
      if needed and not @prevNeeded
        @setup(QR.cooldown.auto and d.activeElement is QR.nodes.status)
      @prevNeeded = needed

  toggle: ->
    if @nodes.container and !@timeouts.destroy
      @destroy()
    else
      @setup true, true

  setup: (focus, force) ->
    return unless @isEnabled and (Captcha.cache.needed() or force)

    if focus
      $.addClass QR.nodes.el, 'focus'
      @nodes.counter.focus()

    if @timeouts.destroy
      clearTimeout @timeouts.destroy
      delete @timeouts.destroy
      return @reload()

    if @nodes.container
      # XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1226835
      $.queueTask =>
        if @nodes.container and d.activeElement is @nodes.counter and (iframe = $ 'iframe', @nodes.container)
          iframe.focus()
          QR.focus() # Event handler not fired in Firefox
      return

    @nodes.container = $.el 'div', className: 'captcha-container'
    $.prepend @nodes.root, @nodes.container
    new MutationObserver(@afterSetup.bind @).observe @nodes.container,
      childList: true
      subtree: true

    if @noscript
      @setupNoscript()
    else
      @setupJS()

  setupNoscript: ->
    iframe = $.el 'iframe',
      id: 'qr-captcha-iframe'
      scrolling: 'no'
      src: @noscriptURL()
    div = $.el 'div'
    textarea = $.el 'textarea'
    $.add div, textarea
    $.add @nodes.container, [iframe, div]

  setupJS: ->
    $.global ->
      render = ->
        {classList} = document.documentElement
        container = document.querySelector '#qr .captcha-container'
        container.dataset.widgetID = window.grecaptcha.render container,
          sitekey:  '<%= meta.recaptchaKey %>'
          theme:    if classList.contains('tomorrow') or classList.contains('spooky') or classList.contains('dark-captcha') then 'dark' else 'light'
          callback: (response) ->
            window.dispatchEvent new CustomEvent('captcha:success', {detail: response})
      if window.grecaptcha
        render()
      else
        cbNative = window.onRecaptchaLoaded
        window.onRecaptchaLoaded = ->
          render()
          cbNative()
        unless document.head.querySelector 'script[src^="https://www.google.com/recaptcha/api.js"]'
          script = document.createElement 'script'
          script.src = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoaded&render=explicit'
          document.head.appendChild script

  afterSetup: (mutations) ->
    for mutation in mutations
      for node in mutation.addedNodes
        @setupIFrame   iframe   if (iframe   = $.x './descendant-or-self::iframe',   node)
        @setupTextArea textarea if (textarea = $.x './descendant-or-self::textarea', node)
    return

  setupIFrame: (iframe) ->
    return unless doc.contains iframe
    Captcha.replace.iframe iframe
    $.addClass QR.nodes.el, 'captcha-open'
    @fixQRPosition()
    $.on iframe, 'load', @fixQRPosition
    iframe.focus() if d.activeElement is @nodes.counter
    # XXX Make sure scroll on space prevention (see src/css/style.css) doesn't cause scrolling of div
    if $.engine in ['blink', 'edge'] and iframe.parentNode in $$('#qr .captcha-container > div > div:first-of-type')
      $.on iframe.parentNode, 'scroll', -> @scrollTop = 0

  fixQRPosition: ->
    if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight
      QR.nodes.el.style.top    = ''
      QR.nodes.el.style.bottom = '0px'

  setupTextArea: (textarea) ->
    $.one textarea, 'input', => @save true

  destroy: ->
    return unless @isEnabled
    delete @timeouts.destroy
    $.rmClass QR.nodes.el, 'captcha-open'
    if @nodes.container
      $.global ->
        container = document.querySelector '#qr .captcha-container'
        window.grecaptcha.reset container.dataset.widgetID
      $.rm @nodes.container
      delete @nodes.container

  getOne: (isReply) ->
    Captcha.cache.getOne isReply

  save: (pasted, token) ->
    Captcha.cache.save
      response: token or $('textarea', @nodes.container).value
      timeout:  Date.now() + @lifetime

    focus = d.activeElement?.nodeName is 'IFRAME' and /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src)
    if Captcha.cache.needed()
      if focus
        if QR.cooldown.auto or Conf['Post on Captcha Completion']
          @nodes.counter.focus()
        else
          QR.nodes.status.focus()
      @reload()
    else
      if pasted
        @destroy()
      else
        @timeouts.destroy ?= setTimeout @destroy.bind(@), 3 * $.SECOND
      QR.nodes.status.focus() if focus

    QR.submit() if Conf['Post on Captcha Completion'] and !QR.cooldown.auto

  count: ->
    count = Captcha.cache.getCount()
    loading = if Captcha.cache.submitCB then '...' else ''
    @nodes.counter.textContent = "Captchas: #{count}#{loading}"
    @moreNeeded()

  reload: ->
    if $ 'iframe[src^="https://www.google.com/recaptcha/api/fallback?"]', @nodes.container
      @destroy()
      @setup false, true
    else
      $.global ->
        container = document.querySelector '#qr .captcha-container'
        window.grecaptcha.reset container.dataset.widgetID

  occupied: ->
    !!@nodes.container and !@timeouts.destroy
