3vot-model
Version:
3VOT Model based on SpineJS
1,787 lines (1,541 loc) • 41.3 kB
JavaScript
require=(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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.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})({"0tnfhX":[function(require,module,exports){
(function() {
var Action, Ajax, AjaxUtils, Collection, Extend, Include, Singleton, View, ajax_request, _3Model,
__slice = [].slice;
_3Model = require('3vot-model');
AjaxUtils = require("./ajax_utils");
Collection = require("./ajax_collection");
Singleton = require("./ajax_singleton");
Action = require("./ajax_action");
View = require("./ajax_view");
ajax_request = require("./ajax_request");
Include = {
ajax: function() {
return new Singleton(this);
},
url: function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
args.unshift(encodeURIComponent(this.id));
return AjaxUtils.generateURL.apply(AjaxUtils, [this].concat(__slice.call(args)));
}
};
Extend = {
ajax: function() {
return new Collection(this);
},
view: function() {
return new View(this);
},
action: function() {
return new Action(this);
},
url: function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
return AjaxUtils.generateURL.apply(AjaxUtils, [this].concat(__slice.call(args)));
}
};
Ajax = {
extended: function() {
this.fetch(this.ajaxFetch);
this.extend(Extend);
return this.include(Include);
},
ajaxFetch: function() {
var _ref;
return (_ref = this.ajax()).fetch.apply(_ref, arguments);
},
callAction: function() {
var _ref;
return (_ref = this.action()).call.apply(_ref, arguments);
},
callView: function() {
var _ref;
return (_ref = this.view()).call.apply(_ref, arguments);
},
destroy: function() {
var _ref;
return (_ref = this.action()).destroy.apply(_ref, arguments);
}
};
Ajax.Auto = {
extended: function() {
return this.change(this.ajaxChange);
},
ajaxChange: function(record, type, options) {
if (options == null) {
options = {};
}
if (options.ajax === false) {
return;
}
return record.ajax()[type](options.ajax || {}, options);
}
};
if (!_3Model.Model.host) {
_3Model.Model.host = "";
}
Ajax.request = ajax_request;
module.exports = Ajax;
}).call(this);
},{"./ajax_action":3,"./ajax_collection":4,"./ajax_request":5,"./ajax_singleton":6,"./ajax_utils":7,"./ajax_view":8}],"_3Ajax":[function(require,module,exports){
module.exports=require('0tnfhX');
},{}],3:[function(require,module,exports){
(function() {
var Action, ajax_request,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
ajax_request = require("./ajax_request");
Action = (function() {
function Action(model) {
this.model = model;
this.failResponse = __bind(this.failResponse, this);
this.recordsResponse = __bind(this.recordsResponse, this);
}
Action.prototype.destroy = function(values, options) {
var params;
if (values == null) {
values = {};
}
if (options == null) {
options = {};
}
options.url = this.model.url();
params = {
query: values
};
ajax_request.queueRequest.del(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordsResponse(res.body, options);
};
})(this));
return true;
};
Action.prototype.call = function(name, values, options) {
var params;
if (values == null) {
values = {};
}
if (options == null) {
options = {};
}
options.url = this.model.url() + "/actions/" + name;
params = {
data: values
};
ajax_request.queueRequest.post(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordsResponse(res.body, options);
};
})(this));
return true;
};
Action.prototype.recordsResponse = function(data, options) {
var _ref;
this.model.trigger('ajaxSuccess', data);
return (_ref = options.done) != null ? _ref.apply(this.model, [data]) : void 0;
};
Action.prototype.failResponse = function(error, options) {
var _ref;
this.model.trigger('ajaxError', error);
return (_ref = options.fail) != null ? _ref.apply(this.model, [error]) : void 0;
};
return Action;
})();
module.exports = Action;
}).call(this);
},{"./ajax_request":5}],4:[function(require,module,exports){
(function() {
var Collection, ajax_request,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
ajax_request = require("./ajax_request");
Collection = (function() {
function Collection(model) {
this.model = model;
this.failResponse = __bind(this.failResponse, this);
this.recordsResponse = __bind(this.recordsResponse, this);
}
Collection.prototype.find = function(id, params, options) {
var record;
if (options == null) {
options = {};
}
record = new this.model({
id: id
});
if (!options.url) {
options.url = record.url;
}
return ajax_request.queueRequest.get(params, options);
};
Collection.prototype.all = function(params, options) {
if (options == null) {
options = {};
}
if (!options.url) {
options.url = this.model.url();
}
return ajax_request.queueRequest.get(params, options);
};
Collection.prototype.fetch = function(params, options) {
var id;
if (params == null) {
params = {};
}
if (options == null) {
options = {};
}
if (id = params.id) {
delete params.id;
this.find(id, params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
_this.model.refresh(res.body, options);
return _this.recordsResponse(res, options);
};
})(this));
return true;
} else {
this.all(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
_this.model.refresh(res.body, options);
return _this.recordsResponse(res, options);
};
})(this));
return true;
}
};
Collection.prototype.recordsResponse = function(data, options) {
var _ref;
this.model.trigger('ajaxSuccess', data);
return (_ref = options.done) != null ? _ref.apply(this.model, [data]) : void 0;
};
Collection.prototype.failResponse = function(error, options) {
var _ref;
this.model.trigger('ajaxError', error);
return (_ref = options.fail) != null ? _ref.apply(this.model, [error]) : void 0;
};
return Collection;
})();
module.exports = Collection;
}).call(this);
},{"./ajax_request":5}],5:[function(require,module,exports){
(function() {
var AjaxRequest, AjaxUtils, superagent;
superagent = require("superagent");
AjaxUtils = require("./ajax_utils");
AjaxRequest = (function() {
function AjaxRequest() {}
AjaxRequest.promise = {
end: function() {}
};
AjaxRequest.enabled = true;
AjaxRequest.disable = function(callback) {
var e;
if (this.enabled) {
this.enabled = false;
try {
return callback();
} catch (_error) {
e = _error;
throw e;
} finally {
this.enabled = true;
}
} else {
return callback();
}
};
AjaxRequest.queueRequest = {
get: function(params, options) {
return AjaxRequest.executeRequest("get", params, options);
},
post: function(params, options) {
return AjaxRequest.executeRequest("post", params, options);
},
put: function(params, options) {
return AjaxRequest.executeRequest("put", params, options);
},
del: function(params, options) {
return AjaxRequest.executeRequest("del", params, options);
}
};
AjaxRequest.executeRequest = function(type, params, options) {
var request;
if (this.enabled === false) {
return this.promise;
}
request = superagent[type](options.url).type('json').set('X-Requested-With', 'XMLHttpRequest');
if (typeof request.withCredentials === "function") {
request.withCredentials();
}
if (options.error) {
request.on("error", options.error);
}
if (params.query) {
request = request.query(params.query);
}
if (params.data) {
if (typeof params.data !== 'string') {
params.data = JSON.stringify(params.data);
}
request = request.send(params.data);
}
return request;
};
return AjaxRequest;
})();
module.exports = AjaxRequest;
}).call(this);
},{"./ajax_utils":7,"superagent":9}],6:[function(require,module,exports){
(function() {
var AjaxUtils, Singleton, ajax_request, _3Model,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
ajax_request = require("./ajax_request");
AjaxUtils = require("./ajax_utils");
_3Model = require("3vot-model");
Singleton = (function() {
function Singleton(record) {
this.record = record;
this.failResponse = __bind(this.failResponse, this);
this.recordResponse = __bind(this.recordResponse, this);
this.model = this.record.constructor;
}
Singleton.prototype.reload = function(params, options) {
if (options == null) {
options = {};
}
params.data = this.record.toJSON();
options.url = options.url || AjaxUtils.getURL(this.record);
options.error = this.failResponse;
return ajax_request.queueRequest.get(params, options).end((function(_this) {
return function(res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordResponse(res.body, options);
};
})(this));
};
Singleton.prototype.create = function(params, options) {
if (options == null) {
options = {};
}
params.data = this.record.toJSON();
options.url = options.url || AjaxUtils.getCollectionURL(this.record);
return ajax_request.queueRequest.post(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordResponse(res.body, options);
};
})(this));
};
Singleton.prototype.update = function(params, options) {
if (options == null) {
options = {};
}
params.data = this.record.toJSON();
options.url = options.url || AjaxUtils.getURL(this.record);
return ajax_request.queueRequest.put(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordResponse(res.body, options);
};
})(this));
};
Singleton.prototype.destroy = function(params, options) {
if (options == null) {
options = {};
}
params.data = this.record.toJSON();
options.url = options.url || AjaxUtils.getURL(this.record);
options.error = this.failResponse;
return ajax_request.queueRequest.del(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordResponse(res.body, options);
};
})(this));
};
Singleton.prototype.recordResponse = function(data, options) {
var _ref;
if (options == null) {
options = {};
}
ajax_request.disable((function(_this) {
return function() {
if (!(_3Model.isBlank(data) || _this.record.destroyed)) {
if (data.id && _this.record.id !== data.id) {
_this.record.changeID(data.id);
}
return _this.record.refresh(data);
}
};
})(this));
this.record.trigger('ajaxSuccess', data);
return (_ref = options.done) != null ? _ref.apply(this.record) : void 0;
};
Singleton.prototype.failResponse = function(error, options) {
var _ref;
if (options == null) {
options = {};
}
this.record.trigger('ajaxError', error);
return (_ref = options.fail) != null ? _ref.apply(this.record, [error]) : void 0;
};
return Singleton;
})();
module.exports = Singleton;
}).call(this);
},{"./ajax_request":5,"./ajax_utils":7}],7:[function(require,module,exports){
(function() {
var AjaxUtils, _3Model,
__slice = [].slice;
_3Model = require("3vot-model");
AjaxUtils = (function() {
function AjaxUtils() {}
AjaxUtils.getURL = function(object) {
return (typeof object.url === "function" ? object.url() : void 0) || object.url;
};
AjaxUtils.getScope = function(object) {
return (typeof object.scope === "function" ? object.scope() : void 0) || object.scope;
};
AjaxUtils.generateURL = function() {
var args, collection, object, path, scope;
object = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (object.className) {
collection = object.className.toLowerCase() + 's';
scope = AjaxUtils.getScope(object);
} else {
if (typeof object.constructor.url === 'string') {
collection = object.constructor.url;
} else {
collection = object.constructor.className.toLowerCase() + 's';
}
scope = AjaxUtils.getScope(object) || AjaxUtils.getScope(object.constructor);
}
args.unshift(collection);
args.unshift(scope);
path = args.join('/');
path = path.replace(/(\/\/)/g, "/");
path = path.replace(/^\/|\/$/g, "");
if (path.indexOf("../") !== 0) {
return _3Model.Model.host + "/" + path;
} else {
return path;
}
};
AjaxUtils.getCollectionURL = function(object) {
if (object) {
if (typeof object.url === "function") {
return AjaxUtils.generateURL(object);
} else {
return object.url;
}
}
};
return AjaxUtils;
})();
module.exports = AjaxUtils;
}).call(this);
},{}],8:[function(require,module,exports){
(function() {
var View, ajax_request,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
ajax_request = require("./ajax_request");
View = (function() {
function View(model) {
this.model = model;
this.failResponse = __bind(this.failResponse, this);
this.recordsResponse = __bind(this.recordsResponse, this);
}
View.prototype.call = function(name, values, options) {
var params;
if (options == null) {
options = {};
}
options.url = this.model.url() + "/views/" + name;
params = {
query: values
};
ajax_request.queueRequest.get(params, options).end((function(_this) {
return function(err, res) {
if (err) {
return _this.failResponse(err, options);
} else if (res.status >= 400) {
return _this.failResponse(res.text, options);
}
return _this.recordsResponse(res.body, options);
};
})(this));
return true;
};
View.prototype.recordsResponse = function(data, options) {
var _ref;
this.model.trigger('ajaxSuccess', data);
return (_ref = options.done) != null ? _ref.apply(this.model, [data]) : void 0;
};
View.prototype.failResponse = function(error, options) {
var _ref;
this.model.trigger('ajaxError', error);
return (_ref = options.fail) != null ? _ref.apply(this.model, [error]) : void 0;
};
return View;
})();
module.exports = View;
}).call(this);
},{"./ajax_request":5}],9:[function(require,module,exports){
/**
* Module dependencies.
*/
var Emitter = require('emitter');
var reduce = require('reduce');
/**
* Root reference for iframes.
*/
var root = 'undefined' == typeof window
? this
: window;
/**
* Noop.
*/
function noop(){};
/**
* Check if `obj` is a host object,
* we don't want to serialize these :)
*
* TODO: future proof, move to compoent land
*
* @param {Object} obj
* @return {Boolean}
* @api private
*/
function isHost(obj) {
var str = {}.toString.call(obj);
switch (str) {
case '[object File]':
case '[object Blob]':
case '[object FormData]':
return true;
default:
return false;
}
}
/**
* Determine XHR.
*/
function getXHR() {
if (root.XMLHttpRequest
&& ('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) {}
}
return false;
}
/**
* 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, ''); };
/**
* Check if `obj` is an object.
*
* @param {Object} obj
* @return {Boolean}
* @api private
*/
function isObject(obj) {
return obj === Object(obj);
}
/**
* 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) {
if (null != obj[key]) {
pairs.push(encodeURIComponent(key)
+ '=' + encodeURIComponent(obj[key]));
}
}
return pairs.join('&');
}
/**
* 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 parts;
var pair;
for (var i = 0, len = pairs.length; i < len; ++i) {
pair = pairs[i];
parts = pair.split('=');
obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[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: 'application/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;
lines.pop(); // trailing CRLF
for (var i = 0, len = lines.length; i < len; ++i) {
line = lines[i];
index = line.indexOf(':');
field = line.slice(0, index).toLowerCase();
val = trim(line.slice(index + 1));
fields[field] = val;
}
return fields;
}
/**
* Return the mime type for the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
function type(str){
return str.split(/ *; */).shift();
};
/**
* Return header field parameters.
*
* @param {String} str
* @return {Object}
* @api private
*/
function params(str){
return reduce(str.split(/ *; */), function(obj, str){
var parts = str.split(/ *= */)
, key = parts.shift()
, val = parts.shift();
if (key && val) obj[key] = val;
return obj;
}, {});
};
/**
* 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, options) {
options = options || {};
this.req = req;
this.xhr = this.req.xhr;
this.text = this.xhr.responseText;
this.setStatusProperties(this.xhr.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);
this.body = this.req.method != 'HEAD'
? this.parseBody(this.text)
: null;
}
/**
* Get case-insensitive `field` value.
*
* @param {String} field
* @return {String}
* @api public
*/
Response.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
*/
Response.prototype.setHeaderProperties = function(header){
// content-type
var ct = this.header['content-type'] || '';
this.type = type(ct);
// params
var obj = params(ct);
for (var key in obj) this[key] = obj[key];
};
/**
* 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];
return parse
? parse(str)
: null;
};
/**
* 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
*/
Response.prototype.setStatusProperties = function(status){
var type = status / 100 | 0;
// status / class
this.status = status;
this.statusType = type;
// basics
this.info = 1 == type;
this.ok = 2 == 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 || 1223 == status;
this.badRequest = 400 == status;
this.unauthorized = 401 == status;
this.notAcceptable = 406 == status;
this.notFound = 404 == status;
this.forbidden = 403 == status;
};
/**
* Return an `Error` representative of this response.
*
* @return {Error}
* @api public
*/
Response.prototype.toError = function(){
var req = this.req;
var method = req.method;
var path = req.path;
var msg = 'cannot ' + method + ' ' + path + ' (' + this.status + ')';
var err = new Error(msg);
err.status = this.status;
err.method = method;
err.path = path;
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;
Emitter.call(this);
this._query = this._query || [];
this.method = method;
this.url = url;
this.header = {};
this._header = {};
this.on('end', function(){
var res = new Response(self);
if ('HEAD' == method) res.text = null;
self.callback(null, res);
});
}
/**
* Mixin `Emitter`.
*/
Emitter(Request.prototype);
/**
* Set timeout to `ms`.
*
* @param {Number} ms
* @return {Request} for chaining
* @api public
*/
Request.prototype.timeout = function(ms){
this._timeout = ms;
return this;
};
/**
* Clear previous timeout.
*
* @return {Request} for chaining
* @api public
*/
Request.prototype.clearTimeout = function(){
this._timeout = 0;
clearTimeout(this._timer);
return this;
};
/**
* Abort the request, and clear potential timeout.
*
* @return {Request}
* @api public
*/
Request.prototype.abort = function(){
if (this.aborted) return;
this.aborted = true;
this.xhr.abort();
this.clearTimeout();
this.emit('abort');
return this;
};
/**
* Set header `field` to `val`, or multiple fields with one object.
*
* 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
*/
Request.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;
};
/**
* Get case-insensitive header `field` value.
*
* @param {String} field
* @return {String}
* @api private
*/
Request.prototype.getHeader = function(field){
return this._header[field.toLowerCase()];
};
/**
* 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
* @return {Request} for chaining
* @api public
*/
Request.prototype.auth = function(user, pass){
var str = btoa(user + ':' + pass);
this.set('Authorization', 'Basic ' + str);
return this;
};
/**
* 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;
};
/**
* Send `data`, defaulting the `.type()` to "json" when
* an object is given.
*
* Examples:
*
* // querystring
* request.get('/search')
* .end(callback)
*
* // multiple data "writes"
* request.get('/search')
* .send({ search: 'query' })
* .send({ range: '1..5' })
* .send({ order: 'desc' })
* .end(callback)
*
* // 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
*/
Request.prototype.send = function(data){
var obj = isObject(data);
var type = this.getHeader('Content-Type');
// merge
if (obj && isObject(this._data)) {
for (var key in data) {
this._data[key] = data[key];
}
} else if ('string' == typeof data) {
if (!type) this.type('form');
type = this.getHeader('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 (!obj) return this;
if (!type) this.type('json');
return this;
};
/**
* 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){
var fn = this._callback;
if (2 == fn.length) return fn(err, res);
if (err) return this.emit('error', err);
fn(res);
};
/**
* Invoke callback with x-domain error.
*
* @api private
*/
Request.prototype.crossDomainError = function(){
var err = new Error('Origin is not allowed by Access-Control-Allow-Origin');
err.crossDomain = true;
this.callback(err);
};
/**
* Invoke callback with timeout error.
*
* @api private
*/
Request.prototype.timeoutError = function(){
var timeout = this._timeout;
var err = new Error('timeout of ' + timeout + 'ms exceeded');
err.timeout = timeout;
this.callback(err);
};
/**
* 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
*/
Request.prototype.withCredentials = function(){
this._withCredentials = true;
return this;
};
/**
* Initiate request, invoking callback `fn(res)`
* with an instanceof `Response`.
*
* @param {Function} fn
* @return {Request} for chaining
* @api public
*/
Request.prototype.end = function(fn){
var self = this;
var xhr = this.xhr = getXHR();
var query = this._query.join('&');
var timeout = this._timeout;
var data = this._data;
// store callback
this._callback = fn || noop;
// state change
xhr.onreadystatechange = function(){
if (4 != xhr.readyState) return;
if (0 == xhr.status) {
if (self.aborted) return self.timeoutError();
return self.crossDomainError();
}
self.emit('end');
};
// progress
if (xhr.upload) {
xhr.upload.onprogress = function(e){
e.percent = e.loaded / e.total * 100;
self.emit('progress', e);
};
}
// timeout
if (timeout && !this._timer) {
this._timer = setTimeout(function(){
self.abort();
}, timeout);
}
// querystring
if (query) {
query = request.serializeObject(query);
this.url += ~this.url.indexOf('?')
? '&' + query
: '?' + query;
}
// initiate request
xhr.open(this.method, this.url, true);
// CORS
if (this._withCredentials) xhr.withCredentials = true;
// body
if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) {
// serialize stuff
var serialize = request.serialize[this.getHeader('Content-Type')];
if (serialize) data = serialize(data);
}
// set header fields
for (var field in this.header) {
if (null == this.header[field]) continue;
xhr.setRequestHeader(field, this.header[field]);
}
// send stuff
xhr.send(data);
return this;
};
/**
* Expose `Request`.
*/
request.Request = Request;
/**
* Issue a request:
*
* Examples:
*
* request('GET', '/users').end(callback)
* request('/users').end(callback)
* request('/users', callback)
*
* @param {String} method
* @param {String|Function} url or callback
* @return {Request}
* @api public
*/
function request(method, url) {
// callback
if ('function' == typeof url) {
return new Request('GET', method).end(url);
}
// url first
if (1 == arguments.length) {
return new Request('GET', method);
}
return new Request(method, url);
}
/**
* 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.send(data);
if (fn) req.end(fn);
return req;
};
/**
* DELETE `url` with optional callback `fn(res)`.
*
* @param {String} url
* @param {Function} fn
* @return {Request}
* @api public
*/
request.del = function(url, fn){
var req = request('DELETE', url);
if (fn) req.end(fn);
return req;
};
/**
* 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;
};
/**
* Expose `request`.
*/
module.exports = request;
},{"emitter":10,"reduce":11}],10:[function(require,module,exports){
/**
* Expose `Emitter`.
*/
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 = 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){
var self = this;
this._callbacks = this._callbacks || {};
function on() {
self.off(event, on);
fn.apply(this, arguments);
}
fn._off = on;
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 = 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 i = callbacks.indexOf(fn._off || fn);
if (~i) callbacks.splice(i, 1);
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;
};
},{}],11:[function(require,module,exports){
/**
* Reduce `arr` with `fn`.
*
* @param {Array} arr
* @param {Function} fn
* @param {Mixed} initial
*
* TODO: combatible error handling?
*/
module.exports = function(arr, fn, initial){
var idx = 0;
var len = arr.length;
var curr = arguments.length == 3
? initial
: arr[idx++];
while (idx < len) {
curr = fn.call(null, curr, arr[idx], ++idx, arr);
}
return curr;
};
},{}]},{},["0tnfhX"])