UNPKG

@uirouter/core

Version:

UI-Router Core: Framework agnostic, State-based routing for JavaScript Single Page Apps

1,284 lines (1,272 loc) 388 kB
/** * UI-Router Core: Framework agnostic, State-based routing for JavaScript Single Page Apps * @version v6.1.0 * @link https://ui-router.github.io * @license MIT License, http://www.opensource.org/licenses/MIT */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global['@uirouter/core'] = {})); }(this, (function (exports) { 'use strict'; /** * Higher order functions * * These utility functions are exported, but are subject to change without notice. * * @packageDocumentation */ var __spreadArrays = (undefined && undefined.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; /** * Returns a new function for [Partial Application](https://en.wikipedia.org/wiki/Partial_application) of the original function. * * Given a function with N parameters, returns a new function that supports partial application. * The new function accepts anywhere from 1 to N parameters. When that function is called with M parameters, * where M is less than N, it returns a new function that accepts the remaining parameters. It continues to * accept more parameters until all N parameters have been supplied. * * * This contrived example uses a partially applied function as an predicate, which returns true * if an object is found in both arrays. * @example * ``` * // returns true if an object is in both of the two arrays * function inBoth(array1, array2, object) { * return array1.indexOf(object) !== -1 && * array2.indexOf(object) !== 1; * } * let obj1, obj2, obj3, obj4, obj5, obj6, obj7 * let foos = [obj1, obj3] * let bars = [obj3, obj4, obj5] * * // A curried "copy" of inBoth * let curriedInBoth = curry(inBoth); * // Partially apply both the array1 and array2 * let inFoosAndBars = curriedInBoth(foos, bars); * * // Supply the final argument; since all arguments are * // supplied, the original inBoth function is then called. * let obj1InBoth = inFoosAndBars(obj1); // false * * // Use the inFoosAndBars as a predicate. * // Filter, on each iteration, supplies the final argument * let allObjs = [ obj1, obj2, obj3, obj4, obj5, obj6, obj7 ]; * let foundInBoth = allObjs.filter(inFoosAndBars); // [ obj3 ] * * ``` * * @param fn * @returns {*|function(): (*|any)} */ function curry(fn) { return function curried() { if (arguments.length >= fn.length) { return fn.apply(this, arguments); } var args = Array.prototype.slice.call(arguments); return curried.bind.apply(curried, __spreadArrays([this], args)); }; } /** * Given a varargs list of functions, returns a function that composes the argument functions, right-to-left * given: f(x), g(x), h(x) * let composed = compose(f,g,h) * then, composed is: f(g(h(x))) */ function compose() { var args = arguments; var start = args.length - 1; return function () { var i = start, result = args[start].apply(this, arguments); while (i--) result = args[i].call(this, result); return result; }; } /** * Given a varargs list of functions, returns a function that is composes the argument functions, left-to-right * given: f(x), g(x), h(x) * let piped = pipe(f,g,h); * then, piped is: h(g(f(x))) */ function pipe() { var funcs = []; for (var _i = 0; _i < arguments.length; _i++) { funcs[_i] = arguments[_i]; } return compose.apply(null, [].slice.call(arguments).reverse()); } /** * Given a property name, returns a function that returns that property from an object * let obj = { foo: 1, name: "blarg" }; * let getName = prop("name"); * getName(obj) === "blarg" */ var prop = function (name) { return function (obj) { return obj && obj[name]; }; }; /** * Given a property name and a value, returns a function that returns a boolean based on whether * the passed object has a property that matches the value * let obj = { foo: 1, name: "blarg" }; * let getName = propEq("name", "blarg"); * getName(obj) === true */ var propEq = curry(function (name, _val, obj) { return obj && obj[name] === _val; }); /** * Given a dotted property name, returns a function that returns a nested property from an object, or undefined * let obj = { id: 1, nestedObj: { foo: 1, name: "blarg" }, }; * let getName = prop("nestedObj.name"); * getName(obj) === "blarg" * let propNotFound = prop("this.property.doesnt.exist"); * propNotFound(obj) === undefined */ var parse = function (name) { return pipe.apply(null, name.split('.').map(prop)); }; /** * Given a function that returns a truthy or falsey value, returns a * function that returns the opposite (falsey or truthy) value given the same inputs */ var not = function (fn) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return !fn.apply(null, args); }; }; /** * Given two functions that return truthy or falsey values, returns a function that returns truthy * if both functions return truthy for the given arguments */ function and(fn1, fn2) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return fn1.apply(null, args) && fn2.apply(null, args); }; } /** * Given two functions that return truthy or falsey values, returns a function that returns truthy * if at least one of the functions returns truthy for the given arguments */ function or(fn1, fn2) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return fn1.apply(null, args) || fn2.apply(null, args); }; } /** * Check if all the elements of an array match a predicate function * * @param fn1 a predicate function `fn1` * @returns a function which takes an array and returns true if `fn1` is true for all elements of the array */ var all = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b && !!fn1(x); }, true); }; }; // tslint:disable-next-line:variable-name var any = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b || !!fn1(x); }, false); }; }; /** Given a class, returns a Predicate function that returns true if the object is of that class */ var is = function (ctor) { return function (obj) { return (obj != null && obj.constructor === ctor) || obj instanceof ctor; }; }; /** Given a value, returns a Predicate function that returns true if another value is === equal to the original value */ var eq = function (value) { return function (other) { return value === other; }; }; /** Given a value, returns a function which returns the value */ var val = function (v) { return function () { return v; }; }; function invoke(fnName, args) { return function (obj) { return obj[fnName].apply(obj, args); }; } /** * Sorta like Pattern Matching (a functional programming conditional construct) * * See http://c2.com/cgi/wiki?PatternMatching * * This is a conditional construct which allows a series of predicates and output functions * to be checked and then applied. Each predicate receives the input. If the predicate * returns truthy, then its matching output function (mapping function) is provided with * the input and, then the result is returned. * * Each combination (2-tuple) of predicate + output function should be placed in an array * of size 2: [ predicate, mapFn ] * * These 2-tuples should be put in an outer array. * * @example * ``` * * // Here's a 2-tuple where the first element is the isString predicate * // and the second element is a function that returns a description of the input * let firstTuple = [ angular.isString, (input) => `Heres your string ${input}` ]; * * // Second tuple: predicate "isNumber", mapfn returns a description * let secondTuple = [ angular.isNumber, (input) => `(${input}) That's a number!` ]; * * let third = [ (input) => input === null, (input) => `Oh, null...` ]; * * let fourth = [ (input) => input === undefined, (input) => `notdefined` ]; * * let descriptionOf = pattern([ firstTuple, secondTuple, third, fourth ]); * * console.log(descriptionOf(undefined)); // 'notdefined' * console.log(descriptionOf(55)); // '(55) That's a number!' * console.log(descriptionOf("foo")); // 'Here's your string foo' * ``` * * @param struct A 2D array. Each element of the array should be an array, a 2-tuple, * with a Predicate and a mapping/output function * @returns {function(any): *} */ function pattern(struct) { return function (x) { for (var i = 0; i < struct.length; i++) { if (struct[i][0](x)) return struct[i][1](x); } }; } /** * Predicates * * These predicates return true/false based on the input. * Although these functions are exported, they are subject to change without notice. * * @packageDocumentation */ var toStr = Object.prototype.toString; var tis = function (t) { return function (x) { return typeof x === t; }; }; var isUndefined = tis('undefined'); var isDefined = not(isUndefined); var isNull = function (o) { return o === null; }; var isNullOrUndefined = or(isNull, isUndefined); var isFunction = tis('function'); var isNumber = tis('number'); var isString = tis('string'); var isObject = function (x) { return x !== null && typeof x === 'object'; }; var isArray = Array.isArray; var isDate = (function (x) { return toStr.call(x) === '[object Date]'; }); var isRegExp = (function (x) { return toStr.call(x) === '[object RegExp]'; }); /** * Predicate which checks if a value is injectable * * A value is "injectable" if it is a function, or if it is an ng1 array-notation-style array * where all the elements in the array are Strings, except the last one, which is a Function */ function isInjectable(val) { if (isArray(val) && val.length) { var head = val.slice(0, -1), tail = val.slice(-1); return !(head.filter(not(isString)).length || tail.filter(not(isFunction)).length); } return isFunction(val); } /** * Predicate which checks if a value looks like a Promise * * It is probably a Promise if it's an object, and it has a `then` property which is a Function */ var isPromise = and(isObject, pipe(prop('then'), isFunction)); var noImpl = function (fnname) { return function () { throw new Error("No implementation for " + fnname + ". The framework specific code did not implement this method."); }; }; var makeStub = function (service, methods) { return methods.reduce(function (acc, key) { return ((acc[key] = noImpl(service + "." + key + "()")), acc); }, {}); }; var services = { $q: undefined, $injector: undefined, }; var __spreadArrays$1 = (undefined && undefined.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; var root = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || undefined; var angular = root.angular || {}; var fromJson = angular.fromJson || JSON.parse.bind(JSON); var toJson = angular.toJson || JSON.stringify.bind(JSON); var forEach = angular.forEach || _forEach; var extend = Object.assign || _extend; var equals = angular.equals || _equals; function identity(x) { return x; } function noop() { } /** * Builds proxy functions on the `to` object which pass through to the `from` object. * * For each key in `fnNames`, creates a proxy function on the `to` object. * The proxy function calls the real function on the `from` object. * * * #### Example: * This example creates an new class instance whose functions are prebound to the new'd object. * ```js * class Foo { * constructor(data) { * // Binds all functions from Foo.prototype to 'this', * // then copies them to 'this' * bindFunctions(Foo.prototype, this, this); * this.data = data; * } * * log() { * console.log(this.data); * } * } * * let myFoo = new Foo([1,2,3]); * var logit = myFoo.log; * logit(); // logs [1, 2, 3] from the myFoo 'this' instance * ``` * * #### Example: * This example creates a bound version of a service function, and copies it to another object * ``` * * var SomeService = { * this.data = [3, 4, 5]; * this.log = function() { * console.log(this.data); * } * } * * // Constructor fn * function OtherThing() { * // Binds all functions from SomeService to SomeService, * // then copies them to 'this' * bindFunctions(SomeService, this, SomeService); * } * * let myOtherThing = new OtherThing(); * myOtherThing.log(); // logs [3, 4, 5] from SomeService's 'this' * ``` * * @param source A function that returns the source object which contains the original functions to be bound * @param target A function that returns the target object which will receive the bound functions * @param bind A function that returns the object which the functions will be bound to * @param fnNames The function names which will be bound (Defaults to all the functions found on the 'from' object) * @param latebind If true, the binding of the function is delayed until the first time it's invoked */ function createProxyFunctions(source, target, bind, fnNames, latebind) { if (latebind === void 0) { latebind = false; } var bindFunction = function (fnName) { return source()[fnName].bind(bind()); }; var makeLateRebindFn = function (fnName) { return function lateRebindFunction() { target[fnName] = bindFunction(fnName); return target[fnName].apply(null, arguments); }; }; fnNames = fnNames || Object.keys(source()); return fnNames.reduce(function (acc, name) { acc[name] = latebind ? makeLateRebindFn(name) : bindFunction(name); return acc; }, target); } /** * prototypal inheritance helper. * Creates a new object which has `parent` object as its prototype, and then copies the properties from `extra` onto it */ var inherit = function (parent, extra) { return extend(Object.create(parent), extra); }; /** Given an array, returns true if the object is found in the array, (using indexOf) */ var inArray = curry(_inArray); function _inArray(array, obj) { return array.indexOf(obj) !== -1; } /** * Given an array, and an item, if the item is found in the array, it removes it (in-place). * The same array is returned */ var removeFrom = curry(_removeFrom); function _removeFrom(array, obj) { var idx = array.indexOf(obj); if (idx >= 0) array.splice(idx, 1); return array; } /** pushes a values to an array and returns the value */ var pushTo = curry(_pushTo); function _pushTo(arr, val) { return arr.push(val), val; } /** Given an array of (deregistration) functions, calls all functions and removes each one from the source array */ var deregAll = function (functions) { return functions.slice().forEach(function (fn) { typeof fn === 'function' && fn(); removeFrom(functions, fn); }); }; /** * Applies a set of defaults to an options object. The options object is filtered * to only those properties of the objects in the defaultsList. * Earlier objects in the defaultsList take precedence when applying defaults. */ function defaults(opts) { var defaultsList = []; for (var _i = 1; _i < arguments.length; _i++) { defaultsList[_i - 1] = arguments[_i]; } var defaultVals = extend.apply(void 0, __spreadArrays$1([{}], defaultsList.reverse())); return extend(defaultVals, pick(opts || {}, Object.keys(defaultVals))); } /** Reduce function that merges each element of the list into a single object, using extend */ var mergeR = function (memo, item) { return extend(memo, item); }; /** * Finds the common ancestor path between two states. * * @param {Object} first The first state. * @param {Object} second The second state. * @return {Array} Returns an array of state names in descending order, not including the root. */ function ancestors(first, second) { var path = []; // tslint:disable-next-line:forin for (var n in first.path) { if (first.path[n] !== second.path[n]) break; path.push(first.path[n]); } return path; } /** * Return a copy of the object only containing the whitelisted properties. * * #### Example: * ``` * var foo = { a: 1, b: 2, c: 3 }; * var ab = pick(foo, ['a', 'b']); // { a: 1, b: 2 } * ``` * @param obj the source object * @param propNames an Array of strings, which are the whitelisted property names */ function pick(obj, propNames) { var objCopy = {}; for (var _prop in obj) { if (propNames.indexOf(_prop) !== -1) { objCopy[_prop] = obj[_prop]; } } return objCopy; } /** * Return a copy of the object omitting the blacklisted properties. * * @example * ``` * * var foo = { a: 1, b: 2, c: 3 }; * var ab = omit(foo, ['a', 'b']); // { c: 3 } * ``` * @param obj the source object * @param propNames an Array of strings, which are the blacklisted property names */ function omit(obj, propNames) { return Object.keys(obj) .filter(not(inArray(propNames))) .reduce(function (acc, key) { return ((acc[key] = obj[key]), acc); }, {}); } /** * Maps an array, or object to a property (by name) */ function pluck(collection, propName) { return map(collection, prop(propName)); } /** Filters an Array or an Object's properties based on a predicate */ function filter(collection, callback) { var arr = isArray(collection), result = arr ? [] : {}; var accept = arr ? function (x) { return result.push(x); } : function (x, key) { return (result[key] = x); }; forEach(collection, function (item, i) { if (callback(item, i)) accept(item, i); }); return result; } /** Finds an object from an array, or a property of an object, that matches a predicate */ function find(collection, callback) { var result; forEach(collection, function (item, i) { if (result) return; if (callback(item, i)) result = item; }); return result; } /** Given an object, returns a new object, where each property is transformed by the callback function */ var mapObj = map; /** Maps an array or object properties using a callback function */ function map(collection, callback, target) { target = target || (isArray(collection) ? [] : {}); forEach(collection, function (item, i) { return (target[i] = callback(item, i)); }); return target; } /** * Given an object, return its enumerable property values * * @example * ``` * * let foo = { a: 1, b: 2, c: 3 } * let vals = values(foo); // [ 1, 2, 3 ] * ``` */ var values = function (obj) { return Object.keys(obj).map(function (key) { return obj[key]; }); }; /** * Reduce function that returns true if all of the values are truthy. * * @example * ``` * * let vals = [ 1, true, {}, "hello world"]; * vals.reduce(allTrueR, true); // true * * vals.push(0); * vals.reduce(allTrueR, true); // false * ``` */ var allTrueR = function (memo, elem) { return memo && elem; }; /** * Reduce function that returns true if any of the values are truthy. * * * @example * ``` * * let vals = [ 0, null, undefined ]; * vals.reduce(anyTrueR, true); // false * * vals.push("hello world"); * vals.reduce(anyTrueR, true); // true * ``` */ var anyTrueR = function (memo, elem) { return memo || elem; }; /** * Reduce function which un-nests a single level of arrays * @example * ``` * * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; * input.reduce(unnestR, []) // [ "a", "b", "c", "d", [ "double, "nested" ] ] * ``` */ var unnestR = function (memo, elem) { return memo.concat(elem); }; /** * Reduce function which recursively un-nests all arrays * * @example * ``` * * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; * input.reduce(unnestR, []) // [ "a", "b", "c", "d", "double, "nested" ] * ``` */ var flattenR = function (memo, elem) { return isArray(elem) ? memo.concat(elem.reduce(flattenR, [])) : pushR(memo, elem); }; /** * Reduce function that pushes an object to an array, then returns the array. * Mostly just for [[flattenR]] and [[uniqR]] */ function pushR(arr, obj) { arr.push(obj); return arr; } /** Reduce function that filters out duplicates */ var uniqR = function (acc, token) { return (inArray(acc, token) ? acc : pushR(acc, token)); }; /** * Return a new array with a single level of arrays unnested. * * @example * ``` * * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; * unnest(input) // [ "a", "b", "c", "d", [ "double, "nested" ] ] * ``` */ var unnest = function (arr) { return arr.reduce(unnestR, []); }; /** * Return a completely flattened version of an array. * * @example * ``` * * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ]; * flatten(input) // [ "a", "b", "c", "d", "double, "nested" ] * ``` */ var flatten = function (arr) { return arr.reduce(flattenR, []); }; /** * Given a .filter Predicate, builds a .filter Predicate which throws an error if any elements do not pass. * @example * ``` * * let isNumber = (obj) => typeof(obj) === 'number'; * let allNumbers = [ 1, 2, 3, 4, 5 ]; * allNumbers.filter(assertPredicate(isNumber)); //OK * * let oneString = [ 1, 2, 3, 4, "5" ]; * oneString.filter(assertPredicate(isNumber, "Not all numbers")); // throws Error(""Not all numbers""); * ``` */ var assertPredicate = assertFn; /** * Given a .map function, builds a .map function which throws an error if any mapped elements do not pass a truthyness test. * @example * ``` * * var data = { foo: 1, bar: 2 }; * * let keys = [ 'foo', 'bar' ] * let values = keys.map(assertMap(key => data[key], "Key not found")); * // values is [1, 2] * * let keys = [ 'foo', 'bar', 'baz' ] * let values = keys.map(assertMap(key => data[key], "Key not found")); * // throws Error("Key not found") * ``` */ var assertMap = assertFn; function assertFn(predicateOrMap, errMsg) { if (errMsg === void 0) { errMsg = 'assert failure'; } return function (obj) { var result = predicateOrMap(obj); if (!result) { throw new Error(isFunction(errMsg) ? errMsg(obj) : errMsg); } return result; }; } /** * Like _.pairs: Given an object, returns an array of key/value pairs * * @example * ``` * * pairs({ foo: "FOO", bar: "BAR }) // [ [ "foo", "FOO" ], [ "bar": "BAR" ] ] * ``` */ var pairs = function (obj) { return Object.keys(obj).map(function (key) { return [key, obj[key]]; }); }; /** * Given two or more parallel arrays, returns an array of tuples where * each tuple is composed of [ a[i], b[i], ... z[i] ] * * @example * ``` * * let foo = [ 0, 2, 4, 6 ]; * let bar = [ 1, 3, 5, 7 ]; * let baz = [ 10, 30, 50, 70 ]; * arrayTuples(foo, bar); // [ [0, 1], [2, 3], [4, 5], [6, 7] ] * arrayTuples(foo, bar, baz); // [ [0, 1, 10], [2, 3, 30], [4, 5, 50], [6, 7, 70] ] * ``` */ function arrayTuples() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 0) return []; var maxArrayLen = args.reduce(function (min, arr) { return Math.min(arr.length, min); }, 9007199254740991); // aka 2^53 − 1 aka Number.MAX_SAFE_INTEGER var result = []; var _loop_1 = function (i) { // This is a hot function // Unroll when there are 1-4 arguments switch (args.length) { case 1: result.push([args[0][i]]); break; case 2: result.push([args[0][i], args[1][i]]); break; case 3: result.push([args[0][i], args[1][i], args[2][i]]); break; case 4: result.push([args[0][i], args[1][i], args[2][i], args[3][i]]); break; default: result.push(args.map(function (array) { return array[i]; })); break; } }; for (var i = 0; i < maxArrayLen; i++) { _loop_1(i); } return result; } /** * Reduce function which builds an object from an array of [key, value] pairs. * * Each iteration sets the key/val pair on the memo object, then returns the memo for the next iteration. * * Each keyValueTuple should be an array with values [ key: string, value: any ] * * @example * ``` * * var pairs = [ ["fookey", "fooval"], ["barkey", "barval"] ] * * var pairsToObj = pairs.reduce((memo, pair) => applyPairs(memo, pair), {}) * // pairsToObj == { fookey: "fooval", barkey: "barval" } * * // Or, more simply: * var pairsToObj = pairs.reduce(applyPairs, {}) * // pairsToObj == { fookey: "fooval", barkey: "barval" } * ``` */ function applyPairs(memo, keyValTuple) { var key, value; if (isArray(keyValTuple)) key = keyValTuple[0], value = keyValTuple[1]; if (!isString(key)) throw new Error('invalid parameters to applyPairs'); memo[key] = value; return memo; } /** Get the last element of an array */ function tail(arr) { return (arr.length && arr[arr.length - 1]) || undefined; } /** * shallow copy from src to dest */ function copy(src, dest) { if (dest) Object.keys(dest).forEach(function (key) { return delete dest[key]; }); if (!dest) dest = {}; return extend(dest, src); } /** Naive forEach implementation works with Objects or Arrays */ function _forEach(obj, cb, _this) { if (isArray(obj)) return obj.forEach(cb, _this); Object.keys(obj).forEach(function (key) { return cb(obj[key], key); }); } function _extend(toObj) { for (var i = 1; i < arguments.length; i++) { var obj = arguments[i]; if (!obj) continue; var keys = Object.keys(obj); for (var j = 0; j < keys.length; j++) { toObj[keys[j]] = obj[keys[j]]; } } return toObj; } function _equals(o1, o2) { if (o1 === o2) return true; if (o1 === null || o2 === null) return false; if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN var t1 = typeof o1, t2 = typeof o2; if (t1 !== t2 || t1 !== 'object') return false; var tup = [o1, o2]; if (all(isArray)(tup)) return _arraysEq(o1, o2); if (all(isDate)(tup)) return o1.getTime() === o2.getTime(); if (all(isRegExp)(tup)) return o1.toString() === o2.toString(); if (all(isFunction)(tup)) return true; // meh var predicates = [isFunction, isArray, isDate, isRegExp]; if (predicates.map(any).reduce(function (b, fn) { return b || !!fn(tup); }, false)) return false; var keys = {}; // tslint:disable-next-line:forin for (var key in o1) { if (!_equals(o1[key], o2[key])) return false; keys[key] = true; } for (var key in o2) { if (!keys[key]) return false; } return true; } function _arraysEq(a1, a2) { if (a1.length !== a2.length) return false; return arrayTuples(a1, a2).reduce(function (b, t) { return b && _equals(t[0], t[1]); }, true); } // issue #2676 var silenceUncaughtInPromise = function (promise) { return promise.catch(function (e) { return 0; }) && promise; }; var silentRejection = function (error) { return silenceUncaughtInPromise(services.$q.reject(error)); }; /** * Matches state names using glob-like pattern strings. * * Globs can be used in specific APIs including: * * - [[StateService.is]] * - [[StateService.includes]] * - The first argument to Hook Registration functions like [[TransitionService.onStart]] * - [[HookMatchCriteria]] and [[HookMatchCriterion]] * * A `Glob` string is a pattern which matches state names. * Nested state names are split into segments (separated by a dot) when processing. * The state named `foo.bar.baz` is split into three segments ['foo', 'bar', 'baz'] * * Globs work according to the following rules: * * ### Exact match: * * The glob `'A.B'` matches the state named exactly `'A.B'`. * * | Glob |Matches states named|Does not match state named| * |:------------|:--------------------|:---------------------| * | `'A'` | `'A'` | `'B'` , `'A.C'` | * | `'A.B'` | `'A.B'` | `'A'` , `'A.B.C'` | * | `'foo'` | `'foo'` | `'FOO'` , `'foo.bar'`| * * ### Single star (`*`) * * A single star (`*`) is a wildcard that matches exactly one segment. * * | Glob |Matches states named |Does not match state named | * |:------------|:---------------------|:--------------------------| * | `'*'` | `'A'` , `'Z'` | `'A.B'` , `'Z.Y.X'` | * | `'A.*'` | `'A.B'` , `'A.C'` | `'A'` , `'A.B.C'` | * | `'A.*.*'` | `'A.B.C'` , `'A.X.Y'`| `'A'`, `'A.B'` , `'Z.Y.X'`| * * ### Double star (`**`) * * A double star (`'**'`) is a wildcard that matches *zero or more segments* * * | Glob |Matches states named |Does not match state named | * |:------------|:----------------------------------------------|:----------------------------------| * | `'**'` | `'A'` , `'A.B'`, `'Z.Y.X'` | (matches all states) | * | `'A.**'` | `'A'` , `'A.B'` , `'A.C.X'` | `'Z.Y.X'` | * | `'**.X'` | `'X'` , `'A.X'` , `'Z.Y.X'` | `'A'` , `'A.login.Z'` | * | `'A.**.X'` | `'A.X'` , `'A.B.X'` , `'A.B.C.X'` | `'A'` , `'A.B.C'` | * * @packageDocumentation */ var Glob = /** @class */ (function () { function Glob(text) { this.text = text; this.glob = text.split('.'); var regexpString = this.text .split('.') .map(function (seg) { if (seg === '**') return '(?:|(?:\\.[^.]*)*)'; if (seg === '*') return '\\.[^.]*'; return '\\.' + seg; }) .join(''); this.regexp = new RegExp('^' + regexpString + '$'); } /** Returns true if the string has glob-like characters in it */ Glob.is = function (text) { return !!/[!,*]+/.exec(text); }; /** Returns a glob from the string, or null if the string isn't Glob-like */ Glob.fromString = function (text) { return Glob.is(text) ? new Glob(text) : null; }; Glob.prototype.matches = function (name) { return this.regexp.test('.' + name); }; return Glob; }()); var Queue = /** @class */ (function () { function Queue(_items, _limit) { if (_items === void 0) { _items = []; } if (_limit === void 0) { _limit = null; } this._items = _items; this._limit = _limit; this._evictListeners = []; this.onEvict = pushTo(this._evictListeners); } Queue.prototype.enqueue = function (item) { var items = this._items; items.push(item); if (this._limit && items.length > this._limit) this.evict(); return item; }; Queue.prototype.evict = function () { var item = this._items.shift(); this._evictListeners.forEach(function (fn) { return fn(item); }); return item; }; Queue.prototype.dequeue = function () { if (this.size()) return this._items.splice(0, 1)[0]; }; Queue.prototype.clear = function () { var current = this._items; this._items = []; return current; }; Queue.prototype.size = function () { return this._items.length; }; Queue.prototype.remove = function (item) { var idx = this._items.indexOf(item); return idx > -1 && this._items.splice(idx, 1)[0]; }; Queue.prototype.peekTail = function () { return this._items[this._items.length - 1]; }; Queue.prototype.peekHead = function () { if (this.size()) return this._items[0]; }; return Queue; }()); /** An enum for Transition Rejection reasons */ (function (RejectType) { /** * A new transition superseded this one. * * While this transition was running, a new transition started. * This transition is cancelled because it was superseded by new transition. */ RejectType[RejectType["SUPERSEDED"] = 2] = "SUPERSEDED"; /** * The transition was aborted * * The transition was aborted by a hook which returned `false` */ RejectType[RejectType["ABORTED"] = 3] = "ABORTED"; /** * The transition was invalid * * The transition was never started because it was invalid */ RejectType[RejectType["INVALID"] = 4] = "INVALID"; /** * The transition was ignored * * The transition was ignored because it would have no effect. * * Either: * * - The transition is targeting the current state and parameter values * - The transition is targeting the same state and parameter values as the currently running transition. */ RejectType[RejectType["IGNORED"] = 5] = "IGNORED"; /** * The transition errored. * * This generally means a hook threw an error or returned a rejected promise */ RejectType[RejectType["ERROR"] = 6] = "ERROR"; })(exports.RejectType || (exports.RejectType = {})); /** @internal */ var id = 0; var Rejection = /** @class */ (function () { function Rejection(type, message, detail) { /** @internal */ this.$id = id++; this.type = type; this.message = message; this.detail = detail; } /** Returns true if the obj is a rejected promise created from the `asPromise` factory */ Rejection.isRejectionPromise = function (obj) { return obj && typeof obj.then === 'function' && is(Rejection)(obj._transitionRejection); }; /** Returns a Rejection due to transition superseded */ Rejection.superseded = function (detail, options) { var message = 'The transition has been superseded by a different transition'; var rejection = new Rejection(exports.RejectType.SUPERSEDED, message, detail); if (options && options.redirected) { rejection.redirected = true; } return rejection; }; /** Returns a Rejection due to redirected transition */ Rejection.redirected = function (detail) { return Rejection.superseded(detail, { redirected: true }); }; /** Returns a Rejection due to invalid transition */ Rejection.invalid = function (detail) { var message = 'This transition is invalid'; return new Rejection(exports.RejectType.INVALID, message, detail); }; /** Returns a Rejection due to ignored transition */ Rejection.ignored = function (detail) { var message = 'The transition was ignored'; return new Rejection(exports.RejectType.IGNORED, message, detail); }; /** Returns a Rejection due to aborted transition */ Rejection.aborted = function (detail) { var message = 'The transition has been aborted'; return new Rejection(exports.RejectType.ABORTED, message, detail); }; /** Returns a Rejection due to aborted transition */ Rejection.errored = function (detail) { var message = 'The transition errored'; return new Rejection(exports.RejectType.ERROR, message, detail); }; /** * Returns a Rejection * * Normalizes a value as a Rejection. * If the value is already a Rejection, returns it. * Otherwise, wraps and returns the value as a Rejection (Rejection type: ERROR). * * @returns `detail` if it is already a `Rejection`, else returns an ERROR Rejection. */ Rejection.normalize = function (detail) { return is(Rejection)(detail) ? detail : Rejection.errored(detail); }; Rejection.prototype.toString = function () { var detailString = function (d) { return (d && d.toString !== Object.prototype.toString ? d.toString() : stringify(d)); }; var detail = detailString(this.detail); var _a = this, $id = _a.$id, type = _a.type, message = _a.message; return "Transition Rejection($id: " + $id + " type: " + type + ", message: " + message + ", detail: " + detail + ")"; }; Rejection.prototype.toPromise = function () { return extend(silentRejection(this), { _transitionRejection: this }); }; return Rejection; }()); /** * Functions that manipulate strings * * Although these functions are exported, they are subject to change without notice. * * @packageDocumentation */ /** * Returns a string shortened to a maximum length * * If the string is already less than the `max` length, return the string. * Else return the string, shortened to `max - 3` and append three dots ("..."). * * @param max the maximum length of the string to return * @param str the input string */ function maxLength(max, str) { if (str.length <= max) return str; return str.substr(0, max - 3) + '...'; } /** * Returns a string, with spaces added to the end, up to a desired str length * * If the string is already longer than the desired length, return the string. * Else returns the string, with extra spaces on the end, such that it reaches `length` characters. * * @param length the desired length of the string to return * @param str the input string */ function padString(length, str) { while (str.length < length) str += ' '; return str; } function kebobString(camelCase) { return camelCase .replace(/^([A-Z])/, function ($1) { return $1.toLowerCase(); }) // replace first char .replace(/([A-Z])/g, function ($1) { return '-' + $1.toLowerCase(); }); // replace rest } function functionToString(fn) { var fnStr = fnToString(fn); var namedFunctionMatch = fnStr.match(/^(function [^ ]+\([^)]*\))/); var toStr = namedFunctionMatch ? namedFunctionMatch[1] : fnStr; var fnName = fn['name'] || ''; if (fnName && toStr.match(/function \(/)) { return 'function ' + fnName + toStr.substr(9); } return toStr; } function fnToString(fn) { var _fn = isArray(fn) ? fn.slice(-1)[0] : fn; return (_fn && _fn.toString()) || 'undefined'; } function stringify(o) { var seen = []; var isRejection = Rejection.isRejectionPromise; var hasToString = function (obj) { return isObject(obj) && !isArray(obj) && obj.constructor !== Object && isFunction(obj.toString); }; var stringifyPattern = pattern([ [isUndefined, val('undefined')], [isNull, val('null')], [isPromise, val('[Promise]')], [isRejection, function (x) { return x._transitionRejection.toString(); }], [hasToString, function (x) { return x.toString(); }], [isInjectable, functionToString], [val(true), identity], ]); function format(value) { if (isObject(value)) { if (seen.indexOf(value) !== -1) return '[circular ref]'; seen.push(value); } return stringifyPattern(value); } if (isUndefined(o)) { // Workaround for IE & Edge Spec incompatibility where replacer function would not be called when JSON.stringify // is given `undefined` as value. To work around that, we simply detect `undefined` and bail out early by // manually stringifying it. return format(o); } return JSON.stringify(o, function (key, value) { return format(value); }).replace(/\\"/g, '"'); } /** Returns a function that splits a string on a character or substring */ var beforeAfterSubstr = function (char) { return function (str) { if (!str) return ['', '']; var idx = str.indexOf(char); if (idx === -1) return [str, '']; return [str.substr(0, idx), str.substr(idx + 1)]; }; }; var hostRegex = new RegExp('^(?:[a-z]+:)?//[^/]+/'); var stripLastPathElement = function (str) { return str.replace(/\/[^/]*$/, ''); }; var splitHash = beforeAfterSubstr('#'); var splitQuery = beforeAfterSubstr('?'); var splitEqual = beforeAfterSubstr('='); var trimHashVal = function (str) { return (str ? str.replace(/^#/, '') : ''); }; /** * Splits on a delimiter, but returns the delimiters in the array * * #### Example: * ```js * var splitOnSlashes = splitOnDelim('/'); * splitOnSlashes("/foo"); // ["/", "foo"] * splitOnSlashes("/foo/"); // ["/", "foo", "/"] * ``` */ function splitOnDelim(delim) { var re = new RegExp('(' + delim + ')', 'g'); return function (str) { return str.split(re).filter(identity); }; } /** * Reduce fn that joins neighboring strings * * Given an array of strings, returns a new array * where all neighboring strings have been joined. * * #### Example: * ```js * let arr = ["foo", "bar", 1, "baz", "", "qux" ]; * arr.reduce(joinNeighborsR, []) // ["foobar", 1, "bazqux" ] * ``` */ function joinNeighborsR(acc, x) { if (isString(tail(acc)) && isString(x)) return acc.slice(0, -1).concat(tail(acc) + x); return pushR(acc, x); } /** * workaround for missing console object in IE9 when dev tools haven't been opened o_O * @packageDocumentation */ var noopConsoleStub = { log: noop, error: noop, table: noop }; function ie9Console(console) { var bound = function (fn) { return Function.prototype.bind.call(fn, console); }; return { log: bound(console.log), error: bound(console.log), table: bound(console.log), }; } function fallbackConsole(console) { var log = console.log.bind(console); var error = console.error ? console.error.bind(console) : log; var table = console.table ? console.table.bind(console) : log; return { log: log, error: error, table: table }; } function getSafeConsole() { // @ts-ignore var isIE9 = typeof document !== 'undefined' && document.documentMode && document.documentMode === 9; if (isIE9) { return window && window.console ? ie9Console(window.console) : noopConsoleStub; } else if (!console.table || !console.error) { return fallbackConsole(console); } else { return console; } } var safeConsole = getSafeConsole(); /** * # Transition tracing (debug) * * Enable transition tracing to print transition information to the console, * in order to help debug your application. * Tracing logs detailed information about each Transition to your console. * * To enable tracing, import the [[Trace]] singleton and enable one or more categories. * * ### ES6 * ```js * import {trace} from "@uirouter/core"; * trace.enable(1, 5); // TRANSITION and VIEWCONFIG * ``` * * ### CJS * ```js * let trace = require("@uirouter/core").trace; * trace.enable("TRANSITION", "VIEWCONFIG"); * ``` * * ### Globals * ```js * let trace = window["@uirouter/core"].trace; * trace.enable(); // Trace everything (very verbose) * ``` * * ### Angular 1: * ```js * app.run($trace => $trace.enable()); * ``` * * @packageDocumentation */ function uiViewString(uiview) { if (!uiview) return 'ui-view (defunct)'; var state = uiview.creationContext ? uiview.creationContext.name || '(root)' : '(none)'; return "[ui-view#" + uiview.id + " " + uiview.$type + ":" + uiview.fqn + " (" + uiview.name + "@" + state + ")]"; } var viewConfigString = function (viewConfig) { var view = viewConfig.viewDecl; var state = view.$context.name || '(root)'; return "[View#" + viewConfig.$id + " from '" + state + "' state]: target ui-view: '" + view.$uiViewName + "@" + view.$uiViewContextAnchor + "'"; }; function normalizedCat(input) { return isNumber(input) ? exports.Category[input] : exports.Category[exports.Category[input]]; } /** * Trace categories Enum * * Enable or disable a category using [[Trace.enable]] or [[Trace.disable]] * * `trace.enable(Category.TRANSITION)` * * These can also be provided using a matching string, or position ordinal * * `trace.enable("TRANSITION")` * * `trace.enable(1)` */ (function (Category) { Category[Category["RESOLVE"] = 0] = "RESOLVE"; Category[Category["TRANSITION"] = 1] = "TRA