mirror of
https://github.com/hanzoai/form.js.git
synced 2026-09-30 21:50:42 +00:00
[email protected] — managed submission to api.hanzo.ai/v1/forms (opt-in pk/token), OnError event, drop dead xhr; backward-compatible
This commit is contained in:
@@ -9,6 +9,57 @@
|
||||
|
||||
> Automagic form validation and submission.
|
||||
|
||||
## Install
|
||||
|
||||
npm install form.js
|
||||
|
||||
Or drop it inside a `<form>` and let it wire itself to the surrounding fields:
|
||||
|
||||
<form>
|
||||
<script src="https://unpkg.com/form.js" data-key="pk_live_…"></script>
|
||||
<input name="email">
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
|
||||
## Submission
|
||||
|
||||
Without a key, form.js is a thin controller around the surrounding form: it
|
||||
emits lifecycle events and hands control back to your page. Set a Hanzo
|
||||
publishable key on the script tag and it POSTs the serialized form to the Hanzo
|
||||
forms ingest instead:
|
||||
|
||||
POST https://api.hanzo.ai/v1/forms
|
||||
Authorization: Bearer pk_live_…
|
||||
Content-Type: application/json
|
||||
|
||||
{ "email": "ada@example.com", "metadata": { "name": "Ada" } }
|
||||
|
||||
Script-tag options (all `data-*`):
|
||||
|
||||
| attribute | purpose |
|
||||
| --------------- | ------------------------------------------------------ |
|
||||
| `data-key` | publishable key (`pk_live_…` / `pk_test_…`) |
|
||||
| `data-token` | `@hanzo/iam` access token (takes precedence over key) |
|
||||
| `data-endpoint` | override the full submission URL |
|
||||
| `data-host` | override the API host (default `https://api.hanzo.ai`) |
|
||||
|
||||
## Events
|
||||
|
||||
`window.Inform` is an event emitter — listen with `Inform.on` / `Inform.once`:
|
||||
|
||||
| event | `Inform.Events` | arguments |
|
||||
| --------------------- | --------------- | ------------------------------------------------------- |
|
||||
| `init-inform-script` | `InitScript` | the `<script>` element |
|
||||
| `init-inform-form` | `InitForm` | the `<form>` element |
|
||||
| `init-inform-inputs` | `InitInputs` | array of input/select nodes |
|
||||
| `init-inform-submits` | `InitSubmits` | array of submit buttons |
|
||||
| `inform-submit` | `OnSubmit` | unmanaged: `(done, event)`; managed: `(payload, event)` |
|
||||
| `inform-done` | `OnDone` | the submit event, or the API response (managed) |
|
||||
| `inform-error` | `OnError` | submission error (managed) |
|
||||
|
||||
In unmanaged mode, call `done()` from your `OnSubmit` listener to let the native
|
||||
form submit proceed.
|
||||
|
||||
## License
|
||||
[BSD][license-url]
|
||||
|
||||
|
||||
Generated
+15050
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "form.js",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Automagic form validation and submission library.",
|
||||
"main": "lib/form.cjs.js",
|
||||
"module": "lib/form.es.js",
|
||||
@@ -29,24 +29,24 @@
|
||||
},
|
||||
"homepage": "https://github.com/hanzo-io/form.js#readme",
|
||||
"dependencies": {
|
||||
"little-emitter": "^0.1.3"
|
||||
"little-emitter": "^0.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cake-bundle": "^0.2.16",
|
||||
"cake-outdated": "^0.1.16",
|
||||
"cake-publish": "0.1.10",
|
||||
"cake-version": "0.1.11",
|
||||
"chai": "^3.1.0",
|
||||
"coffee-script": "^1.9.3",
|
||||
"connect": "^3.4.0",
|
||||
"mocha": "^3.2.0",
|
||||
"cake-bundle": "^0.2.21",
|
||||
"cake-outdated": "^0.1.18",
|
||||
"cake-publish": "^0.1.10",
|
||||
"cake-version": "^0.1.15",
|
||||
"chai": "^4.5.0",
|
||||
"coffee-script": "^1.12.7",
|
||||
"connect": "^3.7.0",
|
||||
"mocha": "^3.5.3",
|
||||
"phantomjs": "^2.1.7",
|
||||
"requisite": "^1.9.16",
|
||||
"selenium-standalone": "^6.1.0",
|
||||
"serve-static": "^1.10.0",
|
||||
"shortcake": "1.2.5",
|
||||
"uglify-js": "^2.4.23",
|
||||
"webdriverio": "^4.6.2"
|
||||
"requisite": "^1.19.17",
|
||||
"selenium-standalone": "^6.24.0",
|
||||
"serve-static": "^1.16.3",
|
||||
"shortcake": "^1.2.8",
|
||||
"uglify-js": "^2.8.29",
|
||||
"webdriverio": "^4.14.4"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublishOnly": "cake build",
|
||||
|
||||
+63
-365
@@ -1,4 +1,5 @@
|
||||
import Emitter from 'little-emitter'
|
||||
import submit from './submit'
|
||||
|
||||
events =
|
||||
InitScript: 'init-inform-script'
|
||||
@@ -7,75 +8,108 @@ events =
|
||||
InitSubmits: 'init-inform-submits'
|
||||
OnSubmit: 'inform-submit'
|
||||
OnDone: 'inform-done'
|
||||
OnError: 'inform-error'
|
||||
|
||||
do ->
|
||||
emitter = new Emitter
|
||||
emitter.Events = events
|
||||
emitter.events = events
|
||||
|
||||
# get this script tag
|
||||
getLastTag = (selector)->
|
||||
# find last script node
|
||||
# last tag of a kind — the running <script> and the <form> that wraps it
|
||||
getLastTag = (selector) ->
|
||||
tags = document.getElementsByTagName selector
|
||||
tags[tags.length - 1]
|
||||
|
||||
# last element is this tag
|
||||
tag = tags[tags.length - 1]
|
||||
return tag
|
||||
# read a data-* attribute off an element, coercing true/false
|
||||
attr = (el, name) ->
|
||||
val = el?.getAttribute?('data-' + name)
|
||||
return unless val?
|
||||
val = val.trim()
|
||||
switch val.toLowerCase()
|
||||
when 'false' then false
|
||||
when 'true' then true
|
||||
else val
|
||||
|
||||
script = getLastTag 'script'
|
||||
form = getLastTag 'form'
|
||||
# {email, metadata} from a set of inputs — email detected by field name
|
||||
serialize = (inputs) ->
|
||||
data = email: '', metadata: {}
|
||||
for input in inputs
|
||||
name = (input.getAttribute('name') ? input.id ? '').trim().toLowerCase()
|
||||
value = (input.value ? '').trim()
|
||||
continue if name is '' or value is ''
|
||||
if /email/.test name
|
||||
data.email = value
|
||||
else
|
||||
data.metadata[name] = value
|
||||
data
|
||||
|
||||
script = getLastTag 'script'
|
||||
form = getLastTag 'form'
|
||||
|
||||
first = true
|
||||
|
||||
init = ->
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
return
|
||||
return unless first
|
||||
first = false
|
||||
|
||||
# you need to get the attributes off these things manually
|
||||
emitter.emit events.InitScript, script
|
||||
emitter.emit events.InitForm, form
|
||||
|
||||
# submission target: publishable key (pk_…) or @hanzo/iam token, plus
|
||||
# optional endpoint/host overrides — all off the script tag
|
||||
config =
|
||||
key: attr(script, 'key') ? attr(script, 'token')
|
||||
endpoint: attr script, 'endpoint'
|
||||
host: attr script, 'host'
|
||||
|
||||
submits = []
|
||||
inputs = []
|
||||
ins = form.getElementsByTagName 'input'
|
||||
|
||||
for input in ins
|
||||
for input in form.getElementsByTagName 'input'
|
||||
if input.getAttribute('type') != 'submit'
|
||||
inputs.push input
|
||||
else
|
||||
submits.push input
|
||||
|
||||
ins = form.getElementsByTagName 'select'
|
||||
for input in ins
|
||||
for input in form.getElementsByTagName 'select'
|
||||
inputs.push input
|
||||
|
||||
emitter.emit events.InitInputs, inputs
|
||||
|
||||
buttons = form.getElementsByTagName 'button'
|
||||
for button in buttons
|
||||
type = button.getAttribute('type')
|
||||
if !type? || type == 'submit'
|
||||
submits.push(button)
|
||||
for button in form.getElementsByTagName 'button'
|
||||
type = button.getAttribute 'type'
|
||||
if !type? or type == 'submit'
|
||||
submits.push button
|
||||
|
||||
emitter.emit events.InitSubmits, submits
|
||||
|
||||
# managed: form.js POSTs to the Hanzo forms ingest. Unmanaged (no key or
|
||||
# endpoint configured): defer to the host, which calls done() to proceed.
|
||||
managed = config.key? or config.endpoint?
|
||||
|
||||
# Called on successful submit
|
||||
ondone = (event)->
|
||||
ondone = (event) ->
|
||||
emitter.emit events.OnDone, event
|
||||
|
||||
# Called when trying to submit
|
||||
onsubmit = (event)->
|
||||
if event.defaultPrevented
|
||||
return
|
||||
else
|
||||
event.preventDefault()
|
||||
onsubmit = (event) ->
|
||||
return if event.defaultPrevented
|
||||
event.preventDefault()
|
||||
|
||||
done = ()->
|
||||
if managed
|
||||
payload = serialize inputs
|
||||
emitter.emit events.OnSubmit, payload, event
|
||||
submit payload, config
|
||||
.then (res) -> emitter.emit events.OnDone, res
|
||||
.catch (err) -> emitter.emit events.OnError, err
|
||||
return
|
||||
|
||||
done = ->
|
||||
form.addEventListener 'submit', ondone
|
||||
form.removeEventListener 'submit', onsubmit
|
||||
|
||||
setTimeout ()->
|
||||
setTimeout ->
|
||||
form.dispatchEvent new Event 'submit',
|
||||
bubbles: false
|
||||
cancelable: true
|
||||
@@ -103,339 +137,3 @@ do ->
|
||||
window.attachEvent 'onload', init
|
||||
|
||||
return emitter
|
||||
|
||||
# do ->
|
||||
# `var endpoint = "%s", ml = %s`
|
||||
|
||||
# called = false
|
||||
# errors = null
|
||||
# forms = null
|
||||
# handlers = null
|
||||
# parent = null
|
||||
# script = null
|
||||
# selectors = {}
|
||||
# validate = false
|
||||
|
||||
# XHR = ->
|
||||
# xhr = null
|
||||
|
||||
# if window.XMLHttpRequest
|
||||
# xhr = new XMLHttpRequest()
|
||||
|
||||
# else if window.ActiveXObject
|
||||
# xhr = new ActiveXObject 'Microsoft.XMLHTTP'
|
||||
|
||||
# setHeaders: (headers) ->
|
||||
# for k,v of headers
|
||||
# xhr.setRequestHeader k, v
|
||||
# return
|
||||
|
||||
# post: (url, headers, payload, cb) ->
|
||||
# xhr.open 'POST', url, true
|
||||
# @setHeaders headers
|
||||
# xhr.send payload
|
||||
|
||||
# xhr.onreadystatechange = ->
|
||||
# if xhr.readyState == 4
|
||||
# if xhr.status == 200 or xhr.status == 201
|
||||
# cb null, xhr.status, xhr
|
||||
# else
|
||||
# cb (new Error 'Subscription failed'), xhr.status, xhr
|
||||
# return
|
||||
# return
|
||||
|
||||
# # get form container
|
||||
# getContainer = (script, selector = '')->
|
||||
# if selector != ''
|
||||
# document.querySelector selectors.container
|
||||
# else
|
||||
# parent = script.parentNode
|
||||
# inputs = parent.getElementsByTagName 'input'
|
||||
# if inputs.length < 1
|
||||
# document.body
|
||||
# else
|
||||
# parent
|
||||
|
||||
# # get this script tag
|
||||
# getScript = ->
|
||||
# # find last script node
|
||||
# scripts = document.getElementsByTagName( 'script' )
|
||||
|
||||
# # last element is this script tag
|
||||
# script = scripts[ scripts.length - 1 ]
|
||||
# script
|
||||
|
||||
# # Get elements from inside a parent
|
||||
# getElements = (parent, selector) ->
|
||||
# console.log 'getElements', parent, selector
|
||||
|
||||
# if selector? and selector != ''
|
||||
# parent.querySelectorAll selector
|
||||
# else
|
||||
# [parent]
|
||||
|
||||
# # get value from a selector
|
||||
# getValue = (parent = document.body, selector) ->
|
||||
# el = parent.querySelector selector
|
||||
# el?.value?.trim()
|
||||
|
||||
# # serialize a form
|
||||
# serializeForm = (el) ->
|
||||
# return {} unless el?
|
||||
|
||||
# data =
|
||||
# metadata: {}
|
||||
|
||||
# inputs = el.getElementsByTagName 'input'
|
||||
|
||||
# # Loop over form elements
|
||||
# for input in inputs
|
||||
# # Clean up inputs
|
||||
# k = input.name.trim().toLowerCase()
|
||||
# v = input.value.trim()
|
||||
|
||||
# # Skip inputs we don't care about
|
||||
# if k == '' or v == '' or (input.getAttribute 'type') == 'submit'
|
||||
# continue
|
||||
|
||||
# # Detect emails
|
||||
# if /email/.test k
|
||||
# data.email = v
|
||||
# else
|
||||
# data.metadata[k] = v
|
||||
|
||||
# # Use selectors if necessary
|
||||
# if selectors.email
|
||||
# data.email = getValue el, selectors.email
|
||||
# else
|
||||
# data.email ?= ''
|
||||
|
||||
# for prop in ['firstname', 'lastname', 'name']
|
||||
# if (selector = selectors[prop])?
|
||||
# data.metadata[prop] = getValue el, selector
|
||||
|
||||
# console.error 'Email is required' if data.email == ''
|
||||
|
||||
# data
|
||||
|
||||
# # get setting off script tag data attribute
|
||||
# attr = (name) ->
|
||||
# val = script.getAttribute 'data-' + name
|
||||
# return unless val?
|
||||
|
||||
# switch val.trim().toLowerCase()
|
||||
# when 'false'
|
||||
# false
|
||||
# when 'true'
|
||||
# true
|
||||
# else
|
||||
# val
|
||||
|
||||
# # Google event tracking code
|
||||
# google =
|
||||
# setup: ->
|
||||
# return if window.ga? or window._gaq?
|
||||
|
||||
# ((i, s, o, g, r, a, m) ->
|
||||
# i['GoogleAnalyticsObject'] = r
|
||||
# i[r] = i[r] or ->
|
||||
# (i[r].q = i[r].q or []).push arguments
|
||||
# return
|
||||
|
||||
# i[r].l = 1 * new Date()
|
||||
|
||||
# a = s.createElement(o)
|
||||
# m = s.getElementsByTagName(o)[0]
|
||||
|
||||
# a.async = 1
|
||||
# a.src = g
|
||||
# m.parentNode.insertBefore a, m
|
||||
# return
|
||||
# ) window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'
|
||||
# return
|
||||
|
||||
# track: (opts) ->
|
||||
# return unless opts.category?
|
||||
|
||||
# google.setup()
|
||||
|
||||
# category = opts.category ? 'Subscription'
|
||||
# action = opts.action ? opts.name ? 'Signup'
|
||||
# label = opts.label ? 'Lead'
|
||||
# value = opts.value ? 1
|
||||
|
||||
# if window._gaq?
|
||||
# window._gaq.push ['_trackEvent', category, action, label, value]
|
||||
# if window.ga?
|
||||
# window.ga 'send', 'event', category, action, label, value
|
||||
# return
|
||||
|
||||
# # Facebook event tracking
|
||||
# facebook =
|
||||
# setup: ->
|
||||
# return if window._fbq?.loaded
|
||||
|
||||
# _fbq = window._fbq or (window._fbq = [])
|
||||
|
||||
# fbds = document.createElement('script')
|
||||
# fbds.async = true
|
||||
# fbds.src = '//connect.facebook.net/en_US/fbds.js'
|
||||
# s = document.getElementsByTagName('script')[0]
|
||||
# s.parentNode.insertBefore fbds, s
|
||||
# _fbq.loaded = true
|
||||
# return
|
||||
|
||||
# track: (opts) ->
|
||||
# return unless opts.id?
|
||||
|
||||
# facebook.setup()
|
||||
|
||||
# value = opts.value ? '1.00'
|
||||
# currency = opts.currency ? 'USD'
|
||||
|
||||
# window._fbq.push ['track', opts.id,
|
||||
# value: value,
|
||||
# currency: currency,
|
||||
# ]
|
||||
# return
|
||||
|
||||
# # Trigger event tracking
|
||||
# track = ->
|
||||
# facebook.track ml.facebook
|
||||
# google.track ml.google
|
||||
# return
|
||||
|
||||
# # Wire up submit handler
|
||||
# addHandler = (el, errorEl) ->
|
||||
# unless errorEl?
|
||||
# errorEl = document.createElement 'div'
|
||||
# errorEl.className = 'crowdstart-mailinglist-error'
|
||||
# errorEl.style.display = 'none'
|
||||
# errorEl.style.width = 100 + '%%'[0] # Prevents interpolation from picking up % as a thing needing interpolatin'
|
||||
# el.appendChild errorEl
|
||||
|
||||
# showError = (msg) ->
|
||||
# errorEl.style.display = 'inline'
|
||||
# errorEl.innerHTML = msg
|
||||
# false
|
||||
|
||||
# hideError = ->
|
||||
# errorEl.style.display = 'none'
|
||||
|
||||
# thankYou = ->
|
||||
# switch ml.thankyou.type
|
||||
# when 'redirect'
|
||||
# setTimeout ->
|
||||
# window.location = ml.thankyou.url
|
||||
# , 1000
|
||||
# when 'html'
|
||||
# el.innerHTML = ml.thankyou.html
|
||||
|
||||
# if document.createEvent && document.dispatchEvent
|
||||
# event = document.createEvent 'Event'
|
||||
# event.initEvent 'thankyou', true, true
|
||||
# document.dispatchEvent event
|
||||
# else
|
||||
# console.log "Could not create or dispatch thankyou event"
|
||||
|
||||
# submitHandler = (ev) ->
|
||||
# if ev.defaultPrevented
|
||||
# return
|
||||
# else
|
||||
# ev.preventDefault()
|
||||
|
||||
# data = serializeForm el
|
||||
# console.log data
|
||||
|
||||
# if ml.validate
|
||||
# unless data.email?
|
||||
# return showError 'Email is required'
|
||||
# if (data.email.indexOf '@') == -1
|
||||
# return showError 'Invalid email'
|
||||
# if data.email.length < 3
|
||||
# return showError 'Invalid email'
|
||||
# hideError()
|
||||
|
||||
# payload = JSON.stringify data
|
||||
|
||||
# headers =
|
||||
# 'X-Requested-With': 'XMLHttpRequest',
|
||||
# 'Content-type': 'application/json; charset=utf-8'
|
||||
|
||||
# xhr = XHR()
|
||||
# xhr.post endpoint, headers, payload, (err, status, xhr) ->
|
||||
# return thankYou() if status == 409
|
||||
# return showError(err) if err?
|
||||
|
||||
# # Fire tracking pixels
|
||||
# track()
|
||||
# thankYou()
|
||||
|
||||
# false
|
||||
|
||||
# (ev) ->
|
||||
# el.removeEventListener 'submit', addHandler
|
||||
# el.addEventListener 'submit', submitHandler
|
||||
|
||||
# setTimeout ->
|
||||
# el.dispatchEvent new Event 'submit',
|
||||
# bubbles: false
|
||||
# cancelable: true
|
||||
# , 500
|
||||
|
||||
# ev.preventDefault()
|
||||
# false
|
||||
|
||||
# # Init all the things
|
||||
# init = ->
|
||||
# if called then return else called = true
|
||||
|
||||
# props = ['container', 'forms', 'submits', 'errors', 'email', 'firstname', 'lastname', 'name']
|
||||
# for prop in props
|
||||
# selectors[prop] = (attr prop) ? ml.selectors?[prop]
|
||||
|
||||
# # default selector for submit button
|
||||
# selectors.submits ?= 'input[type="submit"]'
|
||||
|
||||
# # are we validating?
|
||||
# ml.validate ?= (attr 'validate') ? false
|
||||
|
||||
# parent = getContainer script, selectors.container
|
||||
# forms = getElements parent, selectors.forms
|
||||
# handlers = getElements parent, selectors.submits
|
||||
|
||||
# # find error divs
|
||||
# if selectors.errors
|
||||
# errors = getElements parent, selectors.errors
|
||||
# else
|
||||
# errors = []
|
||||
|
||||
# console.log 'selectors', selectors
|
||||
# console.log 'forms', forms
|
||||
# console.log 'handlers', handlers
|
||||
# console.log 'errors', errors
|
||||
|
||||
# for handler, i in handlers
|
||||
# do (handler, i) ->
|
||||
# return if handler.getAttribute 'data-hijacked'
|
||||
|
||||
# handler.setAttribute 'data-hijacked', true
|
||||
# handler.addEventListener 'click', (addHandler forms[i], errors[i])
|
||||
# handler.addEventListener 'submit', (addHandler forms[i], errors[i])
|
||||
|
||||
# # Get script tag, has to run before rest of DOM loads
|
||||
# script = getScript()
|
||||
|
||||
# # Run init after DOM loads, attach various listeners
|
||||
# if document.addEventListener
|
||||
# document.addEventListener 'DOMContentLoaded', init, false
|
||||
# else if document.attachEvent
|
||||
# document.attachEvent 'onreadystatechange', ->
|
||||
# init() if document.readyState == 'complete'
|
||||
|
||||
# if window.addEventListener
|
||||
# window.addEventListener 'load', init, false
|
||||
# else if window.attachEvent
|
||||
# window.attachEvent 'onload', init
|
||||
|
||||
# return
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Hanzo forms ingest.
|
||||
#
|
||||
# POST https://api.hanzo.ai/v1/forms
|
||||
#
|
||||
# Auth: Authorization: Bearer <token>, where <token> is an @hanzo/iam access
|
||||
# token when present, otherwise a publishable key (pk_live_… / pk_test_…). The
|
||||
# server stamps org/tenant from the validated credential; the client only ships
|
||||
# the serialized submission.
|
||||
|
||||
DEFAULT_HOST = 'https://api.hanzo.ai'
|
||||
FORMS_PATH = '/v1/forms'
|
||||
|
||||
export host = DEFAULT_HOST
|
||||
export path = FORMS_PATH
|
||||
|
||||
# POST a serialized submission. Returns a Promise that resolves on a 2xx
|
||||
# response and rejects otherwise. `opts.token` (IAM access token) wins over
|
||||
# `opts.key` (publishable key); `opts.endpoint` overrides host + path.
|
||||
export default (data, opts = {}) ->
|
||||
base = (opts.host ? DEFAULT_HOST).replace /\/+$/, ''
|
||||
url = opts.endpoint ? base + FORMS_PATH
|
||||
token = opts.token ? opts.key
|
||||
body = JSON.stringify data
|
||||
|
||||
headers = 'Content-Type': 'application/json'
|
||||
headers.Authorization = "Bearer #{token}" if token
|
||||
|
||||
if typeof fetch is 'function'
|
||||
return fetch url,
|
||||
method: 'POST'
|
||||
headers: headers
|
||||
body: body
|
||||
credentials: 'include'
|
||||
.then (res) ->
|
||||
throw new Error "form.js: submission failed (#{res.status})" unless res.ok
|
||||
res
|
||||
|
||||
new Promise (resolve, reject) ->
|
||||
xhr = new XMLHttpRequest
|
||||
xhr.open 'POST', url, true
|
||||
xhr.withCredentials = true
|
||||
xhr.setRequestHeader k, v for k, v of headers
|
||||
xhr.onreadystatechange = ->
|
||||
return unless xhr.readyState is 4
|
||||
if 200 <= xhr.status < 300
|
||||
resolve xhr
|
||||
else
|
||||
reject new Error "form.js: submission failed (#{xhr.status})"
|
||||
xhr.send body
|
||||
@@ -1,28 +0,0 @@
|
||||
module.exports = class XHR
|
||||
constructor: ->
|
||||
xhr = null
|
||||
|
||||
if window.XMLHttpRequest
|
||||
xhr = new XMLHttpRequest()
|
||||
|
||||
else if window.ActiveXObject
|
||||
xhr = new ActiveXObject 'Microsoft.XMLHTTP'
|
||||
|
||||
setHeaders: (headers) ->
|
||||
for k,v of headers
|
||||
@xhr.setRequestHeader k, v
|
||||
return
|
||||
|
||||
post: (url, headers, payload, cb) ->
|
||||
@xhr.open 'POST', url, true
|
||||
@setHeaders headers
|
||||
@xhr.send payload
|
||||
|
||||
@xhr.onreadystatechange = =>
|
||||
if @xhr.readyState == 4
|
||||
if @xhr.status == 200 or @xhr.status == 201
|
||||
cb null, @xhr.status, @xhr
|
||||
else
|
||||
cb (new Error 'Subscription failed'), @xhr.status, @xhr
|
||||
return
|
||||
return
|
||||
Reference in New Issue
Block a user