xhr.coffee | |
---|---|
window.XMLHttpRequest | html = require("jsdom").dom.level3.html
http = require("http")
URL = require("url")
{ raise } = require("./helpers") |
Additional error codes defines for XHR and not in JSDOM. | html.SECURITY_ERR = 18
html.NETWORK_ERR = 19
html.ABORT_ERR = 20
XMLHttpRequest = (window)-> |
Fire onreadystatechange event | stateChanged = (state)=>
@__defineGetter__ "readyState", -> state
if @onreadystatechange |
Since we want to wait on these events, put them in the event loop. | window.browser._eventloop.perform (done)=>
process.nextTick =>
try
@onreadystatechange.call(@)
catch error
raise window.document, null, __filename, "XHR", error
finally
done() |
Bring XHR to initial state (open/abort). | reset = => |
Switch back to unsent state | @__defineGetter__ "readyState", -> 0
@__defineGetter__ "status", -> 0
@__defineGetter__ "statusText", -> |
These methods not applicable yet. | @abort = -> # do nothing
@setRequestHeader = @send = -> throw new html.DOMException(html.INVALID_STATE_ERR, "Invalid state")
@getResponseHeader = @getAllResponseHeaders = -> |
Open method. | @open = (method, url, async, user, password)->
method = method.toUpperCase()
throw new html.DOMException(html.SECURITY_ERR, "Unsupported HTTP method") if /^(CONNECT|TRACE|TRACK)$/.test(method)
throw new html.DOMException(html.SYNTAX_ERR, "Unsupported HTTP method") unless /^(DELETE|GET|HEAD|OPTIONS|POST|PUT)$/.test(method)
url = URL.parse(URL.resolve(window.location.href, url))
url.hostname ||= window.location.hostname
url.host = if url.port then "#{url.hostname}:#{url.port}" else url.hostname
url.hash = null
throw new html.DOMException(html.SECURITY_ERR, "Cannot make request to different domain") unless url.host == window.location.host
throw new html.DOMException(html.NOT_SUPPORTED_ERR, "Only HTTP/S protocol supported") unless url.protocol in ["http:", "https:"]
[user, password] = url.auth.split(":") if url.auth |
Aborting open request. | @_error = null
aborted = false
@abort = ->
aborted = true
reset()
headers = {}
@setRequestHeader = (header, value)-> headers[header.toString().toLowerCase()] = value.toString() |
Allow calling send method. | @send = (data)-> |
Aborting request in progress. | @abort = ->
aborted = true
@_error = new html.DOMException(html.ABORT_ERR, "Request aborted")
stateChanged 4
reset() |
Make the actual request: called again when dealing with a redirect. | window.browser.resources.request method, url, data, headers, (error, response)=>
if error
console.error "XHR error", error
@_error = new html.DOMException(html.NETWORK_ERR, error.message)
stateChanged 4
reset()
else |
At this state, allow retrieving of headers and status code. | @getResponseHeader = (header)-> response.headers[header.toLowerCase()]
@getAllResponseHeaders = -> |
# | XHR's getAllResponseHeaders, against all reason, returns a multi-line string.
See http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method |
# | headerStrings = for header, value of response.headers
"#{header}: #{value}"
return headerStrings.join("\n")
@__defineGetter__ "status", -> response.statusCode
@__defineGetter__ "statusText", -> response.statusText
stateChanged 2
unless aborted
@__defineGetter__ "responseText", -> response.body
@__defineGetter__ "responseXML", -> # not implemented
stateChanged 4 |
Calling open at this point aborts the ongoing request, resets the state and starts a new request going | @open = (method, url, async, user, password)->
@abort()
@open method, url, async, user, password |
Successfully completed open method | stateChanged 1
reset()
return
XMLHttpRequest.UNSENT = 0
XMLHttpRequest.OPENED = 1
XMLHttpRequest.HEADERS_RECEIVED = 2
XMLHttpRequest.LOADING = 3
XMLHttpRequest.DONE = 4
exports.use = -> |
Add XHR constructor to window. | extend = (window)->
window.XMLHttpRequest = -> XMLHttpRequest.call this, window
return extend: extend
|