class XHRData

constructor: (el) ->
  @backend = {}
  if el instanceof HTMLFormElement
    @form = el

set: (k, v) =>
  @backend[k] = v
  @keys().push(k)

value: (k) =>
  @backend[k]

has: (k) =>
  @backend[k]?

keys: =>
  store = []
  fn = ->
    store
  @keys = fn
  fn()

any: =>
  @keys().length > 0

serialize: =>
  formData = new FormData(@form)
  for key in @keys()
    formData.append key, @value(key)

  formData

urlEncoded: (action) =>
  parser = document.createElement('a')
  parser.href = action
  params = parser.search.substring(1).split('&')

  for key in @keys()
    params.push "#{key}=#{@value(key)}"

  if @form?
    for element in @form.elements
      unless element.hasAttribute('name')
        continue

      type = element.getAttribute("type").toUpperCase() || 'TEXT'
      if (type != 'FILE' && type != 'RADIO' && type != 'CHECKBOX') || element.checked
        params.push "#{encodeURIComponent(element.name)}=#{encodeURIComponent(element.value)}"

  parser.search = "?#{params.join("&")}"
  parser.href

class Response

constructor: (@xhr) ->
  @xhr.request.addEventListener 'load', @process
  @xhr.request.addEventListener 'error', @failure
  @xhr.request.addEventListener 'abort', @failure

process: (e) =>
  if e.target.status >= 200 && e.target.status < 300
    @success(e)
  else
    @failure(e)

success: (e) =>
  if e.target.responseText.length > 1
    eval(e.target.responseText)(e.target.element)

failure: (e) =>
  event = new CustomEvent("ObserveJS:XHR:Failed", {bubbles: true})
  event.response = e
  @xhr.request.element.dispatchEvent(event)

class XHR

script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"

@send: (el) ->

  xhr = new XHR(el)
  xhr.send()
  xhr

constructor: (el) ->
  @data = new XHRData(el)
  @request = new XMLHttpRequest()
  @request.element = el
  @response = new Response(this)

  @method = el.getAttribute('method') || 'GET'

open: (method, action) =>
  @request.open @method, action
  @request.setRequestHeader 'accept', "*/*;q=0.5, #{@script}"
  @request.setRequestHeader 'X-Requested-With', "XMLHttpRequest"

send: =>
  action = @request.element.getAttribute('action') || @request.element.getAttribute('href')

  unless action?
    throw "Cannot send a request to the server if the element isn't a Form or if the element doesn't have the HREF attributes: <div href>"
    return

  if /post|put|delete/i.test(@method)
    @open(@method, action)

    token = document.querySelector('meta[name=csrf-token]').getAttribute('content')
    @request.setRequestHeader 'X-CSRF-Token', token

    @request.send(@data.serialize())
  else
    action = @data.urlEncoded(action)
    @open(@method, action)
    @request.send()

ObserveJS.XHR = XHR

document.addEventListener ‘submit’, (e) =>

if e.target.getAttribute('disabled')? || e.target.dataset['remote'] != 'true'
  return

XHR.send(e.target)

e.preventDefault()
return false

document.addEventListener ‘click’, (e) =>

el = e.target
while el? && !(el instanceof HTMLAnchorElement)
  el = el.parentElement

return unless el?

if el.getAttribute('disabled')? || el.dataset['remote'] != 'true'
  return

XHR.send(el)

e.preventDefault()
return false