UNPKG

5 kBJavaScriptView Raw
1var qs = require('./querystring.js')
2
3var METHODS = ['put', 'head', 'patch', 'delete', 'post', 'get']
4var CONTENT_TYPE = 'Content-Type'
5var CONTENT_TYPE_FORM = 'application/x-www-form-urlencoded'
6var CONTENT_TYPE_JSON = 'application/json; charset=utf-8'
7
8function combineUrl (base, newurl) {
9 base = norm(base)
10 newurl = norm(newurl)
11
12 if (!newurl || !base) return base + newurl
13
14 if (newurl.startsWith('http://') || newurl.startsWith('https://') || newurl.startsWith('//')) {
15 return newurl
16 }
17
18 if (!base.endsWith('/')) base += '/'
19 if (newurl.startsWith('/')) newurl = newurl.substring(1)
20 return base + newurl
21}
22
23function merge (req, obj) {
24 return Object.assign(req.clone(), obj)
25}
26
27function newRequest () {
28 var r = {
29 parser: '',
30 beforehooks: [],
31 afterhooks: [],
32 baseurl: '',
33 query: {},
34 meta: {},
35 }
36
37 r.clone = function () {
38 return Object.assign({}, this, {
39 query: Object.assign({}, this.query),
40 meta: Object.assign({}, this.meta),
41 })
42 }
43
44 r.addQuery = function (key, val) {
45 var req = this.clone()
46 req.query[key] = val
47 return req
48 }
49
50 r.removeQuery = function (key) {
51 var req = this.clone()
52 if (req.query[key] !== undefined) req.query[key] = undefined
53 return req
54 }
55
56 r.setQuery = function (query) {
57 return merge(this, { query: query })
58 }
59
60 r.clearHooks = function () {
61 return merge(this, { beforehooks: [], afterhooks: [] })
62 }
63
64 r.beforeHook = function (cb) {
65 var beforehooks = this.beforehooks.slice()
66 beforehooks.push(cb)
67 return merge(this, { beforehooks: beforehooks })
68 }
69
70 r.afterHook = function (cb) {
71 var afterhooks = this.afterhooks.slice()
72 afterhooks.push(cb)
73 return merge(this, { afterhooks: afterhooks })
74 }
75
76 r.setHeader = function (headers) {
77 headers = Object.assign({}, this.headers, headers)
78 headers[CONTENT_TYPE] = undefined
79 return merge(this, { headers: headers })
80 }
81
82 METHODS.map(function (method) {
83 r[method] = function (url, data, cb) {
84 return send(merge(this, { method: method, baseurl: combineUrl(this.baseurl, url) }), data, cb)
85 }
86 })
87
88 // pass // to clean
89 r.setBaseUrl = function (url) {
90 return merge(this, { baseurl: url })
91 }
92
93 r.contentTypeJson = function () {
94 return merge(this, { content_type: CONTENT_TYPE_JSON })
95 }
96
97 r.contentTypeForm = function () {
98 return merge(this, { content_type: CONTENT_TYPE_FORM })
99 }
100
101 r.setContentType = function (ty) {
102 return merge(this, { content_type: norm(ty) })
103 }
104
105 r.setParser = function (parser) {
106 return merge(this, { parser: norm(parser) })
107 }
108
109 r.setBody = function (body) {
110 return merge(this, { body: body })
111 }
112
113 r.setMeta = function (k, v) {
114 var req = this.clone()
115 req.meta[k] = v
116 return req
117 }
118
119 return r
120}
121
122function send (req, data, cb) {
123 cb = cb || function () {}
124 if (isFunc(data)) {
125 cb = data
126 data = undefined
127 }
128
129 var rs
130 var promise = new Promise(function (resolve) {
131 rs = function (res) {
132 try {
133 cb(res.error, res.body, res.code)
134 } catch (_) {}
135 resolve(res)
136 }
137 })
138
139 if (data) {
140 req = req.clone()
141 req.body = data
142 if (req.content_type === CONTENT_TYPE_JSON) req.body = env.Jsonify(data)
143 if (req.content_type === CONTENT_TYPE_FORM) {
144 req.body = qs.stringify(data)
145 }
146 }
147
148 waterfall(req.beforehooks.slice(), { request: req }, function (bp) {
149 if (bp.error) return rs({ body: undefined, code: 0, error: bp.error })
150 dosend(bp.request, function (err, body, code) {
151 waterfall(req.afterhooks.slice(), { request: req, code: code, body: body, err: err }, function (param) {
152 var body = param.body
153
154 if (req.parser == 'json' && param.body) {
155 try {
156 body = env.ParseJson(param.body)
157 } catch (e) {
158 param.err = param.err || 'invalid json'
159 }
160 }
161 var err = param.err
162 if (code !== 200) err = 'not 200'
163 rs({ body: body, code: param.code, error: err })
164 })
165 })
166 })
167 return promise
168}
169
170var dosend = function (req, cb) {
171 var q = qs.stringify(req.query)
172 if (q) q = '?' + q
173
174 var request = new env.XMLHttpRequest()
175 request.onreadystatechange = function (e) {
176 if (request.readyState !== 4) return
177 if (request.status === 0) {
178 cb('network_error', request.responseText, request.status) // network error
179 } else {
180 cb(undefined, request.responseText, request.status)
181 }
182 }
183
184 request.open(req.method, req.baseurl + q)
185 for (var i in req.headers) request.setRequestHeader(i, req.headers[i])
186 if (req.content_type) {
187 request.setRequestHeader(CONTENT_TYPE, req.content_type)
188 }
189 request.send(req.body)
190}
191
192function norm (str) {
193 return (str || '').trim()
194}
195
196function isFunc (f) {
197 return f && {}.toString.call(f) === '[object Function]'
198}
199
200function waterfall (ps, param, cb) {
201 if (!ps || ps.length === 0) return cb(param)
202
203 var fp = ps.shift()
204 if (!isFunc(fp)) return waterfall(ps, param, cb)
205 fp(param, function (out) {
206 return out === false ? cb(param) : waterfall(ps, param, cb)
207 })
208}
209
210var ajax = newRequest()
211var env = { XMLHttpRequest: {}, Jsonify: JSON.stringify, ParseJson: JSON.parse }
212ajax.env = env
213ajax.waterfall = waterfall
214module.exports = ajax