Last active
February 14, 2019 08:17
-
-
Save justrhysism/7ad1279eb4e839c97f403861a94c0d3c to your computer and use it in GitHub Desktop.
Pretender v3.0.0 (Browser Ready)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Pretender = (function () { | |
'use strict'; | |
// This is just a placeholder for the build step | |
var createObject = Object.create; | |
function createMap() { | |
var map = createObject(null); | |
map["__"] = undefined; | |
delete map["__"]; | |
return map; | |
} | |
var Target = function Target(path, matcher, delegate) { | |
this.path = path; | |
this.matcher = matcher; | |
this.delegate = delegate; | |
}; | |
Target.prototype.to = function to (target, callback) { | |
var delegate = this.delegate; | |
if (delegate && delegate.willAddRoute) { | |
target = delegate.willAddRoute(this.matcher.target, target); | |
} | |
this.matcher.add(this.path, target); | |
if (callback) { | |
if (callback.length === 0) { | |
throw new Error("You must have an argument in the function passed to `to`"); | |
} | |
this.matcher.addChild(this.path, target, callback, this.delegate); | |
} | |
}; | |
var Matcher = function Matcher(target) { | |
this.routes = createMap(); | |
this.children = createMap(); | |
this.target = target; | |
}; | |
Matcher.prototype.add = function add (path, target) { | |
this.routes[path] = target; | |
}; | |
Matcher.prototype.addChild = function addChild (path, target, callback, delegate) { | |
var matcher = new Matcher(target); | |
this.children[path] = matcher; | |
var match = generateMatch(path, matcher, delegate); | |
if (delegate && delegate.contextEntered) { | |
delegate.contextEntered(target, match); | |
} | |
callback(match); | |
}; | |
function generateMatch(startingPath, matcher, delegate) { | |
function match(path, callback) { | |
var fullPath = startingPath + path; | |
if (callback) { | |
callback(generateMatch(fullPath, matcher, delegate)); | |
} | |
else { | |
return new Target(fullPath, matcher, delegate); | |
} | |
} | |
return match; | |
} | |
function addRoute(routeArray, path, handler) { | |
var len = 0; | |
for (var i = 0; i < routeArray.length; i++) { | |
len += routeArray[i].path.length; | |
} | |
path = path.substr(len); | |
var route = { path: path, handler: handler }; | |
routeArray.push(route); | |
} | |
function eachRoute(baseRoute, matcher, callback, binding) { | |
var routes = matcher.routes; | |
var paths = Object.keys(routes); | |
for (var i = 0; i < paths.length; i++) { | |
var path = paths[i]; | |
var routeArray = baseRoute.slice(); | |
addRoute(routeArray, path, routes[path]); | |
var nested = matcher.children[path]; | |
if (nested) { | |
eachRoute(routeArray, nested, callback, binding); | |
} | |
else { | |
callback.call(binding, routeArray); | |
} | |
} | |
} | |
var map = function (callback, addRouteCallback) { | |
var matcher = new Matcher(); | |
callback(generateMatch("", matcher, this.delegate)); | |
eachRoute([], matcher, function (routes) { | |
if (addRouteCallback) { | |
addRouteCallback(this, routes); | |
} | |
else { | |
this.add(routes); | |
} | |
}, this); | |
}; | |
// Normalizes percent-encoded values in `path` to upper-case and decodes percent-encoded | |
// values that are not reserved (i.e., unicode characters, emoji, etc). The reserved | |
// chars are "/" and "%". | |
// Safe to call multiple times on the same path. | |
// Normalizes percent-encoded values in `path` to upper-case and decodes percent-encoded | |
function normalizePath(path) { | |
return path.split("/") | |
.map(normalizeSegment) | |
.join("/"); | |
} | |
// We want to ensure the characters "%" and "/" remain in percent-encoded | |
// form when normalizing paths, so replace them with their encoded form after | |
// decoding the rest of the path | |
var SEGMENT_RESERVED_CHARS = /%|\//g; | |
function normalizeSegment(segment) { | |
if (segment.length < 3 || segment.indexOf("%") === -1) | |
{ return segment; } | |
return decodeURIComponent(segment).replace(SEGMENT_RESERVED_CHARS, encodeURIComponent); | |
} | |
// We do not want to encode these characters when generating dynamic path segments | |
// See https://tools.ietf.org/html/rfc3986#section-3.3 | |
// sub-delims: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" | |
// others allowed by RFC 3986: ":", "@" | |
// | |
// First encode the entire path segment, then decode any of the encoded special chars. | |
// | |
// The chars "!", "'", "(", ")", "*" do not get changed by `encodeURIComponent`, | |
// so the possible encoded chars are: | |
// ['%24', '%26', '%2B', '%2C', '%3B', '%3D', '%3A', '%40']. | |
var PATH_SEGMENT_ENCODINGS = /%(?:2(?:4|6|B|C)|3(?:B|D|A)|40)/g; | |
function encodePathSegment(str) { | |
return encodeURIComponent(str).replace(PATH_SEGMENT_ENCODINGS, decodeURIComponent); | |
} | |
var escapeRegex = /(\/|\.|\*|\+|\?|\||\(|\)|\[|\]|\{|\}|\\)/g; | |
var isArray = Array.isArray; | |
var hasOwnProperty = Object.prototype.hasOwnProperty; | |
function getParam(params, key) { | |
if (typeof params !== "object" || params === null) { | |
throw new Error("You must pass an object as the second argument to `generate`."); | |
} | |
if (!hasOwnProperty.call(params, key)) { | |
throw new Error("You must provide param `" + key + "` to `generate`."); | |
} | |
var value = params[key]; | |
var str = typeof value === "string" ? value : "" + value; | |
if (str.length === 0) { | |
throw new Error("You must provide a param `" + key + "`."); | |
} | |
return str; | |
} | |
var eachChar = []; | |
eachChar[0 /* Static */] = function (segment, currentState) { | |
var state = currentState; | |
var value = segment.value; | |
for (var i = 0; i < value.length; i++) { | |
var ch = value.charCodeAt(i); | |
state = state.put(ch, false, false); | |
} | |
return state; | |
}; | |
eachChar[1 /* Dynamic */] = function (_, currentState) { | |
return currentState.put(47 /* SLASH */, true, true); | |
}; | |
eachChar[2 /* Star */] = function (_, currentState) { | |
return currentState.put(-1 /* ANY */, false, true); | |
}; | |
eachChar[4 /* Epsilon */] = function (_, currentState) { | |
return currentState; | |
}; | |
var regex = []; | |
regex[0 /* Static */] = function (segment) { | |
return segment.value.replace(escapeRegex, "\\$1"); | |
}; | |
regex[1 /* Dynamic */] = function () { | |
return "([^/]+)"; | |
}; | |
regex[2 /* Star */] = function () { | |
return "(.+)"; | |
}; | |
regex[4 /* Epsilon */] = function () { | |
return ""; | |
}; | |
var generate = []; | |
generate[0 /* Static */] = function (segment) { | |
return segment.value; | |
}; | |
generate[1 /* Dynamic */] = function (segment, params) { | |
var value = getParam(params, segment.value); | |
if (RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS) { | |
return encodePathSegment(value); | |
} | |
else { | |
return value; | |
} | |
}; | |
generate[2 /* Star */] = function (segment, params) { | |
return getParam(params, segment.value); | |
}; | |
generate[4 /* Epsilon */] = function () { | |
return ""; | |
}; | |
var EmptyObject = Object.freeze({}); | |
var EmptyArray = Object.freeze([]); | |
// The `names` will be populated with the paramter name for each dynamic/star | |
// segment. `shouldDecodes` will be populated with a boolean for each dyanamic/star | |
// segment, indicating whether it should be decoded during recognition. | |
function parse(segments, route, types) { | |
// normalize route as not starting with a "/". Recognition will | |
// also normalize. | |
if (route.length > 0 && route.charCodeAt(0) === 47 /* SLASH */) { | |
route = route.substr(1); | |
} | |
var parts = route.split("/"); | |
var names = undefined; | |
var shouldDecodes = undefined; | |
for (var i = 0; i < parts.length; i++) { | |
var part = parts[i]; | |
var flags = 0; | |
var type = 0; | |
if (part === "") { | |
type = 4 /* Epsilon */; | |
} | |
else if (part.charCodeAt(0) === 58 /* COLON */) { | |
type = 1 /* Dynamic */; | |
} | |
else if (part.charCodeAt(0) === 42 /* STAR */) { | |
type = 2 /* Star */; | |
} | |
else { | |
type = 0 /* Static */; | |
} | |
flags = 2 << type; | |
if (flags & 12 /* Named */) { | |
part = part.slice(1); | |
names = names || []; | |
names.push(part); | |
shouldDecodes = shouldDecodes || []; | |
shouldDecodes.push((flags & 4 /* Decoded */) !== 0); | |
} | |
if (flags & 14 /* Counted */) { | |
types[type]++; | |
} | |
segments.push({ | |
type: type, | |
value: normalizeSegment(part) | |
}); | |
} | |
return { | |
names: names || EmptyArray, | |
shouldDecodes: shouldDecodes || EmptyArray, | |
}; | |
} | |
function isEqualCharSpec(spec, char, negate) { | |
return spec.char === char && spec.negate === negate; | |
} | |
// A State has a character specification and (`charSpec`) and a list of possible | |
// subsequent states (`nextStates`). | |
// | |
// If a State is an accepting state, it will also have several additional | |
// properties: | |
// | |
// * `regex`: A regular expression that is used to extract parameters from paths | |
// that reached this accepting state. | |
// * `handlers`: Information on how to convert the list of captures into calls | |
// to registered handlers with the specified parameters | |
// * `types`: How many static, dynamic or star segments in this route. Used to | |
// decide which route to use if multiple registered routes match a path. | |
// | |
// Currently, State is implemented naively by looping over `nextStates` and | |
// comparing a character specification against a character. A more efficient | |
// implementation would use a hash of keys pointing at one or more next states. | |
var State = function State(states, id, char, negate, repeat) { | |
this.states = states; | |
this.id = id; | |
this.char = char; | |
this.negate = negate; | |
this.nextStates = repeat ? id : null; | |
this.pattern = ""; | |
this._regex = undefined; | |
this.handlers = undefined; | |
this.types = undefined; | |
}; | |
State.prototype.regex = function regex$1 () { | |
if (!this._regex) { | |
this._regex = new RegExp(this.pattern); | |
} | |
return this._regex; | |
}; | |
State.prototype.get = function get (char, negate) { | |
var this$1 = this; | |
var nextStates = this.nextStates; | |
if (nextStates === null) | |
{ return; } | |
if (isArray(nextStates)) { | |
for (var i = 0; i < nextStates.length; i++) { | |
var child = this$1.states[nextStates[i]]; | |
if (isEqualCharSpec(child, char, negate)) { | |
return child; | |
} | |
} | |
} | |
else { | |
var child$1 = this.states[nextStates]; | |
if (isEqualCharSpec(child$1, char, negate)) { | |
return child$1; | |
} | |
} | |
}; | |
State.prototype.put = function put (char, negate, repeat) { | |
var state; | |
// If the character specification already exists in a child of the current | |
// state, just return that state. | |
if (state = this.get(char, negate)) { | |
return state; | |
} | |
// Make a new state for the character spec | |
var states = this.states; | |
state = new State(states, states.length, char, negate, repeat); | |
states[states.length] = state; | |
// Insert the new state as a child of the current state | |
if (this.nextStates == null) { | |
this.nextStates = state.id; | |
} | |
else if (isArray(this.nextStates)) { | |
this.nextStates.push(state.id); | |
} | |
else { | |
this.nextStates = [this.nextStates, state.id]; | |
} | |
// Return the new state | |
return state; | |
}; | |
// Find a list of child states matching the next character | |
State.prototype.match = function match (ch) { | |
var this$1 = this; | |
var nextStates = this.nextStates; | |
if (!nextStates) | |
{ return []; } | |
var returned = []; | |
if (isArray(nextStates)) { | |
for (var i = 0; i < nextStates.length; i++) { | |
var child = this$1.states[nextStates[i]]; | |
if (isMatch(child, ch)) { | |
returned.push(child); | |
} | |
} | |
} | |
else { | |
var child$1 = this.states[nextStates]; | |
if (isMatch(child$1, ch)) { | |
returned.push(child$1); | |
} | |
} | |
return returned; | |
}; | |
function isMatch(spec, char) { | |
return spec.negate ? spec.char !== char && spec.char !== -1 /* ANY */ : spec.char === char || spec.char === -1 /* ANY */; | |
} | |
// This is a somewhat naive strategy, but should work in a lot of cases | |
// A better strategy would properly resolve /posts/:id/new and /posts/edit/:id. | |
// | |
// This strategy generally prefers more static and less dynamic matching. | |
// Specifically, it | |
// | |
// * prefers fewer stars to more, then | |
// * prefers using stars for less of the match to more, then | |
// * prefers fewer dynamic segments to more, then | |
// * prefers more static segments to more | |
function sortSolutions(states) { | |
return states.sort(function (a, b) { | |
var ref = a.types || [0, 0, 0]; | |
var astatics = ref[0]; | |
var adynamics = ref[1]; | |
var astars = ref[2]; | |
var ref$1 = b.types || [0, 0, 0]; | |
var bstatics = ref$1[0]; | |
var bdynamics = ref$1[1]; | |
var bstars = ref$1[2]; | |
if (astars !== bstars) { | |
return astars - bstars; | |
} | |
if (astars) { | |
if (astatics !== bstatics) { | |
return bstatics - astatics; | |
} | |
if (adynamics !== bdynamics) { | |
return bdynamics - adynamics; | |
} | |
} | |
if (adynamics !== bdynamics) { | |
return adynamics - bdynamics; | |
} | |
if (astatics !== bstatics) { | |
return bstatics - astatics; | |
} | |
return 0; | |
}); | |
} | |
function recognizeChar(states, ch) { | |
var nextStates = []; | |
for (var i = 0, l = states.length; i < l; i++) { | |
var state = states[i]; | |
nextStates = nextStates.concat(state.match(ch)); | |
} | |
return nextStates; | |
} | |
var RecognizeResults = function RecognizeResults(queryParams) { | |
this.length = 0; | |
this.queryParams = queryParams || {}; | |
}; | |
RecognizeResults.prototype.splice = Array.prototype.splice; | |
RecognizeResults.prototype.slice = Array.prototype.slice; | |
RecognizeResults.prototype.push = Array.prototype.push; | |
function findHandler(state, originalPath, queryParams) { | |
var handlers = state.handlers; | |
var regex = state.regex(); | |
if (!regex || !handlers) | |
{ throw new Error("state not initialized"); } | |
var captures = originalPath.match(regex); | |
var currentCapture = 1; | |
var result = new RecognizeResults(queryParams); | |
result.length = handlers.length; | |
for (var i = 0; i < handlers.length; i++) { | |
var handler = handlers[i]; | |
var names = handler.names; | |
var shouldDecodes = handler.shouldDecodes; | |
var params = EmptyObject; | |
var isDynamic = false; | |
if (names !== EmptyArray && shouldDecodes !== EmptyArray) { | |
for (var j = 0; j < names.length; j++) { | |
isDynamic = true; | |
var name = names[j]; | |
var capture = captures && captures[currentCapture++]; | |
if (params === EmptyObject) { | |
params = {}; | |
} | |
if (RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS && shouldDecodes[j]) { | |
params[name] = capture && decodeURIComponent(capture); | |
} | |
else { | |
params[name] = capture; | |
} | |
} | |
} | |
result[i] = { | |
handler: handler.handler, | |
params: params, | |
isDynamic: isDynamic | |
}; | |
} | |
return result; | |
} | |
function decodeQueryParamPart(part) { | |
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 | |
part = part.replace(/\+/gm, "%20"); | |
var result; | |
try { | |
result = decodeURIComponent(part); | |
} | |
catch (error) { | |
result = ""; | |
} | |
return result; | |
} | |
var RouteRecognizer = function RouteRecognizer() { | |
this.names = createMap(); | |
var states = []; | |
var state = new State(states, 0, -1 /* ANY */, true, false); | |
states[0] = state; | |
this.states = states; | |
this.rootState = state; | |
}; | |
RouteRecognizer.prototype.add = function add (routes, options) { | |
var currentState = this.rootState; | |
var pattern = "^"; | |
var types = [0, 0, 0]; | |
var handlers = new Array(routes.length); | |
var allSegments = []; | |
var isEmpty = true; | |
var j = 0; | |
for (var i = 0; i < routes.length; i++) { | |
var route = routes[i]; | |
var ref = parse(allSegments, route.path, types); | |
var names = ref.names; | |
var shouldDecodes = ref.shouldDecodes; | |
// preserve j so it points to the start of newly added segments | |
for (; j < allSegments.length; j++) { | |
var segment = allSegments[j]; | |
if (segment.type === 4 /* Epsilon */) { | |
continue; | |
} | |
isEmpty = false; | |
// Add a "/" for the new segment | |
currentState = currentState.put(47 /* SLASH */, false, false); | |
pattern += "/"; | |
// Add a representation of the segment to the NFA and regex | |
currentState = eachChar[segment.type](segment, currentState); | |
pattern += regex[segment.type](segment); | |
} | |
handlers[i] = { | |
handler: route.handler, | |
names: names, | |
shouldDecodes: shouldDecodes | |
}; | |
} | |
if (isEmpty) { | |
currentState = currentState.put(47 /* SLASH */, false, false); | |
pattern += "/"; | |
} | |
currentState.handlers = handlers; | |
currentState.pattern = pattern + "$"; | |
currentState.types = types; | |
var name; | |
if (typeof options === "object" && options !== null && options.as) { | |
name = options.as; | |
} | |
if (name) { | |
// if (this.names[name]) { | |
// throw new Error("You may not add a duplicate route named `" + name + "`."); | |
// } | |
this.names[name] = { | |
segments: allSegments, | |
handlers: handlers | |
}; | |
} | |
}; | |
RouteRecognizer.prototype.handlersFor = function handlersFor (name) { | |
var route = this.names[name]; | |
if (!route) { | |
throw new Error("There is no route named " + name); | |
} | |
var result = new Array(route.handlers.length); | |
for (var i = 0; i < route.handlers.length; i++) { | |
var handler = route.handlers[i]; | |
result[i] = handler; | |
} | |
return result; | |
}; | |
RouteRecognizer.prototype.hasRoute = function hasRoute (name) { | |
return !!this.names[name]; | |
}; | |
RouteRecognizer.prototype.generate = function generate$1 (name, params) { | |
var route = this.names[name]; | |
var output = ""; | |
if (!route) { | |
throw new Error("There is no route named " + name); | |
} | |
var segments = route.segments; | |
for (var i = 0; i < segments.length; i++) { | |
var segment = segments[i]; | |
if (segment.type === 4 /* Epsilon */) { | |
continue; | |
} | |
output += "/"; | |
output += generate[segment.type](segment, params); | |
} | |
if (output.charAt(0) !== "/") { | |
output = "/" + output; | |
} | |
if (params && params.queryParams) { | |
output += this.generateQueryString(params.queryParams); | |
} | |
return output; | |
}; | |
RouteRecognizer.prototype.generateQueryString = function generateQueryString (params) { | |
var pairs = []; | |
var keys = Object.keys(params); | |
keys.sort(); | |
for (var i = 0; i < keys.length; i++) { | |
var key = keys[i]; | |
var value = params[key]; | |
if (value == null) { | |
continue; | |
} | |
var pair = encodeURIComponent(key); | |
if (isArray(value)) { | |
for (var j = 0; j < value.length; j++) { | |
var arrayPair = key + "[]" + "=" + encodeURIComponent(value[j]); | |
pairs.push(arrayPair); | |
} | |
} | |
else { | |
pair += "=" + encodeURIComponent(value); | |
pairs.push(pair); | |
} | |
} | |
if (pairs.length === 0) { | |
return ""; | |
} | |
return "?" + pairs.join("&"); | |
}; | |
RouteRecognizer.prototype.parseQueryString = function parseQueryString (queryString) { | |
var pairs = queryString.split("&"); | |
var queryParams = {}; | |
for (var i = 0; i < pairs.length; i++) { | |
var pair = pairs[i].split("="), key = decodeQueryParamPart(pair[0]), keyLength = key.length, isArray = false, value = (void 0); | |
if (pair.length === 1) { | |
value = "true"; | |
} | |
else { | |
// Handle arrays | |
if (keyLength > 2 && key.slice(keyLength - 2) === "[]") { | |
isArray = true; | |
key = key.slice(0, keyLength - 2); | |
if (!queryParams[key]) { | |
queryParams[key] = []; | |
} | |
} | |
value = pair[1] ? decodeQueryParamPart(pair[1]) : ""; | |
} | |
if (isArray) { | |
queryParams[key].push(value); | |
} | |
else { | |
queryParams[key] = value; | |
} | |
} | |
return queryParams; | |
}; | |
RouteRecognizer.prototype.recognize = function recognize (path) { | |
var results; | |
var states = [this.rootState]; | |
var queryParams = {}; | |
var isSlashDropped = false; | |
var hashStart = path.indexOf("#"); | |
if (hashStart !== -1) { | |
path = path.substr(0, hashStart); | |
} | |
var queryStart = path.indexOf("?"); | |
if (queryStart !== -1) { | |
var queryString = path.substr(queryStart + 1, path.length); | |
path = path.substr(0, queryStart); | |
queryParams = this.parseQueryString(queryString); | |
} | |
if (path.charAt(0) !== "/") { | |
path = "/" + path; | |
} | |
var originalPath = path; | |
if (RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS) { | |
path = normalizePath(path); | |
} | |
else { | |
path = decodeURI(path); | |
originalPath = decodeURI(originalPath); | |
} | |
var pathLen = path.length; | |
if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { | |
path = path.substr(0, pathLen - 1); | |
originalPath = originalPath.substr(0, originalPath.length - 1); | |
isSlashDropped = true; | |
} | |
for (var i = 0; i < path.length; i++) { | |
states = recognizeChar(states, path.charCodeAt(i)); | |
if (!states.length) { | |
break; | |
} | |
} | |
var solutions = []; | |
for (var i$1 = 0; i$1 < states.length; i$1++) { | |
if (states[i$1].handlers) { | |
solutions.push(states[i$1]); | |
} | |
} | |
states = sortSolutions(solutions); | |
var state = solutions[0]; | |
if (state && state.handlers) { | |
// if a trailing slash was dropped and a star segment is the last segment | |
// specified, put the trailing slash back | |
if (isSlashDropped && state.pattern && state.pattern.slice(-5) === "(.+)$") { | |
originalPath = originalPath + "/"; | |
} | |
results = findHandler(state, originalPath, queryParams); | |
} | |
return results; | |
}; | |
RouteRecognizer.VERSION = "0.3.4"; | |
// Set to false to opt-out of encoding and decoding path segments. | |
// See https://github.com/tildeio/route-recognizer/pull/55 | |
RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS = true; | |
RouteRecognizer.Normalizer = { | |
normalizeSegment: normalizeSegment, normalizePath: normalizePath, encodePathSegment: encodePathSegment | |
}; | |
RouteRecognizer.prototype.map = map; | |
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | |
function createCommonjsModule(fn, module) { | |
return module = { exports: {} }, fn(module, module.exports), module.exports; | |
} | |
var fake_xml_http_request = createCommonjsModule(function (module, exports) { | |
(function (global, factory) { | |
module.exports = factory(); | |
}(commonjsGlobal, function () { | |
/** | |
* Minimal Event interface implementation | |
* | |
* Original implementation by Sven Fuchs: https://gist.github.com/995028 | |
* Modifications and tests by Christian Johansen. | |
* | |
* @author Sven Fuchs (svenfuchs@artweb-design.de) | |
* @author Christian Johansen (christian@cjohansen.no) | |
* @license BSD | |
* | |
* Copyright (c) 2011 Sven Fuchs, Christian Johansen | |
*/ | |
var _Event = function Event(type, bubbles, cancelable, target) { | |
this.type = type; | |
this.bubbles = bubbles; | |
this.cancelable = cancelable; | |
this.target = target; | |
}; | |
_Event.prototype = { | |
stopPropagation: function () {}, | |
preventDefault: function () { | |
this.defaultPrevented = true; | |
} | |
}; | |
/* | |
Used to set the statusText property of an xhr object | |
*/ | |
var httpStatusCodes = { | |
100: "Continue", | |
101: "Switching Protocols", | |
200: "OK", | |
201: "Created", | |
202: "Accepted", | |
203: "Non-Authoritative Information", | |
204: "No Content", | |
205: "Reset Content", | |
206: "Partial Content", | |
300: "Multiple Choice", | |
301: "Moved Permanently", | |
302: "Found", | |
303: "See Other", | |
304: "Not Modified", | |
305: "Use Proxy", | |
307: "Temporary Redirect", | |
400: "Bad Request", | |
401: "Unauthorized", | |
402: "Payment Required", | |
403: "Forbidden", | |
404: "Not Found", | |
405: "Method Not Allowed", | |
406: "Not Acceptable", | |
407: "Proxy Authentication Required", | |
408: "Request Timeout", | |
409: "Conflict", | |
410: "Gone", | |
411: "Length Required", | |
412: "Precondition Failed", | |
413: "Request Entity Too Large", | |
414: "Request-URI Too Long", | |
415: "Unsupported Media Type", | |
416: "Requested Range Not Satisfiable", | |
417: "Expectation Failed", | |
422: "Unprocessable Entity", | |
500: "Internal Server Error", | |
501: "Not Implemented", | |
502: "Bad Gateway", | |
503: "Service Unavailable", | |
504: "Gateway Timeout", | |
505: "HTTP Version Not Supported" | |
}; | |
/* | |
Cross-browser XML parsing. Used to turn | |
XML responses into Document objects | |
Borrowed from JSpec | |
*/ | |
function parseXML(text) { | |
var xmlDoc; | |
if (typeof DOMParser != "undefined") { | |
var parser = new DOMParser(); | |
xmlDoc = parser.parseFromString(text, "text/xml"); | |
} else { | |
xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); | |
xmlDoc.async = "false"; | |
xmlDoc.loadXML(text); | |
} | |
return xmlDoc; | |
} | |
/* | |
Without mocking, the native XMLHttpRequest object will throw | |
an error when attempting to set these headers. We match this behavior. | |
*/ | |
var unsafeHeaders = { | |
"Accept-Charset": true, | |
"Accept-Encoding": true, | |
"Connection": true, | |
"Content-Length": true, | |
"Cookie": true, | |
"Cookie2": true, | |
"Content-Transfer-Encoding": true, | |
"Date": true, | |
"Expect": true, | |
"Host": true, | |
"Keep-Alive": true, | |
"Referer": true, | |
"TE": true, | |
"Trailer": true, | |
"Transfer-Encoding": true, | |
"Upgrade": true, | |
"User-Agent": true, | |
"Via": true | |
}; | |
/* | |
Adds an "event" onto the fake xhr object | |
that just calls the same-named method. This is | |
in case a library adds callbacks for these events. | |
*/ | |
function _addEventListener(eventName, xhr){ | |
xhr.addEventListener(eventName, function (event) { | |
var listener = xhr["on" + eventName]; | |
if (listener && typeof listener == "function") { | |
listener.call(event.target, event); | |
} | |
}); | |
} | |
function EventedObject() { | |
this._eventListeners = {}; | |
var events = ["loadstart", "progress", "load", "abort", "loadend"]; | |
for (var i = events.length - 1; i >= 0; i--) { | |
_addEventListener(events[i], this); | |
} | |
} | |
EventedObject.prototype = { | |
/* | |
Duplicates the behavior of native XMLHttpRequest's addEventListener function | |
*/ | |
addEventListener: function addEventListener(event, listener) { | |
this._eventListeners[event] = this._eventListeners[event] || []; | |
this._eventListeners[event].push(listener); | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's removeEventListener function | |
*/ | |
removeEventListener: function removeEventListener(event, listener) { | |
var listeners = this._eventListeners[event] || []; | |
for (var i = 0, l = listeners.length; i < l; ++i) { | |
if (listeners[i] == listener) { | |
return listeners.splice(i, 1); | |
} | |
} | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's dispatchEvent function | |
*/ | |
dispatchEvent: function dispatchEvent(event) { | |
var type = event.type; | |
var listeners = this._eventListeners[type] || []; | |
for (var i = 0; i < listeners.length; i++) { | |
if (typeof listeners[i] == "function") { | |
listeners[i].call(this, event); | |
} else { | |
listeners[i].handleEvent(event); | |
} | |
} | |
return !!event.defaultPrevented; | |
}, | |
/* | |
Triggers an `onprogress` event with the given parameters. | |
*/ | |
_progress: function _progress(lengthComputable, loaded, total) { | |
var event = new _Event('progress'); | |
event.target = this; | |
event.lengthComputable = lengthComputable; | |
event.loaded = loaded; | |
event.total = total; | |
this.dispatchEvent(event); | |
} | |
}; | |
/* | |
Constructor for a fake window.XMLHttpRequest | |
*/ | |
function FakeXMLHttpRequest() { | |
EventedObject.call(this); | |
this.readyState = FakeXMLHttpRequest.UNSENT; | |
this.requestHeaders = {}; | |
this.requestBody = null; | |
this.status = 0; | |
this.statusText = ""; | |
this.upload = new EventedObject(); | |
} | |
FakeXMLHttpRequest.prototype = new EventedObject(); | |
// These status codes are available on the native XMLHttpRequest | |
// object, so we match that here in case a library is relying on them. | |
FakeXMLHttpRequest.UNSENT = 0; | |
FakeXMLHttpRequest.OPENED = 1; | |
FakeXMLHttpRequest.HEADERS_RECEIVED = 2; | |
FakeXMLHttpRequest.LOADING = 3; | |
FakeXMLHttpRequest.DONE = 4; | |
var FakeXMLHttpRequestProto = { | |
UNSENT: 0, | |
OPENED: 1, | |
HEADERS_RECEIVED: 2, | |
LOADING: 3, | |
DONE: 4, | |
async: true, | |
withCredentials: false, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's open function | |
*/ | |
open: function open(method, url, async, username, password) { | |
this.method = method; | |
this.url = url; | |
this.async = typeof async == "boolean" ? async : true; | |
this.username = username; | |
this.password = password; | |
this.responseText = null; | |
this.responseXML = null; | |
this.requestHeaders = {}; | |
this.sendFlag = false; | |
this._readyStateChange(FakeXMLHttpRequest.OPENED); | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's setRequestHeader function | |
*/ | |
setRequestHeader: function setRequestHeader(header, value) { | |
verifyState(this); | |
if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { | |
throw new Error("Refused to set unsafe header \"" + header + "\""); | |
} | |
if (this.requestHeaders[header]) { | |
this.requestHeaders[header] += "," + value; | |
} else { | |
this.requestHeaders[header] = value; | |
} | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's send function | |
*/ | |
send: function send(data) { | |
verifyState(this); | |
if (!/^(get|head)$/i.test(this.method)) { | |
var hasContentTypeHeader = false; | |
Object.keys(this.requestHeaders).forEach(function (key) { | |
if (key.toLowerCase() === 'content-type') { | |
hasContentTypeHeader = true; | |
} | |
}); | |
if (!hasContentTypeHeader && !(data || '').toString().match('FormData')) { | |
this.requestHeaders["Content-Type"] = "text/plain;charset=UTF-8"; | |
} | |
this.requestBody = data; | |
} | |
this.errorFlag = false; | |
this.sendFlag = this.async; | |
this._readyStateChange(FakeXMLHttpRequest.OPENED); | |
if (typeof this.onSend == "function") { | |
this.onSend(this); | |
} | |
this.dispatchEvent(new _Event("loadstart", false, false, this)); | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's abort function | |
*/ | |
abort: function abort() { | |
this.aborted = true; | |
this.responseText = null; | |
this.errorFlag = true; | |
this.requestHeaders = {}; | |
if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) { | |
this._readyStateChange(FakeXMLHttpRequest.DONE); | |
this.sendFlag = false; | |
} | |
this.readyState = FakeXMLHttpRequest.UNSENT; | |
this.dispatchEvent(new _Event("abort", false, false, this)); | |
if (typeof this.onerror === "function") { | |
this.onerror(); | |
} | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's getResponseHeader function | |
*/ | |
getResponseHeader: function getResponseHeader(header) { | |
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { | |
return null; | |
} | |
if (/^Set-Cookie2?$/i.test(header)) { | |
return null; | |
} | |
header = header.toLowerCase(); | |
for (var h in this.responseHeaders) { | |
if (h.toLowerCase() == header) { | |
return this.responseHeaders[h]; | |
} | |
} | |
return null; | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's getAllResponseHeaders function | |
*/ | |
getAllResponseHeaders: function getAllResponseHeaders() { | |
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { | |
return ""; | |
} | |
var headers = ""; | |
for (var header in this.responseHeaders) { | |
if (this.responseHeaders.hasOwnProperty(header) && !/^Set-Cookie2?$/i.test(header)) { | |
headers += header + ": " + this.responseHeaders[header] + "\r\n"; | |
} | |
} | |
return headers; | |
}, | |
/* | |
Duplicates the behavior of native XMLHttpRequest's overrideMimeType function | |
*/ | |
overrideMimeType: function overrideMimeType(mimeType) { | |
if (typeof mimeType === "string") { | |
this.forceMimeType = mimeType.toLowerCase(); | |
} | |
}, | |
/* | |
Places a FakeXMLHttpRequest object into the passed | |
state. | |
*/ | |
_readyStateChange: function _readyStateChange(state) { | |
this.readyState = state; | |
if (typeof this.onreadystatechange == "function") { | |
this.onreadystatechange(new _Event("readystatechange")); | |
} | |
this.dispatchEvent(new _Event("readystatechange")); | |
if (this.readyState == FakeXMLHttpRequest.DONE) { | |
this.dispatchEvent(new _Event("load", false, false, this)); | |
this.dispatchEvent(new _Event("loadend", false, false, this)); | |
} | |
}, | |
/* | |
Sets the FakeXMLHttpRequest object's response headers and | |
places the object into readyState 2 | |
*/ | |
_setResponseHeaders: function _setResponseHeaders(headers) { | |
this.responseHeaders = {}; | |
for (var header in headers) { | |
if (headers.hasOwnProperty(header)) { | |
this.responseHeaders[header] = headers[header]; | |
} | |
} | |
if (this.forceMimeType) { | |
this.responseHeaders['Content-Type'] = this.forceMimeType; | |
} | |
if (this.async) { | |
this._readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); | |
} else { | |
this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; | |
} | |
}, | |
/* | |
Sets the FakeXMLHttpRequest object's response body and | |
if body text is XML, sets responseXML to parsed document | |
object | |
*/ | |
_setResponseBody: function _setResponseBody(body) { | |
verifyRequestSent(this); | |
verifyHeadersReceived(this); | |
verifyResponseBodyType(body); | |
var chunkSize = this.chunkSize || 10; | |
var index = 0; | |
this.responseText = ""; | |
do { | |
if (this.async) { | |
this._readyStateChange(FakeXMLHttpRequest.LOADING); | |
} | |
this.responseText += body.substring(index, index + chunkSize); | |
index += chunkSize; | |
} while (index < body.length); | |
var type = this.getResponseHeader("Content-Type"); | |
if (this.responseText && (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) { | |
try { | |
this.responseXML = parseXML(this.responseText); | |
} catch (e) { | |
// Unable to parse XML - no biggie | |
} | |
} | |
if (this.async) { | |
this._readyStateChange(FakeXMLHttpRequest.DONE); | |
} else { | |
this.readyState = FakeXMLHttpRequest.DONE; | |
} | |
}, | |
/* | |
Forces a response on to the FakeXMLHttpRequest object. | |
This is the public API for faking responses. This function | |
takes a number status, headers object, and string body: | |
``` | |
xhr.respond(404, {Content-Type: 'text/plain'}, "Sorry. This object was not found.") | |
``` | |
*/ | |
respond: function respond(status, headers, body) { | |
this._setResponseHeaders(headers || {}); | |
this.status = typeof status == "number" ? status : 200; | |
this.statusText = httpStatusCodes[this.status]; | |
this._setResponseBody(body || ""); | |
} | |
}; | |
for (var property in FakeXMLHttpRequestProto) { | |
FakeXMLHttpRequest.prototype[property] = FakeXMLHttpRequestProto[property]; | |
} | |
function verifyState(xhr) { | |
if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { | |
throw new Error("INVALID_STATE_ERR"); | |
} | |
if (xhr.sendFlag) { | |
throw new Error("INVALID_STATE_ERR"); | |
} | |
} | |
function verifyRequestSent(xhr) { | |
if (xhr.readyState == FakeXMLHttpRequest.DONE) { | |
throw new Error("Request done"); | |
} | |
} | |
function verifyHeadersReceived(xhr) { | |
if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) { | |
throw new Error("No headers received"); | |
} | |
} | |
function verifyResponseBodyType(body) { | |
if (typeof body != "string") { | |
var error = new Error("Attempted to respond to fake XMLHttpRequest with " + | |
body + ", which is not a string."); | |
error.name = "InvalidBodyException"; | |
throw error; | |
} | |
} | |
var fake_xml_http_request = FakeXMLHttpRequest; | |
return fake_xml_http_request; | |
})); | |
}); | |
var support = { | |
searchParams: 'URLSearchParams' in self, | |
iterable: 'Symbol' in self && 'iterator' in Symbol, | |
blob: | |
'FileReader' in self && | |
'Blob' in self && | |
(function() { | |
try { | |
new Blob(); | |
return true | |
} catch (e) { | |
return false | |
} | |
})(), | |
formData: 'FormData' in self, | |
arrayBuffer: 'ArrayBuffer' in self | |
}; | |
function isDataView(obj) { | |
return obj && DataView.prototype.isPrototypeOf(obj) | |
} | |
if (support.arrayBuffer) { | |
var viewClasses = [ | |
'[object Int8Array]', | |
'[object Uint8Array]', | |
'[object Uint8ClampedArray]', | |
'[object Int16Array]', | |
'[object Uint16Array]', | |
'[object Int32Array]', | |
'[object Uint32Array]', | |
'[object Float32Array]', | |
'[object Float64Array]' | |
]; | |
var isArrayBufferView = | |
ArrayBuffer.isView || | |
function(obj) { | |
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 | |
}; | |
} | |
function normalizeName(name) { | |
if (typeof name !== 'string') { | |
name = String(name); | |
} | |
if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) { | |
throw new TypeError('Invalid character in header field name') | |
} | |
return name.toLowerCase() | |
} | |
function normalizeValue(value) { | |
if (typeof value !== 'string') { | |
value = String(value); | |
} | |
return value | |
} | |
// Build a destructive iterator for the value list | |
function iteratorFor(items) { | |
var iterator = { | |
next: function() { | |
var value = items.shift(); | |
return {done: value === undefined, value: value} | |
} | |
}; | |
if (support.iterable) { | |
iterator[Symbol.iterator] = function() { | |
return iterator | |
}; | |
} | |
return iterator | |
} | |
function Headers(headers) { | |
this.map = {}; | |
if (headers instanceof Headers) { | |
headers.forEach(function(value, name) { | |
this.append(name, value); | |
}, this); | |
} else if (Array.isArray(headers)) { | |
headers.forEach(function(header) { | |
this.append(header[0], header[1]); | |
}, this); | |
} else if (headers) { | |
Object.getOwnPropertyNames(headers).forEach(function(name) { | |
this.append(name, headers[name]); | |
}, this); | |
} | |
} | |
Headers.prototype.append = function(name, value) { | |
name = normalizeName(name); | |
value = normalizeValue(value); | |
var oldValue = this.map[name]; | |
this.map[name] = oldValue ? oldValue + ', ' + value : value; | |
}; | |
Headers.prototype['delete'] = function(name) { | |
delete this.map[normalizeName(name)]; | |
}; | |
Headers.prototype.get = function(name) { | |
name = normalizeName(name); | |
return this.has(name) ? this.map[name] : null | |
}; | |
Headers.prototype.has = function(name) { | |
return this.map.hasOwnProperty(normalizeName(name)) | |
}; | |
Headers.prototype.set = function(name, value) { | |
this.map[normalizeName(name)] = normalizeValue(value); | |
}; | |
Headers.prototype.forEach = function(callback, thisArg) { | |
for (var name in this.map) { | |
if (this.map.hasOwnProperty(name)) { | |
callback.call(thisArg, this.map[name], name, this); | |
} | |
} | |
}; | |
Headers.prototype.keys = function() { | |
var items = []; | |
this.forEach(function(value, name) { | |
items.push(name); | |
}); | |
return iteratorFor(items) | |
}; | |
Headers.prototype.values = function() { | |
var items = []; | |
this.forEach(function(value) { | |
items.push(value); | |
}); | |
return iteratorFor(items) | |
}; | |
Headers.prototype.entries = function() { | |
var items = []; | |
this.forEach(function(value, name) { | |
items.push([name, value]); | |
}); | |
return iteratorFor(items) | |
}; | |
if (support.iterable) { | |
Headers.prototype[Symbol.iterator] = Headers.prototype.entries; | |
} | |
function consumed(body) { | |
if (body.bodyUsed) { | |
return Promise.reject(new TypeError('Already read')) | |
} | |
body.bodyUsed = true; | |
} | |
function fileReaderReady(reader) { | |
return new Promise(function(resolve, reject) { | |
reader.onload = function() { | |
resolve(reader.result); | |
}; | |
reader.onerror = function() { | |
reject(reader.error); | |
}; | |
}) | |
} | |
function readBlobAsArrayBuffer(blob) { | |
var reader = new FileReader(); | |
var promise = fileReaderReady(reader); | |
reader.readAsArrayBuffer(blob); | |
return promise | |
} | |
function readBlobAsText(blob) { | |
var reader = new FileReader(); | |
var promise = fileReaderReady(reader); | |
reader.readAsText(blob); | |
return promise | |
} | |
function readArrayBufferAsText(buf) { | |
var view = new Uint8Array(buf); | |
var chars = new Array(view.length); | |
for (var i = 0; i < view.length; i++) { | |
chars[i] = String.fromCharCode(view[i]); | |
} | |
return chars.join('') | |
} | |
function bufferClone(buf) { | |
if (buf.slice) { | |
return buf.slice(0) | |
} else { | |
var view = new Uint8Array(buf.byteLength); | |
view.set(new Uint8Array(buf)); | |
return view.buffer | |
} | |
} | |
function Body() { | |
this.bodyUsed = false; | |
this._initBody = function(body) { | |
this._bodyInit = body; | |
if (!body) { | |
this._bodyText = ''; | |
} else if (typeof body === 'string') { | |
this._bodyText = body; | |
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) { | |
this._bodyBlob = body; | |
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) { | |
this._bodyFormData = body; | |
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | |
this._bodyText = body.toString(); | |
} else if (support.arrayBuffer && support.blob && isDataView(body)) { | |
this._bodyArrayBuffer = bufferClone(body.buffer); | |
// IE 10-11 can't handle a DataView body. | |
this._bodyInit = new Blob([this._bodyArrayBuffer]); | |
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { | |
this._bodyArrayBuffer = bufferClone(body); | |
} else { | |
this._bodyText = body = Object.prototype.toString.call(body); | |
} | |
if (!this.headers.get('content-type')) { | |
if (typeof body === 'string') { | |
this.headers.set('content-type', 'text/plain;charset=UTF-8'); | |
} else if (this._bodyBlob && this._bodyBlob.type) { | |
this.headers.set('content-type', this._bodyBlob.type); | |
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | |
this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); | |
} | |
} | |
}; | |
if (support.blob) { | |
this.blob = function() { | |
var rejected = consumed(this); | |
if (rejected) { | |
return rejected | |
} | |
if (this._bodyBlob) { | |
return Promise.resolve(this._bodyBlob) | |
} else if (this._bodyArrayBuffer) { | |
return Promise.resolve(new Blob([this._bodyArrayBuffer])) | |
} else if (this._bodyFormData) { | |
throw new Error('could not read FormData body as blob') | |
} else { | |
return Promise.resolve(new Blob([this._bodyText])) | |
} | |
}; | |
this.arrayBuffer = function() { | |
if (this._bodyArrayBuffer) { | |
return consumed(this) || Promise.resolve(this._bodyArrayBuffer) | |
} else { | |
return this.blob().then(readBlobAsArrayBuffer) | |
} | |
}; | |
} | |
this.text = function() { | |
var rejected = consumed(this); | |
if (rejected) { | |
return rejected | |
} | |
if (this._bodyBlob) { | |
return readBlobAsText(this._bodyBlob) | |
} else if (this._bodyArrayBuffer) { | |
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) | |
} else if (this._bodyFormData) { | |
throw new Error('could not read FormData body as text') | |
} else { | |
return Promise.resolve(this._bodyText) | |
} | |
}; | |
if (support.formData) { | |
this.formData = function() { | |
return this.text().then(decode) | |
}; | |
} | |
this.json = function() { | |
return this.text().then(JSON.parse) | |
}; | |
return this | |
} | |
// HTTP methods whose capitalization should be normalized | |
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']; | |
function normalizeMethod(method) { | |
var upcased = method.toUpperCase(); | |
return methods.indexOf(upcased) > -1 ? upcased : method | |
} | |
function Request(input, options) { | |
options = options || {}; | |
var body = options.body; | |
if (input instanceof Request) { | |
if (input.bodyUsed) { | |
throw new TypeError('Already read') | |
} | |
this.url = input.url; | |
this.credentials = input.credentials; | |
if (!options.headers) { | |
this.headers = new Headers(input.headers); | |
} | |
this.method = input.method; | |
this.mode = input.mode; | |
this.signal = input.signal; | |
if (!body && input._bodyInit != null) { | |
body = input._bodyInit; | |
input.bodyUsed = true; | |
} | |
} else { | |
this.url = String(input); | |
} | |
this.credentials = options.credentials || this.credentials || 'same-origin'; | |
if (options.headers || !this.headers) { | |
this.headers = new Headers(options.headers); | |
} | |
this.method = normalizeMethod(options.method || this.method || 'GET'); | |
this.mode = options.mode || this.mode || null; | |
this.signal = options.signal || this.signal; | |
this.referrer = null; | |
if ((this.method === 'GET' || this.method === 'HEAD') && body) { | |
throw new TypeError('Body not allowed for GET or HEAD requests') | |
} | |
this._initBody(body); | |
} | |
Request.prototype.clone = function() { | |
return new Request(this, {body: this._bodyInit}) | |
}; | |
function decode(body) { | |
var form = new FormData(); | |
body | |
.trim() | |
.split('&') | |
.forEach(function(bytes) { | |
if (bytes) { | |
var split = bytes.split('='); | |
var name = split.shift().replace(/\+/g, ' '); | |
var value = split.join('=').replace(/\+/g, ' '); | |
form.append(decodeURIComponent(name), decodeURIComponent(value)); | |
} | |
}); | |
return form | |
} | |
function parseHeaders(rawHeaders) { | |
var headers = new Headers(); | |
// Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space | |
// https://tools.ietf.org/html/rfc7230#section-3.2 | |
var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); | |
preProcessedHeaders.split(/\r?\n/).forEach(function(line) { | |
var parts = line.split(':'); | |
var key = parts.shift().trim(); | |
if (key) { | |
var value = parts.join(':').trim(); | |
headers.append(key, value); | |
} | |
}); | |
return headers | |
} | |
Body.call(Request.prototype); | |
function Response(bodyInit, options) { | |
if (!options) { | |
options = {}; | |
} | |
this.type = 'default'; | |
this.status = options.status === undefined ? 200 : options.status; | |
this.ok = this.status >= 200 && this.status < 300; | |
this.statusText = 'statusText' in options ? options.statusText : 'OK'; | |
this.headers = new Headers(options.headers); | |
this.url = options.url || ''; | |
this._initBody(bodyInit); | |
} | |
Body.call(Response.prototype); | |
Response.prototype.clone = function() { | |
return new Response(this._bodyInit, { | |
status: this.status, | |
statusText: this.statusText, | |
headers: new Headers(this.headers), | |
url: this.url | |
}) | |
}; | |
Response.error = function() { | |
var response = new Response(null, {status: 0, statusText: ''}); | |
response.type = 'error'; | |
return response | |
}; | |
var redirectStatuses = [301, 302, 303, 307, 308]; | |
Response.redirect = function(url, status) { | |
if (redirectStatuses.indexOf(status) === -1) { | |
throw new RangeError('Invalid status code') | |
} | |
return new Response(null, {status: status, headers: {location: url}}) | |
}; | |
var DOMException = self.DOMException; | |
try { | |
new DOMException(); | |
} catch (err) { | |
DOMException = function(message, name) { | |
this.message = message; | |
this.name = name; | |
var error = Error(message); | |
this.stack = error.stack; | |
}; | |
DOMException.prototype = Object.create(Error.prototype); | |
DOMException.prototype.constructor = DOMException; | |
} | |
function fetch(input, init) { | |
return new Promise(function(resolve, reject) { | |
var request = new Request(input, init); | |
if (request.signal && request.signal.aborted) { | |
return reject(new DOMException('Aborted', 'AbortError')) | |
} | |
var xhr = new XMLHttpRequest(); | |
function abortXhr() { | |
xhr.abort(); | |
} | |
xhr.onload = function() { | |
var options = { | |
status: xhr.status, | |
statusText: xhr.statusText, | |
headers: parseHeaders(xhr.getAllResponseHeaders() || '') | |
}; | |
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); | |
var body = 'response' in xhr ? xhr.response : xhr.responseText; | |
resolve(new Response(body, options)); | |
}; | |
xhr.onerror = function() { | |
reject(new TypeError('Network request failed')); | |
}; | |
xhr.ontimeout = function() { | |
reject(new TypeError('Network request failed')); | |
}; | |
xhr.onabort = function() { | |
reject(new DOMException('Aborted', 'AbortError')); | |
}; | |
xhr.open(request.method, request.url, true); | |
if (request.credentials === 'include') { | |
xhr.withCredentials = true; | |
} else if (request.credentials === 'omit') { | |
xhr.withCredentials = false; | |
} | |
if ('responseType' in xhr && support.blob) { | |
xhr.responseType = 'blob'; | |
} | |
request.headers.forEach(function(value, name) { | |
xhr.setRequestHeader(name, value); | |
}); | |
if (request.signal) { | |
request.signal.addEventListener('abort', abortXhr); | |
xhr.onreadystatechange = function() { | |
// DONE (success or failure) | |
if (xhr.readyState === 4) { | |
request.signal.removeEventListener('abort', abortXhr); | |
} | |
}; | |
} | |
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); | |
}) | |
} | |
fetch.polyfill = true; | |
if (!self.fetch) { | |
self.fetch = fetch; | |
self.Headers = Headers; | |
self.Request = Request; | |
self.Response = Response; | |
} | |
var FakeFetch = /*#__PURE__*/Object.freeze({ | |
Headers: Headers, | |
Request: Request, | |
Response: Response, | |
get DOMException () { return DOMException; }, | |
fetch: fetch | |
}); | |
/** | |
* parseURL - decompose a URL into its parts | |
* @param {String} url a URL | |
* @return {Object} parts of the URL, including the following | |
* | |
* 'https://www.yahoo.com:1234/mypage?test=yes#abc' | |
* | |
* { | |
* host: 'www.yahoo.com:1234', | |
* protocol: 'https:', | |
* search: '?test=yes', | |
* hash: '#abc', | |
* href: 'https://www.yahoo.com:1234/mypage?test=yes#abc', | |
* pathname: '/mypage', | |
* fullpath: '/mypage?test=yes' | |
* } | |
*/ | |
function parseURL(url) { | |
// TODO: something for when document isn't present... #yolo | |
var anchor = document.createElement('a'); | |
anchor.href = url; | |
if (!anchor.host) { | |
anchor.href = anchor.href; // IE: load the host and protocol | |
} | |
var pathname = anchor.pathname; | |
if (pathname.charAt(0) !== '/') { | |
pathname = '/' + pathname; // IE: prepend leading slash | |
} | |
var host = anchor.host; | |
if (anchor.port === '80' || anchor.port === '443') { | |
host = anchor.hostname; // IE: remove default port | |
} | |
return { | |
host: host, | |
protocol: anchor.protocol, | |
search: anchor.search, | |
hash: anchor.hash, | |
href: anchor.href, | |
pathname: pathname, | |
fullpath: pathname + (anchor.search || '') + (anchor.hash || '') | |
}; | |
} | |
/** | |
* Registry | |
* | |
* A registry is a map of HTTP verbs to route recognizers. | |
*/ | |
function Registry( /* host */) { | |
// Herein we keep track of RouteRecognizer instances | |
// keyed by HTTP method. Feel free to add more as needed. | |
this.verbs = { | |
GET: new RouteRecognizer(), | |
PUT: new RouteRecognizer(), | |
POST: new RouteRecognizer(), | |
DELETE: new RouteRecognizer(), | |
PATCH: new RouteRecognizer(), | |
HEAD: new RouteRecognizer(), | |
OPTIONS: new RouteRecognizer() | |
}; | |
} | |
/** | |
* Hosts | |
* | |
* a map of hosts to Registries, ultimately allowing | |
* a per-host-and-port, per HTTP verb lookup of RouteRecognizers | |
*/ | |
function Hosts() { | |
this._registries = {}; | |
} | |
/** | |
* Hosts#forURL - retrieve a map of HTTP verbs to RouteRecognizers | |
* for a given URL | |
* | |
* @param {String} url a URL | |
* @return {Registry} a map of HTTP verbs to RouteRecognizers | |
* corresponding to the provided URL's | |
* hostname and port | |
*/ | |
Hosts.prototype.forURL = function (url) { | |
var host = parseURL(url).host; | |
var registry = this._registries[host]; | |
if (registry === undefined) { | |
registry = (this._registries[host] = new Registry(host)); | |
} | |
return registry.verbs; | |
}; | |
function Pretender( /* routeMap1, routeMap2, ..., options*/) { | |
this.hosts = new Hosts(); | |
var lastArg = arguments[arguments.length - 1]; | |
var options = typeof lastArg === 'object' ? lastArg : null; | |
var shouldNotTrack = options && (options.trackRequests === false); | |
var noopArray = { push: function () { }, length: 0 }; | |
this.handlers = []; | |
this.handledRequests = shouldNotTrack ? noopArray : []; | |
this.passthroughRequests = shouldNotTrack ? noopArray : []; | |
this.unhandledRequests = shouldNotTrack ? noopArray : []; | |
this.requestReferences = []; | |
this.forcePassthrough = options && (options.forcePassthrough === true); | |
this.disableUnhandled = options && (options.disableUnhandled === true); | |
// reference the native XMLHttpRequest object so | |
// it can be restored later | |
this._nativeXMLHttpRequest = window.XMLHttpRequest; | |
this.running = false; | |
var ctx = { pretender: this }; | |
this.ctx = ctx; | |
// capture xhr requests, channeling them into | |
// the route map. | |
window.XMLHttpRequest = interceptor(ctx); | |
// polyfill fetch when xhr is ready | |
this._fetchProps = FakeFetch ? ['fetch', 'Headers', 'Request', 'Response'] : []; | |
this._fetchProps.forEach(function (name) { | |
this['_native' + name] = window[name]; | |
window[name] = FakeFetch[name]; | |
}, this); | |
// 'start' the server | |
this.running = true; | |
// trigger the route map DSL. | |
var argLength = options ? arguments.length - 1 : arguments.length; | |
for (var i = 0; i < argLength; i++) { | |
this.map(arguments[i]); | |
} | |
} | |
function interceptor(ctx) { | |
function FakeRequest() { | |
// super() | |
fake_xml_http_request.call(this); | |
} | |
FakeRequest.prototype = Object.create(fake_xml_http_request.prototype); | |
FakeRequest.prototype.constructor = FakeRequest; | |
// extend | |
FakeRequest.prototype.send = function send() { | |
if (!ctx.pretender.running) { | |
throw new Error('You shut down a Pretender instance while there was a pending request. ' + | |
'That request just tried to complete. Check to see if you accidentally shut down ' + | |
'a pretender earlier than you intended to'); | |
} | |
fake_xml_http_request.prototype.send.apply(this, arguments); | |
if (ctx.pretender.checkPassthrough(this)) { | |
var xhr = createPassthrough(this); | |
xhr.send.apply(xhr, arguments); | |
} | |
else { | |
ctx.pretender.handleRequest(this); | |
} | |
}; | |
function createPassthrough(fakeXHR) { | |
// event types to handle on the xhr | |
var evts = ['error', 'timeout', 'abort', 'readystatechange']; | |
// event types to handle on the xhr.upload | |
var uploadEvents = []; | |
// properties to copy from the native xhr to fake xhr | |
var lifecycleProps = ['readyState', 'responseText', 'responseXML', 'status', 'statusText']; | |
var xhr = fakeXHR._passthroughRequest = new ctx.pretender._nativeXMLHttpRequest(); | |
xhr.open(fakeXHR.method, fakeXHR.url, fakeXHR.async, fakeXHR.username, fakeXHR.password); | |
if (fakeXHR.responseType === 'arraybuffer') { | |
lifecycleProps = ['readyState', 'response', 'status', 'statusText']; | |
xhr.responseType = fakeXHR.responseType; | |
} | |
// use onload if the browser supports it | |
if ('onload' in xhr) { | |
evts.push('load'); | |
} | |
// add progress event for async calls | |
// avoid using progress events for sync calls, they will hang https://bugs.webkit.org/show_bug.cgi?id=40996. | |
if (fakeXHR.async && fakeXHR.responseType !== 'arraybuffer') { | |
evts.push('progress'); | |
uploadEvents.push('progress'); | |
} | |
// update `propertyNames` properties from `fromXHR` to `toXHR` | |
function copyLifecycleProperties(propertyNames, fromXHR, toXHR) { | |
for (var i = 0; i < propertyNames.length; i++) { | |
var prop = propertyNames[i]; | |
if (prop in fromXHR) { | |
toXHR[prop] = fromXHR[prop]; | |
} | |
} | |
} | |
// fire fake event on `eventable` | |
function dispatchEvent(eventable, eventType, event) { | |
eventable.dispatchEvent(event); | |
if (eventable['on' + eventType]) { | |
eventable['on' + eventType](event); | |
} | |
} | |
// set the on- handler on the native xhr for the given eventType | |
function createHandler(eventType) { | |
xhr['on' + eventType] = function (event) { | |
copyLifecycleProperties(lifecycleProps, xhr, fakeXHR); | |
dispatchEvent(fakeXHR, eventType, event); | |
}; | |
} | |
// set the on- handler on the native xhr's `upload` property for | |
// the given eventType | |
function createUploadHandler(eventType) { | |
if (xhr.upload) { | |
xhr.upload['on' + eventType] = function (event) { | |
dispatchEvent(fakeXHR.upload, eventType, event); | |
}; | |
} | |
} | |
var i; | |
for (i = 0; i < evts.length; i++) { | |
createHandler(evts[i]); | |
} | |
for (i = 0; i < uploadEvents.length; i++) { | |
createUploadHandler(uploadEvents[i]); | |
} | |
if (fakeXHR.async) { | |
xhr.timeout = fakeXHR.timeout; | |
xhr.withCredentials = fakeXHR.withCredentials; | |
} | |
for (var h in fakeXHR.requestHeaders) { | |
xhr.setRequestHeader(h, fakeXHR.requestHeaders[h]); | |
} | |
return xhr; | |
} | |
FakeRequest.prototype._passthroughCheck = function (method, args) { | |
if (this._passthroughRequest) { | |
return this._passthroughRequest[method].apply(this._passthroughRequest, args); | |
} | |
return fake_xml_http_request.prototype[method].apply(this, args); | |
}; | |
FakeRequest.prototype.abort = function abort() { | |
return this._passthroughCheck('abort', arguments); | |
}; | |
FakeRequest.prototype.getResponseHeader = function getResponseHeader() { | |
return this._passthroughCheck('getResponseHeader', arguments); | |
}; | |
FakeRequest.prototype.getAllResponseHeaders = function getAllResponseHeaders() { | |
return this._passthroughCheck('getAllResponseHeaders', arguments); | |
}; | |
if (ctx.pretender._nativeXMLHttpRequest.prototype._passthroughCheck) { | |
console.warn('You created a second Pretender instance while there was already one running. ' + | |
'Running two Pretender servers at once will lead to unexpected results and will ' + | |
'be removed entirely in a future major version.' + | |
'Please call .shutdown() on your instances when you no longer need them to respond.'); | |
} | |
return FakeRequest; | |
} | |
function verbify(verb) { | |
return function (path, handler, async) { | |
return this.register(verb, path, handler, async); | |
}; | |
} | |
function scheduleProgressEvent(request, startTime, totalTime) { | |
setTimeout(function () { | |
if (!request.aborted && !request.status) { | |
var ellapsedTime = new Date().getTime() - startTime.getTime(); | |
request.upload._progress(true, ellapsedTime, totalTime); | |
request._progress(true, ellapsedTime, totalTime); | |
scheduleProgressEvent(request, startTime, totalTime); | |
} | |
}, 50); | |
} | |
function isArray$1(array) { | |
return Object.prototype.toString.call(array) === '[object Array]'; | |
} | |
var PASSTHROUGH = {}; | |
Pretender.prototype = { | |
get: verbify('GET'), | |
post: verbify('POST'), | |
put: verbify('PUT'), | |
'delete': verbify('DELETE'), | |
patch: verbify('PATCH'), | |
head: verbify('HEAD'), | |
options: verbify('OPTIONS'), | |
map: function (maps) { | |
maps.call(this); | |
}, | |
register: function register(verb, url, handler, async) { | |
if (!handler) { | |
throw new Error('The function you tried passing to Pretender to handle ' + | |
verb + ' ' + url + ' is undefined or missing.'); | |
} | |
handler.numberOfCalls = 0; | |
handler.async = async; | |
this.handlers.push(handler); | |
var registry = this.hosts.forURL(url)[verb]; | |
registry.add([{ | |
path: parseURL(url).fullpath, | |
handler: handler | |
}]); | |
return handler; | |
}, | |
passthrough: PASSTHROUGH, | |
checkPassthrough: function checkPassthrough(request) { | |
var verb = request.method.toUpperCase(); | |
var path = parseURL(request.url).fullpath; | |
var recognized = this.hosts.forURL(request.url)[verb].recognize(path); | |
var match = recognized && recognized[0]; | |
if ((match && match.handler === PASSTHROUGH) || this.forcePassthrough) { | |
this.passthroughRequests.push(request); | |
this.passthroughRequest(verb, path, request); | |
return true; | |
} | |
return false; | |
}, | |
handleRequest: function handleRequest(request) { | |
var verb = request.method.toUpperCase(); | |
var path = request.url; | |
var handler = this._handlerFor(verb, path, request); | |
if (handler) { | |
handler.handler.numberOfCalls++; | |
var async = handler.handler.async; | |
this.handledRequests.push(request); | |
var pretender = this; | |
var _handleRequest = function (statusHeadersAndBody) { | |
if (!isArray$1(statusHeadersAndBody)) { | |
var note = 'Remember to `return [status, headers, body];` in your route handler.'; | |
throw new Error('Nothing returned by handler for ' + path + '. ' + note); | |
} | |
var status = statusHeadersAndBody[0], headers = pretender.prepareHeaders(statusHeadersAndBody[1]), body = pretender.prepareBody(statusHeadersAndBody[2], headers); | |
pretender.handleResponse(request, async, function () { | |
request.respond(status, headers, body); | |
pretender.handledRequest(verb, path, request); | |
}); | |
}; | |
try { | |
var result = handler.handler(request); | |
if (result && typeof result.then === 'function') { | |
// `result` is a promise, resolve it | |
result.then(function (resolvedResult) { | |
_handleRequest(resolvedResult); | |
}); | |
} | |
else { | |
_handleRequest(result); | |
} | |
} | |
catch (error) { | |
this.erroredRequest(verb, path, request, error); | |
this.resolve(request); | |
} | |
} | |
else { | |
if (!this.disableUnhandled) { | |
this.unhandledRequests.push(request); | |
this.unhandledRequest(verb, path, request); | |
} | |
} | |
}, | |
handleResponse: function handleResponse(request, strategy, callback) { | |
var delay = typeof strategy === 'function' ? strategy() : strategy; | |
delay = typeof delay === 'boolean' || typeof delay === 'number' ? delay : 0; | |
if (delay === false) { | |
callback(); | |
} | |
else { | |
var pretender = this; | |
pretender.requestReferences.push({ | |
request: request, | |
callback: callback | |
}); | |
if (delay !== true) { | |
scheduleProgressEvent(request, new Date(), delay); | |
setTimeout(function () { | |
pretender.resolve(request); | |
}, delay); | |
} | |
} | |
}, | |
resolve: function resolve(request) { | |
for (var i = 0, len = this.requestReferences.length; i < len; i++) { | |
var res = this.requestReferences[i]; | |
if (res.request === request) { | |
res.callback(); | |
this.requestReferences.splice(i, 1); | |
break; | |
} | |
} | |
}, | |
requiresManualResolution: function (verb, path) { | |
var handler = this._handlerFor(verb.toUpperCase(), path, {}); | |
if (!handler) { | |
return false; | |
} | |
var async = handler.handler.async; | |
return typeof async === 'function' ? async() === true : async === true; | |
}, | |
prepareBody: function (body) { return body; }, | |
prepareHeaders: function (headers) { return headers; }, | |
handledRequest: function ( /* verb, path, request */) { }, | |
passthroughRequest: function ( /* verb, path, request */) { }, | |
unhandledRequest: function (verb, path /*, request */) { | |
throw new Error('Pretender intercepted ' + verb + ' ' + | |
path + ' but no handler was defined for this type of request'); | |
}, | |
erroredRequest: function (verb, path, request, error) { | |
error.message = 'Pretender intercepted ' + verb + ' ' + | |
path + ' but encountered an error: ' + error.message; | |
throw error; | |
}, | |
_handlerFor: function (verb, url, request) { | |
var registry = this.hosts.forURL(url)[verb]; | |
var matches = registry.recognize(parseURL(url).fullpath); | |
var match = matches ? matches[0] : null; | |
if (match) { | |
request.params = match.params; | |
request.queryParams = matches.queryParams; | |
} | |
return match; | |
}, | |
shutdown: function shutdown() { | |
window.XMLHttpRequest = this._nativeXMLHttpRequest; | |
this._fetchProps.forEach(function (name) { | |
window[name] = this['_native' + name]; | |
}, this); | |
this.ctx.pretender = undefined; | |
// 'stop' the server | |
this.running = false; | |
} | |
}; | |
Pretender.parseURL = parseURL; | |
Pretender.Hosts = Hosts; | |
Pretender.Registry = Registry; | |
return Pretender; | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Pretender=function(){"use strict";var t=Object.create;function r(){var e=t(null);return e.__=void 0,delete e.__,e}var i=function(e,t,r){this.path=e,this.matcher=t,this.delegate=r};i.prototype.to=function(e,t){var r=this.delegate;if(r&&r.willAddRoute&&(e=r.willAddRoute(this.matcher.target,e)),this.matcher.add(this.path,e),t){if(0===t.length)throw new Error("You must have an argument in the function passed to `to`");this.matcher.addChild(this.path,e,t,this.delegate)}};var a=function(e){this.routes=r(),this.children=r(),this.target=e};function h(n,s,o){return function(e,t){var r=n+e;if(!t)return new i(r,s,o);t(h(r,s,o))}}function p(e,t,r){for(var n=0,s=0;s<e.length;s++)n+=e[s].path.length;var o={path:t=t.substr(n),handler:r};e.push(o)}a.prototype.add=function(e,t){this.routes[e]=t},a.prototype.addChild=function(e,t,r,n){var s=new a(t),o=h(e,this.children[e]=s,n);n&&n.contextEntered&&n.contextEntered(t,o),r(o)};function l(e){return e.split("/").map(d).join("/")}var n=/%|\//g;function d(e){return e.length<3||-1===e.indexOf("%")?e:decodeURIComponent(e).replace(n,encodeURIComponent)}var s=/%(?:2(?:4|6|B|C)|3(?:B|D|A)|40)/g;function o(e){return encodeURIComponent(e).replace(s,decodeURIComponent)}var u=/(\/|\.|\*|\+|\?|\||\(|\)|\[|\]|\{|\}|\\)/g,c=Array.isArray,f=Object.prototype.hasOwnProperty;function y(e,t){if("object"!=typeof e||null===e)throw new Error("You must pass an object as the second argument to `generate`.");if(!f.call(e,t))throw new Error("You must provide param `"+t+"` to `generate`.");var r=e[t],n="string"==typeof r?r:""+r;if(0===n.length)throw new Error("You must provide a param `"+t+"`.");return n}var v=[];v[0]=function(e,t){for(var r=t,n=e.value,s=0;s<n.length;s++){var o=n.charCodeAt(s);r=r.put(o,!1,!1)}return r},v[1]=function(e,t){return t.put(47,!0,!0)},v[2]=function(e,t){return t.put(-1,!1,!0)},v[4]=function(e,t){return t};var g=[];g[0]=function(e){return e.value.replace(u,"\\$1")},g[1]=function(){return"([^/]+)"},g[2]=function(){return"(.+)"},g[4]=function(){return""};var E=[];E[0]=function(e){return e.value},E[1]=function(e,t){var r=y(t,e.value);return D.ENCODE_AND_DECODE_PATH_SEGMENTS?o(r):r},E[2]=function(e,t){return y(t,e.value)},E[4]=function(){return""};var m=Object.freeze({}),w=Object.freeze([]);function b(e,t,r){0<t.length&&47===t.charCodeAt(0)&&(t=t.substr(1));for(var n=t.split("/"),s=void 0,o=void 0,i=0;i<n.length;i++){var a,h=n[i],u=0;12&(a=2<<(u=""===h?4:58===h.charCodeAt(0)?1:42===h.charCodeAt(0)?2:0))&&(h=h.slice(1),(s=s||[]).push(h),(o=o||[]).push(0!=(4&a))),14&a&&r[u]++,e.push({type:u,value:d(h)})}return{names:s||w,shouldDecodes:o||w}}function R(e,t,r){return e.char===t&&e.negate===r}var _=function(e,t,r,n,s){this.states=e,this.id=t,this.char=r,this.negate=n,this.nextStates=s?t:null,this.pattern="",this._regex=void 0,this.handlers=void 0,this.types=void 0};function T(e,t){return e.negate?e.char!==t&&-1!==e.char:e.char===t||-1===e.char}function A(e,t){for(var r=[],n=0,s=e.length;n<s;n++){var o=e[n];r=r.concat(o.match(t))}return r}_.prototype.regex=function(){return this._regex||(this._regex=new RegExp(this.pattern)),this._regex},_.prototype.get=function(e,t){var r=this.nextStates;if(null!==r)if(c(r))for(var n=0;n<r.length;n++){var s=this.states[r[n]];if(R(s,e,t))return s}else{var o=this.states[r];if(R(o,e,t))return o}},_.prototype.put=function(e,t,r){var n;if(n=this.get(e,t))return n;var s=this.states;return n=new _(s,s.length,e,t,r),s[s.length]=n,null==this.nextStates?this.nextStates=n.id:c(this.nextStates)?this.nextStates.push(n.id):this.nextStates=[this.nextStates,n.id],n},_.prototype.match=function(e){var t=this.nextStates;if(!t)return[];var r=[];if(c(t))for(var n=0;n<t.length;n++){var s=this.states[t[n]];T(s,e)&&r.push(s)}else{var o=this.states[t];T(o,e)&&r.push(o)}return r};var S=function(e){this.length=0,this.queryParams=e||{}};function C(e){var t;e=e.replace(/\+/gm,"%20");try{t=decodeURIComponent(e)}catch(e){t=""}return t}S.prototype.splice=Array.prototype.splice,S.prototype.slice=Array.prototype.slice,S.prototype.push=Array.prototype.push;var D=function(){this.names=r();var e=[],t=new _(e,0,-1,!0,!1);e[0]=t,this.states=e,this.rootState=t};D.prototype.add=function(e,t){for(var r,n=this.rootState,s="^",o=[0,0,0],i=new Array(e.length),a=[],h=!0,u=0,d=0;d<e.length;d++){for(var p=e[d],c=b(a,p.path,o),f=c.names,l=c.shouldDecodes;u<a.length;u++){var y=a[u];4!==y.type&&(h=!1,n=n.put(47,!1,!1),s+="/",n=v[y.type](y,n),s+=g[y.type](y))}i[d]={handler:p.handler,names:f,shouldDecodes:l}}h&&(n=n.put(47,!1,!1),s+="/"),n.handlers=i,n.pattern=s+"$",n.types=o,"object"==typeof t&&null!==t&&t.as&&(r=t.as),r&&(this.names[r]={segments:a,handlers:i})},D.prototype.handlersFor=function(e){var t=this.names[e];if(!t)throw new Error("There is no route named "+e);for(var r=new Array(t.handlers.length),n=0;n<t.handlers.length;n++){var s=t.handlers[n];r[n]=s}return r},D.prototype.hasRoute=function(e){return!!this.names[e]},D.prototype.generate=function(e,t){var r=this.names[e],n="";if(!r)throw new Error("There is no route named "+e);for(var s=r.segments,o=0;o<s.length;o++){var i=s[o];4!==i.type&&(n+="/",n+=E[i.type](i,t))}return"/"!==n.charAt(0)&&(n="/"+n),t&&t.queryParams&&(n+=this.generateQueryString(t.queryParams)),n},D.prototype.generateQueryString=function(e){var t=[],r=Object.keys(e);r.sort();for(var n=0;n<r.length;n++){var s=r[n],o=e[s];if(null!=o){var i=encodeURIComponent(s);if(c(o))for(var a=0;a<o.length;a++){var h=s+"[]="+encodeURIComponent(o[a]);t.push(h)}else i+="="+encodeURIComponent(o),t.push(i)}}return 0===t.length?"":"?"+t.join("&")},D.prototype.parseQueryString=function(e){for(var t=e.split("&"),r={},n=0;n<t.length;n++){var s=t[n].split("="),o=C(s[0]),i=o.length,a=!1,h=void 0;h=1===s.length?"true":(2<i&&"[]"===o.slice(i-2)&&(a=!0,r[o=o.slice(0,i-2)]||(r[o]=[])),s[1]?C(s[1]):""),a?r[o].push(h):r[o]=h}return r},D.prototype.recognize=function(e){var t,r=[this.rootState],n={},s=!1,o=e.indexOf("#");-1!==o&&(e=e.substr(0,o));var i=e.indexOf("?");if(-1!==i){var a=e.substr(i+1,e.length);e=e.substr(0,i),n=this.parseQueryString(a)}"/"!==e.charAt(0)&&(e="/"+e);var h=e;D.ENCODE_AND_DECODE_PATH_SEGMENTS?e=l(e):(e=decodeURI(e),h=decodeURI(h));var u=e.length;1<u&&"/"===e.charAt(u-1)&&(e=e.substr(0,u-1),h=h.substr(0,h.length-1),s=!0);for(var d=0;d<e.length&&(r=A(r,e.charCodeAt(d))).length;d++);for(var p=[],c=0;c<r.length;c++)r[c].handlers&&p.push(r[c]);r=p.sort(function(e,t){var r=e.types||[0,0,0],n=r[0],s=r[1],o=r[2],i=t.types||[0,0,0],a=i[0],h=i[1],u=i[2];if(o!==u)return o-u;if(o){if(n!==a)return a-n;if(s!==h)return h-s}return s!==h?s-h:n!==a?a-n:0});var f=p[0];return f&&f.handlers&&(s&&f.pattern&&"(.+)$"===f.pattern.slice(-5)&&(h+="/"),t=function(e,t,r){var n=e.handlers,s=e.regex();if(!s||!n)throw new Error("state not initialized");var o=t.match(s),i=1,a=new S(r);a.length=n.length;for(var h=0;h<n.length;h++){var u=n[h],d=u.names,p=u.shouldDecodes,c=m,f=!1;if(d!==w&&p!==w)for(var l=0;l<d.length;l++){f=!0;var y=d[l],v=o&&o[i++];c===m&&(c={}),D.ENCODE_AND_DECODE_PATH_SEGMENTS&&p[l]?c[y]=v&&decodeURIComponent(v):c[y]=v}a[h]={handler:u.handler,params:c,isDynamic:f}}return a}(f,h,n)),t},D.VERSION="0.3.4",D.ENCODE_AND_DECODE_PATH_SEGMENTS=!0,D.Normalizer={normalizeSegment:d,normalizePath:l,encodePathSegment:o},D.prototype.map=function(e,t){var r=new a;e(h("",r,this.delegate)),function e(t,r,n,s){for(var o=r.routes,i=Object.keys(o),a=0;a<i.length;a++){var h=i[a],u=t.slice();p(u,h,o[h]);var d=r.children[h];d?e(u,d,n,s):n.call(s,u)}}([],r,function(e){t?t(this,e):this.add(e)},this)};"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var e,P=(function(e,t){e.exports=function(){var s=function(e,t,r,n){this.type=e,this.bubbles=t,this.cancelable=r,this.target=n};s.prototype={stopPropagation:function(){},preventDefault:function(){this.defaultPrevented=!0}};var n={100:"Continue",101:"Switching Protocols",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",300:"Multiple Choice",301:"Moved Permanently",302:"Found",303:"See Other",304:"Not Modified",305:"Use Proxy",307:"Temporary Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Request Entity Too Large",414:"Request-URI Too Long",415:"Unsupported Media Type",416:"Requested Range Not Satisfiable",417:"Expectation Failed",422:"Unprocessable Entity",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported"},r={"Accept-Charset":!0,"Accept-Encoding":!0,Connection:!0,"Content-Length":!0,Cookie:!0,Cookie2:!0,"Content-Transfer-Encoding":!0,Date:!0,Expect:!0,Host:!0,"Keep-Alive":!0,Referer:!0,TE:!0,Trailer:!0,"Transfer-Encoding":!0,Upgrade:!0,"User-Agent":!0,Via:!0};function o(r,n){n.addEventListener(r,function(e){var t=n["on"+r];t&&"function"==typeof t&&t.call(e.target,e)})}function e(){this._eventListeners={};for(var e=["loadstart","progress","load","abort","loadend"],t=e.length-1;0<=t;t--)o(e[t],this)}function i(){e.call(this),this.readyState=i.UNSENT,this.requestHeaders={},this.requestBody=null,this.status=0,this.statusText="",this.upload=new e}e.prototype={addEventListener:function(e,t){this._eventListeners[e]=this._eventListeners[e]||[],this._eventListeners[e].push(t)},removeEventListener:function(e,t){for(var r=this._eventListeners[e]||[],n=0,s=r.length;n<s;++n)if(r[n]==t)return r.splice(n,1)},dispatchEvent:function(e){for(var t=e.type,r=this._eventListeners[t]||[],n=0;n<r.length;n++)"function"==typeof r[n]?r[n].call(this,e):r[n].handleEvent(e);return!!e.defaultPrevented},_progress:function(e,t,r){var n=new s("progress");n.target=this,n.lengthComputable=e,n.loaded=t,n.total=r,this.dispatchEvent(n)}},i.prototype=new e;var t={UNSENT:i.UNSENT=0,OPENED:i.OPENED=1,HEADERS_RECEIVED:i.HEADERS_RECEIVED=2,LOADING:i.LOADING=3,DONE:i.DONE=4,async:!0,withCredentials:!1,open:function(e,t,r,n,s){this.method=e,this.url=t,this.async="boolean"!=typeof r||r,this.username=n,this.password=s,this.responseText=null,this.responseXML=null,this.requestHeaders={},this.sendFlag=!1,this._readyStateChange(i.OPENED)},setRequestHeader:function(e,t){if(h(this),r[e]||/^(Sec-|Proxy-)/.test(e))throw new Error('Refused to set unsafe header "'+e+'"');this.requestHeaders[e]?this.requestHeaders[e]+=","+t:this.requestHeaders[e]=t},send:function(e){if(h(this),!/^(get|head)$/i.test(this.method)){var t=!1;Object.keys(this.requestHeaders).forEach(function(e){"content-type"===e.toLowerCase()&&(t=!0)}),t||(e||"").toString().match("FormData")||(this.requestHeaders["Content-Type"]="text/plain;charset=UTF-8"),this.requestBody=e}this.errorFlag=!1,this.sendFlag=this.async,this._readyStateChange(i.OPENED),"function"==typeof this.onSend&&this.onSend(this),this.dispatchEvent(new s("loadstart",!1,!1,this))},abort:function(){this.aborted=!0,this.responseText=null,this.errorFlag=!0,this.requestHeaders={},this.readyState>i.UNSENT&&this.sendFlag&&(this._readyStateChange(i.DONE),this.sendFlag=!1),this.readyState=i.UNSENT,this.dispatchEvent(new s("abort",!1,!1,this)),"function"==typeof this.onerror&&this.onerror()},getResponseHeader:function(e){if(this.readyState<i.HEADERS_RECEIVED)return null;if(/^Set-Cookie2?$/i.test(e))return null;for(var t in e=e.toLowerCase(),this.responseHeaders)if(t.toLowerCase()==e)return this.responseHeaders[t];return null},getAllResponseHeaders:function(){if(this.readyState<i.HEADERS_RECEIVED)return"";var e="";for(var t in this.responseHeaders)this.responseHeaders.hasOwnProperty(t)&&!/^Set-Cookie2?$/i.test(t)&&(e+=t+": "+this.responseHeaders[t]+"\r\n");return e},overrideMimeType:function(e){"string"==typeof e&&(this.forceMimeType=e.toLowerCase())},_readyStateChange:function(e){this.readyState=e,"function"==typeof this.onreadystatechange&&this.onreadystatechange(new s("readystatechange")),this.dispatchEvent(new s("readystatechange")),this.readyState==i.DONE&&(this.dispatchEvent(new s("load",!1,!1,this)),this.dispatchEvent(new s("loadend",!1,!1,this)))},_setResponseHeaders:function(e){for(var t in this.responseHeaders={},e)e.hasOwnProperty(t)&&(this.responseHeaders[t]=e[t]);this.forceMimeType&&(this.responseHeaders["Content-Type"]=this.forceMimeType),this.async?this._readyStateChange(i.HEADERS_RECEIVED):this.readyState=i.HEADERS_RECEIVED},_setResponseBody:function(e){!function(e){if(e.readyState==i.DONE)throw new Error("Request done")}(this),function(e){if(e.async&&e.readyState!=i.HEADERS_RECEIVED)throw new Error("No headers received")}(this),function(e){if("string"!=typeof e){var t=new Error("Attempted to respond to fake XMLHttpRequest with "+e+", which is not a string.");throw t.name="InvalidBodyException",t}}(e);var t=this.chunkSize||10,r=0;for(this.responseText="";this.async&&this._readyStateChange(i.LOADING),this.responseText+=e.substring(r,r+t),(r+=t)<e.length;);var n=this.getResponseHeader("Content-Type");if(this.responseText&&(!n||/(text\/xml)|(application\/xml)|(\+xml)/.test(n)))try{this.responseXML=function(e){var t;if("undefined"!=typeof DOMParser){var r=new DOMParser;t=r.parseFromString(e,"text/xml")}else(t=new ActiveXObject("Microsoft.XMLDOM")).async="false",t.loadXML(e);return t}(this.responseText)}catch(e){}this.async?this._readyStateChange(i.DONE):this.readyState=i.DONE},respond:function(e,t,r){this._setResponseHeaders(t||{}),this.status="number"==typeof e?e:200,this.statusText=n[this.status],this._setResponseBody(r||"")}};for(var a in t)i.prototype[a]=t[a];function h(e){if(e.readyState!==i.OPENED)throw new Error("INVALID_STATE_ERR");if(e.sendFlag)throw new Error("INVALID_STATE_ERR")}return i}()}(e={exports:{}},e.exports),e.exports),x="URLSearchParams"in self,q="Symbol"in self&&"iterator"in Symbol,O="FileReader"in self&&"Blob"in self&&function(){try{return new Blob,!0}catch(e){return!1}}(),H="FormData"in self,U="ArrayBuffer"in self;if(U)var N=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],L=ArrayBuffer.isView||function(e){return e&&-1<N.indexOf(Object.prototype.toString.call(e))};function I(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(e))throw new TypeError("Invalid character in header field name");return e.toLowerCase()}function B(e){return"string"!=typeof e&&(e=String(e)),e}function j(t){var e={next:function(){var e=t.shift();return{done:void 0===e,value:e}}};return q&&(e[Symbol.iterator]=function(){return e}),e}function F(t){this.map={},t instanceof F?t.forEach(function(e,t){this.append(t,e)},this):Array.isArray(t)?t.forEach(function(e){this.append(e[0],e[1])},this):t&&Object.getOwnPropertyNames(t).forEach(function(e){this.append(e,t[e])},this)}function M(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function k(r){return new Promise(function(e,t){r.onload=function(){e(r.result)},r.onerror=function(){t(r.error)}})}function G(e){var t=new FileReader,r=k(t);return t.readAsArrayBuffer(e),r}function X(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function V(){return this.bodyUsed=!1,this._initBody=function(e){var t;(this._bodyInit=e)?"string"==typeof e?this._bodyText=e:O&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:H&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:x&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():U&&O&&((t=e)&&DataView.prototype.isPrototypeOf(t))?(this._bodyArrayBuffer=X(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):U&&(ArrayBuffer.prototype.isPrototypeOf(e)||L(e))?this._bodyArrayBuffer=X(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):x&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},O&&(this.blob=function(){var e=M(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?M(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(G)}),this.text=function(){var e,t,r,n=M(this);if(n)return n;if(this._bodyBlob)return e=this._bodyBlob,t=new FileReader,r=k(t),t.readAsText(e),r;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),r=new Array(t.length),n=0;n<t.length;n++)r[n]=String.fromCharCode(t[n]);return r.join("")}(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},H&&(this.formData=function(){return this.text().then(Y)}),this.json=function(){return this.text().then(JSON.parse)},this}F.prototype.append=function(e,t){e=I(e),t=B(t);var r=this.map[e];this.map[e]=r?r+", "+t:t},F.prototype.delete=function(e){delete this.map[I(e)]},F.prototype.get=function(e){return e=I(e),this.has(e)?this.map[e]:null},F.prototype.has=function(e){return this.map.hasOwnProperty(I(e))},F.prototype.set=function(e,t){this.map[I(e)]=B(t)},F.prototype.forEach=function(e,t){for(var r in this.map)this.map.hasOwnProperty(r)&&e.call(t,this.map[r],r,this)},F.prototype.keys=function(){var r=[];return this.forEach(function(e,t){r.push(t)}),j(r)},F.prototype.values=function(){var t=[];return this.forEach(function(e){t.push(e)}),j(t)},F.prototype.entries=function(){var r=[];return this.forEach(function(e,t){r.push([t,e])}),j(r)},q&&(F.prototype[Symbol.iterator]=F.prototype.entries);var z=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function $(e,t){var r,n,s=(t=t||{}).body;if(e instanceof $){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,t.headers||(this.headers=new F(e.headers)),this.method=e.method,this.mode=e.mode,this.signal=e.signal,s||null==e._bodyInit||(s=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=t.credentials||this.credentials||"same-origin",!t.headers&&this.headers||(this.headers=new F(t.headers)),this.method=(r=t.method||this.method||"GET",n=r.toUpperCase(),-1<z.indexOf(n)?n:r),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&s)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(s)}function Y(e){var s=new FormData;return e.trim().split("&").forEach(function(e){if(e){var t=e.split("="),r=t.shift().replace(/\+/g," "),n=t.join("=").replace(/\+/g," ");s.append(decodeURIComponent(r),decodeURIComponent(n))}}),s}function Q(e,t){t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=200<=this.status&&this.status<300,this.statusText="statusText"in t?t.statusText:"OK",this.headers=new F(t.headers),this.url=t.url||"",this._initBody(e)}$.prototype.clone=function(){return new $(this,{body:this._bodyInit})},V.call($.prototype),V.call(Q.prototype),Q.prototype.clone=function(){return new Q(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new F(this.headers),url:this.url})},Q.error=function(){var e=new Q(null,{status:0,statusText:""});return e.type="error",e};var K=[301,302,303,307,308];Q.redirect=function(e,t){if(-1===K.indexOf(t))throw new RangeError("Invalid status code");return new Q(null,{status:t,headers:{location:e}})};var J=self.DOMException;try{new J}catch(e){(J=function(e,t){this.message=e,this.name=t;var r=Error(e);this.stack=r.stack}).prototype=Object.create(Error.prototype),J.prototype.constructor=J}function W(s,i){return new Promise(function(n,e){var t=new $(s,i);if(t.signal&&t.signal.aborted)return e(new J("Aborted","AbortError"));var o=new XMLHttpRequest;function r(){o.abort()}o.onload=function(){var e,s,t={status:o.status,statusText:o.statusText,headers:(e=o.getAllResponseHeaders()||"",s=new F,e.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(e){var t=e.split(":"),r=t.shift().trim();if(r){var n=t.join(":").trim();s.append(r,n)}}),s)};t.url="responseURL"in o?o.responseURL:t.headers.get("X-Request-URL");var r="response"in o?o.response:o.responseText;n(new Q(r,t))},o.onerror=function(){e(new TypeError("Network request failed"))},o.ontimeout=function(){e(new TypeError("Network request failed"))},o.onabort=function(){e(new J("Aborted","AbortError"))},o.open(t.method,t.url,!0),"include"===t.credentials?o.withCredentials=!0:"omit"===t.credentials&&(o.withCredentials=!1),"responseType"in o&&O&&(o.responseType="blob"),t.headers.forEach(function(e,t){o.setRequestHeader(t,e)}),t.signal&&(t.signal.addEventListener("abort",r),o.onreadystatechange=function(){4===o.readyState&&t.signal.removeEventListener("abort",r)}),o.send(void 0===t._bodyInit?null:t._bodyInit)})}W.polyfill=!0,self.fetch||(self.fetch=W,self.Headers=F,self.Request=$,self.Response=Q);var Z=Object.freeze({Headers:F,Request:$,Response:Q,get DOMException(){return J},fetch:W});function ee(e){var t=document.createElement("a");t.href=e,t.host||(t.href=t.href);var r=t.pathname;"/"!==r.charAt(0)&&(r="/"+r);var n=t.host;return"80"!==t.port&&"443"!==t.port||(n=t.hostname),{host:n,protocol:t.protocol,search:t.search,hash:t.hash,href:t.href,pathname:r,fullpath:r+(t.search||"")+(t.hash||"")}}function te(){this.verbs={GET:new D,PUT:new D,POST:new D,DELETE:new D,PATCH:new D,HEAD:new D,OPTIONS:new D}}function re(){this._registries={}}function ne(){this.hosts=new re;var e=arguments[arguments.length-1],t="object"==typeof e?e:null,r=t&&!1===t.trackRequests,n={push:function(){},length:0};this.handlers=[],this.handledRequests=r?n:[],this.passthroughRequests=r?n:[],this.unhandledRequests=r?n:[],this.requestReferences=[],this.forcePassthrough=t&&!0===t.forcePassthrough,this.disableUnhandled=t&&!0===t.disableUnhandled,this._nativeXMLHttpRequest=window.XMLHttpRequest,this.running=!1;var s={pretender:this};this.ctx=s,window.XMLHttpRequest=function(d){function e(){P.call(this)}((e.prototype=Object.create(P.prototype)).constructor=e).prototype.send=function(){if(!d.pretender.running)throw new Error("You shut down a Pretender instance while there was a pending request. That request just tried to complete. Check to see if you accidentally shut down a pretender earlier than you intended to");if(P.prototype.send.apply(this,arguments),d.pretender.checkPassthrough(this)){var e=function(r){var e,t=["error","timeout","abort","readystatechange"],n=[],s=["readyState","responseText","responseXML","status","statusText"],o=r._passthroughRequest=new d.pretender._nativeXMLHttpRequest;function i(e,t,r){e.dispatchEvent(r),e["on"+t]&&e["on"+t](r)}function a(t){o["on"+t]=function(e){!function(e,t,r){for(var n=0;n<e.length;n++){var s=e[n];s in t&&(r[s]=t[s])}}(s,o,r),i(r,t,e)}}function h(t){o.upload&&(o.upload["on"+t]=function(e){i(r.upload,t,e)})}o.open(r.method,r.url,r.async,r.username,r.password),"arraybuffer"===r.responseType&&(s=["readyState","response","status","statusText"],o.responseType=r.responseType),"onload"in o&&t.push("load"),r.async&&"arraybuffer"!==r.responseType&&(t.push("progress"),n.push("progress"));for(e=0;e<t.length;e++)a(t[e]);for(e=0;e<n.length;e++)h(n[e]);for(var u in r.async&&(o.timeout=r.timeout,o.withCredentials=r.withCredentials),r.requestHeaders)o.setRequestHeader(u,r.requestHeaders[u]);return o}(this);e.send.apply(e,arguments)}else d.pretender.handleRequest(this)},e.prototype._passthroughCheck=function(e,t){return this._passthroughRequest?this._passthroughRequest[e].apply(this._passthroughRequest,t):P.prototype[e].apply(this,t)},e.prototype.abort=function(){return this._passthroughCheck("abort",arguments)},e.prototype.getResponseHeader=function(){return this._passthroughCheck("getResponseHeader",arguments)},e.prototype.getAllResponseHeaders=function(){return this._passthroughCheck("getAllResponseHeaders",arguments)},d.pretender._nativeXMLHttpRequest.prototype._passthroughCheck&&console.warn("You created a second Pretender instance while there was already one running. Running two Pretender servers at once will lead to unexpected results and will be removed entirely in a future major version.Please call .shutdown() on your instances when you no longer need them to respond.");return e}(s),this._fetchProps=Z?["fetch","Headers","Request","Response"]:[],this._fetchProps.forEach(function(e){this["_native"+e]=window[e],window[e]=Z[e]},this),this.running=!0;for(var o=t?arguments.length-1:arguments.length,i=0;i<o;i++)this.map(arguments[i])}function se(n){return function(e,t,r){return this.register(n,e,t,r)}}re.prototype.forURL=function(e){var t=ee(e).host,r=this._registries[t];return void 0===r&&(r=this._registries[t]=new te(t)),r.verbs};var oe={};return ne.prototype={get:se("GET"),post:se("POST"),put:se("PUT"),delete:se("DELETE"),patch:se("PATCH"),head:se("HEAD"),options:se("OPTIONS"),map:function(e){e.call(this)},register:function(e,t,r,n){if(!r)throw new Error("The function you tried passing to Pretender to handle "+e+" "+t+" is undefined or missing.");return r.numberOfCalls=0,r.async=n,this.handlers.push(r),this.hosts.forURL(t)[e].add([{path:ee(t).fullpath,handler:r}]),r},passthrough:oe,checkPassthrough:function(e){var t=e.method.toUpperCase(),r=ee(e.url).fullpath,n=this.hosts.forURL(e.url)[t].recognize(r),s=n&&n[0];return!!(s&&s.handler===oe||this.forcePassthrough)&&(this.passthroughRequests.push(e),this.passthroughRequest(t,r,e),!0)},handleRequest:function(o){var i=o.method.toUpperCase(),a=o.url,e=this._handlerFor(i,a,o);if(e){e.handler.numberOfCalls++;var h=e.handler.async;this.handledRequests.push(o);var u=this,t=function(e){if(t=e,"[object Array]"!==Object.prototype.toString.call(t)){throw new Error("Nothing returned by handler for "+a+". Remember to `return [status, headers, body];` in your route handler.")}var t,r=e[0],n=u.prepareHeaders(e[1]),s=u.prepareBody(e[2],n);u.handleResponse(o,h,function(){o.respond(r,n,s),u.handledRequest(i,a,o)})};try{var r=e.handler(o);r&&"function"==typeof r.then?r.then(function(e){t(e)}):t(r)}catch(e){this.erroredRequest(i,a,o,e),this.resolve(o)}}else this.disableUnhandled||(this.unhandledRequests.push(o),this.unhandledRequest(i,a,o))},handleResponse:function(e,t,r){var n="function"==typeof t?t():t;if(!1===(n="boolean"==typeof n||"number"==typeof n?n:0))r();else{var s=this;s.requestReferences.push({request:e,callback:r}),!0!==n&&(!function t(r,n,s){setTimeout(function(){if(!r.aborted&&!r.status){var e=(new Date).getTime()-n.getTime();r.upload._progress(!0,e,s),r._progress(!0,e,s),t(r,n,s)}},50)}(e,new Date,n),setTimeout(function(){s.resolve(e)},n))}},resolve:function(e){for(var t=0,r=this.requestReferences.length;t<r;t++){var n=this.requestReferences[t];if(n.request===e){n.callback(),this.requestReferences.splice(t,1);break}}},requiresManualResolution:function(e,t){var r=this._handlerFor(e.toUpperCase(),t,{});if(!r)return!1;var n=r.handler.async;return"function"==typeof n?!0===n():!0===n},prepareBody:function(e){return e},prepareHeaders:function(e){return e},handledRequest:function(){},passthroughRequest:function(){},unhandledRequest:function(e,t){throw new Error("Pretender intercepted "+e+" "+t+" but no handler was defined for this type of request")},erroredRequest:function(e,t,r,n){throw n.message="Pretender intercepted "+e+" "+t+" but encountered an error: "+n.message,n},_handlerFor:function(e,t,r){var n=this.hosts.forURL(t)[e].recognize(ee(t).fullpath),s=n?n[0]:null;return s&&(r.params=s.params,r.queryParams=n.queryParams),s},shutdown:function(){window.XMLHttpRequest=this._nativeXMLHttpRequest,this._fetchProps.forEach(function(e){window[e]=this["_native"+e]},this),this.ctx.pretender=void 0,this.running=!1}},ne.parseURL=ee,ne.Hosts=re,ne.Registry=te,ne}(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment