diff --git a/browser/dist/traverson-angular.external.js b/browser/dist/traverson-angular.external.js new file mode 100644 index 0000000..dbcf12a --- /dev/null +++ b/browser/dist/traverson-angular.external.js @@ -0,0 +1,5156 @@ +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);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 1) {exprList.shift();} + var result = this._trace(exprList, json, ['$'], currParent, currParentProperty, callback); + result = result.filter(function (ea) {return ea && !ea.isParentSelector;}); + + if (!result.length) {return wrap ? [] : undefined;} + if (result.length === 1 && !wrap && !Array.isArray(result[0].value)) { + return this._getPreferredOutput(result[0]); + } + return result.reduce(function (result, ea) { + var valOrPath = self._getPreferredOutput(ea); + if (flatten && Array.isArray(valOrPath)) { + result = result.concat(valOrPath); + } + else { + result.push(valOrPath); + } + return result; + }, []); +}; + +// PRIVATE METHODS + +JSONPath.prototype._getPreferredOutput = function (ea) { + var resultType = this.currResultType; + switch (resultType) { + case 'all': + ea.path = typeof ea.path === 'string' ? ea.path : JSONPath.toPathString(ea.path); + return ea; + case 'value': case 'parent': case 'parentProperty': + return ea[resultType]; + case 'path': + return JSONPath.toPathString(ea[resultType]); + case 'pointer': + return JSONPath.toPointer(ea.path); + } +}; + +JSONPath.prototype._handleCallback = function (fullRetObj, callback, type) { + if (callback) { + var preferredOutput = this._getPreferredOutput(fullRetObj); + fullRetObj.path = typeof fullRetObj.path === 'string' ? fullRetObj.path : JSONPath.toPathString(fullRetObj.path); + callback(preferredOutput, type, fullRetObj); + } +}; + +JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, callback) { + // No expr to follow? return path and value as the result of this trace branch + var retObj, self = this; + if (!expr.length) { + retObj = {path: path, value: val, parent: parent, parentProperty: parentPropName}; + this._handleCallback(retObj, callback, 'value'); + return retObj; + } + + var loc = expr[0], x = expr.slice(1); + + // We need to gather the return value of recursive trace calls in order to + // do the parent sel computation. + var ret = []; + function addRet (elems) {ret = ret.concat(elems);} + + if (val && Object.prototype.hasOwnProperty.call(val, loc)) { // simple case--directly follow property + addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback)); + } + else if (loc === '*') { // all child properties + this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, x, v, p, par, pr, cb) { + addRet(self._trace(unshift(m, x), v, p, par, pr, cb)); + }); + } + else if (loc === '..') { // all descendent parent properties + addRet(this._trace(x, val, path, parent, parentPropName, callback)); // Check remaining expression with val's immediate children + this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, x, v, p, par, pr, cb) { + // We don't join m and x here because we only want parents, not scalar values + if (typeof v[m] === 'object') { // Keep going with recursive descent on val's object children + addRet(self._trace(unshift(l, x), v[m], push(p, m), v, m, cb)); + } + }); + } + else if (loc[0] === '(') { // [(expr)] (dynamic property/index) + if (this.currPreventEval) { + throw new Error('Eval [(expr)] prevented in JSONPath expression.'); + } + // As this will resolve to a property name (but we don't know it yet), property and parent information is relative to the parent of the property to which this expression will resolve + addRet(this._trace(unshift(this._eval(loc, val, path[path.length - 1], path.slice(0, -1), parent, parentPropName), x), val, path, parent, parentPropName, callback)); + } + // The parent sel computation is handled in the frame above using the + // ancestor object of val + else if (loc === '^') { + // This is not a final endpoint, so we do not invoke the callback here + return path.length ? { + path: path.slice(0, -1), + expr: x, + isParentSelector: true + } : []; + } + else if (loc === '~') { // property name + retObj = {path: push(path, loc), value: parentPropName, parent: parent, parentProperty: null}; + this._handleCallback(retObj, callback, 'property'); + return retObj; + } + else if (loc === '$') { // root only + addRet(this._trace(x, val, path, null, null, callback)); + } + else if (loc.indexOf('?(') === 0) { // [?(expr)] (filtering) + if (this.currPreventEval) { + throw new Error('Eval [?(expr)] prevented in JSONPath expression.'); + } + this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, x, v, p, par, pr, cb) { + if (self._eval(l.replace(/^\?\((.*?)\)$/, '$1'), v[m], m, p, par, pr)) { + addRet(self._trace(unshift(m, x), v, p, par, pr, cb)); + } + }); + } + else if (loc.indexOf(',') > -1) { // [name1,name2,...] + var parts, i; + for (parts = loc.split(','), i = 0; i < parts.length; i++) { + addRet(this._trace(unshift(parts[i], x), val, path, parent, parentPropName, callback)); + } + } + else if (loc[0] === '@') { // value type: @boolean(), etc. + var addType = false; + var valueType = loc.slice(1, -2); + switch (valueType) { + case 'scalar': + if (!val || (['object', 'function'].indexOf(typeof val) === -1)) { + addType = true; + } + break; + case 'boolean': case 'string': case 'undefined': case 'function': + if (typeof val === valueType) { + addType = true; + } + break; + case 'number': + if (typeof val === valueType && isFinite(val)) { + addType = true; + } + break; + case 'nonFinite': + if (typeof val === 'number' && !isFinite(val)) { + addType = true; + } + break; + case 'object': + if (val && typeof val === valueType) { + addType = true; + } + break; + case 'array': + if (Array.isArray(val)) { + addType = true; + } + break; + case 'other': + addType = this.currOtherTypeCallback(val, path, parent, parentPropName); + break; + case 'integer': + if (val === +val && isFinite(val) && !(val % 1)) { + addType = true; + } + break; + case 'null': + if (val === null) { + addType = true; + } + break; + } + if (addType) { + retObj = {path: path, value: val, parent: parent, parentProperty: parentPropName}; + this._handleCallback(retObj, callback, 'value'); + return retObj; + } + } + else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] Python slice syntax + addRet(this._slice(loc, x, val, path, parent, parentPropName, callback)); + } + + // We check the resulting values for parent selections. For parent + // selections we discard the value object and continue the trace with the + // current val object + return ret.reduce(function (all, ea) { + return all.concat(ea.isParentSelector ? self._trace(ea.expr, val, ea.path, parent, parentPropName, callback) : ea); + }, []); +}; + +JSONPath.prototype._walk = function (loc, expr, val, path, parent, parentPropName, callback, f) { + var i, n, m; + if (Array.isArray(val)) { + for (i = 0, n = val.length; i < n; i++) { + f(i, loc, expr, val, path, parent, parentPropName, callback); + } + } + else if (typeof val === 'object') { + for (m in val) { + if (Object.prototype.hasOwnProperty.call(val, m)) { + f(m, loc, expr, val, path, parent, parentPropName, callback); + } + } + } +}; + +JSONPath.prototype._slice = function (loc, expr, val, path, parent, parentPropName, callback) { + if (!Array.isArray(val)) {return;} + var i, + len = val.length, parts = loc.split(':'), + start = (parts[0] && parseInt(parts[0], 10)) || 0, + end = (parts[1] && parseInt(parts[1], 10)) || len, + step = (parts[2] && parseInt(parts[2], 10)) || 1; + start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start); + end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end); + var ret = []; + for (i = start; i < end; i += step) { + ret = ret.concat(this._trace(unshift(i, expr), val, path, parent, parentPropName, callback)); + } + return ret; +}; + +JSONPath.prototype._eval = function (code, _v, _vname, path, parent, parentPropName) { + if (!this._obj || !_v) {return false;} + if (code.indexOf('@parentProperty') > -1) { + this.currSandbox._$_parentProperty = parentPropName; + code = code.replace(/@parentProperty/g, '_$_parentProperty'); + } + if (code.indexOf('@parent') > -1) { + this.currSandbox._$_parent = parent; + code = code.replace(/@parent/g, '_$_parent'); + } + if (code.indexOf('@property') > -1) { + this.currSandbox._$_property = _vname; + code = code.replace(/@property/g, '_$_property'); + } + if (code.indexOf('@path') > -1) { + this.currSandbox._$_path = JSONPath.toPathString(path.concat([_vname])); + code = code.replace(/@path/g, '_$_path'); + } + if (code.match(/@([\.\s\)\[])/)) { + this.currSandbox._$_v = _v; + code = code.replace(/@([\.\s\)\[])/g, '_$_v$1'); + } + try { + return vm.runInNewContext(code, this.currSandbox); + } + catch (e) { + console.log(e); + throw new Error('jsonPath: ' + e.message + ': ' + code); + } +}; + +// PUBLIC CLASS PROPERTIES AND METHODS + +// Could store the cache object itself +JSONPath.cache = {}; + +JSONPath.toPathString = function (pathArr) { + var i, n, x = pathArr, p = '$'; + for (i = 1, n = x.length; i < n; i++) { + if (!(/^(~|\^|@.*?\(\))$/).test(x[i])) { + p += (/^[0-9*]+$/).test(x[i]) ? ('[' + x[i] + ']') : ("['" + x[i] + "']"); + } + } + return p; +}; + +JSONPath.toPointer = function (pointer) { + var i, n, x = pointer, p = ''; + for (i = 1, n = x.length; i < n; i++) { + if (!(/^(~|\^|@.*?\(\))$/).test(x[i])) { + p += '/' + x[i].toString() + .replace(/\~/g, '~0') + .replace(/\//g, '~1'); + } + } + return p; +}; + +JSONPath.toPathArray = function (expr) { + var cache = JSONPath.cache; + if (cache[expr]) {return cache[expr];} + var subx = []; + var normalized = expr + // Properties + .replace(/@(?:null|boolean|number|string|integer|undefined|nonFinite|scalar|array|object|function|other)\(\)/g, ';$&;') + // Parenthetical evaluations (filtering and otherwise), directly within brackets or single quotes + .replace(/[\['](\??\(.*?\))[\]']/g, function ($0, $1) {return '[#' + (subx.push($1) - 1) + ']';}) + // Escape periods and tildes within properties + .replace(/\['([^'\]]*)'\]/g, function ($0, prop) { + return "['" + prop.replace(/\./g, '%@%').replace(/~/g, '%%@@%%') + "']"; + }) + // Properties operator + .replace(/~/g, ';~;') + // Split by property boundaries + .replace(/'?\.'?(?![^\[]*\])|\['?/g, ';') + // Reinsert periods within properties + .replace(/%@%/g, '.') + // Reinsert tildes within properties + .replace(/%%@@%%/g, '~') + // Parent + .replace(/(?:;)?(\^+)(?:;)?/g, function ($0, ups) {return ';' + ups.split('').join(';') + ';';}) + // Descendents + .replace(/;;;|;;/g, ';..;') + // Remove trailing + .replace(/;$|'?\]|'$/g, ''); + + var exprList = normalized.split(';').map(function (expr) { + var match = expr.match(/#([0-9]+)/); + return !match || !match[1] ? expr : subx[match[1]]; + }); + cache[expr] = exprList; + return cache[expr]; +}; + +// For backward compatibility (deprecated) +JSONPath.eval = function (obj, expr, opts) { + return JSONPath(opts, expr, obj); +}; + +if (typeof define === 'function' && define.amd) { + define(function () {return JSONPath;}); +} +else if (isNode) { + module.exports = JSONPath; +} +else { + glbl.jsonPath = { // Deprecated + eval: JSONPath.eval + }; + glbl.JSONPath = JSONPath; +} +}(this || self, typeof require === 'undefined' ? null : require)); + +},{"vm":50}],5:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],6:[function(require,module,exports){ +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) + +void (function(root, factory) { + if (typeof define === "function" && define.amd) { + define(factory) + } else if (typeof exports === "object") { + module.exports = factory() + } else { + root.resolveUrl = factory() + } +}(this, function() { + + function resolveUrl(/* ...urls */) { + var numUrls = arguments.length + + if (numUrls === 0) { + throw new Error("resolveUrl requires at least one argument; got none.") + } + + var base = document.createElement("base") + base.href = arguments[0] + + if (numUrls === 1) { + return base.href + } + + var head = document.getElementsByTagName("head")[0] + head.insertBefore(base, head.firstChild) + + var a = document.createElement("a") + var resolved + + for (var index = 1; index < numUrls; index++) { + a.href = arguments[index] + resolved = a.href + base.href = resolved + } + + head.removeChild(base) + + return resolved + } + + return resolveUrl + +})); + +},{}],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('emitter'); +var requestBase = require('./request-base'); +var isObject = require('./is-object'); + +/** + * Noop. + */ + +function noop(){}; + +/** + * Expose `request`. + */ + +var request = module.exports = require('./request').bind(null, 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 verison 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: '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; +} + +/** + * Check if `mime` is json or has +json structured syntax suffix. + * + * @param {String} mime + * @return {Boolean} + * @api private + */ + +function isJSON(mime) { + return /[\/+]json\b/.test(mime); +} + +/** + * 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 str.split(/ *; */).reduce(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; + // 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; + 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 ? this.text : this.xhr.response) + : 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]; + if (!parse && isJSON(this.type)) { + parse = request.parse['application/json']; + } + return parse && str && (str.length || str instanceof Object) + ? 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){ + // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request + if (status === 1223) { + status = 204; + } + + 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.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.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 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 + err.rawResponse = self.xhr && self.xhr.responseText ? self.xhr.responseText : null; + // issue #876: return the http status code if the response parsing fails + err.statusCode = self.xhr && self.xhr.status ? self.xhr.status : null; + return self.callback(err); + } + + self.emit('response', res); + + var new_err; + try { + if (res.status < 200 || res.status >= 300) { + new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); + new_err.original = err; + new_err.response = res; + new_err.status = res.status; + } + } catch(e) { + new_err = e; // #985 touching res may cause INVALID_STATE_ERR on old Android + } + + // #1000 don't catch errors from the callback to avoid double calling it + if (new_err) { + self.callback(new_err, res); + } else { + self.callback(null, res); + } + }); +} + +/** + * Mixin `Emitter` and `requestBase`. + */ + +Emitter(Request.prototype); +for (var key in requestBase) { + Request.prototype[key] = requestBase[key]; +} + +/** + * 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 responseType to `val`. Presently valid responseTypes are 'blob' and + * 'arraybuffer'. + * + * Examples: + * + * req.get('/') + * .responseType('blob') + * .end(callback); + * + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +Request.prototype.responseType = function(val){ + this._responseType = val; + 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 + * @param {Object} options with 'type' property 'auto' or 'basic' (default 'basic') + * @return {Request} for chaining + * @api public + */ + +Request.prototype.auth = function(user, pass, options){ + if (!options) { + options = { + type: 'basic' + } + } + + switch (options.type) { + case 'basic': + var str = btoa(user + ':' + pass); + this.set('Authorization', 'Basic ' + str); + break; + + case 'auto': + this.username = user; + this.password = pass; + break; + } + 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; +}; + +/** + * Queue the given `file` as an attachment to the specified `field`, + * with optional `filename`. + * + * ``` js + * request.post('/upload') + * .attach('content', new Blob(['hey!'], { type: "text/html"})) + * .end(callback); + * ``` + * + * @param {String} field + * @param {Blob|File} file + * @param {String} filename + * @return {Request} for chaining + * @api public + */ + +Request.prototype.attach = function(field, file, filename){ + this._getFormData().append(field, file, filename || 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){ + var fn = this._callback; + this.clearTimeout(); + 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); +}; + +/** + * 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); +}; + +/** + * Compose querystring to append to req.url + * + * @api private + */ + +Request.prototype._appendQueryString = function(){ + var query = this._query.join('&'); + if (query) { + this.url += ~this.url.indexOf('?') + ? '&' + query + : '?' + query; + } +}; + +/** + * 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 = request.getXHR(); + var timeout = this._timeout; + var data = this._formData || this._data; + + // store callback + this._callback = fn || noop; + + // state change + xhr.onreadystatechange = function(){ + if (4 != xhr.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 (0 == status) { + if (self.timedout) return self._timeoutError(); + if (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 + } + } + + // timeout + if (timeout && !this._timer) { + this._timer = setTimeout(function(){ + self.timedout = true; + self.abort(); + }, timeout); + } + + // querystring + this._appendQueryString(); + + // initiate request + if (this.username && this.password) { + xhr.open(this.method, this.url, true, this.username, this.password); + } else { + 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 && !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; + 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; +}; + + +/** + * Expose `Request`. + */ + +request.Request = Request; + +/** + * 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; +}; + +/** + * 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 callback `fn(res)`. + * + * @param {String} url + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +function del(url, fn){ + var req = request('DELETE', url); + 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; +}; + +},{"./is-object":8,"./request":10,"./request-base":9,"emitter":2}],8:[function(require,module,exports){ +/** + * Check if `obj` is an object. + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ + +function isObject(obj) { + return null !== obj && 'object' === typeof obj; +} + +module.exports = isObject; + +},{}],9:[function(require,module,exports){ +/** + * Module of mixed-in functions shared between node and client code + */ +var isObject = require('./is-object'); + +/** + * Clear previous timeout. + * + * @return {Request} for chaining + * @api public + */ + +exports.clearTimeout = function _clearTimeout(){ + this._timeout = 0; + clearTimeout(this._timer); + return this; +}; + +/** + * Override default response body parser + * + * This function will be called to convert incoming data into request.body + * + * @param {Function} + * @api public + */ + +exports.parse = function parse(fn){ + this._parser = fn; + return this; +}; + +/** + * Override default request body serializer + * + * This function will be called to convert data set via .send or .attach into payload to send + * + * @param {Function} + * @api public + */ + +exports.serialize = function serialize(fn){ + this._serializer = fn; + return this; +}; + +/** + * Set timeout to `ms`. + * + * @param {Number} ms + * @return {Request} for chaining + * @api public + */ + +exports.timeout = function timeout(ms){ + this._timeout = ms; + return this; +}; + +/** + * Promise support + * + * @param {Function} resolve + * @param {Function} reject + * @return {Request} + */ + +exports.then = function then(resolve, reject) { + if (!this._fullfilledPromise) { + var self = this; + 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); +} + +exports.catch = function(cb) { + return this.then(undefined, cb); +}; + +/** + * Allow for extension + */ + +exports.use = function use(fn) { + fn(this); + return this; +} + + +/** + * Get request header `field`. + * Case-insensitive. + * + * @param {String} field + * @return {String} + * @api public + */ + +exports.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 + */ + +exports.getHeader = exports.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 + */ + +exports.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 + */ +exports.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 + */ +exports.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 (isObject(name)) { + for (var key in name) { + this.field(key, name[key]); + } + return this; + } + + // val should be defined now + if (null === val || undefined === val) { + throw new Error('.field(name, val) val can not be empty'); + } + this._getFormData().append(name, val); + return this; +}; + +/** + * Abort the request, and clear potential timeout. + * + * @return {Request} + * @api public + */ +exports.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; +}; + +/** + * 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 + */ + +exports.withCredentials = function(){ + // This is browser-only functionality. Node side is no-op. + this._withCredentials = true; + return this; +}; + +/** + * Set the max redirects to `n`. Does noting in browser XHR implementation. + * + * @param {Number} n + * @return {Request} for chaining + * @api public + */ + +exports.redirects = function(n){ + this._maxRedirects = 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 + */ + +exports.toJSON = function(){ + return { + method: this.method, + url: this.url, + data: this._data, + headers: this._header + }; +}; + +/** + * 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 + */ + +exports._isHost = function _isHost(obj) { + var str = {}.toString.call(obj); + + switch (str) { + case '[object File]': + case '[object Blob]': + case '[object FormData]': + return true; + default: + return false; + } +} + +/** + * 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 + */ + +exports.send = function(data){ + var obj = isObject(data); + var type = this._header['content-type']; + + // merge + if (obj && 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 (!obj || this._isHost(data)) return this; + + // default to json + if (!type) this.type('json'); + return this; +}; + +},{"./is-object":8}],10:[function(require,module,exports){ +// The node and browser modules expose versions of this with the +// appropriate constructor function bound as first argument +/** + * 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(RequestConstructor, method, url) { + // callback + if ('function' == typeof url) { + return new RequestConstructor('GET', method).end(url); + } + + // url first + if (2 == arguments.length) { + return new RequestConstructor('GET', method); + } + + return new RequestConstructor(method, url); +} + +module.exports = request; + +},{}],11:[function(require,module,exports){ +'use strict'; + +// TODO Replace by a proper lightweight logging module, suited for the browser + +var enabled = false; +function Logger(id) { + if (id == null) { + id = ''; + } + this.id = id; +} + +Logger.prototype.enable = function() { + this.enabled = true; +}; + +Logger.prototype.debug = function(message) { + if (enabled) { + console.log(this.id + '/debug: ' + message); + } +}; + +Logger.prototype.info = function(message) { + if (enabled) { + console.log(this.id + '/info: ' + message); + } +}; + +Logger.prototype.warn = function(message) { + if (enabled) { + console.log(this.id + '/warn: ' + message); + } +}; + +Logger.prototype.error = function(message) { + if (enabled) { + console.log(this.id + '/error: ' + message); + } +}; + +function minilog(id) { + return new Logger(id); +} + +minilog.enable = function() { + enabled = true; +}; + +module.exports = minilog; + +},{}],12:[function(require,module,exports){ +'use strict'; + +module.exports = { + isArray: function(o) { + if (o == null) { + return false; + } + return Object.prototype.toString.call(o) === '[object Array]'; + } +}; + +},{}],13:[function(require,module,exports){ +'use strict'; + +var superagent = require('superagent'); + +function Request() {} + +Request.prototype.get = function(uri, options, callback) { + return mapRequest(superagent.get(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.post = function(uri, options, callback) { + return mapRequest(superagent.post(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.put = function(uri, options, callback) { + return mapRequest(superagent.put(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.patch = function(uri, options, callback) { + return mapRequest(superagent.patch(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.del = function(uri, options, callback) { + return mapRequest(superagent.del(uri), options) + .end(handleResponse(callback)); +}; + +function mapRequest(superagentRequest, options) { + options = options || {}; + mapQuery(superagentRequest, options); + mapHeaders(superagentRequest, options); + mapAuth(superagentRequest, options); + mapBody(superagentRequest, options); + mapForm(superagentRequest, options); + mapWithCredentials(superagentRequest, options); + return superagentRequest; +} + +function mapQuery(superagentRequest, options) { + var qs = options.qs; + if (qs != null) { + superagentRequest = superagentRequest.query(qs); + } +} + +function mapHeaders(superagentRequest, options) { + var headers = options.headers; + if (headers != null) { + superagentRequest = superagentRequest.set(headers); + } +} + +function mapAuth(superagentRequest, options) { + var auth = options.auth; + if (auth != null) { + superagentRequest = superagentRequest.auth( + auth.user || auth.username, + auth.pass || auth.password + ); + } +} + +function mapBody(superagentRequest, options) { + if (options != null) { + var body = options.body; + if (body != null) { + superagentRequest = superagentRequest.send(body); + } + } +} + +function mapForm(superagentRequest, options) { + if (options != null) { + var form = options.form; + if (form != null) { + // content-type header needs to be set before calling send AND it NEEDS + // to be all lower case otherwise superagent automatically sets + // application/json as content-type :-/ + superagentRequest = superagentRequest.set('content-type', + 'application/x-www-form-urlencoded'); + superagentRequest = superagentRequest.send(form); + } + } +} + +function mapWithCredentials(superagentRequest, options) { + if (options != null) { + var withCredentials = options.withCredentials; + if (withCredentials === true) { + // https://visionmedia.github.io/superagent/#cors + superagentRequest.withCredentials(); + } + } +} + +// map XHR response object properties to Node.js request lib's response object +// properties +function mapResponse(response) { + response.body = response.text; + response.statusCode = response.status; + return response; +} + +function handleResponse(callback) { + return function(err, response) { + if (err) { + if (!response) { + // network error or timeout, no response + return callback(err); + } else { + // Since 1.0.0 superagent calls the callback with an error if the status + // code of the response is not in the 2xx range. In this cases, it also + // passes in the response. To align things with request, call the + // callback without the error but just with the response. + callback(null, mapResponse(response)); + } + } else { + callback(null, mapResponse(response)); + } + }; +} + +module.exports = new Request(); + +},{"superagent":7}],14:[function(require,module,exports){ +'use strict'; + +/* + * Copied from underscore.string module. Just the functions we need, to reduce + * the browserified size. + */ + +var _s = { + startsWith: function(str, starts) { + if (starts === '') return true; + if (str == null || starts == null) return false; + str = String(str); starts = String(starts); + return str.length >= starts.length && str.slice(0, starts.length) === starts; + }, + + endsWith: function(str, ends){ + if (ends === '') return true; + if (str == null || ends == null) return false; + str = String(str); ends = String(ends); + return str.length >= ends.length && + str.slice(str.length - ends.length) === ends; + }, + + splice: function(str, i, howmany, substr){ + var arr = _s.chars(str); + arr.splice(~~i, ~~howmany, substr); + return arr.join(''); + }, + + contains: function(str, needle){ + if (needle === '') return true; + if (str == null) return false; + return String(str).indexOf(needle) !== -1; + }, + + chars: function(str) { + if (str == null) return []; + return String(str).split(''); + } +}; + +module.exports = _s; + +},{}],15:[function(require,module,exports){ +'use strict'; + +var resolveUrl = require('resolve-url'); + +exports.resolve = function(from, to) { + return resolveUrl(from, to); +}; + +},{"resolve-url":6}],16:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError + , log = minilog('traverson'); + +exports.abortTraversal = function abortTraversal() { + log.debug('aborting link traversal'); + this.aborted = true; + if (this.currentRequest) { + log.debug('request in progress. trying to abort it, too.'); + this.currentRequest.abort(); + } +}; + +exports.registerAbortListener = function registerAbortListener(t, callback) { + if (t.currentRequest) { + t.currentRequest.on('abort', function() { + exports.callCallbackOnAbort(t); + }); + } +}; + +exports.callCallbackOnAbort = function callCallbackOnAbort(t) { + log.debug('link traversal aborted'); + if (!t.callbackHasBeenCalledAfterAbort) { + t.callbackHasBeenCalledAfterAbort = true; + t.callback(exports.abortError(), t); + } +}; + +exports.abortError = function abortError() { + var error = createError('Link traversal process has been aborted.', + errors.TraversalAbortedError); + error.aborted = true; + return error; +}; + +},{"./errors":19,"minilog":11}],17:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('./abort_traversal') + , applyTransforms = require('./transforms/apply_transforms') + , httpRequests = require('./http_requests') + , isContinuation = require('./is_continuation') + , walker = require('./walker'); + +var checkHttpStatus = require('./transforms/check_http_status') + , continuationToDoc = + require('./transforms/continuation_to_doc') + , continuationToResponse = + require('./transforms/continuation_to_response') + , convertEmbeddedDocToResponse = + require('./transforms/convert_embedded_doc_to_response') + , extractDoc = require('./transforms/extract_doc') + , extractResponse = require('./transforms/extract_response') + , extractUrl = require('./transforms/extract_url') + , fetchLastResource = require('./transforms/fetch_last_resource') + , executeLastHttpRequest = require('./transforms/execute_last_http_request') + , executeHttpRequest = require('./transforms/execute_http_request') + , parse = require('./transforms/parse'); + +/** + * Starts the link traversal process and end it with an HTTP get. + */ +exports.get = function(t, callback) { + var transformsAfterLastStep; + if (t.convertResponseToObject) { + transformsAfterLastStep = [ + continuationToDoc, + fetchLastResource, + checkHttpStatus, + parse, + extractDoc, + ]; + } else { + transformsAfterLastStep = [ + continuationToResponse, + fetchLastResource, + convertEmbeddedDocToResponse, + extractResponse, + ]; + } + walker.walk(t, transformsAfterLastStep, callback); + return createTraversalHandle(t); +}; + +/** + * Special variant of get() that does not execute the last request but instead + * yields the last URL to the callback. + */ +exports.getUrl = function(t, callback) { + walker.walk(t, [ extractUrl ], callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP POST request with the + * given body to the last URL. Passes the HTTP response of the POST request to + * the callback. + */ +exports.post = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.post, + callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP PUT request with the + * given body to the last URL. Passes the HTTP response of the PUT request to + * the callback. + */ +exports.put = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.put, + callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP PATCH request with the + * given body to the last URL. Passes the HTTP response of the PATCH request to + * the callback. + */ +exports.patch = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.patch, + callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP DELETE request to the + * last URL. Passes the HTTP response of the DELETE request to the callback. + */ +exports.delete = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.del, + callback); + return createTraversalHandle(t); +}; + +function walkAndExecute(t, request, method, callback) { + var transformsAfterLastStep; + if (t.convertResponseToObject) { + transformsAfterLastStep = [ + executeHttpRequest, + checkHttpStatus, + parse, + extractDoc, + ]; + } else { + transformsAfterLastStep = [ + executeLastHttpRequest, + ]; + } + + t.lastMethod = method; + walker.walk(t, transformsAfterLastStep, callback); +} + +function createTraversalHandle(t) { + return { + abort: t.abortTraversal + }; +} + +},{"./abort_traversal":16,"./http_requests":20,"./is_continuation":21,"./transforms/apply_transforms":27,"./transforms/check_http_status":28,"./transforms/continuation_to_doc":29,"./transforms/continuation_to_response":30,"./transforms/convert_embedded_doc_to_response":31,"./transforms/execute_http_request":33,"./transforms/execute_last_http_request":34,"./transforms/extract_doc":35,"./transforms/extract_response":36,"./transforms/extract_url":37,"./transforms/fetch_last_resource":38,"./transforms/parse":41,"./walker":47,"minilog":11}],18:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , standardRequest = require('request') + , util = require('util'); + +var actions = require('./actions') + , abortTraversal = require('./abort_traversal').abortTraversal + , errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError + , mediaTypeRegistry = require('./media_type_registry') + , mediaTypes = require('./media_types') + , mergeRecursive = require('./merge_recursive'); + +var log = minilog('traverson'); + +// Maintenance notice: The constructor is usually called without arguments, the +// mediaType parameter is only used when cloning the request builder in +// newRequest(). +function Builder(mediaType) { + this.mediaType = mediaType || mediaTypes.CONTENT_NEGOTIATION; + this.adapter = this._createAdapter(this.mediaType); + this.contentNegotiation = true; + this.convertResponseToObjectFlag = false; + this.links = []; + this.jsonParser = JSON.parse; + this.requestModuleInstance = standardRequest; + this.requestOptions = {}; + this.resolveRelativeFlag = false; + this.preferEmbedded = false; + this.lastTraversalState = null; + this.continuation = null; + // Maintenance notice: when extending the list of configuration parameters, + // also extend this.newRequest and initFromTraversalState +} + +Builder.prototype._createAdapter = function(mediaType) { + var AdapterType = mediaTypeRegistry.get(mediaType); + if (!AdapterType) { + throw createError('Unknown or unsupported media type: ' + mediaType, + errors.UnsupportedMediaType); + } + log.debug('creating new ' + AdapterType.name); + return new AdapterType(log); +}; + +/** + * Returns a new builder instance which is basically a clone of this builder + * instance. This allows you to initiate a new request but keeping all the setup + * (start URL, template parameters, request options, body parser, ...). + */ +Builder.prototype.newRequest = function() { + var clonedRequestBuilder = new Builder(this.getMediaType()); + clonedRequestBuilder.contentNegotiation = + this.doesContentNegotiation(); + clonedRequestBuilder.convertResponseToObject(this.convertsResponseToObject()); + clonedRequestBuilder.from(shallowCloneArray(this.getFrom())); + clonedRequestBuilder.withTemplateParameters( + cloneArrayOrObject(this.getTemplateParameters())); + clonedRequestBuilder.withRequestOptions( + cloneArrayOrObject(this.getRequestOptions())); + clonedRequestBuilder.withRequestLibrary(this.getRequestLibrary()); + clonedRequestBuilder.parseResponseBodiesWith(this.getJsonParser()); + clonedRequestBuilder.resolveRelative(this.doesResolveRelative()); + clonedRequestBuilder.preferEmbeddedResources( + this.doesPreferEmbeddedResources()); + clonedRequestBuilder.continuation = this.continuation; + // Maintenance notice: when extending the list of configuration parameters, + // also extend initFromTraversalState + return clonedRequestBuilder; +}; + +/** + * Disables content negotiation and forces the use of a given media type. + * The media type has to be registered at Traverson's media type registry + * before via traverson.registerMediaType (except for media type + * application/json, which is traverson.mediaTypes.JSON). + */ +Builder.prototype.setMediaType = function(mediaType) { + this.mediaType = mediaType || mediaTypes.CONTENT_NEGOTIATION; + this.adapter = this._createAdapter(mediaType); + this.contentNegotiation = + (mediaType === mediaTypes.CONTENT_NEGOTIATION); + return this; +}; + +/** + * Shortcut for + * setMediaType(traverson.mediaTypes.JSON); + */ +Builder.prototype.json = function() { + this.setMediaType(mediaTypes.JSON); + return this; +}; + +/** + * Shortcut for + * setMediaType(traverson.mediaTypes.JSON_HAL); + */ +Builder.prototype.jsonHal = function() { + this.setMediaType(mediaTypes.JSON_HAL); + return this; +}; + +/** + * Enables content negotiation (content negotiation is enabled by default, this + * method can be used to enable it after a call to setMediaType disabled it). + */ +Builder.prototype.useContentNegotiation = function() { + this.setMediaType(mediaTypes.CONTENT_NEGOTIATION); + this.contentNegotiation = true; + return this; +}; + +/** + * Set the root URL of the API, that is, where the link traversal begins. + */ +Builder.prototype.from = function(url) { + this.startUrl = url; + return this; +}; + +/** + * Adds link relations to the list of link relations to follow. The initial list + * of link relations is the empty list. Each link relation in this list + * corresponds to one step in the traversal. + */ +Builder.prototype.follow = function() { + var newLinks = Array.prototype.slice.apply( + arguments.length === 1 && util.isArray(arguments[0]) ? + arguments[0] : arguments + ); + + for (var i = 0; i < newLinks.length; i++) { + if (typeof newLinks[i] === 'string') { + newLinks[i] = { + type: 'link-rel', + value: newLinks[i], + }; + } + } + this.links = this.links.concat(newLinks); + return this; +}; + +/** + * Adds a special step to the list of link relations that will follow the + * location header, that is, instead of reading the next URL from a link in the + * document body, it uses the location header and follows the URL from this + * header. + */ +Builder.prototype.followLocationHeader = function() { + this.links.push({ + type: 'header', + value: 'location', + }); + return this; +}; + +/** + * Alias for follow. + */ +Builder.prototype.walk = Builder.prototype.follow; + +/** + * Provide template parameters for URI template substitution. + */ +Builder.prototype.withTemplateParameters = function(parameters) { + this.templateParameters = parameters; + return this; +}; + +/** + * Provide options for HTTP requests (additional HTTP headers, for example). + * This function resets any request options, that had been set previously, that + * is, multiple calls to withRequestOptions are not cumulative. Use + * addRequestOptions to add request options in a cumulative way. + * + * Options can either be passed as an object or an array. If an object is + * passed, the options will be used for each HTTP request. If an array is + * passed, each element should be an options object and the first array element + * will be used for the first request, the second element for the second request + * and so on. null elements are allowed. + */ +Builder.prototype.withRequestOptions = function(options) { + this.requestOptions = options; + return this; +}; + +/** + * Adds options for HTTP requests (additional HTTP headers, for example) on top + * of existing options, if any. To reset all request options and set new ones + * without keeping the old ones, you can use withRequestOptions. + * + * Options can either be passed as an object or an array. If an object is + * passed, the options will be used for each HTTP request. If an array is + * passed, each element should be an options object and the first array element + * will be used for the first request, the second element for the second request + * and so on. null elements are allowed. + * + * When called after a call to withRequestOptions or when combining multiple + * addRequestOptions calls, some with objects and some with arrays, a multitude + * of interesting situations can occur: + * + * 1) The existing request options are an object and the new options passed into + * this method are also an object. Outcome: Both objects are merged and all + * options are applied to all requests. + * + * 2) The existing options are an array and the new options passed into this + * method are also an array. Outcome: Each array element is merged individually. + * The combined options from the n-th array element in the existing options + * array and the n-th array element in the given array are applied to the n-th + * request. + * + * 3) The existing options are an object and the new options passed into this + * method are an array. Outcome: A new options array will be created. For each + * element, a clone of the existing options object will be merged with an + * element from the given options array. + * + * Note that if the given array has less elements than the number of steps in + * the link traversal (usually the number of steps is derived from the number + * of link relations given to the follow method), only the first n http + * requests will use options at all, where n is the number of elements in the + * given array. HTTP request n + 1 and all following HTTP requests will use an + * empty options object. This is due to the fact, that at the time of creating + * the new options array, we can not know with certainty how many steps the + * link traversal will have. + * + * 4) The existing options are an array and the new options passed into this + * method are an object. Outcome: A clone of the given options object will be + * merged into into each array element of the existing options. + */ +Builder.prototype.addRequestOptions = function(options) { + + // case 2: both the present options and the new options are arrays. + // => merge each array element individually + if (util.isArray(this.requestOptions) && util.isArray(options)) { + mergeArrayElements(this.requestOptions, options); + + // case 3: there is an options object the new options are an array. + // => create a new array, each element is a merge of the existing base object + // and the array element from the new options array. + } else if (typeof this.requestOptions === 'object' && + util.isArray(options)) { + this.requestOptions = + mergeBaseObjectWithArrayElements(this.requestOptions, options); + + // case 4: there is an options array and the new options are an object. + // => merge the new object into each array element. + } else if (util.isArray(this.requestOptions) && + typeof options === 'object') { + mergeOptionObjectIntoEachArrayElement(this.requestOptions, options); + + // case 1: both are objects + // => merge both objects + } else { + mergeRecursive(this.requestOptions, options); + } + return this; +}; + +function mergeArrayElements(existingOptions, newOptions) { + for (var i = 0; + i < Math.max(existingOptions.length, newOptions.length); + i++) { + existingOptions[i] = + mergeRecursive(existingOptions[i], newOptions[i]); + } +} + +function mergeBaseObjectWithArrayElements(existingOptions, newOptions) { + var newOptArray = []; + for (var i = 0; + i < newOptions.length; + i++) { + newOptArray[i] = + mergeRecursive(newOptions[i], existingOptions); + } + return newOptArray; +} + +function mergeOptionObjectIntoEachArrayElement(existingOptions, newOptions) { + for (var i = 0; + i < existingOptions.length; + i++) { + mergeRecursive(existingOptions[i], newOptions); + } +} + +/** + * Injects a custom request library. When using this method, you should not + * call withRequestOptions or addRequestOptions but instead pre-configure the + * injected request library instance before passing it to withRequestLibrary. + */ +Builder.prototype.withRequestLibrary = function(request) { + this.requestModuleInstance = request; + return this; +}; + +/** + * Injects a custom JSON parser. + */ +Builder.prototype.parseResponseBodiesWith = function(parser) { + this.jsonParser = parser; + return this; +}; + +/** + * With this option enabled, the body of the response at the end of the + * traversal will be converted into a JavaScript object (for example by passing + * it into JSON.parse) and passing the resulting object into the callback. + * The default is false, which means the full response is handed to the + * callback. + * + * When response body conversion is enabled, you will not get the full + * response, so you won't have access to the HTTP status code or headers. + * Instead only the converted object will be passed into the callback. + * + * Note that the body of any intermediary responses during the traversal is + * always converted by Traverson (to find the next link). + * + * If the method is called without arguments (or the first argument is undefined + * or null), response body conversion is switched on, otherwise the argument is + * interpreted as a boolean flag. If it is a truthy value, response body + * conversion is switched to on, if it is a falsy value (but not null or + * undefined), response body conversion is switched off. + */ +Builder.prototype.convertResponseToObject = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.convertResponseToObjectFlag = !!flag; + return this; +}; + +/** + * Switches URL resolution to relative (default is absolute) or back to + * absolute. + * + * If the method is called without arguments (or the first argument is undefined + * or null), URL resolution is switched to relative, otherwise the argument is + * interpreted as a boolean flag. If it is a truthy value, URL resolution is + * switched to relative, if it is a falsy value, URL resolution is switched to + * absolute. + */ +Builder.prototype.resolveRelative = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.resolveRelativeFlag = !!flag; + return this; +}; + +/** + * Makes Traverson prefer embedded resources over traversing a link or vice + * versa. This only applies to media types which support embedded resources + * (like HAL). It has no effect when using a media type that does not support + * embedded resources. + * + * It also only takes effect when a resource contains both a link _and_ an + * embedded resource with the name that is to be followed at this step in the + * link traversal process. + * + * If the method is called without arguments (or the first argument is undefined + * or null), embedded resources will be preferred over fetching linked resources + * with an additional HTTP request. Otherwise the argument is interpreted as a + * boolean flag. If it is a truthy value, embedded resources will be preferred, + * if it is a falsy value, traversing the link relation will be preferred. + */ +Builder.prototype.preferEmbeddedResources = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.preferEmbedded = !!flag; + return this; +}; + +/** + * Returns the current media type. If no media type is enforced but content type + * detection is used, the string `content-negotiation` is returned. + */ +Builder.prototype.getMediaType = function() { + return this.mediaType; +}; + +/** + * Returns the URL set by the from(url) method, that is, the root URL of the + * API. + */ +Builder.prototype.getFrom = function() { + return this.startUrl; +}; + +/** + * Returns the template parameters set by the withTemplateParameters. + */ +Builder.prototype.getTemplateParameters = function() { + return this.templateParameters; +}; + +/** + * Returns the request options set by the withRequestOptions or + * addRequestOptions. + */ +Builder.prototype.getRequestOptions = function() { + return this.requestOptions; +}; + +/** + * Returns the custom request library instance set by withRequestLibrary or the + * standard request library instance, if a custom one has not been set. + */ +Builder.prototype.getRequestLibrary = function() { + return this.requestModuleInstance; +}; + +/** + * Returns the custom JSON parser function set by parseResponseBodiesWith or the + * standard parser function, if a custom one has not been set. + */ +Builder.prototype.getJsonParser = function() { + return this.jsonParser; +}; + +/** + * Returns true if the body of the last response will be converted to a + * JavaScript object before passing the result back to the callback. + */ +Builder.prototype.convertsResponseToObject = function() { + return this.convertResponseToObjectFlag; +}; + +/** + * Returns the flag controlling if URLs are resolved relative or absolute. + * A return value of true means that URLs are resolved relative, false means + * absolute. + */ +Builder.prototype.doesResolveRelative = function() { + return this.resolveRelativeFlag; +}; + +/** + * Returns the flag controlling if embedded resources are preferred over links. + * A return value of true means that embedded resources are preferred, false + * means that following links is preferred. + */ +Builder.prototype.doesPreferEmbeddedResources = function() { + return this.preferEmbedded; +}; + +/** + * Returns true if content negotiation is enabled and false if a particular + * media type is forced. + */ +Builder.prototype.doesContentNegotiation = function() { + return this.contentNegotiation; +}; + +/** + * Starts the link traversal process and passes the last HTTP response to the + * callback. + */ +Builder.prototype.get = function get(callback) { + log.debug('initiating traversal (get)'); + var t = createInitialTraversalState(this); + return actions.get(t, wrapForContinue(this, t, callback, 'get')); +}; + +/** + * Special variant of get() that does not yield the full http response to the + * callback but instead the already parsed JSON as an object. + * + * This is a shortcut for builder.convertResponseToObject().get(callback). + */ +Builder.prototype.getResource = function getResource(callback) { + log.debug('initiating traversal (getResource)'); + this.convertResponseToObjectFlag = true; + var t = createInitialTraversalState(this); + return actions.get(t, wrapForContinue(this, t, callback, + 'getResource')); +}; + +/** + * Special variant of get() that does not execute the last request but instead + * yields the last URL to the callback. + */ +Builder.prototype.getUrl = function getUrl(callback) { + log.debug('initiating traversal (getUrl)'); + var t = createInitialTraversalState(this); + return actions.getUrl(t, wrapForContinue(this, t, callback, 'getUrl')); +}; + +/** + * Alias for getUrl. + */ +Builder.prototype.getUri = Builder.prototype.getUrl; + + +/** + * Starts the link traversal process and sends an HTTP POST request with the + * given body to the last URL. Passes the HTTP response of the POST request to + * the callback. + */ +Builder.prototype.post = function post(body, callback) { + log.debug('initiating traversal (post)'); + var t = createInitialTraversalState(this, body); + return actions.post(t, wrapForContinue(this, t, callback, 'post')); +}; + +/** + * Starts the link traversal process and sends an HTTP PUT request with the + * given body to the last URL. Passes the HTTP response of the PUT request to + * the callback. + */ +Builder.prototype.put = function put(body, callback) { + log.debug('initiating traversal (put)'); + var t = createInitialTraversalState(this, body); + return actions.put(t, wrapForContinue(this, t, callback, 'put')); +}; + +/** + * Starts the link traversal process and sends an HTTP PATCH request with the + * given body to the last URL. Passes the HTTP response of the PATCH request to + * the callback. + */ +Builder.prototype.patch = function patch(body, callback) { + log.debug('initiating traversal (patch)'); + var t = createInitialTraversalState(this, body); + return actions.patch(t, wrapForContinue(this, t, callback, 'patch')); +}; + +/** + * Starts the link traversal process and sends an HTTP DELETE request to the + * last URL. Passes the HTTP response of the DELETE request to the callback. + */ +Builder.prototype.delete = function del(callback) { + log.debug('initiating traversal (delete)'); + var t = createInitialTraversalState(this); + return actions.delete(t, wrapForContinue(this, t, callback, 'delete')); +}; + +/** + * Alias for delete. + */ +Builder.prototype.del = Builder.prototype.delete; + +function createInitialTraversalState(self, body) { + + var traversalState = { + aborted: false, + adapter: self.adapter || null, + body: body || null, + callbackHasBeenCalledAfterAbort: false, + contentNegotiation: self.doesContentNegotiation(), + continuation: null, + convertResponseToObject: self.convertsResponseToObject(), + links: self.links, + jsonParser: self.getJsonParser(), + requestModuleInstance: self.getRequestLibrary(), + requestOptions: self.getRequestOptions(), + resolveRelative: self.doesResolveRelative(), + preferEmbedded: self.doesPreferEmbeddedResources(), + startUrl: self.startUrl, + step : { + url: self.startUrl, + index: 0, + }, + templateParameters: self.getTemplateParameters(), + }; + traversalState.abortTraversal = abortTraversal.bind(traversalState); + + if (self.continuation) { + traversalState.continuation = self.continuation; + traversalState.step = self.continuation.step; + self.continuation = null; + } + + return traversalState; +} + +function wrapForContinue(self, t, callback, firstTraversalAction) { + return function(err, result) { + if (err) { return callback(err); } + return callback(null, result, { + continue: function() { + if (!t) { + throw createError('No traversal state to continue from.', + errors.InvalidStateError); + } + + log.debug('> continuing finished traversal process'); + self.continuation = { + step: t.step, + action: firstTraversalAction, + }; + self.continuation.step.index = 0; + initFromTraversalState(self, t); + return self; + }, + }); + }; +} + +/* + * Copy configuration from traversal state to builder instance to + * prepare for next traversal process. + */ +function initFromTraversalState(self, t) { + self.aborted = false; + self.adapter = t.adapter; + self.body = t.body; + self.callbackHasBeenCalledAfterAbort = false; + self.contentNegotiation = t.contentNegotiation; + self.convertResponseToObjectFlag = t.convertResponseToObject; + self.links = []; + self.jsonParser = t.jsonParser; + self.requestModuleInstance = t.requestModuleInstance, + self.requestOptions = t.requestOptions, + self.resolveRelativeFlag = t.resolveRelative; + self.preferEmbedded = t.preferEmbedded; + self.startUrl = t.startUrl; + self.templateParameters = t.templateParameters; +} + +function cloneArrayOrObject(thing) { + if (util.isArray(thing)) { + return shallowCloneArray(thing); + } else if (typeof thing === 'object') { + return deepCloneObject(thing); + } else { + return thing; + } +} + +function deepCloneObject(object) { + return mergeRecursive(null, object); +} + +function shallowCloneArray(array) { + if (!array) { + return array; + } + return array.slice(0); +} + +module.exports = Builder; + +},{"./abort_traversal":16,"./actions":17,"./errors":19,"./media_type_registry":23,"./media_types":24,"./merge_recursive":25,"minilog":11,"request":13,"util":12}],19:[function(require,module,exports){ +'use strict'; + +module.exports = { + errors: { + HTTPError: 'HTTPError', + InvalidArgumentError: 'InvalidArgumentError', + InvalidStateError: 'InvalidStateError', + JSONError: 'JSONError', + JSONPathError: 'JSONPathError', + LinkError: 'LinkError', + TraversalAbortedError: 'TraversalAbortedError', + UnsupportedMediaType: 'UnsupportedMediaTypeError', + }, + + createError: function(message, name, data) { + var error = new Error(message); + error.name = name; + if (data) { + error.data = data; + } + return error; + }, + +}; + +},{}],20:[function(require,module,exports){ +(function (process){ +'use strict'; +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('./abort_traversal') + , detectContentType = require('./transforms/detect_content_type') + , errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError + , getOptionsForStep = require('./transforms/get_options_for_step'); + +/** + * Executes a HTTP GET request during the link traversal process. + */ +// This method is currently used for all intermediate GET requests during the +// link traversal process. Coincidentally, it is also used for the final request +// in a link traversal should this happen to be a GET request. Otherwise (POST/ +// PUT/PATCH/DELETE), Traverson uses exectueHttpRequest. +exports.fetchResource = function fetchResource(t, callback) { + log.debug('fetching resource for next step'); + if (t.step.url) { + log.debug('fetching resource from ', t.step.url); + return executeHttpGet(t, callback); + } else if (t.step.doc) { + // The step already has an attached result document, so all is fine and we + // can call the callback immediately + log.debug('resource for next step has already been fetched, using ' + + 'embedded'); + return process.nextTick(function() { + callback(null, t); + }); + } else { + return process.nextTick(function() { + var error = createError('Can not process step.', + errors.InvalidStateError); + error.step = t.step; + callback(error, t); + }); + } +}; + +function executeHttpGet(t, callback) { + var options = getOptionsForStep(t); + log.debug('HTTP GET request to ', t.step.url); + log.debug('options ', options); + t.currentRequest = + t.requestModuleInstance.get(t.step.url, options, + function(err, response, body) { + log.debug('HTTP GET request to ' + t.step.url + ' returned'); + t.currentRequest = null; + + // workaround for cases where response body is empty but body comes in as + // the third argument + if (body && !response.body) { + response.body = body; + } + t.step.response = response; + + if (err) { + return callback(err, t); + } + log.debug('request to ' + t.step.url + ' finished without error (' + + response.statusCode + ')'); + + if (!detectContentType(t, callback)) return; + + return callback(null, t); + }); + abortTraversal.registerAbortListener(t, callback); +} + +/** + * Executes an arbitrary HTTP request. + */ +// This method is currently used for POST/PUT/PATCH/DELETE at the end of a link +// traversal process. If the link traversal process requires a GET as the last +// request, Traverson uses exectueHttpGet. +exports.executeHttpRequest = function(t, request, method, callback) { + var requestOptions = getOptionsForStep(t); + if (t.body) { + requestOptions.body = requestOptions.jsonReplacer ? + t.body : JSON.stringify(t.body); + } + + log.debug('HTTP ' + method.name + ' request to ', t.step.url); + log.debug('options ', requestOptions); + t.currentRequest = + method.call(request, t.step.url, requestOptions, + function(err, response, body) { + log.debug('HTTP ' + method.name + ' request to ' + t.step.url + + ' returned'); + t.currentRequest = null; + + // workaround for cases where response body is empty but body comes in as + // the third argument + if (body && !response.body) { + response.body = body; + } + t.step.response = response; + + if (err) { + return callback(err); + } + + return callback(null, response); + }); + abortTraversal.registerAbortListener(t, callback); +}; + +}).call(this,require('_process')) +},{"./abort_traversal":16,"./errors":19,"./transforms/detect_content_type":32,"./transforms/get_options_for_step":40,"_process":5,"minilog":11}],21:[function(require,module,exports){ +'use strict'; + +module.exports = function isContinuation(t) { + return t.continuation && t.step && t.step.response; +}; + +},{}],22:[function(require,module,exports){ +'use strict'; + +var jsonpath = require('jsonpath-plus') + , minilog = require('minilog') + , _s = require('underscore.string'); + +var errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +function JsonAdapter(log) { + this.log = log; +} + +JsonAdapter.prototype.findNextStep = function(t, link) { + validateLinkObject(link); + var doc = t.lastStep.doc; + this.log.debug('resolving link', link); + switch (link.type) { + case 'link-rel': + return this._handleLinkRel(doc, link); + case 'header': + return this._handleHeader(t.lastStep.response, link); + default: + throw createError('Link objects with type ' + link.type + ' are not ' + + 'supported by this adapter.', errors.InvalidArgumentError, link); + } +}; + +JsonAdapter.prototype._handleLinkRel = function(doc, link) { + var linkRel = link.value; + this.log.debug('looking for link-rel in doc', linkRel, doc); + var url; + if (this._testJSONPath(linkRel)) { + return { url: this._resolveJSONPath(doc, linkRel) }; + } else if (doc[linkRel]) { + return { url : doc[linkRel] }; + } else { + throw createError('Could not find property ' + linkRel + + ' in document.', errors.LinkError, doc); + } +}; + +function validateLinkObject(link) { + if (typeof link === 'undefined' || link === null) { + throw createError('Link object is null or undefined.', + errors.InvalidArgumentError); + } + if (typeof link !== 'object') { + throw createError('Links must be objects, not ' + typeof link + + '.', errors.InvalidArgumentError, link); + } + if (!link.type) { + throw createError('Link objects has no type attribute.', + errors.InvalidArgumentError, link); + } +} + +JsonAdapter.prototype._testJSONPath = function(link) { + return _s.startsWith(link, '$.') || _s.startsWith(link, '$['); +}; + +JsonAdapter.prototype._resolveJSONPath = function(doc, link) { + var matches = jsonpath({ + json: doc, + path: link, + }); + if (matches.length === 1) { + var url = matches[0]; + if (!url) { + throw createError('JSONPath expression ' + link + + ' was resolved but the result was null, undefined or an empty' + + ' string in document:\n' + JSON.stringify(doc), + errors.JSONPathError, doc); + } + if (typeof url !== 'string') { + throw createError('JSONPath expression ' + link + + ' was resolved but the result is not a property of type string. ' + + 'Instead it has type "' + (typeof url) + + '" in document:\n' + JSON.stringify(doc), errors.JSONPathError, + doc); + } + return url; + } else if (matches.length > 1) { + // ambigious match + throw createError('JSONPath expression ' + link + + ' returned more than one match in document:\n' + + JSON.stringify(doc), errors.JSONPathError, doc); + } else { + // no match at all + throw createError('JSONPath expression ' + link + + ' returned no match in document:\n' + JSON.stringify(doc), + errors.JSONPathError, doc); + } +}; + +JsonAdapter.prototype._handleHeader = function(httpResponse, link) { + switch (link.value) { + case 'location': + var locationHeader = httpResponse.headers.location; + if (!locationHeader) { + throw createError('Following the location header but there was no ' + + 'location header in the last response.', errors.LinkError, + httpResponse.headers); + } + return { url : locationHeader }; + default: + throw createError('Link objects with type header and value ' + + link.value + ' are not supported by this adapter.', + errors.InvalidArgumentError, link); + } +}; + +module.exports = JsonAdapter; + +},{"./errors":19,"jsonpath-plus":4,"minilog":11,"underscore.string":14}],23:[function(require,module,exports){ +'use strict'; + +var mediaTypes = require('./media_types'); + +var registry = {}; + +exports.register = function register(contentType, constructor) { + registry[contentType] = constructor; +}; + +exports.get = function get(contentType) { + return registry[contentType]; +}; + +exports.register(mediaTypes.CONTENT_NEGOTIATION, + require('./negotiation_adapter')); +exports.register(mediaTypes.JSON, require('./json_adapter')); + +},{"./json_adapter":22,"./media_types":24,"./negotiation_adapter":26}],24:[function(require,module,exports){ +'use strict'; + +module.exports = { + CONTENT_NEGOTIATION: 'content-negotiation', + JSON: 'application/json', + JSON_HAL: 'application/hal+json', +}; + +},{}],25:[function(require,module,exports){ +'use strict'; + +// TODO Maybe replace with https://github.com/Raynos/xtend +// check browser build size, though. +function mergeRecursive(obj1, obj2) { + if (!obj1 && obj2) { + obj1 = {}; + } + for (var key in obj2) { + if (!obj2.hasOwnProperty(key)) { + continue; + } + merge(obj1, obj2, key); + } + return obj1; +} + +function merge(obj1, obj2, key) { + if (typeof obj2[key] === 'object') { + // if it is an object (that is, a non-leave in the tree), + // and it is not present in obj1 + if (!obj1[key] || typeof obj1[key] !== 'object') { + // ... we create an empty object in obj1 + obj1[key] = {}; + } + // and we recurse deeper into the structure + mergeRecursive(obj1[key], obj2[key]); + } else { + // if it is primitive (string, number, boolean) or a function, we overwrite/ + // add it to obj1 + obj1[key] = obj2[key]; + } +} + +module.exports = mergeRecursive; + +},{}],26:[function(require,module,exports){ +'use strict'; + +var errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +function NegotiationAdapter(log) {} + +NegotiationAdapter.prototype.findNextStep = function(doc, link) { + throw createError('Content negotiation did not happen', + errors.InvalidStateError); +}; + +module.exports = NegotiationAdapter; + +},{"./errors":19}],27:[function(require,module,exports){ +(function (process){ +/* jshint loopfunc: true */ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +/* + * Applies async and sync transforms, one after another. + */ +function applyTransforms(transforms, t, callback) { + log.debug('applying', transforms.length, 'transforms'); + for (var i = 0; i < transforms.length; i++) { + var transform = transforms[i]; + log.debug('next transform', transform); + if (transform.isAsync) { + // asynchronous case + return transform(t, function(t) { + // this is only called when the async transform was successful, + // otherwise t.callback has already been called with an error. + applyTransforms(transforms.slice(i + 1), t, callback); + }); + } else { + // synchronous case + var result = transform(t); + if (!result) { + log.debug('transform has failed or was a final transform'); + // stop processing t.callback has already been called + return; + } + } + } + log.debug('all transformations done, starting next step'); + return process.nextTick(function() { + callback(t); + }); +} + +module.exports = applyTransforms; + +}).call(this,require('_process')) +},{"_process":5,"minilog":11}],28:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError + , isContinuation = require('../is_continuation'); + +module.exports = function checkHttpStatus(t) { + // this step is ommitted for continuations + if (isContinuation(t)) { + return true; + } + + log.debug('checking http status'); + if (!t.step.response && t.step.doc) { + // Last step probably did not execute a HTTP request but used an embedded + // document. + log.debug('found embedded document, assuming no HTTP request has been ' + + 'made'); + return true; + } + + // Only process response if http status was in 200 - 299 range. + // The request module follows redirects for GET requests all by itself, so + // we should not have to handle them here. If a 3xx http status get's here + // something went wrong. 4xx and 5xx of course also indicate an error + // condition. 1xx should not occur. + var httpStatus = t.step.response.statusCode; + if (httpStatus && (httpStatus < 200 || httpStatus >= 300)) { + var error = httpError(t.step.url, httpStatus, t.step.response.body); + log.error('unexpected http status code'); + log.error(error); + t.callback(error); + return false; + } + log.debug('http status code ok (' + httpStatus + ')'); + return true; +}; + +function httpError(url, httpStatus, body) { + var error = createError('HTTP GET for ' + url + + ' resulted in HTTP status code ' + httpStatus + '.', errors.HTTPError); + error.url = url; + error.httpStatus = httpStatus; + error.body = body; + try { + error.doc = JSON.parse(body); + } catch (e) { + // ignore + } + return error; +} + +},{"../errors":19,"../is_continuation":21,"minilog":11}],29:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , isContinuation = require('../is_continuation'); + +/* + * This transform covers the case of a follow() call *without any links* after + * a continue(). Actually, there is nothing to do here since we should have + * fetched everything last time. + */ +module.exports = function continuationToDoc(t) { + if (isContinuation(t)) { + log.debug('continuing from last traversal process (actions)'); + t.continuation = null; + t.callback(null, t.step.doc); + return false; + } + return true; +}; + +},{"../is_continuation":21,"minilog":11}],30:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , convertEmbeddedDocToResponse = + require('./convert_embedded_doc_to_response') + , isContinuation = require('../is_continuation'); + +/* + * follow() call without links after continue(). Actually, there is nothing + * to do here since we should have fetched everything last time. + */ +module.exports = function continuationToResponse(t) { + if (isContinuation(t)) { + log.debug('continuing from last traversal process (actions)'); + t.continuation = null; + // Hm, a transform using another transform. This feels a bit fishy. + convertEmbeddedDocToResponse(t); + t.callback(null, t.step.response); + return false; + } + return true; +}; + +},{"../is_continuation":21,"./convert_embedded_doc_to_response":31,"minilog":11}],31:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +module.exports = function convertEmbeddedDocToResponse(t) { + if (!t.step.response && t.step.doc) { + log.debug('faking HTTP response for embedded resource'); + t.step.response = { + statusCode: 200, + body: JSON.stringify(t.step.doc), + remark: 'This is not an actual HTTP response. The resource you ' + + 'requested was an embedded resource, so no HTTP request was ' + + 'made to acquire it.' + }; + } + return true; +}; + +},{"minilog":11}],32:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +var mediaTypeRegistry = require('../media_type_registry') + , errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +module.exports = function detectContentType(t, callback) { + if (t.contentNegotiation && + t.step.response && + t.step.response.headers && + t.step.response.headers['content-type']) { + var contentType = t.step.response.headers['content-type'].split(/[; ]/)[0]; + var AdapterType = mediaTypeRegistry.get(contentType); + if (!AdapterType) { + callback(createError('Unknown content type for content ' + + 'type detection: ' + contentType, + errors.UnsupportedMediaType)); + return false; + } + // switch to new Adapter depending on Content-Type header of server + t.adapter = new AdapterType(log); + } + return true; +}; + +},{"../errors":19,"../media_type_registry":23,"minilog":11}],33:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , httpRequests = require('../http_requests'); + +/* + * Execute the last HTTP request in a traversal that ends in + * post/put/patch/delete, but do not call t.callback immediately + * (because we still need to do response body to object conversion + * afterwards, for example) + */ +// TODO Why is this different from when do a GET? +// Probably only because the HTTP method is configurable here (with +// t.lastMethod), we might be able to unify this with the +// fetch_resource/fetch_last_resource transform. +function executeLastHttpRequest(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + // only diff to execute_last_http_request: pass a new callback function + // instead of t.callback. + httpRequests.executeHttpRequest( + t, t.requestModuleInstance, t.lastMethod, function(err, response) { + if (err) { + if (!err.aborted) { + log.debug('error while processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +executeLastHttpRequest.isAsync = true; + +module.exports = executeLastHttpRequest; + +},{"../abort_traversal":16,"../http_requests":20,"minilog":11}],34:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , httpRequests = require('../http_requests'); + +/* + * Execute the last http request in a traversal that ends in + * post/put/patch/delete. + */ +// TODO Why is this different from when do a GET at the end of the traversal? +// Probably only because the HTTP method is configurable here (with +// t.lastMethod), we might be able to unify this with the +// fetch_resource/fetch_last_resource transform. +function executeLastHttpRequest(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + httpRequests.executeHttpRequest( + t, t.requestModuleInstance, t.lastMethod, t.callback); +} + +executeLastHttpRequest.isAsync = true; + +module.exports = executeLastHttpRequest; + +},{"../abort_traversal":16,"../http_requests":20,"minilog":11}],35:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +/* + * This transform is meant to be run at the very end of a getResource call. It + * just extracts the last doc from the step and calls t.callback with it. + */ +module.exports = function extractDoc(t) { + log.debug('walker.walk has finished'); + /* + TODO Breaks a lot of tests although it seems to make perfect sense?!? + if (!t.doc) { + t.callback(createError('No document available', errors.InvalidStateError)); + return false; + } + */ + + t.callback(null, t.step.doc); + + // This is a so called final transform that is only applied at the very end + // and it always calls t.callback - in contrast to other transforms it does + // not call t.callback in the error case, but as a success. + // We return false to make sure processing ends here. + return false; +}; + +},{"minilog":11}],36:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +/* + * This transform is meant to be run at the very end of a get/post/put/patch/ + * delete call. It just extracts the last response from the step and calls + * t.callback with it. + */ +module.exports = function extractDoc(t) { + log.debug('walker.walk has finished'); + /* + TODO Breaks a lot of tests although it seems to make perfect sense?!? + if (!t.response) { + t.callback(createError('No response available', errors.InvalidStateError)); + return false; + } + */ + + t.callback(null, t.step.response); + + // This is a so called final transform that is only applied at the very end + // and it always calls t.callback - in contrast to other transforms it does + // not call t.callback in the error case, but as a success. + // We return false to make sure processing ends here. + return false; +}; + +},{"minilog":11}],37:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , url = require('url'); + +var errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +/* + * This transform is meant to be run at the very end of a get/post/put/patch/ + * delete call. It just extracts the last accessed url from the step and calls + * t.callback with it. + */ +module.exports = function extractDoc(t) { + log.debug('walker.walk has finished'); + if (t.step.url) { + return t.callback(null, t.step.url); + } else if (t.step.doc && + // TODO actually this is very HAL specific :-/ + t.step.doc._links && + t.step.doc._links.self && + t.step.doc._links.self.href) { + return t.callback( + null, url.resolve(t.startUrl, t.step.doc._links.self.href)); + } else { + return t.callback(createError('You requested an URL but the last ' + + 'resource is an embedded resource and has no URL of its own ' + + '(that is, it has no link with rel=\"self\"', errors.LinkError)); + } +}; + +},{"../errors":19,"minilog":11,"url":15}],38:[function(require,module,exports){ +'use strict'; + +// TODO Only difference to lib/transform/fetch_resource is the continuation +// checking, which is missing here. Maybe we can delete this transform and use +// fetch_resource in its place everywhere? + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , httpRequests = require('../http_requests'); + +/* + * Execute the last step in a traversal that ends with an HTTP GET. + */ +// This is similar to lib/transforms/fetch_resource.js - refactoring potential? +function fetchLastResource(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + httpRequests.fetchResource(t, function(err, t) { + log.debug('fetchResource returned (fetchLastResource).'); + if (err) { + if (!err.aborted) { + log.debug('error while processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +fetchLastResource.isAsync = true; + +module.exports = fetchLastResource; + +},{"../abort_traversal":16,"../http_requests":20,"minilog":11}],39:[function(require,module,exports){ +(function (process){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , isContinuation = require('../is_continuation') + , httpRequests = require('../http_requests'); + +/* + * Execute the next step in the traversal. In most cases that is an HTTP get to + *the next URL. + */ + +function fetchResource(t, callback) { + if (isContinuation(t)) { + convertContinuation(t, callback); + } else { + fetchViaHttp(t, callback); + } +} + +fetchResource.isAsync = true; + +/* + * This is a continuation of an earlier traversal process. + * We need to shortcut to the next step (without executing the final HTTP + * request of the last traversal again. + */ +function convertContinuation(t, callback) { + log.debug('continuing from last traversal process (walker)'); + process.nextTick(function() { // de-zalgo continuations + callback(t); + }); +} + +function fetchViaHttp(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + httpRequests.fetchResource(t, function(err, t) { + log.debug('fetchResource returned'); + if (err) { + if (!err.aborted) { + log.debug('error while processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +module.exports = fetchResource; + +}).call(this,require('_process')) +},{"../abort_traversal":16,"../http_requests":20,"../is_continuation":21,"_process":5,"minilog":11}],40:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , util = require('util'); + +module.exports = function getOptionsForStep(t) { + var options = t.requestOptions; + if (util.isArray(t.requestOptions)) { + options = t.requestOptions[t.step.index] || {}; + } + log.debug('options: ', options); + return options; +}; + +},{"minilog":11,"util":12}],41:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +var errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError + , isContinuation = require('../is_continuation'); + + +module.exports = function parse(t) { + // TODO Duplicated in actions#afterGetResource etc. + // this step is ommitted for continuations that parse at the end + if (isContinuation(t)) { + log.debug('continuing from last traversal process (transforms/parse)'); + // if last traversal did a parse at the end we do not need to parse again + // (this condition will need to change with + // https://github.com/basti1302/traverson/issues/44) + if (t.continuation.action === 'getResource') { + return true; + } + } + if (t.step.doc) { + // Last step probably did not execute a HTTP request but used an embedded + // document. + log.debug('no parsing necessary, probably an embedded document'); + return true; + } + + try { + parseBody(t); + return true; + } catch (e) { + handleError(t, e); + return false; + } +}; + +function parseBody(t) { + log.debug('parsing response body'); + if (t.requestOptions && t.requestOptions.jsonReviver) { + t.step.doc = t.step.response.body; + } else { + t.step.doc = t.jsonParser(t.step.response.body); + } +} + +function handleError(t, e) { + var error = e; + if (e.name === 'SyntaxError') { + error = jsonError(t.step.url, t.step.response.body); + } + log.error('parsing failed'); + log.error(error); + t.callback(error); +} + +function jsonError(url, body) { + var error = createError('The document at ' + url + + ' could not be parsed as JSON: ' + body, errors.JSONError, body); + error.url = url; + error.body = body; + return error; +} + +},{"../errors":19,"../is_continuation":21,"minilog":11}],42:[function(require,module,exports){ +'use strict'; + +var isContinuation = require('../is_continuation'); + +module.exports = function resetLastStep(t) { + // this step is ommitted for continuations + if (isContinuation(t)) { + return true; + } + + t.continuation = null; + return true; +}; + +},{"../is_continuation":21}],43:[function(require,module,exports){ +'use strict'; + +var isContinuation = require('../is_continuation'); + +module.exports = function resetLastStep(t) { + // this step is ommitted for continuations + if (isContinuation(t)) { + return true; + } + + t.lastStep = null; + return true; +}; + +},{"../is_continuation":21}],44:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , _s = require('underscore.string') + , url = require('url'); + +var protocolRegEx = /https?:\/\//i; + +module.exports = function resolveNextUrl(t) { + if (t.step.url) { + if (t.step.url.search(protocolRegEx) !== 0) { + log.debug('found non full qualified URL'); + if (t.resolveRelative && t.lastStep && t.lastStep.url) { + // edge case: resolve URL relatively (only when requested by client) + log.debug('resolving URL relative'); + if (_s.startsWith(t.step.url, '/') && + _s.endsWith(t.lastStep.url, '/')) { + t.step.url = _s.splice(t.step.url, 0, 1); + } + t.step.url = t.lastStep.url + t.step.url; + } else { + // This is the default case and what happens most likely (not a full + // qualified URL, not resolving relatively) and we simply use Node's url + // module (or the appropriate shim) here. + t.step.url = url.resolve(t.startUrl, t.step.url); + } + } // edge case: full qualified URL -> no URL resolving necessary + } // no t.step.url -> no URL resolving (step might contain an embedded doc) + return true; +}; + +},{"minilog":11,"underscore.string":14,"url":15}],45:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , _s = require('underscore.string') + , uriTemplate = require('url-template') + , util = require('util'); + +module.exports = function resolveUriTemplate(t) { + if (t.step.url) { + // next link found in last response, might be a URI template + var templateParams = t.templateParameters; + if (util.isArray(templateParams)) { + // if template params were given as an array, only use the array element + // for the current index for URI template resolving. + templateParams = templateParams[t.step.index]; + } + templateParams = templateParams || {}; + + if (_s.contains(t.step.url, '{')) { + log.debug('resolving URI template'); + var template = uriTemplate.parse(t.step.url); + var resolved = template.expand(templateParams); + log.debug('resolved to ', resolved); + t.step.url = resolved; + } + } + return true; +}; + + + +},{"minilog":11,"underscore.string":14,"url-template":49,"util":12}],46:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +module.exports = function switchToNextStep(t) { + // extract next link to follow from last response + var link = t.links[t.step.index]; + log.debug('next link: ' + link); + + // save last step before overwriting it with the next step (required for + // relative URL resolution, where we need the last URL) + t.lastStep = t.step; + + t.step = findNextStep(t, link); + if (!t.step) return false; + + log.debug('found next step', t.step); + + // backward compatibility fix for media type plug-ins using step.uri instead + // of step.url (until 1.0.0) + t.step.url = t.step.url || t.step.uri; + + t.step.index = t.lastStep.index + 1; + return true; +}; + +function findNextStep(t, link) { + try { + return t.adapter.findNextStep(t, link); + } catch (e) { + log.error('could not find next step'); + log.error(e); + t.callback(e); + return null; + } +} + +},{"minilog":11}],47:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('./abort_traversal') + , applyTransforms = require('./transforms/apply_transforms') + , isContinuation = require('./is_continuation') + , resolveUriTemplate = require('./transforms/resolve_uri_template'); + +var transforms = [ + require('./transforms/fetch_resource'), + require('./transforms/reset_last_step'), + // check HTTP status code + require('./transforms/check_http_status'), + // parse JSON from last response + require('./transforms/parse'), + // retrieve next link and switch to next step + require('./transforms/switch_to_next_step'), + // URI template has to be resolved before post processing the URL, + // because we do url.resolve with it (in json_hal) and this would URL- + // encode curly braces. + resolveUriTemplate, + require('./transforms/resolve_next_url'), + require('./transforms/reset_continuation'), +]; + +/** + * Walks from resource to resource along the path given by the link relations + * from this.links until it has reached the last URL. On reaching this, it calls + * the given callback with the last resulting step. + */ +exports.walk = function(t, transformsAfterLastStep, callback) { + // even the root URL might be a template, so we apply the resolveUriTemplate + // once before starting the walk. + if (!resolveUriTemplate(t)) return; + + // starts the link rel walking process + log.debug('starting to follow links'); + transformsAfterLastStep = transformsAfterLastStep || []; + t.callback = callback; + processStep(t, transformsAfterLastStep); +}; + +function processStep(t, transformsAfterLastStep) { + log.debug('processing next step'); + if (moreLinksToFollow(t) && !isAborted(t)) { + applyTransforms(transforms, t, function(t) { + log.debug('successfully processed step'); + // call processStep recursively again to follow next link + processStep(t, transformsAfterLastStep); + }); + } else if (isAborted(t)) { + return abortTraversal.callCallbackOnAbort(t); + } else { + // link array is exhausted, we are done and return the last response + // and URL to the callback the client passed into the walk method. + log.debug('link array exhausted'); + + applyTransforms(transformsAfterLastStep, t, function(t) { + // This is pretty ugly. This code implies, that we call t.callback from + // here, but actually we usually call it from lib/transforms/extract_doc + // or lib/transforms/extract_response which then return false to terminate + // the processing. + return t.callback(); + }); + } +} + +function moreLinksToFollow(t) { + return t.step.index < t.links.length; +} + +function isAborted(t) { + return t.aborted; +} + +},{"./abort_traversal":16,"./is_continuation":21,"./transforms/apply_transforms":27,"./transforms/check_http_status":28,"./transforms/fetch_resource":39,"./transforms/parse":41,"./transforms/reset_continuation":42,"./transforms/reset_last_step":43,"./transforms/resolve_next_url":44,"./transforms/resolve_uri_template":45,"./transforms/switch_to_next_step":46,"minilog":11}],48:[function(require,module,exports){ +(function (process){ +'use strict'; + +var minilog = require('minilog') + , errorModule = require('./lib/errors') + , errors = errorModule.errors + , createError = errorModule.createError + , mediaTypes = require('./lib/media_types') + , Builder = require('./lib/builder') + , mediaTypeRegistry = require('./lib/media_type_registry'); + +// activate this line to enable logging +if (process.env.TRAVERSON_LOGGING) { + require('minilog').enable(); +} + +// export builder for traverson-angular +exports._Builder = Builder; + +/** + * Creates a new request builder instance. + */ +exports.newRequest = function newRequest() { + return new Builder(); +}; + +/** + * Creates a new request builder instance with the given root URL. + */ +exports.from = function from(url) { + var builder = new Builder(); + builder.from(url); + return builder; +}; + +// Provided for backward compatibility with pre-1.0.0 versions. +// The preferred way is to use newRequest() or from() to create a request +// builder and either set the media type explicitly by calling json() on the +// request builder instance - or use content negotiation. +exports.json = { + from: function(url) { + var builder = new Builder(); + builder.from(url); + builder.setMediaType(mediaTypes.JSON); + return builder; + } +}, + +// Provided for backward compatibility with pre-1.0.0 versions. +// The preferred way is to use newRequest() or from() to create a request +// builder and then either set the media type explicitly by calling jsonHal() on +// the request builder instance - or use content negotiation. +exports.jsonHal = { + from: function(url) { + if (!mediaTypeRegistry.get(mediaTypes.JSON_HAL)) { + throw createError('JSON HAL adapter is not registered. From version ' + + '1.0.0 on, Traverson has no longer built-in support for ' + + 'application/hal+json. HAL support was moved to a separate, optional ' + + 'plug-in. See https://github.com/basti1302/traverson-hal', + errors.UnsupportedMediaType + ); + } + var builder = new Builder(); + builder.from(url); + builder.setMediaType(mediaTypes.JSON_HAL); + return builder; + } +}; + +// expose media type registry so that media type plug-ins can register +// themselves +exports.registerMediaType = mediaTypeRegistry.register; + +// re-export media type constants +exports.mediaTypes = mediaTypes; + +// re-export error names +exports.errors = errors; + +}).call(this,require('_process')) +},{"./lib/builder":18,"./lib/errors":19,"./lib/media_type_registry":23,"./lib/media_types":24,"_process":5,"minilog":11}],49:[function(require,module,exports){ +(function (root, factory) { + if (typeof exports === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define([], factory); + } else { + root.urltemplate = factory(); + } +}(this, function () { + /** + * @constructor + */ + function UrlTemplate() { + } + + /** + * @private + * @param {string} str + * @return {string} + */ + UrlTemplate.prototype.encodeReserved = function (str) { + return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) { + if (!/%[0-9A-Fa-f]/.test(part)) { + part = encodeURI(part).replace(/%5B/g, '[').replace(/%5D/g, ']'); + } + return part; + }).join(''); + }; + + /** + * @private + * @param {string} str + * @return {string} + */ + UrlTemplate.prototype.encodeUnreserved = function (str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { + return '%' + c.charCodeAt(0).toString(16).toUpperCase(); + }); + } + + /** + * @private + * @param {string} operator + * @param {string} value + * @param {string} key + * @return {string} + */ + UrlTemplate.prototype.encodeValue = function (operator, value, key) { + value = (operator === '+' || operator === '#') ? this.encodeReserved(value) : this.encodeUnreserved(value); + + if (key) { + return this.encodeUnreserved(key) + '=' + value; + } else { + return value; + } + }; + + /** + * @private + * @param {*} value + * @return {boolean} + */ + UrlTemplate.prototype.isDefined = function (value) { + return value !== undefined && value !== null; + }; + + /** + * @private + * @param {string} + * @return {boolean} + */ + UrlTemplate.prototype.isKeyOperator = function (operator) { + return operator === ';' || operator === '&' || operator === '?'; + }; + + /** + * @private + * @param {Object} context + * @param {string} operator + * @param {string} key + * @param {string} modifier + */ + UrlTemplate.prototype.getValues = function (context, operator, key, modifier) { + var value = context[key], + result = []; + + if (this.isDefined(value) && value !== '') { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + value = value.toString(); + + if (modifier && modifier !== '*') { + value = value.substring(0, parseInt(modifier, 10)); + } + + result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); + } else { + if (modifier === '*') { + if (Array.isArray(value)) { + value.filter(this.isDefined).forEach(function (value) { + result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); + }, this); + } else { + Object.keys(value).forEach(function (k) { + if (this.isDefined(value[k])) { + result.push(this.encodeValue(operator, value[k], k)); + } + }, this); + } + } else { + var tmp = []; + + if (Array.isArray(value)) { + value.filter(this.isDefined).forEach(function (value) { + tmp.push(this.encodeValue(operator, value)); + }, this); + } else { + Object.keys(value).forEach(function (k) { + if (this.isDefined(value[k])) { + tmp.push(this.encodeUnreserved(k)); + tmp.push(this.encodeValue(operator, value[k].toString())); + } + }, this); + } + + if (this.isKeyOperator(operator)) { + result.push(this.encodeUnreserved(key) + '=' + tmp.join(',')); + } else if (tmp.length !== 0) { + result.push(tmp.join(',')); + } + } + } + } else { + if (operator === ';') { + if (this.isDefined(value)) { + result.push(this.encodeUnreserved(key)); + } + } else if (value === '' && (operator === '&' || operator === '?')) { + result.push(this.encodeUnreserved(key) + '='); + } else if (value === '') { + result.push(''); + } + } + return result; + }; + + /** + * @param {string} template + * @return {function(Object):string} + */ + UrlTemplate.prototype.parse = function (template) { + var that = this; + var operators = ['+', '#', '.', '/', ';', '?', '&']; + + return { + expand: function (context) { + return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) { + if (expression) { + var operator = null, + values = []; + + if (operators.indexOf(expression.charAt(0)) !== -1) { + operator = expression.charAt(0); + expression = expression.substr(1); + } + + expression.split(/,/g).forEach(function (variable) { + var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable); + values.push.apply(values, that.getValues(context, operator, tmp[1], tmp[2] || tmp[3])); + }); + + if (operator && operator !== '+') { + var separator = ','; + + if (operator === '?') { + separator = '&'; + } else if (operator !== '#') { + separator = operator; + } + return (values.length !== 0 ? operator : '') + values.join(separator); + } else { + return values.join(','); + } + } else { + return that.encodeReserved(literal); + } + }); + } + }; + }; + + return new UrlTemplate(); +})); + +},{}],50:[function(require,module,exports){ +var indexOf = require('indexof'); + +var Object_keys = function (obj) { + if (Object.keys) return Object.keys(obj) + else { + var res = []; + for (var key in obj) res.push(key) + return res; + } +}; + +var forEach = function (xs, fn) { + if (xs.forEach) return xs.forEach(fn) + else for (var i = 0; i < xs.length; i++) { + fn(xs[i], i, xs); + } +}; + +var defineProp = (function() { + try { + Object.defineProperty({}, '_', {}); + return function(obj, name, value) { + Object.defineProperty(obj, name, { + writable: true, + enumerable: false, + configurable: true, + value: value + }) + }; + } catch(e) { + return function(obj, name, value) { + obj[name] = value; + }; + } +}()); + +var globals = ['Array', 'Boolean', 'Date', 'Error', 'EvalError', 'Function', +'Infinity', 'JSON', 'Math', 'NaN', 'Number', 'Object', 'RangeError', +'ReferenceError', 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError', +'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'escape', +'eval', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'undefined', 'unescape']; + +function Context() {} +Context.prototype = {}; + +var Script = exports.Script = function NodeScript (code) { + if (!(this instanceof Script)) return new Script(code); + this.code = code; +}; + +Script.prototype.runInContext = function (context) { + if (!(context instanceof Context)) { + throw new TypeError("needs a 'context' argument."); + } + + var iframe = document.createElement('iframe'); + if (!iframe.style) iframe.style = {}; + iframe.style.display = 'none'; + + document.body.appendChild(iframe); + + var win = iframe.contentWindow; + var wEval = win.eval, wExecScript = win.execScript; + + if (!wEval && wExecScript) { + // win.eval() magically appears when this is called in IE: + wExecScript.call(win, 'null'); + wEval = win.eval; + } + + forEach(Object_keys(context), function (key) { + win[key] = context[key]; + }); + forEach(globals, function (key) { + if (context[key]) { + win[key] = context[key]; + } + }); + + var winKeys = Object_keys(win); + + var res = wEval.call(win, this.code); + + forEach(Object_keys(win), function (key) { + // Avoid copying circular objects like `top` and `window` by only + // updating existing context properties or new properties in the `win` + // that was only introduced after the eval. + if (key in context || indexOf(winKeys, key) === -1) { + context[key] = win[key]; + } + }); + + forEach(globals, function (key) { + if (!(key in context)) { + defineProp(context, key, win[key]); + } + }); + + document.body.removeChild(iframe); + + return res; +}; + +Script.prototype.runInThisContext = function () { + return eval(this.code); // maybe... +}; + +Script.prototype.runInNewContext = function (context) { + var ctx = Script.createContext(context); + var res = this.runInContext(ctx); + + forEach(Object_keys(ctx), function (key) { + context[key] = ctx[key]; + }); + + return res; +}; + +forEach(Object_keys(Script.prototype), function (name) { + exports[name] = Script[name] = function (code) { + var s = Script(code); + return s[name].apply(s, [].slice.call(arguments, 1)); + }; +}); + +exports.createScript = function (code) { + return exports.Script(code); +}; + +exports.createContext = Script.createContext = function (context) { + var copy = new Context(); + if(typeof context === 'object') { + forEach(Object_keys(context), function (key) { + copy[key] = context[key]; + }); + } + return copy; +}; + +},{"indexof":3}],"traverson-angular":[function(require,module,exports){ +/* global angular */ +'use strict'; + +var traverson = require('traverson'); + +var ng; +if (typeof angular !== 'undefined') { + // angular is defined globally, use this + ng = angular; +} else { + // angular is not defined globally, try to require it + ng = require('angular'); + if (typeof ng.module !== 'function') { + throw new Error('angular has either to be provided globally or made ' + + 'available as a shim for browserify. (Also, if the angular module on ' + + 'npm would actually be a proper CommonJS module, this error ' + + 'wouldn\'t be a thing.)'); + } +} + +var traversonAngular = ng.module('traverson', []); +var Builder = traverson._Builder; +var originalMethods = { + get: Builder.prototype.get, + getResource: Builder.prototype.getResource, + getUrl: Builder.prototype.getUrl, + post: Builder.prototype.post, + put: Builder.prototype.put, + patch: Builder.prototype.patch, + delete: Builder.prototype.delete, +}; + +traversonAngular.factory('traverson', + ['$q', '$httpTraversonAdapter', + function traversonFactory($q, $httpTraversonAdapter) { + + function promisify(that, originalMethod, argsArray) { + var deferred = $q.defer(); + + argsArray = argsArray || []; + + var traversal; + var callback = function(err, result, _traversal) { + if (err) { + err.result = result; + deferred.reject(err); + } else { + traversal = _traversal; + deferred.resolve(result); + } + }; + + argsArray.push(callback); + + var traversalHandler = originalMethod.apply(that, argsArray); + + function continueTraversal() { + var deferredContinue = $q.defer(); + deferred.promise.then(function() { + deferredContinue.resolve(traversal.continue()); + }, function() { + var error = new Error('Can\'t continue from a broken traversal.'); + error.name = 'InvalidStateError'; + throw error; + }); + return deferredContinue.promise; + } + + return { + result: deferred.promise, + continue: continueTraversal, + abort: traversalHandler.abort, + then: function() { + throw new Error('As of version 2.0.0, Traverson\'s action methods ' + + 'do no longer return the promise directly. Code like \n' + + 'traverson.from(url).follow(...).getResource().then(...)\n' + + 'needs to be changed to \n' + + 'traverson.from(url).follow(...).getResource().result.then(...)'); + }, + }; + } + + Builder.prototype.get = function() { + return promisify(this, originalMethods.get); + }; + + Builder.prototype.getResource = function() { + return promisify(this, originalMethods.getResource); + }; + + Builder.prototype.getUrl = Builder.prototype.getUri = function() { + return promisify(this, originalMethods.getUrl); + }; + + Builder.prototype.post = function(body) { + return promisify(this, originalMethods.post, [body]); + }; + + Builder.prototype.put = function(body) { + return promisify(this, originalMethods.put, [body]); + }; + + Builder.prototype.patch = function(body) { + return promisify(this, originalMethods.patch, [body]); + }; + + Builder.prototype.delete = Builder.prototype.del = function() { + return promisify(this, originalMethods.delete); + }; + + Builder.prototype.useAngularHttp = function() { + this.withRequestLibrary($httpTraversonAdapter); + return this; + }; + + return traverson; +}]); + +traversonAngular.factory('$httpTraversonAdapter', [ + '$http', '$q', function $httpTraversonAdapterFactory($http, $q) { + + function Request() { } + + Request.prototype.get = function(uri, options, callback) { + options = mapOptions(options); + $http + .get(uri, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.post = function(uri, options, callback) { + options = mapOptions(options); + $http + .post(uri, options.data, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.put = function(uri, options, callback) { + options = mapOptions(options); + $http + .put(uri, options.data, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.patch = function(uri, options, callback) { + options = mapOptions(options); + $http + .patch(uri, options.data, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.del = function(uri, options, callback) { + options = mapOptions(options); + $http + .delete(uri, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + function mapOptions(options) { + options = options || {}; + var mappedOptions = {}; + mapQuery(mappedOptions, options); + mapHeaders(mappedOptions, options); + mapAuth(mappedOptions, options); + mapBody(mappedOptions, options); + mapForm(mappedOptions, options); + // do not parse JSON automatically, this will trip up Traverson + mappedOptions.transformResponse = function(data, headersGetter, status) { + return data; + }; + // hook to abort the request, if necessary + mappedOptions.timeout = $q.defer(); + return mappedOptions; + } + + function mapQuery(mappedOptions, options) { + // options.qs would be correct since we are using request/request options + // object API, but a previous version of traverson-angular incorrectly + // used options.query instead, so we allow this also, to not break + // backwards compatibility. + var qs = options.qs || options.query; + if (qs) { + mappedOptions.params = qs; + } + } + + function mapHeaders(mappedOptions, options) { + if (options.headers) { + mappedOptions.headers = options.headers; + } + } + + function mapAuth(mappedOptions, options) { + var auth = options.auth; + if (auth) { + var username = auth.user || auth.username; + var password = auth.pass || auth.password; + mappedOptions.headers = mappedOptions.headers || {}; + mappedOptions.headers.Authorization = 'Basic ' + btoa(username + ':' + + password); + } + } + + function mapBody(mappedOptions, options) { + if (options.body) { + mappedOptions.data = options.body; + } + } + + function mapForm(mappedOptions, options) { + var form = options.form; + if (form) { + mappedOptions.data = form; + mappedOptions.headers = mappedOptions.headers || {}; + mappedOptions.headers['Content-Type'] = + 'application/x-www-form-urlencoded'; + } + } + + function mapResponse(response) { + response.body = response.data; + response.headers = response.headers(); + response.statusCode = response.status; + return response; + } + + function handleResponse(callback) { + return function(response) { + return callback(null, mapResponse(response)); + }; + } + + function handleError(callback) { + return function(response) { + if (response.status >= 100 && response.status < 600) { + // This happens on a completed HTTP request with a status code outside + // of the 2xx range. In the context of Traverson, this is not an + // error, in particular, if this is the last request in a traversal. + // Thus, we re-route it to the successCallback. Handling 4xx and 5xx + // errors during the traversal is the responsibility of traverson, not + // traverson-angular. + return callback(null, mapResponse(response)); + } else { + // This happens on network errors, timeouts etc. In this case, + // AngularJS sets the status property to 0. In the context of + // Traverson, only these are to be interpreted as errors. + return callback(response); + } + }; + } + + return new Request(); + } +]); + +function AbortHandle(abortPromise) { + this.abortPromise = abortPromise; + this.listeners = []; +} + +AbortHandle.prototype.abort = function() { + this.abortPromise.resolve(); + this.listeners.forEach(function(fn) { + fn.call(); + }); +}; + +AbortHandle.prototype.on = function(event, fn) { + if (event !== 'abort') { + var error = new Error('Event ' + event + ' not supported'); + error.name = 'InvalidArgumentError'; + throw error; + } + this.listeners.push(fn); +}; + +module.exports = traversonAngular; + +},{"angular":1,"traverson":48}]},{},["traverson-angular"]); diff --git a/browser/dist/traverson-angular.external.min.js b/browser/dist/traverson-angular.external.min.js new file mode 100644 index 0000000..6d08e3f --- /dev/null +++ b/browser/dist/traverson-angular.external.min.js @@ -0,0 +1,2 @@ +require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g1&&j.shift();var k=this._trace(j,b,["$"],h,i,c);return k=k.filter(function(a){return a&&!a.isParentSelector}),k.length?1!==k.length||g||Array.isArray(k[0].value)?k.reduce(function(a,b){var c=e._getPreferredOutput(b);return f&&Array.isArray(c)?a=a.concat(c):a.push(c),a},[]):this._getPreferredOutput(k[0]):g?[]:void 0}},JSONPath.prototype._getPreferredOutput=function(a){var b=this.currResultType;switch(b){case"all":return a.path="string"==typeof a.path?a.path:JSONPath.toPathString(a.path),a;case"value":case"parent":case"parentProperty":return a[b];case"path":return JSONPath.toPathString(a[b]);case"pointer":return JSONPath.toPointer(a.path)}},JSONPath.prototype._handleCallback=function(a,b,c){if(b){var d=this._getPreferredOutput(a);a.path="string"==typeof a.path?a.path:JSONPath.toPathString(a.path),b(d,c,a)}},JSONPath.prototype._trace=function(a,b,c,d,e,f){function g(a){l=l.concat(a)}var h,i=this;if(!a.length)return h={path:c,value:b,parent:d,parentProperty:e},this._handleCallback(h,f,"value"),h;var j=a[0],k=a.slice(1),l=[];if(b&&Object.prototype.hasOwnProperty.call(b,j))g(this._trace(k,b[j],push(c,j),b,j,f));else if("*"===j)this._walk(j,k,b,c,d,e,f,function(a,b,c,d,e,f,h,j){g(i._trace(unshift(a,c),d,e,f,h,j))});else if(".."===j)g(this._trace(k,b,c,d,e,f)),this._walk(j,k,b,c,d,e,f,function(a,b,c,d,e,f,h,j){"object"==typeof d[a]&&g(i._trace(unshift(b,c),d[a],push(e,a),d,a,j))});else if("("===j[0]){if(this.currPreventEval)throw new Error("Eval [(expr)] prevented in JSONPath expression.");g(this._trace(unshift(this._eval(j,b,c[c.length-1],c.slice(0,-1),d,e),k),b,c,d,e,f))}else{if("^"===j)return c.length?{path:c.slice(0,-1),expr:k,isParentSelector:!0}:[];if("~"===j)return h={path:push(c,j),value:e,parent:d,parentProperty:null},this._handleCallback(h,f,"property"),h;if("$"===j)g(this._trace(k,b,c,null,null,f));else if(0===j.indexOf("?(")){if(this.currPreventEval)throw new Error("Eval [?(expr)] prevented in JSONPath expression.");this._walk(j,k,b,c,d,e,f,function(a,b,c,d,e,f,h,j){i._eval(b.replace(/^\?\((.*?)\)$/,"$1"),d[a],a,e,f,h)&&g(i._trace(unshift(a,c),d,e,f,h,j))})}else if(j.indexOf(",")>-1){var m,n;for(m=j.split(","),n=0;n-1&&(this.currSandbox._$_parentProperty=f,a=a.replace(/@parentProperty/g,"_$_parentProperty")),a.indexOf("@parent")>-1&&(this.currSandbox._$_parent=e,a=a.replace(/@parent/g,"_$_parent")),a.indexOf("@property")>-1&&(this.currSandbox._$_property=c,a=a.replace(/@property/g,"_$_property")),a.indexOf("@path")>-1&&(this.currSandbox._$_path=JSONPath.toPathString(d.concat([c])),a=a.replace(/@path/g,"_$_path")),a.match(/@([\.\s\)\[])/)&&(this.currSandbox._$_v=b,a=a.replace(/@([\.\s\)\[])/g,"_$_v$1"));try{return vm.runInNewContext(a,this.currSandbox)}catch(b){throw console.log(b),new Error("jsonPath: "+b.message+": "+a)}},JSONPath.cache={},JSONPath.toPathString=function(a){var b,c,d=a,e="$";for(b=1,c=d.length;b1)for(var c=1;c=300)&&(d=new Error(b.statusText||"Unsuccessful HTTP response"),d.original=a,d.response=b,d.status=b.status)}catch(a){d=a}d?c.callback(d,b):c.callback(null,b)})}function n(a,b){var c=s("DELETE",a);return b&&c.end(b),c}var o;"undefined"!=typeof window?o=window:"undefined"!=typeof self?o=self:(console.warn("Using browser-only version of superagent in non-browser environment"),o=this);var p=a("emitter"),q=a("./request-base"),r=a("./is-object"),s=b.exports=a("./request").bind(null,m);s.getXHR=function(){if(!(!o.XMLHttpRequest||o.location&&"file:"==o.location.protocol&&o.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(a){}throw Error("Browser-only verison of superagent could not find XHR")};var t="".trim?function(a){return a.trim()}:function(a){return a.replace(/(^\s*|\s*$)/g,"")};s.serializeObject=e,s.parseString=g,s.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"},s.serialize={"application/x-www-form-urlencoded":e,"application/json":JSON.stringify},s.parse={"application/x-www-form-urlencoded":g,"application/json":JSON.parse},l.prototype.get=function(a){return this.header[a.toLowerCase()]},l.prototype._setHeaderProperties=function(a){var b=this.header["content-type"]||"";this.type=j(b);var c=k(b);for(var d in c)this[d]=c[d]},l.prototype._parseBody=function(a){var b=s.parse[this.type];return!b&&i(this.type)&&(b=s.parse["application/json"]),b&&a&&(a.length||a instanceof Object)?b(a):null},l.prototype._setStatusProperties=function(a){1223===a&&(a=204);var b=a/100|0;this.status=this.statusCode=a,this.statusType=b,this.info=1==b,this.ok=2==b,this.clientError=4==b,this.serverError=5==b,this.error=(4==b||5==b)&&this.toError(),this.accepted=202==a,this.noContent=204==a,this.badRequest=400==a,this.unauthorized=401==a,this.notAcceptable=406==a,this.notFound=404==a,this.forbidden=403==a},l.prototype.toError=function(){var a=this.req,b=a.method,c=a.url,d="cannot "+b+" "+c+" ("+this.status+")",e=new Error(d);return e.status=this.status,e.method=b,e.url=c,e},s.Response=l,p(m.prototype);for(var u in q)m.prototype[u]=q[u];m.prototype.type=function(a){return this.set("Content-Type",s.types[a]||a),this},m.prototype.responseType=function(a){return this._responseType=a,this},m.prototype.accept=function(a){return this.set("Accept",s.types[a]||a),this},m.prototype.auth=function(a,b,c){switch(c||(c={type:"basic"}),c.type){case"basic":var d=btoa(a+":"+b);this.set("Authorization","Basic "+d);break;case"auto":this.username=a,this.password=b}return this},m.prototype.query=function(a){return"string"!=typeof a&&(a=e(a)),a&&this._query.push(a),this},m.prototype.attach=function(a,b,c){return this._getFormData().append(a,b,c||b.name),this},m.prototype._getFormData=function(){return this._formData||(this._formData=new o.FormData),this._formData},m.prototype.callback=function(a,b){var c=this._callback;this.clearTimeout(),c(a,b)},m.prototype.crossDomainError=function(){var a=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.");a.crossDomain=!0,a.status=this.status,a.method=this.method,a.url=this.url,this.callback(a)},m.prototype._timeoutError=function(){var a=this._timeout,b=new Error("timeout of "+a+"ms exceeded");b.timeout=a,this.callback(b)},m.prototype._appendQueryString=function(){var a=this._query.join("&");a&&(this.url+=~this.url.indexOf("?")?"&"+a:"?"+a)},m.prototype.end=function(a){var b=this,c=this.xhr=s.getXHR(),e=this._timeout,f=this._formData||this._data;this._callback=a||d,c.onreadystatechange=function(){if(4==c.readyState){var a;try{a=c.status}catch(b){a=0}if(0==a){if(b.timedout)return b._timeoutError();if(b._aborted)return;return b.crossDomainError()}b.emit("end")}};var g=function(a,c){c.total>0&&(c.percent=c.loaded/c.total*100),c.direction=a,b.emit("progress",c)};if(this.hasListeners("progress"))try{c.onprogress=g.bind(null,"download"),c.upload&&(c.upload.onprogress=g.bind(null,"upload"))}catch(a){}if(e&&!this._timer&&(this._timer=setTimeout(function(){b.timedout=!0,b.abort()},e)),this._appendQueryString(),this.username&&this.password?c.open(this.method,this.url,!0,this.username,this.password):c.open(this.method,this.url,!0),this._withCredentials&&(c.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof f&&!this._isHost(f)){var h=this._header["content-type"],j=this._serializer||s.serialize[h?h.split(";")[0]:""];!j&&i(h)&&(j=s.serialize["application/json"]),j&&(f=j(f))}for(var k in this.header)null!=this.header[k]&&c.setRequestHeader(k,this.header[k]);return this._responseType&&(c.responseType=this._responseType),this.emit("request",this),c.send("undefined"!=typeof f?f:null),this},s.Request=m,s.get=function(a,b,c){var d=s("GET",a);return"function"==typeof b&&(c=b,b=null),b&&d.query(b),c&&d.end(c),d},s.head=function(a,b,c){var d=s("HEAD",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.options=function(a,b,c){var d=s("OPTIONS",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.del=n,s.delete=n,s.patch=function(a,b,c){var d=s("PATCH",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.post=function(a,b,c){var d=s("POST",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.put=function(a,b,c){var d=s("PUT",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d}},{"./is-object":8,"./request":10,"./request-base":9,emitter:2}],8:[function(a,b,c){function d(a){return null!==a&&"object"==typeof a}b.exports=d},{}],9:[function(a,b,c){var d=a("./is-object");c.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},c.parse=function(a){return this._parser=a,this},c.serialize=function(a){return this._serializer=a,this},c.timeout=function(a){return this._timeout=a,this},c.then=function(a,b){if(!this._fullfilledPromise){var c=this;this._fullfilledPromise=new Promise(function(a,b){c.end(function(c,d){c?b(c):a(d)})})}return this._fullfilledPromise.then(a,b)},c.catch=function(a){return this.then(void 0,a)},c.use=function(a){return a(this),this},c.get=function(a){return this._header[a.toLowerCase()]},c.getHeader=c.get,c.set=function(a,b){if(d(a)){for(var c in a)this.set(c,a[c]);return this}return this._header[a.toLowerCase()]=b,this.header[a]=b,this},c.unset=function(a){return delete this._header[a.toLowerCase()],delete this.header[a],this},c.field=function(a,b){if(null===a||void 0===a)throw new Error(".field(name, val) name can not be empty");if(d(a)){for(var c in a)this.field(c,a[c]);return this}if(null===b||void 0===b)throw new Error(".field(name, val) val can not be empty");return this._getFormData().append(a,b),this},c.abort=function(){return this._aborted?this:(this._aborted=!0,this.xhr&&this.xhr.abort(),this.req&&this.req.abort(),this.clearTimeout(),this.emit("abort"),this)},c.withCredentials=function(){return this._withCredentials=!0,this},c.redirects=function(a){return this._maxRedirects=a,this},c.toJSON=function(){return{method:this.method,url:this.url,data:this._data,headers:this._header}},c._isHost=function(a){var b={}.toString.call(a);switch(b){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}},c.send=function(a){var b=d(a),c=this._header["content-type"];if(b&&d(this._data))for(var e in a)this._data[e]=a[e];else"string"==typeof a?(c||this.type("form"),c=this._header["content-type"],"application/x-www-form-urlencoded"==c?this._data=this._data?this._data+"&"+a:a:this._data=(this._data||"")+a):this._data=a;return!b||this._isHost(a)?this:(c||this.type("json"),this)}},{"./is-object":8}],10:[function(a,b,c){function d(a,b,c){return"function"==typeof c?new a("GET",b).end(c):2==arguments.length?new a("GET",b):new a(b,c)}b.exports=d},{}],11:[function(a,b,c){"use strict";function d(a){null==a&&(a=""),this.id=a}function e(a){return new d(a)}var f=!1;d.prototype.enable=function(){this.enabled=!0},d.prototype.debug=function(a){f&&console.log(this.id+"/debug: "+a)},d.prototype.info=function(a){f&&console.log(this.id+"/info: "+a)},d.prototype.warn=function(a){f&&console.log(this.id+"/warn: "+a)},d.prototype.error=function(a){f&&console.log(this.id+"/error: "+a)},e.enable=function(){f=!0},b.exports=e},{}],12:[function(a,b,c){"use strict";b.exports={isArray:function(a){return null!=a&&"[object Array]"===Object.prototype.toString.call(a)}}},{}],13:[function(a,b,c){"use strict";function d(){}function e(a,b){return b=b||{},f(a,b),g(a,b),h(a,b),i(a,b),j(a,b),k(a,b),a}function f(a,b){var c=b.qs;null!=c&&(a=a.query(c))}function g(a,b){var c=b.headers;null!=c&&(a=a.set(c))}function h(a,b){var c=b.auth;null!=c&&(a=a.auth(c.user||c.username,c.pass||c.password))}function i(a,b){if(null!=b){var c=b.body;null!=c&&(a=a.send(c))}}function j(a,b){if(null!=b){var c=b.form;null!=c&&(a=a.set("content-type","application/x-www-form-urlencoded"),a=a.send(c))}}function k(a,b){if(null!=b){var c=b.withCredentials;c===!0&&a.withCredentials()}}function l(a){return a.body=a.text,a.statusCode=a.status,a}function m(a){return function(b,c){if(b){if(!c)return a(b);a(null,l(c))}else a(null,l(c))}}var n=a("superagent");d.prototype.get=function(a,b,c){return e(n.get(a),b).end(m(c))},d.prototype.post=function(a,b,c){return e(n.post(a),b).end(m(c))},d.prototype.put=function(a,b,c){return e(n.put(a),b).end(m(c))},d.prototype.patch=function(a,b,c){return e(n.patch(a),b).end(m(c))},d.prototype.del=function(a,b,c){return e(n.del(a),b).end(m(c))},b.exports=new d},{superagent:7}],14:[function(a,b,c){"use strict";var d={startsWith:function(a,b){return""===b||null!=a&&null!=b&&(a=String(a),b=String(b),a.length>=b.length&&a.slice(0,b.length)===b)},endsWith:function(a,b){return""===b||null!=a&&null!=b&&(a=String(a),b=String(b),a.length>=b.length&&a.slice(a.length-b.length)===b)},splice:function(a,b,c,e){var f=d.chars(a);return f.splice(~~b,~~c,e),f.join("")},contains:function(a,b){return""===b||null!=a&&String(a).indexOf(b)!==-1},chars:function(a){return null==a?[]:String(a).split("")}};b.exports=d},{}],15:[function(a,b,c){"use strict";var d=a("resolve-url");c.resolve=function(a,b){return d(a,b)}},{"resolve-url":6}],16:[function(a,b,c){"use strict";var d=a("minilog"),e=a("./errors"),f=e.errors,g=e.createError,h=d("traverson");c.abortTraversal=function(){h.debug("aborting link traversal"),this.aborted=!0,this.currentRequest&&(h.debug("request in progress. trying to abort it, too."),this.currentRequest.abort())},c.registerAbortListener=function(a,b){a.currentRequest&&a.currentRequest.on("abort",function(){c.callCallbackOnAbort(a)})},c.callCallbackOnAbort=function(a){h.debug("link traversal aborted"),a.callbackHasBeenCalledAfterAbort||(a.callbackHasBeenCalledAfterAbort=!0,a.callback(c.abortError(),a))},c.abortError=function(){var a=g("Link traversal process has been aborted.",f.TraversalAbortedError);return a.aborted=!0,a}},{"./errors":19,minilog:11}],17:[function(a,b,c){"use strict";function d(a,b,c,d){var e;e=a.convertResponseToObject?[q,h,r,l]:[p],a.lastMethod=c,g.walk(a,e,d)}function e(a){return{abort:a.abortTraversal}}var f=a("minilog"),g=(f("traverson"),a("./abort_traversal"),a("./transforms/apply_transforms"),a("./http_requests"),a("./is_continuation"),a("./walker")),h=a("./transforms/check_http_status"),i=a("./transforms/continuation_to_doc"),j=a("./transforms/continuation_to_response"),k=a("./transforms/convert_embedded_doc_to_response"),l=a("./transforms/extract_doc"),m=a("./transforms/extract_response"),n=a("./transforms/extract_url"),o=a("./transforms/fetch_last_resource"),p=a("./transforms/execute_last_http_request"),q=a("./transforms/execute_http_request"),r=a("./transforms/parse");c.get=function(a,b){var c;return c=a.convertResponseToObject?[i,o,h,r,l]:[j,o,k,m],g.walk(a,c,b),e(a)},c.getUrl=function(a,b){return g.walk(a,[n],b),e(a)},c.post=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.post,b),e(a)},c.put=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.put,b),e(a)},c.patch=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.patch,b),e(a)},c.delete=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.del,b),e(a)}},{"./abort_traversal":16,"./http_requests":20,"./is_continuation":21,"./transforms/apply_transforms":27,"./transforms/check_http_status":28,"./transforms/continuation_to_doc":29,"./transforms/continuation_to_response":30,"./transforms/convert_embedded_doc_to_response":31,"./transforms/execute_http_request":33,"./transforms/execute_last_http_request":34,"./transforms/extract_doc":35,"./transforms/extract_response":36,"./transforms/extract_url":37,"./transforms/fetch_last_resource":38,"./transforms/parse":41,"./walker":47,minilog:11}],18:[function(a,b,c){"use strict";function d(a){this.mediaType=a||w.CONTENT_NEGOTIATION,this.adapter=this._createAdapter(this.mediaType),this.contentNegotiation=!0,this.convertResponseToObjectFlag=!1,this.links=[],this.jsonParser=JSON.parse,this.requestModuleInstance=o,this.requestOptions={},this.resolveRelativeFlag=!1,this.preferEmbedded=!1,this.lastTraversalState=null,this.continuation=null}function e(a,b){for(var c=0;c continuing finished traversal process"),a.continuation={step:b.step,action:d},a.continuation.step.index=0,j(a,b),a}})}}function j(a,b){a.aborted=!1,a.adapter=b.adapter,a.body=b.body,a.callbackHasBeenCalledAfterAbort=!1,a.contentNegotiation=b.contentNegotiation,a.convertResponseToObjectFlag=b.convertResponseToObject,a.links=[],a.jsonParser=b.jsonParser,a.requestModuleInstance=b.requestModuleInstance,a.requestOptions=b.requestOptions,a.resolveRelativeFlag=b.resolveRelative,a.preferEmbedded=b.preferEmbedded,a.startUrl=b.startUrl,a.templateParameters=b.templateParameters}function k(a){return p.isArray(a)?m(a):"object"==typeof a?l(a):a}function l(a){return x(null,a)}function m(a){return a?a.slice(0):a}var n=a("minilog"),o=a("request"),p=a("util"),q=a("./actions"),r=a("./abort_traversal").abortTraversal,s=a("./errors"),t=s.errors,u=s.createError,v=a("./media_type_registry"),w=a("./media_types"),x=a("./merge_recursive"),y=n("traverson");d.prototype._createAdapter=function(a){var b=v.get(a);if(!b)throw u("Unknown or unsupported media type: "+a,t.UnsupportedMediaType);return y.debug("creating new "+b.name),new b(y)},d.prototype.newRequest=function(){var a=new d(this.getMediaType());return a.contentNegotiation=this.doesContentNegotiation(),a.convertResponseToObject(this.convertsResponseToObject()),a.from(m(this.getFrom())),a.withTemplateParameters(k(this.getTemplateParameters())),a.withRequestOptions(k(this.getRequestOptions())),a.withRequestLibrary(this.getRequestLibrary()),a.parseResponseBodiesWith(this.getJsonParser()),a.resolveRelative(this.doesResolveRelative()),a.preferEmbeddedResources(this.doesPreferEmbeddedResources()),a.continuation=this.continuation,a},d.prototype.setMediaType=function(a){return this.mediaType=a||w.CONTENT_NEGOTIATION,this.adapter=this._createAdapter(a),this.contentNegotiation=a===w.CONTENT_NEGOTIATION,this},d.prototype.json=function(){return this.setMediaType(w.JSON),this},d.prototype.jsonHal=function(){return this.setMediaType(w.JSON_HAL),this},d.prototype.useContentNegotiation=function(){return this.setMediaType(w.CONTENT_NEGOTIATION),this.contentNegotiation=!0,this},d.prototype.from=function(a){return this.startUrl=a,this},d.prototype.follow=function(){for(var a=Array.prototype.slice.apply(1===arguments.length&&p.isArray(arguments[0])?arguments[0]:arguments),b=0;b1?j("JSONPath expression "+b+" returned more than one match in document:\n"+JSON.stringify(a),i.JSONPathError,a):j("JSONPath expression "+b+" returned no match in document:\n"+JSON.stringify(a),i.JSONPathError,a)},d.prototype._handleHeader=function(a,b){switch(b.value){case"location":var c=a.headers.location;if(!c)throw j("Following the location header but there was no location header in the last response.",i.LinkError,a.headers);return{url:c};default:throw j("Link objects with type header and value "+b.value+" are not supported by this adapter.",i.InvalidArgumentError,b)}},b.exports=d},{"./errors":19,"jsonpath-plus":4,minilog:11,"underscore.string":14}],23:[function(a,b,c){"use strict";var d=a("./media_types"),e={};c.register=function(a,b){e[a]=b},c.get=function(a){return e[a]},c.register(d.CONTENT_NEGOTIATION,a("./negotiation_adapter")),c.register(d.JSON,a("./json_adapter"))},{"./json_adapter":22,"./media_types":24,"./negotiation_adapter":26}],24:[function(a,b,c){"use strict";b.exports={CONTENT_NEGOTIATION:"content-negotiation",JSON:"application/json",JSON_HAL:"application/hal+json"}},{}],25:[function(a,b,c){"use strict";function d(a,b){!a&&b&&(a={});for(var c in b)b.hasOwnProperty(c)&&e(a,b,c);return a}function e(a,b,c){"object"==typeof b[c]?(a[c]&&"object"==typeof a[c]||(a[c]={}),d(a[c],b[c])):a[c]=b[c]}b.exports=d},{}],26:[function(a,b,c){"use strict";function d(a){}var e=a("./errors"),f=e.errors,g=e.createError;d.prototype.findNextStep=function(a,b){throw g("Content negotiation did not happen",f.InvalidStateError)},b.exports=d},{"./errors":19}],27:[function(a,b,c){(function(c){"use strict";function d(a,b,e){f.debug("applying",a.length,"transforms");for(var g=0;g=300)){var c=d(a.step.url,b,a.step.response.body);return f.error("unexpected http status code"),f.error(c),a.callback(c),!1}return f.debug("http status code ok ("+b+")"),!0}},{"../errors":19,"../is_continuation":21,minilog:11}],29:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("../is_continuation");b.exports=function(a){return!f(a)||(e.debug("continuing from last traversal process (actions)"),a.continuation=null,a.callback(null,a.step.doc),!1)}},{"../is_continuation":21,minilog:11}],30:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("./convert_embedded_doc_to_response"),g=a("../is_continuation");b.exports=function(a){return!g(a)||(e.debug("continuing from last traversal process (actions)"),a.continuation=null,f(a),a.callback(null,a.step.response),!1)}},{"../is_continuation":21,"./convert_embedded_doc_to_response":31,minilog:11}],31:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson");b.exports=function(a){return!a.step.response&&a.step.doc&&(e.debug("faking HTTP response for embedded resource"),a.step.response={statusCode:200,body:JSON.stringify(a.step.doc),remark:"This is not an actual HTTP response. The resource you requested was an embedded resource, so no HTTP request was made to acquire it."}),!0}},{minilog:11}],32:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("../media_type_registry"),g=a("../errors"),h=g.errors,i=g.createError;b.exports=function(a,b){if(a.contentNegotiation&&a.step.response&&a.step.response.headers&&a.step.response.headers["content-type"]){var c=a.step.response.headers["content-type"].split(/[; ]/)[0],d=f.get(c);if(!d)return b(i("Unknown content type for content type detection: "+c,h.UnsupportedMediaType)),!1;a.adapter=new d(e)}return!0}},{"../errors":19,"../media_type_registry":23,minilog:11}],33:[function(a,b,c){"use strict";function d(a,b){return a.aborted?g.callCallbackOnAbort(a):void h.executeHttpRequest(a,a.requestModuleInstance,a.lastMethod,function(c,d){return c?(c.aborted||(f.debug("error while processing step ",a.step),f.error(c)),a.callback(c)):void b(a)})}var e=a("minilog"),f=e("traverson"),g=a("../abort_traversal"),h=a("../http_requests");d.isAsync=!0,b.exports=d},{"../abort_traversal":16,"../http_requests":20,minilog:11}],34:[function(a,b,c){"use strict";function d(a,b){return a.aborted?f.callCallbackOnAbort(a):void g.executeHttpRequest(a,a.requestModuleInstance,a.lastMethod,a.callback)}var e=a("minilog"),f=(e("traverson"),a("../abort_traversal")),g=a("../http_requests");d.isAsync=!0,b.exports=d},{"../abort_traversal":16,"../http_requests":20,minilog:11}],35:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson");b.exports=function(a){return e.debug("walker.walk has finished"),a.callback(null,a.step.doc),!1}},{minilog:11}],36:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson");b.exports=function(a){return e.debug("walker.walk has finished"),a.callback(null,a.step.response),!1}},{minilog:11}],37:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("url"),g=a("../errors"),h=g.errors,i=g.createError;b.exports=function(a){return e.debug("walker.walk has finished"),a.step.url?a.callback(null,a.step.url):a.step.doc&&a.step.doc._links&&a.step.doc._links.self&&a.step.doc._links.self.href?a.callback(null,f.resolve(a.startUrl,a.step.doc._links.self.href)):a.callback(i('You requested an URL but the last resource is an embedded resource and has no URL of its own (that is, it has no link with rel="self"',h.LinkError))}},{"../errors":19,minilog:11,url:15}],38:[function(a,b,c){"use strict";function d(a,b){return a.aborted?g.callCallbackOnAbort(a):void h.fetchResource(a,function(a,c){return f.debug("fetchResource returned (fetchLastResource)."),a?(a.aborted||(f.debug("error while processing step ",c.step),f.error(a)),c.callback(a)):void b(c)})}var e=a("minilog"),f=e("traverson"),g=a("../abort_traversal"),h=a("../http_requests");d.isAsync=!0,b.exports=d},{"../abort_traversal":16,"../http_requests":20,minilog:11}],39:[function(a,b,c){(function(c){"use strict";function d(a,b){j(a)?e(a,b):f(a,b)}function e(a,b){h.debug("continuing from last traversal process (walker)"),c.nextTick(function(){b(a)})}function f(a,b){return a.aborted?i.callCallbackOnAbort(a):void k.fetchResource(a,function(a,c){return h.debug("fetchResource returned"),a?(a.aborted||(h.debug("error while processing step ",c.step),h.error(a)),c.callback(a)):void b(c)})}var g=a("minilog"),h=g("traverson"),i=a("../abort_traversal"),j=a("../is_continuation"),k=a("../http_requests");d.isAsync=!0,b.exports=d}).call(this,a("_process"))},{"../abort_traversal":16,"../http_requests":20,"../is_continuation":21,_process:5,minilog:11}],40:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("util");b.exports=function(a){var b=a.requestOptions;return f.isArray(a.requestOptions)&&(b=a.requestOptions[a.step.index]||{}),e.debug("options: ",b),b}},{minilog:11,util:12}],41:[function(a,b,c){"use strict";function d(a){h.debug("parsing response body"),a.requestOptions&&a.requestOptions.jsonReviver?a.step.doc=a.step.response.body:a.step.doc=a.jsonParser(a.step.response.body)}function e(a,b){var c=b;"SyntaxError"===b.name&&(c=f(a.step.url,a.step.response.body)),h.error("parsing failed"),h.error(c),a.callback(c)}function f(a,b){var c=k("The document at "+a+" could not be parsed as JSON: "+b,j.JSONError,b);return c.url=a,c.body=b,c}var g=a("minilog"),h=g("traverson"),i=a("../errors"),j=i.errors,k=i.createError,l=a("../is_continuation");b.exports=function(a){if(l(a)&&(h.debug("continuing from last traversal process (transforms/parse)"),"getResource"===a.continuation.action))return!0;if(a.step.doc)return h.debug("no parsing necessary, probably an embedded document"),!0;try{return d(a),!0}catch(b){return e(a,b),!1}}},{"../errors":19,"../is_continuation":21,minilog:11}],42:[function(a,b,c){"use strict";var d=a("../is_continuation");b.exports=function(a){return!!d(a)||(a.continuation=null,!0)}},{"../is_continuation":21}],43:[function(a,b,c){"use strict";var d=a("../is_continuation");b.exports=function(a){return!!d(a)||(a.lastStep=null,!0)}},{"../is_continuation":21}],44:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("underscore.string"),g=a("url"),h=/https?:\/\//i;b.exports=function(a){return a.step.url&&0!==a.step.url.search(h)&&(e.debug("found non full qualified URL"),a.resolveRelative&&a.lastStep&&a.lastStep.url?(e.debug("resolving URL relative"),f.startsWith(a.step.url,"/")&&f.endsWith(a.lastStep.url,"/")&&(a.step.url=f.splice(a.step.url,0,1)),a.step.url=a.lastStep.url+a.step.url):a.step.url=g.resolve(a.startUrl,a.step.url)),!0}},{minilog:11,"underscore.string":14,url:15}],45:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("underscore.string"),g=a("url-template"),h=a("util");b.exports=function(a){if(a.step.url){var b=a.templateParameters;if(h.isArray(b)&&(b=b[a.step.index]),b=b||{},f.contains(a.step.url,"{")){e.debug("resolving URI template");var c=g.parse(a.step.url),d=c.expand(b);e.debug("resolved to ",d),a.step.url=d}}return!0}},{minilog:11,"underscore.string":14,"url-template":49,util:12}],46:[function(a,b,c){"use strict";function d(a,b){try{return a.adapter.findNextStep(a,b)}catch(b){return f.error("could not find next step"),f.error(b),a.callback(b),null}}var e=a("minilog"),f=e("traverson");b.exports=function(a){var b=a.links[a.step.index];return f.debug("next link: "+b),a.lastStep=a.step,a.step=d(a,b),!!a.step&&(f.debug("found next step",a.step),a.step.url=a.step.url||a.step.uri,a.step.index=a.lastStep.index+1,!0)}},{minilog:11}],47:[function(a,b,c){"use strict";function d(a,b){if(h.debug("processing next step"),e(a)&&!f(a))j(l,a,function(a){h.debug("successfully processed step"),d(a,b)});else{if(f(a))return i.callCallbackOnAbort(a);h.debug("link array exhausted"),j(b,a,function(a){return a.callback()})}}function e(a){return a.step.index=100&&b.status<600?a(null,k(b)):a(b)}}return c.prototype.get=function(b,c,f){return c=e(c),a.get(b,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.post=function(b,c,f){return c=e(c),a.post(b,c.data,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.put=function(b,c,f){return c=e(c),a.put(b,c.data,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.patch=function(b,c,f){return c=e(c),a.patch(b,c.data,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.del=function(b,c,f){return c=e(c),a.delete(b,c).then(l(f)).catch(m(f)),new d(c.timeout)},new c}]),d.prototype.abort=function(){this.abortPromise.resolve(),this.listeners.forEach(function(a){a.call()})},d.prototype.on=function(a,b){if("abort"!==a){var c=new Error("Event "+a+" not supported");throw c.name="InvalidArgumentError",c}this.listeners.push(b)},b.exports=g},{angular:1,traverson:48}]},{},["traverson-angular"]); \ No newline at end of file diff --git a/browser/dist/traverson-angular.js b/browser/dist/traverson-angular.js new file mode 100644 index 0000000..212c025 --- /dev/null +++ b/browser/dist/traverson-angular.js @@ -0,0 +1,5157 @@ +(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.traversonAngular = 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 1) {exprList.shift();} + var result = this._trace(exprList, json, ['$'], currParent, currParentProperty, callback); + result = result.filter(function (ea) {return ea && !ea.isParentSelector;}); + + if (!result.length) {return wrap ? [] : undefined;} + if (result.length === 1 && !wrap && !Array.isArray(result[0].value)) { + return this._getPreferredOutput(result[0]); + } + return result.reduce(function (result, ea) { + var valOrPath = self._getPreferredOutput(ea); + if (flatten && Array.isArray(valOrPath)) { + result = result.concat(valOrPath); + } + else { + result.push(valOrPath); + } + return result; + }, []); +}; + +// PRIVATE METHODS + +JSONPath.prototype._getPreferredOutput = function (ea) { + var resultType = this.currResultType; + switch (resultType) { + case 'all': + ea.path = typeof ea.path === 'string' ? ea.path : JSONPath.toPathString(ea.path); + return ea; + case 'value': case 'parent': case 'parentProperty': + return ea[resultType]; + case 'path': + return JSONPath.toPathString(ea[resultType]); + case 'pointer': + return JSONPath.toPointer(ea.path); + } +}; + +JSONPath.prototype._handleCallback = function (fullRetObj, callback, type) { + if (callback) { + var preferredOutput = this._getPreferredOutput(fullRetObj); + fullRetObj.path = typeof fullRetObj.path === 'string' ? fullRetObj.path : JSONPath.toPathString(fullRetObj.path); + callback(preferredOutput, type, fullRetObj); + } +}; + +JSONPath.prototype._trace = function (expr, val, path, parent, parentPropName, callback) { + // No expr to follow? return path and value as the result of this trace branch + var retObj, self = this; + if (!expr.length) { + retObj = {path: path, value: val, parent: parent, parentProperty: parentPropName}; + this._handleCallback(retObj, callback, 'value'); + return retObj; + } + + var loc = expr[0], x = expr.slice(1); + + // We need to gather the return value of recursive trace calls in order to + // do the parent sel computation. + var ret = []; + function addRet (elems) {ret = ret.concat(elems);} + + if (val && Object.prototype.hasOwnProperty.call(val, loc)) { // simple case--directly follow property + addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback)); + } + else if (loc === '*') { // all child properties + this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, x, v, p, par, pr, cb) { + addRet(self._trace(unshift(m, x), v, p, par, pr, cb)); + }); + } + else if (loc === '..') { // all descendent parent properties + addRet(this._trace(x, val, path, parent, parentPropName, callback)); // Check remaining expression with val's immediate children + this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, x, v, p, par, pr, cb) { + // We don't join m and x here because we only want parents, not scalar values + if (typeof v[m] === 'object') { // Keep going with recursive descent on val's object children + addRet(self._trace(unshift(l, x), v[m], push(p, m), v, m, cb)); + } + }); + } + else if (loc[0] === '(') { // [(expr)] (dynamic property/index) + if (this.currPreventEval) { + throw new Error('Eval [(expr)] prevented in JSONPath expression.'); + } + // As this will resolve to a property name (but we don't know it yet), property and parent information is relative to the parent of the property to which this expression will resolve + addRet(this._trace(unshift(this._eval(loc, val, path[path.length - 1], path.slice(0, -1), parent, parentPropName), x), val, path, parent, parentPropName, callback)); + } + // The parent sel computation is handled in the frame above using the + // ancestor object of val + else if (loc === '^') { + // This is not a final endpoint, so we do not invoke the callback here + return path.length ? { + path: path.slice(0, -1), + expr: x, + isParentSelector: true + } : []; + } + else if (loc === '~') { // property name + retObj = {path: push(path, loc), value: parentPropName, parent: parent, parentProperty: null}; + this._handleCallback(retObj, callback, 'property'); + return retObj; + } + else if (loc === '$') { // root only + addRet(this._trace(x, val, path, null, null, callback)); + } + else if (loc.indexOf('?(') === 0) { // [?(expr)] (filtering) + if (this.currPreventEval) { + throw new Error('Eval [?(expr)] prevented in JSONPath expression.'); + } + this._walk(loc, x, val, path, parent, parentPropName, callback, function (m, l, x, v, p, par, pr, cb) { + if (self._eval(l.replace(/^\?\((.*?)\)$/, '$1'), v[m], m, p, par, pr)) { + addRet(self._trace(unshift(m, x), v, p, par, pr, cb)); + } + }); + } + else if (loc.indexOf(',') > -1) { // [name1,name2,...] + var parts, i; + for (parts = loc.split(','), i = 0; i < parts.length; i++) { + addRet(this._trace(unshift(parts[i], x), val, path, parent, parentPropName, callback)); + } + } + else if (loc[0] === '@') { // value type: @boolean(), etc. + var addType = false; + var valueType = loc.slice(1, -2); + switch (valueType) { + case 'scalar': + if (!val || (['object', 'function'].indexOf(typeof val) === -1)) { + addType = true; + } + break; + case 'boolean': case 'string': case 'undefined': case 'function': + if (typeof val === valueType) { + addType = true; + } + break; + case 'number': + if (typeof val === valueType && isFinite(val)) { + addType = true; + } + break; + case 'nonFinite': + if (typeof val === 'number' && !isFinite(val)) { + addType = true; + } + break; + case 'object': + if (val && typeof val === valueType) { + addType = true; + } + break; + case 'array': + if (Array.isArray(val)) { + addType = true; + } + break; + case 'other': + addType = this.currOtherTypeCallback(val, path, parent, parentPropName); + break; + case 'integer': + if (val === +val && isFinite(val) && !(val % 1)) { + addType = true; + } + break; + case 'null': + if (val === null) { + addType = true; + } + break; + } + if (addType) { + retObj = {path: path, value: val, parent: parent, parentProperty: parentPropName}; + this._handleCallback(retObj, callback, 'value'); + return retObj; + } + } + else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] Python slice syntax + addRet(this._slice(loc, x, val, path, parent, parentPropName, callback)); + } + + // We check the resulting values for parent selections. For parent + // selections we discard the value object and continue the trace with the + // current val object + return ret.reduce(function (all, ea) { + return all.concat(ea.isParentSelector ? self._trace(ea.expr, val, ea.path, parent, parentPropName, callback) : ea); + }, []); +}; + +JSONPath.prototype._walk = function (loc, expr, val, path, parent, parentPropName, callback, f) { + var i, n, m; + if (Array.isArray(val)) { + for (i = 0, n = val.length; i < n; i++) { + f(i, loc, expr, val, path, parent, parentPropName, callback); + } + } + else if (typeof val === 'object') { + for (m in val) { + if (Object.prototype.hasOwnProperty.call(val, m)) { + f(m, loc, expr, val, path, parent, parentPropName, callback); + } + } + } +}; + +JSONPath.prototype._slice = function (loc, expr, val, path, parent, parentPropName, callback) { + if (!Array.isArray(val)) {return;} + var i, + len = val.length, parts = loc.split(':'), + start = (parts[0] && parseInt(parts[0], 10)) || 0, + end = (parts[1] && parseInt(parts[1], 10)) || len, + step = (parts[2] && parseInt(parts[2], 10)) || 1; + start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start); + end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end); + var ret = []; + for (i = start; i < end; i += step) { + ret = ret.concat(this._trace(unshift(i, expr), val, path, parent, parentPropName, callback)); + } + return ret; +}; + +JSONPath.prototype._eval = function (code, _v, _vname, path, parent, parentPropName) { + if (!this._obj || !_v) {return false;} + if (code.indexOf('@parentProperty') > -1) { + this.currSandbox._$_parentProperty = parentPropName; + code = code.replace(/@parentProperty/g, '_$_parentProperty'); + } + if (code.indexOf('@parent') > -1) { + this.currSandbox._$_parent = parent; + code = code.replace(/@parent/g, '_$_parent'); + } + if (code.indexOf('@property') > -1) { + this.currSandbox._$_property = _vname; + code = code.replace(/@property/g, '_$_property'); + } + if (code.indexOf('@path') > -1) { + this.currSandbox._$_path = JSONPath.toPathString(path.concat([_vname])); + code = code.replace(/@path/g, '_$_path'); + } + if (code.match(/@([\.\s\)\[])/)) { + this.currSandbox._$_v = _v; + code = code.replace(/@([\.\s\)\[])/g, '_$_v$1'); + } + try { + return vm.runInNewContext(code, this.currSandbox); + } + catch (e) { + console.log(e); + throw new Error('jsonPath: ' + e.message + ': ' + code); + } +}; + +// PUBLIC CLASS PROPERTIES AND METHODS + +// Could store the cache object itself +JSONPath.cache = {}; + +JSONPath.toPathString = function (pathArr) { + var i, n, x = pathArr, p = '$'; + for (i = 1, n = x.length; i < n; i++) { + if (!(/^(~|\^|@.*?\(\))$/).test(x[i])) { + p += (/^[0-9*]+$/).test(x[i]) ? ('[' + x[i] + ']') : ("['" + x[i] + "']"); + } + } + return p; +}; + +JSONPath.toPointer = function (pointer) { + var i, n, x = pointer, p = ''; + for (i = 1, n = x.length; i < n; i++) { + if (!(/^(~|\^|@.*?\(\))$/).test(x[i])) { + p += '/' + x[i].toString() + .replace(/\~/g, '~0') + .replace(/\//g, '~1'); + } + } + return p; +}; + +JSONPath.toPathArray = function (expr) { + var cache = JSONPath.cache; + if (cache[expr]) {return cache[expr];} + var subx = []; + var normalized = expr + // Properties + .replace(/@(?:null|boolean|number|string|integer|undefined|nonFinite|scalar|array|object|function|other)\(\)/g, ';$&;') + // Parenthetical evaluations (filtering and otherwise), directly within brackets or single quotes + .replace(/[\['](\??\(.*?\))[\]']/g, function ($0, $1) {return '[#' + (subx.push($1) - 1) + ']';}) + // Escape periods and tildes within properties + .replace(/\['([^'\]]*)'\]/g, function ($0, prop) { + return "['" + prop.replace(/\./g, '%@%').replace(/~/g, '%%@@%%') + "']"; + }) + // Properties operator + .replace(/~/g, ';~;') + // Split by property boundaries + .replace(/'?\.'?(?![^\[]*\])|\['?/g, ';') + // Reinsert periods within properties + .replace(/%@%/g, '.') + // Reinsert tildes within properties + .replace(/%%@@%%/g, '~') + // Parent + .replace(/(?:;)?(\^+)(?:;)?/g, function ($0, ups) {return ';' + ups.split('').join(';') + ';';}) + // Descendents + .replace(/;;;|;;/g, ';..;') + // Remove trailing + .replace(/;$|'?\]|'$/g, ''); + + var exprList = normalized.split(';').map(function (expr) { + var match = expr.match(/#([0-9]+)/); + return !match || !match[1] ? expr : subx[match[1]]; + }); + cache[expr] = exprList; + return cache[expr]; +}; + +// For backward compatibility (deprecated) +JSONPath.eval = function (obj, expr, opts) { + return JSONPath(opts, expr, obj); +}; + +if (typeof define === 'function' && define.amd) { + define(function () {return JSONPath;}); +} +else if (isNode) { + module.exports = JSONPath; +} +else { + glbl.jsonPath = { // Deprecated + eval: JSONPath.eval + }; + glbl.JSONPath = JSONPath; +} +}(this || self, typeof require === 'undefined' ? null : require)); + +},{"vm":50}],5:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],6:[function(require,module,exports){ +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) + +void (function(root, factory) { + if (typeof define === "function" && define.amd) { + define(factory) + } else if (typeof exports === "object") { + module.exports = factory() + } else { + root.resolveUrl = factory() + } +}(this, function() { + + function resolveUrl(/* ...urls */) { + var numUrls = arguments.length + + if (numUrls === 0) { + throw new Error("resolveUrl requires at least one argument; got none.") + } + + var base = document.createElement("base") + base.href = arguments[0] + + if (numUrls === 1) { + return base.href + } + + var head = document.getElementsByTagName("head")[0] + head.insertBefore(base, head.firstChild) + + var a = document.createElement("a") + var resolved + + for (var index = 1; index < numUrls; index++) { + a.href = arguments[index] + resolved = a.href + base.href = resolved + } + + head.removeChild(base) + + return resolved + } + + return resolveUrl + +})); + +},{}],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('emitter'); +var requestBase = require('./request-base'); +var isObject = require('./is-object'); + +/** + * Noop. + */ + +function noop(){}; + +/** + * Expose `request`. + */ + +var request = module.exports = require('./request').bind(null, 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 verison 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: '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; +} + +/** + * Check if `mime` is json or has +json structured syntax suffix. + * + * @param {String} mime + * @return {Boolean} + * @api private + */ + +function isJSON(mime) { + return /[\/+]json\b/.test(mime); +} + +/** + * 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 str.split(/ *; */).reduce(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; + // 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; + 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 ? this.text : this.xhr.response) + : 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]; + if (!parse && isJSON(this.type)) { + parse = request.parse['application/json']; + } + return parse && str && (str.length || str instanceof Object) + ? 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){ + // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request + if (status === 1223) { + status = 204; + } + + 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.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.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 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 + err.rawResponse = self.xhr && self.xhr.responseText ? self.xhr.responseText : null; + // issue #876: return the http status code if the response parsing fails + err.statusCode = self.xhr && self.xhr.status ? self.xhr.status : null; + return self.callback(err); + } + + self.emit('response', res); + + var new_err; + try { + if (res.status < 200 || res.status >= 300) { + new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); + new_err.original = err; + new_err.response = res; + new_err.status = res.status; + } + } catch(e) { + new_err = e; // #985 touching res may cause INVALID_STATE_ERR on old Android + } + + // #1000 don't catch errors from the callback to avoid double calling it + if (new_err) { + self.callback(new_err, res); + } else { + self.callback(null, res); + } + }); +} + +/** + * Mixin `Emitter` and `requestBase`. + */ + +Emitter(Request.prototype); +for (var key in requestBase) { + Request.prototype[key] = requestBase[key]; +} + +/** + * 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 responseType to `val`. Presently valid responseTypes are 'blob' and + * 'arraybuffer'. + * + * Examples: + * + * req.get('/') + * .responseType('blob') + * .end(callback); + * + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +Request.prototype.responseType = function(val){ + this._responseType = val; + 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 + * @param {Object} options with 'type' property 'auto' or 'basic' (default 'basic') + * @return {Request} for chaining + * @api public + */ + +Request.prototype.auth = function(user, pass, options){ + if (!options) { + options = { + type: 'basic' + } + } + + switch (options.type) { + case 'basic': + var str = btoa(user + ':' + pass); + this.set('Authorization', 'Basic ' + str); + break; + + case 'auto': + this.username = user; + this.password = pass; + break; + } + 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; +}; + +/** + * Queue the given `file` as an attachment to the specified `field`, + * with optional `filename`. + * + * ``` js + * request.post('/upload') + * .attach('content', new Blob(['hey!'], { type: "text/html"})) + * .end(callback); + * ``` + * + * @param {String} field + * @param {Blob|File} file + * @param {String} filename + * @return {Request} for chaining + * @api public + */ + +Request.prototype.attach = function(field, file, filename){ + this._getFormData().append(field, file, filename || 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){ + var fn = this._callback; + this.clearTimeout(); + 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); +}; + +/** + * 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); +}; + +/** + * Compose querystring to append to req.url + * + * @api private + */ + +Request.prototype._appendQueryString = function(){ + var query = this._query.join('&'); + if (query) { + this.url += ~this.url.indexOf('?') + ? '&' + query + : '?' + query; + } +}; + +/** + * 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 = request.getXHR(); + var timeout = this._timeout; + var data = this._formData || this._data; + + // store callback + this._callback = fn || noop; + + // state change + xhr.onreadystatechange = function(){ + if (4 != xhr.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 (0 == status) { + if (self.timedout) return self._timeoutError(); + if (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 + } + } + + // timeout + if (timeout && !this._timer) { + this._timer = setTimeout(function(){ + self.timedout = true; + self.abort(); + }, timeout); + } + + // querystring + this._appendQueryString(); + + // initiate request + if (this.username && this.password) { + xhr.open(this.method, this.url, true, this.username, this.password); + } else { + 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 && !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; + 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; +}; + + +/** + * Expose `Request`. + */ + +request.Request = Request; + +/** + * 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; +}; + +/** + * 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 callback `fn(res)`. + * + * @param {String} url + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +function del(url, fn){ + var req = request('DELETE', url); + 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; +}; + +},{"./is-object":8,"./request":10,"./request-base":9,"emitter":2}],8:[function(require,module,exports){ +/** + * Check if `obj` is an object. + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ + +function isObject(obj) { + return null !== obj && 'object' === typeof obj; +} + +module.exports = isObject; + +},{}],9:[function(require,module,exports){ +/** + * Module of mixed-in functions shared between node and client code + */ +var isObject = require('./is-object'); + +/** + * Clear previous timeout. + * + * @return {Request} for chaining + * @api public + */ + +exports.clearTimeout = function _clearTimeout(){ + this._timeout = 0; + clearTimeout(this._timer); + return this; +}; + +/** + * Override default response body parser + * + * This function will be called to convert incoming data into request.body + * + * @param {Function} + * @api public + */ + +exports.parse = function parse(fn){ + this._parser = fn; + return this; +}; + +/** + * Override default request body serializer + * + * This function will be called to convert data set via .send or .attach into payload to send + * + * @param {Function} + * @api public + */ + +exports.serialize = function serialize(fn){ + this._serializer = fn; + return this; +}; + +/** + * Set timeout to `ms`. + * + * @param {Number} ms + * @return {Request} for chaining + * @api public + */ + +exports.timeout = function timeout(ms){ + this._timeout = ms; + return this; +}; + +/** + * Promise support + * + * @param {Function} resolve + * @param {Function} reject + * @return {Request} + */ + +exports.then = function then(resolve, reject) { + if (!this._fullfilledPromise) { + var self = this; + 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); +} + +exports.catch = function(cb) { + return this.then(undefined, cb); +}; + +/** + * Allow for extension + */ + +exports.use = function use(fn) { + fn(this); + return this; +} + + +/** + * Get request header `field`. + * Case-insensitive. + * + * @param {String} field + * @return {String} + * @api public + */ + +exports.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 + */ + +exports.getHeader = exports.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 + */ + +exports.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 + */ +exports.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 + */ +exports.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 (isObject(name)) { + for (var key in name) { + this.field(key, name[key]); + } + return this; + } + + // val should be defined now + if (null === val || undefined === val) { + throw new Error('.field(name, val) val can not be empty'); + } + this._getFormData().append(name, val); + return this; +}; + +/** + * Abort the request, and clear potential timeout. + * + * @return {Request} + * @api public + */ +exports.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; +}; + +/** + * 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 + */ + +exports.withCredentials = function(){ + // This is browser-only functionality. Node side is no-op. + this._withCredentials = true; + return this; +}; + +/** + * Set the max redirects to `n`. Does noting in browser XHR implementation. + * + * @param {Number} n + * @return {Request} for chaining + * @api public + */ + +exports.redirects = function(n){ + this._maxRedirects = 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 + */ + +exports.toJSON = function(){ + return { + method: this.method, + url: this.url, + data: this._data, + headers: this._header + }; +}; + +/** + * 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 + */ + +exports._isHost = function _isHost(obj) { + var str = {}.toString.call(obj); + + switch (str) { + case '[object File]': + case '[object Blob]': + case '[object FormData]': + return true; + default: + return false; + } +} + +/** + * 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 + */ + +exports.send = function(data){ + var obj = isObject(data); + var type = this._header['content-type']; + + // merge + if (obj && 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 (!obj || this._isHost(data)) return this; + + // default to json + if (!type) this.type('json'); + return this; +}; + +},{"./is-object":8}],10:[function(require,module,exports){ +// The node and browser modules expose versions of this with the +// appropriate constructor function bound as first argument +/** + * 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(RequestConstructor, method, url) { + // callback + if ('function' == typeof url) { + return new RequestConstructor('GET', method).end(url); + } + + // url first + if (2 == arguments.length) { + return new RequestConstructor('GET', method); + } + + return new RequestConstructor(method, url); +} + +module.exports = request; + +},{}],11:[function(require,module,exports){ +'use strict'; + +// TODO Replace by a proper lightweight logging module, suited for the browser + +var enabled = false; +function Logger(id) { + if (id == null) { + id = ''; + } + this.id = id; +} + +Logger.prototype.enable = function() { + this.enabled = true; +}; + +Logger.prototype.debug = function(message) { + if (enabled) { + console.log(this.id + '/debug: ' + message); + } +}; + +Logger.prototype.info = function(message) { + if (enabled) { + console.log(this.id + '/info: ' + message); + } +}; + +Logger.prototype.warn = function(message) { + if (enabled) { + console.log(this.id + '/warn: ' + message); + } +}; + +Logger.prototype.error = function(message) { + if (enabled) { + console.log(this.id + '/error: ' + message); + } +}; + +function minilog(id) { + return new Logger(id); +} + +minilog.enable = function() { + enabled = true; +}; + +module.exports = minilog; + +},{}],12:[function(require,module,exports){ +'use strict'; + +module.exports = { + isArray: function(o) { + if (o == null) { + return false; + } + return Object.prototype.toString.call(o) === '[object Array]'; + } +}; + +},{}],13:[function(require,module,exports){ +'use strict'; + +var superagent = require('superagent'); + +function Request() {} + +Request.prototype.get = function(uri, options, callback) { + return mapRequest(superagent.get(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.post = function(uri, options, callback) { + return mapRequest(superagent.post(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.put = function(uri, options, callback) { + return mapRequest(superagent.put(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.patch = function(uri, options, callback) { + return mapRequest(superagent.patch(uri), options) + .end(handleResponse(callback)); +}; + +Request.prototype.del = function(uri, options, callback) { + return mapRequest(superagent.del(uri), options) + .end(handleResponse(callback)); +}; + +function mapRequest(superagentRequest, options) { + options = options || {}; + mapQuery(superagentRequest, options); + mapHeaders(superagentRequest, options); + mapAuth(superagentRequest, options); + mapBody(superagentRequest, options); + mapForm(superagentRequest, options); + mapWithCredentials(superagentRequest, options); + return superagentRequest; +} + +function mapQuery(superagentRequest, options) { + var qs = options.qs; + if (qs != null) { + superagentRequest = superagentRequest.query(qs); + } +} + +function mapHeaders(superagentRequest, options) { + var headers = options.headers; + if (headers != null) { + superagentRequest = superagentRequest.set(headers); + } +} + +function mapAuth(superagentRequest, options) { + var auth = options.auth; + if (auth != null) { + superagentRequest = superagentRequest.auth( + auth.user || auth.username, + auth.pass || auth.password + ); + } +} + +function mapBody(superagentRequest, options) { + if (options != null) { + var body = options.body; + if (body != null) { + superagentRequest = superagentRequest.send(body); + } + } +} + +function mapForm(superagentRequest, options) { + if (options != null) { + var form = options.form; + if (form != null) { + // content-type header needs to be set before calling send AND it NEEDS + // to be all lower case otherwise superagent automatically sets + // application/json as content-type :-/ + superagentRequest = superagentRequest.set('content-type', + 'application/x-www-form-urlencoded'); + superagentRequest = superagentRequest.send(form); + } + } +} + +function mapWithCredentials(superagentRequest, options) { + if (options != null) { + var withCredentials = options.withCredentials; + if (withCredentials === true) { + // https://visionmedia.github.io/superagent/#cors + superagentRequest.withCredentials(); + } + } +} + +// map XHR response object properties to Node.js request lib's response object +// properties +function mapResponse(response) { + response.body = response.text; + response.statusCode = response.status; + return response; +} + +function handleResponse(callback) { + return function(err, response) { + if (err) { + if (!response) { + // network error or timeout, no response + return callback(err); + } else { + // Since 1.0.0 superagent calls the callback with an error if the status + // code of the response is not in the 2xx range. In this cases, it also + // passes in the response. To align things with request, call the + // callback without the error but just with the response. + callback(null, mapResponse(response)); + } + } else { + callback(null, mapResponse(response)); + } + }; +} + +module.exports = new Request(); + +},{"superagent":7}],14:[function(require,module,exports){ +'use strict'; + +/* + * Copied from underscore.string module. Just the functions we need, to reduce + * the browserified size. + */ + +var _s = { + startsWith: function(str, starts) { + if (starts === '') return true; + if (str == null || starts == null) return false; + str = String(str); starts = String(starts); + return str.length >= starts.length && str.slice(0, starts.length) === starts; + }, + + endsWith: function(str, ends){ + if (ends === '') return true; + if (str == null || ends == null) return false; + str = String(str); ends = String(ends); + return str.length >= ends.length && + str.slice(str.length - ends.length) === ends; + }, + + splice: function(str, i, howmany, substr){ + var arr = _s.chars(str); + arr.splice(~~i, ~~howmany, substr); + return arr.join(''); + }, + + contains: function(str, needle){ + if (needle === '') return true; + if (str == null) return false; + return String(str).indexOf(needle) !== -1; + }, + + chars: function(str) { + if (str == null) return []; + return String(str).split(''); + } +}; + +module.exports = _s; + +},{}],15:[function(require,module,exports){ +'use strict'; + +var resolveUrl = require('resolve-url'); + +exports.resolve = function(from, to) { + return resolveUrl(from, to); +}; + +},{"resolve-url":6}],16:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError + , log = minilog('traverson'); + +exports.abortTraversal = function abortTraversal() { + log.debug('aborting link traversal'); + this.aborted = true; + if (this.currentRequest) { + log.debug('request in progress. trying to abort it, too.'); + this.currentRequest.abort(); + } +}; + +exports.registerAbortListener = function registerAbortListener(t, callback) { + if (t.currentRequest) { + t.currentRequest.on('abort', function() { + exports.callCallbackOnAbort(t); + }); + } +}; + +exports.callCallbackOnAbort = function callCallbackOnAbort(t) { + log.debug('link traversal aborted'); + if (!t.callbackHasBeenCalledAfterAbort) { + t.callbackHasBeenCalledAfterAbort = true; + t.callback(exports.abortError(), t); + } +}; + +exports.abortError = function abortError() { + var error = createError('Link traversal process has been aborted.', + errors.TraversalAbortedError); + error.aborted = true; + return error; +}; + +},{"./errors":19,"minilog":11}],17:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('./abort_traversal') + , applyTransforms = require('./transforms/apply_transforms') + , httpRequests = require('./http_requests') + , isContinuation = require('./is_continuation') + , walker = require('./walker'); + +var checkHttpStatus = require('./transforms/check_http_status') + , continuationToDoc = + require('./transforms/continuation_to_doc') + , continuationToResponse = + require('./transforms/continuation_to_response') + , convertEmbeddedDocToResponse = + require('./transforms/convert_embedded_doc_to_response') + , extractDoc = require('./transforms/extract_doc') + , extractResponse = require('./transforms/extract_response') + , extractUrl = require('./transforms/extract_url') + , fetchLastResource = require('./transforms/fetch_last_resource') + , executeLastHttpRequest = require('./transforms/execute_last_http_request') + , executeHttpRequest = require('./transforms/execute_http_request') + , parse = require('./transforms/parse'); + +/** + * Starts the link traversal process and end it with an HTTP get. + */ +exports.get = function(t, callback) { + var transformsAfterLastStep; + if (t.convertResponseToObject) { + transformsAfterLastStep = [ + continuationToDoc, + fetchLastResource, + checkHttpStatus, + parse, + extractDoc, + ]; + } else { + transformsAfterLastStep = [ + continuationToResponse, + fetchLastResource, + convertEmbeddedDocToResponse, + extractResponse, + ]; + } + walker.walk(t, transformsAfterLastStep, callback); + return createTraversalHandle(t); +}; + +/** + * Special variant of get() that does not execute the last request but instead + * yields the last URL to the callback. + */ +exports.getUrl = function(t, callback) { + walker.walk(t, [ extractUrl ], callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP POST request with the + * given body to the last URL. Passes the HTTP response of the POST request to + * the callback. + */ +exports.post = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.post, + callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP PUT request with the + * given body to the last URL. Passes the HTTP response of the PUT request to + * the callback. + */ +exports.put = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.put, + callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP PATCH request with the + * given body to the last URL. Passes the HTTP response of the PATCH request to + * the callback. + */ +exports.patch = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.patch, + callback); + return createTraversalHandle(t); +}; + +/** + * Starts the link traversal process and sends an HTTP DELETE request to the + * last URL. Passes the HTTP response of the DELETE request to the callback. + */ +exports.delete = function(t, callback) { + walkAndExecute(t, + t.requestModuleInstance, + t.requestModuleInstance.del, + callback); + return createTraversalHandle(t); +}; + +function walkAndExecute(t, request, method, callback) { + var transformsAfterLastStep; + if (t.convertResponseToObject) { + transformsAfterLastStep = [ + executeHttpRequest, + checkHttpStatus, + parse, + extractDoc, + ]; + } else { + transformsAfterLastStep = [ + executeLastHttpRequest, + ]; + } + + t.lastMethod = method; + walker.walk(t, transformsAfterLastStep, callback); +} + +function createTraversalHandle(t) { + return { + abort: t.abortTraversal + }; +} + +},{"./abort_traversal":16,"./http_requests":20,"./is_continuation":21,"./transforms/apply_transforms":27,"./transforms/check_http_status":28,"./transforms/continuation_to_doc":29,"./transforms/continuation_to_response":30,"./transforms/convert_embedded_doc_to_response":31,"./transforms/execute_http_request":33,"./transforms/execute_last_http_request":34,"./transforms/extract_doc":35,"./transforms/extract_response":36,"./transforms/extract_url":37,"./transforms/fetch_last_resource":38,"./transforms/parse":41,"./walker":47,"minilog":11}],18:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , standardRequest = require('request') + , util = require('util'); + +var actions = require('./actions') + , abortTraversal = require('./abort_traversal').abortTraversal + , errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError + , mediaTypeRegistry = require('./media_type_registry') + , mediaTypes = require('./media_types') + , mergeRecursive = require('./merge_recursive'); + +var log = minilog('traverson'); + +// Maintenance notice: The constructor is usually called without arguments, the +// mediaType parameter is only used when cloning the request builder in +// newRequest(). +function Builder(mediaType) { + this.mediaType = mediaType || mediaTypes.CONTENT_NEGOTIATION; + this.adapter = this._createAdapter(this.mediaType); + this.contentNegotiation = true; + this.convertResponseToObjectFlag = false; + this.links = []; + this.jsonParser = JSON.parse; + this.requestModuleInstance = standardRequest; + this.requestOptions = {}; + this.resolveRelativeFlag = false; + this.preferEmbedded = false; + this.lastTraversalState = null; + this.continuation = null; + // Maintenance notice: when extending the list of configuration parameters, + // also extend this.newRequest and initFromTraversalState +} + +Builder.prototype._createAdapter = function(mediaType) { + var AdapterType = mediaTypeRegistry.get(mediaType); + if (!AdapterType) { + throw createError('Unknown or unsupported media type: ' + mediaType, + errors.UnsupportedMediaType); + } + log.debug('creating new ' + AdapterType.name); + return new AdapterType(log); +}; + +/** + * Returns a new builder instance which is basically a clone of this builder + * instance. This allows you to initiate a new request but keeping all the setup + * (start URL, template parameters, request options, body parser, ...). + */ +Builder.prototype.newRequest = function() { + var clonedRequestBuilder = new Builder(this.getMediaType()); + clonedRequestBuilder.contentNegotiation = + this.doesContentNegotiation(); + clonedRequestBuilder.convertResponseToObject(this.convertsResponseToObject()); + clonedRequestBuilder.from(shallowCloneArray(this.getFrom())); + clonedRequestBuilder.withTemplateParameters( + cloneArrayOrObject(this.getTemplateParameters())); + clonedRequestBuilder.withRequestOptions( + cloneArrayOrObject(this.getRequestOptions())); + clonedRequestBuilder.withRequestLibrary(this.getRequestLibrary()); + clonedRequestBuilder.parseResponseBodiesWith(this.getJsonParser()); + clonedRequestBuilder.resolveRelative(this.doesResolveRelative()); + clonedRequestBuilder.preferEmbeddedResources( + this.doesPreferEmbeddedResources()); + clonedRequestBuilder.continuation = this.continuation; + // Maintenance notice: when extending the list of configuration parameters, + // also extend initFromTraversalState + return clonedRequestBuilder; +}; + +/** + * Disables content negotiation and forces the use of a given media type. + * The media type has to be registered at Traverson's media type registry + * before via traverson.registerMediaType (except for media type + * application/json, which is traverson.mediaTypes.JSON). + */ +Builder.prototype.setMediaType = function(mediaType) { + this.mediaType = mediaType || mediaTypes.CONTENT_NEGOTIATION; + this.adapter = this._createAdapter(mediaType); + this.contentNegotiation = + (mediaType === mediaTypes.CONTENT_NEGOTIATION); + return this; +}; + +/** + * Shortcut for + * setMediaType(traverson.mediaTypes.JSON); + */ +Builder.prototype.json = function() { + this.setMediaType(mediaTypes.JSON); + return this; +}; + +/** + * Shortcut for + * setMediaType(traverson.mediaTypes.JSON_HAL); + */ +Builder.prototype.jsonHal = function() { + this.setMediaType(mediaTypes.JSON_HAL); + return this; +}; + +/** + * Enables content negotiation (content negotiation is enabled by default, this + * method can be used to enable it after a call to setMediaType disabled it). + */ +Builder.prototype.useContentNegotiation = function() { + this.setMediaType(mediaTypes.CONTENT_NEGOTIATION); + this.contentNegotiation = true; + return this; +}; + +/** + * Set the root URL of the API, that is, where the link traversal begins. + */ +Builder.prototype.from = function(url) { + this.startUrl = url; + return this; +}; + +/** + * Adds link relations to the list of link relations to follow. The initial list + * of link relations is the empty list. Each link relation in this list + * corresponds to one step in the traversal. + */ +Builder.prototype.follow = function() { + var newLinks = Array.prototype.slice.apply( + arguments.length === 1 && util.isArray(arguments[0]) ? + arguments[0] : arguments + ); + + for (var i = 0; i < newLinks.length; i++) { + if (typeof newLinks[i] === 'string') { + newLinks[i] = { + type: 'link-rel', + value: newLinks[i], + }; + } + } + this.links = this.links.concat(newLinks); + return this; +}; + +/** + * Adds a special step to the list of link relations that will follow the + * location header, that is, instead of reading the next URL from a link in the + * document body, it uses the location header and follows the URL from this + * header. + */ +Builder.prototype.followLocationHeader = function() { + this.links.push({ + type: 'header', + value: 'location', + }); + return this; +}; + +/** + * Alias for follow. + */ +Builder.prototype.walk = Builder.prototype.follow; + +/** + * Provide template parameters for URI template substitution. + */ +Builder.prototype.withTemplateParameters = function(parameters) { + this.templateParameters = parameters; + return this; +}; + +/** + * Provide options for HTTP requests (additional HTTP headers, for example). + * This function resets any request options, that had been set previously, that + * is, multiple calls to withRequestOptions are not cumulative. Use + * addRequestOptions to add request options in a cumulative way. + * + * Options can either be passed as an object or an array. If an object is + * passed, the options will be used for each HTTP request. If an array is + * passed, each element should be an options object and the first array element + * will be used for the first request, the second element for the second request + * and so on. null elements are allowed. + */ +Builder.prototype.withRequestOptions = function(options) { + this.requestOptions = options; + return this; +}; + +/** + * Adds options for HTTP requests (additional HTTP headers, for example) on top + * of existing options, if any. To reset all request options and set new ones + * without keeping the old ones, you can use withRequestOptions. + * + * Options can either be passed as an object or an array. If an object is + * passed, the options will be used for each HTTP request. If an array is + * passed, each element should be an options object and the first array element + * will be used for the first request, the second element for the second request + * and so on. null elements are allowed. + * + * When called after a call to withRequestOptions or when combining multiple + * addRequestOptions calls, some with objects and some with arrays, a multitude + * of interesting situations can occur: + * + * 1) The existing request options are an object and the new options passed into + * this method are also an object. Outcome: Both objects are merged and all + * options are applied to all requests. + * + * 2) The existing options are an array and the new options passed into this + * method are also an array. Outcome: Each array element is merged individually. + * The combined options from the n-th array element in the existing options + * array and the n-th array element in the given array are applied to the n-th + * request. + * + * 3) The existing options are an object and the new options passed into this + * method are an array. Outcome: A new options array will be created. For each + * element, a clone of the existing options object will be merged with an + * element from the given options array. + * + * Note that if the given array has less elements than the number of steps in + * the link traversal (usually the number of steps is derived from the number + * of link relations given to the follow method), only the first n http + * requests will use options at all, where n is the number of elements in the + * given array. HTTP request n + 1 and all following HTTP requests will use an + * empty options object. This is due to the fact, that at the time of creating + * the new options array, we can not know with certainty how many steps the + * link traversal will have. + * + * 4) The existing options are an array and the new options passed into this + * method are an object. Outcome: A clone of the given options object will be + * merged into into each array element of the existing options. + */ +Builder.prototype.addRequestOptions = function(options) { + + // case 2: both the present options and the new options are arrays. + // => merge each array element individually + if (util.isArray(this.requestOptions) && util.isArray(options)) { + mergeArrayElements(this.requestOptions, options); + + // case 3: there is an options object the new options are an array. + // => create a new array, each element is a merge of the existing base object + // and the array element from the new options array. + } else if (typeof this.requestOptions === 'object' && + util.isArray(options)) { + this.requestOptions = + mergeBaseObjectWithArrayElements(this.requestOptions, options); + + // case 4: there is an options array and the new options are an object. + // => merge the new object into each array element. + } else if (util.isArray(this.requestOptions) && + typeof options === 'object') { + mergeOptionObjectIntoEachArrayElement(this.requestOptions, options); + + // case 1: both are objects + // => merge both objects + } else { + mergeRecursive(this.requestOptions, options); + } + return this; +}; + +function mergeArrayElements(existingOptions, newOptions) { + for (var i = 0; + i < Math.max(existingOptions.length, newOptions.length); + i++) { + existingOptions[i] = + mergeRecursive(existingOptions[i], newOptions[i]); + } +} + +function mergeBaseObjectWithArrayElements(existingOptions, newOptions) { + var newOptArray = []; + for (var i = 0; + i < newOptions.length; + i++) { + newOptArray[i] = + mergeRecursive(newOptions[i], existingOptions); + } + return newOptArray; +} + +function mergeOptionObjectIntoEachArrayElement(existingOptions, newOptions) { + for (var i = 0; + i < existingOptions.length; + i++) { + mergeRecursive(existingOptions[i], newOptions); + } +} + +/** + * Injects a custom request library. When using this method, you should not + * call withRequestOptions or addRequestOptions but instead pre-configure the + * injected request library instance before passing it to withRequestLibrary. + */ +Builder.prototype.withRequestLibrary = function(request) { + this.requestModuleInstance = request; + return this; +}; + +/** + * Injects a custom JSON parser. + */ +Builder.prototype.parseResponseBodiesWith = function(parser) { + this.jsonParser = parser; + return this; +}; + +/** + * With this option enabled, the body of the response at the end of the + * traversal will be converted into a JavaScript object (for example by passing + * it into JSON.parse) and passing the resulting object into the callback. + * The default is false, which means the full response is handed to the + * callback. + * + * When response body conversion is enabled, you will not get the full + * response, so you won't have access to the HTTP status code or headers. + * Instead only the converted object will be passed into the callback. + * + * Note that the body of any intermediary responses during the traversal is + * always converted by Traverson (to find the next link). + * + * If the method is called without arguments (or the first argument is undefined + * or null), response body conversion is switched on, otherwise the argument is + * interpreted as a boolean flag. If it is a truthy value, response body + * conversion is switched to on, if it is a falsy value (but not null or + * undefined), response body conversion is switched off. + */ +Builder.prototype.convertResponseToObject = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.convertResponseToObjectFlag = !!flag; + return this; +}; + +/** + * Switches URL resolution to relative (default is absolute) or back to + * absolute. + * + * If the method is called without arguments (or the first argument is undefined + * or null), URL resolution is switched to relative, otherwise the argument is + * interpreted as a boolean flag. If it is a truthy value, URL resolution is + * switched to relative, if it is a falsy value, URL resolution is switched to + * absolute. + */ +Builder.prototype.resolveRelative = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.resolveRelativeFlag = !!flag; + return this; +}; + +/** + * Makes Traverson prefer embedded resources over traversing a link or vice + * versa. This only applies to media types which support embedded resources + * (like HAL). It has no effect when using a media type that does not support + * embedded resources. + * + * It also only takes effect when a resource contains both a link _and_ an + * embedded resource with the name that is to be followed at this step in the + * link traversal process. + * + * If the method is called without arguments (or the first argument is undefined + * or null), embedded resources will be preferred over fetching linked resources + * with an additional HTTP request. Otherwise the argument is interpreted as a + * boolean flag. If it is a truthy value, embedded resources will be preferred, + * if it is a falsy value, traversing the link relation will be preferred. + */ +Builder.prototype.preferEmbeddedResources = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.preferEmbedded = !!flag; + return this; +}; + +/** + * Returns the current media type. If no media type is enforced but content type + * detection is used, the string `content-negotiation` is returned. + */ +Builder.prototype.getMediaType = function() { + return this.mediaType; +}; + +/** + * Returns the URL set by the from(url) method, that is, the root URL of the + * API. + */ +Builder.prototype.getFrom = function() { + return this.startUrl; +}; + +/** + * Returns the template parameters set by the withTemplateParameters. + */ +Builder.prototype.getTemplateParameters = function() { + return this.templateParameters; +}; + +/** + * Returns the request options set by the withRequestOptions or + * addRequestOptions. + */ +Builder.prototype.getRequestOptions = function() { + return this.requestOptions; +}; + +/** + * Returns the custom request library instance set by withRequestLibrary or the + * standard request library instance, if a custom one has not been set. + */ +Builder.prototype.getRequestLibrary = function() { + return this.requestModuleInstance; +}; + +/** + * Returns the custom JSON parser function set by parseResponseBodiesWith or the + * standard parser function, if a custom one has not been set. + */ +Builder.prototype.getJsonParser = function() { + return this.jsonParser; +}; + +/** + * Returns true if the body of the last response will be converted to a + * JavaScript object before passing the result back to the callback. + */ +Builder.prototype.convertsResponseToObject = function() { + return this.convertResponseToObjectFlag; +}; + +/** + * Returns the flag controlling if URLs are resolved relative or absolute. + * A return value of true means that URLs are resolved relative, false means + * absolute. + */ +Builder.prototype.doesResolveRelative = function() { + return this.resolveRelativeFlag; +}; + +/** + * Returns the flag controlling if embedded resources are preferred over links. + * A return value of true means that embedded resources are preferred, false + * means that following links is preferred. + */ +Builder.prototype.doesPreferEmbeddedResources = function() { + return this.preferEmbedded; +}; + +/** + * Returns true if content negotiation is enabled and false if a particular + * media type is forced. + */ +Builder.prototype.doesContentNegotiation = function() { + return this.contentNegotiation; +}; + +/** + * Starts the link traversal process and passes the last HTTP response to the + * callback. + */ +Builder.prototype.get = function get(callback) { + log.debug('initiating traversal (get)'); + var t = createInitialTraversalState(this); + return actions.get(t, wrapForContinue(this, t, callback, 'get')); +}; + +/** + * Special variant of get() that does not yield the full http response to the + * callback but instead the already parsed JSON as an object. + * + * This is a shortcut for builder.convertResponseToObject().get(callback). + */ +Builder.prototype.getResource = function getResource(callback) { + log.debug('initiating traversal (getResource)'); + this.convertResponseToObjectFlag = true; + var t = createInitialTraversalState(this); + return actions.get(t, wrapForContinue(this, t, callback, + 'getResource')); +}; + +/** + * Special variant of get() that does not execute the last request but instead + * yields the last URL to the callback. + */ +Builder.prototype.getUrl = function getUrl(callback) { + log.debug('initiating traversal (getUrl)'); + var t = createInitialTraversalState(this); + return actions.getUrl(t, wrapForContinue(this, t, callback, 'getUrl')); +}; + +/** + * Alias for getUrl. + */ +Builder.prototype.getUri = Builder.prototype.getUrl; + + +/** + * Starts the link traversal process and sends an HTTP POST request with the + * given body to the last URL. Passes the HTTP response of the POST request to + * the callback. + */ +Builder.prototype.post = function post(body, callback) { + log.debug('initiating traversal (post)'); + var t = createInitialTraversalState(this, body); + return actions.post(t, wrapForContinue(this, t, callback, 'post')); +}; + +/** + * Starts the link traversal process and sends an HTTP PUT request with the + * given body to the last URL. Passes the HTTP response of the PUT request to + * the callback. + */ +Builder.prototype.put = function put(body, callback) { + log.debug('initiating traversal (put)'); + var t = createInitialTraversalState(this, body); + return actions.put(t, wrapForContinue(this, t, callback, 'put')); +}; + +/** + * Starts the link traversal process and sends an HTTP PATCH request with the + * given body to the last URL. Passes the HTTP response of the PATCH request to + * the callback. + */ +Builder.prototype.patch = function patch(body, callback) { + log.debug('initiating traversal (patch)'); + var t = createInitialTraversalState(this, body); + return actions.patch(t, wrapForContinue(this, t, callback, 'patch')); +}; + +/** + * Starts the link traversal process and sends an HTTP DELETE request to the + * last URL. Passes the HTTP response of the DELETE request to the callback. + */ +Builder.prototype.delete = function del(callback) { + log.debug('initiating traversal (delete)'); + var t = createInitialTraversalState(this); + return actions.delete(t, wrapForContinue(this, t, callback, 'delete')); +}; + +/** + * Alias for delete. + */ +Builder.prototype.del = Builder.prototype.delete; + +function createInitialTraversalState(self, body) { + + var traversalState = { + aborted: false, + adapter: self.adapter || null, + body: body || null, + callbackHasBeenCalledAfterAbort: false, + contentNegotiation: self.doesContentNegotiation(), + continuation: null, + convertResponseToObject: self.convertsResponseToObject(), + links: self.links, + jsonParser: self.getJsonParser(), + requestModuleInstance: self.getRequestLibrary(), + requestOptions: self.getRequestOptions(), + resolveRelative: self.doesResolveRelative(), + preferEmbedded: self.doesPreferEmbeddedResources(), + startUrl: self.startUrl, + step : { + url: self.startUrl, + index: 0, + }, + templateParameters: self.getTemplateParameters(), + }; + traversalState.abortTraversal = abortTraversal.bind(traversalState); + + if (self.continuation) { + traversalState.continuation = self.continuation; + traversalState.step = self.continuation.step; + self.continuation = null; + } + + return traversalState; +} + +function wrapForContinue(self, t, callback, firstTraversalAction) { + return function(err, result) { + if (err) { return callback(err); } + return callback(null, result, { + continue: function() { + if (!t) { + throw createError('No traversal state to continue from.', + errors.InvalidStateError); + } + + log.debug('> continuing finished traversal process'); + self.continuation = { + step: t.step, + action: firstTraversalAction, + }; + self.continuation.step.index = 0; + initFromTraversalState(self, t); + return self; + }, + }); + }; +} + +/* + * Copy configuration from traversal state to builder instance to + * prepare for next traversal process. + */ +function initFromTraversalState(self, t) { + self.aborted = false; + self.adapter = t.adapter; + self.body = t.body; + self.callbackHasBeenCalledAfterAbort = false; + self.contentNegotiation = t.contentNegotiation; + self.convertResponseToObjectFlag = t.convertResponseToObject; + self.links = []; + self.jsonParser = t.jsonParser; + self.requestModuleInstance = t.requestModuleInstance, + self.requestOptions = t.requestOptions, + self.resolveRelativeFlag = t.resolveRelative; + self.preferEmbedded = t.preferEmbedded; + self.startUrl = t.startUrl; + self.templateParameters = t.templateParameters; +} + +function cloneArrayOrObject(thing) { + if (util.isArray(thing)) { + return shallowCloneArray(thing); + } else if (typeof thing === 'object') { + return deepCloneObject(thing); + } else { + return thing; + } +} + +function deepCloneObject(object) { + return mergeRecursive(null, object); +} + +function shallowCloneArray(array) { + if (!array) { + return array; + } + return array.slice(0); +} + +module.exports = Builder; + +},{"./abort_traversal":16,"./actions":17,"./errors":19,"./media_type_registry":23,"./media_types":24,"./merge_recursive":25,"minilog":11,"request":13,"util":12}],19:[function(require,module,exports){ +'use strict'; + +module.exports = { + errors: { + HTTPError: 'HTTPError', + InvalidArgumentError: 'InvalidArgumentError', + InvalidStateError: 'InvalidStateError', + JSONError: 'JSONError', + JSONPathError: 'JSONPathError', + LinkError: 'LinkError', + TraversalAbortedError: 'TraversalAbortedError', + UnsupportedMediaType: 'UnsupportedMediaTypeError', + }, + + createError: function(message, name, data) { + var error = new Error(message); + error.name = name; + if (data) { + error.data = data; + } + return error; + }, + +}; + +},{}],20:[function(require,module,exports){ +(function (process){ +'use strict'; +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('./abort_traversal') + , detectContentType = require('./transforms/detect_content_type') + , errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError + , getOptionsForStep = require('./transforms/get_options_for_step'); + +/** + * Executes a HTTP GET request during the link traversal process. + */ +// This method is currently used for all intermediate GET requests during the +// link traversal process. Coincidentally, it is also used for the final request +// in a link traversal should this happen to be a GET request. Otherwise (POST/ +// PUT/PATCH/DELETE), Traverson uses exectueHttpRequest. +exports.fetchResource = function fetchResource(t, callback) { + log.debug('fetching resource for next step'); + if (t.step.url) { + log.debug('fetching resource from ', t.step.url); + return executeHttpGet(t, callback); + } else if (t.step.doc) { + // The step already has an attached result document, so all is fine and we + // can call the callback immediately + log.debug('resource for next step has already been fetched, using ' + + 'embedded'); + return process.nextTick(function() { + callback(null, t); + }); + } else { + return process.nextTick(function() { + var error = createError('Can not process step.', + errors.InvalidStateError); + error.step = t.step; + callback(error, t); + }); + } +}; + +function executeHttpGet(t, callback) { + var options = getOptionsForStep(t); + log.debug('HTTP GET request to ', t.step.url); + log.debug('options ', options); + t.currentRequest = + t.requestModuleInstance.get(t.step.url, options, + function(err, response, body) { + log.debug('HTTP GET request to ' + t.step.url + ' returned'); + t.currentRequest = null; + + // workaround for cases where response body is empty but body comes in as + // the third argument + if (body && !response.body) { + response.body = body; + } + t.step.response = response; + + if (err) { + return callback(err, t); + } + log.debug('request to ' + t.step.url + ' finished without error (' + + response.statusCode + ')'); + + if (!detectContentType(t, callback)) return; + + return callback(null, t); + }); + abortTraversal.registerAbortListener(t, callback); +} + +/** + * Executes an arbitrary HTTP request. + */ +// This method is currently used for POST/PUT/PATCH/DELETE at the end of a link +// traversal process. If the link traversal process requires a GET as the last +// request, Traverson uses exectueHttpGet. +exports.executeHttpRequest = function(t, request, method, callback) { + var requestOptions = getOptionsForStep(t); + if (t.body) { + requestOptions.body = requestOptions.jsonReplacer ? + t.body : JSON.stringify(t.body); + } + + log.debug('HTTP ' + method.name + ' request to ', t.step.url); + log.debug('options ', requestOptions); + t.currentRequest = + method.call(request, t.step.url, requestOptions, + function(err, response, body) { + log.debug('HTTP ' + method.name + ' request to ' + t.step.url + + ' returned'); + t.currentRequest = null; + + // workaround for cases where response body is empty but body comes in as + // the third argument + if (body && !response.body) { + response.body = body; + } + t.step.response = response; + + if (err) { + return callback(err); + } + + return callback(null, response); + }); + abortTraversal.registerAbortListener(t, callback); +}; + +}).call(this,require('_process')) +},{"./abort_traversal":16,"./errors":19,"./transforms/detect_content_type":32,"./transforms/get_options_for_step":40,"_process":5,"minilog":11}],21:[function(require,module,exports){ +'use strict'; + +module.exports = function isContinuation(t) { + return t.continuation && t.step && t.step.response; +}; + +},{}],22:[function(require,module,exports){ +'use strict'; + +var jsonpath = require('jsonpath-plus') + , minilog = require('minilog') + , _s = require('underscore.string'); + +var errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +function JsonAdapter(log) { + this.log = log; +} + +JsonAdapter.prototype.findNextStep = function(t, link) { + validateLinkObject(link); + var doc = t.lastStep.doc; + this.log.debug('resolving link', link); + switch (link.type) { + case 'link-rel': + return this._handleLinkRel(doc, link); + case 'header': + return this._handleHeader(t.lastStep.response, link); + default: + throw createError('Link objects with type ' + link.type + ' are not ' + + 'supported by this adapter.', errors.InvalidArgumentError, link); + } +}; + +JsonAdapter.prototype._handleLinkRel = function(doc, link) { + var linkRel = link.value; + this.log.debug('looking for link-rel in doc', linkRel, doc); + var url; + if (this._testJSONPath(linkRel)) { + return { url: this._resolveJSONPath(doc, linkRel) }; + } else if (doc[linkRel]) { + return { url : doc[linkRel] }; + } else { + throw createError('Could not find property ' + linkRel + + ' in document.', errors.LinkError, doc); + } +}; + +function validateLinkObject(link) { + if (typeof link === 'undefined' || link === null) { + throw createError('Link object is null or undefined.', + errors.InvalidArgumentError); + } + if (typeof link !== 'object') { + throw createError('Links must be objects, not ' + typeof link + + '.', errors.InvalidArgumentError, link); + } + if (!link.type) { + throw createError('Link objects has no type attribute.', + errors.InvalidArgumentError, link); + } +} + +JsonAdapter.prototype._testJSONPath = function(link) { + return _s.startsWith(link, '$.') || _s.startsWith(link, '$['); +}; + +JsonAdapter.prototype._resolveJSONPath = function(doc, link) { + var matches = jsonpath({ + json: doc, + path: link, + }); + if (matches.length === 1) { + var url = matches[0]; + if (!url) { + throw createError('JSONPath expression ' + link + + ' was resolved but the result was null, undefined or an empty' + + ' string in document:\n' + JSON.stringify(doc), + errors.JSONPathError, doc); + } + if (typeof url !== 'string') { + throw createError('JSONPath expression ' + link + + ' was resolved but the result is not a property of type string. ' + + 'Instead it has type "' + (typeof url) + + '" in document:\n' + JSON.stringify(doc), errors.JSONPathError, + doc); + } + return url; + } else if (matches.length > 1) { + // ambigious match + throw createError('JSONPath expression ' + link + + ' returned more than one match in document:\n' + + JSON.stringify(doc), errors.JSONPathError, doc); + } else { + // no match at all + throw createError('JSONPath expression ' + link + + ' returned no match in document:\n' + JSON.stringify(doc), + errors.JSONPathError, doc); + } +}; + +JsonAdapter.prototype._handleHeader = function(httpResponse, link) { + switch (link.value) { + case 'location': + var locationHeader = httpResponse.headers.location; + if (!locationHeader) { + throw createError('Following the location header but there was no ' + + 'location header in the last response.', errors.LinkError, + httpResponse.headers); + } + return { url : locationHeader }; + default: + throw createError('Link objects with type header and value ' + + link.value + ' are not supported by this adapter.', + errors.InvalidArgumentError, link); + } +}; + +module.exports = JsonAdapter; + +},{"./errors":19,"jsonpath-plus":4,"minilog":11,"underscore.string":14}],23:[function(require,module,exports){ +'use strict'; + +var mediaTypes = require('./media_types'); + +var registry = {}; + +exports.register = function register(contentType, constructor) { + registry[contentType] = constructor; +}; + +exports.get = function get(contentType) { + return registry[contentType]; +}; + +exports.register(mediaTypes.CONTENT_NEGOTIATION, + require('./negotiation_adapter')); +exports.register(mediaTypes.JSON, require('./json_adapter')); + +},{"./json_adapter":22,"./media_types":24,"./negotiation_adapter":26}],24:[function(require,module,exports){ +'use strict'; + +module.exports = { + CONTENT_NEGOTIATION: 'content-negotiation', + JSON: 'application/json', + JSON_HAL: 'application/hal+json', +}; + +},{}],25:[function(require,module,exports){ +'use strict'; + +// TODO Maybe replace with https://github.com/Raynos/xtend +// check browser build size, though. +function mergeRecursive(obj1, obj2) { + if (!obj1 && obj2) { + obj1 = {}; + } + for (var key in obj2) { + if (!obj2.hasOwnProperty(key)) { + continue; + } + merge(obj1, obj2, key); + } + return obj1; +} + +function merge(obj1, obj2, key) { + if (typeof obj2[key] === 'object') { + // if it is an object (that is, a non-leave in the tree), + // and it is not present in obj1 + if (!obj1[key] || typeof obj1[key] !== 'object') { + // ... we create an empty object in obj1 + obj1[key] = {}; + } + // and we recurse deeper into the structure + mergeRecursive(obj1[key], obj2[key]); + } else { + // if it is primitive (string, number, boolean) or a function, we overwrite/ + // add it to obj1 + obj1[key] = obj2[key]; + } +} + +module.exports = mergeRecursive; + +},{}],26:[function(require,module,exports){ +'use strict'; + +var errorModule = require('./errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +function NegotiationAdapter(log) {} + +NegotiationAdapter.prototype.findNextStep = function(doc, link) { + throw createError('Content negotiation did not happen', + errors.InvalidStateError); +}; + +module.exports = NegotiationAdapter; + +},{"./errors":19}],27:[function(require,module,exports){ +(function (process){ +/* jshint loopfunc: true */ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +/* + * Applies async and sync transforms, one after another. + */ +function applyTransforms(transforms, t, callback) { + log.debug('applying', transforms.length, 'transforms'); + for (var i = 0; i < transforms.length; i++) { + var transform = transforms[i]; + log.debug('next transform', transform); + if (transform.isAsync) { + // asynchronous case + return transform(t, function(t) { + // this is only called when the async transform was successful, + // otherwise t.callback has already been called with an error. + applyTransforms(transforms.slice(i + 1), t, callback); + }); + } else { + // synchronous case + var result = transform(t); + if (!result) { + log.debug('transform has failed or was a final transform'); + // stop processing t.callback has already been called + return; + } + } + } + log.debug('all transformations done, starting next step'); + return process.nextTick(function() { + callback(t); + }); +} + +module.exports = applyTransforms; + +}).call(this,require('_process')) +},{"_process":5,"minilog":11}],28:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError + , isContinuation = require('../is_continuation'); + +module.exports = function checkHttpStatus(t) { + // this step is ommitted for continuations + if (isContinuation(t)) { + return true; + } + + log.debug('checking http status'); + if (!t.step.response && t.step.doc) { + // Last step probably did not execute a HTTP request but used an embedded + // document. + log.debug('found embedded document, assuming no HTTP request has been ' + + 'made'); + return true; + } + + // Only process response if http status was in 200 - 299 range. + // The request module follows redirects for GET requests all by itself, so + // we should not have to handle them here. If a 3xx http status get's here + // something went wrong. 4xx and 5xx of course also indicate an error + // condition. 1xx should not occur. + var httpStatus = t.step.response.statusCode; + if (httpStatus && (httpStatus < 200 || httpStatus >= 300)) { + var error = httpError(t.step.url, httpStatus, t.step.response.body); + log.error('unexpected http status code'); + log.error(error); + t.callback(error); + return false; + } + log.debug('http status code ok (' + httpStatus + ')'); + return true; +}; + +function httpError(url, httpStatus, body) { + var error = createError('HTTP GET for ' + url + + ' resulted in HTTP status code ' + httpStatus + '.', errors.HTTPError); + error.url = url; + error.httpStatus = httpStatus; + error.body = body; + try { + error.doc = JSON.parse(body); + } catch (e) { + // ignore + } + return error; +} + +},{"../errors":19,"../is_continuation":21,"minilog":11}],29:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , isContinuation = require('../is_continuation'); + +/* + * This transform covers the case of a follow() call *without any links* after + * a continue(). Actually, there is nothing to do here since we should have + * fetched everything last time. + */ +module.exports = function continuationToDoc(t) { + if (isContinuation(t)) { + log.debug('continuing from last traversal process (actions)'); + t.continuation = null; + t.callback(null, t.step.doc); + return false; + } + return true; +}; + +},{"../is_continuation":21,"minilog":11}],30:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , convertEmbeddedDocToResponse = + require('./convert_embedded_doc_to_response') + , isContinuation = require('../is_continuation'); + +/* + * follow() call without links after continue(). Actually, there is nothing + * to do here since we should have fetched everything last time. + */ +module.exports = function continuationToResponse(t) { + if (isContinuation(t)) { + log.debug('continuing from last traversal process (actions)'); + t.continuation = null; + // Hm, a transform using another transform. This feels a bit fishy. + convertEmbeddedDocToResponse(t); + t.callback(null, t.step.response); + return false; + } + return true; +}; + +},{"../is_continuation":21,"./convert_embedded_doc_to_response":31,"minilog":11}],31:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +module.exports = function convertEmbeddedDocToResponse(t) { + if (!t.step.response && t.step.doc) { + log.debug('faking HTTP response for embedded resource'); + t.step.response = { + statusCode: 200, + body: JSON.stringify(t.step.doc), + remark: 'This is not an actual HTTP response. The resource you ' + + 'requested was an embedded resource, so no HTTP request was ' + + 'made to acquire it.' + }; + } + return true; +}; + +},{"minilog":11}],32:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +var mediaTypeRegistry = require('../media_type_registry') + , errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +module.exports = function detectContentType(t, callback) { + if (t.contentNegotiation && + t.step.response && + t.step.response.headers && + t.step.response.headers['content-type']) { + var contentType = t.step.response.headers['content-type'].split(/[; ]/)[0]; + var AdapterType = mediaTypeRegistry.get(contentType); + if (!AdapterType) { + callback(createError('Unknown content type for content ' + + 'type detection: ' + contentType, + errors.UnsupportedMediaType)); + return false; + } + // switch to new Adapter depending on Content-Type header of server + t.adapter = new AdapterType(log); + } + return true; +}; + +},{"../errors":19,"../media_type_registry":23,"minilog":11}],33:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , httpRequests = require('../http_requests'); + +/* + * Execute the last HTTP request in a traversal that ends in + * post/put/patch/delete, but do not call t.callback immediately + * (because we still need to do response body to object conversion + * afterwards, for example) + */ +// TODO Why is this different from when do a GET? +// Probably only because the HTTP method is configurable here (with +// t.lastMethod), we might be able to unify this with the +// fetch_resource/fetch_last_resource transform. +function executeLastHttpRequest(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + // only diff to execute_last_http_request: pass a new callback function + // instead of t.callback. + httpRequests.executeHttpRequest( + t, t.requestModuleInstance, t.lastMethod, function(err, response) { + if (err) { + if (!err.aborted) { + log.debug('error while processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +executeLastHttpRequest.isAsync = true; + +module.exports = executeLastHttpRequest; + +},{"../abort_traversal":16,"../http_requests":20,"minilog":11}],34:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , httpRequests = require('../http_requests'); + +/* + * Execute the last http request in a traversal that ends in + * post/put/patch/delete. + */ +// TODO Why is this different from when do a GET at the end of the traversal? +// Probably only because the HTTP method is configurable here (with +// t.lastMethod), we might be able to unify this with the +// fetch_resource/fetch_last_resource transform. +function executeLastHttpRequest(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + httpRequests.executeHttpRequest( + t, t.requestModuleInstance, t.lastMethod, t.callback); +} + +executeLastHttpRequest.isAsync = true; + +module.exports = executeLastHttpRequest; + +},{"../abort_traversal":16,"../http_requests":20,"minilog":11}],35:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +/* + * This transform is meant to be run at the very end of a getResource call. It + * just extracts the last doc from the step and calls t.callback with it. + */ +module.exports = function extractDoc(t) { + log.debug('walker.walk has finished'); + /* + TODO Breaks a lot of tests although it seems to make perfect sense?!? + if (!t.doc) { + t.callback(createError('No document available', errors.InvalidStateError)); + return false; + } + */ + + t.callback(null, t.step.doc); + + // This is a so called final transform that is only applied at the very end + // and it always calls t.callback - in contrast to other transforms it does + // not call t.callback in the error case, but as a success. + // We return false to make sure processing ends here. + return false; +}; + +},{"minilog":11}],36:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +/* + * This transform is meant to be run at the very end of a get/post/put/patch/ + * delete call. It just extracts the last response from the step and calls + * t.callback with it. + */ +module.exports = function extractDoc(t) { + log.debug('walker.walk has finished'); + /* + TODO Breaks a lot of tests although it seems to make perfect sense?!? + if (!t.response) { + t.callback(createError('No response available', errors.InvalidStateError)); + return false; + } + */ + + t.callback(null, t.step.response); + + // This is a so called final transform that is only applied at the very end + // and it always calls t.callback - in contrast to other transforms it does + // not call t.callback in the error case, but as a success. + // We return false to make sure processing ends here. + return false; +}; + +},{"minilog":11}],37:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , url = require('url'); + +var errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError; + +/* + * This transform is meant to be run at the very end of a get/post/put/patch/ + * delete call. It just extracts the last accessed url from the step and calls + * t.callback with it. + */ +module.exports = function extractDoc(t) { + log.debug('walker.walk has finished'); + if (t.step.url) { + return t.callback(null, t.step.url); + } else if (t.step.doc && + // TODO actually this is very HAL specific :-/ + t.step.doc._links && + t.step.doc._links.self && + t.step.doc._links.self.href) { + return t.callback( + null, url.resolve(t.startUrl, t.step.doc._links.self.href)); + } else { + return t.callback(createError('You requested an URL but the last ' + + 'resource is an embedded resource and has no URL of its own ' + + '(that is, it has no link with rel=\"self\"', errors.LinkError)); + } +}; + +},{"../errors":19,"minilog":11,"url":15}],38:[function(require,module,exports){ +'use strict'; + +// TODO Only difference to lib/transform/fetch_resource is the continuation +// checking, which is missing here. Maybe we can delete this transform and use +// fetch_resource in its place everywhere? + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , httpRequests = require('../http_requests'); + +/* + * Execute the last step in a traversal that ends with an HTTP GET. + */ +// This is similar to lib/transforms/fetch_resource.js - refactoring potential? +function fetchLastResource(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + httpRequests.fetchResource(t, function(err, t) { + log.debug('fetchResource returned (fetchLastResource).'); + if (err) { + if (!err.aborted) { + log.debug('error while processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +fetchLastResource.isAsync = true; + +module.exports = fetchLastResource; + +},{"../abort_traversal":16,"../http_requests":20,"minilog":11}],39:[function(require,module,exports){ +(function (process){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('../abort_traversal') + , isContinuation = require('../is_continuation') + , httpRequests = require('../http_requests'); + +/* + * Execute the next step in the traversal. In most cases that is an HTTP get to + *the next URL. + */ + +function fetchResource(t, callback) { + if (isContinuation(t)) { + convertContinuation(t, callback); + } else { + fetchViaHttp(t, callback); + } +} + +fetchResource.isAsync = true; + +/* + * This is a continuation of an earlier traversal process. + * We need to shortcut to the next step (without executing the final HTTP + * request of the last traversal again. + */ +function convertContinuation(t, callback) { + log.debug('continuing from last traversal process (walker)'); + process.nextTick(function() { // de-zalgo continuations + callback(t); + }); +} + +function fetchViaHttp(t, callback) { + // always check for aborted before doing an HTTP request + if (t.aborted) { + return abortTraversal.callCallbackOnAbort(t); + } + httpRequests.fetchResource(t, function(err, t) { + log.debug('fetchResource returned'); + if (err) { + if (!err.aborted) { + log.debug('error while processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +module.exports = fetchResource; + +}).call(this,require('_process')) +},{"../abort_traversal":16,"../http_requests":20,"../is_continuation":21,"_process":5,"minilog":11}],40:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , util = require('util'); + +module.exports = function getOptionsForStep(t) { + var options = t.requestOptions; + if (util.isArray(t.requestOptions)) { + options = t.requestOptions[t.step.index] || {}; + } + log.debug('options: ', options); + return options; +}; + +},{"minilog":11,"util":12}],41:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +var errorModule = require('../errors') + , errors = errorModule.errors + , createError = errorModule.createError + , isContinuation = require('../is_continuation'); + + +module.exports = function parse(t) { + // TODO Duplicated in actions#afterGetResource etc. + // this step is ommitted for continuations that parse at the end + if (isContinuation(t)) { + log.debug('continuing from last traversal process (transforms/parse)'); + // if last traversal did a parse at the end we do not need to parse again + // (this condition will need to change with + // https://github.com/basti1302/traverson/issues/44) + if (t.continuation.action === 'getResource') { + return true; + } + } + if (t.step.doc) { + // Last step probably did not execute a HTTP request but used an embedded + // document. + log.debug('no parsing necessary, probably an embedded document'); + return true; + } + + try { + parseBody(t); + return true; + } catch (e) { + handleError(t, e); + return false; + } +}; + +function parseBody(t) { + log.debug('parsing response body'); + if (t.requestOptions && t.requestOptions.jsonReviver) { + t.step.doc = t.step.response.body; + } else { + t.step.doc = t.jsonParser(t.step.response.body); + } +} + +function handleError(t, e) { + var error = e; + if (e.name === 'SyntaxError') { + error = jsonError(t.step.url, t.step.response.body); + } + log.error('parsing failed'); + log.error(error); + t.callback(error); +} + +function jsonError(url, body) { + var error = createError('The document at ' + url + + ' could not be parsed as JSON: ' + body, errors.JSONError, body); + error.url = url; + error.body = body; + return error; +} + +},{"../errors":19,"../is_continuation":21,"minilog":11}],42:[function(require,module,exports){ +'use strict'; + +var isContinuation = require('../is_continuation'); + +module.exports = function resetLastStep(t) { + // this step is ommitted for continuations + if (isContinuation(t)) { + return true; + } + + t.continuation = null; + return true; +}; + +},{"../is_continuation":21}],43:[function(require,module,exports){ +'use strict'; + +var isContinuation = require('../is_continuation'); + +module.exports = function resetLastStep(t) { + // this step is ommitted for continuations + if (isContinuation(t)) { + return true; + } + + t.lastStep = null; + return true; +}; + +},{"../is_continuation":21}],44:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , _s = require('underscore.string') + , url = require('url'); + +var protocolRegEx = /https?:\/\//i; + +module.exports = function resolveNextUrl(t) { + if (t.step.url) { + if (t.step.url.search(protocolRegEx) !== 0) { + log.debug('found non full qualified URL'); + if (t.resolveRelative && t.lastStep && t.lastStep.url) { + // edge case: resolve URL relatively (only when requested by client) + log.debug('resolving URL relative'); + if (_s.startsWith(t.step.url, '/') && + _s.endsWith(t.lastStep.url, '/')) { + t.step.url = _s.splice(t.step.url, 0, 1); + } + t.step.url = t.lastStep.url + t.step.url; + } else { + // This is the default case and what happens most likely (not a full + // qualified URL, not resolving relatively) and we simply use Node's url + // module (or the appropriate shim) here. + t.step.url = url.resolve(t.startUrl, t.step.url); + } + } // edge case: full qualified URL -> no URL resolving necessary + } // no t.step.url -> no URL resolving (step might contain an embedded doc) + return true; +}; + +},{"minilog":11,"underscore.string":14,"url":15}],45:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , _s = require('underscore.string') + , uriTemplate = require('url-template') + , util = require('util'); + +module.exports = function resolveUriTemplate(t) { + if (t.step.url) { + // next link found in last response, might be a URI template + var templateParams = t.templateParameters; + if (util.isArray(templateParams)) { + // if template params were given as an array, only use the array element + // for the current index for URI template resolving. + templateParams = templateParams[t.step.index]; + } + templateParams = templateParams || {}; + + if (_s.contains(t.step.url, '{')) { + log.debug('resolving URI template'); + var template = uriTemplate.parse(t.step.url); + var resolved = template.expand(templateParams); + log.debug('resolved to ', resolved); + t.step.url = resolved; + } + } + return true; +}; + + + +},{"minilog":11,"underscore.string":14,"url-template":49,"util":12}],46:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +module.exports = function switchToNextStep(t) { + // extract next link to follow from last response + var link = t.links[t.step.index]; + log.debug('next link: ' + link); + + // save last step before overwriting it with the next step (required for + // relative URL resolution, where we need the last URL) + t.lastStep = t.step; + + t.step = findNextStep(t, link); + if (!t.step) return false; + + log.debug('found next step', t.step); + + // backward compatibility fix for media type plug-ins using step.uri instead + // of step.url (until 1.0.0) + t.step.url = t.step.url || t.step.uri; + + t.step.index = t.lastStep.index + 1; + return true; +}; + +function findNextStep(t, link) { + try { + return t.adapter.findNextStep(t, link); + } catch (e) { + log.error('could not find next step'); + log.error(e); + t.callback(e); + return null; + } +} + +},{"minilog":11}],47:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , abortTraversal = require('./abort_traversal') + , applyTransforms = require('./transforms/apply_transforms') + , isContinuation = require('./is_continuation') + , resolveUriTemplate = require('./transforms/resolve_uri_template'); + +var transforms = [ + require('./transforms/fetch_resource'), + require('./transforms/reset_last_step'), + // check HTTP status code + require('./transforms/check_http_status'), + // parse JSON from last response + require('./transforms/parse'), + // retrieve next link and switch to next step + require('./transforms/switch_to_next_step'), + // URI template has to be resolved before post processing the URL, + // because we do url.resolve with it (in json_hal) and this would URL- + // encode curly braces. + resolveUriTemplate, + require('./transforms/resolve_next_url'), + require('./transforms/reset_continuation'), +]; + +/** + * Walks from resource to resource along the path given by the link relations + * from this.links until it has reached the last URL. On reaching this, it calls + * the given callback with the last resulting step. + */ +exports.walk = function(t, transformsAfterLastStep, callback) { + // even the root URL might be a template, so we apply the resolveUriTemplate + // once before starting the walk. + if (!resolveUriTemplate(t)) return; + + // starts the link rel walking process + log.debug('starting to follow links'); + transformsAfterLastStep = transformsAfterLastStep || []; + t.callback = callback; + processStep(t, transformsAfterLastStep); +}; + +function processStep(t, transformsAfterLastStep) { + log.debug('processing next step'); + if (moreLinksToFollow(t) && !isAborted(t)) { + applyTransforms(transforms, t, function(t) { + log.debug('successfully processed step'); + // call processStep recursively again to follow next link + processStep(t, transformsAfterLastStep); + }); + } else if (isAborted(t)) { + return abortTraversal.callCallbackOnAbort(t); + } else { + // link array is exhausted, we are done and return the last response + // and URL to the callback the client passed into the walk method. + log.debug('link array exhausted'); + + applyTransforms(transformsAfterLastStep, t, function(t) { + // This is pretty ugly. This code implies, that we call t.callback from + // here, but actually we usually call it from lib/transforms/extract_doc + // or lib/transforms/extract_response which then return false to terminate + // the processing. + return t.callback(); + }); + } +} + +function moreLinksToFollow(t) { + return t.step.index < t.links.length; +} + +function isAborted(t) { + return t.aborted; +} + +},{"./abort_traversal":16,"./is_continuation":21,"./transforms/apply_transforms":27,"./transforms/check_http_status":28,"./transforms/fetch_resource":39,"./transforms/parse":41,"./transforms/reset_continuation":42,"./transforms/reset_last_step":43,"./transforms/resolve_next_url":44,"./transforms/resolve_uri_template":45,"./transforms/switch_to_next_step":46,"minilog":11}],48:[function(require,module,exports){ +(function (process){ +'use strict'; + +var minilog = require('minilog') + , errorModule = require('./lib/errors') + , errors = errorModule.errors + , createError = errorModule.createError + , mediaTypes = require('./lib/media_types') + , Builder = require('./lib/builder') + , mediaTypeRegistry = require('./lib/media_type_registry'); + +// activate this line to enable logging +if (process.env.TRAVERSON_LOGGING) { + require('minilog').enable(); +} + +// export builder for traverson-angular +exports._Builder = Builder; + +/** + * Creates a new request builder instance. + */ +exports.newRequest = function newRequest() { + return new Builder(); +}; + +/** + * Creates a new request builder instance with the given root URL. + */ +exports.from = function from(url) { + var builder = new Builder(); + builder.from(url); + return builder; +}; + +// Provided for backward compatibility with pre-1.0.0 versions. +// The preferred way is to use newRequest() or from() to create a request +// builder and either set the media type explicitly by calling json() on the +// request builder instance - or use content negotiation. +exports.json = { + from: function(url) { + var builder = new Builder(); + builder.from(url); + builder.setMediaType(mediaTypes.JSON); + return builder; + } +}, + +// Provided for backward compatibility with pre-1.0.0 versions. +// The preferred way is to use newRequest() or from() to create a request +// builder and then either set the media type explicitly by calling jsonHal() on +// the request builder instance - or use content negotiation. +exports.jsonHal = { + from: function(url) { + if (!mediaTypeRegistry.get(mediaTypes.JSON_HAL)) { + throw createError('JSON HAL adapter is not registered. From version ' + + '1.0.0 on, Traverson has no longer built-in support for ' + + 'application/hal+json. HAL support was moved to a separate, optional ' + + 'plug-in. See https://github.com/basti1302/traverson-hal', + errors.UnsupportedMediaType + ); + } + var builder = new Builder(); + builder.from(url); + builder.setMediaType(mediaTypes.JSON_HAL); + return builder; + } +}; + +// expose media type registry so that media type plug-ins can register +// themselves +exports.registerMediaType = mediaTypeRegistry.register; + +// re-export media type constants +exports.mediaTypes = mediaTypes; + +// re-export error names +exports.errors = errors; + +}).call(this,require('_process')) +},{"./lib/builder":18,"./lib/errors":19,"./lib/media_type_registry":23,"./lib/media_types":24,"_process":5,"minilog":11}],49:[function(require,module,exports){ +(function (root, factory) { + if (typeof exports === 'object') { + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + define([], factory); + } else { + root.urltemplate = factory(); + } +}(this, function () { + /** + * @constructor + */ + function UrlTemplate() { + } + + /** + * @private + * @param {string} str + * @return {string} + */ + UrlTemplate.prototype.encodeReserved = function (str) { + return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) { + if (!/%[0-9A-Fa-f]/.test(part)) { + part = encodeURI(part).replace(/%5B/g, '[').replace(/%5D/g, ']'); + } + return part; + }).join(''); + }; + + /** + * @private + * @param {string} str + * @return {string} + */ + UrlTemplate.prototype.encodeUnreserved = function (str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { + return '%' + c.charCodeAt(0).toString(16).toUpperCase(); + }); + } + + /** + * @private + * @param {string} operator + * @param {string} value + * @param {string} key + * @return {string} + */ + UrlTemplate.prototype.encodeValue = function (operator, value, key) { + value = (operator === '+' || operator === '#') ? this.encodeReserved(value) : this.encodeUnreserved(value); + + if (key) { + return this.encodeUnreserved(key) + '=' + value; + } else { + return value; + } + }; + + /** + * @private + * @param {*} value + * @return {boolean} + */ + UrlTemplate.prototype.isDefined = function (value) { + return value !== undefined && value !== null; + }; + + /** + * @private + * @param {string} + * @return {boolean} + */ + UrlTemplate.prototype.isKeyOperator = function (operator) { + return operator === ';' || operator === '&' || operator === '?'; + }; + + /** + * @private + * @param {Object} context + * @param {string} operator + * @param {string} key + * @param {string} modifier + */ + UrlTemplate.prototype.getValues = function (context, operator, key, modifier) { + var value = context[key], + result = []; + + if (this.isDefined(value) && value !== '') { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + value = value.toString(); + + if (modifier && modifier !== '*') { + value = value.substring(0, parseInt(modifier, 10)); + } + + result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); + } else { + if (modifier === '*') { + if (Array.isArray(value)) { + value.filter(this.isDefined).forEach(function (value) { + result.push(this.encodeValue(operator, value, this.isKeyOperator(operator) ? key : null)); + }, this); + } else { + Object.keys(value).forEach(function (k) { + if (this.isDefined(value[k])) { + result.push(this.encodeValue(operator, value[k], k)); + } + }, this); + } + } else { + var tmp = []; + + if (Array.isArray(value)) { + value.filter(this.isDefined).forEach(function (value) { + tmp.push(this.encodeValue(operator, value)); + }, this); + } else { + Object.keys(value).forEach(function (k) { + if (this.isDefined(value[k])) { + tmp.push(this.encodeUnreserved(k)); + tmp.push(this.encodeValue(operator, value[k].toString())); + } + }, this); + } + + if (this.isKeyOperator(operator)) { + result.push(this.encodeUnreserved(key) + '=' + tmp.join(',')); + } else if (tmp.length !== 0) { + result.push(tmp.join(',')); + } + } + } + } else { + if (operator === ';') { + if (this.isDefined(value)) { + result.push(this.encodeUnreserved(key)); + } + } else if (value === '' && (operator === '&' || operator === '?')) { + result.push(this.encodeUnreserved(key) + '='); + } else if (value === '') { + result.push(''); + } + } + return result; + }; + + /** + * @param {string} template + * @return {function(Object):string} + */ + UrlTemplate.prototype.parse = function (template) { + var that = this; + var operators = ['+', '#', '.', '/', ';', '?', '&']; + + return { + expand: function (context) { + return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) { + if (expression) { + var operator = null, + values = []; + + if (operators.indexOf(expression.charAt(0)) !== -1) { + operator = expression.charAt(0); + expression = expression.substr(1); + } + + expression.split(/,/g).forEach(function (variable) { + var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable); + values.push.apply(values, that.getValues(context, operator, tmp[1], tmp[2] || tmp[3])); + }); + + if (operator && operator !== '+') { + var separator = ','; + + if (operator === '?') { + separator = '&'; + } else if (operator !== '#') { + separator = operator; + } + return (values.length !== 0 ? operator : '') + values.join(separator); + } else { + return values.join(','); + } + } else { + return that.encodeReserved(literal); + } + }); + } + }; + }; + + return new UrlTemplate(); +})); + +},{}],50:[function(require,module,exports){ +var indexOf = require('indexof'); + +var Object_keys = function (obj) { + if (Object.keys) return Object.keys(obj) + else { + var res = []; + for (var key in obj) res.push(key) + return res; + } +}; + +var forEach = function (xs, fn) { + if (xs.forEach) return xs.forEach(fn) + else for (var i = 0; i < xs.length; i++) { + fn(xs[i], i, xs); + } +}; + +var defineProp = (function() { + try { + Object.defineProperty({}, '_', {}); + return function(obj, name, value) { + Object.defineProperty(obj, name, { + writable: true, + enumerable: false, + configurable: true, + value: value + }) + }; + } catch(e) { + return function(obj, name, value) { + obj[name] = value; + }; + } +}()); + +var globals = ['Array', 'Boolean', 'Date', 'Error', 'EvalError', 'Function', +'Infinity', 'JSON', 'Math', 'NaN', 'Number', 'Object', 'RangeError', +'ReferenceError', 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError', +'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'escape', +'eval', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'undefined', 'unescape']; + +function Context() {} +Context.prototype = {}; + +var Script = exports.Script = function NodeScript (code) { + if (!(this instanceof Script)) return new Script(code); + this.code = code; +}; + +Script.prototype.runInContext = function (context) { + if (!(context instanceof Context)) { + throw new TypeError("needs a 'context' argument."); + } + + var iframe = document.createElement('iframe'); + if (!iframe.style) iframe.style = {}; + iframe.style.display = 'none'; + + document.body.appendChild(iframe); + + var win = iframe.contentWindow; + var wEval = win.eval, wExecScript = win.execScript; + + if (!wEval && wExecScript) { + // win.eval() magically appears when this is called in IE: + wExecScript.call(win, 'null'); + wEval = win.eval; + } + + forEach(Object_keys(context), function (key) { + win[key] = context[key]; + }); + forEach(globals, function (key) { + if (context[key]) { + win[key] = context[key]; + } + }); + + var winKeys = Object_keys(win); + + var res = wEval.call(win, this.code); + + forEach(Object_keys(win), function (key) { + // Avoid copying circular objects like `top` and `window` by only + // updating existing context properties or new properties in the `win` + // that was only introduced after the eval. + if (key in context || indexOf(winKeys, key) === -1) { + context[key] = win[key]; + } + }); + + forEach(globals, function (key) { + if (!(key in context)) { + defineProp(context, key, win[key]); + } + }); + + document.body.removeChild(iframe); + + return res; +}; + +Script.prototype.runInThisContext = function () { + return eval(this.code); // maybe... +}; + +Script.prototype.runInNewContext = function (context) { + var ctx = Script.createContext(context); + var res = this.runInContext(ctx); + + forEach(Object_keys(ctx), function (key) { + context[key] = ctx[key]; + }); + + return res; +}; + +forEach(Object_keys(Script.prototype), function (name) { + exports[name] = Script[name] = function (code) { + var s = Script(code); + return s[name].apply(s, [].slice.call(arguments, 1)); + }; +}); + +exports.createScript = function (code) { + return exports.Script(code); +}; + +exports.createContext = Script.createContext = function (context) { + var copy = new Context(); + if(typeof context === 'object') { + forEach(Object_keys(context), function (key) { + copy[key] = context[key]; + }); + } + return copy; +}; + +},{"indexof":3}],51:[function(require,module,exports){ +/* global angular */ +'use strict'; + +var traverson = require('traverson'); + +var ng; +if (typeof angular !== 'undefined') { + // angular is defined globally, use this + ng = angular; +} else { + // angular is not defined globally, try to require it + ng = require('angular'); + if (typeof ng.module !== 'function') { + throw new Error('angular has either to be provided globally or made ' + + 'available as a shim for browserify. (Also, if the angular module on ' + + 'npm would actually be a proper CommonJS module, this error ' + + 'wouldn\'t be a thing.)'); + } +} + +var traversonAngular = ng.module('traverson', []); +var Builder = traverson._Builder; +var originalMethods = { + get: Builder.prototype.get, + getResource: Builder.prototype.getResource, + getUrl: Builder.prototype.getUrl, + post: Builder.prototype.post, + put: Builder.prototype.put, + patch: Builder.prototype.patch, + delete: Builder.prototype.delete, +}; + +traversonAngular.factory('traverson', + ['$q', '$httpTraversonAdapter', + function traversonFactory($q, $httpTraversonAdapter) { + + function promisify(that, originalMethod, argsArray) { + var deferred = $q.defer(); + + argsArray = argsArray || []; + + var traversal; + var callback = function(err, result, _traversal) { + if (err) { + err.result = result; + deferred.reject(err); + } else { + traversal = _traversal; + deferred.resolve(result); + } + }; + + argsArray.push(callback); + + var traversalHandler = originalMethod.apply(that, argsArray); + + function continueTraversal() { + var deferredContinue = $q.defer(); + deferred.promise.then(function() { + deferredContinue.resolve(traversal.continue()); + }, function() { + var error = new Error('Can\'t continue from a broken traversal.'); + error.name = 'InvalidStateError'; + throw error; + }); + return deferredContinue.promise; + } + + return { + result: deferred.promise, + continue: continueTraversal, + abort: traversalHandler.abort, + then: function() { + throw new Error('As of version 2.0.0, Traverson\'s action methods ' + + 'do no longer return the promise directly. Code like \n' + + 'traverson.from(url).follow(...).getResource().then(...)\n' + + 'needs to be changed to \n' + + 'traverson.from(url).follow(...).getResource().result.then(...)'); + }, + }; + } + + Builder.prototype.get = function() { + return promisify(this, originalMethods.get); + }; + + Builder.prototype.getResource = function() { + return promisify(this, originalMethods.getResource); + }; + + Builder.prototype.getUrl = Builder.prototype.getUri = function() { + return promisify(this, originalMethods.getUrl); + }; + + Builder.prototype.post = function(body) { + return promisify(this, originalMethods.post, [body]); + }; + + Builder.prototype.put = function(body) { + return promisify(this, originalMethods.put, [body]); + }; + + Builder.prototype.patch = function(body) { + return promisify(this, originalMethods.patch, [body]); + }; + + Builder.prototype.delete = Builder.prototype.del = function() { + return promisify(this, originalMethods.delete); + }; + + Builder.prototype.useAngularHttp = function() { + this.withRequestLibrary($httpTraversonAdapter); + return this; + }; + + return traverson; +}]); + +traversonAngular.factory('$httpTraversonAdapter', [ + '$http', '$q', function $httpTraversonAdapterFactory($http, $q) { + + function Request() { } + + Request.prototype.get = function(uri, options, callback) { + options = mapOptions(options); + $http + .get(uri, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.post = function(uri, options, callback) { + options = mapOptions(options); + $http + .post(uri, options.data, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.put = function(uri, options, callback) { + options = mapOptions(options); + $http + .put(uri, options.data, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.patch = function(uri, options, callback) { + options = mapOptions(options); + $http + .patch(uri, options.data, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + Request.prototype.del = function(uri, options, callback) { + options = mapOptions(options); + $http + .delete(uri, options) + .then(handleResponse(callback)) + .catch(handleError(callback)); + return new AbortHandle(options.timeout); + }; + + function mapOptions(options) { + options = options || {}; + var mappedOptions = {}; + mapQuery(mappedOptions, options); + mapHeaders(mappedOptions, options); + mapAuth(mappedOptions, options); + mapBody(mappedOptions, options); + mapForm(mappedOptions, options); + // do not parse JSON automatically, this will trip up Traverson + mappedOptions.transformResponse = function(data, headersGetter, status) { + return data; + }; + // hook to abort the request, if necessary + mappedOptions.timeout = $q.defer(); + return mappedOptions; + } + + function mapQuery(mappedOptions, options) { + // options.qs would be correct since we are using request/request options + // object API, but a previous version of traverson-angular incorrectly + // used options.query instead, so we allow this also, to not break + // backwards compatibility. + var qs = options.qs || options.query; + if (qs) { + mappedOptions.params = qs; + } + } + + function mapHeaders(mappedOptions, options) { + if (options.headers) { + mappedOptions.headers = options.headers; + } + } + + function mapAuth(mappedOptions, options) { + var auth = options.auth; + if (auth) { + var username = auth.user || auth.username; + var password = auth.pass || auth.password; + mappedOptions.headers = mappedOptions.headers || {}; + mappedOptions.headers.Authorization = 'Basic ' + btoa(username + ':' + + password); + } + } + + function mapBody(mappedOptions, options) { + if (options.body) { + mappedOptions.data = options.body; + } + } + + function mapForm(mappedOptions, options) { + var form = options.form; + if (form) { + mappedOptions.data = form; + mappedOptions.headers = mappedOptions.headers || {}; + mappedOptions.headers['Content-Type'] = + 'application/x-www-form-urlencoded'; + } + } + + function mapResponse(response) { + response.body = response.data; + response.headers = response.headers(); + response.statusCode = response.status; + return response; + } + + function handleResponse(callback) { + return function(response) { + return callback(null, mapResponse(response)); + }; + } + + function handleError(callback) { + return function(response) { + if (response.status >= 100 && response.status < 600) { + // This happens on a completed HTTP request with a status code outside + // of the 2xx range. In the context of Traverson, this is not an + // error, in particular, if this is the last request in a traversal. + // Thus, we re-route it to the successCallback. Handling 4xx and 5xx + // errors during the traversal is the responsibility of traverson, not + // traverson-angular. + return callback(null, mapResponse(response)); + } else { + // This happens on network errors, timeouts etc. In this case, + // AngularJS sets the status property to 0. In the context of + // Traverson, only these are to be interpreted as errors. + return callback(response); + } + }; + } + + return new Request(); + } +]); + +function AbortHandle(abortPromise) { + this.abortPromise = abortPromise; + this.listeners = []; +} + +AbortHandle.prototype.abort = function() { + this.abortPromise.resolve(); + this.listeners.forEach(function(fn) { + fn.call(); + }); +}; + +AbortHandle.prototype.on = function(event, fn) { + if (event !== 'abort') { + var error = new Error('Event ' + event + ' not supported'); + error.name = 'InvalidArgumentError'; + throw error; + } + this.listeners.push(fn); +}; + +module.exports = traversonAngular; + +},{"angular":1,"traverson":48}]},{},[51])(51) +}); \ No newline at end of file diff --git a/browser/dist/traverson-angular.min.js b/browser/dist/traverson-angular.min.js new file mode 100644 index 0000000..4d91a30 --- /dev/null +++ b/browser/dist/traverson-angular.min.js @@ -0,0 +1,2 @@ +!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.traversonAngular=a()}}(function(){var define,module,exports;return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g1&&j.shift();var k=this._trace(j,b,["$"],h,i,c);return k=k.filter(function(a){return a&&!a.isParentSelector}),k.length?1!==k.length||g||Array.isArray(k[0].value)?k.reduce(function(a,b){var c=e._getPreferredOutput(b);return f&&Array.isArray(c)?a=a.concat(c):a.push(c),a},[]):this._getPreferredOutput(k[0]):g?[]:void 0}},JSONPath.prototype._getPreferredOutput=function(a){var b=this.currResultType;switch(b){case"all":return a.path="string"==typeof a.path?a.path:JSONPath.toPathString(a.path),a;case"value":case"parent":case"parentProperty":return a[b];case"path":return JSONPath.toPathString(a[b]);case"pointer":return JSONPath.toPointer(a.path)}},JSONPath.prototype._handleCallback=function(a,b,c){if(b){var d=this._getPreferredOutput(a);a.path="string"==typeof a.path?a.path:JSONPath.toPathString(a.path),b(d,c,a)}},JSONPath.prototype._trace=function(a,b,c,d,e,f){function g(a){l=l.concat(a)}var h,i=this;if(!a.length)return h={path:c,value:b,parent:d,parentProperty:e},this._handleCallback(h,f,"value"),h;var j=a[0],k=a.slice(1),l=[];if(b&&Object.prototype.hasOwnProperty.call(b,j))g(this._trace(k,b[j],push(c,j),b,j,f));else if("*"===j)this._walk(j,k,b,c,d,e,f,function(a,b,c,d,e,f,h,j){g(i._trace(unshift(a,c),d,e,f,h,j))});else if(".."===j)g(this._trace(k,b,c,d,e,f)),this._walk(j,k,b,c,d,e,f,function(a,b,c,d,e,f,h,j){"object"==typeof d[a]&&g(i._trace(unshift(b,c),d[a],push(e,a),d,a,j))});else if("("===j[0]){if(this.currPreventEval)throw new Error("Eval [(expr)] prevented in JSONPath expression.");g(this._trace(unshift(this._eval(j,b,c[c.length-1],c.slice(0,-1),d,e),k),b,c,d,e,f))}else{if("^"===j)return c.length?{path:c.slice(0,-1),expr:k,isParentSelector:!0}:[];if("~"===j)return h={path:push(c,j),value:e,parent:d,parentProperty:null},this._handleCallback(h,f,"property"),h;if("$"===j)g(this._trace(k,b,c,null,null,f));else if(0===j.indexOf("?(")){if(this.currPreventEval)throw new Error("Eval [?(expr)] prevented in JSONPath expression.");this._walk(j,k,b,c,d,e,f,function(a,b,c,d,e,f,h,j){i._eval(b.replace(/^\?\((.*?)\)$/,"$1"),d[a],a,e,f,h)&&g(i._trace(unshift(a,c),d,e,f,h,j))})}else if(j.indexOf(",")>-1){var m,n;for(m=j.split(","),n=0;n-1&&(this.currSandbox._$_parentProperty=f,a=a.replace(/@parentProperty/g,"_$_parentProperty")),a.indexOf("@parent")>-1&&(this.currSandbox._$_parent=e,a=a.replace(/@parent/g,"_$_parent")),a.indexOf("@property")>-1&&(this.currSandbox._$_property=c,a=a.replace(/@property/g,"_$_property")),a.indexOf("@path")>-1&&(this.currSandbox._$_path=JSONPath.toPathString(d.concat([c])),a=a.replace(/@path/g,"_$_path")),a.match(/@([\.\s\)\[])/)&&(this.currSandbox._$_v=b,a=a.replace(/@([\.\s\)\[])/g,"_$_v$1"));try{return vm.runInNewContext(a,this.currSandbox)}catch(b){throw console.log(b),new Error("jsonPath: "+b.message+": "+a)}},JSONPath.cache={},JSONPath.toPathString=function(a){var b,c,d=a,e="$";for(b=1,c=d.length;b1)for(var c=1;c=300)&&(d=new Error(b.statusText||"Unsuccessful HTTP response"),d.original=a,d.response=b,d.status=b.status)}catch(a){d=a}d?c.callback(d,b):c.callback(null,b)})}function n(a,b){var c=s("DELETE",a);return b&&c.end(b),c}var o;"undefined"!=typeof window?o=window:"undefined"!=typeof self?o=self:(console.warn("Using browser-only version of superagent in non-browser environment"),o=this);var p=a("emitter"),q=a("./request-base"),r=a("./is-object"),s=b.exports=a("./request").bind(null,m);s.getXHR=function(){if(!(!o.XMLHttpRequest||o.location&&"file:"==o.location.protocol&&o.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(a){}throw Error("Browser-only verison of superagent could not find XHR")};var t="".trim?function(a){return a.trim()}:function(a){return a.replace(/(^\s*|\s*$)/g,"")};s.serializeObject=e,s.parseString=g,s.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"},s.serialize={"application/x-www-form-urlencoded":e,"application/json":JSON.stringify},s.parse={"application/x-www-form-urlencoded":g,"application/json":JSON.parse},l.prototype.get=function(a){return this.header[a.toLowerCase()]},l.prototype._setHeaderProperties=function(a){var b=this.header["content-type"]||"";this.type=j(b);var c=k(b);for(var d in c)this[d]=c[d]},l.prototype._parseBody=function(a){var b=s.parse[this.type];return!b&&i(this.type)&&(b=s.parse["application/json"]),b&&a&&(a.length||a instanceof Object)?b(a):null},l.prototype._setStatusProperties=function(a){1223===a&&(a=204);var b=a/100|0;this.status=this.statusCode=a,this.statusType=b,this.info=1==b,this.ok=2==b,this.clientError=4==b,this.serverError=5==b,this.error=(4==b||5==b)&&this.toError(),this.accepted=202==a,this.noContent=204==a,this.badRequest=400==a,this.unauthorized=401==a,this.notAcceptable=406==a,this.notFound=404==a,this.forbidden=403==a},l.prototype.toError=function(){var a=this.req,b=a.method,c=a.url,d="cannot "+b+" "+c+" ("+this.status+")",e=new Error(d);return e.status=this.status,e.method=b,e.url=c,e},s.Response=l,p(m.prototype);for(var u in q)m.prototype[u]=q[u];m.prototype.type=function(a){return this.set("Content-Type",s.types[a]||a),this},m.prototype.responseType=function(a){return this._responseType=a,this},m.prototype.accept=function(a){return this.set("Accept",s.types[a]||a),this},m.prototype.auth=function(a,b,c){switch(c||(c={type:"basic"}),c.type){case"basic":var d=btoa(a+":"+b);this.set("Authorization","Basic "+d);break;case"auto":this.username=a,this.password=b}return this},m.prototype.query=function(a){return"string"!=typeof a&&(a=e(a)),a&&this._query.push(a),this},m.prototype.attach=function(a,b,c){return this._getFormData().append(a,b,c||b.name),this},m.prototype._getFormData=function(){return this._formData||(this._formData=new o.FormData),this._formData},m.prototype.callback=function(a,b){var c=this._callback;this.clearTimeout(),c(a,b)},m.prototype.crossDomainError=function(){var a=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.");a.crossDomain=!0,a.status=this.status,a.method=this.method,a.url=this.url,this.callback(a)},m.prototype._timeoutError=function(){var a=this._timeout,b=new Error("timeout of "+a+"ms exceeded");b.timeout=a,this.callback(b)},m.prototype._appendQueryString=function(){var a=this._query.join("&");a&&(this.url+=~this.url.indexOf("?")?"&"+a:"?"+a)},m.prototype.end=function(a){var b=this,c=this.xhr=s.getXHR(),e=this._timeout,f=this._formData||this._data;this._callback=a||d,c.onreadystatechange=function(){if(4==c.readyState){var a;try{a=c.status}catch(b){a=0}if(0==a){if(b.timedout)return b._timeoutError();if(b._aborted)return;return b.crossDomainError()}b.emit("end")}};var g=function(a,c){c.total>0&&(c.percent=c.loaded/c.total*100),c.direction=a,b.emit("progress",c)};if(this.hasListeners("progress"))try{c.onprogress=g.bind(null,"download"),c.upload&&(c.upload.onprogress=g.bind(null,"upload"))}catch(a){}if(e&&!this._timer&&(this._timer=setTimeout(function(){b.timedout=!0,b.abort()},e)),this._appendQueryString(),this.username&&this.password?c.open(this.method,this.url,!0,this.username,this.password):c.open(this.method,this.url,!0),this._withCredentials&&(c.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof f&&!this._isHost(f)){var h=this._header["content-type"],j=this._serializer||s.serialize[h?h.split(";")[0]:""];!j&&i(h)&&(j=s.serialize["application/json"]),j&&(f=j(f))}for(var k in this.header)null!=this.header[k]&&c.setRequestHeader(k,this.header[k]);return this._responseType&&(c.responseType=this._responseType),this.emit("request",this),c.send("undefined"!=typeof f?f:null),this},s.Request=m,s.get=function(a,b,c){var d=s("GET",a);return"function"==typeof b&&(c=b,b=null),b&&d.query(b),c&&d.end(c),d},s.head=function(a,b,c){var d=s("HEAD",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.options=function(a,b,c){var d=s("OPTIONS",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.del=n,s.delete=n,s.patch=function(a,b,c){var d=s("PATCH",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.post=function(a,b,c){var d=s("POST",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d},s.put=function(a,b,c){var d=s("PUT",a);return"function"==typeof b&&(c=b,b=null),b&&d.send(b),c&&d.end(c),d}},{"./is-object":8,"./request":10,"./request-base":9,emitter:2}],8:[function(a,b,c){function d(a){return null!==a&&"object"==typeof a}b.exports=d},{}],9:[function(a,b,c){var d=a("./is-object");c.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},c.parse=function(a){return this._parser=a,this},c.serialize=function(a){return this._serializer=a,this},c.timeout=function(a){return this._timeout=a,this},c.then=function(a,b){if(!this._fullfilledPromise){var c=this;this._fullfilledPromise=new Promise(function(a,b){c.end(function(c,d){c?b(c):a(d)})})}return this._fullfilledPromise.then(a,b)},c.catch=function(a){return this.then(void 0,a)},c.use=function(a){return a(this),this},c.get=function(a){return this._header[a.toLowerCase()]},c.getHeader=c.get,c.set=function(a,b){if(d(a)){for(var c in a)this.set(c,a[c]);return this}return this._header[a.toLowerCase()]=b,this.header[a]=b,this},c.unset=function(a){return delete this._header[a.toLowerCase()],delete this.header[a],this},c.field=function(a,b){if(null===a||void 0===a)throw new Error(".field(name, val) name can not be empty");if(d(a)){for(var c in a)this.field(c,a[c]);return this}if(null===b||void 0===b)throw new Error(".field(name, val) val can not be empty");return this._getFormData().append(a,b),this},c.abort=function(){return this._aborted?this:(this._aborted=!0,this.xhr&&this.xhr.abort(),this.req&&this.req.abort(),this.clearTimeout(),this.emit("abort"),this)},c.withCredentials=function(){return this._withCredentials=!0,this},c.redirects=function(a){return this._maxRedirects=a,this},c.toJSON=function(){return{method:this.method,url:this.url,data:this._data,headers:this._header}},c._isHost=function(a){var b={}.toString.call(a);switch(b){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}},c.send=function(a){var b=d(a),c=this._header["content-type"];if(b&&d(this._data))for(var e in a)this._data[e]=a[e];else"string"==typeof a?(c||this.type("form"),c=this._header["content-type"],"application/x-www-form-urlencoded"==c?this._data=this._data?this._data+"&"+a:a:this._data=(this._data||"")+a):this._data=a;return!b||this._isHost(a)?this:(c||this.type("json"),this)}},{"./is-object":8}],10:[function(a,b,c){function d(a,b,c){return"function"==typeof c?new a("GET",b).end(c):2==arguments.length?new a("GET",b):new a(b,c)}b.exports=d},{}],11:[function(a,b,c){"use strict";function d(a){null==a&&(a=""),this.id=a}function e(a){return new d(a)}var f=!1;d.prototype.enable=function(){this.enabled=!0},d.prototype.debug=function(a){f&&console.log(this.id+"/debug: "+a)},d.prototype.info=function(a){f&&console.log(this.id+"/info: "+a)},d.prototype.warn=function(a){f&&console.log(this.id+"/warn: "+a)},d.prototype.error=function(a){f&&console.log(this.id+"/error: "+a)},e.enable=function(){f=!0},b.exports=e},{}],12:[function(a,b,c){"use strict";b.exports={isArray:function(a){return null!=a&&"[object Array]"===Object.prototype.toString.call(a)}}},{}],13:[function(a,b,c){"use strict";function d(){}function e(a,b){return b=b||{},f(a,b),g(a,b),h(a,b),i(a,b),j(a,b),k(a,b),a}function f(a,b){var c=b.qs;null!=c&&(a=a.query(c))}function g(a,b){var c=b.headers;null!=c&&(a=a.set(c))}function h(a,b){var c=b.auth;null!=c&&(a=a.auth(c.user||c.username,c.pass||c.password))}function i(a,b){if(null!=b){var c=b.body;null!=c&&(a=a.send(c))}}function j(a,b){if(null!=b){var c=b.form;null!=c&&(a=a.set("content-type","application/x-www-form-urlencoded"),a=a.send(c))}}function k(a,b){if(null!=b){var c=b.withCredentials;c===!0&&a.withCredentials()}}function l(a){return a.body=a.text,a.statusCode=a.status,a}function m(a){return function(b,c){if(b){if(!c)return a(b);a(null,l(c))}else a(null,l(c))}}var n=a("superagent");d.prototype.get=function(a,b,c){return e(n.get(a),b).end(m(c))},d.prototype.post=function(a,b,c){return e(n.post(a),b).end(m(c))},d.prototype.put=function(a,b,c){return e(n.put(a),b).end(m(c))},d.prototype.patch=function(a,b,c){return e(n.patch(a),b).end(m(c))},d.prototype.del=function(a,b,c){return e(n.del(a),b).end(m(c))},b.exports=new d},{superagent:7}],14:[function(a,b,c){"use strict";var d={startsWith:function(a,b){return""===b||null!=a&&null!=b&&(a=String(a),b=String(b),a.length>=b.length&&a.slice(0,b.length)===b)},endsWith:function(a,b){return""===b||null!=a&&null!=b&&(a=String(a),b=String(b),a.length>=b.length&&a.slice(a.length-b.length)===b)},splice:function(a,b,c,e){var f=d.chars(a);return f.splice(~~b,~~c,e),f.join("")},contains:function(a,b){return""===b||null!=a&&String(a).indexOf(b)!==-1},chars:function(a){return null==a?[]:String(a).split("")}};b.exports=d},{}],15:[function(a,b,c){"use strict";var d=a("resolve-url");c.resolve=function(a,b){return d(a,b)}},{"resolve-url":6}],16:[function(a,b,c){"use strict";var d=a("minilog"),e=a("./errors"),f=e.errors,g=e.createError,h=d("traverson");c.abortTraversal=function(){h.debug("aborting link traversal"),this.aborted=!0,this.currentRequest&&(h.debug("request in progress. trying to abort it, too."),this.currentRequest.abort())},c.registerAbortListener=function(a,b){a.currentRequest&&a.currentRequest.on("abort",function(){c.callCallbackOnAbort(a)})},c.callCallbackOnAbort=function(a){h.debug("link traversal aborted"),a.callbackHasBeenCalledAfterAbort||(a.callbackHasBeenCalledAfterAbort=!0,a.callback(c.abortError(),a))},c.abortError=function(){var a=g("Link traversal process has been aborted.",f.TraversalAbortedError);return a.aborted=!0,a}},{"./errors":19,minilog:11}],17:[function(a,b,c){"use strict";function d(a,b,c,d){var e;e=a.convertResponseToObject?[q,h,r,l]:[p],a.lastMethod=c,g.walk(a,e,d)}function e(a){return{abort:a.abortTraversal}}var f=a("minilog"),g=(f("traverson"),a("./abort_traversal"),a("./transforms/apply_transforms"),a("./http_requests"),a("./is_continuation"),a("./walker")),h=a("./transforms/check_http_status"),i=a("./transforms/continuation_to_doc"),j=a("./transforms/continuation_to_response"),k=a("./transforms/convert_embedded_doc_to_response"),l=a("./transforms/extract_doc"),m=a("./transforms/extract_response"),n=a("./transforms/extract_url"),o=a("./transforms/fetch_last_resource"),p=a("./transforms/execute_last_http_request"),q=a("./transforms/execute_http_request"),r=a("./transforms/parse");c.get=function(a,b){var c;return c=a.convertResponseToObject?[i,o,h,r,l]:[j,o,k,m],g.walk(a,c,b),e(a)},c.getUrl=function(a,b){return g.walk(a,[n],b),e(a)},c.post=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.post,b),e(a)},c.put=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.put,b),e(a)},c.patch=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.patch,b),e(a)},c.delete=function(a,b){return d(a,a.requestModuleInstance,a.requestModuleInstance.del,b),e(a)}},{"./abort_traversal":16,"./http_requests":20,"./is_continuation":21,"./transforms/apply_transforms":27,"./transforms/check_http_status":28,"./transforms/continuation_to_doc":29,"./transforms/continuation_to_response":30,"./transforms/convert_embedded_doc_to_response":31,"./transforms/execute_http_request":33,"./transforms/execute_last_http_request":34,"./transforms/extract_doc":35,"./transforms/extract_response":36,"./transforms/extract_url":37,"./transforms/fetch_last_resource":38,"./transforms/parse":41,"./walker":47,minilog:11}],18:[function(a,b,c){"use strict";function d(a){this.mediaType=a||w.CONTENT_NEGOTIATION,this.adapter=this._createAdapter(this.mediaType),this.contentNegotiation=!0,this.convertResponseToObjectFlag=!1,this.links=[],this.jsonParser=JSON.parse,this.requestModuleInstance=o,this.requestOptions={},this.resolveRelativeFlag=!1,this.preferEmbedded=!1,this.lastTraversalState=null,this.continuation=null}function e(a,b){for(var c=0;c continuing finished traversal process"),a.continuation={step:b.step,action:d},a.continuation.step.index=0,j(a,b),a}})}}function j(a,b){a.aborted=!1,a.adapter=b.adapter,a.body=b.body,a.callbackHasBeenCalledAfterAbort=!1,a.contentNegotiation=b.contentNegotiation,a.convertResponseToObjectFlag=b.convertResponseToObject,a.links=[],a.jsonParser=b.jsonParser,a.requestModuleInstance=b.requestModuleInstance,a.requestOptions=b.requestOptions,a.resolveRelativeFlag=b.resolveRelative,a.preferEmbedded=b.preferEmbedded,a.startUrl=b.startUrl,a.templateParameters=b.templateParameters}function k(a){return p.isArray(a)?m(a):"object"==typeof a?l(a):a}function l(a){return x(null,a)}function m(a){return a?a.slice(0):a}var n=a("minilog"),o=a("request"),p=a("util"),q=a("./actions"),r=a("./abort_traversal").abortTraversal,s=a("./errors"),t=s.errors,u=s.createError,v=a("./media_type_registry"),w=a("./media_types"),x=a("./merge_recursive"),y=n("traverson");d.prototype._createAdapter=function(a){var b=v.get(a);if(!b)throw u("Unknown or unsupported media type: "+a,t.UnsupportedMediaType);return y.debug("creating new "+b.name),new b(y)},d.prototype.newRequest=function(){var a=new d(this.getMediaType());return a.contentNegotiation=this.doesContentNegotiation(),a.convertResponseToObject(this.convertsResponseToObject()),a.from(m(this.getFrom())),a.withTemplateParameters(k(this.getTemplateParameters())),a.withRequestOptions(k(this.getRequestOptions())),a.withRequestLibrary(this.getRequestLibrary()),a.parseResponseBodiesWith(this.getJsonParser()),a.resolveRelative(this.doesResolveRelative()),a.preferEmbeddedResources(this.doesPreferEmbeddedResources()),a.continuation=this.continuation,a},d.prototype.setMediaType=function(a){return this.mediaType=a||w.CONTENT_NEGOTIATION,this.adapter=this._createAdapter(a),this.contentNegotiation=a===w.CONTENT_NEGOTIATION,this},d.prototype.json=function(){return this.setMediaType(w.JSON),this},d.prototype.jsonHal=function(){return this.setMediaType(w.JSON_HAL),this},d.prototype.useContentNegotiation=function(){return this.setMediaType(w.CONTENT_NEGOTIATION),this.contentNegotiation=!0,this},d.prototype.from=function(a){return this.startUrl=a,this},d.prototype.follow=function(){ +for(var a=Array.prototype.slice.apply(1===arguments.length&&p.isArray(arguments[0])?arguments[0]:arguments),b=0;b1?j("JSONPath expression "+b+" returned more than one match in document:\n"+JSON.stringify(a),i.JSONPathError,a):j("JSONPath expression "+b+" returned no match in document:\n"+JSON.stringify(a),i.JSONPathError,a)},d.prototype._handleHeader=function(a,b){switch(b.value){case"location":var c=a.headers.location;if(!c)throw j("Following the location header but there was no location header in the last response.",i.LinkError,a.headers);return{url:c};default:throw j("Link objects with type header and value "+b.value+" are not supported by this adapter.",i.InvalidArgumentError,b)}},b.exports=d},{"./errors":19,"jsonpath-plus":4,minilog:11,"underscore.string":14}],23:[function(a,b,c){"use strict";var d=a("./media_types"),e={};c.register=function(a,b){e[a]=b},c.get=function(a){return e[a]},c.register(d.CONTENT_NEGOTIATION,a("./negotiation_adapter")),c.register(d.JSON,a("./json_adapter"))},{"./json_adapter":22,"./media_types":24,"./negotiation_adapter":26}],24:[function(a,b,c){"use strict";b.exports={CONTENT_NEGOTIATION:"content-negotiation",JSON:"application/json",JSON_HAL:"application/hal+json"}},{}],25:[function(a,b,c){"use strict";function d(a,b){!a&&b&&(a={});for(var c in b)b.hasOwnProperty(c)&&e(a,b,c);return a}function e(a,b,c){"object"==typeof b[c]?(a[c]&&"object"==typeof a[c]||(a[c]={}),d(a[c],b[c])):a[c]=b[c]}b.exports=d},{}],26:[function(a,b,c){"use strict";function d(a){}var e=a("./errors"),f=e.errors,g=e.createError;d.prototype.findNextStep=function(a,b){throw g("Content negotiation did not happen",f.InvalidStateError)},b.exports=d},{"./errors":19}],27:[function(a,b,c){(function(c){"use strict";function d(a,b,e){f.debug("applying",a.length,"transforms");for(var g=0;g=300)){var c=d(a.step.url,b,a.step.response.body);return f.error("unexpected http status code"),f.error(c),a.callback(c),!1}return f.debug("http status code ok ("+b+")"),!0}},{"../errors":19,"../is_continuation":21,minilog:11}],29:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("../is_continuation");b.exports=function(a){return!f(a)||(e.debug("continuing from last traversal process (actions)"),a.continuation=null,a.callback(null,a.step.doc),!1)}},{"../is_continuation":21,minilog:11}],30:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("./convert_embedded_doc_to_response"),g=a("../is_continuation");b.exports=function(a){return!g(a)||(e.debug("continuing from last traversal process (actions)"),a.continuation=null,f(a),a.callback(null,a.step.response),!1)}},{"../is_continuation":21,"./convert_embedded_doc_to_response":31,minilog:11}],31:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson");b.exports=function(a){return!a.step.response&&a.step.doc&&(e.debug("faking HTTP response for embedded resource"),a.step.response={statusCode:200,body:JSON.stringify(a.step.doc),remark:"This is not an actual HTTP response. The resource you requested was an embedded resource, so no HTTP request was made to acquire it."}),!0}},{minilog:11}],32:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("../media_type_registry"),g=a("../errors"),h=g.errors,i=g.createError;b.exports=function(a,b){if(a.contentNegotiation&&a.step.response&&a.step.response.headers&&a.step.response.headers["content-type"]){var c=a.step.response.headers["content-type"].split(/[; ]/)[0],d=f.get(c);if(!d)return b(i("Unknown content type for content type detection: "+c,h.UnsupportedMediaType)),!1;a.adapter=new d(e)}return!0}},{"../errors":19,"../media_type_registry":23,minilog:11}],33:[function(a,b,c){"use strict";function d(a,b){return a.aborted?g.callCallbackOnAbort(a):void h.executeHttpRequest(a,a.requestModuleInstance,a.lastMethod,function(c,d){return c?(c.aborted||(f.debug("error while processing step ",a.step),f.error(c)),a.callback(c)):void b(a)})}var e=a("minilog"),f=e("traverson"),g=a("../abort_traversal"),h=a("../http_requests");d.isAsync=!0,b.exports=d},{"../abort_traversal":16,"../http_requests":20,minilog:11}],34:[function(a,b,c){"use strict";function d(a,b){return a.aborted?f.callCallbackOnAbort(a):void g.executeHttpRequest(a,a.requestModuleInstance,a.lastMethod,a.callback)}var e=a("minilog"),f=(e("traverson"),a("../abort_traversal")),g=a("../http_requests");d.isAsync=!0,b.exports=d},{"../abort_traversal":16,"../http_requests":20,minilog:11}],35:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson");b.exports=function(a){return e.debug("walker.walk has finished"),a.callback(null,a.step.doc),!1}},{minilog:11}],36:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson");b.exports=function(a){return e.debug("walker.walk has finished"),a.callback(null,a.step.response),!1}},{minilog:11}],37:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("url"),g=a("../errors"),h=g.errors,i=g.createError;b.exports=function(a){return e.debug("walker.walk has finished"),a.step.url?a.callback(null,a.step.url):a.step.doc&&a.step.doc._links&&a.step.doc._links.self&&a.step.doc._links.self.href?a.callback(null,f.resolve(a.startUrl,a.step.doc._links.self.href)):a.callback(i('You requested an URL but the last resource is an embedded resource and has no URL of its own (that is, it has no link with rel="self"',h.LinkError))}},{"../errors":19,minilog:11,url:15}],38:[function(a,b,c){"use strict";function d(a,b){return a.aborted?g.callCallbackOnAbort(a):void h.fetchResource(a,function(a,c){return f.debug("fetchResource returned (fetchLastResource)."),a?(a.aborted||(f.debug("error while processing step ",c.step),f.error(a)),c.callback(a)):void b(c)})}var e=a("minilog"),f=e("traverson"),g=a("../abort_traversal"),h=a("../http_requests");d.isAsync=!0,b.exports=d},{"../abort_traversal":16,"../http_requests":20,minilog:11}],39:[function(a,b,c){(function(c){"use strict";function d(a,b){j(a)?e(a,b):f(a,b)}function e(a,b){h.debug("continuing from last traversal process (walker)"),c.nextTick(function(){b(a)})}function f(a,b){return a.aborted?i.callCallbackOnAbort(a):void k.fetchResource(a,function(a,c){return h.debug("fetchResource returned"),a?(a.aborted||(h.debug("error while processing step ",c.step),h.error(a)),c.callback(a)):void b(c)})}var g=a("minilog"),h=g("traverson"),i=a("../abort_traversal"),j=a("../is_continuation"),k=a("../http_requests");d.isAsync=!0,b.exports=d}).call(this,a("_process"))},{"../abort_traversal":16,"../http_requests":20,"../is_continuation":21,_process:5,minilog:11}],40:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("util");b.exports=function(a){var b=a.requestOptions;return f.isArray(a.requestOptions)&&(b=a.requestOptions[a.step.index]||{}),e.debug("options: ",b),b}},{minilog:11,util:12}],41:[function(a,b,c){"use strict";function d(a){h.debug("parsing response body"),a.requestOptions&&a.requestOptions.jsonReviver?a.step.doc=a.step.response.body:a.step.doc=a.jsonParser(a.step.response.body)}function e(a,b){var c=b;"SyntaxError"===b.name&&(c=f(a.step.url,a.step.response.body)),h.error("parsing failed"),h.error(c),a.callback(c)}function f(a,b){var c=k("The document at "+a+" could not be parsed as JSON: "+b,j.JSONError,b);return c.url=a,c.body=b,c}var g=a("minilog"),h=g("traverson"),i=a("../errors"),j=i.errors,k=i.createError,l=a("../is_continuation");b.exports=function(a){if(l(a)&&(h.debug("continuing from last traversal process (transforms/parse)"),"getResource"===a.continuation.action))return!0;if(a.step.doc)return h.debug("no parsing necessary, probably an embedded document"),!0;try{return d(a),!0}catch(b){return e(a,b),!1}}},{"../errors":19,"../is_continuation":21,minilog:11}],42:[function(a,b,c){"use strict";var d=a("../is_continuation");b.exports=function(a){return!!d(a)||(a.continuation=null,!0)}},{"../is_continuation":21}],43:[function(a,b,c){"use strict";var d=a("../is_continuation");b.exports=function(a){return!!d(a)||(a.lastStep=null,!0)}},{"../is_continuation":21}],44:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("underscore.string"),g=a("url"),h=/https?:\/\//i;b.exports=function(a){return a.step.url&&0!==a.step.url.search(h)&&(e.debug("found non full qualified URL"),a.resolveRelative&&a.lastStep&&a.lastStep.url?(e.debug("resolving URL relative"),f.startsWith(a.step.url,"/")&&f.endsWith(a.lastStep.url,"/")&&(a.step.url=f.splice(a.step.url,0,1)),a.step.url=a.lastStep.url+a.step.url):a.step.url=g.resolve(a.startUrl,a.step.url)),!0}},{minilog:11,"underscore.string":14,url:15}],45:[function(a,b,c){"use strict";var d=a("minilog"),e=d("traverson"),f=a("underscore.string"),g=a("url-template"),h=a("util");b.exports=function(a){if(a.step.url){var b=a.templateParameters;if(h.isArray(b)&&(b=b[a.step.index]),b=b||{},f.contains(a.step.url,"{")){e.debug("resolving URI template");var c=g.parse(a.step.url),d=c.expand(b);e.debug("resolved to ",d),a.step.url=d}}return!0}},{minilog:11,"underscore.string":14,"url-template":49,util:12}],46:[function(a,b,c){"use strict";function d(a,b){try{return a.adapter.findNextStep(a,b)}catch(b){return f.error("could not find next step"),f.error(b),a.callback(b),null}}var e=a("minilog"),f=e("traverson");b.exports=function(a){var b=a.links[a.step.index];return f.debug("next link: "+b),a.lastStep=a.step,a.step=d(a,b),!!a.step&&(f.debug("found next step",a.step),a.step.url=a.step.url||a.step.uri,a.step.index=a.lastStep.index+1,!0)}},{minilog:11}],47:[function(a,b,c){"use strict";function d(a,b){if(h.debug("processing next step"),e(a)&&!f(a))j(l,a,function(a){h.debug("successfully processed step"),d(a,b)});else{if(f(a))return i.callCallbackOnAbort(a);h.debug("link array exhausted"),j(b,a,function(a){return a.callback()})}}function e(a){return a.step.index=100&&b.status<600?a(null,k(b)):a(b)}}return c.prototype.get=function(b,c,f){return c=e(c),a.get(b,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.post=function(b,c,f){return c=e(c),a.post(b,c.data,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.put=function(b,c,f){return c=e(c),a.put(b,c.data,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.patch=function(b,c,f){return c=e(c),a.patch(b,c.data,c).then(l(f)).catch(m(f)),new d(c.timeout)},c.prototype.del=function(b,c,f){return c=e(c),a.delete(b,c).then(l(f)).catch(m(f)),new d(c.timeout)},new c}]),d.prototype.abort=function(){this.abortPromise.resolve(),this.listeners.forEach(function(a){a.call()})},d.prototype.on=function(a,b){if("abort"!==a){var c=new Error("Event "+a+" not supported");throw c.name="InvalidArgumentError",c}this.listeners.push(b)},b.exports=g},{angular:1,traverson:48}]},{},[51])(51)}); \ No newline at end of file