(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.superagent = f()}})(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= 500. * * @param {Number} count * @param {Function} [fn] * @return {Request} for chaining * @api public */ RequestBase.prototype.retry = function retry(count, fn){ // Default to 1 if no count passed or true if (arguments.length === 0 || count === true) count = 1; if (count <= 0) count = 0; this._maxRetries = count; this._retries = 0; this._retryCallback = fn; return this; }; var ERROR_CODES = [ 'ECONNRESET', 'ETIMEDOUT', 'EADDRINFO', 'ESOCKETTIMEDOUT' ]; /** * Determine if a request should be retried. * (Borrowed from segmentio/superagent-retry) * * @param {Error} err * @param {Response} [res] * @returns {Boolean} */ RequestBase.prototype._shouldRetry = function(err, res) { if (!this._maxRetries || this._retries++ >= this._maxRetries) { return false; } if (this._retryCallback) { try { var override = this._retryCallback(err, res); if (override === true) return true; if (override === false) return false; // undefined falls back to defaults } catch(e) { console.error(e); } } if (res && res.status && res.status >= 500 && res.status != 501) return true; if (err) { if (err.code && ~ERROR_CODES.indexOf(err.code)) return true; // Superagent timeout if (err.timeout && err.code == 'ECONNABORTED') return true; if (err.crossDomain) return true; } return false; }; /** * Retry request * * @return {Request} for chaining * @api private */ RequestBase.prototype._retry = function() { this.clearTimeout(); // node if (this.req) { this.req = null; this.req = this.request(); } this._aborted = false; this.timedout = false; return this._end(); }; /** * Promise support * * @param {Function} resolve * @param {Function} [reject] * @return {Request} */ RequestBase.prototype.then = function then(resolve, reject) { if (!this._fullfilledPromise) { var self = this; if (this._endCalled) { console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"); } this._fullfilledPromise = new Promise(function(innerResolve, innerReject) { self.end(function(err, res) { if (err) innerReject(err); else innerResolve(res); }); }); } return this._fullfilledPromise.then(resolve, reject); }; RequestBase.prototype.catch = function(cb) { return this.then(undefined, cb); }; /** * Allow for extension */ RequestBase.prototype.use = function use(fn) { fn(this); return this; }; RequestBase.prototype.ok = function(cb) { if ('function' !== typeof cb) throw Error("Callback required"); this._okCallback = cb; return this; }; RequestBase.prototype._isResponseOK = function(res) { if (!res) { return false; } if (this._okCallback) { return this._okCallback(res); } return res.status >= 200 && res.status < 300; }; /** * Get request header `field`. * Case-insensitive. * * @param {String} field * @return {String} * @api public */ RequestBase.prototype.get = function(field){ return this._header[field.toLowerCase()]; }; /** * Get case-insensitive header `field` value. * This is a deprecated internal API. Use `.get(field)` instead. * * (getHeader is no longer used internally by the superagent code base) * * @param {String} field * @return {String} * @api private * @deprecated */ RequestBase.prototype.getHeader = RequestBase.prototype.get; /** * Set header `field` to `val`, or multiple fields with one object. * Case-insensitive. * * Examples: * * req.get('/') * .set('Accept', 'application/json') * .set('X-API-Key', 'foobar') * .end(callback); * * req.get('/') * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) * .end(callback); * * @param {String|Object} field * @param {String} val * @return {Request} for chaining * @api public */ RequestBase.prototype.set = function(field, val){ if (isObject(field)) { for (var key in field) { this.set(key, field[key]); } return this; } this._header[field.toLowerCase()] = val; this.header[field] = val; return this; }; /** * Remove header `field`. * Case-insensitive. * * Example: * * req.get('/') * .unset('User-Agent') * .end(callback); * * @param {String} field */ RequestBase.prototype.unset = function(field){ delete this._header[field.toLowerCase()]; delete this.header[field]; return this; }; /** * Write the field `name` and `val`, or multiple fields with one object * for "multipart/form-data" request bodies. * * ``` js * request.post('/upload') * .field('foo', 'bar') * .end(callback); * * request.post('/upload') * .field({ foo: 'bar', baz: 'qux' }) * .end(callback); * ``` * * @param {String|Object} name * @param {String|Blob|File|Buffer|fs.ReadStream} val * @return {Request} for chaining * @api public */ RequestBase.prototype.field = function(name, val) { // name should be either a string or an object. if (null === name || undefined === name) { throw new Error('.field(name, val) name can not be empty'); } if (this._data) { console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"); } if (isObject(name)) { for (var key in name) { this.field(key, name[key]); } return this; } if (Array.isArray(val)) { for (var i in val) { this.field(name, val[i]); } return this; } // val should be defined now if (null === val || undefined === val) { throw new Error('.field(name, val) val can not be empty'); } if ('boolean' === typeof val) { val = '' + val; } this._getFormData().append(name, val); return this; }; /** * Abort the request, and clear potential timeout. * * @return {Request} * @api public */ RequestBase.prototype.abort = function(){ if (this._aborted) { return this; } this._aborted = true; this.xhr && this.xhr.abort(); // browser this.req && this.req.abort(); // node this.clearTimeout(); this.emit('abort'); return this; }; RequestBase.prototype._auth = function(user, pass, options, base64Encoder) { switch (options.type) { case 'basic': this.set('Authorization', 'Basic ' + base64Encoder(user + ':' + pass)); break; case 'auto': this.username = user; this.password = pass; break; case 'bearer': // usage would be .auth(accessToken, { type: 'bearer' }) this.set('Authorization', 'Bearer ' + user); break; } return this; }; /** * Enable transmission of cookies with x-domain requests. * * Note that for this to work the origin must not be * using "Access-Control-Allow-Origin" with a wildcard, * and also must set "Access-Control-Allow-Credentials" * to "true". * * @api public */ RequestBase.prototype.withCredentials = function(on) { // This is browser-only functionality. Node side is no-op. if (on == undefined) on = true; this._withCredentials = on; return this; }; /** * Set the max redirects to `n`. Does noting in browser XHR implementation. * * @param {Number} n * @return {Request} for chaining * @api public */ RequestBase.prototype.redirects = function(n){ this._maxRedirects = n; return this; }; /** * Maximum size of buffered response body, in bytes. Counts uncompressed size. * Default 200MB. * * @param {Number} n * @return {Request} for chaining */ RequestBase.prototype.maxResponseSize = function(n){ if ('number' !== typeof n) { throw TypeError("Invalid argument"); } this._maxResponseSize = n; return this; }; /** * Convert to a plain javascript object (not JSON string) of scalar properties. * Note as this method is designed to return a useful non-this value, * it cannot be chained. * * @return {Object} describing method, url, and data of this request * @api public */ RequestBase.prototype.toJSON = function() { return { method: this.method, url: this.url, data: this._data, headers: this._header, }; }; /** * Send `data` as the request body, defaulting the `.type()` to "json" when * an object is given. * * Examples: * * // manual json * request.post('/user') * .type('json') * .send('{"name":"tj"}') * .end(callback) * * // auto json * request.post('/user') * .send({ name: 'tj' }) * .end(callback) * * // manual x-www-form-urlencoded * request.post('/user') * .type('form') * .send('name=tj') * .end(callback) * * // auto x-www-form-urlencoded * request.post('/user') * .type('form') * .send({ name: 'tj' }) * .end(callback) * * // defaults to x-www-form-urlencoded * request.post('/user') * .send('name=tobi') * .send('species=ferret') * .end(callback) * * @param {String|Object} data * @return {Request} for chaining * @api public */ RequestBase.prototype.send = function(data){ var isObj = isObject(data); var type = this._header['content-type']; if (this._formData) { console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"); } if (isObj && !this._data) { if (Array.isArray(data)) { this._data = []; } else if (!this._isHost(data)) { this._data = {}; } } else if (data && this._data && this._isHost(this._data)) { throw Error("Can't merge these send calls"); } // merge if (isObj && isObject(this._data)) { for (var key in data) { this._data[key] = data[key]; } } else if ('string' == typeof data) { // default to x-www-form-urlencoded if (!type) this.type('form'); type = this._header['content-type']; if ('application/x-www-form-urlencoded' == type) { this._data = this._data ? this._data + '&' + data : data; } else { this._data = (this._data || '') + data; } } else { this._data = data; } if (!isObj || this._isHost(data)) { return this; } // default to json if (!type) this.type('json'); return this; }; /** * Sort `querystring` by the sort function * * * Examples: * * // default order * request.get('/user') * .query('name=Nick') * .query('search=Manny') * .sortQuery() * .end(callback) * * // customized sort function * request.get('/user') * .query('name=Nick') * .query('search=Manny') * .sortQuery(function(a, b){ * return a.length - b.length; * }) * .end(callback) * * * @param {Function} sort * @return {Request} for chaining * @api public */ RequestBase.prototype.sortQuery = function(sort) { // _sort default to true but otherwise can be a function or boolean this._sort = typeof sort === 'undefined' ? true : sort; return this; }; /** * Compose querystring to append to req.url * * @api private */ RequestBase.prototype._finalizeQueryString = function(){ var query = this._query.join('&'); if (query) { this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') + query; } this._query.length = 0; // Makes the call idempotent if (this._sort) { var index = this.url.indexOf('?'); if (index >= 0) { var queryArr = this.url.substring(index + 1).split('&'); if ('function' === typeof this._sort) { queryArr.sort(this._sort); } else { queryArr.sort(); } this.url = this.url.substring(0, index) + '?' + queryArr.join('&'); } } }; // For backwards compat only RequestBase.prototype._appendQueryString = function() {console.trace("Unsupported");} /** * Invoke callback with timeout error. * * @api private */ RequestBase.prototype._timeoutError = function(reason, timeout, errno){ if (this._aborted) { return; } var err = new Error(reason + timeout + 'ms exceeded'); err.timeout = timeout; err.code = 'ECONNABORTED'; err.errno = errno; this.timedout = true; this.abort(); this.callback(err); }; RequestBase.prototype._setTimeouts = function() { var self = this; // deadline if (this._timeout && !this._timer) { this._timer = setTimeout(function(){ self._timeoutError('Timeout of ', self._timeout, 'ETIME'); }, this._timeout); } // response timeout if (this._responseTimeout && !this._responseTimeoutTimer) { this._responseTimeoutTimer = setTimeout(function(){ self._timeoutError('Response timeout of ', self._responseTimeout, 'ETIMEDOUT'); }, this._responseTimeout); } }; },{"./is-object":2}],4:[function(require,module,exports){ 'use strict'; /** * Module dependencies. */ var utils = require('./utils'); /** * Expose `ResponseBase`. */ module.exports = ResponseBase; /** * Initialize a new `ResponseBase`. * * @api public */ function ResponseBase(obj) { if (obj) return mixin(obj); } /** * Mixin the prototype properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in ResponseBase.prototype) { obj[key] = ResponseBase.prototype[key]; } return obj; } /** * Get case-insensitive `field` value. * * @param {String} field * @return {String} * @api public */ ResponseBase.prototype.get = function(field) { return this.header[field.toLowerCase()]; }; /** * Set header related properties: * * - `.type` the content type without params * * A response of "Content-Type: text/plain; charset=utf-8" * will provide you with a `.type` of "text/plain". * * @param {Object} header * @api private */ ResponseBase.prototype._setHeaderProperties = function(header){ // TODO: moar! // TODO: make this a util // content-type var ct = header['content-type'] || ''; this.type = utils.type(ct); // params var params = utils.params(ct); for (var key in params) this[key] = params[key]; this.links = {}; // links try { if (header.link) { this.links = utils.parseLinks(header.link); } } catch (err) { // ignore } }; /** * Set flags such as `.ok` based on `status`. * * For example a 2xx response will give you a `.ok` of __true__ * whereas 5xx will be __false__ and `.error` will be __true__. The * `.clientError` and `.serverError` are also available to be more * specific, and `.statusType` is the class of error ranging from 1..5 * sometimes useful for mapping respond colors etc. * * "sugar" properties are also defined for common cases. Currently providing: * * - .noContent * - .badRequest * - .unauthorized * - .notAcceptable * - .notFound * * @param {Number} status * @api private */ ResponseBase.prototype._setStatusProperties = function(status){ var type = status / 100 | 0; // status / class this.status = this.statusCode = status; this.statusType = type; // basics this.info = 1 == type; this.ok = 2 == type; this.redirect = 3 == type; this.clientError = 4 == type; this.serverError = 5 == type; this.error = (4 == type || 5 == type) ? this.toError() : false; // sugar this.accepted = 202 == status; this.noContent = 204 == status; this.badRequest = 400 == status; this.unauthorized = 401 == status; this.notAcceptable = 406 == status; this.forbidden = 403 == status; this.notFound = 404 == status; }; },{"./utils":5}],5:[function(require,module,exports){ 'use strict'; /** * Return the mime type for the given `str`. * * @param {String} str * @return {String} * @api private */ exports.type = function(str){ return str.split(/ *; */).shift(); }; /** * Return header field parameters. * * @param {String} str * @return {Object} * @api private */ exports.params = function(str){ return str.split(/ *; */).reduce(function(obj, str){ var parts = str.split(/ *= */); var key = parts.shift(); var val = parts.shift(); if (key && val) obj[key] = val; return obj; }, {}); }; /** * Parse Link header fields. * * @param {String} str * @return {Object} * @api private */ exports.parseLinks = function(str){ return str.split(/ *, */).reduce(function(obj, str){ var parts = str.split(/ *; */); var url = parts[0].slice(1, -1); var rel = parts[1].split(/ *= */)[1].slice(1, -1); obj[rel] = url; return obj; }, {}); }; /** * Strip content related fields from `header`. * * @param {Object} header * @return {Object} header * @api private */ exports.cleanHeader = function(header, changesOrigin){ delete header['content-type']; delete header['content-length']; delete header['transfer-encoding']; delete header['host']; // secuirty if (changesOrigin) { delete header['authorization']; delete header['cookie']; } return header; }; },{}],6:[function(require,module,exports){ /** * Expose `Emitter`. */ if (typeof module !== 'undefined') { module.exports = Emitter; } /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); }; /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks['$' + event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks['$' + event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = [].slice.call(arguments, 1) , callbacks = this._callbacks['$' + event]; if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; return this._callbacks['$' + event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; },{}],7:[function(require,module,exports){ /** * Root reference for iframes. */ var root; if (typeof window !== 'undefined') { // Browser window root = window; } else if (typeof self !== 'undefined') { // Web Worker root = self; } else { // Other environments console.warn("Using browser-only version of superagent in non-browser environment"); root = this; } var Emitter = require('component-emitter'); var RequestBase = require('./request-base'); var isObject = require('./is-object'); var ResponseBase = require('./response-base'); var Agent = require('./agent-base'); /** * Noop. */ function noop(){}; /** * Expose `request`. */ var request = exports = module.exports = function(method, url) { // callback if ('function' == typeof url) { return new exports.Request('GET', method).end(url); } // url first if (1 == arguments.length) { return new exports.Request('GET', method); } return new exports.Request(method, url); } exports.Request = Request; /** * Determine XHR. */ request.getXHR = function () { if (root.XMLHttpRequest && (!root.location || 'file:' != root.location.protocol || !root.ActiveXObject)) { return new XMLHttpRequest; } else { try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} } throw Error("Browser-only version of superagent could not find XHR"); }; /** * Removes leading and trailing whitespace, added to support IE. * * @param {String} s * @return {String} * @api private */ var trim = ''.trim ? function(s) { return s.trim(); } : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; /** * Serialize the given `obj`. * * @param {Object} obj * @return {String} * @api private */ function serialize(obj) { if (!isObject(obj)) return obj; var pairs = []; for (var key in obj) { pushEncodedKeyValuePair(pairs, key, obj[key]); } return pairs.join('&'); } /** * Helps 'serialize' with serializing arrays. * Mutates the pairs array. * * @param {Array} pairs * @param {String} key * @param {Mixed} val */ function pushEncodedKeyValuePair(pairs, key, val) { if (val != null) { if (Array.isArray(val)) { val.forEach(function(v) { pushEncodedKeyValuePair(pairs, key, v); }); } else if (isObject(val)) { for(var subkey in val) { pushEncodedKeyValuePair(pairs, key + '[' + subkey + ']', val[subkey]); } } else { pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(val)); } } else if (val === null) { pairs.push(encodeURIComponent(key)); } } /** * Expose serialization method. */ request.serializeObject = serialize; /** * Parse the given x-www-form-urlencoded `str`. * * @param {String} str * @return {Object} * @api private */ function parseString(str) { var obj = {}; var pairs = str.split('&'); var pair; var pos; for (var i = 0, len = pairs.length; i < len; ++i) { pair = pairs[i]; pos = pair.indexOf('='); if (pos == -1) { obj[decodeURIComponent(pair)] = ''; } else { obj[decodeURIComponent(pair.slice(0, pos))] = decodeURIComponent(pair.slice(pos + 1)); } } return obj; } /** * Expose parser. */ request.parseString = parseString; /** * Default MIME type map. * * superagent.types.xml = 'application/xml'; * */ request.types = { html: 'text/html', json: 'application/json', xml: 'text/xml', urlencoded: 'application/x-www-form-urlencoded', 'form': 'application/x-www-form-urlencoded', 'form-data': 'application/x-www-form-urlencoded' }; /** * Default serialization map. * * superagent.serialize['application/xml'] = function(obj){ * return 'generated xml here'; * }; * */ request.serialize = { 'application/x-www-form-urlencoded': serialize, 'application/json': JSON.stringify, }; /** * Default parsers. * * superagent.parse['application/xml'] = function(str){ * return { object parsed from str }; * }; * */ request.parse = { 'application/x-www-form-urlencoded': parseString, 'application/json': JSON.parse, }; /** * Parse the given header `str` into * an object containing the mapped fields. * * @param {String} str * @return {Object} * @api private */ function parseHeader(str) { var lines = str.split(/\r?\n/); var fields = {}; var index; var line; var field; var val; for (var i = 0, len = lines.length; i < len; ++i) { line = lines[i]; index = line.indexOf(':'); if (index === -1) { // could be empty line, just skip it continue; } field = line.slice(0, index).toLowerCase(); val = trim(line.slice(index + 1)); fields[field] = val; } return fields; } /** * Check if `mime` is json or has +json structured syntax suffix. * * @param {String} mime * @return {Boolean} * @api private */ function isJSON(mime) { // should match /json or +json // but not /json-seq return /[\/+]json($|[^-\w])/.test(mime); } /** * Initialize a new `Response` with the given `xhr`. * * - set flags (.ok, .error, etc) * - parse header * * Examples: * * Aliasing `superagent` as `request` is nice: * * request = superagent; * * We can use the promise-like API, or pass callbacks: * * request.get('/').end(function(res){}); * request.get('/', function(res){}); * * Sending data can be chained: * * request * .post('/user') * .send({ name: 'tj' }) * .end(function(res){}); * * Or passed to `.send()`: * * request * .post('/user') * .send({ name: 'tj' }, function(res){}); * * Or passed to `.post()`: * * request * .post('/user', { name: 'tj' }) * .end(function(res){}); * * Or further reduced to a single call for simple cases: * * request * .post('/user', { name: 'tj' }, function(res){}); * * @param {XMLHTTPRequest} xhr * @param {Object} options * @api private */ function Response(req) { this.req = req; this.xhr = this.req.xhr; // responseText is accessible only if responseType is '' or 'text' and on older browsers this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined') ? this.xhr.responseText : null; this.statusText = this.req.xhr.statusText; var status = this.xhr.status; // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request if (status === 1223) { status = 204; } this._setStatusProperties(status); this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but // getResponseHeader still works. so we get content-type even if getting // other headers fails. this.header['content-type'] = this.xhr.getResponseHeader('content-type'); this._setHeaderProperties(this.header); if (null === this.text && req._responseType) { this.body = this.xhr.response; } else { this.body = this.req.method != 'HEAD' ? this._parseBody(this.text ? this.text : this.xhr.response) : null; } } ResponseBase(Response.prototype); /** * Parse the given body `str`. * * Used for auto-parsing of bodies. Parsers * are defined on the `superagent.parse` object. * * @param {String} str * @return {Mixed} * @api private */ Response.prototype._parseBody = function(str) { var parse = request.parse[this.type]; if (this.req._parser) { return this.req._parser(this, str); } if (!parse && isJSON(this.type)) { parse = request.parse['application/json']; } return parse && str && (str.length || str instanceof Object) ? parse(str) : null; }; /** * Return an `Error` representative of this response. * * @return {Error} * @api public */ Response.prototype.toError = function(){ var req = this.req; var method = req.method; var url = req.url; var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; var err = new Error(msg); err.status = this.status; err.method = method; err.url = url; return err; }; /** * Expose `Response`. */ request.Response = Response; /** * Initialize a new `Request` with the given `method` and `url`. * * @param {String} method * @param {String} url * @api public */ function Request(method, url) { var self = this; this._query = this._query || []; this.method = method; this.url = url; this.header = {}; // preserves header name case this._header = {}; // coerces header names to lowercase this.on('end', function(){ var err = null; var res = null; try { res = new Response(self); } catch(e) { err = new Error('Parser is unable to parse the response'); err.parse = true; err.original = e; // issue #675: return the raw response if the response parsing fails if (self.xhr) { // ie9 doesn't have 'response' property err.rawResponse = typeof self.xhr.responseType == 'undefined' ? self.xhr.responseText : self.xhr.response; // issue #876: return the http status code if the response parsing fails err.status = self.xhr.status ? self.xhr.status : null; err.statusCode = err.status; // backwards-compat only } else { err.rawResponse = null; err.status = null; } return self.callback(err); } self.emit('response', res); var new_err; try { if (!self._isResponseOK(res)) { new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); } } catch(custom_err) { new_err = custom_err; // ok() callback can throw } // #1000 don't catch errors from the callback to avoid double calling it if (new_err) { new_err.original = err; new_err.response = res; new_err.status = res.status; self.callback(new_err, res); } else { self.callback(null, res); } }); } /** * Mixin `Emitter` and `RequestBase`. */ Emitter(Request.prototype); RequestBase(Request.prototype); /** * Set Content-Type to `type`, mapping values from `request.types`. * * Examples: * * superagent.types.xml = 'application/xml'; * * request.post('/') * .type('xml') * .send(xmlstring) * .end(callback); * * request.post('/') * .type('application/xml') * .send(xmlstring) * .end(callback); * * @param {String} type * @return {Request} for chaining * @api public */ Request.prototype.type = function(type){ this.set('Content-Type', request.types[type] || type); return this; }; /** * Set Accept to `type`, mapping values from `request.types`. * * Examples: * * superagent.types.json = 'application/json'; * * request.get('/agent') * .accept('json') * .end(callback); * * request.get('/agent') * .accept('application/json') * .end(callback); * * @param {String} accept * @return {Request} for chaining * @api public */ Request.prototype.accept = function(type){ this.set('Accept', request.types[type] || type); return this; }; /** * Set Authorization field value with `user` and `pass`. * * @param {String} user * @param {String} [pass] optional in case of using 'bearer' as type * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic') * @return {Request} for chaining * @api public */ Request.prototype.auth = function(user, pass, options){ if (1 === arguments.length) pass = ''; if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options options = pass; pass = ''; } if (!options) { options = { type: 'function' === typeof btoa ? 'basic' : 'auto', }; } var encoder = function(string) { if ('function' === typeof btoa) { return btoa(string); } throw new Error('Cannot use basic auth, btoa is not a function'); }; return this._auth(user, pass, options, encoder); }; /** * Add query-string `val`. * * Examples: * * request.get('/shoes') * .query('size=10') * .query({ color: 'blue' }) * * @param {Object|String} val * @return {Request} for chaining * @api public */ Request.prototype.query = function(val){ if ('string' != typeof val) val = serialize(val); if (val) this._query.push(val); return this; }; /** * Queue the given `file` as an attachment to the specified `field`, * with optional `options` (or filename). * * ``` js * request.post('/upload') * .attach('content', new Blob(['hey!'], { type: "text/html"})) * .end(callback); * ``` * * @param {String} field * @param {Blob|File} file * @param {String|Object} options * @return {Request} for chaining * @api public */ Request.prototype.attach = function(field, file, options){ if (file) { if (this._data) { throw Error("superagent can't mix .send() and .attach()"); } this._getFormData().append(field, file, options || file.name); } return this; }; Request.prototype._getFormData = function(){ if (!this._formData) { this._formData = new root.FormData(); } return this._formData; }; /** * Invoke the callback with `err` and `res` * and handle arity check. * * @param {Error} err * @param {Response} res * @api private */ Request.prototype.callback = function(err, res){ if (this._shouldRetry(err, res)) { return this._retry(); } var fn = this._callback; this.clearTimeout(); if (err) { if (this._maxRetries) err.retries = this._retries - 1; this.emit('error', err); } fn(err, res); }; /** * Invoke callback with x-domain error. * * @api private */ Request.prototype.crossDomainError = function(){ 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.'); err.crossDomain = true; err.status = this.status; err.method = this.method; err.url = this.url; this.callback(err); }; // This only warns, because the request is still likely to work Request.prototype.buffer = Request.prototype.ca = Request.prototype.agent = function(){ console.warn("This is not supported in browser version of superagent"); return this; }; // This throws, because it can't send/receive data as expected Request.prototype.pipe = Request.prototype.write = function(){ throw Error("Streaming is not supported in browser version of superagent"); }; /** * Check if `obj` is a host object, * we don't want to serialize these :) * * @param {Object} obj * @return {Boolean} * @api private */ Request.prototype._isHost = function _isHost(obj) { // Native objects stringify to [object File], [object Blob], [object FormData], etc. return obj && 'object' === typeof obj && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== '[object Object]'; } /** * Initiate request, invoking callback `fn(res)` * with an instanceof `Response`. * * @param {Function} fn * @return {Request} for chaining * @api public */ Request.prototype.end = function(fn){ if (this._endCalled) { console.warn("Warning: .end() was called twice. This is not supported in superagent"); } this._endCalled = true; // store callback this._callback = fn || noop; // querystring this._finalizeQueryString(); return this._end(); }; Request.prototype._end = function() { var self = this; var xhr = (this.xhr = request.getXHR()); var data = this._formData || this._data; this._setTimeouts(); // state change xhr.onreadystatechange = function(){ var readyState = xhr.readyState; if (readyState >= 2 && self._responseTimeoutTimer) { clearTimeout(self._responseTimeoutTimer); } if (4 != readyState) { return; } // In IE9, reads to any property (e.g. status) off of an aborted XHR will // result in the error "Could not complete the operation due to error c00c023f" var status; try { status = xhr.status } catch(e) { status = 0; } if (!status) { if (self.timedout || self._aborted) return; return self.crossDomainError(); } self.emit('end'); }; // progress var handleProgress = function(direction, e) { if (e.total > 0) { e.percent = e.loaded / e.total * 100; } e.direction = direction; self.emit('progress', e); }; if (this.hasListeners('progress')) { try { xhr.onprogress = handleProgress.bind(null, 'download'); if (xhr.upload) { xhr.upload.onprogress = handleProgress.bind(null, 'upload'); } } catch(e) { // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist. // Reported here: // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context } } // initiate request try { if (this.username && this.password) { xhr.open(this.method, this.url, true, this.username, this.password); } else { xhr.open(this.method, this.url, true); } } catch (err) { // see #1149 return this.callback(err); } // CORS if (this._withCredentials) xhr.withCredentials = true; // body if (!this._formData && 'GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !this._isHost(data)) { // serialize stuff var contentType = this._header['content-type']; var serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : '']; if (!serialize && isJSON(contentType)) { serialize = request.serialize['application/json']; } if (serialize) data = serialize(data); } // set header fields for (var field in this.header) { if (null == this.header[field]) continue; if (this.header.hasOwnProperty(field)) xhr.setRequestHeader(field, this.header[field]); } if (this._responseType) { xhr.responseType = this._responseType; } // send stuff this.emit('request', this); // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing) // We need null here if data is undefined xhr.send(typeof data !== 'undefined' ? data : null); return this; }; request.agent = function() { return new Agent(); }; ["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(function(method) { Agent.prototype[method.toLowerCase()] = function(url, fn) { var req = new request.Request(method, url); this._setDefaults(req); if (fn) { req.end(fn); } return req; }; }); Agent.prototype.del = Agent.prototype['delete']; /** * GET `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} [data] or fn * @param {Function} [fn] * @return {Request} * @api public */ request.get = function(url, data, fn) { var req = request('GET', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.query(data); if (fn) req.end(fn); return req; }; /** * HEAD `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} [data] or fn * @param {Function} [fn] * @return {Request} * @api public */ request.head = function(url, data, fn) { var req = request('HEAD', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.query(data); if (fn) req.end(fn); return req; }; /** * OPTIONS query to `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} [data] or fn * @param {Function} [fn] * @return {Request} * @api public */ request.options = function(url, data, fn) { var req = request('OPTIONS', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * DELETE `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} [data] * @param {Function} [fn] * @return {Request} * @api public */ function del(url, data, fn) { var req = request('DELETE', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.send(data); if (fn) req.end(fn); return req; } request['del'] = del; request['delete'] = del; /** * PATCH `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} [data] * @param {Function} [fn] * @return {Request} * @api public */ request.patch = function(url, data, fn) { var req = request('PATCH', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * POST `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} [data] * @param {Function} [fn] * @return {Request} * @api public */ request.post = function(url, data, fn) { var req = request('POST', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * PUT `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} [data] or fn * @param {Function} [fn] * @return {Request} * @api public */ request.put = function(url, data, fn) { var req = request('PUT', url); if ('function' == typeof data) (fn = data), (data = null); if (data) req.send(data); if (fn) req.end(fn); return req; }; },{"./agent-base":1,"./is-object":2,"./request-base":3,"./response-base":4,"component-emitter":6}]},{},[7])(7) });