]>
Commit | Line | Data |
---|---|---|
6eb72d64 JZ |
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.superagent=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
2 | ||
3 | /** | |
4 | * Expose `Emitter`. | |
5 | */ | |
6 | ||
7 | module.exports = Emitter; | |
8 | ||
9 | /** | |
10 | * Initialize a new `Emitter`. | |
11 | * | |
12 | * @api public | |
13 | */ | |
14 | ||
15 | function Emitter(obj) { | |
16 | if (obj) return mixin(obj); | |
17 | }; | |
18 | ||
19 | /** | |
20 | * Mixin the emitter properties. | |
21 | * | |
22 | * @param {Object} obj | |
23 | * @return {Object} | |
24 | * @api private | |
25 | */ | |
26 | ||
27 | function mixin(obj) { | |
28 | for (var key in Emitter.prototype) { | |
29 | obj[key] = Emitter.prototype[key]; | |
30 | } | |
31 | return obj; | |
32 | } | |
33 | ||
34 | /** | |
35 | * Listen on the given `event` with `fn`. | |
36 | * | |
37 | * @param {String} event | |
38 | * @param {Function} fn | |
39 | * @return {Emitter} | |
40 | * @api public | |
41 | */ | |
42 | ||
43 | Emitter.prototype.on = | |
44 | Emitter.prototype.addEventListener = function(event, fn){ | |
45 | this._callbacks = this._callbacks || {}; | |
46 | (this._callbacks['$' + event] = this._callbacks['$' + event] || []) | |
47 | .push(fn); | |
48 | return this; | |
49 | }; | |
50 | ||
51 | /** | |
52 | * Adds an `event` listener that will be invoked a single | |
53 | * time then automatically removed. | |
54 | * | |
55 | * @param {String} event | |
56 | * @param {Function} fn | |
57 | * @return {Emitter} | |
58 | * @api public | |
59 | */ | |
60 | ||
61 | Emitter.prototype.once = function(event, fn){ | |
62 | function on() { | |
63 | this.off(event, on); | |
64 | fn.apply(this, arguments); | |
65 | } | |
66 | ||
67 | on.fn = fn; | |
68 | this.on(event, on); | |
69 | return this; | |
70 | }; | |
71 | ||
72 | /** | |
73 | * Remove the given callback for `event` or all | |
74 | * registered callbacks. | |
75 | * | |
76 | * @param {String} event | |
77 | * @param {Function} fn | |
78 | * @return {Emitter} | |
79 | * @api public | |
80 | */ | |
81 | ||
82 | Emitter.prototype.off = | |
83 | Emitter.prototype.removeListener = | |
84 | Emitter.prototype.removeAllListeners = | |
85 | Emitter.prototype.removeEventListener = function(event, fn){ | |
86 | this._callbacks = this._callbacks || {}; | |
87 | ||
88 | // all | |
89 | if (0 == arguments.length) { | |
90 | this._callbacks = {}; | |
91 | return this; | |
92 | } | |
93 | ||
94 | // specific event | |
95 | var callbacks = this._callbacks['$' + event]; | |
96 | if (!callbacks) return this; | |
97 | ||
98 | // remove all handlers | |
99 | if (1 == arguments.length) { | |
100 | delete this._callbacks['$' + event]; | |
101 | return this; | |
102 | } | |
103 | ||
104 | // remove specific handler | |
105 | var cb; | |
106 | for (var i = 0; i < callbacks.length; i++) { | |
107 | cb = callbacks[i]; | |
108 | if (cb === fn || cb.fn === fn) { | |
109 | callbacks.splice(i, 1); | |
110 | break; | |
111 | } | |
112 | } | |
113 | return this; | |
114 | }; | |
115 | ||
116 | /** | |
117 | * Emit `event` with the given args. | |
118 | * | |
119 | * @param {String} event | |
120 | * @param {Mixed} ... | |
121 | * @return {Emitter} | |
122 | */ | |
123 | ||
124 | Emitter.prototype.emit = function(event){ | |
125 | this._callbacks = this._callbacks || {}; | |
126 | var args = [].slice.call(arguments, 1) | |
127 | , callbacks = this._callbacks['$' + event]; | |
128 | ||
129 | if (callbacks) { | |
130 | callbacks = callbacks.slice(0); | |
131 | for (var i = 0, len = callbacks.length; i < len; ++i) { | |
132 | callbacks[i].apply(this, args); | |
133 | } | |
134 | } | |
135 | ||
136 | return this; | |
137 | }; | |
138 | ||
139 | /** | |
140 | * Return array of callbacks for `event`. | |
141 | * | |
142 | * @param {String} event | |
143 | * @return {Array} | |
144 | * @api public | |
145 | */ | |
146 | ||
147 | Emitter.prototype.listeners = function(event){ | |
148 | this._callbacks = this._callbacks || {}; | |
149 | return this._callbacks['$' + event] || []; | |
150 | }; | |
151 | ||
152 | /** | |
153 | * Check if this emitter has `event` handlers. | |
154 | * | |
155 | * @param {String} event | |
156 | * @return {Boolean} | |
157 | * @api public | |
158 | */ | |
159 | ||
160 | Emitter.prototype.hasListeners = function(event){ | |
161 | return !! this.listeners(event).length; | |
162 | }; | |
163 | ||
164 | },{}],2:[function(require,module,exports){ | |
165 | ||
166 | /** | |
167 | * Reduce `arr` with `fn`. | |
168 | * | |
169 | * @param {Array} arr | |
170 | * @param {Function} fn | |
171 | * @param {Mixed} initial | |
172 | * | |
173 | * TODO: combatible error handling? | |
174 | */ | |
175 | ||
176 | module.exports = function(arr, fn, initial){ | |
177 | var idx = 0; | |
178 | var len = arr.length; | |
179 | var curr = arguments.length == 3 | |
180 | ? initial | |
181 | : arr[idx++]; | |
182 | ||
183 | while (idx < len) { | |
184 | curr = fn.call(null, curr, arr[idx], ++idx, arr); | |
185 | } | |
186 | ||
187 | return curr; | |
188 | }; | |
189 | },{}],3:[function(require,module,exports){ | |
190 | /** | |
191 | * Module dependencies. | |
192 | */ | |
193 | ||
194 | var Emitter = require('emitter'); | |
195 | var reduce = require('reduce'); | |
196 | ||
197 | /** | |
198 | * Root reference for iframes. | |
199 | */ | |
200 | ||
201 | var root; | |
202 | if (typeof window !== 'undefined') { // Browser window | |
203 | root = window; | |
204 | } else if (typeof self !== 'undefined') { // Web Worker | |
205 | root = self; | |
206 | } else { // Other environments | |
207 | root = this; | |
208 | } | |
209 | ||
210 | /** | |
211 | * Noop. | |
212 | */ | |
213 | ||
214 | function noop(){}; | |
215 | ||
216 | /** | |
217 | * Check if `obj` is a host object, | |
218 | * we don't want to serialize these :) | |
219 | * | |
220 | * TODO: future proof, move to compoent land | |
221 | * | |
222 | * @param {Object} obj | |
223 | * @return {Boolean} | |
224 | * @api private | |
225 | */ | |
226 | ||
227 | function isHost(obj) { | |
228 | var str = {}.toString.call(obj); | |
229 | ||
230 | switch (str) { | |
231 | case '[object File]': | |
232 | case '[object Blob]': | |
233 | case '[object FormData]': | |
234 | return true; | |
235 | default: | |
236 | return false; | |
237 | } | |
238 | } | |
239 | ||
240 | /** | |
241 | * Determine XHR. | |
242 | */ | |
243 | ||
244 | request.getXHR = function () { | |
245 | if (root.XMLHttpRequest | |
246 | && (!root.location || 'file:' != root.location.protocol | |
247 | || !root.ActiveXObject)) { | |
248 | return new XMLHttpRequest; | |
249 | } else { | |
250 | try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} | |
251 | try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} | |
252 | try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} | |
253 | try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} | |
254 | } | |
255 | return false; | |
256 | }; | |
257 | ||
258 | /** | |
259 | * Removes leading and trailing whitespace, added to support IE. | |
260 | * | |
261 | * @param {String} s | |
262 | * @return {String} | |
263 | * @api private | |
264 | */ | |
265 | ||
266 | var trim = ''.trim | |
267 | ? function(s) { return s.trim(); } | |
268 | : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; | |
269 | ||
270 | /** | |
271 | * Check if `obj` is an object. | |
272 | * | |
273 | * @param {Object} obj | |
274 | * @return {Boolean} | |
275 | * @api private | |
276 | */ | |
277 | ||
278 | function isObject(obj) { | |
279 | return obj === Object(obj); | |
280 | } | |
281 | ||
282 | /** | |
283 | * Serialize the given `obj`. | |
284 | * | |
285 | * @param {Object} obj | |
286 | * @return {String} | |
287 | * @api private | |
288 | */ | |
289 | ||
290 | function serialize(obj) { | |
291 | if (!isObject(obj)) return obj; | |
292 | var pairs = []; | |
293 | for (var key in obj) { | |
294 | if (null != obj[key]) { | |
295 | pushEncodedKeyValuePair(pairs, key, obj[key]); | |
296 | } | |
297 | } | |
298 | return pairs.join('&'); | |
299 | } | |
300 | ||
301 | /** | |
302 | * Helps 'serialize' with serializing arrays. | |
303 | * Mutates the pairs array. | |
304 | * | |
305 | * @param {Array} pairs | |
306 | * @param {String} key | |
307 | * @param {Mixed} val | |
308 | */ | |
309 | ||
310 | function pushEncodedKeyValuePair(pairs, key, val) { | |
311 | if (Array.isArray(val)) { | |
312 | return val.forEach(function(v) { | |
313 | pushEncodedKeyValuePair(pairs, key, v); | |
314 | }); | |
315 | } | |
316 | pairs.push(encodeURIComponent(key) | |
317 | + '=' + encodeURIComponent(val)); | |
318 | } | |
319 | ||
320 | /** | |
321 | * Expose serialization method. | |
322 | */ | |
323 | ||
324 | request.serializeObject = serialize; | |
325 | ||
326 | /** | |
327 | * Parse the given x-www-form-urlencoded `str`. | |
328 | * | |
329 | * @param {String} str | |
330 | * @return {Object} | |
331 | * @api private | |
332 | */ | |
333 | ||
334 | function parseString(str) { | |
335 | var obj = {}; | |
336 | var pairs = str.split('&'); | |
337 | var parts; | |
338 | var pair; | |
339 | ||
340 | for (var i = 0, len = pairs.length; i < len; ++i) { | |
341 | pair = pairs[i]; | |
342 | parts = pair.split('='); | |
343 | obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); | |
344 | } | |
345 | ||
346 | return obj; | |
347 | } | |
348 | ||
349 | /** | |
350 | * Expose parser. | |
351 | */ | |
352 | ||
353 | request.parseString = parseString; | |
354 | ||
355 | /** | |
356 | * Default MIME type map. | |
357 | * | |
358 | * superagent.types.xml = 'application/xml'; | |
359 | * | |
360 | */ | |
361 | ||
362 | request.types = { | |
363 | html: 'text/html', | |
364 | json: 'application/json', | |
365 | xml: 'application/xml', | |
366 | urlencoded: 'application/x-www-form-urlencoded', | |
367 | 'form': 'application/x-www-form-urlencoded', | |
368 | 'form-data': 'application/x-www-form-urlencoded' | |
369 | }; | |
370 | ||
371 | /** | |
372 | * Default serialization map. | |
373 | * | |
374 | * superagent.serialize['application/xml'] = function(obj){ | |
375 | * return 'generated xml here'; | |
376 | * }; | |
377 | * | |
378 | */ | |
379 | ||
380 | request.serialize = { | |
381 | 'application/x-www-form-urlencoded': serialize, | |
382 | 'application/json': JSON.stringify | |
383 | }; | |
384 | ||
385 | /** | |
386 | * Default parsers. | |
387 | * | |
388 | * superagent.parse['application/xml'] = function(str){ | |
389 | * return { object parsed from str }; | |
390 | * }; | |
391 | * | |
392 | */ | |
393 | ||
394 | request.parse = { | |
395 | 'application/x-www-form-urlencoded': parseString, | |
396 | 'application/json': JSON.parse | |
397 | }; | |
398 | ||
399 | /** | |
400 | * Parse the given header `str` into | |
401 | * an object containing the mapped fields. | |
402 | * | |
403 | * @param {String} str | |
404 | * @return {Object} | |
405 | * @api private | |
406 | */ | |
407 | ||
408 | function parseHeader(str) { | |
409 | var lines = str.split(/\r?\n/); | |
410 | var fields = {}; | |
411 | var index; | |
412 | var line; | |
413 | var field; | |
414 | var val; | |
415 | ||
416 | lines.pop(); // trailing CRLF | |
417 | ||
418 | for (var i = 0, len = lines.length; i < len; ++i) { | |
419 | line = lines[i]; | |
420 | index = line.indexOf(':'); | |
421 | field = line.slice(0, index).toLowerCase(); | |
422 | val = trim(line.slice(index + 1)); | |
423 | fields[field] = val; | |
424 | } | |
425 | ||
426 | return fields; | |
427 | } | |
428 | ||
429 | /** | |
430 | * Check if `mime` is json or has +json structured syntax suffix. | |
431 | * | |
432 | * @param {String} mime | |
433 | * @return {Boolean} | |
434 | * @api private | |
435 | */ | |
436 | ||
437 | function isJSON(mime) { | |
438 | return /[\/+]json\b/.test(mime); | |
439 | } | |
440 | ||
441 | /** | |
442 | * Return the mime type for the given `str`. | |
443 | * | |
444 | * @param {String} str | |
445 | * @return {String} | |
446 | * @api private | |
447 | */ | |
448 | ||
449 | function type(str){ | |
450 | return str.split(/ *; */).shift(); | |
451 | }; | |
452 | ||
453 | /** | |
454 | * Return header field parameters. | |
455 | * | |
456 | * @param {String} str | |
457 | * @return {Object} | |
458 | * @api private | |
459 | */ | |
460 | ||
461 | function params(str){ | |
462 | return reduce(str.split(/ *; */), function(obj, str){ | |
463 | var parts = str.split(/ *= */) | |
464 | , key = parts.shift() | |
465 | , val = parts.shift(); | |
466 | ||
467 | if (key && val) obj[key] = val; | |
468 | return obj; | |
469 | }, {}); | |
470 | }; | |
471 | ||
472 | /** | |
473 | * Initialize a new `Response` with the given `xhr`. | |
474 | * | |
475 | * - set flags (.ok, .error, etc) | |
476 | * - parse header | |
477 | * | |
478 | * Examples: | |
479 | * | |
480 | * Aliasing `superagent` as `request` is nice: | |
481 | * | |
482 | * request = superagent; | |
483 | * | |
484 | * We can use the promise-like API, or pass callbacks: | |
485 | * | |
486 | * request.get('/').end(function(res){}); | |
487 | * request.get('/', function(res){}); | |
488 | * | |
489 | * Sending data can be chained: | |
490 | * | |
491 | * request | |
492 | * .post('/user') | |
493 | * .send({ name: 'tj' }) | |
494 | * .end(function(res){}); | |
495 | * | |
496 | * Or passed to `.send()`: | |
497 | * | |
498 | * request | |
499 | * .post('/user') | |
500 | * .send({ name: 'tj' }, function(res){}); | |
501 | * | |
502 | * Or passed to `.post()`: | |
503 | * | |
504 | * request | |
505 | * .post('/user', { name: 'tj' }) | |
506 | * .end(function(res){}); | |
507 | * | |
508 | * Or further reduced to a single call for simple cases: | |
509 | * | |
510 | * request | |
511 | * .post('/user', { name: 'tj' }, function(res){}); | |
512 | * | |
513 | * @param {XMLHTTPRequest} xhr | |
514 | * @param {Object} options | |
515 | * @api private | |
516 | */ | |
517 | ||
518 | function Response(req, options) { | |
519 | options = options || {}; | |
520 | this.req = req; | |
521 | this.xhr = this.req.xhr; | |
522 | // responseText is accessible only if responseType is '' or 'text' and on older browsers | |
523 | this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined') | |
524 | ? this.xhr.responseText | |
525 | : null; | |
526 | this.statusText = this.req.xhr.statusText; | |
527 | this.setStatusProperties(this.xhr.status); | |
528 | this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); | |
529 | // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but | |
530 | // getResponseHeader still works. so we get content-type even if getting | |
531 | // other headers fails. | |
532 | this.header['content-type'] = this.xhr.getResponseHeader('content-type'); | |
533 | this.setHeaderProperties(this.header); | |
534 | this.body = this.req.method != 'HEAD' | |
535 | ? this.parseBody(this.text ? this.text : this.xhr.response) | |
536 | : null; | |
537 | } | |
538 | ||
539 | /** | |
540 | * Get case-insensitive `field` value. | |
541 | * | |
542 | * @param {String} field | |
543 | * @return {String} | |
544 | * @api public | |
545 | */ | |
546 | ||
547 | Response.prototype.get = function(field){ | |
548 | return this.header[field.toLowerCase()]; | |
549 | }; | |
550 | ||
551 | /** | |
552 | * Set header related properties: | |
553 | * | |
554 | * - `.type` the content type without params | |
555 | * | |
556 | * A response of "Content-Type: text/plain; charset=utf-8" | |
557 | * will provide you with a `.type` of "text/plain". | |
558 | * | |
559 | * @param {Object} header | |
560 | * @api private | |
561 | */ | |
562 | ||
563 | Response.prototype.setHeaderProperties = function(header){ | |
564 | // content-type | |
565 | var ct = this.header['content-type'] || ''; | |
566 | this.type = type(ct); | |
567 | ||
568 | // params | |
569 | var obj = params(ct); | |
570 | for (var key in obj) this[key] = obj[key]; | |
571 | }; | |
572 | ||
573 | /** | |
574 | * Parse the given body `str`. | |
575 | * | |
576 | * Used for auto-parsing of bodies. Parsers | |
577 | * are defined on the `superagent.parse` object. | |
578 | * | |
579 | * @param {String} str | |
580 | * @return {Mixed} | |
581 | * @api private | |
582 | */ | |
583 | ||
584 | Response.prototype.parseBody = function(str){ | |
585 | var parse = request.parse[this.type]; | |
586 | return parse && str && (str.length || str instanceof Object) | |
587 | ? parse(str) | |
588 | : null; | |
589 | }; | |
590 | ||
591 | /** | |
592 | * Set flags such as `.ok` based on `status`. | |
593 | * | |
594 | * For example a 2xx response will give you a `.ok` of __true__ | |
595 | * whereas 5xx will be __false__ and `.error` will be __true__. The | |
596 | * `.clientError` and `.serverError` are also available to be more | |
597 | * specific, and `.statusType` is the class of error ranging from 1..5 | |
598 | * sometimes useful for mapping respond colors etc. | |
599 | * | |
600 | * "sugar" properties are also defined for common cases. Currently providing: | |
601 | * | |
602 | * - .noContent | |
603 | * - .badRequest | |
604 | * - .unauthorized | |
605 | * - .notAcceptable | |
606 | * - .notFound | |
607 | * | |
608 | * @param {Number} status | |
609 | * @api private | |
610 | */ | |
611 | ||
612 | Response.prototype.setStatusProperties = function(status){ | |
613 | // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request | |
614 | if (status === 1223) { | |
615 | status = 204; | |
616 | } | |
617 | ||
618 | var type = status / 100 | 0; | |
619 | ||
620 | // status / class | |
621 | this.status = this.statusCode = status; | |
622 | this.statusType = type; | |
623 | ||
624 | // basics | |
625 | this.info = 1 == type; | |
626 | this.ok = 2 == type; | |
627 | this.clientError = 4 == type; | |
628 | this.serverError = 5 == type; | |
629 | this.error = (4 == type || 5 == type) | |
630 | ? this.toError() | |
631 | : false; | |
632 | ||
633 | // sugar | |
634 | this.accepted = 202 == status; | |
635 | this.noContent = 204 == status; | |
636 | this.badRequest = 400 == status; | |
637 | this.unauthorized = 401 == status; | |
638 | this.notAcceptable = 406 == status; | |
639 | this.notFound = 404 == status; | |
640 | this.forbidden = 403 == status; | |
641 | }; | |
642 | ||
643 | /** | |
644 | * Return an `Error` representative of this response. | |
645 | * | |
646 | * @return {Error} | |
647 | * @api public | |
648 | */ | |
649 | ||
650 | Response.prototype.toError = function(){ | |
651 | var req = this.req; | |
652 | var method = req.method; | |
653 | var url = req.url; | |
654 | ||
655 | var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; | |
656 | var err = new Error(msg); | |
657 | err.status = this.status; | |
658 | err.method = method; | |
659 | err.url = url; | |
660 | ||
661 | return err; | |
662 | }; | |
663 | ||
664 | /** | |
665 | * Expose `Response`. | |
666 | */ | |
667 | ||
668 | request.Response = Response; | |
669 | ||
670 | /** | |
671 | * Initialize a new `Request` with the given `method` and `url`. | |
672 | * | |
673 | * @param {String} method | |
674 | * @param {String} url | |
675 | * @api public | |
676 | */ | |
677 | ||
678 | function Request(method, url) { | |
679 | var self = this; | |
680 | Emitter.call(this); | |
681 | this._query = this._query || []; | |
682 | this.method = method; | |
683 | this.url = url; | |
684 | this.header = {}; | |
685 | this._header = {}; | |
686 | this.on('end', function(){ | |
687 | var err = null; | |
688 | var res = null; | |
689 | ||
690 | try { | |
691 | res = new Response(self); | |
692 | } catch(e) { | |
693 | err = new Error('Parser is unable to parse the response'); | |
694 | err.parse = true; | |
695 | err.original = e; | |
696 | // issue #675: return the raw response if the response parsing fails | |
697 | err.rawResponse = self.xhr && self.xhr.responseText ? self.xhr.responseText : null; | |
698 | return self.callback(err); | |
699 | } | |
700 | ||
701 | self.emit('response', res); | |
702 | ||
703 | if (err) { | |
704 | return self.callback(err, res); | |
705 | } | |
706 | ||
707 | if (res.status >= 200 && res.status < 300) { | |
708 | return self.callback(err, res); | |
709 | } | |
710 | ||
711 | var new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); | |
712 | new_err.original = err; | |
713 | new_err.response = res; | |
714 | new_err.status = res.status; | |
715 | ||
716 | self.callback(new_err, res); | |
717 | }); | |
718 | } | |
719 | ||
720 | /** | |
721 | * Mixin `Emitter`. | |
722 | */ | |
723 | ||
724 | Emitter(Request.prototype); | |
725 | ||
726 | /** | |
727 | * Allow for extension | |
728 | */ | |
729 | ||
730 | Request.prototype.use = function(fn) { | |
731 | fn(this); | |
732 | return this; | |
733 | } | |
734 | ||
735 | /** | |
736 | * Set timeout to `ms`. | |
737 | * | |
738 | * @param {Number} ms | |
739 | * @return {Request} for chaining | |
740 | * @api public | |
741 | */ | |
742 | ||
743 | Request.prototype.timeout = function(ms){ | |
744 | this._timeout = ms; | |
745 | return this; | |
746 | }; | |
747 | ||
748 | /** | |
749 | * Clear previous timeout. | |
750 | * | |
751 | * @return {Request} for chaining | |
752 | * @api public | |
753 | */ | |
754 | ||
755 | Request.prototype.clearTimeout = function(){ | |
756 | this._timeout = 0; | |
757 | clearTimeout(this._timer); | |
758 | return this; | |
759 | }; | |
760 | ||
761 | /** | |
762 | * Abort the request, and clear potential timeout. | |
763 | * | |
764 | * @return {Request} | |
765 | * @api public | |
766 | */ | |
767 | ||
768 | Request.prototype.abort = function(){ | |
769 | if (this.aborted) return; | |
770 | this.aborted = true; | |
771 | this.xhr.abort(); | |
772 | this.clearTimeout(); | |
773 | this.emit('abort'); | |
774 | return this; | |
775 | }; | |
776 | ||
777 | /** | |
778 | * Set header `field` to `val`, or multiple fields with one object. | |
779 | * | |
780 | * Examples: | |
781 | * | |
782 | * req.get('/') | |
783 | * .set('Accept', 'application/json') | |
784 | * .set('X-API-Key', 'foobar') | |
785 | * .end(callback); | |
786 | * | |
787 | * req.get('/') | |
788 | * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) | |
789 | * .end(callback); | |
790 | * | |
791 | * @param {String|Object} field | |
792 | * @param {String} val | |
793 | * @return {Request} for chaining | |
794 | * @api public | |
795 | */ | |
796 | ||
797 | Request.prototype.set = function(field, val){ | |
798 | if (isObject(field)) { | |
799 | for (var key in field) { | |
800 | this.set(key, field[key]); | |
801 | } | |
802 | return this; | |
803 | } | |
804 | this._header[field.toLowerCase()] = val; | |
805 | this.header[field] = val; | |
806 | return this; | |
807 | }; | |
808 | ||
809 | /** | |
810 | * Remove header `field`. | |
811 | * | |
812 | * Example: | |
813 | * | |
814 | * req.get('/') | |
815 | * .unset('User-Agent') | |
816 | * .end(callback); | |
817 | * | |
818 | * @param {String} field | |
819 | * @return {Request} for chaining | |
820 | * @api public | |
821 | */ | |
822 | ||
823 | Request.prototype.unset = function(field){ | |
824 | delete this._header[field.toLowerCase()]; | |
825 | delete this.header[field]; | |
826 | return this; | |
827 | }; | |
828 | ||
829 | /** | |
830 | * Get case-insensitive header `field` value. | |
831 | * | |
832 | * @param {String} field | |
833 | * @return {String} | |
834 | * @api private | |
835 | */ | |
836 | ||
837 | Request.prototype.getHeader = function(field){ | |
838 | return this._header[field.toLowerCase()]; | |
839 | }; | |
840 | ||
841 | /** | |
842 | * Set Content-Type to `type`, mapping values from `request.types`. | |
843 | * | |
844 | * Examples: | |
845 | * | |
846 | * superagent.types.xml = 'application/xml'; | |
847 | * | |
848 | * request.post('/') | |
849 | * .type('xml') | |
850 | * .send(xmlstring) | |
851 | * .end(callback); | |
852 | * | |
853 | * request.post('/') | |
854 | * .type('application/xml') | |
855 | * .send(xmlstring) | |
856 | * .end(callback); | |
857 | * | |
858 | * @param {String} type | |
859 | * @return {Request} for chaining | |
860 | * @api public | |
861 | */ | |
862 | ||
863 | Request.prototype.type = function(type){ | |
864 | this.set('Content-Type', request.types[type] || type); | |
865 | return this; | |
866 | }; | |
867 | ||
868 | /** | |
869 | * Force given parser | |
870 | * | |
871 | * Sets the body parser no matter type. | |
872 | * | |
873 | * @param {Function} | |
874 | * @api public | |
875 | */ | |
876 | ||
877 | Request.prototype.parse = function(fn){ | |
878 | this._parser = fn; | |
879 | return this; | |
880 | }; | |
881 | ||
882 | /** | |
883 | * Set Accept to `type`, mapping values from `request.types`. | |
884 | * | |
885 | * Examples: | |
886 | * | |
887 | * superagent.types.json = 'application/json'; | |
888 | * | |
889 | * request.get('/agent') | |
890 | * .accept('json') | |
891 | * .end(callback); | |
892 | * | |
893 | * request.get('/agent') | |
894 | * .accept('application/json') | |
895 | * .end(callback); | |
896 | * | |
897 | * @param {String} accept | |
898 | * @return {Request} for chaining | |
899 | * @api public | |
900 | */ | |
901 | ||
902 | Request.prototype.accept = function(type){ | |
903 | this.set('Accept', request.types[type] || type); | |
904 | return this; | |
905 | }; | |
906 | ||
907 | /** | |
908 | * Set Authorization field value with `user` and `pass`. | |
909 | * | |
910 | * @param {String} user | |
911 | * @param {String} pass | |
912 | * @return {Request} for chaining | |
913 | * @api public | |
914 | */ | |
915 | ||
916 | Request.prototype.auth = function(user, pass){ | |
917 | var str = btoa(user + ':' + pass); | |
918 | this.set('Authorization', 'Basic ' + str); | |
919 | return this; | |
920 | }; | |
921 | ||
922 | /** | |
923 | * Add query-string `val`. | |
924 | * | |
925 | * Examples: | |
926 | * | |
927 | * request.get('/shoes') | |
928 | * .query('size=10') | |
929 | * .query({ color: 'blue' }) | |
930 | * | |
931 | * @param {Object|String} val | |
932 | * @return {Request} for chaining | |
933 | * @api public | |
934 | */ | |
935 | ||
936 | Request.prototype.query = function(val){ | |
937 | if ('string' != typeof val) val = serialize(val); | |
938 | if (val) this._query.push(val); | |
939 | return this; | |
940 | }; | |
941 | ||
942 | /** | |
943 | * Write the field `name` and `val` for "multipart/form-data" | |
944 | * request bodies. | |
945 | * | |
946 | * ``` js | |
947 | * request.post('/upload') | |
948 | * .field('foo', 'bar') | |
949 | * .end(callback); | |
950 | * ``` | |
951 | * | |
952 | * @param {String} name | |
953 | * @param {String|Blob|File} val | |
954 | * @return {Request} for chaining | |
955 | * @api public | |
956 | */ | |
957 | ||
958 | Request.prototype.field = function(name, val){ | |
959 | if (!this._formData) this._formData = new root.FormData(); | |
960 | this._formData.append(name, val); | |
961 | return this; | |
962 | }; | |
963 | ||
964 | /** | |
965 | * Queue the given `file` as an attachment to the specified `field`, | |
966 | * with optional `filename`. | |
967 | * | |
968 | * ``` js | |
969 | * request.post('/upload') | |
970 | * .attach(new Blob(['<a id="a"><b id="b">hey!</b></a>'], { type: "text/html"})) | |
971 | * .end(callback); | |
972 | * ``` | |
973 | * | |
974 | * @param {String} field | |
975 | * @param {Blob|File} file | |
976 | * @param {String} filename | |
977 | * @return {Request} for chaining | |
978 | * @api public | |
979 | */ | |
980 | ||
981 | Request.prototype.attach = function(field, file, filename){ | |
982 | if (!this._formData) this._formData = new root.FormData(); | |
983 | this._formData.append(field, file, filename || file.name); | |
984 | return this; | |
985 | }; | |
986 | ||
987 | /** | |
988 | * Send `data` as the request body, defaulting the `.type()` to "json" when | |
989 | * an object is given. | |
990 | * | |
991 | * Examples: | |
992 | * | |
993 | * // manual json | |
994 | * request.post('/user') | |
995 | * .type('json') | |
996 | * .send('{"name":"tj"}') | |
997 | * .end(callback) | |
998 | * | |
999 | * // auto json | |
1000 | * request.post('/user') | |
1001 | * .send({ name: 'tj' }) | |
1002 | * .end(callback) | |
1003 | * | |
1004 | * // manual x-www-form-urlencoded | |
1005 | * request.post('/user') | |
1006 | * .type('form') | |
1007 | * .send('name=tj') | |
1008 | * .end(callback) | |
1009 | * | |
1010 | * // auto x-www-form-urlencoded | |
1011 | * request.post('/user') | |
1012 | * .type('form') | |
1013 | * .send({ name: 'tj' }) | |
1014 | * .end(callback) | |
1015 | * | |
1016 | * // defaults to x-www-form-urlencoded | |
1017 | * request.post('/user') | |
1018 | * .send('name=tobi') | |
1019 | * .send('species=ferret') | |
1020 | * .end(callback) | |
1021 | * | |
1022 | * @param {String|Object} data | |
1023 | * @return {Request} for chaining | |
1024 | * @api public | |
1025 | */ | |
1026 | ||
1027 | Request.prototype.send = function(data){ | |
1028 | var obj = isObject(data); | |
1029 | var type = this.getHeader('Content-Type'); | |
1030 | ||
1031 | // merge | |
1032 | if (obj && isObject(this._data)) { | |
1033 | for (var key in data) { | |
1034 | this._data[key] = data[key]; | |
1035 | } | |
1036 | } else if ('string' == typeof data) { | |
1037 | if (!type) this.type('form'); | |
1038 | type = this.getHeader('Content-Type'); | |
1039 | if ('application/x-www-form-urlencoded' == type) { | |
1040 | this._data = this._data | |
1041 | ? this._data + '&' + data | |
1042 | : data; | |
1043 | } else { | |
1044 | this._data = (this._data || '') + data; | |
1045 | } | |
1046 | } else { | |
1047 | this._data = data; | |
1048 | } | |
1049 | ||
1050 | if (!obj || isHost(data)) return this; | |
1051 | if (!type) this.type('json'); | |
1052 | return this; | |
1053 | }; | |
1054 | ||
1055 | /** | |
1056 | * Invoke the callback with `err` and `res` | |
1057 | * and handle arity check. | |
1058 | * | |
1059 | * @param {Error} err | |
1060 | * @param {Response} res | |
1061 | * @api private | |
1062 | */ | |
1063 | ||
1064 | Request.prototype.callback = function(err, res){ | |
1065 | var fn = this._callback; | |
1066 | this.clearTimeout(); | |
1067 | fn(err, res); | |
1068 | }; | |
1069 | ||
1070 | /** | |
1071 | * Invoke callback with x-domain error. | |
1072 | * | |
1073 | * @api private | |
1074 | */ | |
1075 | ||
1076 | Request.prototype.crossDomainError = function(){ | |
1077 | var err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'); | |
1078 | err.crossDomain = true; | |
1079 | ||
1080 | err.status = this.status; | |
1081 | err.method = this.method; | |
1082 | err.url = this.url; | |
1083 | ||
1084 | this.callback(err); | |
1085 | }; | |
1086 | ||
1087 | /** | |
1088 | * Invoke callback with timeout error. | |
1089 | * | |
1090 | * @api private | |
1091 | */ | |
1092 | ||
1093 | Request.prototype.timeoutError = function(){ | |
1094 | var timeout = this._timeout; | |
1095 | var err = new Error('timeout of ' + timeout + 'ms exceeded'); | |
1096 | err.timeout = timeout; | |
1097 | this.callback(err); | |
1098 | }; | |
1099 | ||
1100 | /** | |
1101 | * Enable transmission of cookies with x-domain requests. | |
1102 | * | |
1103 | * Note that for this to work the origin must not be | |
1104 | * using "Access-Control-Allow-Origin" with a wildcard, | |
1105 | * and also must set "Access-Control-Allow-Credentials" | |
1106 | * to "true". | |
1107 | * | |
1108 | * @api public | |
1109 | */ | |
1110 | ||
1111 | Request.prototype.withCredentials = function(){ | |
1112 | this._withCredentials = true; | |
1113 | return this; | |
1114 | }; | |
1115 | ||
1116 | /** | |
1117 | * Initiate request, invoking callback `fn(res)` | |
1118 | * with an instanceof `Response`. | |
1119 | * | |
1120 | * @param {Function} fn | |
1121 | * @return {Request} for chaining | |
1122 | * @api public | |
1123 | */ | |
1124 | ||
1125 | Request.prototype.end = function(fn){ | |
1126 | var self = this; | |
1127 | var xhr = this.xhr = request.getXHR(); | |
1128 | var query = this._query.join('&'); | |
1129 | var timeout = this._timeout; | |
1130 | var data = this._formData || this._data; | |
1131 | ||
1132 | // store callback | |
1133 | this._callback = fn || noop; | |
1134 | ||
1135 | // state change | |
1136 | xhr.onreadystatechange = function(){ | |
1137 | if (4 != xhr.readyState) return; | |
1138 | ||
1139 | // In IE9, reads to any property (e.g. status) off of an aborted XHR will | |
1140 | // result in the error "Could not complete the operation due to error c00c023f" | |
1141 | var status; | |
1142 | try { status = xhr.status } catch(e) { status = 0; } | |
1143 | ||
1144 | if (0 == status) { | |
1145 | if (self.timedout) return self.timeoutError(); | |
1146 | if (self.aborted) return; | |
1147 | return self.crossDomainError(); | |
1148 | } | |
1149 | self.emit('end'); | |
1150 | }; | |
1151 | ||
1152 | // progress | |
1153 | var handleProgress = function(e){ | |
1154 | if (e.total > 0) { | |
1155 | e.percent = e.loaded / e.total * 100; | |
1156 | } | |
1157 | e.direction = 'download'; | |
1158 | self.emit('progress', e); | |
1159 | }; | |
1160 | if (this.hasListeners('progress')) { | |
1161 | xhr.onprogress = handleProgress; | |
1162 | } | |
1163 | try { | |
1164 | if (xhr.upload && this.hasListeners('progress')) { | |
1165 | xhr.upload.onprogress = handleProgress; | |
1166 | } | |
1167 | } catch(e) { | |
1168 | // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist. | |
1169 | // Reported here: | |
1170 | // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context | |
1171 | } | |
1172 | ||
1173 | // timeout | |
1174 | if (timeout && !this._timer) { | |
1175 | this._timer = setTimeout(function(){ | |
1176 | self.timedout = true; | |
1177 | self.abort(); | |
1178 | }, timeout); | |
1179 | } | |
1180 | ||
1181 | // querystring | |
1182 | if (query) { | |
1183 | query = request.serializeObject(query); | |
1184 | this.url += ~this.url.indexOf('?') | |
1185 | ? '&' + query | |
1186 | : '?' + query; | |
1187 | } | |
1188 | ||
1189 | // initiate request | |
1190 | xhr.open(this.method, this.url, true); | |
1191 | ||
1192 | // CORS | |
1193 | if (this._withCredentials) xhr.withCredentials = true; | |
1194 | ||
1195 | // body | |
1196 | if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) { | |
1197 | // serialize stuff | |
1198 | var contentType = this.getHeader('Content-Type'); | |
1199 | var serialize = this._parser || request.serialize[contentType ? contentType.split(';')[0] : '']; | |
1200 | if (!serialize && isJSON(contentType)) serialize = request.serialize['application/json']; | |
1201 | if (serialize) data = serialize(data); | |
1202 | } | |
1203 | ||
1204 | // set header fields | |
1205 | for (var field in this.header) { | |
1206 | if (null == this.header[field]) continue; | |
1207 | xhr.setRequestHeader(field, this.header[field]); | |
1208 | } | |
1209 | ||
1210 | // send stuff | |
1211 | this.emit('request', this); | |
1212 | ||
1213 | // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing) | |
1214 | // We need null here if data is undefined | |
1215 | xhr.send(typeof data !== 'undefined' ? data : null); | |
1216 | return this; | |
1217 | }; | |
1218 | ||
1219 | /** | |
1220 | * Faux promise support | |
1221 | * | |
1222 | * @param {Function} fulfill | |
1223 | * @param {Function} reject | |
1224 | * @return {Request} | |
1225 | */ | |
1226 | ||
1227 | Request.prototype.then = function (fulfill, reject) { | |
1228 | return this.end(function(err, res) { | |
1229 | err ? reject(err) : fulfill(res); | |
1230 | }); | |
1231 | } | |
1232 | ||
1233 | /** | |
1234 | * Expose `Request`. | |
1235 | */ | |
1236 | ||
1237 | request.Request = Request; | |
1238 | ||
1239 | /** | |
1240 | * Issue a request: | |
1241 | * | |
1242 | * Examples: | |
1243 | * | |
1244 | * request('GET', '/users').end(callback) | |
1245 | * request('/users').end(callback) | |
1246 | * request('/users', callback) | |
1247 | * | |
1248 | * @param {String} method | |
1249 | * @param {String|Function} url or callback | |
1250 | * @return {Request} | |
1251 | * @api public | |
1252 | */ | |
1253 | ||
1254 | function request(method, url) { | |
1255 | // callback | |
1256 | if ('function' == typeof url) { | |
1257 | return new Request('GET', method).end(url); | |
1258 | } | |
1259 | ||
1260 | // url first | |
1261 | if (1 == arguments.length) { | |
1262 | return new Request('GET', method); | |
1263 | } | |
1264 | ||
1265 | return new Request(method, url); | |
1266 | } | |
1267 | ||
1268 | /** | |
1269 | * GET `url` with optional callback `fn(res)`. | |
1270 | * | |
1271 | * @param {String} url | |
1272 | * @param {Mixed|Function} data or fn | |
1273 | * @param {Function} fn | |
1274 | * @return {Request} | |
1275 | * @api public | |
1276 | */ | |
1277 | ||
1278 | request.get = function(url, data, fn){ | |
1279 | var req = request('GET', url); | |
1280 | if ('function' == typeof data) fn = data, data = null; | |
1281 | if (data) req.query(data); | |
1282 | if (fn) req.end(fn); | |
1283 | return req; | |
1284 | }; | |
1285 | ||
1286 | /** | |
1287 | * HEAD `url` with optional callback `fn(res)`. | |
1288 | * | |
1289 | * @param {String} url | |
1290 | * @param {Mixed|Function} data or fn | |
1291 | * @param {Function} fn | |
1292 | * @return {Request} | |
1293 | * @api public | |
1294 | */ | |
1295 | ||
1296 | request.head = function(url, data, fn){ | |
1297 | var req = request('HEAD', url); | |
1298 | if ('function' == typeof data) fn = data, data = null; | |
1299 | if (data) req.send(data); | |
1300 | if (fn) req.end(fn); | |
1301 | return req; | |
1302 | }; | |
1303 | ||
1304 | /** | |
1305 | * DELETE `url` with optional callback `fn(res)`. | |
1306 | * | |
1307 | * @param {String} url | |
1308 | * @param {Function} fn | |
1309 | * @return {Request} | |
1310 | * @api public | |
1311 | */ | |
1312 | ||
1313 | function del(url, fn){ | |
1314 | var req = request('DELETE', url); | |
1315 | if (fn) req.end(fn); | |
1316 | return req; | |
1317 | }; | |
1318 | ||
1319 | request['del'] = del; | |
1320 | request['delete'] = del; | |
1321 | ||
1322 | /** | |
1323 | * PATCH `url` with optional `data` and callback `fn(res)`. | |
1324 | * | |
1325 | * @param {String} url | |
1326 | * @param {Mixed} data | |
1327 | * @param {Function} fn | |
1328 | * @return {Request} | |
1329 | * @api public | |
1330 | */ | |
1331 | ||
1332 | request.patch = function(url, data, fn){ | |
1333 | var req = request('PATCH', url); | |
1334 | if ('function' == typeof data) fn = data, data = null; | |
1335 | if (data) req.send(data); | |
1336 | if (fn) req.end(fn); | |
1337 | return req; | |
1338 | }; | |
1339 | ||
1340 | /** | |
1341 | * POST `url` with optional `data` and callback `fn(res)`. | |
1342 | * | |
1343 | * @param {String} url | |
1344 | * @param {Mixed} data | |
1345 | * @param {Function} fn | |
1346 | * @return {Request} | |
1347 | * @api public | |
1348 | */ | |
1349 | ||
1350 | request.post = function(url, data, fn){ | |
1351 | var req = request('POST', url); | |
1352 | if ('function' == typeof data) fn = data, data = null; | |
1353 | if (data) req.send(data); | |
1354 | if (fn) req.end(fn); | |
1355 | return req; | |
1356 | }; | |
1357 | ||
1358 | /** | |
1359 | * PUT `url` with optional `data` and callback `fn(res)`. | |
1360 | * | |
1361 | * @param {String} url | |
1362 | * @param {Mixed|Function} data or fn | |
1363 | * @param {Function} fn | |
1364 | * @return {Request} | |
1365 | * @api public | |
1366 | */ | |
1367 | ||
1368 | request.put = function(url, data, fn){ | |
1369 | var req = request('PUT', url); | |
1370 | if ('function' == typeof data) fn = data, data = null; | |
1371 | if (data) req.send(data); | |
1372 | if (fn) req.end(fn); | |
1373 | return req; | |
1374 | }; | |
1375 | ||
1376 | /** | |
1377 | * Expose `request`. | |
1378 | */ | |
1379 | ||
1380 | module.exports = request; | |
1381 | ||
1382 | },{"emitter":1,"reduce":2}]},{},[3])(3) | |
1383 | }); |