// <details> polyfill // caniuse.com/#feat=details

// FF Support for HTML5's <details> and <summary> // bugzilla.mozilla.org/show_bug.cgi?id=591737

// www.sitepoint.com/fixing-the-details-element/

;(function (global) {

'use strict'

var GOVUK = global.GOVUK || {}

GOVUK.details = {
  NATIVE_DETAILS: typeof document.createElement('details').open === 'boolean',
  KEY_ENTER: 13,
  KEY_SPACE: 32,

  // Create a started flag so we can prevent the initialisation
  // function firing from both DOMContentLoaded and window.onload
  started: false,

  // Add event construct for modern browsers or IE
  // which fires the callback with a pre-converted target reference
  addEvent: function (node, type, callback) {
    if (node.addEventListener) {
      node.addEventListener(type, function (e) {
        callback(e, e.target)
      }, false)
    } else if (node.attachEvent) {
      node.attachEvent('on' + type, function (e) {
        callback(e, e.srcElement)
      })
    }
  },

  removeEvent: function (node, type) {
    if (node.removeEventListener) {
      node.removeEventListener(type, function (e) {
      }, false)
    } else if (node.detachEvent) {
      node.detachEvent('on' + type, function (e) {
      })
    }
  },

  // Cross-browser character code / key pressed
  charCode: function (e) {
    return (typeof e.which === 'number') ? e.which : e.keyCode
  },

  // Cross-browser preventing default action
  preventDefault: function (e) {
    if (e.preventDefault) {
      e.preventDefault()
    } else {
      e.returnValue = false
    }
  },

  // Handle cross-modal click events
  addClickEvent: function (node, callback) {
    GOVUK.details.addEvent(node, 'keypress', function (e, target) {
      // When the key gets pressed - check if it is enter or space
      if (GOVUK.details.charCode(e) === GOVUK.details.KEY_ENTER || GOVUK.details.charCode(e) === GOVUK.details.KEY_SPACE) {
        if (target.nodeName.toLowerCase() === 'summary') {
          // Prevent space from scrolling the page
          // and enter from submitting a form
          GOVUK.details.preventDefault(e)
          // Click to let the click event do all the necessary action
          if (target.click) {
            target.click()
          } else {
            // except Safari 5.1 and under don't support .click() here
            callback(e, target)
          }
        }
      }
    })

    // Prevent keyup to prevent clicking twice in Firefox when using space key
    GOVUK.details.addEvent(node, 'keyup', function (e, target) {
      if (GOVUK.details.charCode(e) === GOVUK.details.KEY_SPACE) {
        if (target.nodeName === 'SUMMARY') {
          GOVUK.details.preventDefault(e)
        }
      }
    })

    GOVUK.details.addEvent(node, 'click', function (e, target) {
      callback(e, target)
    })
  },

  // Get the nearest ancestor element of a node that matches a given tag name
  getAncestor: function (node, match) {
    do {
      if (!node || node.nodeName.toLowerCase() === match) {
        break
      }
      node = node.parentNode
    } while (node)

    return node
  },

  // Initialisation function
  addDetailsPolyfill: function (list, container) {
    container = container || document.body
    // If this has already happened, just return
    // else set the flag so it doesn't happen again
    if (GOVUK.details.started) {
      return
    }
    GOVUK.details.started = true
    // Get the collection of details elements, but if that's empty
    // then we don't need to bother with the rest of the scripting
    if ((list = container.getElementsByTagName('details')).length === 0) {
      return
    }
    // else iterate through them to apply their initial state
    var n = list.length
    var i = 0
    for (i; i < n; i++) {
      var details = list[i]

      // Save shortcuts to the inner summary and content elements
      details.__summary = details.getElementsByTagName('summary').item(0)
      details.__content = details.getElementsByTagName('div').item(0)

      if (!details.__summary || !details.__content) {
        return
      }
      // If the content doesn't have an ID, assign it one now
      // which we'll need for the summary's aria-controls assignment
      if (!details.__content.id) {
        details.__content.id = 'details-content-' + i
      }

      // Add ARIA role="group" to details
      details.setAttribute('role', 'group')

      // Add role=button to summary
      details.__summary.setAttribute('role', 'button')

      // Add aria-controls
      details.__summary.setAttribute('aria-controls', details.__content.id)

      // Set tabIndex so the summary is keyboard accessible for non-native elements
      // http://www.saliences.com/browserBugs/tabIndex.html
      if (!GOVUK.details.NATIVE_DETAILS) {
        details.__summary.tabIndex = 0
      }

      // Detect initial open state
      var openAttr = details.getAttribute('open') !== null
      if (openAttr === true) {
        details.__summary.setAttribute('aria-expanded', 'true')
        details.__content.setAttribute('aria-hidden', 'false')
      } else {
        details.__summary.setAttribute('aria-expanded', 'false')
        details.__content.setAttribute('aria-hidden', 'true')
        if (!GOVUK.details.NATIVE_DETAILS) {
          details.__content.style.display = 'none'
        }
      }

      // Create a circular reference from the summary back to its
      // parent details element, for convenience in the click handler
      details.__summary.__details = details

      // If this is not a native implementation, create an arrow
      // inside the summary
      if (!GOVUK.details.NATIVE_DETAILS) {
        var twisty = document.createElement('i')

        if (openAttr === true) {
          twisty.className = 'arrow arrow-open'
          twisty.appendChild(document.createTextNode('\u25bc'))
        } else {
          twisty.className = 'arrow arrow-closed'
          twisty.appendChild(document.createTextNode('\u25ba'))
        }

        details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild)
        details.__summary.__twisty.setAttribute('aria-hidden', 'true')
      }
    }

    // Bind a click event to handle summary elements
    GOVUK.details.addClickEvent(container, function (e, summary) {
      if (!(summary = GOVUK.details.getAncestor(summary, 'summary'))) {
        return true
      }
      return GOVUK.details.statechange(summary)
    })
  },

  // Define a statechange function that updates aria-expanded and style.display
  // Also update the arrow position
  statechange: function (summary) {
    var expanded = summary.__details.__summary.getAttribute('aria-expanded') === 'true'
    var hidden = summary.__details.__content.getAttribute('aria-hidden') === 'true'

    summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true'))
    summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true'))

    if (!GOVUK.details.NATIVE_DETAILS) {
      summary.__details.__content.style.display = (expanded ? 'none' : '')

      var hasOpenAttr = summary.__details.getAttribute('open') !== null
      if (!hasOpenAttr) {
        summary.__details.setAttribute('open', 'open')
      } else {
        summary.__details.removeAttribute('open')
      }
    }

    if (summary.__twisty) {
      summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc')
      summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open'))
    }

    return true
  },

  destroy: function (node) {
    GOVUK.details.removeEvent(node, 'click')
  },

  // Bind two load events for modern and older browsers
  // If the first one fires it will set a flag to block the second one
  // but if it's not supported then the second one will fire
  init: function ($container) {
    GOVUK.details.addEvent(document, 'DOMContentLoaded', GOVUK.details.addDetailsPolyfill)
    GOVUK.details.addEvent(window, 'load', GOVUK.details.addDetailsPolyfill)
  }
}
global.GOVUK = GOVUK

})(window)