|
|
|
MIME_TYPES =
|
|
|
|
json: 'application/json'
|
|
|
|
html: 'text/html'
|
|
|
|
|
|
|
|
@ajax = (options) ->
|
|
|
|
applyDefaults(options)
|
|
|
|
serializeData(options)
|
|
|
|
|
|
|
|
xhr = new XMLHttpRequest()
|
|
|
|
xhr.open(options.type, options.url, options.async)
|
|
|
|
|
|
|
|
applyCallbacks(xhr, options)
|
|
|
|
applyHeaders(xhr, options)
|
|
|
|
|
|
|
|
xhr.send(options.data)
|
|
|
|
|
|
|
|
if options.async
|
|
|
|
abort: abort.bind(undefined, xhr)
|
|
|
|
else
|
|
|
|
parseResponse(xhr, options)
|
|
|
|
|
|
|
|
ajax.defaults =
|
|
|
|
async: true
|
|
|
|
dataType: 'json'
|
|
|
|
timeout: 30
|
|
|
|
type: 'GET'
|
|
|
|
# contentType
|
|
|
|
# context
|
|
|
|
# data
|
|
|
|
# error
|
|
|
|
# headers
|
|
|
|
# progress
|
|
|
|
# success
|
|
|
|
# url
|
|
|
|
|
|
|
|
applyDefaults = (options) ->
|
|
|
|
for key of ajax.defaults
|
|
|
|
options[key] ?= ajax.defaults[key]
|
|
|
|
return
|
|
|
|
|
|
|
|
serializeData = (options) ->
|
|
|
|
return unless options.data
|
|
|
|
|
|
|
|
if options.type is 'GET'
|
|
|
|
options.url += '?' + serializeParams(options.data)
|
|
|
|
options.data = null
|
|
|
|
else
|
|
|
|
options.data = serializeParams(options.data)
|
|
|
|
return
|
|
|
|
|
|
|
|
serializeParams = (params) ->
|
|
|
|
("#{encodeURIComponent key}=#{encodeURIComponent value}" for key, value of params).join '&'
|
|
|
|
|
|
|
|
applyCallbacks = (xhr, options) ->
|
|
|
|
return unless options.async
|
|
|
|
|
|
|
|
xhr.timer = setTimeout onTimeout.bind(undefined, xhr, options), options.timeout * 1000
|
|
|
|
xhr.onprogress = options.progress if options.progress
|
|
|
|
xhr.onreadystatechange = ->
|
|
|
|
if xhr.readyState is 4
|
|
|
|
clearTimeout(xhr.timer)
|
|
|
|
onComplete(xhr, options)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
applyHeaders = (xhr, options) ->
|
|
|
|
options.headers or= {}
|
|
|
|
|
|
|
|
if options.contentType
|
|
|
|
options.headers['Content-Type'] = options.contentType
|
|
|
|
|
|
|
|
if not options.headers['Content-Type'] and options.data and options.type isnt 'GET'
|
|
|
|
options.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
|
|
|
|
|
|
|
if options.dataType
|
|
|
|
options.headers['Accept'] = MIME_TYPES[options.dataType] or options.dataType
|
|
|
|
|
|
|
|
if isSameOrigin(options.url)
|
|
|
|
options.headers['X-Requested-With'] = 'XMLHttpRequest'
|
|
|
|
|
|
|
|
for key, value of options.headers
|
|
|
|
xhr.setRequestHeader(key, value)
|
|
|
|
return
|
|
|
|
|
|
|
|
onComplete = (xhr, options) ->
|
|
|
|
if 200 <= xhr.status < 300
|
|
|
|
if (response = parseResponse(xhr, options))?
|
|
|
|
onSuccess response, xhr, options
|
|
|
|
else
|
|
|
|
onError 'invalid', xhr, options
|
|
|
|
else
|
|
|
|
onError 'error', xhr, options
|
|
|
|
return
|
|
|
|
|
|
|
|
onSuccess = (response, xhr, options) ->
|
|
|
|
options.success?.call options.context, response, xhr, options
|
|
|
|
return
|
|
|
|
|
|
|
|
onError = (type, xhr, options) ->
|
|
|
|
options.error?.call options.context, type, xhr, options
|
|
|
|
return
|
|
|
|
|
|
|
|
onTimeout = (xhr, options) ->
|
|
|
|
xhr.abort()
|
|
|
|
onError 'timeout', xhr, options
|
|
|
|
return
|
|
|
|
|
|
|
|
abort = (xhr) ->
|
|
|
|
clearTimeout(xhr.timer)
|
|
|
|
xhr.onreadystatechange = null
|
|
|
|
xhr.abort()
|
|
|
|
return
|
|
|
|
|
|
|
|
isSameOrigin = (url) ->
|
|
|
|
url.indexOf('http') isnt 0 or url.indexOf(location.origin) is 0
|
|
|
|
|
|
|
|
parseResponse = (xhr, options) ->
|
|
|
|
if options.dataType is 'json'
|
|
|
|
parseJSON(xhr.responseText)
|
|
|
|
else
|
|
|
|
xhr.responseText
|
|
|
|
|
|
|
|
parseJSON = (json) ->
|
|
|
|
try JSON.parse(json) catch
|