From fb695b80c7f5681ac16750a3cdcb4aae1b2fc999 Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Thu, 19 Jul 2018 09:25:00 +0200 Subject: [PATCH] add build artifacts for release --- browser/dist/traverson-angular.external.js | 5911 ++++++++++++++++ .../dist/traverson-angular.external.min.js | 1 + browser/dist/traverson-angular.js | 5912 +++++++++++++++++ browser/dist/traverson-angular.min.js | 1 + 4 files changed, 11825 insertions(+) create mode 100644 browser/dist/traverson-angular.external.js create mode 100644 browser/dist/traverson-angular.external.min.js create mode 100644 browser/dist/traverson-angular.js create mode 100644 browser/dist/traverson-angular.min.js diff --git a/browser/dist/traverson-angular.external.js b/browser/dist/traverson-angular.external.js new file mode 100644 index 0000000..6cd32a5 --- /dev/null +++ b/browser/dist/traverson-angular.external.js @@ -0,0 +1,5911 @@ +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; + }; +} +if (!String.prototype.includes) { + String.prototype.includes = function (item) { // eslint-disable-line no-extend-native + return this.indexOf(item) > -1; + }; +} + +var moveToAnotherArray = function (source, target, conditionCb) { + for (var i = 0, kl = source.length; i < kl; i++) { + var key = source[i]; + if (conditionCb(key)) { + target.push(source.splice(i--, 1)[0]); + } + } +}; + +var vm = isNode + ? require('vm') : { + runInNewContext: function (expr, context) { + var keys = Object.keys(context); + var funcs = []; + moveToAnotherArray(keys, funcs, function (key) { + return typeof context[key] === 'function'; + }); + var code = funcs.reduce(function (s, func) { + return 'var ' + func + '=' + context[func].toString() + ';' + s; + }, ''); + code += keys.reduce(function (s, vr) { + return 'var ' + vr + '=' + JSON.stringify(context[vr]).replace(/\u2028|\u2029/g, function (m) { + // http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/ + return '\\u202' + (m === '\u2028' ? '8' : '9'); + }) + ';' + s; + }, expr); + return eval(code); + } + }; + +function push (arr, elem) {arr = arr.slice(); arr.push(elem); return arr;} +function unshift (elem, arr) {arr = arr.slice(); arr.unshift(elem); return arr;} +function NewError (value) { + this.avoidNew = true; + this.value = value; + this.message = 'JSONPath should not be called with "new" (it prevents return of (unwrapped) scalar values)'; +} + +function JSONPath (opts, expr, obj, callback, otherTypeCallback) { + if (!(this instanceof JSONPath)) { + try { + return new JSONPath(opts, expr, obj, callback, otherTypeCallback); + } + catch (e) { + if (!e.avoidNew) { + throw e; + } + return e.value; + } + } + + if (typeof opts === 'string') { + otherTypeCallback = callback; + callback = obj; + obj = expr; + expr = opts; + opts = {}; + } + opts = opts || {}; + var objArgs = opts.hasOwnProperty('json') && opts.hasOwnProperty('path'); + this.json = opts.json || obj; + this.path = opts.path || expr; + this.resultType = (opts.resultType && opts.resultType.toLowerCase()) || 'value'; + this.flatten = opts.flatten || false; + this.wrap = opts.hasOwnProperty('wrap') ? opts.wrap : true; + this.sandbox = opts.sandbox || {}; + this.preventEval = opts.preventEval || false; + this.parent = opts.parent || null; + this.parentProperty = opts.parentProperty || null; + this.callback = opts.callback || callback || null; + this.otherTypeCallback = opts.otherTypeCallback || otherTypeCallback || function () { + throw new Error('You must supply an otherTypeCallback callback option with the @other() operator.'); + }; + + if (opts.autostart !== false) { + var ret = this.evaluate({ + path: (objArgs ? opts.path : expr), + json: (objArgs ? opts.json : obj) + }); + if (!ret || typeof ret !== 'object') { + throw new NewError(ret); + } + return ret; + } +} + +// PUBLIC METHODS + +JSONPath.prototype.evaluate = function (expr, json, callback, otherTypeCallback) { + var self = this, + flatten = this.flatten, + wrap = this.wrap, + currParent = this.parent, + currParentProperty = this.parentProperty; + + this.currResultType = this.resultType; + this.currPreventEval = this.preventEval; + this.currSandbox = this.sandbox; + callback = callback || this.callback; + this.currOtherTypeCallback = otherTypeCallback || this.otherTypeCallback; + + json = json || this.json; + expr = expr || this.path; + if (expr && typeof expr === 'object') { + if (!expr.path) { + throw new Error('You must supply a "path" property when providing an object argument to JSONPath.evaluate().'); + } + json = expr.hasOwnProperty('json') ? expr.json : json; + flatten = expr.hasOwnProperty('flatten') ? expr.flatten : flatten; + this.currResultType = expr.hasOwnProperty('resultType') ? expr.resultType : this.currResultType; + this.currSandbox = expr.hasOwnProperty('sandbox') ? expr.sandbox : this.currSandbox; + wrap = expr.hasOwnProperty('wrap') ? expr.wrap : wrap; + this.currPreventEval = expr.hasOwnProperty('preventEval') ? expr.preventEval : this.currPreventEval; + callback = expr.hasOwnProperty('callback') ? expr.callback : callback; + this.currOtherTypeCallback = expr.hasOwnProperty('otherTypeCallback') ? expr.otherTypeCallback : this.currOtherTypeCallback; + currParent = expr.hasOwnProperty('parent') ? expr.parent : currParent; + currParentProperty = expr.hasOwnProperty('parentProperty') ? expr.parentProperty : currParentProperty; + expr = expr.path; + } + currParent = currParent || null; + currParentProperty = currParentProperty || null; + + if (Array.isArray(expr)) { + expr = JSONPath.toPathString(expr); + } + if (!expr || !json || !allowedResultTypes.includes(this.currResultType)) { + return; + } + this._obj = json; + + var exprList = JSONPath.toPathArray(expr); + if (exprList[0] === '$' && exprList.length > 1) {exprList.shift();} + this._hasParentSelector = null; + 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, literalPriority) { + // 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 retPush (elem) { + ret.push(elem); + } + function addRet (elems) { + if (Array.isArray(elems)) { + elems.forEach(retPush); + } else { + ret.push(elems); + } + } + + if ((typeof loc !== 'string' || literalPriority) && 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, true)); + }); + } + 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)); + } + }); + } + // 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 + this._hasParentSelector = true; + 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 (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] Python slice syntax + addRet(this._slice(loc, x, val, path, parent, parentPropName, 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[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)); + } + 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'].includes(typeof val))) { + 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 (loc[0] === '`' && val && Object.prototype.hasOwnProperty.call(val, loc.slice(1))) { // `-escaped property + var locProp = loc.slice(1); + addRet(this._trace(x, val[locProp], push(path, locProp), val, locProp, callback, true)); + } + else if (loc.includes(',')) { // [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 (!literalPriority && val && Object.prototype.hasOwnProperty.call(val, loc)) { // simple case--directly follow property + addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, true)); + } + + // 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 + if (this._hasParentSelector) { + for (var t = 0; t < ret.length; t++) { + var rett = ret[t]; + if (rett.isParentSelector) { + var tmp = self._trace(rett.expr, val, rett.path, parent, parentPropName, callback); + if (Array.isArray(tmp)) { + ret[t] = tmp[0]; + for (var tt = 1, tl = tmp.length; tt < tl; tt++) { + t++; + ret.splice(t, 0, tmp[tt]); + } + } else { + ret[t] = tmp; + } + } + } + } + return ret; +}; + +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) { + var tmp = this._trace(unshift(i, expr), val, path, parent, parentPropName, callback); + if (Array.isArray(tmp)) { + tmp.forEach(function (t) { + ret.push(t); + }); + } + else { + ret.push(tmp); + } + } + return ret; +}; + +JSONPath.prototype._eval = function (code, _v, _vname, path, parent, parentPropName) { + if (!this._obj || !_v) {return false;} + if (code.includes('@parentProperty')) { + this.currSandbox._$_parentProperty = parentPropName; + code = code.replace(/@parentProperty/g, '_$_parentProperty'); + } + if (code.includes('@parent')) { + this.currSandbox._$_parent = parent; + code = code.replace(/@parent/g, '_$_parent'); + } + if (code.includes('@property')) { + this.currSandbox._$_property = _vname; + code = code.replace(/@property/g, '_$_property'); + } + if (code.includes('@path')) { + 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].concat();} + 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":54}],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.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +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){ +function Agent() { + this._defaults = []; +} + +["use", "on", "once", "set", "query", "type", "accept", "auth", "withCredentials", "sortQuery", "retry", "ok", "redirects", + "timeout", "buffer", "serialize", "parse", "ca", "key", "pfx", "cert"].forEach(function(fn) { + /** Default setting for all requests from this agent */ + Agent.prototype[fn] = function(/*varargs*/) { + this._defaults.push({fn:fn, arguments:arguments}); + return this; + } +}); + +Agent.prototype._setDefaults = function(req) { + this._defaults.forEach(function(def) { + req[def.fn].apply(req, def.arguments); + }); +}; + +module.exports = Agent; + +},{}],8:[function(require,module,exports){ +/** + * Root reference for iframes. + */ + +var root; +if (typeof window !== 'undefined') { // Browser window + root = window; +} else if (typeof self !== 'undefined') { // Web Worker + root = self; +} else { // Other environments + console.warn("Using browser-only version of superagent in non-browser environment"); + root = this; +} + +var Emitter = require('component-emitter'); +var RequestBase = require('./request-base'); +var isObject = require('./is-object'); +var ResponseBase = require('./response-base'); +var Agent = require('./agent-base'); + +/** + * Noop. + */ + +function noop(){}; + +/** + * Expose `request`. + */ + +var request = exports = module.exports = function(method, url) { + // callback + if ('function' == typeof url) { + return new exports.Request('GET', method).end(url); + } + + // url first + if (1 == arguments.length) { + return new exports.Request('GET', method); + } + + return new exports.Request(method, url); +} + +exports.Request = Request; + +/** + * Determine XHR. + */ + +request.getXHR = function () { + if (root.XMLHttpRequest + && (!root.location || 'file:' != root.location.protocol + || !root.ActiveXObject)) { + return new XMLHttpRequest; + } else { + try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} + } + throw Error("Browser-only version of superagent could not find XHR"); +}; + +/** + * Removes leading and trailing whitespace, added to support IE. + * + * @param {String} s + * @return {String} + * @api private + */ + +var trim = ''.trim + ? function(s) { return s.trim(); } + : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; + +/** + * Serialize the given `obj`. + * + * @param {Object} obj + * @return {String} + * @api private + */ + +function serialize(obj) { + if (!isObject(obj)) return obj; + var pairs = []; + for (var key in obj) { + pushEncodedKeyValuePair(pairs, key, obj[key]); + } + return pairs.join('&'); +} + +/** + * Helps 'serialize' with serializing arrays. + * Mutates the pairs array. + * + * @param {Array} pairs + * @param {String} key + * @param {Mixed} val + */ + +function pushEncodedKeyValuePair(pairs, key, val) { + if (val != null) { + if (Array.isArray(val)) { + val.forEach(function(v) { + pushEncodedKeyValuePair(pairs, key, v); + }); + } else if (isObject(val)) { + for(var subkey in val) { + pushEncodedKeyValuePair(pairs, key + '[' + subkey + ']', val[subkey]); + } + } else { + pairs.push(encodeURIComponent(key) + + '=' + encodeURIComponent(val)); + } + } else if (val === null) { + pairs.push(encodeURIComponent(key)); + } +} + +/** + * Expose serialization method. + */ + +request.serializeObject = serialize; + +/** + * Parse the given x-www-form-urlencoded `str`. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function parseString(str) { + var obj = {}; + var pairs = str.split('&'); + var pair; + var pos; + + for (var i = 0, len = pairs.length; i < len; ++i) { + pair = pairs[i]; + pos = pair.indexOf('='); + if (pos == -1) { + obj[decodeURIComponent(pair)] = ''; + } else { + obj[decodeURIComponent(pair.slice(0, pos))] = + decodeURIComponent(pair.slice(pos + 1)); + } + } + + return obj; +} + +/** + * Expose parser. + */ + +request.parseString = parseString; + +/** + * Default MIME type map. + * + * superagent.types.xml = 'application/xml'; + * + */ + +request.types = { + html: 'text/html', + json: 'application/json', + xml: 'text/xml', + urlencoded: 'application/x-www-form-urlencoded', + 'form': 'application/x-www-form-urlencoded', + 'form-data': 'application/x-www-form-urlencoded' +}; + +/** + * Default serialization map. + * + * superagent.serialize['application/xml'] = function(obj){ + * return 'generated xml here'; + * }; + * + */ + +request.serialize = { + 'application/x-www-form-urlencoded': serialize, + 'application/json': JSON.stringify +}; + +/** + * Default parsers. + * + * superagent.parse['application/xml'] = function(str){ + * return { object parsed from str }; + * }; + * + */ + +request.parse = { + 'application/x-www-form-urlencoded': parseString, + 'application/json': JSON.parse +}; + +/** + * Parse the given header `str` into + * an object containing the mapped fields. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function parseHeader(str) { + var lines = str.split(/\r?\n/); + var fields = {}; + var index; + var line; + var field; + var val; + + for (var i = 0, len = lines.length; i < len; ++i) { + line = lines[i]; + index = line.indexOf(':'); + if (index === -1) { // could be empty line, just skip it + continue; + } + field = line.slice(0, index).toLowerCase(); + val = trim(line.slice(index + 1)); + fields[field] = val; + } + + return fields; +} + +/** + * Check if `mime` is json or has +json structured syntax suffix. + * + * @param {String} mime + * @return {Boolean} + * @api private + */ + +function isJSON(mime) { + // should match /json or +json + // but not /json-seq + return /[\/+]json($|[^-\w])/.test(mime); +} + +/** + * Initialize a new `Response` with the given `xhr`. + * + * - set flags (.ok, .error, etc) + * - parse header + * + * Examples: + * + * Aliasing `superagent` as `request` is nice: + * + * request = superagent; + * + * We can use the promise-like API, or pass callbacks: + * + * request.get('/').end(function(res){}); + * request.get('/', function(res){}); + * + * Sending data can be chained: + * + * request + * .post('/user') + * .send({ name: 'tj' }) + * .end(function(res){}); + * + * Or passed to `.send()`: + * + * request + * .post('/user') + * .send({ name: 'tj' }, function(res){}); + * + * Or passed to `.post()`: + * + * request + * .post('/user', { name: 'tj' }) + * .end(function(res){}); + * + * Or further reduced to a single call for simple cases: + * + * request + * .post('/user', { name: 'tj' }, function(res){}); + * + * @param {XMLHTTPRequest} xhr + * @param {Object} options + * @api private + */ + +function Response(req) { + this.req = req; + this.xhr = this.req.xhr; + // responseText is accessible only if responseType is '' or 'text' and on older browsers + this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined') + ? this.xhr.responseText + : null; + this.statusText = this.req.xhr.statusText; + var status = this.xhr.status; + // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request + if (status === 1223) { + status = 204; + } + this._setStatusProperties(status); + this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); + // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but + // getResponseHeader still works. so we get content-type even if getting + // other headers fails. + this.header['content-type'] = this.xhr.getResponseHeader('content-type'); + this._setHeaderProperties(this.header); + + if (null === this.text && req._responseType) { + this.body = this.xhr.response; + } else { + this.body = this.req.method != 'HEAD' + ? this._parseBody(this.text ? this.text : this.xhr.response) + : null; + } +} + +ResponseBase(Response.prototype); + +/** + * Parse the given body `str`. + * + * Used for auto-parsing of bodies. Parsers + * are defined on the `superagent.parse` object. + * + * @param {String} str + * @return {Mixed} + * @api private + */ + +Response.prototype._parseBody = function(str) { + var parse = request.parse[this.type]; + if (this.req._parser) { + return this.req._parser(this, str); + } + if (!parse && isJSON(this.type)) { + parse = request.parse['application/json']; + } + return parse && str && (str.length || str instanceof Object) + ? parse(str) + : null; +}; + +/** + * Return an `Error` representative of this response. + * + * @return {Error} + * @api public + */ + +Response.prototype.toError = function(){ + var req = this.req; + var method = req.method; + var url = req.url; + + var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; + var err = new Error(msg); + err.status = this.status; + err.method = method; + err.url = url; + + return err; +}; + +/** + * Expose `Response`. + */ + +request.Response = Response; + +/** + * Initialize a new `Request` with the given `method` and `url`. + * + * @param {String} method + * @param {String} url + * @api public + */ + +function Request(method, url) { + var self = this; + this._query = this._query || []; + this.method = method; + this.url = url; + this.header = {}; // preserves header name case + this._header = {}; // coerces header names to lowercase + this.on('end', function(){ + var err = null; + var res = null; + + try { + res = new Response(self); + } catch(e) { + err = new Error('Parser is unable to parse the response'); + err.parse = true; + err.original = e; + // issue #675: return the raw response if the response parsing fails + if (self.xhr) { + // ie9 doesn't have 'response' property + err.rawResponse = typeof self.xhr.responseType == 'undefined' ? self.xhr.responseText : self.xhr.response; + // issue #876: return the http status code if the response parsing fails + err.status = self.xhr.status ? self.xhr.status : null; + err.statusCode = err.status; // backwards-compat only + } else { + err.rawResponse = null; + err.status = null; + } + + return self.callback(err); + } + + self.emit('response', res); + + var new_err; + try { + if (!self._isResponseOK(res)) { + new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); + } + } catch(custom_err) { + new_err = custom_err; // ok() callback can throw + } + + // #1000 don't catch errors from the callback to avoid double calling it + if (new_err) { + new_err.original = err; + new_err.response = res; + new_err.status = res.status; + self.callback(new_err, res); + } else { + self.callback(null, res); + } + }); +} + +/** + * Mixin `Emitter` and `RequestBase`. + */ + +Emitter(Request.prototype); +RequestBase(Request.prototype); + +/** + * Set Content-Type to `type`, mapping values from `request.types`. + * + * Examples: + * + * superagent.types.xml = 'application/xml'; + * + * request.post('/') + * .type('xml') + * .send(xmlstring) + * .end(callback); + * + * request.post('/') + * .type('application/xml') + * .send(xmlstring) + * .end(callback); + * + * @param {String} type + * @return {Request} for chaining + * @api public + */ + +Request.prototype.type = function(type){ + this.set('Content-Type', request.types[type] || type); + return this; +}; + +/** + * Set Accept to `type`, mapping values from `request.types`. + * + * Examples: + * + * superagent.types.json = 'application/json'; + * + * request.get('/agent') + * .accept('json') + * .end(callback); + * + * request.get('/agent') + * .accept('application/json') + * .end(callback); + * + * @param {String} accept + * @return {Request} for chaining + * @api public + */ + +Request.prototype.accept = function(type){ + this.set('Accept', request.types[type] || type); + return this; +}; + +/** + * Set Authorization field value with `user` and `pass`. + * + * @param {String} user + * @param {String} [pass] optional in case of using 'bearer' as type + * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic') + * @return {Request} for chaining + * @api public + */ + +Request.prototype.auth = function(user, pass, options){ + if (1 === arguments.length) pass = ''; + if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options + options = pass; + pass = ''; + } + if (!options) { + options = { + type: 'function' === typeof btoa ? 'basic' : 'auto', + }; + } + + var encoder = function(string) { + if ('function' === typeof btoa) { + return btoa(string); + } + throw new Error('Cannot use basic auth, btoa is not a function'); + }; + + return this._auth(user, pass, options, encoder); +}; + +/** + * Add query-string `val`. + * + * Examples: + * + * request.get('/shoes') + * .query('size=10') + * .query({ color: 'blue' }) + * + * @param {Object|String} val + * @return {Request} for chaining + * @api public + */ + +Request.prototype.query = function(val){ + if ('string' != typeof val) val = serialize(val); + if (val) this._query.push(val); + return this; +}; + +/** + * Queue the given `file` as an attachment to the specified `field`, + * with optional `options` (or filename). + * + * ``` js + * request.post('/upload') + * .attach('content', new Blob(['hey!'], { type: "text/html"})) + * .end(callback); + * ``` + * + * @param {String} field + * @param {Blob|File} file + * @param {String|Object} options + * @return {Request} for chaining + * @api public + */ + +Request.prototype.attach = function(field, file, options){ + if (file) { + if (this._data) { + throw Error("superagent can't mix .send() and .attach()"); + } + + this._getFormData().append(field, file, options || file.name); + } + return this; +}; + +Request.prototype._getFormData = function(){ + if (!this._formData) { + this._formData = new root.FormData(); + } + return this._formData; +}; + +/** + * Invoke the callback with `err` and `res` + * and handle arity check. + * + * @param {Error} err + * @param {Response} res + * @api private + */ + +Request.prototype.callback = function(err, res){ + if (this._shouldRetry(err, res)) { + return this._retry(); + } + + var fn = this._callback; + this.clearTimeout(); + + if (err) { + if (this._maxRetries) err.retries = this._retries - 1; + this.emit('error', err); + } + + fn(err, res); +}; + +/** + * Invoke callback with x-domain error. + * + * @api private + */ + +Request.prototype.crossDomainError = function(){ + var err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'); + err.crossDomain = true; + + err.status = this.status; + err.method = this.method; + err.url = this.url; + + this.callback(err); +}; + +// This only warns, because the request is still likely to work +Request.prototype.buffer = Request.prototype.ca = Request.prototype.agent = function(){ + console.warn("This is not supported in browser version of superagent"); + return this; +}; + +// This throws, because it can't send/receive data as expected +Request.prototype.pipe = Request.prototype.write = function(){ + throw Error("Streaming is not supported in browser version of superagent"); +}; + +/** + * Check if `obj` is a host object, + * we don't want to serialize these :) + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ +Request.prototype._isHost = function _isHost(obj) { + // Native objects stringify to [object File], [object Blob], [object FormData], etc. + return obj && 'object' === typeof obj && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== '[object Object]'; +} + +/** + * Initiate request, invoking callback `fn(res)` + * with an instanceof `Response`. + * + * @param {Function} fn + * @return {Request} for chaining + * @api public + */ + +Request.prototype.end = function(fn){ + if (this._endCalled) { + console.warn("Warning: .end() was called twice. This is not supported in superagent"); + } + this._endCalled = true; + + // store callback + this._callback = fn || noop; + + // querystring + this._finalizeQueryString(); + + return this._end(); +}; + +Request.prototype._end = function() { + var self = this; + var xhr = (this.xhr = request.getXHR()); + var data = this._formData || this._data; + + this._setTimeouts(); + + // state change + xhr.onreadystatechange = function(){ + var readyState = xhr.readyState; + if (readyState >= 2 && self._responseTimeoutTimer) { + clearTimeout(self._responseTimeoutTimer); + } + if (4 != readyState) { + return; + } + + // In IE9, reads to any property (e.g. status) off of an aborted XHR will + // result in the error "Could not complete the operation due to error c00c023f" + var status; + try { status = xhr.status } catch(e) { status = 0; } + + if (!status) { + if (self.timedout || self._aborted) return; + return self.crossDomainError(); + } + self.emit('end'); + }; + + // progress + var handleProgress = function(direction, e) { + if (e.total > 0) { + e.percent = e.loaded / e.total * 100; + } + e.direction = direction; + self.emit('progress', e); + }; + if (this.hasListeners('progress')) { + try { + xhr.onprogress = handleProgress.bind(null, 'download'); + if (xhr.upload) { + xhr.upload.onprogress = handleProgress.bind(null, 'upload'); + } + } catch(e) { + // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist. + // Reported here: + // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context + } + } + + // initiate request + try { + if (this.username && this.password) { + xhr.open(this.method, this.url, true, this.username, this.password); + } else { + xhr.open(this.method, this.url, true); + } + } catch (err) { + // see #1149 + return this.callback(err); + } + + // CORS + if (this._withCredentials) xhr.withCredentials = true; + + // body + if (!this._formData && 'GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !this._isHost(data)) { + // serialize stuff + var contentType = this._header['content-type']; + var serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : '']; + if (!serialize && isJSON(contentType)) { + serialize = request.serialize['application/json']; + } + if (serialize) data = serialize(data); + } + + // set header fields + for (var field in this.header) { + if (null == this.header[field]) continue; + + if (this.header.hasOwnProperty(field)) + xhr.setRequestHeader(field, this.header[field]); + } + + if (this._responseType) { + xhr.responseType = this._responseType; + } + + // send stuff + this.emit('request', this); + + // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing) + // We need null here if data is undefined + xhr.send(typeof data !== 'undefined' ? data : null); + return this; +}; + +request.agent = function() { + return new Agent(); +}; + +["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(function(method) { + Agent.prototype[method.toLowerCase()] = function(url, fn) { + var req = new request.Request(method, url); + this._setDefaults(req); + if (fn) { + req.end(fn); + } + return req; + }; +}); + +Agent.prototype.del = Agent.prototype['delete']; + +/** + * GET `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.get = function(url, data, fn) { + var req = request('GET', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.query(data); + if (fn) req.end(fn); + return req; +}; + +/** + * HEAD `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.head = function(url, data, fn) { + var req = request('HEAD', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.query(data); + if (fn) req.end(fn); + return req; +}; + +/** + * OPTIONS query to `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.options = function(url, data, fn) { + var req = request('OPTIONS', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * DELETE `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +function del(url, data, fn) { + var req = request('DELETE', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +} + +request['del'] = del; +request['delete'] = del; + +/** + * PATCH `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.patch = function(url, data, fn) { + var req = request('PATCH', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * POST `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.post = function(url, data, fn) { + var req = request('POST', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * PUT `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.put = function(url, data, fn) { + var req = request('PUT', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +},{"./agent-base":7,"./is-object":9,"./request-base":10,"./response-base":11,"component-emitter":2}],9:[function(require,module,exports){ +'use strict'; + +/** + * 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; + +},{}],10:[function(require,module,exports){ +'use strict'; + +/** + * Module of mixed-in functions shared between node and client code + */ +var isObject = require('./is-object'); + +/** + * Expose `RequestBase`. + */ + +module.exports = RequestBase; + +/** + * Initialize a new `RequestBase`. + * + * @api public + */ + +function RequestBase(obj) { + if (obj) return mixin(obj); +} + +/** + * Mixin the prototype properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in RequestBase.prototype) { + obj[key] = RequestBase.prototype[key]; + } + return obj; +} + +/** + * Clear previous timeout. + * + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.clearTimeout = function _clearTimeout(){ + clearTimeout(this._timer); + clearTimeout(this._responseTimeoutTimer); + delete this._timer; + delete this._responseTimeoutTimer; + return this; +}; + +/** + * Override default response body parser + * + * This function will be called to convert incoming data into request.body + * + * @param {Function} + * @api public + */ + +RequestBase.prototype.parse = function parse(fn){ + this._parser = fn; + return this; +}; + +/** + * Set format of binary response body. + * In browser valid formats are 'blob' and 'arraybuffer', + * which return Blob and ArrayBuffer, respectively. + * + * In Node all values result in Buffer. + * + * Examples: + * + * req.get('/') + * .responseType('blob') + * .end(callback); + * + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.responseType = function(val){ + this._responseType = val; + 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 + */ + +RequestBase.prototype.serialize = function serialize(fn){ + this._serializer = fn; + return this; +}; + +/** + * Set timeouts. + * + * - response timeout is time between sending request and receiving the first byte of the response. Includes DNS and connection time. + * - deadline is the time from start of the request to receiving response body in full. If the deadline is too short large files may not load at all on slow connections. + * + * Value of 0 or false means no timeout. + * + * @param {Number|Object} ms or {response, deadline} + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.timeout = function timeout(options){ + if (!options || 'object' !== typeof options) { + this._timeout = options; + this._responseTimeout = 0; + return this; + } + + for(var option in options) { + switch(option) { + case 'deadline': + this._timeout = options.deadline; + break; + case 'response': + this._responseTimeout = options.response; + break; + default: + console.warn("Unknown timeout option", option); + } + } + return this; +}; + +/** + * Set number of retry attempts on error. + * + * Failed requests will be retried 'count' times if timeout or err.code >= 500. + * + * @param {Number} count + * @param {Function} [fn] + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.retry = function retry(count, fn){ + // Default to 1 if no count passed or true + if (arguments.length === 0 || count === true) count = 1; + if (count <= 0) count = 0; + this._maxRetries = count; + this._retries = 0; + this._retryCallback = fn; + return this; +}; + +var ERROR_CODES = [ + 'ECONNRESET', + 'ETIMEDOUT', + 'EADDRINFO', + 'ESOCKETTIMEDOUT' +]; + +/** + * Determine if a request should be retried. + * (Borrowed from segmentio/superagent-retry) + * + * @param {Error} err + * @param {Response} [res] + * @returns {Boolean} + */ +RequestBase.prototype._shouldRetry = function(err, res) { + if (!this._maxRetries || this._retries++ >= this._maxRetries) { + return false; + } + if (this._retryCallback) { + try { + var override = this._retryCallback(err, res); + if (override === true) return true; + if (override === false) return false; + // undefined falls back to defaults + } catch(e) { + console.error(e); + } + } + if (res && res.status && res.status >= 500 && res.status != 501) return true; + if (err) { + if (err.code && ~ERROR_CODES.indexOf(err.code)) return true; + // Superagent timeout + if (err.timeout && err.code == 'ECONNABORTED') return true; + if (err.crossDomain) return true; + } + return false; +}; + +/** + * Retry request + * + * @return {Request} for chaining + * @api private + */ + +RequestBase.prototype._retry = function() { + + this.clearTimeout(); + + // node + if (this.req) { + this.req = null; + this.req = this.request(); + } + + this._aborted = false; + this.timedout = false; + + return this._end(); +}; + +/** + * Promise support + * + * @param {Function} resolve + * @param {Function} [reject] + * @return {Request} + */ + +RequestBase.prototype.then = function then(resolve, reject) { + if (!this._fullfilledPromise) { + var self = this; + if (this._endCalled) { + console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"); + } + this._fullfilledPromise = new Promise(function(innerResolve, innerReject) { + self.end(function(err, res) { + if (err) innerReject(err); + else innerResolve(res); + }); + }); + } + return this._fullfilledPromise.then(resolve, reject); +}; + +RequestBase.prototype['catch'] = function(cb) { + return this.then(undefined, cb); +}; + +/** + * Allow for extension + */ + +RequestBase.prototype.use = function use(fn) { + fn(this); + return this; +}; + +RequestBase.prototype.ok = function(cb) { + if ('function' !== typeof cb) throw Error("Callback required"); + this._okCallback = cb; + return this; +}; + +RequestBase.prototype._isResponseOK = function(res) { + if (!res) { + return false; + } + + if (this._okCallback) { + return this._okCallback(res); + } + + return res.status >= 200 && res.status < 300; +}; + +/** + * Get request header `field`. + * Case-insensitive. + * + * @param {String} field + * @return {String} + * @api public + */ + +RequestBase.prototype.get = function(field){ + return this._header[field.toLowerCase()]; +}; + +/** + * Get case-insensitive header `field` value. + * This is a deprecated internal API. Use `.get(field)` instead. + * + * (getHeader is no longer used internally by the superagent code base) + * + * @param {String} field + * @return {String} + * @api private + * @deprecated + */ + +RequestBase.prototype.getHeader = RequestBase.prototype.get; + +/** + * Set header `field` to `val`, or multiple fields with one object. + * Case-insensitive. + * + * Examples: + * + * req.get('/') + * .set('Accept', 'application/json') + * .set('X-API-Key', 'foobar') + * .end(callback); + * + * req.get('/') + * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) + * .end(callback); + * + * @param {String|Object} field + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.set = function(field, val){ + if (isObject(field)) { + for (var key in field) { + this.set(key, field[key]); + } + return this; + } + this._header[field.toLowerCase()] = val; + this.header[field] = val; + return this; +}; + +/** + * Remove header `field`. + * Case-insensitive. + * + * Example: + * + * req.get('/') + * .unset('User-Agent') + * .end(callback); + * + * @param {String} field + */ +RequestBase.prototype.unset = function(field){ + delete this._header[field.toLowerCase()]; + delete this.header[field]; + return this; +}; + +/** + * Write the field `name` and `val`, or multiple fields with one object + * for "multipart/form-data" request bodies. + * + * ``` js + * request.post('/upload') + * .field('foo', 'bar') + * .end(callback); + * + * request.post('/upload') + * .field({ foo: 'bar', baz: 'qux' }) + * .end(callback); + * ``` + * + * @param {String|Object} name + * @param {String|Blob|File|Buffer|fs.ReadStream} val + * @return {Request} for chaining + * @api public + */ +RequestBase.prototype.field = function(name, val) { + // name should be either a string or an object. + if (null === name || undefined === name) { + throw new Error('.field(name, val) name can not be empty'); + } + + if (this._data) { + console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"); + } + + if (isObject(name)) { + for (var key in name) { + this.field(key, name[key]); + } + return this; + } + + if (Array.isArray(val)) { + for (var i in val) { + this.field(name, val[i]); + } + return this; + } + + // val should be defined now + if (null === val || undefined === val) { + throw new Error('.field(name, val) val can not be empty'); + } + if ('boolean' === typeof val) { + val = '' + val; + } + this._getFormData().append(name, val); + return this; +}; + +/** + * Abort the request, and clear potential timeout. + * + * @return {Request} + * @api public + */ +RequestBase.prototype.abort = function(){ + if (this._aborted) { + return this; + } + this._aborted = true; + this.xhr && this.xhr.abort(); // browser + this.req && this.req.abort(); // node + this.clearTimeout(); + this.emit('abort'); + return this; +}; + +RequestBase.prototype._auth = function(user, pass, options, base64Encoder) { + switch (options.type) { + case 'basic': + this.set('Authorization', 'Basic ' + base64Encoder(user + ':' + pass)); + break; + + case 'auto': + this.username = user; + this.password = pass; + break; + + case 'bearer': // usage would be .auth(accessToken, { type: 'bearer' }) + this.set('Authorization', 'Bearer ' + user); + break; + } + return this; +}; + +/** + * Enable transmission of cookies with x-domain requests. + * + * Note that for this to work the origin must not be + * using "Access-Control-Allow-Origin" with a wildcard, + * and also must set "Access-Control-Allow-Credentials" + * to "true". + * + * @api public + */ + +RequestBase.prototype.withCredentials = function(on) { + // This is browser-only functionality. Node side is no-op. + if (on == undefined) on = true; + this._withCredentials = on; + return this; +}; + +/** + * Set the max redirects to `n`. Does noting in browser XHR implementation. + * + * @param {Number} n + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.redirects = function(n){ + this._maxRedirects = n; + return this; +}; + +/** + * Maximum size of buffered response body, in bytes. Counts uncompressed size. + * Default 200MB. + * + * @param {Number} n + * @return {Request} for chaining + */ +RequestBase.prototype.maxResponseSize = function(n){ + if ('number' !== typeof n) { + throw TypeError("Invalid argument"); + } + this._maxResponseSize = n; + return this; +}; + +/** + * Convert to a plain javascript object (not JSON string) of scalar properties. + * Note as this method is designed to return a useful non-this value, + * it cannot be chained. + * + * @return {Object} describing method, url, and data of this request + * @api public + */ + +RequestBase.prototype.toJSON = function() { + return { + method: this.method, + url: this.url, + data: this._data, + headers: this._header, + }; +}; + +/** + * Send `data` as the request body, defaulting the `.type()` to "json" when + * an object is given. + * + * Examples: + * + * // manual json + * request.post('/user') + * .type('json') + * .send('{"name":"tj"}') + * .end(callback) + * + * // auto json + * request.post('/user') + * .send({ name: 'tj' }) + * .end(callback) + * + * // manual x-www-form-urlencoded + * request.post('/user') + * .type('form') + * .send('name=tj') + * .end(callback) + * + * // auto x-www-form-urlencoded + * request.post('/user') + * .type('form') + * .send({ name: 'tj' }) + * .end(callback) + * + * // defaults to x-www-form-urlencoded + * request.post('/user') + * .send('name=tobi') + * .send('species=ferret') + * .end(callback) + * + * @param {String|Object} data + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.send = function(data){ + var isObj = isObject(data); + var type = this._header['content-type']; + + if (this._formData) { + console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"); + } + + if (isObj && !this._data) { + if (Array.isArray(data)) { + this._data = []; + } else if (!this._isHost(data)) { + this._data = {}; + } + } else if (data && this._data && this._isHost(this._data)) { + throw Error("Can't merge these send calls"); + } + + // merge + if (isObj && isObject(this._data)) { + for (var key in data) { + this._data[key] = data[key]; + } + } else if ('string' == typeof data) { + // default to x-www-form-urlencoded + if (!type) this.type('form'); + type = this._header['content-type']; + if ('application/x-www-form-urlencoded' == type) { + this._data = this._data + ? this._data + '&' + data + : data; + } else { + this._data = (this._data || '') + data; + } + } else { + this._data = data; + } + + if (!isObj || this._isHost(data)) { + return this; + } + + // default to json + if (!type) this.type('json'); + return this; +}; + +/** + * Sort `querystring` by the sort function + * + * + * Examples: + * + * // default order + * request.get('/user') + * .query('name=Nick') + * .query('search=Manny') + * .sortQuery() + * .end(callback) + * + * // customized sort function + * request.get('/user') + * .query('name=Nick') + * .query('search=Manny') + * .sortQuery(function(a, b){ + * return a.length - b.length; + * }) + * .end(callback) + * + * + * @param {Function} sort + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.sortQuery = function(sort) { + // _sort default to true but otherwise can be a function or boolean + this._sort = typeof sort === 'undefined' ? true : sort; + return this; +}; + +/** + * Compose querystring to append to req.url + * + * @api private + */ +RequestBase.prototype._finalizeQueryString = function(){ + var query = this._query.join('&'); + if (query) { + this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') + query; + } + this._query.length = 0; // Makes the call idempotent + + if (this._sort) { + var index = this.url.indexOf('?'); + if (index >= 0) { + var queryArr = this.url.substring(index + 1).split('&'); + if ('function' === typeof this._sort) { + queryArr.sort(this._sort); + } else { + queryArr.sort(); + } + this.url = this.url.substring(0, index) + '?' + queryArr.join('&'); + } + } +}; + +// For backwards compat only +RequestBase.prototype._appendQueryString = function() {console.trace("Unsupported");} + +/** + * Invoke callback with timeout error. + * + * @api private + */ + +RequestBase.prototype._timeoutError = function(reason, timeout, errno){ + if (this._aborted) { + return; + } + var err = new Error(reason + timeout + 'ms exceeded'); + err.timeout = timeout; + err.code = 'ECONNABORTED'; + err.errno = errno; + this.timedout = true; + this.abort(); + this.callback(err); +}; + +RequestBase.prototype._setTimeouts = function() { + var self = this; + + // deadline + if (this._timeout && !this._timer) { + this._timer = setTimeout(function(){ + self._timeoutError('Timeout of ', self._timeout, 'ETIME'); + }, this._timeout); + } + // response timeout + if (this._responseTimeout && !this._responseTimeoutTimer) { + this._responseTimeoutTimer = setTimeout(function(){ + self._timeoutError('Response timeout of ', self._responseTimeout, 'ETIMEDOUT'); + }, this._responseTimeout); + } +}; + +},{"./is-object":9}],11:[function(require,module,exports){ +'use strict'; + +/** + * Module dependencies. + */ + +var utils = require('./utils'); + +/** + * Expose `ResponseBase`. + */ + +module.exports = ResponseBase; + +/** + * Initialize a new `ResponseBase`. + * + * @api public + */ + +function ResponseBase(obj) { + if (obj) return mixin(obj); +} + +/** + * Mixin the prototype properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in ResponseBase.prototype) { + obj[key] = ResponseBase.prototype[key]; + } + return obj; +} + +/** + * Get case-insensitive `field` value. + * + * @param {String} field + * @return {String} + * @api public + */ + +ResponseBase.prototype.get = function(field) { + return this.header[field.toLowerCase()]; +}; + +/** + * Set header related properties: + * + * - `.type` the content type without params + * + * A response of "Content-Type: text/plain; charset=utf-8" + * will provide you with a `.type` of "text/plain". + * + * @param {Object} header + * @api private + */ + +ResponseBase.prototype._setHeaderProperties = function(header){ + // TODO: moar! + // TODO: make this a util + + // content-type + var ct = header['content-type'] || ''; + this.type = utils.type(ct); + + // params + var params = utils.params(ct); + for (var key in params) this[key] = params[key]; + + this.links = {}; + + // links + try { + if (header.link) { + this.links = utils.parseLinks(header.link); + } + } catch (err) { + // ignore + } +}; + +/** + * Set flags such as `.ok` based on `status`. + * + * For example a 2xx response will give you a `.ok` of __true__ + * whereas 5xx will be __false__ and `.error` will be __true__. The + * `.clientError` and `.serverError` are also available to be more + * specific, and `.statusType` is the class of error ranging from 1..5 + * sometimes useful for mapping respond colors etc. + * + * "sugar" properties are also defined for common cases. Currently providing: + * + * - .noContent + * - .badRequest + * - .unauthorized + * - .notAcceptable + * - .notFound + * + * @param {Number} status + * @api private + */ + +ResponseBase.prototype._setStatusProperties = function(status){ + var type = status / 100 | 0; + + // status / class + this.status = this.statusCode = status; + this.statusType = type; + + // basics + this.info = 1 == type; + this.ok = 2 == type; + this.redirect = 3 == type; + this.clientError = 4 == type; + this.serverError = 5 == type; + this.error = (4 == type || 5 == type) + ? this.toError() + : false; + + // sugar + this.created = 201 == status; + this.accepted = 202 == status; + this.noContent = 204 == status; + this.badRequest = 400 == status; + this.unauthorized = 401 == status; + this.notAcceptable = 406 == status; + this.forbidden = 403 == status; + this.notFound = 404 == status; + this.unprocessableEntity = 422 == status; +}; + +},{"./utils":12}],12:[function(require,module,exports){ +'use strict'; + +/** + * Return the mime type for the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.type = function(str){ + return str.split(/ *; */).shift(); +}; + +/** + * Return header field parameters. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.params = function(str){ + return str.split(/ *; */).reduce(function(obj, str){ + var parts = str.split(/ *= */); + var key = parts.shift(); + var val = parts.shift(); + + if (key && val) obj[key] = val; + return obj; + }, {}); +}; + +/** + * Parse Link header fields. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.parseLinks = function(str){ + return str.split(/ *, */).reduce(function(obj, str){ + var parts = str.split(/ *; */); + var url = parts[0].slice(1, -1); + var rel = parts[1].split(/ *= */)[1].slice(1, -1); + obj[rel] = url; + return obj; + }, {}); +}; + +/** + * Strip content related fields from `header`. + * + * @param {Object} header + * @return {Object} header + * @api private + */ + +exports.cleanHeader = function(header, changesOrigin){ + delete header['content-type']; + delete header['content-length']; + delete header['transfer-encoding']; + delete header['host']; + // secuirty + if (changesOrigin) { + delete header['authorization']; + delete header['cookie']; + } + return header; +}; + +},{}],13:[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; + +},{}],14:[function(require,module,exports){ +'use strict'; + +module.exports = { + isArray: function(o) { + if (o == null) { + return false; + } + return Object.prototype.toString.call(o) === '[object Array]'; + } +}; + +},{}],15:[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":8}],16:[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; + +},{}],17:[function(require,module,exports){ +'use strict'; + +var resolveUrl = require('resolve-url'); + +exports.resolve = function(from, to) { + return resolveUrl(from, to); +}; + +},{"resolve-url":6}],18:[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":21,"minilog":13}],19:[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') + , parseLinkHeader = require('./transforms/parse_link_header'); + +/** + * 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, + parseLinkHeader, + 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, + parseLinkHeader, + extractDoc, + ]; + } else { + transformsAfterLastStep = [ + executeLastHttpRequest, + ]; + } + + t.lastMethod = method; + walker.walk(t, transformsAfterLastStep, callback); +} + +function createTraversalHandle(t) { + return { + abort: t.abortTraversal + }; +} + +},{"./abort_traversal":18,"./http_requests":22,"./is_continuation":23,"./transforms/apply_transforms":30,"./transforms/check_http_status":31,"./transforms/continuation_to_doc":32,"./transforms/continuation_to_response":33,"./transforms/convert_embedded_doc_to_response":34,"./transforms/execute_http_request":36,"./transforms/execute_last_http_request":37,"./transforms/extract_doc":38,"./transforms/extract_response":39,"./transforms/extract_url":40,"./transforms/fetch_last_resource":41,"./transforms/parse":44,"./transforms/parse_link_header":45,"./walker":51,"minilog":13}],20:[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, linkType) { + this.mediaType = mediaType || mediaTypes.CONTENT_NEGOTIATION; + this.linkType = linkType || 'link-rel'; + this.adapter = this._createAdapter(this.mediaType); + this.autoHeaders = true; + 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(), + this.getLinkType()); + clonedRequestBuilder.useAutoHeaders(this.setsAutoHeaders()); + 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; +}; + +Builder.prototype.getLinkType = function() { + return this.linkType; +}; + +/** + * 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: this.linkType, + 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; +}; + +/** + * Disables automatic Accept and Content-Type headers. See useAutoHeaders(). + */ +Builder.prototype.disableAutoHeaders = function() { + return this.useAutoHeaders(false); +}; + +/** + * Enables automatic Accept and Content-Type headers. See useAutoHeaders(). + */ +Builder.prototype.enableAutoHeaders = function() { + return this.useAutoHeaders(true); +}; + +/** + * Enables or disables automatic headers. With automatic headers enabled, + * traverson will set default Accept and the Content-Type headers for HTTP + * requests, unless you provide these headers explicitly with withRequestOptions + * or addRequestOptions. + * + * The header values depend on the media type (see setMediaType()). For example, + * for plain vanilla JSON (that is, when using setMediaType('application/json') + * or the corresponding shortcut .json()), both headers will be send with the + * value 'application/json'. For HAL (that is, when using + * setMediaType('application/hal+json') or the corresponding shortcut + * jsonHal()), both headers will be send with the value 'application/hal+json'. + * + * If the method is called without arguments (or the first argument is undefined + * or null), automatic headers are turned on, otherwise the argument is + * interpreted as a boolean flag. If it is a truthy value, auto headers + * are enabled, if it is a falsy value (but not null or undefined), auto headers + * are disabled. + * + * A note about the condition "unless you provide these headers explicitly with + * withRequestOptions or addRequestOptions" in the first paragraph: Traverson + * with automatic headers enabled will only check for the header option + * "Accept" and "Content-Type", not for "accept" or "Content-type" or any other + * variation regarding upper case/lower case letters. So to be on the safe + * side, if you mix auto headers with explicitly specified headers, make sure + * to specify your explicit headers with this exact same combination of upper + * case and lower case letters. + */ +Builder.prototype.useAutoHeaders = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.autoHeaders = !!flag; + 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 default Accept and the Content-Type headers will be set + * automatically for HTTP requests. + */ +Builder.prototype.setsAutoHeaders = function() { + return this.autoHeaders; +}; + +/** + * 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; + +/** +* Set linkType property indicating that traverson must follow relations from +* header 'link' +*/ +Builder.prototype.linkHeader = function() { + this.linkType = 'link-header'; + return this; +}; + + +function createInitialTraversalState(self, body) { + + var normalizedBody = + (body !== null && typeof body !== undefined) ? body : null; + + var traversalState = { + aborted: false, + adapter: self.adapter || null, + body: normalizedBody, + callbackHasBeenCalledAfterAbort: false, + autoHeaders: self.setsAutoHeaders(), + 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.autoHeaders = t.autoHeaders; + 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":18,"./actions":19,"./errors":21,"./media_type_registry":25,"./media_types":26,"./merge_recursive":27,"minilog":13,"request":15,"util":14}],21:[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; + }, + +}; + +},{}],22:[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 !== null && typeof t.body !== 'undefined') { + 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":18,"./errors":21,"./transforms/detect_content_type":35,"./transforms/get_options_for_step":43,"_process":5,"minilog":13}],23:[function(require,module,exports){ +'use strict'; + +module.exports = function isContinuation(t) { + return t.continuation && t.step && t.step.response; +}; + +},{}],24:[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 + , parseLinkHeaderValue = require('./parse_link_header_value'); + +function JsonAdapter(log) { + this.log = log; +} + +JsonAdapter.mediaType = 'application/json'; + +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); + case 'link-header': + return this._handleLinkHeader(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); + } +}; + +JsonAdapter.prototype._handleLinkHeader = function(httpResponse, link) { + if (!httpResponse.headers.link) + throw createError('There was no link header in the last response.', + errors.InvalidArgumentError, link); + + var links = parseLinkHeaderValue(httpResponse.headers.link); + if (links[link.value]) { + return { url : links[link.value].url}; + } else { + throw createError('Link with relation ' + link.value + + ' not found in link header.', + errors.InvalidArgumentError, link); + } +}; + +module.exports = JsonAdapter; + +},{"./errors":21,"./parse_link_header_value":29,"jsonpath-plus":4,"minilog":13,"underscore.string":16}],25:[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":24,"./media_types":26,"./negotiation_adapter":28}],26:[function(require,module,exports){ +'use strict'; + +var JsonAdapter = require('./json_adapter'); + +module.exports = { + CONTENT_NEGOTIATION: 'content-negotiation', + JSON: JsonAdapter.mediaType, + JSON_HAL: 'application/hal+json', +}; + +},{"./json_adapter":24}],27:[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; + +},{}],28:[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":21}],29:[function(require,module,exports){ +var mergeRecursive = require('./merge_recursive'); + +module.exports = function parseLinkHeaderValue(linkHeader) { + if (!linkHeader) { + return null; + } + return linkHeader + .split(/,\s*]/g, ''); + var info = parts.reduce(createObjects, {}); + info.url = linkUrl; + return info; + } catch (e) { + return null; + } +} + +function createObjects(acc, p) { + // rel="next" => 1: rel 2: next + var m = p.match(/\s*(.+)\s*=\s*"?([^"]+)"?/); + if (m) acc[m[1]] = m[2]; + return acc; +} + +function hasRel(linkHeaderValuePart) { + return linkHeaderValuePart && linkHeaderValuePart.rel; +} + +function intoRels(acc, linkHeaderValuePart) { + function splitRel (rel) { + acc[rel] = mergeRecursive({ rel: rel }, linkHeaderValuePart); + } + + linkHeaderValuePart.rel.split(/\s+/).forEach(splitRel); + return acc; +} + +},{"./merge_recursive":27}],30:[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":13}],31:[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":21,"../is_continuation":23,"minilog":13}],32:[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":23,"minilog":13}],33:[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":23,"./convert_embedded_doc_to_response":34,"minilog":13}],34:[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":13}],35:[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]; + log.debug('found content type string', contentType); + var AdapterType = mediaTypeRegistry.get(contentType); + if (!AdapterType) { + log.error('no adapter for content type', contentType); + callback(createError('Unknown content type for content ' + + 'type detection: ' + contentType, errors.UnsupportedMediaType), t); + return false; + } + // switch to new Adapter depending on Content-Type header of server + t.adapter = new AdapterType(log); + log.debug('switched to media type adapter', + t.adapter.name || t.adapter.constructor.name); + } + return true; +}; + +},{"../errors":21,"../media_type_registry":25,"minilog":13}],36:[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 executing http request, ' + + 'processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +executeLastHttpRequest.isAsync = true; + +module.exports = executeLastHttpRequest; + +},{"../abort_traversal":18,"../http_requests":22,"minilog":13}],37:[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":18,"../http_requests":22,"minilog":13}],38:[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":13}],39:[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":13}],40:[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":21,"minilog":13,"url":17}],41:[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 executing http request'); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +fetchLastResource.isAsync = true; + +module.exports = fetchLastResource; + +},{"../abort_traversal":18,"../http_requests":22,"minilog":13}],42:[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 executing http request'); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +module.exports = fetchResource; + +}).call(this,require('_process')) +},{"../abort_traversal":18,"../http_requests":22,"../is_continuation":23,"_process":5,"minilog":13}],43:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , util = require('util') + , mergeRecursive = require('../merge_recursive'); + +module.exports = function getOptionsForStep(t) { + var options = t.requestOptions; + if (util.isArray(t.requestOptions)) { + options = t.requestOptions[t.step.index] || {}; + } + if (t.autoHeaders) { + addAutoHeaders(t, options); + } + log.debug('options', options); + return options; +}; + +function addAutoHeaders(t, options) { + var autoHeaderValue = + // we accept a static mediaType property as well as an instance property + t.adapter.constructor.mediaType || + t.adapter.mediaType; + + // The content negotiation adapter does not (and can not) provide a value + // for automatical Accept and Content-Type headers, in this case auto header + // value is undefined and we skip setting auto headers. This also happens + // arbitrary media type plug-ins that do not behave well and have no + // mediaType property. + if (autoHeaderValue) { + if (!options.headers) { + options.headers = createAutoHeaders(options, autoHeaderValue); + } else { + options.headers = + mergeRecursive( + createAutoHeaders(options, autoHeaderValue), + options.headers + ); + } + } +} + +function createAutoHeaders(options, autoHeaderValue) { + if (!options.form) { + // default: set Accept and Content-Type header + return { + 'Accept': autoHeaderValue, + 'Content-Type': autoHeaderValue, + }; + } else { + // if options.form is set, we only set the Accept header, but not not the + // Content-Type header. The Content-Type header is set automatically by + // request/request or by browser/lib/shim/request.js. + return { + 'Accept': autoHeaderValue + }; + } +} + +},{"../merge_recursive":27,"minilog":13,"util":14}],44:[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/traverson/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":21,"../is_continuation":23,"minilog":13}],45:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +var parseLinkHeaderValue = require('../parse_link_header_value'); + +module.exports = function parseLinkHeader(t) { + if (!t.step.doc || + !t.step.response || + !t.step.response.headers || + !t.step.response.headers.link) { + return true; + } + + try { + var links = parseLinkHeaderValue(t.step.response.headers.link); + t.step.doc._linkHeaders = links; + return true; + } catch (e) { + handleError(t, e); + return false; + } +}; + +function handleError(t, e) { + log.error('failed to parse link header'); + log.error(e); + t.callback(e); +} + +},{"../parse_link_header_value":29,"minilog":13}],46:[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":23}],47:[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":23}],48:[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":13,"underscore.string":16,"url":17}],49:[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":13,"underscore.string":16,"url-template":53,"util":14}],50:[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); + + 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":13}],51:[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":18,"./is_continuation":23,"./transforms/apply_transforms":30,"./transforms/check_http_status":31,"./transforms/fetch_resource":42,"./transforms/parse":44,"./transforms/reset_continuation":46,"./transforms/reset_last_step":47,"./transforms/resolve_next_url":48,"./transforms/resolve_uri_template":49,"./transforms/switch_to_next_step":50,"minilog":13}],52:[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/traverson/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":20,"./lib/errors":21,"./lib/media_type_registry":25,"./lib/media_types":26,"_process":5,"minilog":13}],53:[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(); +})); + +},{}],54:[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":52}]},{},["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..2d585e6 --- /dev/null +++ b/browser/dist/traverson-angular.external.min.js @@ -0,0 +1 @@ +require=function t(e,r,n){function o(i,a){if(!r[i]){if(!e[i]){var u="function"==typeof require&&require;if(!a&&u)return u(i,!0);if(s)return s(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var p=r[i]={exports:{}};e[i][0].call(p.exports,function(t){var r=e[i][1][t];return o(r||t)},p,p.exports,t,e,r,n)}return r[i].exports}for(var s="function"==typeof require&&require,i=0;i-1}),String.prototype.includes||(String.prototype.includes=function(t){return this.indexOf(t)>-1});var moveToAnotherArray=function(t,e,r){for(var n=0,o=t.length;n1&&c.shift(),this._hasParentSelector=null;var p=this._trace(c,e,["$"],a,u,r);return(p=p.filter(function(t){return t&&!t.isParentSelector})).length?1!==p.length||i||Array.isArray(p[0].value)?p.reduce(function(t,e){var r=o._getPreferredOutput(e);return s&&Array.isArray(r)?t=t.concat(r):t.push(r),t},[]):this._getPreferredOutput(p[0]):i?[]:void 0}},JSONPath.prototype._getPreferredOutput=function(t){var e=this.currResultType;switch(e){case"all":return t.path="string"==typeof t.path?t.path:JSONPath.toPathString(t.path),t;case"value":case"parent":case"parentProperty":return t[e];case"path":return JSONPath.toPathString(t[e]);case"pointer":return JSONPath.toPointer(t.path)}},JSONPath.prototype._handleCallback=function(t,e,r){if(e){var n=this._getPreferredOutput(t);t.path="string"==typeof t.path?t.path:JSONPath.toPathString(t.path),e(n,r,t)}},JSONPath.prototype._trace=function(t,e,r,n,o,s,i){function a(t){f.push(t)}function u(t){Array.isArray(t)?t.forEach(a):f.push(t)}var c,p=this;if(!t.length)return c={path:r,value:e,parent:n,parentProperty:o},this._handleCallback(c,s,"value"),c;var l=t[0],h=t.slice(1),f=[];if(("string"!=typeof l||i)&&e&&Object.prototype.hasOwnProperty.call(e,l))u(this._trace(h,e[l],push(r,l),e,l,s));else if("*"===l)this._walk(l,h,e,r,n,o,s,function(t,e,r,n,o,s,i,a){u(p._trace(unshift(t,r),n,o,s,i,a,!0))});else if(".."===l)u(this._trace(h,e,r,n,o,s)),this._walk(l,h,e,r,n,o,s,function(t,e,r,n,o,s,i,a){"object"==typeof n[t]&&u(p._trace(unshift(e,r),n[t],push(o,t),n,t,a))});else{if("^"===l)return this._hasParentSelector=!0,r.length?{path:r.slice(0,-1),expr:h,isParentSelector:!0}:[];if("~"===l)return c={path:push(r,l),value:o,parent:n,parentProperty:null},this._handleCallback(c,s,"property"),c;if("$"===l)u(this._trace(h,e,r,null,null,s));else if(/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(l))u(this._slice(l,h,e,r,n,o,s));else if(0===l.indexOf("?(")){if(this.currPreventEval)throw new Error("Eval [?(expr)] prevented in JSONPath expression.");this._walk(l,h,e,r,n,o,s,function(t,e,r,n,o,s,i,a){p._eval(e.replace(/^\?\((.*?)\)$/,"$1"),n[t],t,o,s,i)&&u(p._trace(unshift(t,r),n,o,s,i,a))})}else if("("===l[0]){if(this.currPreventEval)throw new Error("Eval [(expr)] prevented in JSONPath expression.");u(this._trace(unshift(this._eval(l,e,r[r.length-1],r.slice(0,-1),n,o),h),e,r,n,o,s))}else if("@"===l[0]){var d=!1,y=l.slice(1,-2);switch(y){case"scalar":e&&["object","function"].includes(typeof e)||(d=!0);break;case"boolean":case"string":case"undefined":case"function":typeof e===y&&(d=!0);break;case"number":typeof e===y&&isFinite(e)&&(d=!0);break;case"nonFinite":"number"!=typeof e||isFinite(e)||(d=!0);break;case"object":e&&typeof e===y&&(d=!0);break;case"array":Array.isArray(e)&&(d=!0);break;case"other":d=this.currOtherTypeCallback(e,r,n,o);break;case"integer":e!==+e||!isFinite(e)||e%1||(d=!0);break;case"null":null===e&&(d=!0)}if(d)return c={path:r,value:e,parent:n,parentProperty:o},this._handleCallback(c,s,"value"),c}else if("`"===l[0]&&e&&Object.prototype.hasOwnProperty.call(e,l.slice(1))){var v=l.slice(1);u(this._trace(h,e[v],push(r,v),e,v,s,!0))}else if(l.includes(",")){var g,m;for(g=l.split(","),m=0;m1)for(var r=1;r=2&&t._responseTimeoutTimer&&clearTimeout(t._responseTimeoutTimer),4==r){var n;try{n=e.status}catch(t){n=0}if(!n){if(t.timedout||t._aborted)return;return t.crossDomainError()}t.emit("end")}};var n=function(e,r){r.total>0&&(r.percent=r.loaded/r.total*100),r.direction=e,t.emit("progress",r)};if(this.hasListeners("progress"))try{e.onprogress=n.bind(null,"download"),e.upload&&(e.upload.onprogress=n.bind(null,"upload"))}catch(t){}try{this.username&&this.password?e.open(this.method,this.url,!0,this.username,this.password):e.open(this.method,this.url,!0)}catch(t){return this.callback(t)}if(this._withCredentials&&(e.withCredentials=!0),!this._formData&&"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof r&&!this._isHost(r)){var o=this._header["content-type"],s=this._serializer||m.serialize[o?o.split(";")[0]:""];!s&&u(o)&&(s=m.serialize["application/json"]),s&&(r=s(r))}for(var i in this.header)null!=this.header[i]&&this.header.hasOwnProperty(i)&&e.setRequestHeader(i,this.header[i]);return this._responseType&&(e.responseType=this._responseType),this.emit("request",this),e.send(void 0!==r?r:null),this},m.agent=function(){return new g},["GET","POST","OPTIONS","PATCH","PUT","DELETE"].forEach(function(t){g.prototype[t.toLowerCase()]=function(e,r){var n=new m.Request(t,e);return this._setDefaults(n),r&&n.end(r),n}}),g.prototype.del=g.prototype.delete,m.get=function(t,e,r){var n=m("GET",t);return"function"==typeof e&&(r=e,e=null),e&&n.query(e),r&&n.end(r),n},m.head=function(t,e,r){var n=m("HEAD",t);return"function"==typeof e&&(r=e,e=null),e&&n.query(e),r&&n.end(r),n},m.options=function(t,e,r){var n=m("OPTIONS",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n},m.del=l,m.delete=l,m.patch=function(t,e,r){var n=m("PATCH",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n},m.post=function(t,e,r){var n=m("POST",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n},m.put=function(t,e,r){var n=m("PUT",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n}},{"./agent-base":7,"./is-object":9,"./request-base":10,"./response-base":11,"component-emitter":2}],9:[function(t,e,r){"use strict";e.exports=function(t){return null!==t&&"object"==typeof t}},{}],10:[function(t,e,r){"use strict";function n(t){if(t)return o(t)}function o(t){for(var e in n.prototype)t[e]=n.prototype[e];return t}var s=t("./is-object");e.exports=n,n.prototype.clearTimeout=function(){return clearTimeout(this._timer),clearTimeout(this._responseTimeoutTimer),delete this._timer,delete this._responseTimeoutTimer,this},n.prototype.parse=function(t){return this._parser=t,this},n.prototype.responseType=function(t){return this._responseType=t,this},n.prototype.serialize=function(t){return this._serializer=t,this},n.prototype.timeout=function(t){if(!t||"object"!=typeof t)return this._timeout=t,this._responseTimeout=0,this;for(var e in t)switch(e){case"deadline":this._timeout=t.deadline;break;case"response":this._responseTimeout=t.response;break;default:console.warn("Unknown timeout option",e)}return this},n.prototype.retry=function(t,e){return 0!==arguments.length&&!0!==t||(t=1),t<=0&&(t=0),this._maxRetries=t,this._retries=0,this._retryCallback=e,this};var i=["ECONNRESET","ETIMEDOUT","EADDRINFO","ESOCKETTIMEDOUT"];n.prototype._shouldRetry=function(t,e){if(!this._maxRetries||this._retries++>=this._maxRetries)return!1;if(this._retryCallback)try{var r=this._retryCallback(t,e);if(!0===r)return!0;if(!1===r)return!1}catch(t){console.error(t)}if(e&&e.status&&e.status>=500&&501!=e.status)return!0;if(t){if(t.code&&~i.indexOf(t.code))return!0;if(t.timeout&&"ECONNABORTED"==t.code)return!0;if(t.crossDomain)return!0}return!1},n.prototype._retry=function(){return this.clearTimeout(),this.req&&(this.req=null,this.req=this.request()),this._aborted=!1,this.timedout=!1,this._end()},n.prototype.then=function(t,e){if(!this._fullfilledPromise){var r=this;this._endCalled&&console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"),this._fullfilledPromise=new Promise(function(t,e){r.end(function(r,n){r?e(r):t(n)})})}return this._fullfilledPromise.then(t,e)},n.prototype.catch=function(t){return this.then(void 0,t)},n.prototype.use=function(t){return t(this),this},n.prototype.ok=function(t){if("function"!=typeof t)throw Error("Callback required");return this._okCallback=t,this},n.prototype._isResponseOK=function(t){return!!t&&(this._okCallback?this._okCallback(t):t.status>=200&&t.status<300)},n.prototype.get=function(t){return this._header[t.toLowerCase()]},n.prototype.getHeader=n.prototype.get,n.prototype.set=function(t,e){if(s(t)){for(var r in t)this.set(r,t[r]);return this}return this._header[t.toLowerCase()]=e,this.header[t]=e,this},n.prototype.unset=function(t){return delete this._header[t.toLowerCase()],delete this.header[t],this},n.prototype.field=function(t,e){if(null===t||void 0===t)throw new Error(".field(name, val) name can not be empty");if(this._data&&console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"),s(t)){for(var r in t)this.field(r,t[r]);return this}if(Array.isArray(e)){for(var n in e)this.field(t,e[n]);return this}if(null===e||void 0===e)throw new Error(".field(name, val) val can not be empty");return"boolean"==typeof e&&(e=""+e),this._getFormData().append(t,e),this},n.prototype.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)},n.prototype._auth=function(t,e,r,n){switch(r.type){case"basic":this.set("Authorization","Basic "+n(t+":"+e));break;case"auto":this.username=t,this.password=e;break;case"bearer":this.set("Authorization","Bearer "+t)}return this},n.prototype.withCredentials=function(t){return void 0==t&&(t=!0),this._withCredentials=t,this},n.prototype.redirects=function(t){return this._maxRedirects=t,this},n.prototype.maxResponseSize=function(t){if("number"!=typeof t)throw TypeError("Invalid argument");return this._maxResponseSize=t,this},n.prototype.toJSON=function(){return{method:this.method,url:this.url,data:this._data,headers:this._header}},n.prototype.send=function(t){var e=s(t),r=this._header["content-type"];if(this._formData&&console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"),e&&!this._data)Array.isArray(t)?this._data=[]:this._isHost(t)||(this._data={});else if(t&&this._data&&this._isHost(this._data))throw Error("Can't merge these send calls");if(e&&s(this._data))for(var n in t)this._data[n]=t[n];else"string"==typeof t?(r||this.type("form"),r=this._header["content-type"],this._data="application/x-www-form-urlencoded"==r?this._data?this._data+"&"+t:t:(this._data||"")+t):this._data=t;return!e||this._isHost(t)?this:(r||this.type("json"),this)},n.prototype.sortQuery=function(t){return this._sort=void 0===t||t,this},n.prototype._finalizeQueryString=function(){var t=this._query.join("&");if(t&&(this.url+=(this.url.indexOf("?")>=0?"&":"?")+t),this._query.length=0,this._sort){var e=this.url.indexOf("?");if(e>=0){var r=this.url.substring(e+1).split("&");"function"==typeof this._sort?r.sort(this._sort):r.sort(),this.url=this.url.substring(0,e)+"?"+r.join("&")}}},n.prototype._appendQueryString=function(){console.trace("Unsupported")},n.prototype._timeoutError=function(t,e,r){if(!this._aborted){var n=new Error(t+e+"ms exceeded");n.timeout=e,n.code="ECONNABORTED",n.errno=r,this.timedout=!0,this.abort(),this.callback(n)}},n.prototype._setTimeouts=function(){var t=this;this._timeout&&!this._timer&&(this._timer=setTimeout(function(){t._timeoutError("Timeout of ",t._timeout,"ETIME")},this._timeout)),this._responseTimeout&&!this._responseTimeoutTimer&&(this._responseTimeoutTimer=setTimeout(function(){t._timeoutError("Response timeout of ",t._responseTimeout,"ETIMEDOUT")},this._responseTimeout))}},{"./is-object":9}],11:[function(t,e,r){"use strict";function n(t){if(t)return o(t)}function o(t){for(var e in n.prototype)t[e]=n.prototype[e];return t}var s=t("./utils");e.exports=n,n.prototype.get=function(t){return this.header[t.toLowerCase()]},n.prototype._setHeaderProperties=function(t){var e=t["content-type"]||"";this.type=s.type(e);var r=s.params(e);for(var n in r)this[n]=r[n];this.links={};try{t.link&&(this.links=s.parseLinks(t.link))}catch(t){}},n.prototype._setStatusProperties=function(t){var e=t/100|0;this.status=this.statusCode=t,this.statusType=e,this.info=1==e,this.ok=2==e,this.redirect=3==e,this.clientError=4==e,this.serverError=5==e,this.error=(4==e||5==e)&&this.toError(),this.created=201==t,this.accepted=202==t,this.noContent=204==t,this.badRequest=400==t,this.unauthorized=401==t,this.notAcceptable=406==t,this.forbidden=403==t,this.notFound=404==t,this.unprocessableEntity=422==t}},{"./utils":12}],12:[function(t,e,r){"use strict";r.type=function(t){return t.split(/ *; */).shift()},r.params=function(t){return t.split(/ *; */).reduce(function(t,e){var r=e.split(/ *= */),n=r.shift(),o=r.shift();return n&&o&&(t[n]=o),t},{})},r.parseLinks=function(t){return t.split(/ *, */).reduce(function(t,e){var r=e.split(/ *; */),n=r[0].slice(1,-1);return t[r[1].split(/ *= */)[1].slice(1,-1)]=n,t},{})},r.cleanHeader=function(t,e){return delete t["content-type"],delete t["content-length"],delete t["transfer-encoding"],delete t.host,e&&(delete t.authorization,delete t.cookie),t}},{}],13:[function(t,e,r){"use strict";function n(t){null==t&&(t=""),this.id=t}function o(t){return new n(t)}var s=!1;n.prototype.enable=function(){this.enabled=!0},n.prototype.debug=function(t){s&&console.log(this.id+"/debug: "+t)},n.prototype.info=function(t){s&&console.log(this.id+"/info: "+t)},n.prototype.warn=function(t){s&&console.log(this.id+"/warn: "+t)},n.prototype.error=function(t){s&&console.log(this.id+"/error: "+t)},o.enable=function(){s=!0},e.exports=o},{}],14:[function(t,e,r){"use strict";e.exports={isArray:function(t){return null!=t&&"[object Array]"===Object.prototype.toString.call(t)}}},{}],15:[function(t,e,r){"use strict";function n(){}function o(t,e){return e=e||{},s(t,e),i(t,e),a(t,e),u(t,e),c(t,e),p(t,e),t}function s(t,e){var r=e.qs;null!=r&&(t=t.query(r))}function i(t,e){var r=e.headers;null!=r&&(t=t.set(r))}function a(t,e){var r=e.auth;null!=r&&(t=t.auth(r.user||r.username,r.pass||r.password))}function u(t,e){if(null!=e){var r=e.body;null!=r&&(t=t.send(r))}}function c(t,e){if(null!=e){var r=e.form;null!=r&&(t=(t=t.set("content-type","application/x-www-form-urlencoded")).send(r))}}function p(t,e){null!=e&&!0===e.withCredentials&&t.withCredentials()}function l(t){return t.body=t.text,t.statusCode=t.status,t}function h(t){return function(e,r){if(e){if(!r)return t(e);t(null,l(r))}else t(null,l(r))}}var f=t("superagent");n.prototype.get=function(t,e,r){return o(f.get(t),e).end(h(r))},n.prototype.post=function(t,e,r){return o(f.post(t),e).end(h(r))},n.prototype.put=function(t,e,r){return o(f.put(t),e).end(h(r))},n.prototype.patch=function(t,e,r){return o(f.patch(t),e).end(h(r))},n.prototype.del=function(t,e,r){return o(f.del(t),e).end(h(r))},e.exports=new n},{superagent:8}],16:[function(t,e,r){"use strict";var n={startsWith:function(t,e){return""===e||null!=t&&null!=e&&(t=String(t),e=String(e),t.length>=e.length&&t.slice(0,e.length)===e)},endsWith:function(t,e){return""===e||null!=t&&null!=e&&(t=String(t),e=String(e),t.length>=e.length&&t.slice(t.length-e.length)===e)},splice:function(t,e,r,o){var s=n.chars(t);return s.splice(~~e,~~r,o),s.join("")},contains:function(t,e){return""===e||null!=t&&-1!==String(t).indexOf(e)},chars:function(t){return null==t?[]:String(t).split("")}};e.exports=n},{}],17:[function(t,e,r){"use strict";var n=t("resolve-url");r.resolve=function(t,e){return n(t,e)}},{"resolve-url":6}],18:[function(t,e,r){"use strict";var n=t("minilog"),o=t("./errors"),s=o.errors,i=o.createError,a=n("traverson");r.abortTraversal=function(){a.debug("aborting link traversal"),this.aborted=!0,this.currentRequest&&(a.debug("request in progress. trying to abort it, too."),this.currentRequest.abort())},r.registerAbortListener=function(t,e){t.currentRequest&&t.currentRequest.on("abort",function(){r.callCallbackOnAbort(t)})},r.callCallbackOnAbort=function(t){a.debug("link traversal aborted"),t.callbackHasBeenCalledAfterAbort||(t.callbackHasBeenCalledAfterAbort=!0,t.callback(r.abortError(),t))},r.abortError=function(){var t=i("Link traversal process has been aborted.",s.TraversalAbortedError);return t.aborted=!0,t}},{"./errors":21,minilog:13}],19:[function(t,e,r){"use strict";function n(t,e,r,n){var o;o=t.convertResponseToObject?[y,i,v,g,p]:[d],t.lastMethod=r,s.walk(t,o,n)}function o(t){return{abort:t.abortTraversal}}t("minilog")("traverson"),t("./abort_traversal"),t("./transforms/apply_transforms"),t("./http_requests"),t("./is_continuation");var s=t("./walker"),i=t("./transforms/check_http_status"),a=t("./transforms/continuation_to_doc"),u=t("./transforms/continuation_to_response"),c=t("./transforms/convert_embedded_doc_to_response"),p=t("./transforms/extract_doc"),l=t("./transforms/extract_response"),h=t("./transforms/extract_url"),f=t("./transforms/fetch_last_resource"),d=t("./transforms/execute_last_http_request"),y=t("./transforms/execute_http_request"),v=t("./transforms/parse"),g=t("./transforms/parse_link_header");r.get=function(t,e){var r;return r=t.convertResponseToObject?[a,f,i,v,g,p]:[u,f,c,l],s.walk(t,r,e),o(t)},r.getUrl=function(t,e){return s.walk(t,[h],e),o(t)},r.post=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.post,e),o(t)},r.put=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.put,e),o(t)},r.patch=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.patch,e),o(t)},r.delete=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.del,e),o(t)}},{"./abort_traversal":18,"./http_requests":22,"./is_continuation":23,"./transforms/apply_transforms":30,"./transforms/check_http_status":31,"./transforms/continuation_to_doc":32,"./transforms/continuation_to_response":33,"./transforms/convert_embedded_doc_to_response":34,"./transforms/execute_http_request":36,"./transforms/execute_last_http_request":37,"./transforms/extract_doc":38,"./transforms/extract_response":39,"./transforms/extract_url":40,"./transforms/fetch_last_resource":41,"./transforms/parse":44,"./transforms/parse_link_header":45,"./walker":51,minilog:13}],20:[function(t,e,r){"use strict";function n(t,e){this.mediaType=t||T.CONTENT_NEGOTIATION,this.linkType=e||"link-rel",this.adapter=this._createAdapter(this.mediaType),this.autoHeaders=!0,this.contentNegotiation=!0,this.convertResponseToObjectFlag=!1,this.links=[],this.jsonParser=JSON.parse,this.requestModuleInstance=d,this.requestOptions={},this.resolveRelativeFlag=!1,this.preferEmbedded=!1,this.lastTraversalState=null,this.continuation=null}function o(t,e){for(var r=0;r continuing finished traversal process"),t.continuation={step:e.step,action:n},t.continuation.step.index=0,c(t,e),t}})}}function c(t,e){t.aborted=!1,t.adapter=e.adapter,t.body=e.body,t.callbackHasBeenCalledAfterAbort=!1,t.autoHeaders=e.autoHeaders,t.contentNegotiation=e.contentNegotiation,t.convertResponseToObjectFlag=e.convertResponseToObject,t.links=[],t.jsonParser=e.jsonParser,t.requestModuleInstance=e.requestModuleInstance,t.requestOptions=e.requestOptions,t.resolveRelativeFlag=e.resolveRelative,t.preferEmbedded=e.preferEmbedded,t.startUrl=e.startUrl,t.templateParameters=e.templateParameters}function p(t){return y.isArray(t)?h(t):"object"==typeof t?l(t):t}function l(t){return O(null,t)}function h(t){return t?t.slice(0):t}var f=t("minilog"),d=t("request"),y=t("util"),v=t("./actions"),g=t("./abort_traversal").abortTraversal,m=t("./errors"),b=m.errors,_=m.createError,w=t("./media_type_registry"),T=t("./media_types"),O=t("./merge_recursive"),x=f("traverson");n.prototype._createAdapter=function(t){var e=w.get(t);if(!e)throw _("Unknown or unsupported media type: "+t,b.UnsupportedMediaType);return x.debug("creating new "+e.name),new e(x)},n.prototype.newRequest=function(){var t=new n(this.getMediaType(),this.getLinkType());return t.useAutoHeaders(this.setsAutoHeaders()),t.contentNegotiation=this.doesContentNegotiation(),t.convertResponseToObject(this.convertsResponseToObject()),t.from(h(this.getFrom())),t.withTemplateParameters(p(this.getTemplateParameters())),t.withRequestOptions(p(this.getRequestOptions())),t.withRequestLibrary(this.getRequestLibrary()),t.parseResponseBodiesWith(this.getJsonParser()),t.resolveRelative(this.doesResolveRelative()),t.preferEmbeddedResources(this.doesPreferEmbeddedResources()),t.continuation=this.continuation,t},n.prototype.setMediaType=function(t){return this.mediaType=t||T.CONTENT_NEGOTIATION,this.adapter=this._createAdapter(t),this.contentNegotiation=t===T.CONTENT_NEGOTIATION,this},n.prototype.getLinkType=function(){return this.linkType},n.prototype.json=function(){return this.setMediaType(T.JSON),this},n.prototype.jsonHal=function(){return this.setMediaType(T.JSON_HAL),this},n.prototype.useContentNegotiation=function(){return this.setMediaType(T.CONTENT_NEGOTIATION),this.contentNegotiation=!0,this},n.prototype.from=function(t){return this.startUrl=t,this},n.prototype.follow=function(){for(var t=Array.prototype.slice.apply(1===arguments.length&&y.isArray(arguments[0])?arguments[0]:arguments),e=0;e1?c("JSONPath expression "+e+" returned more than one match in document:\n"+JSON.stringify(t),u.JSONPathError,t):c("JSONPath expression "+e+" returned no match in document:\n"+JSON.stringify(t),u.JSONPathError,t)},n.prototype._handleHeader=function(t,e){switch(e.value){case"location":var r=t.headers.location;if(!r)throw c("Following the location header but there was no location header in the last response.",u.LinkError,t.headers);return{url:r};default:throw c("Link objects with type header and value "+e.value+" are not supported by this adapter.",u.InvalidArgumentError,e)}},n.prototype._handleLinkHeader=function(t,e){if(!t.headers.link)throw c("There was no link header in the last response.",u.InvalidArgumentError,e);var r=p(t.headers.link);if(r[e.value])return{url:r[e.value].url};throw c("Link with relation "+e.value+" not found in link header.",u.InvalidArgumentError,e)},e.exports=n},{"./errors":21,"./parse_link_header_value":29,"jsonpath-plus":4,minilog:13,"underscore.string":16}],25:[function(t,e,r){"use strict";var n=t("./media_types"),o={};r.register=function(t,e){o[t]=e},r.get=function(t){return o[t]},r.register(n.CONTENT_NEGOTIATION,t("./negotiation_adapter")),r.register(n.JSON,t("./json_adapter"))},{"./json_adapter":24,"./media_types":26,"./negotiation_adapter":28}],26:[function(t,e,r){"use strict";var n=t("./json_adapter");e.exports={CONTENT_NEGOTIATION:"content-negotiation",JSON:n.mediaType,JSON_HAL:"application/hal+json"}},{"./json_adapter":24}],27:[function(t,e,r){"use strict";function n(t,e){!t&&e&&(t={});for(var r in e)e.hasOwnProperty(r)&&o(t,e,r);return t}function o(t,e,r){"object"==typeof e[r]?(t[r]&&"object"==typeof t[r]||(t[r]={}),n(t[r],e[r])):t[r]=e[r]}e.exports=n},{}],28:[function(t,e,r){"use strict";function n(t){}var o=t("./errors"),s=o.errors,i=o.createError;n.prototype.findNextStep=function(t,e){throw i("Content negotiation did not happen",s.InvalidStateError)},e.exports=n},{"./errors":21}],29:[function(t,e,r){function n(t){try{var e=t.split(";"),r=e.shift().replace(/[<>]/g,""),n=e.reduce(o,{});return n.url=r,n}catch(t){return null}}function o(t,e){var r=e.match(/\s*(.+)\s*=\s*"?([^"]+)"?/);return r&&(t[r[1]]=r[2]),t}function s(t){return t&&t.rel}function i(t,e){return e.rel.split(/\s+/).forEach(function(r){t[r]=a({rel:r},e)}),t}var a=t("./merge_recursive");e.exports=function(t){return t?t.split(/,\s*=300)){var r=n(t.step.url,e,t.step.response.body);return o.error("unexpected http status code"),o.error(r),t.callback(r),!1}return o.debug("http status code ok ("+e+")"),!0}},{"../errors":21,"../is_continuation":23,minilog:13}],32:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("../is_continuation");e.exports=function(t){return!o(t)||(n.debug("continuing from last traversal process (actions)"),t.continuation=null,t.callback(null,t.step.doc),!1)}},{"../is_continuation":23,minilog:13}],33:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("./convert_embedded_doc_to_response"),s=t("../is_continuation");e.exports=function(t){return!s(t)||(n.debug("continuing from last traversal process (actions)"),t.continuation=null,o(t),t.callback(null,t.step.response),!1)}},{"../is_continuation":23,"./convert_embedded_doc_to_response":34,minilog:13}],34:[function(t,e,r){"use strict";var n=t("minilog")("traverson");e.exports=function(t){return!t.step.response&&t.step.doc&&(n.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."}),!0}},{minilog:13}],35:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("../media_type_registry"),s=t("../errors"),i=s.errors,a=s.createError;e.exports=function(t,e){if(t.contentNegotiation&&t.step.response&&t.step.response.headers&&t.step.response.headers["content-type"]){var r=t.step.response.headers["content-type"].split(/[; ]/)[0];n.debug("found content type string",r);var s=o.get(r);if(!s)return n.error("no adapter for content type",r),e(a("Unknown content type for content type detection: "+r,i.UnsupportedMediaType),t),!1;t.adapter=new s(n),n.debug("switched to media type adapter",t.adapter.name||t.adapter.constructor.name)}return!0}},{"../errors":21,"../media_type_registry":25,minilog:13}],36:[function(t,e,r){"use strict";function n(t,e){if(t.aborted)return s.callCallbackOnAbort(t);i.executeHttpRequest(t,t.requestModuleInstance,t.lastMethod,function(r,n){if(r)return r.aborted||(o.debug("error while executing http request, processing step ",t.step),o.error(r)),t.callback(r);e(t)})}var o=t("minilog")("traverson"),s=t("../abort_traversal"),i=t("../http_requests");n.isAsync=!0,e.exports=n},{"../abort_traversal":18,"../http_requests":22,minilog:13}],37:[function(t,e,r){"use strict";function n(t,e){if(t.aborted)return o.callCallbackOnAbort(t);s.executeHttpRequest(t,t.requestModuleInstance,t.lastMethod,t.callback)}t("minilog")("traverson");var o=t("../abort_traversal"),s=t("../http_requests");n.isAsync=!0,e.exports=n},{"../abort_traversal":18,"../http_requests":22,minilog:13}],38:[function(t,e,r){"use strict";var n=t("minilog")("traverson");e.exports=function(t){return n.debug("walker.walk has finished"),t.callback(null,t.step.doc),!1}},{minilog:13}],39:[function(t,e,r){"use strict";var n=t("minilog")("traverson");e.exports=function(t){return n.debug("walker.walk has finished"),t.callback(null,t.step.response),!1}},{minilog:13}],40:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("url"),s=t("../errors"),i=s.errors,a=s.createError;e.exports=function(t){return n.debug("walker.walk has finished"),t.step.url?t.callback(null,t.step.url):t.step.doc&&t.step.doc._links&&t.step.doc._links.self&&t.step.doc._links.self.href?t.callback(null,o.resolve(t.startUrl,t.step.doc._links.self.href)):t.callback(a('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"',i.LinkError))}},{"../errors":21,minilog:13,url:17}],41:[function(t,e,r){"use strict";function n(t,e){if(t.aborted)return s.callCallbackOnAbort(t);i.fetchResource(t,function(t,r){if(o.debug("fetchResource returned (fetchLastResource)."),t)return t.aborted||(o.debug("error while executing http request"),o.error(t)),r.callback(t);e(r)})}var o=t("minilog")("traverson"),s=t("../abort_traversal"),i=t("../http_requests");n.isAsync=!0,e.exports=n},{"../abort_traversal":18,"../http_requests":22,minilog:13}],42:[function(t,e,r){(function(r){"use strict";function n(t,e){u(t)?o(t,e):s(t,e)}function o(t,e){i.debug("continuing from last traversal process (walker)"),r.nextTick(function(){e(t)})}function s(t,e){if(t.aborted)return a.callCallbackOnAbort(t);c.fetchResource(t,function(t,r){if(i.debug("fetchResource returned"),t)return t.aborted||(i.debug("error while executing http request"),i.error(t)),r.callback(t);e(r)})}var i=t("minilog")("traverson"),a=t("../abort_traversal"),u=t("../is_continuation"),c=t("../http_requests");n.isAsync=!0,e.exports=n}).call(this,t("_process"))},{"../abort_traversal":18,"../http_requests":22,"../is_continuation":23,_process:5,minilog:13}],43:[function(t,e,r){"use strict";function n(t,e){var r=t.adapter.constructor.mediaType||t.adapter.mediaType;r&&(e.headers?e.headers=a(o(e,r),e.headers):e.headers=o(e,r))}function o(t,e){return t.form?{Accept:e}:{Accept:e,"Content-Type":e}}var s=t("minilog")("traverson"),i=t("util"),a=t("../merge_recursive");e.exports=function(t){var e=t.requestOptions;return i.isArray(t.requestOptions)&&(e=t.requestOptions[t.step.index]||{}),t.autoHeaders&&n(t,e),s.debug("options",e),e}},{"../merge_recursive":27,minilog:13,util:14}],44:[function(t,e,r){"use strict";function n(t){i.debug("parsing response body"),t.requestOptions&&t.requestOptions.jsonReviver?t.step.doc=t.step.response.body:t.step.doc=t.jsonParser(t.step.response.body)}function o(t,e){var r=e;"SyntaxError"===e.name&&(r=s(t.step.url,t.step.response.body)),i.error("parsing failed"),i.error(r),t.callback(r)}function s(t,e){var r=c("The document at "+t+" could not be parsed as JSON: "+e,u.JSONError,e);return r.url=t,r.body=e,r}var i=t("minilog")("traverson"),a=t("../errors"),u=a.errors,c=a.createError,p=t("../is_continuation");e.exports=function(t){if(p(t)&&(i.debug("continuing from last traversal process (transforms/parse)"),"getResource"===t.continuation.action))return!0;if(t.step.doc)return i.debug("no parsing necessary, probably an embedded document"),!0;try{return n(t),!0}catch(e){return o(t,e),!1}}},{"../errors":21,"../is_continuation":23,minilog:13}],45:[function(t,e,r){"use strict";function n(t,e){o.error("failed to parse link header"),o.error(e),t.callback(e)}var o=t("minilog")("traverson"),s=t("../parse_link_header_value");e.exports=function(t){if(!(t.step.doc&&t.step.response&&t.step.response.headers&&t.step.response.headers.link))return!0;try{var e=s(t.step.response.headers.link);return t.step.doc._linkHeaders=e,!0}catch(e){return n(t,e),!1}}},{"../parse_link_header_value":29,minilog:13}],46:[function(t,e,r){"use strict";var n=t("../is_continuation");e.exports=function(t){return!!n(t)||(t.continuation=null,!0)}},{"../is_continuation":23}],47:[function(t,e,r){"use strict";var n=t("../is_continuation");e.exports=function(t){return!!n(t)||(t.lastStep=null,!0)}},{"../is_continuation":23}],48:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("underscore.string"),s=t("url"),i=/https?:\/\//i;e.exports=function(t){return t.step.url&&0!==t.step.url.search(i)&&(n.debug("found non full qualified URL"),t.resolveRelative&&t.lastStep&&t.lastStep.url?(n.debug("resolving URL relative"),o.startsWith(t.step.url,"/")&&o.endsWith(t.lastStep.url,"/")&&(t.step.url=o.splice(t.step.url,0,1)),t.step.url=t.lastStep.url+t.step.url):t.step.url=s.resolve(t.startUrl,t.step.url)),!0}},{minilog:13,"underscore.string":16,url:17}],49:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("underscore.string"),s=t("url-template"),i=t("util");e.exports=function(t){if(t.step.url){var e=t.templateParameters;if(i.isArray(e)&&(e=e[t.step.index]),e=e||{},o.contains(t.step.url,"{")){n.debug("resolving URI template");var r=s.parse(t.step.url).expand(e);n.debug("resolved to ",r),t.step.url=r}}return!0}},{minilog:13,"underscore.string":16,"url-template":53,util:14}],50:[function(t,e,r){"use strict";function n(t,e){try{return t.adapter.findNextStep(t,e)}catch(e){return o.error("could not find next step"),o.error(e),t.callback(e),null}}var o=t("minilog")("traverson");e.exports=function(t){var e=t.links[t.step.index];return o.debug("next link: "+e),t.lastStep=t.step,t.step=n(t,e),!!t.step&&(o.debug("found next step",t.step),t.step.index=t.lastStep.index+1,!0)}},{minilog:13}],51:[function(t,e,r){"use strict";function n(t,e){if(i.debug("processing next step"),o(t)&&!s(t))u(p,t,function(t){i.debug("successfully processed step"),n(t,e)});else{if(s(t))return a.callCallbackOnAbort(t);i.debug("link array exhausted"),u(e,t,function(t){return t.callback()})}}function o(t){return t.step.index=100&&e.status<600?t(null,p(e)):t(e)}}return r.prototype.get=function(e,r,s){return r=o(r),t.get(e,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.post=function(e,r,s){return r=o(r),t.post(e,r.data,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.put=function(e,r,s){return r=o(r),t.put(e,r.data,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.patch=function(e,r,s){return r=o(r),t.patch(e,r.data,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.del=function(e,r,s){return r=o(r),t.delete(e,r).then(l(s)).catch(h(s)),new n(r.timeout)},new r}]),n.prototype.abort=function(){this.abortPromise.resolve(),this.listeners.forEach(function(t){t.call()})},n.prototype.on=function(t,e){if("abort"!==t){var r=new Error("Event "+t+" not supported");throw r.name="InvalidArgumentError",r}this.listeners.push(e)},e.exports=i},{angular:1,traverson:52}]},{},["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..87d0996 --- /dev/null +++ b/browser/dist/traverson-angular.js @@ -0,0 +1,5912 @@ +(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; + }; +} +if (!String.prototype.includes) { + String.prototype.includes = function (item) { // eslint-disable-line no-extend-native + return this.indexOf(item) > -1; + }; +} + +var moveToAnotherArray = function (source, target, conditionCb) { + for (var i = 0, kl = source.length; i < kl; i++) { + var key = source[i]; + if (conditionCb(key)) { + target.push(source.splice(i--, 1)[0]); + } + } +}; + +var vm = isNode + ? require('vm') : { + runInNewContext: function (expr, context) { + var keys = Object.keys(context); + var funcs = []; + moveToAnotherArray(keys, funcs, function (key) { + return typeof context[key] === 'function'; + }); + var code = funcs.reduce(function (s, func) { + return 'var ' + func + '=' + context[func].toString() + ';' + s; + }, ''); + code += keys.reduce(function (s, vr) { + return 'var ' + vr + '=' + JSON.stringify(context[vr]).replace(/\u2028|\u2029/g, function (m) { + // http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/ + return '\\u202' + (m === '\u2028' ? '8' : '9'); + }) + ';' + s; + }, expr); + return eval(code); + } + }; + +function push (arr, elem) {arr = arr.slice(); arr.push(elem); return arr;} +function unshift (elem, arr) {arr = arr.slice(); arr.unshift(elem); return arr;} +function NewError (value) { + this.avoidNew = true; + this.value = value; + this.message = 'JSONPath should not be called with "new" (it prevents return of (unwrapped) scalar values)'; +} + +function JSONPath (opts, expr, obj, callback, otherTypeCallback) { + if (!(this instanceof JSONPath)) { + try { + return new JSONPath(opts, expr, obj, callback, otherTypeCallback); + } + catch (e) { + if (!e.avoidNew) { + throw e; + } + return e.value; + } + } + + if (typeof opts === 'string') { + otherTypeCallback = callback; + callback = obj; + obj = expr; + expr = opts; + opts = {}; + } + opts = opts || {}; + var objArgs = opts.hasOwnProperty('json') && opts.hasOwnProperty('path'); + this.json = opts.json || obj; + this.path = opts.path || expr; + this.resultType = (opts.resultType && opts.resultType.toLowerCase()) || 'value'; + this.flatten = opts.flatten || false; + this.wrap = opts.hasOwnProperty('wrap') ? opts.wrap : true; + this.sandbox = opts.sandbox || {}; + this.preventEval = opts.preventEval || false; + this.parent = opts.parent || null; + this.parentProperty = opts.parentProperty || null; + this.callback = opts.callback || callback || null; + this.otherTypeCallback = opts.otherTypeCallback || otherTypeCallback || function () { + throw new Error('You must supply an otherTypeCallback callback option with the @other() operator.'); + }; + + if (opts.autostart !== false) { + var ret = this.evaluate({ + path: (objArgs ? opts.path : expr), + json: (objArgs ? opts.json : obj) + }); + if (!ret || typeof ret !== 'object') { + throw new NewError(ret); + } + return ret; + } +} + +// PUBLIC METHODS + +JSONPath.prototype.evaluate = function (expr, json, callback, otherTypeCallback) { + var self = this, + flatten = this.flatten, + wrap = this.wrap, + currParent = this.parent, + currParentProperty = this.parentProperty; + + this.currResultType = this.resultType; + this.currPreventEval = this.preventEval; + this.currSandbox = this.sandbox; + callback = callback || this.callback; + this.currOtherTypeCallback = otherTypeCallback || this.otherTypeCallback; + + json = json || this.json; + expr = expr || this.path; + if (expr && typeof expr === 'object') { + if (!expr.path) { + throw new Error('You must supply a "path" property when providing an object argument to JSONPath.evaluate().'); + } + json = expr.hasOwnProperty('json') ? expr.json : json; + flatten = expr.hasOwnProperty('flatten') ? expr.flatten : flatten; + this.currResultType = expr.hasOwnProperty('resultType') ? expr.resultType : this.currResultType; + this.currSandbox = expr.hasOwnProperty('sandbox') ? expr.sandbox : this.currSandbox; + wrap = expr.hasOwnProperty('wrap') ? expr.wrap : wrap; + this.currPreventEval = expr.hasOwnProperty('preventEval') ? expr.preventEval : this.currPreventEval; + callback = expr.hasOwnProperty('callback') ? expr.callback : callback; + this.currOtherTypeCallback = expr.hasOwnProperty('otherTypeCallback') ? expr.otherTypeCallback : this.currOtherTypeCallback; + currParent = expr.hasOwnProperty('parent') ? expr.parent : currParent; + currParentProperty = expr.hasOwnProperty('parentProperty') ? expr.parentProperty : currParentProperty; + expr = expr.path; + } + currParent = currParent || null; + currParentProperty = currParentProperty || null; + + if (Array.isArray(expr)) { + expr = JSONPath.toPathString(expr); + } + if (!expr || !json || !allowedResultTypes.includes(this.currResultType)) { + return; + } + this._obj = json; + + var exprList = JSONPath.toPathArray(expr); + if (exprList[0] === '$' && exprList.length > 1) {exprList.shift();} + this._hasParentSelector = null; + 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, literalPriority) { + // 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 retPush (elem) { + ret.push(elem); + } + function addRet (elems) { + if (Array.isArray(elems)) { + elems.forEach(retPush); + } else { + ret.push(elems); + } + } + + if ((typeof loc !== 'string' || literalPriority) && 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, true)); + }); + } + 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)); + } + }); + } + // 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 + this._hasParentSelector = true; + 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 (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // [start:end:step] Python slice syntax + addRet(this._slice(loc, x, val, path, parent, parentPropName, 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[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)); + } + 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'].includes(typeof val))) { + 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 (loc[0] === '`' && val && Object.prototype.hasOwnProperty.call(val, loc.slice(1))) { // `-escaped property + var locProp = loc.slice(1); + addRet(this._trace(x, val[locProp], push(path, locProp), val, locProp, callback, true)); + } + else if (loc.includes(',')) { // [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 (!literalPriority && val && Object.prototype.hasOwnProperty.call(val, loc)) { // simple case--directly follow property + addRet(this._trace(x, val[loc], push(path, loc), val, loc, callback, true)); + } + + // 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 + if (this._hasParentSelector) { + for (var t = 0; t < ret.length; t++) { + var rett = ret[t]; + if (rett.isParentSelector) { + var tmp = self._trace(rett.expr, val, rett.path, parent, parentPropName, callback); + if (Array.isArray(tmp)) { + ret[t] = tmp[0]; + for (var tt = 1, tl = tmp.length; tt < tl; tt++) { + t++; + ret.splice(t, 0, tmp[tt]); + } + } else { + ret[t] = tmp; + } + } + } + } + return ret; +}; + +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) { + var tmp = this._trace(unshift(i, expr), val, path, parent, parentPropName, callback); + if (Array.isArray(tmp)) { + tmp.forEach(function (t) { + ret.push(t); + }); + } + else { + ret.push(tmp); + } + } + return ret; +}; + +JSONPath.prototype._eval = function (code, _v, _vname, path, parent, parentPropName) { + if (!this._obj || !_v) {return false;} + if (code.includes('@parentProperty')) { + this.currSandbox._$_parentProperty = parentPropName; + code = code.replace(/@parentProperty/g, '_$_parentProperty'); + } + if (code.includes('@parent')) { + this.currSandbox._$_parent = parent; + code = code.replace(/@parent/g, '_$_parent'); + } + if (code.includes('@property')) { + this.currSandbox._$_property = _vname; + code = code.replace(/@property/g, '_$_property'); + } + if (code.includes('@path')) { + 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].concat();} + 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":54}],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.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +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){ +function Agent() { + this._defaults = []; +} + +["use", "on", "once", "set", "query", "type", "accept", "auth", "withCredentials", "sortQuery", "retry", "ok", "redirects", + "timeout", "buffer", "serialize", "parse", "ca", "key", "pfx", "cert"].forEach(function(fn) { + /** Default setting for all requests from this agent */ + Agent.prototype[fn] = function(/*varargs*/) { + this._defaults.push({fn:fn, arguments:arguments}); + return this; + } +}); + +Agent.prototype._setDefaults = function(req) { + this._defaults.forEach(function(def) { + req[def.fn].apply(req, def.arguments); + }); +}; + +module.exports = Agent; + +},{}],8:[function(require,module,exports){ +/** + * Root reference for iframes. + */ + +var root; +if (typeof window !== 'undefined') { // Browser window + root = window; +} else if (typeof self !== 'undefined') { // Web Worker + root = self; +} else { // Other environments + console.warn("Using browser-only version of superagent in non-browser environment"); + root = this; +} + +var Emitter = require('component-emitter'); +var RequestBase = require('./request-base'); +var isObject = require('./is-object'); +var ResponseBase = require('./response-base'); +var Agent = require('./agent-base'); + +/** + * Noop. + */ + +function noop(){}; + +/** + * Expose `request`. + */ + +var request = exports = module.exports = function(method, url) { + // callback + if ('function' == typeof url) { + return new exports.Request('GET', method).end(url); + } + + // url first + if (1 == arguments.length) { + return new exports.Request('GET', method); + } + + return new exports.Request(method, url); +} + +exports.Request = Request; + +/** + * Determine XHR. + */ + +request.getXHR = function () { + if (root.XMLHttpRequest + && (!root.location || 'file:' != root.location.protocol + || !root.ActiveXObject)) { + return new XMLHttpRequest; + } else { + try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} + try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} + } + throw Error("Browser-only version of superagent could not find XHR"); +}; + +/** + * Removes leading and trailing whitespace, added to support IE. + * + * @param {String} s + * @return {String} + * @api private + */ + +var trim = ''.trim + ? function(s) { return s.trim(); } + : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; + +/** + * Serialize the given `obj`. + * + * @param {Object} obj + * @return {String} + * @api private + */ + +function serialize(obj) { + if (!isObject(obj)) return obj; + var pairs = []; + for (var key in obj) { + pushEncodedKeyValuePair(pairs, key, obj[key]); + } + return pairs.join('&'); +} + +/** + * Helps 'serialize' with serializing arrays. + * Mutates the pairs array. + * + * @param {Array} pairs + * @param {String} key + * @param {Mixed} val + */ + +function pushEncodedKeyValuePair(pairs, key, val) { + if (val != null) { + if (Array.isArray(val)) { + val.forEach(function(v) { + pushEncodedKeyValuePair(pairs, key, v); + }); + } else if (isObject(val)) { + for(var subkey in val) { + pushEncodedKeyValuePair(pairs, key + '[' + subkey + ']', val[subkey]); + } + } else { + pairs.push(encodeURIComponent(key) + + '=' + encodeURIComponent(val)); + } + } else if (val === null) { + pairs.push(encodeURIComponent(key)); + } +} + +/** + * Expose serialization method. + */ + +request.serializeObject = serialize; + +/** + * Parse the given x-www-form-urlencoded `str`. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function parseString(str) { + var obj = {}; + var pairs = str.split('&'); + var pair; + var pos; + + for (var i = 0, len = pairs.length; i < len; ++i) { + pair = pairs[i]; + pos = pair.indexOf('='); + if (pos == -1) { + obj[decodeURIComponent(pair)] = ''; + } else { + obj[decodeURIComponent(pair.slice(0, pos))] = + decodeURIComponent(pair.slice(pos + 1)); + } + } + + return obj; +} + +/** + * Expose parser. + */ + +request.parseString = parseString; + +/** + * Default MIME type map. + * + * superagent.types.xml = 'application/xml'; + * + */ + +request.types = { + html: 'text/html', + json: 'application/json', + xml: 'text/xml', + urlencoded: 'application/x-www-form-urlencoded', + 'form': 'application/x-www-form-urlencoded', + 'form-data': 'application/x-www-form-urlencoded' +}; + +/** + * Default serialization map. + * + * superagent.serialize['application/xml'] = function(obj){ + * return 'generated xml here'; + * }; + * + */ + +request.serialize = { + 'application/x-www-form-urlencoded': serialize, + 'application/json': JSON.stringify +}; + +/** + * Default parsers. + * + * superagent.parse['application/xml'] = function(str){ + * return { object parsed from str }; + * }; + * + */ + +request.parse = { + 'application/x-www-form-urlencoded': parseString, + 'application/json': JSON.parse +}; + +/** + * Parse the given header `str` into + * an object containing the mapped fields. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function parseHeader(str) { + var lines = str.split(/\r?\n/); + var fields = {}; + var index; + var line; + var field; + var val; + + for (var i = 0, len = lines.length; i < len; ++i) { + line = lines[i]; + index = line.indexOf(':'); + if (index === -1) { // could be empty line, just skip it + continue; + } + field = line.slice(0, index).toLowerCase(); + val = trim(line.slice(index + 1)); + fields[field] = val; + } + + return fields; +} + +/** + * Check if `mime` is json or has +json structured syntax suffix. + * + * @param {String} mime + * @return {Boolean} + * @api private + */ + +function isJSON(mime) { + // should match /json or +json + // but not /json-seq + return /[\/+]json($|[^-\w])/.test(mime); +} + +/** + * Initialize a new `Response` with the given `xhr`. + * + * - set flags (.ok, .error, etc) + * - parse header + * + * Examples: + * + * Aliasing `superagent` as `request` is nice: + * + * request = superagent; + * + * We can use the promise-like API, or pass callbacks: + * + * request.get('/').end(function(res){}); + * request.get('/', function(res){}); + * + * Sending data can be chained: + * + * request + * .post('/user') + * .send({ name: 'tj' }) + * .end(function(res){}); + * + * Or passed to `.send()`: + * + * request + * .post('/user') + * .send({ name: 'tj' }, function(res){}); + * + * Or passed to `.post()`: + * + * request + * .post('/user', { name: 'tj' }) + * .end(function(res){}); + * + * Or further reduced to a single call for simple cases: + * + * request + * .post('/user', { name: 'tj' }, function(res){}); + * + * @param {XMLHTTPRequest} xhr + * @param {Object} options + * @api private + */ + +function Response(req) { + this.req = req; + this.xhr = this.req.xhr; + // responseText is accessible only if responseType is '' or 'text' and on older browsers + this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined') + ? this.xhr.responseText + : null; + this.statusText = this.req.xhr.statusText; + var status = this.xhr.status; + // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request + if (status === 1223) { + status = 204; + } + this._setStatusProperties(status); + this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); + // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but + // getResponseHeader still works. so we get content-type even if getting + // other headers fails. + this.header['content-type'] = this.xhr.getResponseHeader('content-type'); + this._setHeaderProperties(this.header); + + if (null === this.text && req._responseType) { + this.body = this.xhr.response; + } else { + this.body = this.req.method != 'HEAD' + ? this._parseBody(this.text ? this.text : this.xhr.response) + : null; + } +} + +ResponseBase(Response.prototype); + +/** + * Parse the given body `str`. + * + * Used for auto-parsing of bodies. Parsers + * are defined on the `superagent.parse` object. + * + * @param {String} str + * @return {Mixed} + * @api private + */ + +Response.prototype._parseBody = function(str) { + var parse = request.parse[this.type]; + if (this.req._parser) { + return this.req._parser(this, str); + } + if (!parse && isJSON(this.type)) { + parse = request.parse['application/json']; + } + return parse && str && (str.length || str instanceof Object) + ? parse(str) + : null; +}; + +/** + * Return an `Error` representative of this response. + * + * @return {Error} + * @api public + */ + +Response.prototype.toError = function(){ + var req = this.req; + var method = req.method; + var url = req.url; + + var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; + var err = new Error(msg); + err.status = this.status; + err.method = method; + err.url = url; + + return err; +}; + +/** + * Expose `Response`. + */ + +request.Response = Response; + +/** + * Initialize a new `Request` with the given `method` and `url`. + * + * @param {String} method + * @param {String} url + * @api public + */ + +function Request(method, url) { + var self = this; + this._query = this._query || []; + this.method = method; + this.url = url; + this.header = {}; // preserves header name case + this._header = {}; // coerces header names to lowercase + this.on('end', function(){ + var err = null; + var res = null; + + try { + res = new Response(self); + } catch(e) { + err = new Error('Parser is unable to parse the response'); + err.parse = true; + err.original = e; + // issue #675: return the raw response if the response parsing fails + if (self.xhr) { + // ie9 doesn't have 'response' property + err.rawResponse = typeof self.xhr.responseType == 'undefined' ? self.xhr.responseText : self.xhr.response; + // issue #876: return the http status code if the response parsing fails + err.status = self.xhr.status ? self.xhr.status : null; + err.statusCode = err.status; // backwards-compat only + } else { + err.rawResponse = null; + err.status = null; + } + + return self.callback(err); + } + + self.emit('response', res); + + var new_err; + try { + if (!self._isResponseOK(res)) { + new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); + } + } catch(custom_err) { + new_err = custom_err; // ok() callback can throw + } + + // #1000 don't catch errors from the callback to avoid double calling it + if (new_err) { + new_err.original = err; + new_err.response = res; + new_err.status = res.status; + self.callback(new_err, res); + } else { + self.callback(null, res); + } + }); +} + +/** + * Mixin `Emitter` and `RequestBase`. + */ + +Emitter(Request.prototype); +RequestBase(Request.prototype); + +/** + * Set Content-Type to `type`, mapping values from `request.types`. + * + * Examples: + * + * superagent.types.xml = 'application/xml'; + * + * request.post('/') + * .type('xml') + * .send(xmlstring) + * .end(callback); + * + * request.post('/') + * .type('application/xml') + * .send(xmlstring) + * .end(callback); + * + * @param {String} type + * @return {Request} for chaining + * @api public + */ + +Request.prototype.type = function(type){ + this.set('Content-Type', request.types[type] || type); + return this; +}; + +/** + * Set Accept to `type`, mapping values from `request.types`. + * + * Examples: + * + * superagent.types.json = 'application/json'; + * + * request.get('/agent') + * .accept('json') + * .end(callback); + * + * request.get('/agent') + * .accept('application/json') + * .end(callback); + * + * @param {String} accept + * @return {Request} for chaining + * @api public + */ + +Request.prototype.accept = function(type){ + this.set('Accept', request.types[type] || type); + return this; +}; + +/** + * Set Authorization field value with `user` and `pass`. + * + * @param {String} user + * @param {String} [pass] optional in case of using 'bearer' as type + * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic') + * @return {Request} for chaining + * @api public + */ + +Request.prototype.auth = function(user, pass, options){ + if (1 === arguments.length) pass = ''; + if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options + options = pass; + pass = ''; + } + if (!options) { + options = { + type: 'function' === typeof btoa ? 'basic' : 'auto', + }; + } + + var encoder = function(string) { + if ('function' === typeof btoa) { + return btoa(string); + } + throw new Error('Cannot use basic auth, btoa is not a function'); + }; + + return this._auth(user, pass, options, encoder); +}; + +/** + * Add query-string `val`. + * + * Examples: + * + * request.get('/shoes') + * .query('size=10') + * .query({ color: 'blue' }) + * + * @param {Object|String} val + * @return {Request} for chaining + * @api public + */ + +Request.prototype.query = function(val){ + if ('string' != typeof val) val = serialize(val); + if (val) this._query.push(val); + return this; +}; + +/** + * Queue the given `file` as an attachment to the specified `field`, + * with optional `options` (or filename). + * + * ``` js + * request.post('/upload') + * .attach('content', new Blob(['hey!'], { type: "text/html"})) + * .end(callback); + * ``` + * + * @param {String} field + * @param {Blob|File} file + * @param {String|Object} options + * @return {Request} for chaining + * @api public + */ + +Request.prototype.attach = function(field, file, options){ + if (file) { + if (this._data) { + throw Error("superagent can't mix .send() and .attach()"); + } + + this._getFormData().append(field, file, options || file.name); + } + return this; +}; + +Request.prototype._getFormData = function(){ + if (!this._formData) { + this._formData = new root.FormData(); + } + return this._formData; +}; + +/** + * Invoke the callback with `err` and `res` + * and handle arity check. + * + * @param {Error} err + * @param {Response} res + * @api private + */ + +Request.prototype.callback = function(err, res){ + if (this._shouldRetry(err, res)) { + return this._retry(); + } + + var fn = this._callback; + this.clearTimeout(); + + if (err) { + if (this._maxRetries) err.retries = this._retries - 1; + this.emit('error', err); + } + + fn(err, res); +}; + +/** + * Invoke callback with x-domain error. + * + * @api private + */ + +Request.prototype.crossDomainError = function(){ + var err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'); + err.crossDomain = true; + + err.status = this.status; + err.method = this.method; + err.url = this.url; + + this.callback(err); +}; + +// This only warns, because the request is still likely to work +Request.prototype.buffer = Request.prototype.ca = Request.prototype.agent = function(){ + console.warn("This is not supported in browser version of superagent"); + return this; +}; + +// This throws, because it can't send/receive data as expected +Request.prototype.pipe = Request.prototype.write = function(){ + throw Error("Streaming is not supported in browser version of superagent"); +}; + +/** + * Check if `obj` is a host object, + * we don't want to serialize these :) + * + * @param {Object} obj + * @return {Boolean} + * @api private + */ +Request.prototype._isHost = function _isHost(obj) { + // Native objects stringify to [object File], [object Blob], [object FormData], etc. + return obj && 'object' === typeof obj && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== '[object Object]'; +} + +/** + * Initiate request, invoking callback `fn(res)` + * with an instanceof `Response`. + * + * @param {Function} fn + * @return {Request} for chaining + * @api public + */ + +Request.prototype.end = function(fn){ + if (this._endCalled) { + console.warn("Warning: .end() was called twice. This is not supported in superagent"); + } + this._endCalled = true; + + // store callback + this._callback = fn || noop; + + // querystring + this._finalizeQueryString(); + + return this._end(); +}; + +Request.prototype._end = function() { + var self = this; + var xhr = (this.xhr = request.getXHR()); + var data = this._formData || this._data; + + this._setTimeouts(); + + // state change + xhr.onreadystatechange = function(){ + var readyState = xhr.readyState; + if (readyState >= 2 && self._responseTimeoutTimer) { + clearTimeout(self._responseTimeoutTimer); + } + if (4 != readyState) { + return; + } + + // In IE9, reads to any property (e.g. status) off of an aborted XHR will + // result in the error "Could not complete the operation due to error c00c023f" + var status; + try { status = xhr.status } catch(e) { status = 0; } + + if (!status) { + if (self.timedout || self._aborted) return; + return self.crossDomainError(); + } + self.emit('end'); + }; + + // progress + var handleProgress = function(direction, e) { + if (e.total > 0) { + e.percent = e.loaded / e.total * 100; + } + e.direction = direction; + self.emit('progress', e); + }; + if (this.hasListeners('progress')) { + try { + xhr.onprogress = handleProgress.bind(null, 'download'); + if (xhr.upload) { + xhr.upload.onprogress = handleProgress.bind(null, 'upload'); + } + } catch(e) { + // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist. + // Reported here: + // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context + } + } + + // initiate request + try { + if (this.username && this.password) { + xhr.open(this.method, this.url, true, this.username, this.password); + } else { + xhr.open(this.method, this.url, true); + } + } catch (err) { + // see #1149 + return this.callback(err); + } + + // CORS + if (this._withCredentials) xhr.withCredentials = true; + + // body + if (!this._formData && 'GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !this._isHost(data)) { + // serialize stuff + var contentType = this._header['content-type']; + var serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : '']; + if (!serialize && isJSON(contentType)) { + serialize = request.serialize['application/json']; + } + if (serialize) data = serialize(data); + } + + // set header fields + for (var field in this.header) { + if (null == this.header[field]) continue; + + if (this.header.hasOwnProperty(field)) + xhr.setRequestHeader(field, this.header[field]); + } + + if (this._responseType) { + xhr.responseType = this._responseType; + } + + // send stuff + this.emit('request', this); + + // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing) + // We need null here if data is undefined + xhr.send(typeof data !== 'undefined' ? data : null); + return this; +}; + +request.agent = function() { + return new Agent(); +}; + +["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(function(method) { + Agent.prototype[method.toLowerCase()] = function(url, fn) { + var req = new request.Request(method, url); + this._setDefaults(req); + if (fn) { + req.end(fn); + } + return req; + }; +}); + +Agent.prototype.del = Agent.prototype['delete']; + +/** + * GET `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.get = function(url, data, fn) { + var req = request('GET', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.query(data); + if (fn) req.end(fn); + return req; +}; + +/** + * HEAD `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.head = function(url, data, fn) { + var req = request('HEAD', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.query(data); + if (fn) req.end(fn); + return req; +}; + +/** + * OPTIONS query to `url` with optional callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.options = function(url, data, fn) { + var req = request('OPTIONS', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * DELETE `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +function del(url, data, fn) { + var req = request('DELETE', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +} + +request['del'] = del; +request['delete'] = del; + +/** + * PATCH `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.patch = function(url, data, fn) { + var req = request('PATCH', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * POST `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed} [data] + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.post = function(url, data, fn) { + var req = request('POST', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +/** + * PUT `url` with optional `data` and callback `fn(res)`. + * + * @param {String} url + * @param {Mixed|Function} [data] or fn + * @param {Function} [fn] + * @return {Request} + * @api public + */ + +request.put = function(url, data, fn) { + var req = request('PUT', url); + if ('function' == typeof data) (fn = data), (data = null); + if (data) req.send(data); + if (fn) req.end(fn); + return req; +}; + +},{"./agent-base":7,"./is-object":9,"./request-base":10,"./response-base":11,"component-emitter":2}],9:[function(require,module,exports){ +'use strict'; + +/** + * 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; + +},{}],10:[function(require,module,exports){ +'use strict'; + +/** + * Module of mixed-in functions shared between node and client code + */ +var isObject = require('./is-object'); + +/** + * Expose `RequestBase`. + */ + +module.exports = RequestBase; + +/** + * Initialize a new `RequestBase`. + * + * @api public + */ + +function RequestBase(obj) { + if (obj) return mixin(obj); +} + +/** + * Mixin the prototype properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in RequestBase.prototype) { + obj[key] = RequestBase.prototype[key]; + } + return obj; +} + +/** + * Clear previous timeout. + * + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.clearTimeout = function _clearTimeout(){ + clearTimeout(this._timer); + clearTimeout(this._responseTimeoutTimer); + delete this._timer; + delete this._responseTimeoutTimer; + return this; +}; + +/** + * Override default response body parser + * + * This function will be called to convert incoming data into request.body + * + * @param {Function} + * @api public + */ + +RequestBase.prototype.parse = function parse(fn){ + this._parser = fn; + return this; +}; + +/** + * Set format of binary response body. + * In browser valid formats are 'blob' and 'arraybuffer', + * which return Blob and ArrayBuffer, respectively. + * + * In Node all values result in Buffer. + * + * Examples: + * + * req.get('/') + * .responseType('blob') + * .end(callback); + * + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.responseType = function(val){ + this._responseType = val; + 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 + */ + +RequestBase.prototype.serialize = function serialize(fn){ + this._serializer = fn; + return this; +}; + +/** + * Set timeouts. + * + * - response timeout is time between sending request and receiving the first byte of the response. Includes DNS and connection time. + * - deadline is the time from start of the request to receiving response body in full. If the deadline is too short large files may not load at all on slow connections. + * + * Value of 0 or false means no timeout. + * + * @param {Number|Object} ms or {response, deadline} + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.timeout = function timeout(options){ + if (!options || 'object' !== typeof options) { + this._timeout = options; + this._responseTimeout = 0; + return this; + } + + for(var option in options) { + switch(option) { + case 'deadline': + this._timeout = options.deadline; + break; + case 'response': + this._responseTimeout = options.response; + break; + default: + console.warn("Unknown timeout option", option); + } + } + return this; +}; + +/** + * Set number of retry attempts on error. + * + * Failed requests will be retried 'count' times if timeout or err.code >= 500. + * + * @param {Number} count + * @param {Function} [fn] + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.retry = function retry(count, fn){ + // Default to 1 if no count passed or true + if (arguments.length === 0 || count === true) count = 1; + if (count <= 0) count = 0; + this._maxRetries = count; + this._retries = 0; + this._retryCallback = fn; + return this; +}; + +var ERROR_CODES = [ + 'ECONNRESET', + 'ETIMEDOUT', + 'EADDRINFO', + 'ESOCKETTIMEDOUT' +]; + +/** + * Determine if a request should be retried. + * (Borrowed from segmentio/superagent-retry) + * + * @param {Error} err + * @param {Response} [res] + * @returns {Boolean} + */ +RequestBase.prototype._shouldRetry = function(err, res) { + if (!this._maxRetries || this._retries++ >= this._maxRetries) { + return false; + } + if (this._retryCallback) { + try { + var override = this._retryCallback(err, res); + if (override === true) return true; + if (override === false) return false; + // undefined falls back to defaults + } catch(e) { + console.error(e); + } + } + if (res && res.status && res.status >= 500 && res.status != 501) return true; + if (err) { + if (err.code && ~ERROR_CODES.indexOf(err.code)) return true; + // Superagent timeout + if (err.timeout && err.code == 'ECONNABORTED') return true; + if (err.crossDomain) return true; + } + return false; +}; + +/** + * Retry request + * + * @return {Request} for chaining + * @api private + */ + +RequestBase.prototype._retry = function() { + + this.clearTimeout(); + + // node + if (this.req) { + this.req = null; + this.req = this.request(); + } + + this._aborted = false; + this.timedout = false; + + return this._end(); +}; + +/** + * Promise support + * + * @param {Function} resolve + * @param {Function} [reject] + * @return {Request} + */ + +RequestBase.prototype.then = function then(resolve, reject) { + if (!this._fullfilledPromise) { + var self = this; + if (this._endCalled) { + console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"); + } + this._fullfilledPromise = new Promise(function(innerResolve, innerReject) { + self.end(function(err, res) { + if (err) innerReject(err); + else innerResolve(res); + }); + }); + } + return this._fullfilledPromise.then(resolve, reject); +}; + +RequestBase.prototype['catch'] = function(cb) { + return this.then(undefined, cb); +}; + +/** + * Allow for extension + */ + +RequestBase.prototype.use = function use(fn) { + fn(this); + return this; +}; + +RequestBase.prototype.ok = function(cb) { + if ('function' !== typeof cb) throw Error("Callback required"); + this._okCallback = cb; + return this; +}; + +RequestBase.prototype._isResponseOK = function(res) { + if (!res) { + return false; + } + + if (this._okCallback) { + return this._okCallback(res); + } + + return res.status >= 200 && res.status < 300; +}; + +/** + * Get request header `field`. + * Case-insensitive. + * + * @param {String} field + * @return {String} + * @api public + */ + +RequestBase.prototype.get = function(field){ + return this._header[field.toLowerCase()]; +}; + +/** + * Get case-insensitive header `field` value. + * This is a deprecated internal API. Use `.get(field)` instead. + * + * (getHeader is no longer used internally by the superagent code base) + * + * @param {String} field + * @return {String} + * @api private + * @deprecated + */ + +RequestBase.prototype.getHeader = RequestBase.prototype.get; + +/** + * Set header `field` to `val`, or multiple fields with one object. + * Case-insensitive. + * + * Examples: + * + * req.get('/') + * .set('Accept', 'application/json') + * .set('X-API-Key', 'foobar') + * .end(callback); + * + * req.get('/') + * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) + * .end(callback); + * + * @param {String|Object} field + * @param {String} val + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.set = function(field, val){ + if (isObject(field)) { + for (var key in field) { + this.set(key, field[key]); + } + return this; + } + this._header[field.toLowerCase()] = val; + this.header[field] = val; + return this; +}; + +/** + * Remove header `field`. + * Case-insensitive. + * + * Example: + * + * req.get('/') + * .unset('User-Agent') + * .end(callback); + * + * @param {String} field + */ +RequestBase.prototype.unset = function(field){ + delete this._header[field.toLowerCase()]; + delete this.header[field]; + return this; +}; + +/** + * Write the field `name` and `val`, or multiple fields with one object + * for "multipart/form-data" request bodies. + * + * ``` js + * request.post('/upload') + * .field('foo', 'bar') + * .end(callback); + * + * request.post('/upload') + * .field({ foo: 'bar', baz: 'qux' }) + * .end(callback); + * ``` + * + * @param {String|Object} name + * @param {String|Blob|File|Buffer|fs.ReadStream} val + * @return {Request} for chaining + * @api public + */ +RequestBase.prototype.field = function(name, val) { + // name should be either a string or an object. + if (null === name || undefined === name) { + throw new Error('.field(name, val) name can not be empty'); + } + + if (this._data) { + console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"); + } + + if (isObject(name)) { + for (var key in name) { + this.field(key, name[key]); + } + return this; + } + + if (Array.isArray(val)) { + for (var i in val) { + this.field(name, val[i]); + } + return this; + } + + // val should be defined now + if (null === val || undefined === val) { + throw new Error('.field(name, val) val can not be empty'); + } + if ('boolean' === typeof val) { + val = '' + val; + } + this._getFormData().append(name, val); + return this; +}; + +/** + * Abort the request, and clear potential timeout. + * + * @return {Request} + * @api public + */ +RequestBase.prototype.abort = function(){ + if (this._aborted) { + return this; + } + this._aborted = true; + this.xhr && this.xhr.abort(); // browser + this.req && this.req.abort(); // node + this.clearTimeout(); + this.emit('abort'); + return this; +}; + +RequestBase.prototype._auth = function(user, pass, options, base64Encoder) { + switch (options.type) { + case 'basic': + this.set('Authorization', 'Basic ' + base64Encoder(user + ':' + pass)); + break; + + case 'auto': + this.username = user; + this.password = pass; + break; + + case 'bearer': // usage would be .auth(accessToken, { type: 'bearer' }) + this.set('Authorization', 'Bearer ' + user); + break; + } + return this; +}; + +/** + * Enable transmission of cookies with x-domain requests. + * + * Note that for this to work the origin must not be + * using "Access-Control-Allow-Origin" with a wildcard, + * and also must set "Access-Control-Allow-Credentials" + * to "true". + * + * @api public + */ + +RequestBase.prototype.withCredentials = function(on) { + // This is browser-only functionality. Node side is no-op. + if (on == undefined) on = true; + this._withCredentials = on; + return this; +}; + +/** + * Set the max redirects to `n`. Does noting in browser XHR implementation. + * + * @param {Number} n + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.redirects = function(n){ + this._maxRedirects = n; + return this; +}; + +/** + * Maximum size of buffered response body, in bytes. Counts uncompressed size. + * Default 200MB. + * + * @param {Number} n + * @return {Request} for chaining + */ +RequestBase.prototype.maxResponseSize = function(n){ + if ('number' !== typeof n) { + throw TypeError("Invalid argument"); + } + this._maxResponseSize = n; + return this; +}; + +/** + * Convert to a plain javascript object (not JSON string) of scalar properties. + * Note as this method is designed to return a useful non-this value, + * it cannot be chained. + * + * @return {Object} describing method, url, and data of this request + * @api public + */ + +RequestBase.prototype.toJSON = function() { + return { + method: this.method, + url: this.url, + data: this._data, + headers: this._header, + }; +}; + +/** + * Send `data` as the request body, defaulting the `.type()` to "json" when + * an object is given. + * + * Examples: + * + * // manual json + * request.post('/user') + * .type('json') + * .send('{"name":"tj"}') + * .end(callback) + * + * // auto json + * request.post('/user') + * .send({ name: 'tj' }) + * .end(callback) + * + * // manual x-www-form-urlencoded + * request.post('/user') + * .type('form') + * .send('name=tj') + * .end(callback) + * + * // auto x-www-form-urlencoded + * request.post('/user') + * .type('form') + * .send({ name: 'tj' }) + * .end(callback) + * + * // defaults to x-www-form-urlencoded + * request.post('/user') + * .send('name=tobi') + * .send('species=ferret') + * .end(callback) + * + * @param {String|Object} data + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.send = function(data){ + var isObj = isObject(data); + var type = this._header['content-type']; + + if (this._formData) { + console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"); + } + + if (isObj && !this._data) { + if (Array.isArray(data)) { + this._data = []; + } else if (!this._isHost(data)) { + this._data = {}; + } + } else if (data && this._data && this._isHost(this._data)) { + throw Error("Can't merge these send calls"); + } + + // merge + if (isObj && isObject(this._data)) { + for (var key in data) { + this._data[key] = data[key]; + } + } else if ('string' == typeof data) { + // default to x-www-form-urlencoded + if (!type) this.type('form'); + type = this._header['content-type']; + if ('application/x-www-form-urlencoded' == type) { + this._data = this._data + ? this._data + '&' + data + : data; + } else { + this._data = (this._data || '') + data; + } + } else { + this._data = data; + } + + if (!isObj || this._isHost(data)) { + return this; + } + + // default to json + if (!type) this.type('json'); + return this; +}; + +/** + * Sort `querystring` by the sort function + * + * + * Examples: + * + * // default order + * request.get('/user') + * .query('name=Nick') + * .query('search=Manny') + * .sortQuery() + * .end(callback) + * + * // customized sort function + * request.get('/user') + * .query('name=Nick') + * .query('search=Manny') + * .sortQuery(function(a, b){ + * return a.length - b.length; + * }) + * .end(callback) + * + * + * @param {Function} sort + * @return {Request} for chaining + * @api public + */ + +RequestBase.prototype.sortQuery = function(sort) { + // _sort default to true but otherwise can be a function or boolean + this._sort = typeof sort === 'undefined' ? true : sort; + return this; +}; + +/** + * Compose querystring to append to req.url + * + * @api private + */ +RequestBase.prototype._finalizeQueryString = function(){ + var query = this._query.join('&'); + if (query) { + this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') + query; + } + this._query.length = 0; // Makes the call idempotent + + if (this._sort) { + var index = this.url.indexOf('?'); + if (index >= 0) { + var queryArr = this.url.substring(index + 1).split('&'); + if ('function' === typeof this._sort) { + queryArr.sort(this._sort); + } else { + queryArr.sort(); + } + this.url = this.url.substring(0, index) + '?' + queryArr.join('&'); + } + } +}; + +// For backwards compat only +RequestBase.prototype._appendQueryString = function() {console.trace("Unsupported");} + +/** + * Invoke callback with timeout error. + * + * @api private + */ + +RequestBase.prototype._timeoutError = function(reason, timeout, errno){ + if (this._aborted) { + return; + } + var err = new Error(reason + timeout + 'ms exceeded'); + err.timeout = timeout; + err.code = 'ECONNABORTED'; + err.errno = errno; + this.timedout = true; + this.abort(); + this.callback(err); +}; + +RequestBase.prototype._setTimeouts = function() { + var self = this; + + // deadline + if (this._timeout && !this._timer) { + this._timer = setTimeout(function(){ + self._timeoutError('Timeout of ', self._timeout, 'ETIME'); + }, this._timeout); + } + // response timeout + if (this._responseTimeout && !this._responseTimeoutTimer) { + this._responseTimeoutTimer = setTimeout(function(){ + self._timeoutError('Response timeout of ', self._responseTimeout, 'ETIMEDOUT'); + }, this._responseTimeout); + } +}; + +},{"./is-object":9}],11:[function(require,module,exports){ +'use strict'; + +/** + * Module dependencies. + */ + +var utils = require('./utils'); + +/** + * Expose `ResponseBase`. + */ + +module.exports = ResponseBase; + +/** + * Initialize a new `ResponseBase`. + * + * @api public + */ + +function ResponseBase(obj) { + if (obj) return mixin(obj); +} + +/** + * Mixin the prototype properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in ResponseBase.prototype) { + obj[key] = ResponseBase.prototype[key]; + } + return obj; +} + +/** + * Get case-insensitive `field` value. + * + * @param {String} field + * @return {String} + * @api public + */ + +ResponseBase.prototype.get = function(field) { + return this.header[field.toLowerCase()]; +}; + +/** + * Set header related properties: + * + * - `.type` the content type without params + * + * A response of "Content-Type: text/plain; charset=utf-8" + * will provide you with a `.type` of "text/plain". + * + * @param {Object} header + * @api private + */ + +ResponseBase.prototype._setHeaderProperties = function(header){ + // TODO: moar! + // TODO: make this a util + + // content-type + var ct = header['content-type'] || ''; + this.type = utils.type(ct); + + // params + var params = utils.params(ct); + for (var key in params) this[key] = params[key]; + + this.links = {}; + + // links + try { + if (header.link) { + this.links = utils.parseLinks(header.link); + } + } catch (err) { + // ignore + } +}; + +/** + * Set flags such as `.ok` based on `status`. + * + * For example a 2xx response will give you a `.ok` of __true__ + * whereas 5xx will be __false__ and `.error` will be __true__. The + * `.clientError` and `.serverError` are also available to be more + * specific, and `.statusType` is the class of error ranging from 1..5 + * sometimes useful for mapping respond colors etc. + * + * "sugar" properties are also defined for common cases. Currently providing: + * + * - .noContent + * - .badRequest + * - .unauthorized + * - .notAcceptable + * - .notFound + * + * @param {Number} status + * @api private + */ + +ResponseBase.prototype._setStatusProperties = function(status){ + var type = status / 100 | 0; + + // status / class + this.status = this.statusCode = status; + this.statusType = type; + + // basics + this.info = 1 == type; + this.ok = 2 == type; + this.redirect = 3 == type; + this.clientError = 4 == type; + this.serverError = 5 == type; + this.error = (4 == type || 5 == type) + ? this.toError() + : false; + + // sugar + this.created = 201 == status; + this.accepted = 202 == status; + this.noContent = 204 == status; + this.badRequest = 400 == status; + this.unauthorized = 401 == status; + this.notAcceptable = 406 == status; + this.forbidden = 403 == status; + this.notFound = 404 == status; + this.unprocessableEntity = 422 == status; +}; + +},{"./utils":12}],12:[function(require,module,exports){ +'use strict'; + +/** + * Return the mime type for the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.type = function(str){ + return str.split(/ *; */).shift(); +}; + +/** + * Return header field parameters. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.params = function(str){ + return str.split(/ *; */).reduce(function(obj, str){ + var parts = str.split(/ *= */); + var key = parts.shift(); + var val = parts.shift(); + + if (key && val) obj[key] = val; + return obj; + }, {}); +}; + +/** + * Parse Link header fields. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.parseLinks = function(str){ + return str.split(/ *, */).reduce(function(obj, str){ + var parts = str.split(/ *; */); + var url = parts[0].slice(1, -1); + var rel = parts[1].split(/ *= */)[1].slice(1, -1); + obj[rel] = url; + return obj; + }, {}); +}; + +/** + * Strip content related fields from `header`. + * + * @param {Object} header + * @return {Object} header + * @api private + */ + +exports.cleanHeader = function(header, changesOrigin){ + delete header['content-type']; + delete header['content-length']; + delete header['transfer-encoding']; + delete header['host']; + // secuirty + if (changesOrigin) { + delete header['authorization']; + delete header['cookie']; + } + return header; +}; + +},{}],13:[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; + +},{}],14:[function(require,module,exports){ +'use strict'; + +module.exports = { + isArray: function(o) { + if (o == null) { + return false; + } + return Object.prototype.toString.call(o) === '[object Array]'; + } +}; + +},{}],15:[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":8}],16:[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; + +},{}],17:[function(require,module,exports){ +'use strict'; + +var resolveUrl = require('resolve-url'); + +exports.resolve = function(from, to) { + return resolveUrl(from, to); +}; + +},{"resolve-url":6}],18:[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":21,"minilog":13}],19:[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') + , parseLinkHeader = require('./transforms/parse_link_header'); + +/** + * 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, + parseLinkHeader, + 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, + parseLinkHeader, + extractDoc, + ]; + } else { + transformsAfterLastStep = [ + executeLastHttpRequest, + ]; + } + + t.lastMethod = method; + walker.walk(t, transformsAfterLastStep, callback); +} + +function createTraversalHandle(t) { + return { + abort: t.abortTraversal + }; +} + +},{"./abort_traversal":18,"./http_requests":22,"./is_continuation":23,"./transforms/apply_transforms":30,"./transforms/check_http_status":31,"./transforms/continuation_to_doc":32,"./transforms/continuation_to_response":33,"./transforms/convert_embedded_doc_to_response":34,"./transforms/execute_http_request":36,"./transforms/execute_last_http_request":37,"./transforms/extract_doc":38,"./transforms/extract_response":39,"./transforms/extract_url":40,"./transforms/fetch_last_resource":41,"./transforms/parse":44,"./transforms/parse_link_header":45,"./walker":51,"minilog":13}],20:[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, linkType) { + this.mediaType = mediaType || mediaTypes.CONTENT_NEGOTIATION; + this.linkType = linkType || 'link-rel'; + this.adapter = this._createAdapter(this.mediaType); + this.autoHeaders = true; + 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(), + this.getLinkType()); + clonedRequestBuilder.useAutoHeaders(this.setsAutoHeaders()); + 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; +}; + +Builder.prototype.getLinkType = function() { + return this.linkType; +}; + +/** + * 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: this.linkType, + 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; +}; + +/** + * Disables automatic Accept and Content-Type headers. See useAutoHeaders(). + */ +Builder.prototype.disableAutoHeaders = function() { + return this.useAutoHeaders(false); +}; + +/** + * Enables automatic Accept and Content-Type headers. See useAutoHeaders(). + */ +Builder.prototype.enableAutoHeaders = function() { + return this.useAutoHeaders(true); +}; + +/** + * Enables or disables automatic headers. With automatic headers enabled, + * traverson will set default Accept and the Content-Type headers for HTTP + * requests, unless you provide these headers explicitly with withRequestOptions + * or addRequestOptions. + * + * The header values depend on the media type (see setMediaType()). For example, + * for plain vanilla JSON (that is, when using setMediaType('application/json') + * or the corresponding shortcut .json()), both headers will be send with the + * value 'application/json'. For HAL (that is, when using + * setMediaType('application/hal+json') or the corresponding shortcut + * jsonHal()), both headers will be send with the value 'application/hal+json'. + * + * If the method is called without arguments (or the first argument is undefined + * or null), automatic headers are turned on, otherwise the argument is + * interpreted as a boolean flag. If it is a truthy value, auto headers + * are enabled, if it is a falsy value (but not null or undefined), auto headers + * are disabled. + * + * A note about the condition "unless you provide these headers explicitly with + * withRequestOptions or addRequestOptions" in the first paragraph: Traverson + * with automatic headers enabled will only check for the header option + * "Accept" and "Content-Type", not for "accept" or "Content-type" or any other + * variation regarding upper case/lower case letters. So to be on the safe + * side, if you mix auto headers with explicitly specified headers, make sure + * to specify your explicit headers with this exact same combination of upper + * case and lower case letters. + */ +Builder.prototype.useAutoHeaders = function(flag) { + if (typeof flag === 'undefined' || flag === null) { + flag = true; + } + this.autoHeaders = !!flag; + 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 default Accept and the Content-Type headers will be set + * automatically for HTTP requests. + */ +Builder.prototype.setsAutoHeaders = function() { + return this.autoHeaders; +}; + +/** + * 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; + +/** +* Set linkType property indicating that traverson must follow relations from +* header 'link' +*/ +Builder.prototype.linkHeader = function() { + this.linkType = 'link-header'; + return this; +}; + + +function createInitialTraversalState(self, body) { + + var normalizedBody = + (body !== null && typeof body !== undefined) ? body : null; + + var traversalState = { + aborted: false, + adapter: self.adapter || null, + body: normalizedBody, + callbackHasBeenCalledAfterAbort: false, + autoHeaders: self.setsAutoHeaders(), + 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.autoHeaders = t.autoHeaders; + 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":18,"./actions":19,"./errors":21,"./media_type_registry":25,"./media_types":26,"./merge_recursive":27,"minilog":13,"request":15,"util":14}],21:[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; + }, + +}; + +},{}],22:[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 !== null && typeof t.body !== 'undefined') { + 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":18,"./errors":21,"./transforms/detect_content_type":35,"./transforms/get_options_for_step":43,"_process":5,"minilog":13}],23:[function(require,module,exports){ +'use strict'; + +module.exports = function isContinuation(t) { + return t.continuation && t.step && t.step.response; +}; + +},{}],24:[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 + , parseLinkHeaderValue = require('./parse_link_header_value'); + +function JsonAdapter(log) { + this.log = log; +} + +JsonAdapter.mediaType = 'application/json'; + +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); + case 'link-header': + return this._handleLinkHeader(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); + } +}; + +JsonAdapter.prototype._handleLinkHeader = function(httpResponse, link) { + if (!httpResponse.headers.link) + throw createError('There was no link header in the last response.', + errors.InvalidArgumentError, link); + + var links = parseLinkHeaderValue(httpResponse.headers.link); + if (links[link.value]) { + return { url : links[link.value].url}; + } else { + throw createError('Link with relation ' + link.value + + ' not found in link header.', + errors.InvalidArgumentError, link); + } +}; + +module.exports = JsonAdapter; + +},{"./errors":21,"./parse_link_header_value":29,"jsonpath-plus":4,"minilog":13,"underscore.string":16}],25:[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":24,"./media_types":26,"./negotiation_adapter":28}],26:[function(require,module,exports){ +'use strict'; + +var JsonAdapter = require('./json_adapter'); + +module.exports = { + CONTENT_NEGOTIATION: 'content-negotiation', + JSON: JsonAdapter.mediaType, + JSON_HAL: 'application/hal+json', +}; + +},{"./json_adapter":24}],27:[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; + +},{}],28:[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":21}],29:[function(require,module,exports){ +var mergeRecursive = require('./merge_recursive'); + +module.exports = function parseLinkHeaderValue(linkHeader) { + if (!linkHeader) { + return null; + } + return linkHeader + .split(/,\s*]/g, ''); + var info = parts.reduce(createObjects, {}); + info.url = linkUrl; + return info; + } catch (e) { + return null; + } +} + +function createObjects(acc, p) { + // rel="next" => 1: rel 2: next + var m = p.match(/\s*(.+)\s*=\s*"?([^"]+)"?/); + if (m) acc[m[1]] = m[2]; + return acc; +} + +function hasRel(linkHeaderValuePart) { + return linkHeaderValuePart && linkHeaderValuePart.rel; +} + +function intoRels(acc, linkHeaderValuePart) { + function splitRel (rel) { + acc[rel] = mergeRecursive({ rel: rel }, linkHeaderValuePart); + } + + linkHeaderValuePart.rel.split(/\s+/).forEach(splitRel); + return acc; +} + +},{"./merge_recursive":27}],30:[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":13}],31:[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":21,"../is_continuation":23,"minilog":13}],32:[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":23,"minilog":13}],33:[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":23,"./convert_embedded_doc_to_response":34,"minilog":13}],34:[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":13}],35:[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]; + log.debug('found content type string', contentType); + var AdapterType = mediaTypeRegistry.get(contentType); + if (!AdapterType) { + log.error('no adapter for content type', contentType); + callback(createError('Unknown content type for content ' + + 'type detection: ' + contentType, errors.UnsupportedMediaType), t); + return false; + } + // switch to new Adapter depending on Content-Type header of server + t.adapter = new AdapterType(log); + log.debug('switched to media type adapter', + t.adapter.name || t.adapter.constructor.name); + } + return true; +}; + +},{"../errors":21,"../media_type_registry":25,"minilog":13}],36:[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 executing http request, ' + + 'processing step ', t.step); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +executeLastHttpRequest.isAsync = true; + +module.exports = executeLastHttpRequest; + +},{"../abort_traversal":18,"../http_requests":22,"minilog":13}],37:[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":18,"../http_requests":22,"minilog":13}],38:[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":13}],39:[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":13}],40:[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":21,"minilog":13,"url":17}],41:[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 executing http request'); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +fetchLastResource.isAsync = true; + +module.exports = fetchLastResource; + +},{"../abort_traversal":18,"../http_requests":22,"minilog":13}],42:[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 executing http request'); + log.error(err); + } + return t.callback(err); + } + callback(t); + }); +} + +module.exports = fetchResource; + +}).call(this,require('_process')) +},{"../abort_traversal":18,"../http_requests":22,"../is_continuation":23,"_process":5,"minilog":13}],43:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson') + , util = require('util') + , mergeRecursive = require('../merge_recursive'); + +module.exports = function getOptionsForStep(t) { + var options = t.requestOptions; + if (util.isArray(t.requestOptions)) { + options = t.requestOptions[t.step.index] || {}; + } + if (t.autoHeaders) { + addAutoHeaders(t, options); + } + log.debug('options', options); + return options; +}; + +function addAutoHeaders(t, options) { + var autoHeaderValue = + // we accept a static mediaType property as well as an instance property + t.adapter.constructor.mediaType || + t.adapter.mediaType; + + // The content negotiation adapter does not (and can not) provide a value + // for automatical Accept and Content-Type headers, in this case auto header + // value is undefined and we skip setting auto headers. This also happens + // arbitrary media type plug-ins that do not behave well and have no + // mediaType property. + if (autoHeaderValue) { + if (!options.headers) { + options.headers = createAutoHeaders(options, autoHeaderValue); + } else { + options.headers = + mergeRecursive( + createAutoHeaders(options, autoHeaderValue), + options.headers + ); + } + } +} + +function createAutoHeaders(options, autoHeaderValue) { + if (!options.form) { + // default: set Accept and Content-Type header + return { + 'Accept': autoHeaderValue, + 'Content-Type': autoHeaderValue, + }; + } else { + // if options.form is set, we only set the Accept header, but not not the + // Content-Type header. The Content-Type header is set automatically by + // request/request or by browser/lib/shim/request.js. + return { + 'Accept': autoHeaderValue + }; + } +} + +},{"../merge_recursive":27,"minilog":13,"util":14}],44:[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/traverson/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":21,"../is_continuation":23,"minilog":13}],45:[function(require,module,exports){ +'use strict'; + +var minilog = require('minilog') + , log = minilog('traverson'); + +var parseLinkHeaderValue = require('../parse_link_header_value'); + +module.exports = function parseLinkHeader(t) { + if (!t.step.doc || + !t.step.response || + !t.step.response.headers || + !t.step.response.headers.link) { + return true; + } + + try { + var links = parseLinkHeaderValue(t.step.response.headers.link); + t.step.doc._linkHeaders = links; + return true; + } catch (e) { + handleError(t, e); + return false; + } +}; + +function handleError(t, e) { + log.error('failed to parse link header'); + log.error(e); + t.callback(e); +} + +},{"../parse_link_header_value":29,"minilog":13}],46:[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":23}],47:[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":23}],48:[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":13,"underscore.string":16,"url":17}],49:[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":13,"underscore.string":16,"url-template":53,"util":14}],50:[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); + + 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":13}],51:[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":18,"./is_continuation":23,"./transforms/apply_transforms":30,"./transforms/check_http_status":31,"./transforms/fetch_resource":42,"./transforms/parse":44,"./transforms/reset_continuation":46,"./transforms/reset_last_step":47,"./transforms/resolve_next_url":48,"./transforms/resolve_uri_template":49,"./transforms/switch_to_next_step":50,"minilog":13}],52:[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/traverson/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":20,"./lib/errors":21,"./lib/media_type_registry":25,"./lib/media_types":26,"_process":5,"minilog":13}],53:[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(); +})); + +},{}],54:[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}],55:[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":52}]},{},[55])(55) +}); \ 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..ac9a812 --- /dev/null +++ b/browser/dist/traverson-angular.min.js @@ -0,0 +1 @@ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).traversonAngular=t()}}(function(){var define,module,exports;return function t(e,r,n){function o(i,a){if(!r[i]){if(!e[i]){var u="function"==typeof require&&require;if(!a&&u)return u(i,!0);if(s)return s(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var p=r[i]={exports:{}};e[i][0].call(p.exports,function(t){var r=e[i][1][t];return o(r||t)},p,p.exports,t,e,r,n)}return r[i].exports}for(var s="function"==typeof require&&require,i=0;i-1}),String.prototype.includes||(String.prototype.includes=function(t){return this.indexOf(t)>-1});var moveToAnotherArray=function(t,e,r){for(var n=0,o=t.length;n1&&c.shift(),this._hasParentSelector=null;var p=this._trace(c,e,["$"],a,u,r);return(p=p.filter(function(t){return t&&!t.isParentSelector})).length?1!==p.length||i||Array.isArray(p[0].value)?p.reduce(function(t,e){var r=o._getPreferredOutput(e);return s&&Array.isArray(r)?t=t.concat(r):t.push(r),t},[]):this._getPreferredOutput(p[0]):i?[]:void 0}},JSONPath.prototype._getPreferredOutput=function(t){var e=this.currResultType;switch(e){case"all":return t.path="string"==typeof t.path?t.path:JSONPath.toPathString(t.path),t;case"value":case"parent":case"parentProperty":return t[e];case"path":return JSONPath.toPathString(t[e]);case"pointer":return JSONPath.toPointer(t.path)}},JSONPath.prototype._handleCallback=function(t,e,r){if(e){var n=this._getPreferredOutput(t);t.path="string"==typeof t.path?t.path:JSONPath.toPathString(t.path),e(n,r,t)}},JSONPath.prototype._trace=function(t,e,r,n,o,s,i){function a(t){f.push(t)}function u(t){Array.isArray(t)?t.forEach(a):f.push(t)}var c,p=this;if(!t.length)return c={path:r,value:e,parent:n,parentProperty:o},this._handleCallback(c,s,"value"),c;var l=t[0],h=t.slice(1),f=[];if(("string"!=typeof l||i)&&e&&Object.prototype.hasOwnProperty.call(e,l))u(this._trace(h,e[l],push(r,l),e,l,s));else if("*"===l)this._walk(l,h,e,r,n,o,s,function(t,e,r,n,o,s,i,a){u(p._trace(unshift(t,r),n,o,s,i,a,!0))});else if(".."===l)u(this._trace(h,e,r,n,o,s)),this._walk(l,h,e,r,n,o,s,function(t,e,r,n,o,s,i,a){"object"==typeof n[t]&&u(p._trace(unshift(e,r),n[t],push(o,t),n,t,a))});else{if("^"===l)return this._hasParentSelector=!0,r.length?{path:r.slice(0,-1),expr:h,isParentSelector:!0}:[];if("~"===l)return c={path:push(r,l),value:o,parent:n,parentProperty:null},this._handleCallback(c,s,"property"),c;if("$"===l)u(this._trace(h,e,r,null,null,s));else if(/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(l))u(this._slice(l,h,e,r,n,o,s));else if(0===l.indexOf("?(")){if(this.currPreventEval)throw new Error("Eval [?(expr)] prevented in JSONPath expression.");this._walk(l,h,e,r,n,o,s,function(t,e,r,n,o,s,i,a){p._eval(e.replace(/^\?\((.*?)\)$/,"$1"),n[t],t,o,s,i)&&u(p._trace(unshift(t,r),n,o,s,i,a))})}else if("("===l[0]){if(this.currPreventEval)throw new Error("Eval [(expr)] prevented in JSONPath expression.");u(this._trace(unshift(this._eval(l,e,r[r.length-1],r.slice(0,-1),n,o),h),e,r,n,o,s))}else if("@"===l[0]){var d=!1,y=l.slice(1,-2);switch(y){case"scalar":e&&["object","function"].includes(typeof e)||(d=!0);break;case"boolean":case"string":case"undefined":case"function":typeof e===y&&(d=!0);break;case"number":typeof e===y&&isFinite(e)&&(d=!0);break;case"nonFinite":"number"!=typeof e||isFinite(e)||(d=!0);break;case"object":e&&typeof e===y&&(d=!0);break;case"array":Array.isArray(e)&&(d=!0);break;case"other":d=this.currOtherTypeCallback(e,r,n,o);break;case"integer":e!==+e||!isFinite(e)||e%1||(d=!0);break;case"null":null===e&&(d=!0)}if(d)return c={path:r,value:e,parent:n,parentProperty:o},this._handleCallback(c,s,"value"),c}else if("`"===l[0]&&e&&Object.prototype.hasOwnProperty.call(e,l.slice(1))){var v=l.slice(1);u(this._trace(h,e[v],push(r,v),e,v,s,!0))}else if(l.includes(",")){var g,m;for(g=l.split(","),m=0;m1)for(var r=1;r=2&&t._responseTimeoutTimer&&clearTimeout(t._responseTimeoutTimer),4==r){var n;try{n=e.status}catch(t){n=0}if(!n){if(t.timedout||t._aborted)return;return t.crossDomainError()}t.emit("end")}};var n=function(e,r){r.total>0&&(r.percent=r.loaded/r.total*100),r.direction=e,t.emit("progress",r)};if(this.hasListeners("progress"))try{e.onprogress=n.bind(null,"download"),e.upload&&(e.upload.onprogress=n.bind(null,"upload"))}catch(t){}try{this.username&&this.password?e.open(this.method,this.url,!0,this.username,this.password):e.open(this.method,this.url,!0)}catch(t){return this.callback(t)}if(this._withCredentials&&(e.withCredentials=!0),!this._formData&&"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof r&&!this._isHost(r)){var o=this._header["content-type"],s=this._serializer||m.serialize[o?o.split(";")[0]:""];!s&&u(o)&&(s=m.serialize["application/json"]),s&&(r=s(r))}for(var i in this.header)null!=this.header[i]&&this.header.hasOwnProperty(i)&&e.setRequestHeader(i,this.header[i]);return this._responseType&&(e.responseType=this._responseType),this.emit("request",this),e.send(void 0!==r?r:null),this},m.agent=function(){return new g},["GET","POST","OPTIONS","PATCH","PUT","DELETE"].forEach(function(t){g.prototype[t.toLowerCase()]=function(e,r){var n=new m.Request(t,e);return this._setDefaults(n),r&&n.end(r),n}}),g.prototype.del=g.prototype.delete,m.get=function(t,e,r){var n=m("GET",t);return"function"==typeof e&&(r=e,e=null),e&&n.query(e),r&&n.end(r),n},m.head=function(t,e,r){var n=m("HEAD",t);return"function"==typeof e&&(r=e,e=null),e&&n.query(e),r&&n.end(r),n},m.options=function(t,e,r){var n=m("OPTIONS",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n},m.del=l,m.delete=l,m.patch=function(t,e,r){var n=m("PATCH",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n},m.post=function(t,e,r){var n=m("POST",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n},m.put=function(t,e,r){var n=m("PUT",t);return"function"==typeof e&&(r=e,e=null),e&&n.send(e),r&&n.end(r),n}},{"./agent-base":7,"./is-object":9,"./request-base":10,"./response-base":11,"component-emitter":2}],9:[function(t,e,r){"use strict";e.exports=function(t){return null!==t&&"object"==typeof t}},{}],10:[function(t,e,r){"use strict";function n(t){if(t)return o(t)}function o(t){for(var e in n.prototype)t[e]=n.prototype[e];return t}var s=t("./is-object");e.exports=n,n.prototype.clearTimeout=function(){return clearTimeout(this._timer),clearTimeout(this._responseTimeoutTimer),delete this._timer,delete this._responseTimeoutTimer,this},n.prototype.parse=function(t){return this._parser=t,this},n.prototype.responseType=function(t){return this._responseType=t,this},n.prototype.serialize=function(t){return this._serializer=t,this},n.prototype.timeout=function(t){if(!t||"object"!=typeof t)return this._timeout=t,this._responseTimeout=0,this;for(var e in t)switch(e){case"deadline":this._timeout=t.deadline;break;case"response":this._responseTimeout=t.response;break;default:console.warn("Unknown timeout option",e)}return this},n.prototype.retry=function(t,e){return 0!==arguments.length&&!0!==t||(t=1),t<=0&&(t=0),this._maxRetries=t,this._retries=0,this._retryCallback=e,this};var i=["ECONNRESET","ETIMEDOUT","EADDRINFO","ESOCKETTIMEDOUT"];n.prototype._shouldRetry=function(t,e){if(!this._maxRetries||this._retries++>=this._maxRetries)return!1;if(this._retryCallback)try{var r=this._retryCallback(t,e);if(!0===r)return!0;if(!1===r)return!1}catch(t){console.error(t)}if(e&&e.status&&e.status>=500&&501!=e.status)return!0;if(t){if(t.code&&~i.indexOf(t.code))return!0;if(t.timeout&&"ECONNABORTED"==t.code)return!0;if(t.crossDomain)return!0}return!1},n.prototype._retry=function(){return this.clearTimeout(),this.req&&(this.req=null,this.req=this.request()),this._aborted=!1,this.timedout=!1,this._end()},n.prototype.then=function(t,e){if(!this._fullfilledPromise){var r=this;this._endCalled&&console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises"),this._fullfilledPromise=new Promise(function(t,e){r.end(function(r,n){r?e(r):t(n)})})}return this._fullfilledPromise.then(t,e)},n.prototype.catch=function(t){return this.then(void 0,t)},n.prototype.use=function(t){return t(this),this},n.prototype.ok=function(t){if("function"!=typeof t)throw Error("Callback required");return this._okCallback=t,this},n.prototype._isResponseOK=function(t){return!!t&&(this._okCallback?this._okCallback(t):t.status>=200&&t.status<300)},n.prototype.get=function(t){return this._header[t.toLowerCase()]},n.prototype.getHeader=n.prototype.get,n.prototype.set=function(t,e){if(s(t)){for(var r in t)this.set(r,t[r]);return this}return this._header[t.toLowerCase()]=e,this.header[t]=e,this},n.prototype.unset=function(t){return delete this._header[t.toLowerCase()],delete this.header[t],this},n.prototype.field=function(t,e){if(null===t||void 0===t)throw new Error(".field(name, val) name can not be empty");if(this._data&&console.error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()"),s(t)){for(var r in t)this.field(r,t[r]);return this}if(Array.isArray(e)){for(var n in e)this.field(t,e[n]);return this}if(null===e||void 0===e)throw new Error(".field(name, val) val can not be empty");return"boolean"==typeof e&&(e=""+e),this._getFormData().append(t,e),this},n.prototype.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)},n.prototype._auth=function(t,e,r,n){switch(r.type){case"basic":this.set("Authorization","Basic "+n(t+":"+e));break;case"auto":this.username=t,this.password=e;break;case"bearer":this.set("Authorization","Bearer "+t)}return this},n.prototype.withCredentials=function(t){return void 0==t&&(t=!0),this._withCredentials=t,this},n.prototype.redirects=function(t){return this._maxRedirects=t,this},n.prototype.maxResponseSize=function(t){if("number"!=typeof t)throw TypeError("Invalid argument");return this._maxResponseSize=t,this},n.prototype.toJSON=function(){return{method:this.method,url:this.url,data:this._data,headers:this._header}},n.prototype.send=function(t){var e=s(t),r=this._header["content-type"];if(this._formData&&console.error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()"),e&&!this._data)Array.isArray(t)?this._data=[]:this._isHost(t)||(this._data={});else if(t&&this._data&&this._isHost(this._data))throw Error("Can't merge these send calls");if(e&&s(this._data))for(var n in t)this._data[n]=t[n];else"string"==typeof t?(r||this.type("form"),r=this._header["content-type"],this._data="application/x-www-form-urlencoded"==r?this._data?this._data+"&"+t:t:(this._data||"")+t):this._data=t;return!e||this._isHost(t)?this:(r||this.type("json"),this)},n.prototype.sortQuery=function(t){return this._sort=void 0===t||t,this},n.prototype._finalizeQueryString=function(){var t=this._query.join("&");if(t&&(this.url+=(this.url.indexOf("?")>=0?"&":"?")+t),this._query.length=0,this._sort){var e=this.url.indexOf("?");if(e>=0){var r=this.url.substring(e+1).split("&");"function"==typeof this._sort?r.sort(this._sort):r.sort(),this.url=this.url.substring(0,e)+"?"+r.join("&")}}},n.prototype._appendQueryString=function(){console.trace("Unsupported")},n.prototype._timeoutError=function(t,e,r){if(!this._aborted){var n=new Error(t+e+"ms exceeded");n.timeout=e,n.code="ECONNABORTED",n.errno=r,this.timedout=!0,this.abort(),this.callback(n)}},n.prototype._setTimeouts=function(){var t=this;this._timeout&&!this._timer&&(this._timer=setTimeout(function(){t._timeoutError("Timeout of ",t._timeout,"ETIME")},this._timeout)),this._responseTimeout&&!this._responseTimeoutTimer&&(this._responseTimeoutTimer=setTimeout(function(){t._timeoutError("Response timeout of ",t._responseTimeout,"ETIMEDOUT")},this._responseTimeout))}},{"./is-object":9}],11:[function(t,e,r){"use strict";function n(t){if(t)return o(t)}function o(t){for(var e in n.prototype)t[e]=n.prototype[e];return t}var s=t("./utils");e.exports=n,n.prototype.get=function(t){return this.header[t.toLowerCase()]},n.prototype._setHeaderProperties=function(t){var e=t["content-type"]||"";this.type=s.type(e);var r=s.params(e);for(var n in r)this[n]=r[n];this.links={};try{t.link&&(this.links=s.parseLinks(t.link))}catch(t){}},n.prototype._setStatusProperties=function(t){var e=t/100|0;this.status=this.statusCode=t,this.statusType=e,this.info=1==e,this.ok=2==e,this.redirect=3==e,this.clientError=4==e,this.serverError=5==e,this.error=(4==e||5==e)&&this.toError(),this.created=201==t,this.accepted=202==t,this.noContent=204==t,this.badRequest=400==t,this.unauthorized=401==t,this.notAcceptable=406==t,this.forbidden=403==t,this.notFound=404==t,this.unprocessableEntity=422==t}},{"./utils":12}],12:[function(t,e,r){"use strict";r.type=function(t){return t.split(/ *; */).shift()},r.params=function(t){return t.split(/ *; */).reduce(function(t,e){var r=e.split(/ *= */),n=r.shift(),o=r.shift();return n&&o&&(t[n]=o),t},{})},r.parseLinks=function(t){return t.split(/ *, */).reduce(function(t,e){var r=e.split(/ *; */),n=r[0].slice(1,-1);return t[r[1].split(/ *= */)[1].slice(1,-1)]=n,t},{})},r.cleanHeader=function(t,e){return delete t["content-type"],delete t["content-length"],delete t["transfer-encoding"],delete t.host,e&&(delete t.authorization,delete t.cookie),t}},{}],13:[function(t,e,r){"use strict";function n(t){null==t&&(t=""),this.id=t}function o(t){return new n(t)}var s=!1;n.prototype.enable=function(){this.enabled=!0},n.prototype.debug=function(t){s&&console.log(this.id+"/debug: "+t)},n.prototype.info=function(t){s&&console.log(this.id+"/info: "+t)},n.prototype.warn=function(t){s&&console.log(this.id+"/warn: "+t)},n.prototype.error=function(t){s&&console.log(this.id+"/error: "+t)},o.enable=function(){s=!0},e.exports=o},{}],14:[function(t,e,r){"use strict";e.exports={isArray:function(t){return null!=t&&"[object Array]"===Object.prototype.toString.call(t)}}},{}],15:[function(t,e,r){"use strict";function n(){}function o(t,e){return e=e||{},s(t,e),i(t,e),a(t,e),u(t,e),c(t,e),p(t,e),t}function s(t,e){var r=e.qs;null!=r&&(t=t.query(r))}function i(t,e){var r=e.headers;null!=r&&(t=t.set(r))}function a(t,e){var r=e.auth;null!=r&&(t=t.auth(r.user||r.username,r.pass||r.password))}function u(t,e){if(null!=e){var r=e.body;null!=r&&(t=t.send(r))}}function c(t,e){if(null!=e){var r=e.form;null!=r&&(t=(t=t.set("content-type","application/x-www-form-urlencoded")).send(r))}}function p(t,e){null!=e&&!0===e.withCredentials&&t.withCredentials()}function l(t){return t.body=t.text,t.statusCode=t.status,t}function h(t){return function(e,r){if(e){if(!r)return t(e);t(null,l(r))}else t(null,l(r))}}var f=t("superagent");n.prototype.get=function(t,e,r){return o(f.get(t),e).end(h(r))},n.prototype.post=function(t,e,r){return o(f.post(t),e).end(h(r))},n.prototype.put=function(t,e,r){return o(f.put(t),e).end(h(r))},n.prototype.patch=function(t,e,r){return o(f.patch(t),e).end(h(r))},n.prototype.del=function(t,e,r){return o(f.del(t),e).end(h(r))},e.exports=new n},{superagent:8}],16:[function(t,e,r){"use strict";var n={startsWith:function(t,e){return""===e||null!=t&&null!=e&&(t=String(t),e=String(e),t.length>=e.length&&t.slice(0,e.length)===e)},endsWith:function(t,e){return""===e||null!=t&&null!=e&&(t=String(t),e=String(e),t.length>=e.length&&t.slice(t.length-e.length)===e)},splice:function(t,e,r,o){var s=n.chars(t);return s.splice(~~e,~~r,o),s.join("")},contains:function(t,e){return""===e||null!=t&&-1!==String(t).indexOf(e)},chars:function(t){return null==t?[]:String(t).split("")}};e.exports=n},{}],17:[function(t,e,r){"use strict";var n=t("resolve-url");r.resolve=function(t,e){return n(t,e)}},{"resolve-url":6}],18:[function(t,e,r){"use strict";var n=t("minilog"),o=t("./errors"),s=o.errors,i=o.createError,a=n("traverson");r.abortTraversal=function(){a.debug("aborting link traversal"),this.aborted=!0,this.currentRequest&&(a.debug("request in progress. trying to abort it, too."),this.currentRequest.abort())},r.registerAbortListener=function(t,e){t.currentRequest&&t.currentRequest.on("abort",function(){r.callCallbackOnAbort(t)})},r.callCallbackOnAbort=function(t){a.debug("link traversal aborted"),t.callbackHasBeenCalledAfterAbort||(t.callbackHasBeenCalledAfterAbort=!0,t.callback(r.abortError(),t))},r.abortError=function(){var t=i("Link traversal process has been aborted.",s.TraversalAbortedError);return t.aborted=!0,t}},{"./errors":21,minilog:13}],19:[function(t,e,r){"use strict";function n(t,e,r,n){var o;o=t.convertResponseToObject?[y,i,v,g,p]:[d],t.lastMethod=r,s.walk(t,o,n)}function o(t){return{abort:t.abortTraversal}}t("minilog")("traverson"),t("./abort_traversal"),t("./transforms/apply_transforms"),t("./http_requests"),t("./is_continuation");var s=t("./walker"),i=t("./transforms/check_http_status"),a=t("./transforms/continuation_to_doc"),u=t("./transforms/continuation_to_response"),c=t("./transforms/convert_embedded_doc_to_response"),p=t("./transforms/extract_doc"),l=t("./transforms/extract_response"),h=t("./transforms/extract_url"),f=t("./transforms/fetch_last_resource"),d=t("./transforms/execute_last_http_request"),y=t("./transforms/execute_http_request"),v=t("./transforms/parse"),g=t("./transforms/parse_link_header");r.get=function(t,e){var r;return r=t.convertResponseToObject?[a,f,i,v,g,p]:[u,f,c,l],s.walk(t,r,e),o(t)},r.getUrl=function(t,e){return s.walk(t,[h],e),o(t)},r.post=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.post,e),o(t)},r.put=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.put,e),o(t)},r.patch=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.patch,e),o(t)},r.delete=function(t,e){return n(t,t.requestModuleInstance,t.requestModuleInstance.del,e),o(t)}},{"./abort_traversal":18,"./http_requests":22,"./is_continuation":23,"./transforms/apply_transforms":30,"./transforms/check_http_status":31,"./transforms/continuation_to_doc":32,"./transforms/continuation_to_response":33,"./transforms/convert_embedded_doc_to_response":34,"./transforms/execute_http_request":36,"./transforms/execute_last_http_request":37,"./transforms/extract_doc":38,"./transforms/extract_response":39,"./transforms/extract_url":40,"./transforms/fetch_last_resource":41,"./transforms/parse":44,"./transforms/parse_link_header":45,"./walker":51,minilog:13}],20:[function(t,e,r){"use strict";function n(t,e){this.mediaType=t||T.CONTENT_NEGOTIATION,this.linkType=e||"link-rel",this.adapter=this._createAdapter(this.mediaType),this.autoHeaders=!0,this.contentNegotiation=!0,this.convertResponseToObjectFlag=!1,this.links=[],this.jsonParser=JSON.parse,this.requestModuleInstance=d,this.requestOptions={},this.resolveRelativeFlag=!1,this.preferEmbedded=!1,this.lastTraversalState=null,this.continuation=null}function o(t,e){for(var r=0;r continuing finished traversal process"),t.continuation={step:e.step,action:n},t.continuation.step.index=0,c(t,e),t}})}}function c(t,e){t.aborted=!1,t.adapter=e.adapter,t.body=e.body,t.callbackHasBeenCalledAfterAbort=!1,t.autoHeaders=e.autoHeaders,t.contentNegotiation=e.contentNegotiation,t.convertResponseToObjectFlag=e.convertResponseToObject,t.links=[],t.jsonParser=e.jsonParser,t.requestModuleInstance=e.requestModuleInstance,t.requestOptions=e.requestOptions,t.resolveRelativeFlag=e.resolveRelative,t.preferEmbedded=e.preferEmbedded,t.startUrl=e.startUrl,t.templateParameters=e.templateParameters}function p(t){return y.isArray(t)?h(t):"object"==typeof t?l(t):t}function l(t){return O(null,t)}function h(t){return t?t.slice(0):t}var f=t("minilog"),d=t("request"),y=t("util"),v=t("./actions"),g=t("./abort_traversal").abortTraversal,m=t("./errors"),b=m.errors,_=m.createError,w=t("./media_type_registry"),T=t("./media_types"),O=t("./merge_recursive"),x=f("traverson");n.prototype._createAdapter=function(t){var e=w.get(t);if(!e)throw _("Unknown or unsupported media type: "+t,b.UnsupportedMediaType);return x.debug("creating new "+e.name),new e(x)},n.prototype.newRequest=function(){var t=new n(this.getMediaType(),this.getLinkType());return t.useAutoHeaders(this.setsAutoHeaders()),t.contentNegotiation=this.doesContentNegotiation(),t.convertResponseToObject(this.convertsResponseToObject()),t.from(h(this.getFrom())),t.withTemplateParameters(p(this.getTemplateParameters())),t.withRequestOptions(p(this.getRequestOptions())),t.withRequestLibrary(this.getRequestLibrary()),t.parseResponseBodiesWith(this.getJsonParser()),t.resolveRelative(this.doesResolveRelative()),t.preferEmbeddedResources(this.doesPreferEmbeddedResources()),t.continuation=this.continuation,t},n.prototype.setMediaType=function(t){return this.mediaType=t||T.CONTENT_NEGOTIATION,this.adapter=this._createAdapter(t),this.contentNegotiation=t===T.CONTENT_NEGOTIATION,this},n.prototype.getLinkType=function(){return this.linkType},n.prototype.json=function(){return this.setMediaType(T.JSON),this},n.prototype.jsonHal=function(){return this.setMediaType(T.JSON_HAL),this},n.prototype.useContentNegotiation=function(){return this.setMediaType(T.CONTENT_NEGOTIATION),this.contentNegotiation=!0,this},n.prototype.from=function(t){return this.startUrl=t,this},n.prototype.follow=function(){for(var t=Array.prototype.slice.apply(1===arguments.length&&y.isArray(arguments[0])?arguments[0]:arguments),e=0;e1?c("JSONPath expression "+e+" returned more than one match in document:\n"+JSON.stringify(t),u.JSONPathError,t):c("JSONPath expression "+e+" returned no match in document:\n"+JSON.stringify(t),u.JSONPathError,t)},n.prototype._handleHeader=function(t,e){switch(e.value){case"location":var r=t.headers.location;if(!r)throw c("Following the location header but there was no location header in the last response.",u.LinkError,t.headers);return{url:r};default:throw c("Link objects with type header and value "+e.value+" are not supported by this adapter.",u.InvalidArgumentError,e)}},n.prototype._handleLinkHeader=function(t,e){if(!t.headers.link)throw c("There was no link header in the last response.",u.InvalidArgumentError,e);var r=p(t.headers.link);if(r[e.value])return{url:r[e.value].url};throw c("Link with relation "+e.value+" not found in link header.",u.InvalidArgumentError,e)},e.exports=n},{"./errors":21,"./parse_link_header_value":29,"jsonpath-plus":4,minilog:13,"underscore.string":16}],25:[function(t,e,r){"use strict";var n=t("./media_types"),o={};r.register=function(t,e){o[t]=e},r.get=function(t){return o[t]},r.register(n.CONTENT_NEGOTIATION,t("./negotiation_adapter")),r.register(n.JSON,t("./json_adapter"))},{"./json_adapter":24,"./media_types":26,"./negotiation_adapter":28}],26:[function(t,e,r){"use strict";var n=t("./json_adapter");e.exports={CONTENT_NEGOTIATION:"content-negotiation",JSON:n.mediaType,JSON_HAL:"application/hal+json"}},{"./json_adapter":24}],27:[function(t,e,r){"use strict";function n(t,e){!t&&e&&(t={});for(var r in e)e.hasOwnProperty(r)&&o(t,e,r);return t}function o(t,e,r){"object"==typeof e[r]?(t[r]&&"object"==typeof t[r]||(t[r]={}),n(t[r],e[r])):t[r]=e[r]}e.exports=n},{}],28:[function(t,e,r){"use strict";function n(t){}var o=t("./errors"),s=o.errors,i=o.createError;n.prototype.findNextStep=function(t,e){throw i("Content negotiation did not happen",s.InvalidStateError)},e.exports=n},{"./errors":21}],29:[function(t,e,r){function n(t){try{var e=t.split(";"),r=e.shift().replace(/[<>]/g,""),n=e.reduce(o,{});return n.url=r,n}catch(t){return null}}function o(t,e){var r=e.match(/\s*(.+)\s*=\s*"?([^"]+)"?/);return r&&(t[r[1]]=r[2]),t}function s(t){return t&&t.rel}function i(t,e){return e.rel.split(/\s+/).forEach(function(r){t[r]=a({rel:r},e)}),t}var a=t("./merge_recursive");e.exports=function(t){return t?t.split(/,\s*=300)){var r=n(t.step.url,e,t.step.response.body);return o.error("unexpected http status code"),o.error(r),t.callback(r),!1}return o.debug("http status code ok ("+e+")"),!0}},{"../errors":21,"../is_continuation":23,minilog:13}],32:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("../is_continuation");e.exports=function(t){return!o(t)||(n.debug("continuing from last traversal process (actions)"),t.continuation=null,t.callback(null,t.step.doc),!1)}},{"../is_continuation":23,minilog:13}],33:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("./convert_embedded_doc_to_response"),s=t("../is_continuation");e.exports=function(t){return!s(t)||(n.debug("continuing from last traversal process (actions)"),t.continuation=null,o(t),t.callback(null,t.step.response),!1)}},{"../is_continuation":23,"./convert_embedded_doc_to_response":34,minilog:13}],34:[function(t,e,r){"use strict";var n=t("minilog")("traverson");e.exports=function(t){return!t.step.response&&t.step.doc&&(n.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."}),!0}},{minilog:13}],35:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("../media_type_registry"),s=t("../errors"),i=s.errors,a=s.createError;e.exports=function(t,e){if(t.contentNegotiation&&t.step.response&&t.step.response.headers&&t.step.response.headers["content-type"]){var r=t.step.response.headers["content-type"].split(/[; ]/)[0];n.debug("found content type string",r);var s=o.get(r);if(!s)return n.error("no adapter for content type",r),e(a("Unknown content type for content type detection: "+r,i.UnsupportedMediaType),t),!1;t.adapter=new s(n),n.debug("switched to media type adapter",t.adapter.name||t.adapter.constructor.name)}return!0}},{"../errors":21,"../media_type_registry":25,minilog:13}],36:[function(t,e,r){"use strict";function n(t,e){if(t.aborted)return s.callCallbackOnAbort(t);i.executeHttpRequest(t,t.requestModuleInstance,t.lastMethod,function(r,n){if(r)return r.aborted||(o.debug("error while executing http request, processing step ",t.step),o.error(r)),t.callback(r);e(t)})}var o=t("minilog")("traverson"),s=t("../abort_traversal"),i=t("../http_requests");n.isAsync=!0,e.exports=n},{"../abort_traversal":18,"../http_requests":22,minilog:13}],37:[function(t,e,r){"use strict";function n(t,e){if(t.aborted)return o.callCallbackOnAbort(t);s.executeHttpRequest(t,t.requestModuleInstance,t.lastMethod,t.callback)}t("minilog")("traverson");var o=t("../abort_traversal"),s=t("../http_requests");n.isAsync=!0,e.exports=n},{"../abort_traversal":18,"../http_requests":22,minilog:13}],38:[function(t,e,r){"use strict";var n=t("minilog")("traverson");e.exports=function(t){return n.debug("walker.walk has finished"),t.callback(null,t.step.doc),!1}},{minilog:13}],39:[function(t,e,r){"use strict";var n=t("minilog")("traverson");e.exports=function(t){return n.debug("walker.walk has finished"),t.callback(null,t.step.response),!1}},{minilog:13}],40:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("url"),s=t("../errors"),i=s.errors,a=s.createError;e.exports=function(t){return n.debug("walker.walk has finished"),t.step.url?t.callback(null,t.step.url):t.step.doc&&t.step.doc._links&&t.step.doc._links.self&&t.step.doc._links.self.href?t.callback(null,o.resolve(t.startUrl,t.step.doc._links.self.href)):t.callback(a('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"',i.LinkError))}},{"../errors":21,minilog:13,url:17}],41:[function(t,e,r){"use strict";function n(t,e){if(t.aborted)return s.callCallbackOnAbort(t);i.fetchResource(t,function(t,r){if(o.debug("fetchResource returned (fetchLastResource)."),t)return t.aborted||(o.debug("error while executing http request"),o.error(t)),r.callback(t);e(r)})}var o=t("minilog")("traverson"),s=t("../abort_traversal"),i=t("../http_requests");n.isAsync=!0,e.exports=n},{"../abort_traversal":18,"../http_requests":22,minilog:13}],42:[function(t,e,r){(function(r){"use strict";function n(t,e){u(t)?o(t,e):s(t,e)}function o(t,e){i.debug("continuing from last traversal process (walker)"),r.nextTick(function(){e(t)})}function s(t,e){if(t.aborted)return a.callCallbackOnAbort(t);c.fetchResource(t,function(t,r){if(i.debug("fetchResource returned"),t)return t.aborted||(i.debug("error while executing http request"),i.error(t)),r.callback(t);e(r)})}var i=t("minilog")("traverson"),a=t("../abort_traversal"),u=t("../is_continuation"),c=t("../http_requests");n.isAsync=!0,e.exports=n}).call(this,t("_process"))},{"../abort_traversal":18,"../http_requests":22,"../is_continuation":23,_process:5,minilog:13}],43:[function(t,e,r){"use strict";function n(t,e){var r=t.adapter.constructor.mediaType||t.adapter.mediaType;r&&(e.headers?e.headers=a(o(e,r),e.headers):e.headers=o(e,r))}function o(t,e){return t.form?{Accept:e}:{Accept:e,"Content-Type":e}}var s=t("minilog")("traverson"),i=t("util"),a=t("../merge_recursive");e.exports=function(t){var e=t.requestOptions;return i.isArray(t.requestOptions)&&(e=t.requestOptions[t.step.index]||{}),t.autoHeaders&&n(t,e),s.debug("options",e),e}},{"../merge_recursive":27,minilog:13,util:14}],44:[function(t,e,r){"use strict";function n(t){i.debug("parsing response body"),t.requestOptions&&t.requestOptions.jsonReviver?t.step.doc=t.step.response.body:t.step.doc=t.jsonParser(t.step.response.body)}function o(t,e){var r=e;"SyntaxError"===e.name&&(r=s(t.step.url,t.step.response.body)),i.error("parsing failed"),i.error(r),t.callback(r)}function s(t,e){var r=c("The document at "+t+" could not be parsed as JSON: "+e,u.JSONError,e);return r.url=t,r.body=e,r}var i=t("minilog")("traverson"),a=t("../errors"),u=a.errors,c=a.createError,p=t("../is_continuation");e.exports=function(t){if(p(t)&&(i.debug("continuing from last traversal process (transforms/parse)"),"getResource"===t.continuation.action))return!0;if(t.step.doc)return i.debug("no parsing necessary, probably an embedded document"),!0;try{return n(t),!0}catch(e){return o(t,e),!1}}},{"../errors":21,"../is_continuation":23,minilog:13}],45:[function(t,e,r){"use strict";function n(t,e){o.error("failed to parse link header"),o.error(e),t.callback(e)}var o=t("minilog")("traverson"),s=t("../parse_link_header_value");e.exports=function(t){if(!(t.step.doc&&t.step.response&&t.step.response.headers&&t.step.response.headers.link))return!0;try{var e=s(t.step.response.headers.link);return t.step.doc._linkHeaders=e,!0}catch(e){return n(t,e),!1}}},{"../parse_link_header_value":29,minilog:13}],46:[function(t,e,r){"use strict";var n=t("../is_continuation");e.exports=function(t){return!!n(t)||(t.continuation=null,!0)}},{"../is_continuation":23}],47:[function(t,e,r){"use strict";var n=t("../is_continuation");e.exports=function(t){return!!n(t)||(t.lastStep=null,!0)}},{"../is_continuation":23}],48:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("underscore.string"),s=t("url"),i=/https?:\/\//i;e.exports=function(t){return t.step.url&&0!==t.step.url.search(i)&&(n.debug("found non full qualified URL"),t.resolveRelative&&t.lastStep&&t.lastStep.url?(n.debug("resolving URL relative"),o.startsWith(t.step.url,"/")&&o.endsWith(t.lastStep.url,"/")&&(t.step.url=o.splice(t.step.url,0,1)),t.step.url=t.lastStep.url+t.step.url):t.step.url=s.resolve(t.startUrl,t.step.url)),!0}},{minilog:13,"underscore.string":16,url:17}],49:[function(t,e,r){"use strict";var n=t("minilog")("traverson"),o=t("underscore.string"),s=t("url-template"),i=t("util");e.exports=function(t){if(t.step.url){var e=t.templateParameters;if(i.isArray(e)&&(e=e[t.step.index]),e=e||{},o.contains(t.step.url,"{")){n.debug("resolving URI template");var r=s.parse(t.step.url).expand(e);n.debug("resolved to ",r),t.step.url=r}}return!0}},{minilog:13,"underscore.string":16,"url-template":53,util:14}],50:[function(t,e,r){"use strict";function n(t,e){try{return t.adapter.findNextStep(t,e)}catch(e){return o.error("could not find next step"),o.error(e),t.callback(e),null}}var o=t("minilog")("traverson");e.exports=function(t){var e=t.links[t.step.index];return o.debug("next link: "+e),t.lastStep=t.step,t.step=n(t,e),!!t.step&&(o.debug("found next step",t.step),t.step.index=t.lastStep.index+1,!0)}},{minilog:13}],51:[function(t,e,r){"use strict";function n(t,e){if(i.debug("processing next step"),o(t)&&!s(t))u(p,t,function(t){i.debug("successfully processed step"),n(t,e)});else{if(s(t))return a.callCallbackOnAbort(t);i.debug("link array exhausted"),u(e,t,function(t){return t.callback()})}}function o(t){return t.step.index=100&&e.status<600?t(null,p(e)):t(e)}}return r.prototype.get=function(e,r,s){return r=o(r),t.get(e,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.post=function(e,r,s){return r=o(r),t.post(e,r.data,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.put=function(e,r,s){return r=o(r),t.put(e,r.data,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.patch=function(e,r,s){return r=o(r),t.patch(e,r.data,r).then(l(s)).catch(h(s)),new n(r.timeout)},r.prototype.del=function(e,r,s){return r=o(r),t.delete(e,r).then(l(s)).catch(h(s)),new n(r.timeout)},new r}]),n.prototype.abort=function(){this.abortPromise.resolve(),this.listeners.forEach(function(t){t.call()})},n.prototype.on=function(t,e){if("abort"!==t){var r=new Error("Event "+t+" not supported");throw r.name="InvalidArgumentError",r}this.listeners.push(e)},e.exports=i},{angular:1,traverson:52}]},{},[55])(55)}); \ No newline at end of file