UNPKG

488 kBJavaScriptView Raw
1/**
2 * State-based routing for AngularJS 1.x
3 * NOTICE: This monolithic bundle also bundles the @uirouter/core code.
4 * This causes it to be incompatible with plugins that depend on @uirouter/core.
5 * We recommend switching to the ui-router-core.js and ui-router-angularjs.js bundles instead.
6 * For more information, see https://ui-router.github.io/blog/uirouter-for-angularjs-umd-bundles
7 * @version v1.0.30
8 * @link https://ui-router.github.io
9 * @license MIT License, http://www.opensource.org/licenses/MIT
10 */
11(function (global, factory) {
12 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('angular')) :
13 typeof define === 'function' && define.amd ? define(['exports', 'angular'], factory) :
14 (global = global || self, factory(global['@uirouter/angularjs'] = {}, global.angular));
15}(this, (function (exports, ng_from_import) { 'use strict';
16
17 /** @publicapi @module ng1 */ /** */
18 /** @hidden */ var ng_from_global = angular;
19 /** @hidden */ var ng = ng_from_import && ng_from_import.module ? ng_from_import : ng_from_global;
20
21 /**
22 * Higher order functions
23 *
24 * These utility functions are exported, but are subject to change without notice.
25 *
26 * @packageDocumentation
27 */
28 var __spreadArrays = (undefined && undefined.__spreadArrays) || function () {
29 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
30 for (var r = Array(s), k = 0, i = 0; i < il; i++)
31 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
32 r[k] = a[j];
33 return r;
34 };
35 /**
36 * Returns a new function for [Partial Application](https://en.wikipedia.org/wiki/Partial_application) of the original function.
37 *
38 * Given a function with N parameters, returns a new function that supports partial application.
39 * The new function accepts anywhere from 1 to N parameters. When that function is called with M parameters,
40 * where M is less than N, it returns a new function that accepts the remaining parameters. It continues to
41 * accept more parameters until all N parameters have been supplied.
42 *
43 *
44 * This contrived example uses a partially applied function as an predicate, which returns true
45 * if an object is found in both arrays.
46 * @example
47 * ```
48 * // returns true if an object is in both of the two arrays
49 * function inBoth(array1, array2, object) {
50 * return array1.indexOf(object) !== -1 &&
51 * array2.indexOf(object) !== 1;
52 * }
53 * let obj1, obj2, obj3, obj4, obj5, obj6, obj7
54 * let foos = [obj1, obj3]
55 * let bars = [obj3, obj4, obj5]
56 *
57 * // A curried "copy" of inBoth
58 * let curriedInBoth = curry(inBoth);
59 * // Partially apply both the array1 and array2
60 * let inFoosAndBars = curriedInBoth(foos, bars);
61 *
62 * // Supply the final argument; since all arguments are
63 * // supplied, the original inBoth function is then called.
64 * let obj1InBoth = inFoosAndBars(obj1); // false
65 *
66 * // Use the inFoosAndBars as a predicate.
67 * // Filter, on each iteration, supplies the final argument
68 * let allObjs = [ obj1, obj2, obj3, obj4, obj5, obj6, obj7 ];
69 * let foundInBoth = allObjs.filter(inFoosAndBars); // [ obj3 ]
70 *
71 * ```
72 *
73 * @param fn
74 * @returns {*|function(): (*|any)}
75 */
76 function curry(fn) {
77 return function curried() {
78 if (arguments.length >= fn.length) {
79 return fn.apply(this, arguments);
80 }
81 var args = Array.prototype.slice.call(arguments);
82 return curried.bind.apply(curried, __spreadArrays([this], args));
83 };
84 }
85 /**
86 * Given a varargs list of functions, returns a function that composes the argument functions, right-to-left
87 * given: f(x), g(x), h(x)
88 * let composed = compose(f,g,h)
89 * then, composed is: f(g(h(x)))
90 */
91 function compose() {
92 var args = arguments;
93 var start = args.length - 1;
94 return function () {
95 var i = start, result = args[start].apply(this, arguments);
96 while (i--)
97 result = args[i].call(this, result);
98 return result;
99 };
100 }
101 /**
102 * Given a varargs list of functions, returns a function that is composes the argument functions, left-to-right
103 * given: f(x), g(x), h(x)
104 * let piped = pipe(f,g,h);
105 * then, piped is: h(g(f(x)))
106 */
107 function pipe() {
108 var funcs = [];
109 for (var _i = 0; _i < arguments.length; _i++) {
110 funcs[_i] = arguments[_i];
111 }
112 return compose.apply(null, [].slice.call(arguments).reverse());
113 }
114 /**
115 * Given a property name, returns a function that returns that property from an object
116 * let obj = { foo: 1, name: "blarg" };
117 * let getName = prop("name");
118 * getName(obj) === "blarg"
119 */
120 var prop = function (name) { return function (obj) { return obj && obj[name]; }; };
121 /**
122 * Given a property name and a value, returns a function that returns a boolean based on whether
123 * the passed object has a property that matches the value
124 * let obj = { foo: 1, name: "blarg" };
125 * let getName = propEq("name", "blarg");
126 * getName(obj) === true
127 */
128 var propEq = curry(function (name, _val, obj) { return obj && obj[name] === _val; });
129 /**
130 * Given a dotted property name, returns a function that returns a nested property from an object, or undefined
131 * let obj = { id: 1, nestedObj: { foo: 1, name: "blarg" }, };
132 * let getName = prop("nestedObj.name");
133 * getName(obj) === "blarg"
134 * let propNotFound = prop("this.property.doesnt.exist");
135 * propNotFound(obj) === undefined
136 */
137 var parse = function (name) { return pipe.apply(null, name.split('.').map(prop)); };
138 /**
139 * Given a function that returns a truthy or falsey value, returns a
140 * function that returns the opposite (falsey or truthy) value given the same inputs
141 */
142 var not = function (fn) { return function () {
143 var args = [];
144 for (var _i = 0; _i < arguments.length; _i++) {
145 args[_i] = arguments[_i];
146 }
147 return !fn.apply(null, args);
148 }; };
149 /**
150 * Given two functions that return truthy or falsey values, returns a function that returns truthy
151 * if both functions return truthy for the given arguments
152 */
153 function and(fn1, fn2) {
154 return function () {
155 var args = [];
156 for (var _i = 0; _i < arguments.length; _i++) {
157 args[_i] = arguments[_i];
158 }
159 return fn1.apply(null, args) && fn2.apply(null, args);
160 };
161 }
162 /**
163 * Given two functions that return truthy or falsey values, returns a function that returns truthy
164 * if at least one of the functions returns truthy for the given arguments
165 */
166 function or(fn1, fn2) {
167 return function () {
168 var args = [];
169 for (var _i = 0; _i < arguments.length; _i++) {
170 args[_i] = arguments[_i];
171 }
172 return fn1.apply(null, args) || fn2.apply(null, args);
173 };
174 }
175 /**
176 * Check if all the elements of an array match a predicate function
177 *
178 * @param fn1 a predicate function `fn1`
179 * @returns a function which takes an array and returns true if `fn1` is true for all elements of the array
180 */
181 var all = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b && !!fn1(x); }, true); }; };
182 // tslint:disable-next-line:variable-name
183 var any = function (fn1) { return function (arr) { return arr.reduce(function (b, x) { return b || !!fn1(x); }, false); }; };
184 /** Given a class, returns a Predicate function that returns true if the object is of that class */
185 var is = function (ctor) { return function (obj) {
186 return (obj != null && obj.constructor === ctor) || obj instanceof ctor;
187 }; };
188 /** Given a value, returns a Predicate function that returns true if another value is === equal to the original value */
189 var eq = function (value) { return function (other) { return value === other; }; };
190 /** Given a value, returns a function which returns the value */
191 var val = function (v) { return function () { return v; }; };
192 function invoke(fnName, args) {
193 return function (obj) { return obj[fnName].apply(obj, args); };
194 }
195 /**
196 * Sorta like Pattern Matching (a functional programming conditional construct)
197 *
198 * See http://c2.com/cgi/wiki?PatternMatching
199 *
200 * This is a conditional construct which allows a series of predicates and output functions
201 * to be checked and then applied. Each predicate receives the input. If the predicate
202 * returns truthy, then its matching output function (mapping function) is provided with
203 * the input and, then the result is returned.
204 *
205 * Each combination (2-tuple) of predicate + output function should be placed in an array
206 * of size 2: [ predicate, mapFn ]
207 *
208 * These 2-tuples should be put in an outer array.
209 *
210 * @example
211 * ```
212 *
213 * // Here's a 2-tuple where the first element is the isString predicate
214 * // and the second element is a function that returns a description of the input
215 * let firstTuple = [ angular.isString, (input) => `Heres your string ${input}` ];
216 *
217 * // Second tuple: predicate "isNumber", mapfn returns a description
218 * let secondTuple = [ angular.isNumber, (input) => `(${input}) That's a number!` ];
219 *
220 * let third = [ (input) => input === null, (input) => `Oh, null...` ];
221 *
222 * let fourth = [ (input) => input === undefined, (input) => `notdefined` ];
223 *
224 * let descriptionOf = pattern([ firstTuple, secondTuple, third, fourth ]);
225 *
226 * console.log(descriptionOf(undefined)); // 'notdefined'
227 * console.log(descriptionOf(55)); // '(55) That's a number!'
228 * console.log(descriptionOf("foo")); // 'Here's your string foo'
229 * ```
230 *
231 * @param struct A 2D array. Each element of the array should be an array, a 2-tuple,
232 * with a Predicate and a mapping/output function
233 * @returns {function(any): *}
234 */
235 function pattern(struct) {
236 return function (x) {
237 for (var i = 0; i < struct.length; i++) {
238 if (struct[i][0](x))
239 return struct[i][1](x);
240 }
241 };
242 }
243
244 /**
245 * Predicates
246 *
247 * These predicates return true/false based on the input.
248 * Although these functions are exported, they are subject to change without notice.
249 *
250 * @packageDocumentation
251 */
252 var toStr = Object.prototype.toString;
253 var tis = function (t) { return function (x) { return typeof x === t; }; };
254 var isUndefined = tis('undefined');
255 var isDefined = not(isUndefined);
256 var isNull = function (o) { return o === null; };
257 var isNullOrUndefined = or(isNull, isUndefined);
258 var isFunction = tis('function');
259 var isNumber = tis('number');
260 var isString = tis('string');
261 var isObject = function (x) { return x !== null && typeof x === 'object'; };
262 var isArray = Array.isArray;
263 var isDate = (function (x) { return toStr.call(x) === '[object Date]'; });
264 var isRegExp = (function (x) { return toStr.call(x) === '[object RegExp]'; });
265 /**
266 * Predicate which checks if a value is injectable
267 *
268 * A value is "injectable" if it is a function, or if it is an ng1 array-notation-style array
269 * where all the elements in the array are Strings, except the last one, which is a Function
270 */
271 function isInjectable(val) {
272 if (isArray(val) && val.length) {
273 var head = val.slice(0, -1), tail = val.slice(-1);
274 return !(head.filter(not(isString)).length || tail.filter(not(isFunction)).length);
275 }
276 return isFunction(val);
277 }
278 /**
279 * Predicate which checks if a value looks like a Promise
280 *
281 * It is probably a Promise if it's an object, and it has a `then` property which is a Function
282 */
283 var isPromise = and(isObject, pipe(prop('then'), isFunction));
284
285 var noImpl = function (fnname) { return function () {
286 throw new Error("No implementation for " + fnname + ". The framework specific code did not implement this method.");
287 }; };
288 var makeStub = function (service, methods) {
289 return methods.reduce(function (acc, key) { return ((acc[key] = noImpl(service + "." + key + "()")), acc); }, {});
290 };
291 var services = {
292 $q: undefined,
293 $injector: undefined,
294 };
295
296 var __spreadArrays$1 = (undefined && undefined.__spreadArrays) || function () {
297 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
298 for (var r = Array(s), k = 0, i = 0; i < il; i++)
299 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
300 r[k] = a[j];
301 return r;
302 };
303 var root = (typeof self === 'object' && self.self === self && self) ||
304 (typeof global === 'object' && global.global === global && global) ||
305 undefined;
306 var angular$1 = root.angular || {};
307 var fromJson = angular$1.fromJson || JSON.parse.bind(JSON);
308 var toJson = angular$1.toJson || JSON.stringify.bind(JSON);
309 var forEach = angular$1.forEach || _forEach;
310 var extend = Object.assign || _extend;
311 var equals = angular$1.equals || _equals;
312 function identity(x) {
313 return x;
314 }
315 function noop() { }
316 /**
317 * Builds proxy functions on the `to` object which pass through to the `from` object.
318 *
319 * For each key in `fnNames`, creates a proxy function on the `to` object.
320 * The proxy function calls the real function on the `from` object.
321 *
322 *
323 * #### Example:
324 * This example creates an new class instance whose functions are prebound to the new'd object.
325 * ```js
326 * class Foo {
327 * constructor(data) {
328 * // Binds all functions from Foo.prototype to 'this',
329 * // then copies them to 'this'
330 * bindFunctions(Foo.prototype, this, this);
331 * this.data = data;
332 * }
333 *
334 * log() {
335 * console.log(this.data);
336 * }
337 * }
338 *
339 * let myFoo = new Foo([1,2,3]);
340 * var logit = myFoo.log;
341 * logit(); // logs [1, 2, 3] from the myFoo 'this' instance
342 * ```
343 *
344 * #### Example:
345 * This example creates a bound version of a service function, and copies it to another object
346 * ```
347 *
348 * var SomeService = {
349 * this.data = [3, 4, 5];
350 * this.log = function() {
351 * console.log(this.data);
352 * }
353 * }
354 *
355 * // Constructor fn
356 * function OtherThing() {
357 * // Binds all functions from SomeService to SomeService,
358 * // then copies them to 'this'
359 * bindFunctions(SomeService, this, SomeService);
360 * }
361 *
362 * let myOtherThing = new OtherThing();
363 * myOtherThing.log(); // logs [3, 4, 5] from SomeService's 'this'
364 * ```
365 *
366 * @param source A function that returns the source object which contains the original functions to be bound
367 * @param target A function that returns the target object which will receive the bound functions
368 * @param bind A function that returns the object which the functions will be bound to
369 * @param fnNames The function names which will be bound (Defaults to all the functions found on the 'from' object)
370 * @param latebind If true, the binding of the function is delayed until the first time it's invoked
371 */
372 function createProxyFunctions(source, target, bind, fnNames, latebind) {
373 if (latebind === void 0) { latebind = false; }
374 var bindFunction = function (fnName) { return source()[fnName].bind(bind()); };
375 var makeLateRebindFn = function (fnName) {
376 return function lateRebindFunction() {
377 target[fnName] = bindFunction(fnName);
378 return target[fnName].apply(null, arguments);
379 };
380 };
381 fnNames = fnNames || Object.keys(source());
382 return fnNames.reduce(function (acc, name) {
383 acc[name] = latebind ? makeLateRebindFn(name) : bindFunction(name);
384 return acc;
385 }, target);
386 }
387 /**
388 * prototypal inheritance helper.
389 * Creates a new object which has `parent` object as its prototype, and then copies the properties from `extra` onto it
390 */
391 var inherit = function (parent, extra) { return extend(Object.create(parent), extra); };
392 /** Given an array, returns true if the object is found in the array, (using indexOf) */
393 var inArray = curry(_inArray);
394 function _inArray(array, obj) {
395 return array.indexOf(obj) !== -1;
396 }
397 /**
398 * Given an array, and an item, if the item is found in the array, it removes it (in-place).
399 * The same array is returned
400 */
401 var removeFrom = curry(_removeFrom);
402 function _removeFrom(array, obj) {
403 var idx = array.indexOf(obj);
404 if (idx >= 0)
405 array.splice(idx, 1);
406 return array;
407 }
408 /** pushes a values to an array and returns the value */
409 var pushTo = curry(_pushTo);
410 function _pushTo(arr, val) {
411 return arr.push(val), val;
412 }
413 /** Given an array of (deregistration) functions, calls all functions and removes each one from the source array */
414 var deregAll = function (functions) {
415 return functions.slice().forEach(function (fn) {
416 typeof fn === 'function' && fn();
417 removeFrom(functions, fn);
418 });
419 };
420 /**
421 * Applies a set of defaults to an options object. The options object is filtered
422 * to only those properties of the objects in the defaultsList.
423 * Earlier objects in the defaultsList take precedence when applying defaults.
424 */
425 function defaults(opts) {
426 var defaultsList = [];
427 for (var _i = 1; _i < arguments.length; _i++) {
428 defaultsList[_i - 1] = arguments[_i];
429 }
430 var defaultVals = extend.apply(void 0, __spreadArrays$1([{}], defaultsList.reverse()));
431 return extend(defaultVals, pick(opts || {}, Object.keys(defaultVals)));
432 }
433 /** Reduce function that merges each element of the list into a single object, using extend */
434 var mergeR = function (memo, item) { return extend(memo, item); };
435 /**
436 * Finds the common ancestor path between two states.
437 *
438 * @param {Object} first The first state.
439 * @param {Object} second The second state.
440 * @return {Array} Returns an array of state names in descending order, not including the root.
441 */
442 function ancestors(first, second) {
443 var path = [];
444 // tslint:disable-next-line:forin
445 for (var n in first.path) {
446 if (first.path[n] !== second.path[n])
447 break;
448 path.push(first.path[n]);
449 }
450 return path;
451 }
452 /**
453 * Return a copy of the object only containing the whitelisted properties.
454 *
455 * #### Example:
456 * ```
457 * var foo = { a: 1, b: 2, c: 3 };
458 * var ab = pick(foo, ['a', 'b']); // { a: 1, b: 2 }
459 * ```
460 * @param obj the source object
461 * @param propNames an Array of strings, which are the whitelisted property names
462 */
463 function pick(obj, propNames) {
464 var objCopy = {};
465 for (var _prop in obj) {
466 if (propNames.indexOf(_prop) !== -1) {
467 objCopy[_prop] = obj[_prop];
468 }
469 }
470 return objCopy;
471 }
472 /**
473 * Return a copy of the object omitting the blacklisted properties.
474 *
475 * @example
476 * ```
477 *
478 * var foo = { a: 1, b: 2, c: 3 };
479 * var ab = omit(foo, ['a', 'b']); // { c: 3 }
480 * ```
481 * @param obj the source object
482 * @param propNames an Array of strings, which are the blacklisted property names
483 */
484 function omit(obj, propNames) {
485 return Object.keys(obj)
486 .filter(not(inArray(propNames)))
487 .reduce(function (acc, key) { return ((acc[key] = obj[key]), acc); }, {});
488 }
489 /**
490 * Maps an array, or object to a property (by name)
491 */
492 function pluck(collection, propName) {
493 return map(collection, prop(propName));
494 }
495 /** Filters an Array or an Object's properties based on a predicate */
496 function filter(collection, callback) {
497 var arr = isArray(collection), result = arr ? [] : {};
498 var accept = arr ? function (x) { return result.push(x); } : function (x, key) { return (result[key] = x); };
499 forEach(collection, function (item, i) {
500 if (callback(item, i))
501 accept(item, i);
502 });
503 return result;
504 }
505 /** Finds an object from an array, or a property of an object, that matches a predicate */
506 function find(collection, callback) {
507 var result;
508 forEach(collection, function (item, i) {
509 if (result)
510 return;
511 if (callback(item, i))
512 result = item;
513 });
514 return result;
515 }
516 /** Given an object, returns a new object, where each property is transformed by the callback function */
517 var mapObj = map;
518 /** Maps an array or object properties using a callback function */
519 function map(collection, callback, target) {
520 target = target || (isArray(collection) ? [] : {});
521 forEach(collection, function (item, i) { return (target[i] = callback(item, i)); });
522 return target;
523 }
524 /**
525 * Given an object, return its enumerable property values
526 *
527 * @example
528 * ```
529 *
530 * let foo = { a: 1, b: 2, c: 3 }
531 * let vals = values(foo); // [ 1, 2, 3 ]
532 * ```
533 */
534 var values = function (obj) { return Object.keys(obj).map(function (key) { return obj[key]; }); };
535 /**
536 * Reduce function that returns true if all of the values are truthy.
537 *
538 * @example
539 * ```
540 *
541 * let vals = [ 1, true, {}, "hello world"];
542 * vals.reduce(allTrueR, true); // true
543 *
544 * vals.push(0);
545 * vals.reduce(allTrueR, true); // false
546 * ```
547 */
548 var allTrueR = function (memo, elem) { return memo && elem; };
549 /**
550 * Reduce function that returns true if any of the values are truthy.
551 *
552 * * @example
553 * ```
554 *
555 * let vals = [ 0, null, undefined ];
556 * vals.reduce(anyTrueR, true); // false
557 *
558 * vals.push("hello world");
559 * vals.reduce(anyTrueR, true); // true
560 * ```
561 */
562 var anyTrueR = function (memo, elem) { return memo || elem; };
563 /**
564 * Reduce function which un-nests a single level of arrays
565 * @example
566 * ```
567 *
568 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ];
569 * input.reduce(unnestR, []) // [ "a", "b", "c", "d", [ "double, "nested" ] ]
570 * ```
571 */
572 var unnestR = function (memo, elem) { return memo.concat(elem); };
573 /**
574 * Reduce function which recursively un-nests all arrays
575 *
576 * @example
577 * ```
578 *
579 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ];
580 * input.reduce(unnestR, []) // [ "a", "b", "c", "d", "double, "nested" ]
581 * ```
582 */
583 var flattenR = function (memo, elem) {
584 return isArray(elem) ? memo.concat(elem.reduce(flattenR, [])) : pushR(memo, elem);
585 };
586 /**
587 * Reduce function that pushes an object to an array, then returns the array.
588 * Mostly just for [[flattenR]] and [[uniqR]]
589 */
590 function pushR(arr, obj) {
591 arr.push(obj);
592 return arr;
593 }
594 /** Reduce function that filters out duplicates */
595 var uniqR = function (acc, token) { return (inArray(acc, token) ? acc : pushR(acc, token)); };
596 /**
597 * Return a new array with a single level of arrays unnested.
598 *
599 * @example
600 * ```
601 *
602 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ];
603 * unnest(input) // [ "a", "b", "c", "d", [ "double, "nested" ] ]
604 * ```
605 */
606 var unnest = function (arr) { return arr.reduce(unnestR, []); };
607 /**
608 * Return a completely flattened version of an array.
609 *
610 * @example
611 * ```
612 *
613 * let input = [ [ "a", "b" ], [ "c", "d" ], [ [ "double", "nested" ] ] ];
614 * flatten(input) // [ "a", "b", "c", "d", "double, "nested" ]
615 * ```
616 */
617 var flatten = function (arr) { return arr.reduce(flattenR, []); };
618 /**
619 * Given a .filter Predicate, builds a .filter Predicate which throws an error if any elements do not pass.
620 * @example
621 * ```
622 *
623 * let isNumber = (obj) => typeof(obj) === 'number';
624 * let allNumbers = [ 1, 2, 3, 4, 5 ];
625 * allNumbers.filter(assertPredicate(isNumber)); //OK
626 *
627 * let oneString = [ 1, 2, 3, 4, "5" ];
628 * oneString.filter(assertPredicate(isNumber, "Not all numbers")); // throws Error(""Not all numbers"");
629 * ```
630 */
631 var assertPredicate = assertFn;
632 /**
633 * Given a .map function, builds a .map function which throws an error if any mapped elements do not pass a truthyness test.
634 * @example
635 * ```
636 *
637 * var data = { foo: 1, bar: 2 };
638 *
639 * let keys = [ 'foo', 'bar' ]
640 * let values = keys.map(assertMap(key => data[key], "Key not found"));
641 * // values is [1, 2]
642 *
643 * let keys = [ 'foo', 'bar', 'baz' ]
644 * let values = keys.map(assertMap(key => data[key], "Key not found"));
645 * // throws Error("Key not found")
646 * ```
647 */
648 var assertMap = assertFn;
649 function assertFn(predicateOrMap, errMsg) {
650 if (errMsg === void 0) { errMsg = 'assert failure'; }
651 return function (obj) {
652 var result = predicateOrMap(obj);
653 if (!result) {
654 throw new Error(isFunction(errMsg) ? errMsg(obj) : errMsg);
655 }
656 return result;
657 };
658 }
659 /**
660 * Like _.pairs: Given an object, returns an array of key/value pairs
661 *
662 * @example
663 * ```
664 *
665 * pairs({ foo: "FOO", bar: "BAR }) // [ [ "foo", "FOO" ], [ "bar": "BAR" ] ]
666 * ```
667 */
668 var pairs = function (obj) { return Object.keys(obj).map(function (key) { return [key, obj[key]]; }); };
669 /**
670 * Given two or more parallel arrays, returns an array of tuples where
671 * each tuple is composed of [ a[i], b[i], ... z[i] ]
672 *
673 * @example
674 * ```
675 *
676 * let foo = [ 0, 2, 4, 6 ];
677 * let bar = [ 1, 3, 5, 7 ];
678 * let baz = [ 10, 30, 50, 70 ];
679 * arrayTuples(foo, bar); // [ [0, 1], [2, 3], [4, 5], [6, 7] ]
680 * arrayTuples(foo, bar, baz); // [ [0, 1, 10], [2, 3, 30], [4, 5, 50], [6, 7, 70] ]
681 * ```
682 */
683 function arrayTuples() {
684 var args = [];
685 for (var _i = 0; _i < arguments.length; _i++) {
686 args[_i] = arguments[_i];
687 }
688 if (args.length === 0)
689 return [];
690 var maxArrayLen = args.reduce(function (min, arr) { return Math.min(arr.length, min); }, 9007199254740991); // aka 2^53 − 1 aka Number.MAX_SAFE_INTEGER
691 var result = [];
692 var _loop_1 = function (i) {
693 // This is a hot function
694 // Unroll when there are 1-4 arguments
695 switch (args.length) {
696 case 1:
697 result.push([args[0][i]]);
698 break;
699 case 2:
700 result.push([args[0][i], args[1][i]]);
701 break;
702 case 3:
703 result.push([args[0][i], args[1][i], args[2][i]]);
704 break;
705 case 4:
706 result.push([args[0][i], args[1][i], args[2][i], args[3][i]]);
707 break;
708 default:
709 result.push(args.map(function (array) { return array[i]; }));
710 break;
711 }
712 };
713 for (var i = 0; i < maxArrayLen; i++) {
714 _loop_1(i);
715 }
716 return result;
717 }
718 /**
719 * Reduce function which builds an object from an array of [key, value] pairs.
720 *
721 * Each iteration sets the key/val pair on the memo object, then returns the memo for the next iteration.
722 *
723 * Each keyValueTuple should be an array with values [ key: string, value: any ]
724 *
725 * @example
726 * ```
727 *
728 * var pairs = [ ["fookey", "fooval"], ["barkey", "barval"] ]
729 *
730 * var pairsToObj = pairs.reduce((memo, pair) => applyPairs(memo, pair), {})
731 * // pairsToObj == { fookey: "fooval", barkey: "barval" }
732 *
733 * // Or, more simply:
734 * var pairsToObj = pairs.reduce(applyPairs, {})
735 * // pairsToObj == { fookey: "fooval", barkey: "barval" }
736 * ```
737 */
738 function applyPairs(memo, keyValTuple) {
739 var key, value;
740 if (isArray(keyValTuple))
741 key = keyValTuple[0], value = keyValTuple[1];
742 if (!isString(key))
743 throw new Error('invalid parameters to applyPairs');
744 memo[key] = value;
745 return memo;
746 }
747 /** Get the last element of an array */
748 function tail(arr) {
749 return (arr.length && arr[arr.length - 1]) || undefined;
750 }
751 /**
752 * shallow copy from src to dest
753 */
754 function copy(src, dest) {
755 if (dest)
756 Object.keys(dest).forEach(function (key) { return delete dest[key]; });
757 if (!dest)
758 dest = {};
759 return extend(dest, src);
760 }
761 /** Naive forEach implementation works with Objects or Arrays */
762 function _forEach(obj, cb, _this) {
763 if (isArray(obj))
764 return obj.forEach(cb, _this);
765 Object.keys(obj).forEach(function (key) { return cb(obj[key], key); });
766 }
767 function _extend(toObj) {
768 for (var i = 1; i < arguments.length; i++) {
769 var obj = arguments[i];
770 if (!obj)
771 continue;
772 var keys = Object.keys(obj);
773 for (var j = 0; j < keys.length; j++) {
774 toObj[keys[j]] = obj[keys[j]];
775 }
776 }
777 return toObj;
778 }
779 function _equals(o1, o2) {
780 if (o1 === o2)
781 return true;
782 if (o1 === null || o2 === null)
783 return false;
784 if (o1 !== o1 && o2 !== o2)
785 return true; // NaN === NaN
786 var t1 = typeof o1, t2 = typeof o2;
787 if (t1 !== t2 || t1 !== 'object')
788 return false;
789 var tup = [o1, o2];
790 if (all(isArray)(tup))
791 return _arraysEq(o1, o2);
792 if (all(isDate)(tup))
793 return o1.getTime() === o2.getTime();
794 if (all(isRegExp)(tup))
795 return o1.toString() === o2.toString();
796 if (all(isFunction)(tup))
797 return true; // meh
798 var predicates = [isFunction, isArray, isDate, isRegExp];
799 if (predicates.map(any).reduce(function (b, fn) { return b || !!fn(tup); }, false))
800 return false;
801 var keys = {};
802 // tslint:disable-next-line:forin
803 for (var key in o1) {
804 if (!_equals(o1[key], o2[key]))
805 return false;
806 keys[key] = true;
807 }
808 for (var key in o2) {
809 if (!keys[key])
810 return false;
811 }
812 return true;
813 }
814 function _arraysEq(a1, a2) {
815 if (a1.length !== a2.length)
816 return false;
817 return arrayTuples(a1, a2).reduce(function (b, t) { return b && _equals(t[0], t[1]); }, true);
818 }
819 // issue #2676
820 var silenceUncaughtInPromise = function (promise) { return promise.catch(function (e) { return 0; }) && promise; };
821 var silentRejection = function (error) { return silenceUncaughtInPromise(services.$q.reject(error)); };
822
823 /**
824 * Matches state names using glob-like pattern strings.
825 *
826 * Globs can be used in specific APIs including:
827 *
828 * - [[StateService.is]]
829 * - [[StateService.includes]]
830 * - The first argument to Hook Registration functions like [[TransitionService.onStart]]
831 * - [[HookMatchCriteria]] and [[HookMatchCriterion]]
832 *
833 * A `Glob` string is a pattern which matches state names.
834 * Nested state names are split into segments (separated by a dot) when processing.
835 * The state named `foo.bar.baz` is split into three segments ['foo', 'bar', 'baz']
836 *
837 * Globs work according to the following rules:
838 *
839 * ### Exact match:
840 *
841 * The glob `'A.B'` matches the state named exactly `'A.B'`.
842 *
843 * | Glob |Matches states named|Does not match state named|
844 * |:------------|:--------------------|:---------------------|
845 * | `'A'` | `'A'` | `'B'` , `'A.C'` |
846 * | `'A.B'` | `'A.B'` | `'A'` , `'A.B.C'` |
847 * | `'foo'` | `'foo'` | `'FOO'` , `'foo.bar'`|
848 *
849 * ### Single star (`*`)
850 *
851 * A single star (`*`) is a wildcard that matches exactly one segment.
852 *
853 * | Glob |Matches states named |Does not match state named |
854 * |:------------|:---------------------|:--------------------------|
855 * | `'*'` | `'A'` , `'Z'` | `'A.B'` , `'Z.Y.X'` |
856 * | `'A.*'` | `'A.B'` , `'A.C'` | `'A'` , `'A.B.C'` |
857 * | `'A.*.*'` | `'A.B.C'` , `'A.X.Y'`| `'A'`, `'A.B'` , `'Z.Y.X'`|
858 *
859 * ### Double star (`**`)
860 *
861 * A double star (`'**'`) is a wildcard that matches *zero or more segments*
862 *
863 * | Glob |Matches states named |Does not match state named |
864 * |:------------|:----------------------------------------------|:----------------------------------|
865 * | `'**'` | `'A'` , `'A.B'`, `'Z.Y.X'` | (matches all states) |
866 * | `'A.**'` | `'A'` , `'A.B'` , `'A.C.X'` | `'Z.Y.X'` |
867 * | `'**.X'` | `'X'` , `'A.X'` , `'Z.Y.X'` | `'A'` , `'A.login.Z'` |
868 * | `'A.**.X'` | `'A.X'` , `'A.B.X'` , `'A.B.C.X'` | `'A'` , `'A.B.C'` |
869 *
870 * @packageDocumentation
871 */
872 var Glob = /** @class */ (function () {
873 function Glob(text) {
874 this.text = text;
875 this.glob = text.split('.');
876 var regexpString = this.text
877 .split('.')
878 .map(function (seg) {
879 if (seg === '**')
880 return '(?:|(?:\\.[^.]*)*)';
881 if (seg === '*')
882 return '\\.[^.]*';
883 return '\\.' + seg;
884 })
885 .join('');
886 this.regexp = new RegExp('^' + regexpString + '$');
887 }
888 /** Returns true if the string has glob-like characters in it */
889 Glob.is = function (text) {
890 return !!/[!,*]+/.exec(text);
891 };
892 /** Returns a glob from the string, or null if the string isn't Glob-like */
893 Glob.fromString = function (text) {
894 return Glob.is(text) ? new Glob(text) : null;
895 };
896 Glob.prototype.matches = function (name) {
897 return this.regexp.test('.' + name);
898 };
899 return Glob;
900 }());
901
902 var Queue = /** @class */ (function () {
903 function Queue(_items, _limit) {
904 if (_items === void 0) { _items = []; }
905 if (_limit === void 0) { _limit = null; }
906 this._items = _items;
907 this._limit = _limit;
908 this._evictListeners = [];
909 this.onEvict = pushTo(this._evictListeners);
910 }
911 Queue.prototype.enqueue = function (item) {
912 var items = this._items;
913 items.push(item);
914 if (this._limit && items.length > this._limit)
915 this.evict();
916 return item;
917 };
918 Queue.prototype.evict = function () {
919 var item = this._items.shift();
920 this._evictListeners.forEach(function (fn) { return fn(item); });
921 return item;
922 };
923 Queue.prototype.dequeue = function () {
924 if (this.size())
925 return this._items.splice(0, 1)[0];
926 };
927 Queue.prototype.clear = function () {
928 var current = this._items;
929 this._items = [];
930 return current;
931 };
932 Queue.prototype.size = function () {
933 return this._items.length;
934 };
935 Queue.prototype.remove = function (item) {
936 var idx = this._items.indexOf(item);
937 return idx > -1 && this._items.splice(idx, 1)[0];
938 };
939 Queue.prototype.peekTail = function () {
940 return this._items[this._items.length - 1];
941 };
942 Queue.prototype.peekHead = function () {
943 if (this.size())
944 return this._items[0];
945 };
946 return Queue;
947 }());
948
949 /** An enum for Transition Rejection reasons */
950
951 (function (RejectType) {
952 /**
953 * A new transition superseded this one.
954 *
955 * While this transition was running, a new transition started.
956 * This transition is cancelled because it was superseded by new transition.
957 */
958 RejectType[RejectType["SUPERSEDED"] = 2] = "SUPERSEDED";
959 /**
960 * The transition was aborted
961 *
962 * The transition was aborted by a hook which returned `false`
963 */
964 RejectType[RejectType["ABORTED"] = 3] = "ABORTED";
965 /**
966 * The transition was invalid
967 *
968 * The transition was never started because it was invalid
969 */
970 RejectType[RejectType["INVALID"] = 4] = "INVALID";
971 /**
972 * The transition was ignored
973 *
974 * The transition was ignored because it would have no effect.
975 *
976 * Either:
977 *
978 * - The transition is targeting the current state and parameter values
979 * - The transition is targeting the same state and parameter values as the currently running transition.
980 */
981 RejectType[RejectType["IGNORED"] = 5] = "IGNORED";
982 /**
983 * The transition errored.
984 *
985 * This generally means a hook threw an error or returned a rejected promise
986 */
987 RejectType[RejectType["ERROR"] = 6] = "ERROR";
988 })(exports.RejectType || (exports.RejectType = {}));
989 /** @internal */
990 var id = 0;
991 var Rejection = /** @class */ (function () {
992 function Rejection(type, message, detail) {
993 /** @internal */
994 this.$id = id++;
995 this.type = type;
996 this.message = message;
997 this.detail = detail;
998 }
999 /** Returns true if the obj is a rejected promise created from the `asPromise` factory */
1000 Rejection.isRejectionPromise = function (obj) {
1001 return obj && typeof obj.then === 'function' && is(Rejection)(obj._transitionRejection);
1002 };
1003 /** Returns a Rejection due to transition superseded */
1004 Rejection.superseded = function (detail, options) {
1005 var message = 'The transition has been superseded by a different transition';
1006 var rejection = new Rejection(exports.RejectType.SUPERSEDED, message, detail);
1007 if (options && options.redirected) {
1008 rejection.redirected = true;
1009 }
1010 return rejection;
1011 };
1012 /** Returns a Rejection due to redirected transition */
1013 Rejection.redirected = function (detail) {
1014 return Rejection.superseded(detail, { redirected: true });
1015 };
1016 /** Returns a Rejection due to invalid transition */
1017 Rejection.invalid = function (detail) {
1018 var message = 'This transition is invalid';
1019 return new Rejection(exports.RejectType.INVALID, message, detail);
1020 };
1021 /** Returns a Rejection due to ignored transition */
1022 Rejection.ignored = function (detail) {
1023 var message = 'The transition was ignored';
1024 return new Rejection(exports.RejectType.IGNORED, message, detail);
1025 };
1026 /** Returns a Rejection due to aborted transition */
1027 Rejection.aborted = function (detail) {
1028 var message = 'The transition has been aborted';
1029 return new Rejection(exports.RejectType.ABORTED, message, detail);
1030 };
1031 /** Returns a Rejection due to aborted transition */
1032 Rejection.errored = function (detail) {
1033 var message = 'The transition errored';
1034 return new Rejection(exports.RejectType.ERROR, message, detail);
1035 };
1036 /**
1037 * Returns a Rejection
1038 *
1039 * Normalizes a value as a Rejection.
1040 * If the value is already a Rejection, returns it.
1041 * Otherwise, wraps and returns the value as a Rejection (Rejection type: ERROR).
1042 *
1043 * @returns `detail` if it is already a `Rejection`, else returns an ERROR Rejection.
1044 */
1045 Rejection.normalize = function (detail) {
1046 return is(Rejection)(detail) ? detail : Rejection.errored(detail);
1047 };
1048 Rejection.prototype.toString = function () {
1049 var detailString = function (d) { return (d && d.toString !== Object.prototype.toString ? d.toString() : stringify(d)); };
1050 var detail = detailString(this.detail);
1051 var _a = this, $id = _a.$id, type = _a.type, message = _a.message;
1052 return "Transition Rejection($id: " + $id + " type: " + type + ", message: " + message + ", detail: " + detail + ")";
1053 };
1054 Rejection.prototype.toPromise = function () {
1055 return extend(silentRejection(this), { _transitionRejection: this });
1056 };
1057 return Rejection;
1058 }());
1059
1060 /**
1061 * Functions that manipulate strings
1062 *
1063 * Although these functions are exported, they are subject to change without notice.
1064 *
1065 * @packageDocumentation
1066 */
1067 /**
1068 * Returns a string shortened to a maximum length
1069 *
1070 * If the string is already less than the `max` length, return the string.
1071 * Else return the string, shortened to `max - 3` and append three dots ("...").
1072 *
1073 * @param max the maximum length of the string to return
1074 * @param str the input string
1075 */
1076 function maxLength(max, str) {
1077 if (str.length <= max)
1078 return str;
1079 return str.substr(0, max - 3) + '...';
1080 }
1081 /**
1082 * Returns a string, with spaces added to the end, up to a desired str length
1083 *
1084 * If the string is already longer than the desired length, return the string.
1085 * Else returns the string, with extra spaces on the end, such that it reaches `length` characters.
1086 *
1087 * @param length the desired length of the string to return
1088 * @param str the input string
1089 */
1090 function padString(length, str) {
1091 while (str.length < length)
1092 str += ' ';
1093 return str;
1094 }
1095 function kebobString(camelCase) {
1096 return camelCase
1097 .replace(/^([A-Z])/, function ($1) { return $1.toLowerCase(); }) // replace first char
1098 .replace(/([A-Z])/g, function ($1) { return '-' + $1.toLowerCase(); }); // replace rest
1099 }
1100 function functionToString(fn) {
1101 var fnStr = fnToString(fn);
1102 var namedFunctionMatch = fnStr.match(/^(function [^ ]+\([^)]*\))/);
1103 var toStr = namedFunctionMatch ? namedFunctionMatch[1] : fnStr;
1104 var fnName = fn['name'] || '';
1105 if (fnName && toStr.match(/function \(/)) {
1106 return 'function ' + fnName + toStr.substr(9);
1107 }
1108 return toStr;
1109 }
1110 function fnToString(fn) {
1111 var _fn = isArray(fn) ? fn.slice(-1)[0] : fn;
1112 return (_fn && _fn.toString()) || 'undefined';
1113 }
1114 function stringify(o) {
1115 var seen = [];
1116 var isRejection = Rejection.isRejectionPromise;
1117 var hasToString = function (obj) {
1118 return isObject(obj) && !isArray(obj) && obj.constructor !== Object && isFunction(obj.toString);
1119 };
1120 var stringifyPattern = pattern([
1121 [isUndefined, val('undefined')],
1122 [isNull, val('null')],
1123 [isPromise, val('[Promise]')],
1124 [isRejection, function (x) { return x._transitionRejection.toString(); }],
1125 [hasToString, function (x) { return x.toString(); }],
1126 [isInjectable, functionToString],
1127 [val(true), identity],
1128 ]);
1129 function format(value) {
1130 if (isObject(value)) {
1131 if (seen.indexOf(value) !== -1)
1132 return '[circular ref]';
1133 seen.push(value);
1134 }
1135 return stringifyPattern(value);
1136 }
1137 if (isUndefined(o)) {
1138 // Workaround for IE & Edge Spec incompatibility where replacer function would not be called when JSON.stringify
1139 // is given `undefined` as value. To work around that, we simply detect `undefined` and bail out early by
1140 // manually stringifying it.
1141 return format(o);
1142 }
1143 return JSON.stringify(o, function (key, value) { return format(value); }).replace(/\\"/g, '"');
1144 }
1145 /** Returns a function that splits a string on a character or substring */
1146 var beforeAfterSubstr = function (char) {
1147 return function (str) {
1148 if (!str)
1149 return ['', ''];
1150 var idx = str.indexOf(char);
1151 if (idx === -1)
1152 return [str, ''];
1153 return [str.substr(0, idx), str.substr(idx + 1)];
1154 };
1155 };
1156 var hostRegex = new RegExp('^(?:[a-z]+:)?//[^/]+/');
1157 var stripLastPathElement = function (str) { return str.replace(/\/[^/]*$/, ''); };
1158 var splitHash = beforeAfterSubstr('#');
1159 var splitQuery = beforeAfterSubstr('?');
1160 var splitEqual = beforeAfterSubstr('=');
1161 var trimHashVal = function (str) { return (str ? str.replace(/^#/, '') : ''); };
1162 /**
1163 * Splits on a delimiter, but returns the delimiters in the array
1164 *
1165 * #### Example:
1166 * ```js
1167 * var splitOnSlashes = splitOnDelim('/');
1168 * splitOnSlashes("/foo"); // ["/", "foo"]
1169 * splitOnSlashes("/foo/"); // ["/", "foo", "/"]
1170 * ```
1171 */
1172 function splitOnDelim(delim) {
1173 var re = new RegExp('(' + delim + ')', 'g');
1174 return function (str) { return str.split(re).filter(identity); };
1175 }
1176 /**
1177 * Reduce fn that joins neighboring strings
1178 *
1179 * Given an array of strings, returns a new array
1180 * where all neighboring strings have been joined.
1181 *
1182 * #### Example:
1183 * ```js
1184 * let arr = ["foo", "bar", 1, "baz", "", "qux" ];
1185 * arr.reduce(joinNeighborsR, []) // ["foobar", 1, "bazqux" ]
1186 * ```
1187 */
1188 function joinNeighborsR(acc, x) {
1189 if (isString(tail(acc)) && isString(x))
1190 return acc.slice(0, -1).concat(tail(acc) + x);
1191 return pushR(acc, x);
1192 }
1193
1194 /**
1195 * workaround for missing console object in IE9 when dev tools haven't been opened o_O
1196 * @packageDocumentation
1197 */
1198 var noopConsoleStub = { log: noop, error: noop, table: noop };
1199 function ie9Console(console) {
1200 var bound = function (fn) { return Function.prototype.bind.call(fn, console); };
1201 return {
1202 log: bound(console.log),
1203 error: bound(console.log),
1204 table: bound(console.log),
1205 };
1206 }
1207 function fallbackConsole(console) {
1208 var log = console.log.bind(console);
1209 var error = console.error ? console.error.bind(console) : log;
1210 var table = console.table ? console.table.bind(console) : log;
1211 return { log: log, error: error, table: table };
1212 }
1213 function getSafeConsole() {
1214 // @ts-ignore
1215 var isIE9 = typeof document !== 'undefined' && document.documentMode && document.documentMode === 9;
1216 if (isIE9) {
1217 return window && window.console ? ie9Console(window.console) : noopConsoleStub;
1218 }
1219 else if (!console.table || !console.error) {
1220 return fallbackConsole(console);
1221 }
1222 else {
1223 return console;
1224 }
1225 }
1226 var safeConsole = getSafeConsole();
1227
1228 /**
1229 * # Transition tracing (debug)
1230 *
1231 * Enable transition tracing to print transition information to the console,
1232 * in order to help debug your application.
1233 * Tracing logs detailed information about each Transition to your console.
1234 *
1235 * To enable tracing, import the [[Trace]] singleton and enable one or more categories.
1236 *
1237 * ### ES6
1238 * ```js
1239 * import {trace} from "@uirouter/core";
1240 * trace.enable(1, 5); // TRANSITION and VIEWCONFIG
1241 * ```
1242 *
1243 * ### CJS
1244 * ```js
1245 * let trace = require("@uirouter/core").trace;
1246 * trace.enable("TRANSITION", "VIEWCONFIG");
1247 * ```
1248 *
1249 * ### Globals
1250 * ```js
1251 * let trace = window["@uirouter/core"].trace;
1252 * trace.enable(); // Trace everything (very verbose)
1253 * ```
1254 *
1255 * ### Angular 1:
1256 * ```js
1257 * app.run($trace => $trace.enable());
1258 * ```
1259 *
1260 * @packageDocumentation
1261 */
1262 function uiViewString(uiview) {
1263 if (!uiview)
1264 return 'ui-view (defunct)';
1265 var state = uiview.creationContext ? uiview.creationContext.name || '(root)' : '(none)';
1266 return "[ui-view#" + uiview.id + " " + uiview.$type + ":" + uiview.fqn + " (" + uiview.name + "@" + state + ")]";
1267 }
1268 var viewConfigString = function (viewConfig) {
1269 var view = viewConfig.viewDecl;
1270 var state = view.$context.name || '(root)';
1271 return "[View#" + viewConfig.$id + " from '" + state + "' state]: target ui-view: '" + view.$uiViewName + "@" + view.$uiViewContextAnchor + "'";
1272 };
1273 function normalizedCat(input) {
1274 return isNumber(input) ? exports.Category[input] : exports.Category[exports.Category[input]];
1275 }
1276 /**
1277 * Trace categories Enum
1278 *
1279 * Enable or disable a category using [[Trace.enable]] or [[Trace.disable]]
1280 *
1281 * `trace.enable(Category.TRANSITION)`
1282 *
1283 * These can also be provided using a matching string, or position ordinal
1284 *
1285 * `trace.enable("TRANSITION")`
1286 *
1287 * `trace.enable(1)`
1288 */
1289
1290 (function (Category) {
1291 Category[Category["RESOLVE"] = 0] = "RESOLVE";
1292 Category[Category["TRANSITION"] = 1] = "TRANSITION";
1293 Category[Category["HOOK"] = 2] = "HOOK";
1294 Category[Category["UIVIEW"] = 3] = "UIVIEW";
1295 Category[Category["VIEWCONFIG"] = 4] = "VIEWCONFIG";
1296 })(exports.Category || (exports.Category = {}));
1297 var _tid = parse('$id');
1298 var _rid = parse('router.$id');
1299 var transLbl = function (trans) { return "Transition #" + _tid(trans) + "-" + _rid(trans); };
1300 /**
1301 * Prints UI-Router Transition trace information to the console.
1302 */
1303 var Trace = /** @class */ (function () {
1304 /** @internal */
1305 function Trace() {
1306 /** @internal */
1307 this._enabled = {};
1308 this.approximateDigests = 0;
1309 }
1310 /** @internal */
1311 Trace.prototype._set = function (enabled, categories) {
1312 var _this = this;
1313 if (!categories.length) {
1314 categories = Object.keys(exports.Category)
1315 .map(function (k) { return parseInt(k, 10); })
1316 .filter(function (k) { return !isNaN(k); })
1317 .map(function (key) { return exports.Category[key]; });
1318 }
1319 categories.map(normalizedCat).forEach(function (category) { return (_this._enabled[category] = enabled); });
1320 };
1321 Trace.prototype.enable = function () {
1322 var categories = [];
1323 for (var _i = 0; _i < arguments.length; _i++) {
1324 categories[_i] = arguments[_i];
1325 }
1326 this._set(true, categories);
1327 };
1328 Trace.prototype.disable = function () {
1329 var categories = [];
1330 for (var _i = 0; _i < arguments.length; _i++) {
1331 categories[_i] = arguments[_i];
1332 }
1333 this._set(false, categories);
1334 };
1335 /**
1336 * Retrieves the enabled stateus of a [[Category]]
1337 *
1338 * ```js
1339 * trace.enabled("VIEWCONFIG"); // true or false
1340 * ```
1341 *
1342 * @returns boolean true if the category is enabled
1343 */
1344 Trace.prototype.enabled = function (category) {
1345 return !!this._enabled[normalizedCat(category)];
1346 };
1347 /** @internal called by ui-router code */
1348 Trace.prototype.traceTransitionStart = function (trans) {
1349 if (!this.enabled(exports.Category.TRANSITION))
1350 return;
1351 safeConsole.log(transLbl(trans) + ": Started -> " + stringify(trans));
1352 };
1353 /** @internal called by ui-router code */
1354 Trace.prototype.traceTransitionIgnored = function (trans) {
1355 if (!this.enabled(exports.Category.TRANSITION))
1356 return;
1357 safeConsole.log(transLbl(trans) + ": Ignored <> " + stringify(trans));
1358 };
1359 /** @internal called by ui-router code */
1360 Trace.prototype.traceHookInvocation = function (step, trans, options) {
1361 if (!this.enabled(exports.Category.HOOK))
1362 return;
1363 var event = parse('traceData.hookType')(options) || 'internal', context = parse('traceData.context.state.name')(options) || parse('traceData.context')(options) || 'unknown', name = functionToString(step.registeredHook.callback);
1364 safeConsole.log(transLbl(trans) + ": Hook -> " + event + " context: " + context + ", " + maxLength(200, name));
1365 };
1366 /** @internal called by ui-router code */
1367 Trace.prototype.traceHookResult = function (hookResult, trans, transitionOptions) {
1368 if (!this.enabled(exports.Category.HOOK))
1369 return;
1370 safeConsole.log(transLbl(trans) + ": <- Hook returned: " + maxLength(200, stringify(hookResult)));
1371 };
1372 /** @internal called by ui-router code */
1373 Trace.prototype.traceResolvePath = function (path, when, trans) {
1374 if (!this.enabled(exports.Category.RESOLVE))
1375 return;
1376 safeConsole.log(transLbl(trans) + ": Resolving " + path + " (" + when + ")");
1377 };
1378 /** @internal called by ui-router code */
1379 Trace.prototype.traceResolvableResolved = function (resolvable, trans) {
1380 if (!this.enabled(exports.Category.RESOLVE))
1381 return;
1382 safeConsole.log(transLbl(trans) + ": <- Resolved " + resolvable + " to: " + maxLength(200, stringify(resolvable.data)));
1383 };
1384 /** @internal called by ui-router code */
1385 Trace.prototype.traceError = function (reason, trans) {
1386 if (!this.enabled(exports.Category.TRANSITION))
1387 return;
1388 safeConsole.log(transLbl(trans) + ": <- Rejected " + stringify(trans) + ", reason: " + reason);
1389 };
1390 /** @internal called by ui-router code */
1391 Trace.prototype.traceSuccess = function (finalState, trans) {
1392 if (!this.enabled(exports.Category.TRANSITION))
1393 return;
1394 safeConsole.log(transLbl(trans) + ": <- Success " + stringify(trans) + ", final state: " + finalState.name);
1395 };
1396 /** @internal called by ui-router code */
1397 Trace.prototype.traceUIViewEvent = function (event, viewData, extra) {
1398 if (extra === void 0) { extra = ''; }
1399 if (!this.enabled(exports.Category.UIVIEW))
1400 return;
1401 safeConsole.log("ui-view: " + padString(30, event) + " " + uiViewString(viewData) + extra);
1402 };
1403 /** @internal called by ui-router code */
1404 Trace.prototype.traceUIViewConfigUpdated = function (viewData, context) {
1405 if (!this.enabled(exports.Category.UIVIEW))
1406 return;
1407 this.traceUIViewEvent('Updating', viewData, " with ViewConfig from context='" + context + "'");
1408 };
1409 /** @internal called by ui-router code */
1410 Trace.prototype.traceUIViewFill = function (viewData, html) {
1411 if (!this.enabled(exports.Category.UIVIEW))
1412 return;
1413 this.traceUIViewEvent('Fill', viewData, " with: " + maxLength(200, html));
1414 };
1415 /** @internal called by ui-router code */
1416 Trace.prototype.traceViewSync = function (pairs) {
1417 if (!this.enabled(exports.Category.VIEWCONFIG))
1418 return;
1419 var uivheader = 'uiview component fqn';
1420 var cfgheader = 'view config state (view name)';
1421 var mapping = pairs
1422 .map(function (_a) {
1423 var _b;
1424 var uiView = _a.uiView, viewConfig = _a.viewConfig;
1425 var uiv = uiView && uiView.fqn;
1426 var cfg = viewConfig && viewConfig.viewDecl.$context.name + ": (" + viewConfig.viewDecl.$name + ")";
1427 return _b = {}, _b[uivheader] = uiv, _b[cfgheader] = cfg, _b;
1428 })
1429 .sort(function (a, b) { return (a[uivheader] || '').localeCompare(b[uivheader] || ''); });
1430 safeConsole.table(mapping);
1431 };
1432 /** @internal called by ui-router code */
1433 Trace.prototype.traceViewServiceEvent = function (event, viewConfig) {
1434 if (!this.enabled(exports.Category.VIEWCONFIG))
1435 return;
1436 safeConsole.log("VIEWCONFIG: " + event + " " + viewConfigString(viewConfig));
1437 };
1438 /** @internal called by ui-router code */
1439 Trace.prototype.traceViewServiceUIViewEvent = function (event, viewData) {
1440 if (!this.enabled(exports.Category.VIEWCONFIG))
1441 return;
1442 safeConsole.log("VIEWCONFIG: " + event + " " + uiViewString(viewData));
1443 };
1444 return Trace;
1445 }());
1446 /**
1447 * The [[Trace]] singleton
1448 *
1449 * #### Example:
1450 * ```js
1451 * import {trace} from "@uirouter/core";
1452 * trace.enable(1, 5);
1453 * ```
1454 */
1455 var trace = new Trace();
1456
1457 /**
1458 * An internal class which implements [[ParamTypeDefinition]].
1459 *
1460 * A [[ParamTypeDefinition]] is a plain javascript object used to register custom parameter types.
1461 * When a param type definition is registered, an instance of this class is created internally.
1462 *
1463 * This class has naive implementations for all the [[ParamTypeDefinition]] methods.
1464 *
1465 * Used by [[UrlMatcher]] when matching or formatting URLs, or comparing and validating parameter values.
1466 *
1467 * #### Example:
1468 * ```js
1469 * var paramTypeDef = {
1470 * decode: function(val) { return parseInt(val, 10); },
1471 * encode: function(val) { return val && val.toString(); },
1472 * equals: function(a, b) { return this.is(a) && a === b; },
1473 * is: function(val) { return angular.isNumber(val) && isFinite(val) && val % 1 === 0; },
1474 * pattern: /\d+/
1475 * }
1476 *
1477 * var paramType = new ParamType(paramTypeDef);
1478 * ```
1479 */
1480 var ParamType = /** @class */ (function () {
1481 /**
1482 * @param def A configuration object which contains the custom type definition. The object's
1483 * properties will override the default methods and/or pattern in `ParamType`'s public interface.
1484 * @returns a new ParamType object
1485 */
1486 function ParamType(def) {
1487 /** @inheritdoc */
1488 this.pattern = /.*/;
1489 /** @inheritdoc */
1490 this.inherit = true;
1491 extend(this, def);
1492 }
1493 // consider these four methods to be "abstract methods" that should be overridden
1494 /** @inheritdoc */
1495 ParamType.prototype.is = function (val, key) {
1496 return true;
1497 };
1498 /** @inheritdoc */
1499 ParamType.prototype.encode = function (val, key) {
1500 return val;
1501 };
1502 /** @inheritdoc */
1503 ParamType.prototype.decode = function (val, key) {
1504 return val;
1505 };
1506 /** @inheritdoc */
1507 ParamType.prototype.equals = function (a, b) {
1508 // tslint:disable-next-line:triple-equals
1509 return a == b;
1510 };
1511 ParamType.prototype.$subPattern = function () {
1512 var sub = this.pattern.toString();
1513 return sub.substr(1, sub.length - 2);
1514 };
1515 ParamType.prototype.toString = function () {
1516 return "{ParamType:" + this.name + "}";
1517 };
1518 /** Given an encoded string, or a decoded object, returns a decoded object */
1519 ParamType.prototype.$normalize = function (val) {
1520 return this.is(val) ? val : this.decode(val);
1521 };
1522 /**
1523 * Wraps an existing custom ParamType as an array of ParamType, depending on 'mode'.
1524 * e.g.:
1525 * - urlmatcher pattern "/path?{queryParam[]:int}"
1526 * - url: "/path?queryParam=1&queryParam=2
1527 * - $stateParams.queryParam will be [1, 2]
1528 * if `mode` is "auto", then
1529 * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
1530 * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
1531 */
1532 ParamType.prototype.$asArray = function (mode, isSearch) {
1533 if (!mode)
1534 return this;
1535 if (mode === 'auto' && !isSearch)
1536 throw new Error("'auto' array mode is for query parameters only");
1537 return new ArrayType(this, mode);
1538 };
1539 return ParamType;
1540 }());
1541 /** Wraps up a `ParamType` object to handle array values. */
1542 function ArrayType(type, mode) {
1543 var _this = this;
1544 // Wrap non-array value as array
1545 function arrayWrap(val) {
1546 return isArray(val) ? val : isDefined(val) ? [val] : [];
1547 }
1548 // Unwrap array value for "auto" mode. Return undefined for empty array.
1549 function arrayUnwrap(val) {
1550 switch (val.length) {
1551 case 0:
1552 return undefined;
1553 case 1:
1554 return mode === 'auto' ? val[0] : val;
1555 default:
1556 return val;
1557 }
1558 }
1559 // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
1560 function arrayHandler(callback, allTruthyMode) {
1561 return function handleArray(val) {
1562 if (isArray(val) && val.length === 0)
1563 return val;
1564 var arr = arrayWrap(val);
1565 var result = map(arr, callback);
1566 return allTruthyMode === true ? filter(result, function (x) { return !x; }).length === 0 : arrayUnwrap(result);
1567 };
1568 }
1569 // Wraps type (.equals) functions to operate on each value of an array
1570 function arrayEqualsHandler(callback) {
1571 return function handleArray(val1, val2) {
1572 var left = arrayWrap(val1), right = arrayWrap(val2);
1573 if (left.length !== right.length)
1574 return false;
1575 for (var i = 0; i < left.length; i++) {
1576 if (!callback(left[i], right[i]))
1577 return false;
1578 }
1579 return true;
1580 };
1581 }
1582 ['encode', 'decode', 'equals', '$normalize'].forEach(function (name) {
1583 var paramTypeFn = type[name].bind(type);
1584 var wrapperFn = name === 'equals' ? arrayEqualsHandler : arrayHandler;
1585 _this[name] = wrapperFn(paramTypeFn);
1586 });
1587 extend(this, {
1588 dynamic: type.dynamic,
1589 name: type.name,
1590 pattern: type.pattern,
1591 inherit: type.inherit,
1592 raw: type.raw,
1593 is: arrayHandler(type.is.bind(type), true),
1594 $arrayMode: mode,
1595 });
1596 }
1597
1598 var hasOwn = Object.prototype.hasOwnProperty;
1599 var isShorthand = function (cfg) {
1600 return ['value', 'type', 'squash', 'array', 'dynamic'].filter(hasOwn.bind(cfg || {})).length === 0;
1601 };
1602
1603 (function (DefType) {
1604 DefType[DefType["PATH"] = 0] = "PATH";
1605 DefType[DefType["SEARCH"] = 1] = "SEARCH";
1606 DefType[DefType["CONFIG"] = 2] = "CONFIG";
1607 })(exports.DefType || (exports.DefType = {}));
1608 function getParamDeclaration(paramName, location, state) {
1609 var noReloadOnSearch = (state.reloadOnSearch === false && location === exports.DefType.SEARCH) || undefined;
1610 var dynamic = find([state.dynamic, noReloadOnSearch], isDefined);
1611 var defaultConfig = isDefined(dynamic) ? { dynamic: dynamic } : {};
1612 var paramConfig = unwrapShorthand(state && state.params && state.params[paramName]);
1613 return extend(defaultConfig, paramConfig);
1614 }
1615 function unwrapShorthand(cfg) {
1616 cfg = isShorthand(cfg) ? { value: cfg } : cfg;
1617 getStaticDefaultValue['__cacheable'] = true;
1618 function getStaticDefaultValue() {
1619 return cfg.value;
1620 }
1621 var $$fn = isInjectable(cfg.value) ? cfg.value : getStaticDefaultValue;
1622 return extend(cfg, { $$fn: $$fn });
1623 }
1624 function getType(cfg, urlType, location, id, paramTypes) {
1625 if (cfg.type && urlType && urlType.name !== 'string')
1626 throw new Error("Param '" + id + "' has two type configurations.");
1627 if (cfg.type && urlType && urlType.name === 'string' && paramTypes.type(cfg.type))
1628 return paramTypes.type(cfg.type);
1629 if (urlType)
1630 return urlType;
1631 if (!cfg.type) {
1632 var type = location === exports.DefType.CONFIG
1633 ? 'any'
1634 : location === exports.DefType.PATH
1635 ? 'path'
1636 : location === exports.DefType.SEARCH
1637 ? 'query'
1638 : 'string';
1639 return paramTypes.type(type);
1640 }
1641 return cfg.type instanceof ParamType ? cfg.type : paramTypes.type(cfg.type);
1642 }
1643 /** returns false, true, or the squash value to indicate the "default parameter url squash policy". */
1644 function getSquashPolicy(config, isOptional, defaultPolicy) {
1645 var squash = config.squash;
1646 if (!isOptional || squash === false)
1647 return false;
1648 if (!isDefined(squash) || squash == null)
1649 return defaultPolicy;
1650 if (squash === true || isString(squash))
1651 return squash;
1652 throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
1653 }
1654 function getReplace(config, arrayMode, isOptional, squash) {
1655 var defaultPolicy = [
1656 { from: '', to: isOptional || arrayMode ? undefined : '' },
1657 { from: null, to: isOptional || arrayMode ? undefined : '' },
1658 ];
1659 var replace = isArray(config.replace) ? config.replace : [];
1660 if (isString(squash))
1661 replace.push({ from: squash, to: undefined });
1662 var configuredKeys = map(replace, prop('from'));
1663 return filter(defaultPolicy, function (item) { return configuredKeys.indexOf(item.from) === -1; }).concat(replace);
1664 }
1665 var Param = /** @class */ (function () {
1666 function Param(id, type, location, urlConfig, state) {
1667 var config = getParamDeclaration(id, location, state);
1668 type = getType(config, type, location, id, urlConfig.paramTypes);
1669 var arrayMode = getArrayMode();
1670 type = arrayMode ? type.$asArray(arrayMode, location === exports.DefType.SEARCH) : type;
1671 var isOptional = config.value !== undefined || location === exports.DefType.SEARCH;
1672 var dynamic = isDefined(config.dynamic) ? !!config.dynamic : !!type.dynamic;
1673 var raw = isDefined(config.raw) ? !!config.raw : !!type.raw;
1674 var squash = getSquashPolicy(config, isOptional, urlConfig.defaultSquashPolicy());
1675 var replace = getReplace(config, arrayMode, isOptional, squash);
1676 var inherit = isDefined(config.inherit) ? !!config.inherit : !!type.inherit;
1677 // array config: param name (param[]) overrides default settings. explicit config overrides param name.
1678 function getArrayMode() {
1679 var arrayDefaults = { array: location === exports.DefType.SEARCH ? 'auto' : false };
1680 var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
1681 return extend(arrayDefaults, arrayParamNomenclature, config).array;
1682 }
1683 extend(this, { id: id, type: type, location: location, isOptional: isOptional, dynamic: dynamic, raw: raw, squash: squash, replace: replace, inherit: inherit, array: arrayMode, config: config });
1684 }
1685 Param.values = function (params, values) {
1686 if (values === void 0) { values = {}; }
1687 var paramValues = {};
1688 for (var _i = 0, params_1 = params; _i < params_1.length; _i++) {
1689 var param = params_1[_i];
1690 paramValues[param.id] = param.value(values[param.id]);
1691 }
1692 return paramValues;
1693 };
1694 /**
1695 * Finds [[Param]] objects which have different param values
1696 *
1697 * Filters a list of [[Param]] objects to only those whose parameter values differ in two param value objects
1698 *
1699 * @param params: The list of Param objects to filter
1700 * @param values1: The first set of parameter values
1701 * @param values2: the second set of parameter values
1702 *
1703 * @returns any Param objects whose values were different between values1 and values2
1704 */
1705 Param.changed = function (params, values1, values2) {
1706 if (values1 === void 0) { values1 = {}; }
1707 if (values2 === void 0) { values2 = {}; }
1708 return params.filter(function (param) { return !param.type.equals(values1[param.id], values2[param.id]); });
1709 };
1710 /**
1711 * Checks if two param value objects are equal (for a set of [[Param]] objects)
1712 *
1713 * @param params The list of [[Param]] objects to check
1714 * @param values1 The first set of param values
1715 * @param values2 The second set of param values
1716 *
1717 * @returns true if the param values in values1 and values2 are equal
1718 */
1719 Param.equals = function (params, values1, values2) {
1720 if (values1 === void 0) { values1 = {}; }
1721 if (values2 === void 0) { values2 = {}; }
1722 return Param.changed(params, values1, values2).length === 0;
1723 };
1724 /** Returns true if a the parameter values are valid, according to the Param definitions */
1725 Param.validates = function (params, values) {
1726 if (values === void 0) { values = {}; }
1727 return params.map(function (param) { return param.validates(values[param.id]); }).reduce(allTrueR, true);
1728 };
1729 Param.prototype.isDefaultValue = function (value) {
1730 return this.isOptional && this.type.equals(this.value(), value);
1731 };
1732 /**
1733 * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
1734 * default value, which may be the result of an injectable function.
1735 */
1736 Param.prototype.value = function (value) {
1737 var _this = this;
1738 /**
1739 * [Internal] Get the default value of a parameter, which may be an injectable function.
1740 */
1741 var getDefaultValue = function () {
1742 if (_this._defaultValueCache)
1743 return _this._defaultValueCache.defaultValue;
1744 if (!services.$injector)
1745 throw new Error('Injectable functions cannot be called at configuration time');
1746 var defaultValue = services.$injector.invoke(_this.config.$$fn);
1747 if (defaultValue !== null && defaultValue !== undefined && !_this.type.is(defaultValue))
1748 throw new Error("Default value (" + defaultValue + ") for parameter '" + _this.id + "' is not an instance of ParamType (" + _this.type.name + ")");
1749 if (_this.config.$$fn['__cacheable']) {
1750 _this._defaultValueCache = { defaultValue: defaultValue };
1751 }
1752 return defaultValue;
1753 };
1754 var replaceSpecialValues = function (val) {
1755 for (var _i = 0, _a = _this.replace; _i < _a.length; _i++) {
1756 var tuple = _a[_i];
1757 if (tuple.from === val)
1758 return tuple.to;
1759 }
1760 return val;
1761 };
1762 value = replaceSpecialValues(value);
1763 return isUndefined(value) ? getDefaultValue() : this.type.$normalize(value);
1764 };
1765 Param.prototype.isSearch = function () {
1766 return this.location === exports.DefType.SEARCH;
1767 };
1768 Param.prototype.validates = function (value) {
1769 // There was no parameter value, but the param is optional
1770 if ((isUndefined(value) || value === null) && this.isOptional)
1771 return true;
1772 // The value was not of the correct ParamType, and could not be decoded to the correct ParamType
1773 var normalized = this.type.$normalize(value);
1774 if (!this.type.is(normalized))
1775 return false;
1776 // The value was of the correct type, but when encoded, did not match the ParamType's regexp
1777 var encoded = this.type.encode(normalized);
1778 return !(isString(encoded) && !this.type.pattern.exec(encoded));
1779 };
1780 Param.prototype.toString = function () {
1781 return "{Param:" + this.id + " " + this.type + " squash: '" + this.squash + "' optional: " + this.isOptional + "}";
1782 };
1783 return Param;
1784 }());
1785
1786 /**
1787 * A registry for parameter types.
1788 *
1789 * This registry manages the built-in (and custom) parameter types.
1790 *
1791 * The built-in parameter types are:
1792 *
1793 * - [[string]]
1794 * - [[path]]
1795 * - [[query]]
1796 * - [[hash]]
1797 * - [[int]]
1798 * - [[bool]]
1799 * - [[date]]
1800 * - [[json]]
1801 * - [[any]]
1802 *
1803 * To register custom parameter types, use [[UrlConfig.type]], i.e.,
1804 *
1805 * ```js
1806 * router.urlService.config.type(customType)
1807 * ```
1808 */
1809 var ParamTypes = /** @class */ (function () {
1810 function ParamTypes() {
1811 this.enqueue = true;
1812 this.typeQueue = [];
1813 this.defaultTypes = pick(ParamTypes.prototype, [
1814 'hash',
1815 'string',
1816 'query',
1817 'path',
1818 'int',
1819 'bool',
1820 'date',
1821 'json',
1822 'any',
1823 ]);
1824 // Register default types. Store them in the prototype of this.types.
1825 var makeType = function (definition, name) { return new ParamType(extend({ name: name }, definition)); };
1826 this.types = inherit(map(this.defaultTypes, makeType), {});
1827 }
1828 ParamTypes.prototype.dispose = function () {
1829 this.types = {};
1830 };
1831 /**
1832 * Registers a parameter type
1833 *
1834 * End users should call [[UrlMatcherFactory.type]], which delegates to this method.
1835 */
1836 ParamTypes.prototype.type = function (name, definition, definitionFn) {
1837 if (!isDefined(definition))
1838 return this.types[name];
1839 if (this.types.hasOwnProperty(name))
1840 throw new Error("A type named '" + name + "' has already been defined.");
1841 this.types[name] = new ParamType(extend({ name: name }, definition));
1842 if (definitionFn) {
1843 this.typeQueue.push({ name: name, def: definitionFn });
1844 if (!this.enqueue)
1845 this._flushTypeQueue();
1846 }
1847 return this;
1848 };
1849 ParamTypes.prototype._flushTypeQueue = function () {
1850 while (this.typeQueue.length) {
1851 var type = this.typeQueue.shift();
1852 if (type.pattern)
1853 throw new Error("You cannot override a type's .pattern at runtime.");
1854 extend(this.types[type.name], services.$injector.invoke(type.def));
1855 }
1856 };
1857 return ParamTypes;
1858 }());
1859 function initDefaultTypes() {
1860 var makeDefaultType = function (def) {
1861 var valToString = function (val) { return (val != null ? val.toString() : val); };
1862 var defaultTypeBase = {
1863 encode: valToString,
1864 decode: valToString,
1865 is: is(String),
1866 pattern: /.*/,
1867 // tslint:disable-next-line:triple-equals
1868 equals: function (a, b) { return a == b; },
1869 };
1870 return extend({}, defaultTypeBase, def);
1871 };
1872 // Default Parameter Type Definitions
1873 extend(ParamTypes.prototype, {
1874 string: makeDefaultType({}),
1875 path: makeDefaultType({
1876 pattern: /[^/]*/,
1877 }),
1878 query: makeDefaultType({}),
1879 hash: makeDefaultType({
1880 inherit: false,
1881 }),
1882 int: makeDefaultType({
1883 decode: function (val) { return parseInt(val, 10); },
1884 is: function (val) {
1885 return !isNullOrUndefined(val) && this.decode(val.toString()) === val;
1886 },
1887 pattern: /-?\d+/,
1888 }),
1889 bool: makeDefaultType({
1890 encode: function (val) { return (val && 1) || 0; },
1891 decode: function (val) { return parseInt(val, 10) !== 0; },
1892 is: is(Boolean),
1893 pattern: /0|1/,
1894 }),
1895 date: makeDefaultType({
1896 encode: function (val) {
1897 return !this.is(val)
1898 ? undefined
1899 : [val.getFullYear(), ('0' + (val.getMonth() + 1)).slice(-2), ('0' + val.getDate()).slice(-2)].join('-');
1900 },
1901 decode: function (val) {
1902 if (this.is(val))
1903 return val;
1904 var match = this.capture.exec(val);
1905 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
1906 },
1907 is: function (val) { return val instanceof Date && !isNaN(val.valueOf()); },
1908 equals: function (l, r) {
1909 return ['getFullYear', 'getMonth', 'getDate'].reduce(function (acc, fn) { return acc && l[fn]() === r[fn](); }, true);
1910 },
1911 pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
1912 capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/,
1913 }),
1914 json: makeDefaultType({
1915 encode: toJson,
1916 decode: fromJson,
1917 is: is(Object),
1918 equals: equals,
1919 pattern: /[^/]*/,
1920 }),
1921 // does not encode/decode
1922 any: makeDefaultType({
1923 encode: identity,
1924 decode: identity,
1925 is: function () { return true; },
1926 equals: equals,
1927 }),
1928 });
1929 }
1930 initDefaultTypes();
1931
1932 var StateParams = /** @class */ (function () {
1933 function StateParams(params) {
1934 if (params === void 0) { params = {}; }
1935 extend(this, params);
1936 }
1937 /**
1938 * Merges a set of parameters with all parameters inherited between the common parents of the
1939 * current state and a given destination state.
1940 *
1941 * @param {Object} newParams The set of parameters which will be composited with inherited params.
1942 * @param {Object} $current Internal definition of object representing the current state.
1943 * @param {Object} $to Internal definition of object representing state to transition to.
1944 */
1945 StateParams.prototype.$inherit = function (newParams, $current, $to) {
1946 var parentParams;
1947 var parents = ancestors($current, $to), inherited = {}, inheritList = [];
1948 for (var i in parents) {
1949 if (!parents[i] || !parents[i].params)
1950 continue;
1951 parentParams = Object.keys(parents[i].params);
1952 if (!parentParams.length)
1953 continue;
1954 for (var j in parentParams) {
1955 if (inheritList.indexOf(parentParams[j]) >= 0)
1956 continue;
1957 inheritList.push(parentParams[j]);
1958 inherited[parentParams[j]] = this[parentParams[j]];
1959 }
1960 }
1961 return extend({}, inherited, newParams);
1962 };
1963 return StateParams;
1964 }());
1965
1966 /**
1967 * A node in a [[TreeChanges]] path
1968 *
1969 * For a [[TreeChanges]] path, this class holds the stateful information for a single node in the path.
1970 * Each PathNode corresponds to a state being entered, exited, or retained.
1971 * The stateful information includes parameter values and resolve data.
1972 */
1973 var PathNode = /** @class */ (function () {
1974 function PathNode(stateOrNode) {
1975 if (stateOrNode instanceof PathNode) {
1976 var node = stateOrNode;
1977 this.state = node.state;
1978 this.paramSchema = node.paramSchema.slice();
1979 this.paramValues = extend({}, node.paramValues);
1980 this.resolvables = node.resolvables.slice();
1981 this.views = node.views && node.views.slice();
1982 }
1983 else {
1984 var state = stateOrNode;
1985 this.state = state;
1986 this.paramSchema = state.parameters({ inherit: false });
1987 this.paramValues = {};
1988 this.resolvables = state.resolvables.map(function (res) { return res.clone(); });
1989 }
1990 }
1991 PathNode.prototype.clone = function () {
1992 return new PathNode(this);
1993 };
1994 /** Sets [[paramValues]] for the node, from the values of an object hash */
1995 PathNode.prototype.applyRawParams = function (params) {
1996 var getParamVal = function (paramDef) { return [paramDef.id, paramDef.value(params[paramDef.id])]; };
1997 this.paramValues = this.paramSchema.reduce(function (memo, pDef) { return applyPairs(memo, getParamVal(pDef)); }, {});
1998 return this;
1999 };
2000 /** Gets a specific [[Param]] metadata that belongs to the node */
2001 PathNode.prototype.parameter = function (name) {
2002 return find(this.paramSchema, propEq('id', name));
2003 };
2004 /**
2005 * @returns true if the state and parameter values for another PathNode are
2006 * equal to the state and param values for this PathNode
2007 */
2008 PathNode.prototype.equals = function (node, paramsFn) {
2009 var diff = this.diff(node, paramsFn);
2010 return diff && diff.length === 0;
2011 };
2012 /**
2013 * Finds Params with different parameter values on another PathNode.
2014 *
2015 * Given another node (of the same state), finds the parameter values which differ.
2016 * Returns the [[Param]] (schema objects) whose parameter values differ.
2017 *
2018 * Given another node for a different state, returns `false`
2019 *
2020 * @param node The node to compare to
2021 * @param paramsFn A function that returns which parameters should be compared.
2022 * @returns The [[Param]]s which differ, or null if the two nodes are for different states
2023 */
2024 PathNode.prototype.diff = function (node, paramsFn) {
2025 if (this.state !== node.state)
2026 return false;
2027 var params = paramsFn ? paramsFn(this) : this.paramSchema;
2028 return Param.changed(params, this.paramValues, node.paramValues);
2029 };
2030 /**
2031 * Returns a clone of the PathNode
2032 * @deprecated use instance method `node.clone()`
2033 */
2034 PathNode.clone = function (node) { return node.clone(); };
2035 return PathNode;
2036 }());
2037
2038 /**
2039 * Encapsulate the target (destination) state/params/options of a [[Transition]].
2040 *
2041 * This class is frequently used to redirect a transition to a new destination.
2042 *
2043 * See:
2044 *
2045 * - [[HookResult]]
2046 * - [[TransitionHookFn]]
2047 * - [[TransitionService.onStart]]
2048 *
2049 * To create a `TargetState`, use [[StateService.target]].
2050 *
2051 * ---
2052 *
2053 * This class wraps:
2054 *
2055 * 1) an identifier for a state
2056 * 2) a set of parameters
2057 * 3) and transition options
2058 * 4) the registered state object (the [[StateDeclaration]])
2059 *
2060 * Many UI-Router APIs such as [[StateService.go]] take a [[StateOrName]] argument which can
2061 * either be a *state object* (a [[StateDeclaration]] or [[StateObject]]) or a *state name* (a string).
2062 * The `TargetState` class normalizes those options.
2063 *
2064 * A `TargetState` may be valid (the state being targeted exists in the registry)
2065 * or invalid (the state being targeted is not registered).
2066 */
2067 var TargetState = /** @class */ (function () {
2068 /**
2069 * The TargetState constructor
2070 *
2071 * Note: Do not construct a `TargetState` manually.
2072 * To create a `TargetState`, use the [[StateService.target]] factory method.
2073 *
2074 * @param _stateRegistry The StateRegistry to use to look up the _definition
2075 * @param _identifier An identifier for a state.
2076 * Either a fully-qualified state name, or the object used to define the state.
2077 * @param _params Parameters for the target state
2078 * @param _options Transition options.
2079 *
2080 * @internal
2081 */
2082 function TargetState(_stateRegistry, _identifier, _params, _options) {
2083 this._stateRegistry = _stateRegistry;
2084 this._identifier = _identifier;
2085 this._identifier = _identifier;
2086 this._params = extend({}, _params || {});
2087 this._options = extend({}, _options || {});
2088 this._definition = _stateRegistry.matcher.find(_identifier, this._options.relative);
2089 }
2090 /** The name of the state this object targets */
2091 TargetState.prototype.name = function () {
2092 return (this._definition && this._definition.name) || this._identifier;
2093 };
2094 /** The identifier used when creating this TargetState */
2095 TargetState.prototype.identifier = function () {
2096 return this._identifier;
2097 };
2098 /** The target parameter values */
2099 TargetState.prototype.params = function () {
2100 return this._params;
2101 };
2102 /** The internal state object (if it was found) */
2103 TargetState.prototype.$state = function () {
2104 return this._definition;
2105 };
2106 /** The internal state declaration (if it was found) */
2107 TargetState.prototype.state = function () {
2108 return this._definition && this._definition.self;
2109 };
2110 /** The target options */
2111 TargetState.prototype.options = function () {
2112 return this._options;
2113 };
2114 /** True if the target state was found */
2115 TargetState.prototype.exists = function () {
2116 return !!(this._definition && this._definition.self);
2117 };
2118 /** True if the object is valid */
2119 TargetState.prototype.valid = function () {
2120 return !this.error();
2121 };
2122 /** If the object is invalid, returns the reason why */
2123 TargetState.prototype.error = function () {
2124 var base = this.options().relative;
2125 if (!this._definition && !!base) {
2126 var stateName = base.name ? base.name : base;
2127 return "Could not resolve '" + this.name() + "' from state '" + stateName + "'";
2128 }
2129 if (!this._definition)
2130 return "No such state '" + this.name() + "'";
2131 if (!this._definition.self)
2132 return "State '" + this.name() + "' has an invalid definition";
2133 };
2134 TargetState.prototype.toString = function () {
2135 return "'" + this.name() + "'" + stringify(this.params());
2136 };
2137 /**
2138 * Returns a copy of this TargetState which targets a different state.
2139 * The new TargetState has the same parameter values and transition options.
2140 *
2141 * @param state The new state that should be targeted
2142 */
2143 TargetState.prototype.withState = function (state) {
2144 return new TargetState(this._stateRegistry, state, this._params, this._options);
2145 };
2146 /**
2147 * Returns a copy of this TargetState, using the specified parameter values.
2148 *
2149 * @param params the new parameter values to use
2150 * @param replace When false (default) the new parameter values will be merged with the current values.
2151 * When true the parameter values will be used instead of the current values.
2152 */
2153 TargetState.prototype.withParams = function (params, replace) {
2154 if (replace === void 0) { replace = false; }
2155 var newParams = replace ? params : extend({}, this._params, params);
2156 return new TargetState(this._stateRegistry, this._identifier, newParams, this._options);
2157 };
2158 /**
2159 * Returns a copy of this TargetState, using the specified Transition Options.
2160 *
2161 * @param options the new options to use
2162 * @param replace When false (default) the new options will be merged with the current options.
2163 * When true the options will be used instead of the current options.
2164 */
2165 TargetState.prototype.withOptions = function (options, replace) {
2166 if (replace === void 0) { replace = false; }
2167 var newOpts = replace ? options : extend({}, this._options, options);
2168 return new TargetState(this._stateRegistry, this._identifier, this._params, newOpts);
2169 };
2170 /** Returns true if the object has a state property that might be a state or state name */
2171 TargetState.isDef = function (obj) {
2172 return obj && obj.state && (isString(obj.state) || (isObject(obj.state) && isString(obj.state.name)));
2173 };
2174 return TargetState;
2175 }());
2176
2177 /**
2178 * This class contains functions which convert TargetStates, Nodes and paths from one type to another.
2179 */
2180 var PathUtils = /** @class */ (function () {
2181 function PathUtils() {
2182 }
2183 /** Given a PathNode[], create an TargetState */
2184 PathUtils.makeTargetState = function (registry, path) {
2185 var state = tail(path).state;
2186 return new TargetState(registry, state, path.map(prop('paramValues')).reduce(mergeR, {}), {});
2187 };
2188 PathUtils.buildPath = function (targetState) {
2189 var toParams = targetState.params();
2190 return targetState.$state().path.map(function (state) { return new PathNode(state).applyRawParams(toParams); });
2191 };
2192 /** Given a fromPath: PathNode[] and a TargetState, builds a toPath: PathNode[] */
2193 PathUtils.buildToPath = function (fromPath, targetState) {
2194 var toPath = PathUtils.buildPath(targetState);
2195 if (targetState.options().inherit) {
2196 return PathUtils.inheritParams(fromPath, toPath, Object.keys(targetState.params()));
2197 }
2198 return toPath;
2199 };
2200 /**
2201 * Creates ViewConfig objects and adds to nodes.
2202 *
2203 * On each [[PathNode]], creates ViewConfig objects from the views: property of the node's state
2204 */
2205 PathUtils.applyViewConfigs = function ($view, path, states) {
2206 // Only apply the viewConfigs to the nodes for the given states
2207 path
2208 .filter(function (node) { return inArray(states, node.state); })
2209 .forEach(function (node) {
2210 var viewDecls = values(node.state.views || {});
2211 var subPath = PathUtils.subPath(path, function (n) { return n === node; });
2212 var viewConfigs = viewDecls.map(function (view) { return $view.createViewConfig(subPath, view); });
2213 node.views = viewConfigs.reduce(unnestR, []);
2214 });
2215 };
2216 /**
2217 * Given a fromPath and a toPath, returns a new to path which inherits parameters from the fromPath
2218 *
2219 * For a parameter in a node to be inherited from the from path:
2220 * - The toPath's node must have a matching node in the fromPath (by state).
2221 * - The parameter name must not be found in the toKeys parameter array.
2222 *
2223 * Note: the keys provided in toKeys are intended to be those param keys explicitly specified by some
2224 * caller, for instance, $state.transitionTo(..., toParams). If a key was found in toParams,
2225 * it is not inherited from the fromPath.
2226 */
2227 PathUtils.inheritParams = function (fromPath, toPath, toKeys) {
2228 if (toKeys === void 0) { toKeys = []; }
2229 function nodeParamVals(path, state) {
2230 var node = find(path, propEq('state', state));
2231 return extend({}, node && node.paramValues);
2232 }
2233 var noInherit = fromPath
2234 .map(function (node) { return node.paramSchema; })
2235 .reduce(unnestR, [])
2236 .filter(function (param) { return !param.inherit; })
2237 .map(prop('id'));
2238 /**
2239 * Given an [[PathNode]] "toNode", return a new [[PathNode]] with param values inherited from the
2240 * matching node in fromPath. Only inherit keys that aren't found in "toKeys" from the node in "fromPath""
2241 */
2242 function makeInheritedParamsNode(toNode) {
2243 // All param values for the node (may include default key/vals, when key was not found in toParams)
2244 var toParamVals = extend({}, toNode && toNode.paramValues);
2245 // limited to only those keys found in toParams
2246 var incomingParamVals = pick(toParamVals, toKeys);
2247 toParamVals = omit(toParamVals, toKeys);
2248 var fromParamVals = omit(nodeParamVals(fromPath, toNode.state) || {}, noInherit);
2249 // extend toParamVals with any fromParamVals, then override any of those those with incomingParamVals
2250 var ownParamVals = extend(toParamVals, fromParamVals, incomingParamVals);
2251 return new PathNode(toNode.state).applyRawParams(ownParamVals);
2252 }
2253 // The param keys specified by the incoming toParams
2254 return toPath.map(makeInheritedParamsNode);
2255 };
2256 /**
2257 * Computes the tree changes (entering, exiting) between a fromPath and toPath.
2258 */
2259 PathUtils.treeChanges = function (fromPath, toPath, reloadState) {
2260 var max = Math.min(fromPath.length, toPath.length);
2261 var keep = 0;
2262 var nodesMatch = function (node1, node2) { return node1.equals(node2, PathUtils.nonDynamicParams); };
2263 while (keep < max && fromPath[keep].state !== reloadState && nodesMatch(fromPath[keep], toPath[keep])) {
2264 keep++;
2265 }
2266 /** Given a retained node, return a new node which uses the to node's param values */
2267 function applyToParams(retainedNode, idx) {
2268 var cloned = retainedNode.clone();
2269 cloned.paramValues = toPath[idx].paramValues;
2270 return cloned;
2271 }
2272 var from, retained, exiting, entering, to;
2273 from = fromPath;
2274 retained = from.slice(0, keep);
2275 exiting = from.slice(keep);
2276 // Create a new retained path (with shallow copies of nodes) which have the params of the toPath mapped
2277 var retainedWithToParams = retained.map(applyToParams);
2278 entering = toPath.slice(keep);
2279 to = retainedWithToParams.concat(entering);
2280 return { from: from, to: to, retained: retained, retainedWithToParams: retainedWithToParams, exiting: exiting, entering: entering };
2281 };
2282 /**
2283 * Returns a new path which is: the subpath of the first path which matches the second path.
2284 *
2285 * The new path starts from root and contains any nodes that match the nodes in the second path.
2286 * It stops before the first non-matching node.
2287 *
2288 * Nodes are compared using their state property and their parameter values.
2289 * If a `paramsFn` is provided, only the [[Param]] returned by the function will be considered when comparing nodes.
2290 *
2291 * @param pathA the first path
2292 * @param pathB the second path
2293 * @param paramsFn a function which returns the parameters to consider when comparing
2294 *
2295 * @returns an array of PathNodes from the first path which match the nodes in the second path
2296 */
2297 PathUtils.matching = function (pathA, pathB, paramsFn) {
2298 var done = false;
2299 var tuples = arrayTuples(pathA, pathB);
2300 return tuples.reduce(function (matching, _a) {
2301 var nodeA = _a[0], nodeB = _a[1];
2302 done = done || !nodeA.equals(nodeB, paramsFn);
2303 return done ? matching : matching.concat(nodeA);
2304 }, []);
2305 };
2306 /**
2307 * Returns true if two paths are identical.
2308 *
2309 * @param pathA
2310 * @param pathB
2311 * @param paramsFn a function which returns the parameters to consider when comparing
2312 * @returns true if the the states and parameter values for both paths are identical
2313 */
2314 PathUtils.equals = function (pathA, pathB, paramsFn) {
2315 return pathA.length === pathB.length && PathUtils.matching(pathA, pathB, paramsFn).length === pathA.length;
2316 };
2317 /**
2318 * Return a subpath of a path, which stops at the first matching node
2319 *
2320 * Given an array of nodes, returns a subset of the array starting from the first node,
2321 * stopping when the first node matches the predicate.
2322 *
2323 * @param path a path of [[PathNode]]s
2324 * @param predicate a [[Predicate]] fn that matches [[PathNode]]s
2325 * @returns a subpath up to the matching node, or undefined if no match is found
2326 */
2327 PathUtils.subPath = function (path, predicate) {
2328 var node = find(path, predicate);
2329 var elementIdx = path.indexOf(node);
2330 return elementIdx === -1 ? undefined : path.slice(0, elementIdx + 1);
2331 };
2332 PathUtils.nonDynamicParams = function (node) {
2333 return node.state.parameters({ inherit: false }).filter(function (param) { return !param.dynamic; });
2334 };
2335 /** Gets the raw parameter values from a path */
2336 PathUtils.paramValues = function (path) { return path.reduce(function (acc, node) { return extend(acc, node.paramValues); }, {}); };
2337 return PathUtils;
2338 }());
2339
2340 var resolvePolicies = {
2341 when: {
2342 LAZY: 'LAZY',
2343 EAGER: 'EAGER',
2344 },
2345 async: {
2346 WAIT: 'WAIT',
2347 NOWAIT: 'NOWAIT',
2348 },
2349 };
2350
2351 // TODO: explicitly make this user configurable
2352 var defaultResolvePolicy = {
2353 when: 'LAZY',
2354 async: 'WAIT',
2355 };
2356 /**
2357 * The basic building block for the resolve system.
2358 *
2359 * Resolvables encapsulate a state's resolve's resolveFn, the resolveFn's declared dependencies, the wrapped (.promise),
2360 * and the unwrapped-when-complete (.data) result of the resolveFn.
2361 *
2362 * Resolvable.get() either retrieves the Resolvable's existing promise, or else invokes resolve() (which invokes the
2363 * resolveFn) and returns the resulting promise.
2364 *
2365 * Resolvable.get() and Resolvable.resolve() both execute within a context path, which is passed as the first
2366 * parameter to those fns.
2367 */
2368 var Resolvable = /** @class */ (function () {
2369 function Resolvable(arg1, resolveFn, deps, policy, data) {
2370 this.resolved = false;
2371 this.promise = undefined;
2372 if (arg1 instanceof Resolvable) {
2373 extend(this, arg1);
2374 }
2375 else if (isFunction(resolveFn)) {
2376 if (isNullOrUndefined(arg1))
2377 throw new Error('new Resolvable(): token argument is required');
2378 if (!isFunction(resolveFn))
2379 throw new Error('new Resolvable(): resolveFn argument must be a function');
2380 this.token = arg1;
2381 this.policy = policy;
2382 this.resolveFn = resolveFn;
2383 this.deps = deps || [];
2384 this.data = data;
2385 this.resolved = data !== undefined;
2386 this.promise = this.resolved ? services.$q.when(this.data) : undefined;
2387 }
2388 else if (isObject(arg1) && arg1.token && (arg1.hasOwnProperty('resolveFn') || arg1.hasOwnProperty('data'))) {
2389 var literal = arg1;
2390 return new Resolvable(literal.token, literal.resolveFn, literal.deps, literal.policy, literal.data);
2391 }
2392 }
2393 Resolvable.prototype.getPolicy = function (state) {
2394 var thisPolicy = this.policy || {};
2395 var statePolicy = (state && state.resolvePolicy) || {};
2396 return {
2397 when: thisPolicy.when || statePolicy.when || defaultResolvePolicy.when,
2398 async: thisPolicy.async || statePolicy.async || defaultResolvePolicy.async,
2399 };
2400 };
2401 /**
2402 * Asynchronously resolve this Resolvable's data
2403 *
2404 * Given a ResolveContext that this Resolvable is found in:
2405 * Wait for this Resolvable's dependencies, then invoke this Resolvable's function
2406 * and update the Resolvable's state
2407 */
2408 Resolvable.prototype.resolve = function (resolveContext, trans) {
2409 var _this = this;
2410 var $q = services.$q;
2411 // Gets all dependencies from ResolveContext and wait for them to be resolved
2412 var getResolvableDependencies = function () {
2413 return $q.all(resolveContext.getDependencies(_this).map(function (resolvable) { return resolvable.get(resolveContext, trans); }));
2414 };
2415 // Invokes the resolve function passing the resolved dependencies as arguments
2416 var invokeResolveFn = function (resolvedDeps) { return _this.resolveFn.apply(null, resolvedDeps); };
2417 var node = resolveContext.findNode(this);
2418 var state = node && node.state;
2419 var asyncPolicy = this.getPolicy(state).async;
2420 var customAsyncPolicy = isFunction(asyncPolicy) ? asyncPolicy : identity;
2421 // After the final value has been resolved, update the state of the Resolvable
2422 var applyResolvedValue = function (resolvedValue) {
2423 _this.data = resolvedValue;
2424 _this.resolved = true;
2425 _this.resolveFn = null;
2426 trace.traceResolvableResolved(_this, trans);
2427 return _this.data;
2428 };
2429 // Sets the promise property first, then getsResolvableDependencies in the context of the promise chain. Always waits one tick.
2430 return (this.promise = $q
2431 .when()
2432 .then(getResolvableDependencies)
2433 .then(invokeResolveFn)
2434 .then(customAsyncPolicy)
2435 .then(applyResolvedValue));
2436 };
2437 /**
2438 * Gets a promise for this Resolvable's data.
2439 *
2440 * Fetches the data and returns a promise.
2441 * Returns the existing promise if it has already been fetched once.
2442 */
2443 Resolvable.prototype.get = function (resolveContext, trans) {
2444 return this.promise || this.resolve(resolveContext, trans);
2445 };
2446 Resolvable.prototype.toString = function () {
2447 return "Resolvable(token: " + stringify(this.token) + ", requires: [" + this.deps.map(stringify) + "])";
2448 };
2449 Resolvable.prototype.clone = function () {
2450 return new Resolvable(this);
2451 };
2452 Resolvable.fromData = function (token, data) { return new Resolvable(token, function () { return data; }, null, null, data); };
2453 return Resolvable;
2454 }());
2455
2456 var whens = resolvePolicies.when;
2457 var ALL_WHENS = [whens.EAGER, whens.LAZY];
2458 var EAGER_WHENS = [whens.EAGER];
2459 // tslint:disable-next-line:no-inferrable-types
2460 var NATIVE_INJECTOR_TOKEN = 'Native Injector';
2461 /**
2462 * Encapsulates Dependency Injection for a path of nodes
2463 *
2464 * UI-Router states are organized as a tree.
2465 * A nested state has a path of ancestors to the root of the tree.
2466 * When a state is being activated, each element in the path is wrapped as a [[PathNode]].
2467 * A `PathNode` is a stateful object that holds things like parameters and resolvables for the state being activated.
2468 *
2469 * The ResolveContext closes over the [[PathNode]]s, and provides DI for the last node in the path.
2470 */
2471 var ResolveContext = /** @class */ (function () {
2472 function ResolveContext(_path) {
2473 this._path = _path;
2474 }
2475 /** Gets all the tokens found in the resolve context, de-duplicated */
2476 ResolveContext.prototype.getTokens = function () {
2477 return this._path.reduce(function (acc, node) { return acc.concat(node.resolvables.map(function (r) { return r.token; })); }, []).reduce(uniqR, []);
2478 };
2479 /**
2480 * Gets the Resolvable that matches the token
2481 *
2482 * Gets the last Resolvable that matches the token in this context, or undefined.
2483 * Throws an error if it doesn't exist in the ResolveContext
2484 */
2485 ResolveContext.prototype.getResolvable = function (token) {
2486 var matching = this._path
2487 .map(function (node) { return node.resolvables; })
2488 .reduce(unnestR, [])
2489 .filter(function (r) { return r.token === token; });
2490 return tail(matching);
2491 };
2492 /** Returns the [[ResolvePolicy]] for the given [[Resolvable]] */
2493 ResolveContext.prototype.getPolicy = function (resolvable) {
2494 var node = this.findNode(resolvable);
2495 return resolvable.getPolicy(node.state);
2496 };
2497 /**
2498 * Returns a ResolveContext that includes a portion of this one
2499 *
2500 * Given a state, this method creates a new ResolveContext from this one.
2501 * The new context starts at the first node (root) and stops at the node for the `state` parameter.
2502 *
2503 * #### Why
2504 *
2505 * When a transition is created, the nodes in the "To Path" are injected from a ResolveContext.
2506 * A ResolveContext closes over a path of [[PathNode]]s and processes the resolvables.
2507 * The "To State" can inject values from its own resolvables, as well as those from all its ancestor state's (node's).
2508 * This method is used to create a narrower context when injecting ancestor nodes.
2509 *
2510 * @example
2511 * `let ABCD = new ResolveContext([A, B, C, D]);`
2512 *
2513 * Given a path `[A, B, C, D]`, where `A`, `B`, `C` and `D` are nodes for states `a`, `b`, `c`, `d`:
2514 * When injecting `D`, `D` should have access to all resolvables from `A`, `B`, `C`, `D`.
2515 * However, `B` should only be able to access resolvables from `A`, `B`.
2516 *
2517 * When resolving for the `B` node, first take the full "To Path" Context `[A,B,C,D]` and limit to the subpath `[A,B]`.
2518 * `let AB = ABCD.subcontext(a)`
2519 */
2520 ResolveContext.prototype.subContext = function (state) {
2521 return new ResolveContext(PathUtils.subPath(this._path, function (node) { return node.state === state; }));
2522 };
2523 /**
2524 * Adds Resolvables to the node that matches the state
2525 *
2526 * This adds a [[Resolvable]] (generally one created on the fly; not declared on a [[StateDeclaration.resolve]] block).
2527 * The resolvable is added to the node matching the `state` parameter.
2528 *
2529 * These new resolvables are not automatically fetched.
2530 * The calling code should either fetch them, fetch something that depends on them,
2531 * or rely on [[resolvePath]] being called when some state is being entered.
2532 *
2533 * Note: each resolvable's [[ResolvePolicy]] is merged with the state's policy, and the global default.
2534 *
2535 * @param newResolvables the new Resolvables
2536 * @param state Used to find the node to put the resolvable on
2537 */
2538 ResolveContext.prototype.addResolvables = function (newResolvables, state) {
2539 var node = find(this._path, propEq('state', state));
2540 var keys = newResolvables.map(function (r) { return r.token; });
2541 node.resolvables = node.resolvables.filter(function (r) { return keys.indexOf(r.token) === -1; }).concat(newResolvables);
2542 };
2543 /**
2544 * Returns a promise for an array of resolved path Element promises
2545 *
2546 * @param when
2547 * @param trans
2548 * @returns {Promise<any>|any}
2549 */
2550 ResolveContext.prototype.resolvePath = function (when, trans) {
2551 var _this = this;
2552 if (when === void 0) { when = 'LAZY'; }
2553 // This option determines which 'when' policy Resolvables we are about to fetch.
2554 var whenOption = inArray(ALL_WHENS, when) ? when : 'LAZY';
2555 // If the caller specified EAGER, only the EAGER Resolvables are fetched.
2556 // if the caller specified LAZY, both EAGER and LAZY Resolvables are fetched.`
2557 var matchedWhens = whenOption === resolvePolicies.when.EAGER ? EAGER_WHENS : ALL_WHENS;
2558 // get the subpath to the state argument, if provided
2559 trace.traceResolvePath(this._path, when, trans);
2560 var matchesPolicy = function (acceptedVals, whenOrAsync) { return function (resolvable) {
2561 return inArray(acceptedVals, _this.getPolicy(resolvable)[whenOrAsync]);
2562 }; };
2563 // Trigger all the (matching) Resolvables in the path
2564 // Reduce all the "WAIT" Resolvables into an array
2565 var promises = this._path.reduce(function (acc, node) {
2566 var nodeResolvables = node.resolvables.filter(matchesPolicy(matchedWhens, 'when'));
2567 var nowait = nodeResolvables.filter(matchesPolicy(['NOWAIT'], 'async'));
2568 var wait = nodeResolvables.filter(not(matchesPolicy(['NOWAIT'], 'async')));
2569 // For the matching Resolvables, start their async fetch process.
2570 var subContext = _this.subContext(node.state);
2571 var getResult = function (r) {
2572 return r
2573 .get(subContext, trans)
2574 // Return a tuple that includes the Resolvable's token
2575 .then(function (value) { return ({ token: r.token, value: value }); });
2576 };
2577 nowait.forEach(getResult);
2578 return acc.concat(wait.map(getResult));
2579 }, []);
2580 // Wait for all the "WAIT" resolvables
2581 return services.$q.all(promises);
2582 };
2583 ResolveContext.prototype.injector = function () {
2584 return this._injector || (this._injector = new UIInjectorImpl(this));
2585 };
2586 ResolveContext.prototype.findNode = function (resolvable) {
2587 return find(this._path, function (node) { return inArray(node.resolvables, resolvable); });
2588 };
2589 /**
2590 * Gets the async dependencies of a Resolvable
2591 *
2592 * Given a Resolvable, returns its dependencies as a Resolvable[]
2593 */
2594 ResolveContext.prototype.getDependencies = function (resolvable) {
2595 var _this = this;
2596 var node = this.findNode(resolvable);
2597 // Find which other resolvables are "visible" to the `resolvable` argument
2598 // subpath stopping at resolvable's node, or the whole path (if the resolvable isn't in the path)
2599 var subPath = PathUtils.subPath(this._path, function (x) { return x === node; }) || this._path;
2600 var availableResolvables = subPath
2601 .reduce(function (acc, _node) { return acc.concat(_node.resolvables); }, []) // all of subpath's resolvables
2602 .filter(function (res) { return res !== resolvable; }); // filter out the `resolvable` argument
2603 var getDependency = function (token) {
2604 var matching = availableResolvables.filter(function (r) { return r.token === token; });
2605 if (matching.length)
2606 return tail(matching);
2607 var fromInjector = _this.injector().getNative(token);
2608 if (isUndefined(fromInjector)) {
2609 throw new Error('Could not find Dependency Injection token: ' + stringify(token));
2610 }
2611 return new Resolvable(token, function () { return fromInjector; }, [], fromInjector);
2612 };
2613 return resolvable.deps.map(getDependency);
2614 };
2615 return ResolveContext;
2616 }());
2617 /** @internal */
2618 var UIInjectorImpl = /** @class */ (function () {
2619 function UIInjectorImpl(context) {
2620 this.context = context;
2621 this.native = this.get(NATIVE_INJECTOR_TOKEN) || services.$injector;
2622 }
2623 UIInjectorImpl.prototype.get = function (token) {
2624 var resolvable = this.context.getResolvable(token);
2625 if (resolvable) {
2626 if (this.context.getPolicy(resolvable).async === 'NOWAIT') {
2627 return resolvable.get(this.context);
2628 }
2629 if (!resolvable.resolved) {
2630 throw new Error('Resolvable async .get() not complete:' + stringify(resolvable.token));
2631 }
2632 return resolvable.data;
2633 }
2634 return this.getNative(token);
2635 };
2636 UIInjectorImpl.prototype.getAsync = function (token) {
2637 var resolvable = this.context.getResolvable(token);
2638 if (resolvable)
2639 return resolvable.get(this.context);
2640 return services.$q.when(this.native.get(token));
2641 };
2642 UIInjectorImpl.prototype.getNative = function (token) {
2643 return this.native && this.native.get(token);
2644 };
2645 return UIInjectorImpl;
2646 }());
2647
2648 var parseUrl = function (url) {
2649 if (!isString(url))
2650 return false;
2651 var root = url.charAt(0) === '^';
2652 return { val: root ? url.substring(1) : url, root: root };
2653 };
2654 function nameBuilder(state) {
2655 return state.name;
2656 }
2657 function selfBuilder(state) {
2658 state.self.$$state = function () { return state; };
2659 return state.self;
2660 }
2661 function dataBuilder(state) {
2662 if (state.parent && state.parent.data) {
2663 state.data = state.self.data = inherit(state.parent.data, state.data);
2664 }
2665 return state.data;
2666 }
2667 var getUrlBuilder = function ($urlMatcherFactoryProvider, root) {
2668 return function urlBuilder(stateObject) {
2669 var stateDec = stateObject.self;
2670 // For future states, i.e., states whose name ends with `.**`,
2671 // match anything that starts with the url prefix
2672 if (stateDec && stateDec.url && stateDec.name && stateDec.name.match(/\.\*\*$/)) {
2673 var newStateDec = {};
2674 copy(stateDec, newStateDec);
2675 newStateDec.url += '{remainder:any}'; // match any path (.*)
2676 stateDec = newStateDec;
2677 }
2678 var parent = stateObject.parent;
2679 var parsed = parseUrl(stateDec.url);
2680 var url = !parsed ? stateDec.url : $urlMatcherFactoryProvider.compile(parsed.val, { state: stateDec });
2681 if (!url)
2682 return null;
2683 if (!$urlMatcherFactoryProvider.isMatcher(url))
2684 throw new Error("Invalid url '" + url + "' in state '" + stateObject + "'");
2685 return parsed && parsed.root ? url : ((parent && parent.navigable) || root()).url.append(url);
2686 };
2687 };
2688 var getNavigableBuilder = function (isRoot) {
2689 return function navigableBuilder(state) {
2690 return !isRoot(state) && state.url ? state : state.parent ? state.parent.navigable : null;
2691 };
2692 };
2693 var getParamsBuilder = function (paramFactory) {
2694 return function paramsBuilder(state) {
2695 var makeConfigParam = function (config, id) { return paramFactory.fromConfig(id, null, state.self); };
2696 var urlParams = (state.url && state.url.parameters({ inherit: false })) || [];
2697 var nonUrlParams = values(mapObj(omit(state.params || {}, urlParams.map(prop('id'))), makeConfigParam));
2698 return urlParams
2699 .concat(nonUrlParams)
2700 .map(function (p) { return [p.id, p]; })
2701 .reduce(applyPairs, {});
2702 };
2703 };
2704 function pathBuilder(state) {
2705 return state.parent ? state.parent.path.concat(state) : /*root*/ [state];
2706 }
2707 function includesBuilder(state) {
2708 var includes = state.parent ? extend({}, state.parent.includes) : {};
2709 includes[state.name] = true;
2710 return includes;
2711 }
2712 /**
2713 * This is a [[StateBuilder.builder]] function for the `resolve:` block on a [[StateDeclaration]].
2714 *
2715 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
2716 * validates the `resolve` property and converts it to a [[Resolvable]] array.
2717 *
2718 * resolve: input value can be:
2719 *
2720 * {
2721 * // analyzed but not injected
2722 * myFooResolve: function() { return "myFooData"; },
2723 *
2724 * // function.toString() parsed, "DependencyName" dep as string (not min-safe)
2725 * myBarResolve: function(DependencyName) { return DependencyName.fetchSomethingAsPromise() },
2726 *
2727 * // Array split; "DependencyName" dep as string
2728 * myBazResolve: [ "DependencyName", function(dep) { return dep.fetchSomethingAsPromise() },
2729 *
2730 * // Array split; DependencyType dep as token (compared using ===)
2731 * myQuxResolve: [ DependencyType, function(dep) { return dep.fetchSometingAsPromise() },
2732 *
2733 * // val.$inject used as deps
2734 * // where:
2735 * // corgeResolve.$inject = ["DependencyName"];
2736 * // function corgeResolve(dep) { dep.fetchSometingAsPromise() }
2737 * // then "DependencyName" dep as string
2738 * myCorgeResolve: corgeResolve,
2739 *
2740 * // inject service by name
2741 * // When a string is found, desugar creating a resolve that injects the named service
2742 * myGraultResolve: "SomeService"
2743 * }
2744 *
2745 * or:
2746 *
2747 * [
2748 * new Resolvable("myFooResolve", function() { return "myFooData" }),
2749 * new Resolvable("myBarResolve", function(dep) { return dep.fetchSomethingAsPromise() }, [ "DependencyName" ]),
2750 * { provide: "myBazResolve", useFactory: function(dep) { dep.fetchSomethingAsPromise() }, deps: [ "DependencyName" ] }
2751 * ]
2752 */
2753 function resolvablesBuilder(state) {
2754 /** convert resolve: {} and resolvePolicy: {} objects to an array of tuples */
2755 var objects2Tuples = function (resolveObj, resolvePolicies) {
2756 return Object.keys(resolveObj || {}).map(function (token) { return ({
2757 token: token,
2758 val: resolveObj[token],
2759 deps: undefined,
2760 policy: resolvePolicies[token],
2761 }); });
2762 };
2763 /** fetch DI annotations from a function or ng1-style array */
2764 var annotate = function (fn) {
2765 var $injector = services.$injector;
2766 // ng1 doesn't have an $injector until runtime.
2767 // If the $injector doesn't exist, use "deferred" literal as a
2768 // marker indicating they should be annotated when runtime starts
2769 return fn['$inject'] || ($injector && $injector.annotate(fn, $injector.strictDi)) || 'deferred';
2770 };
2771 /** true if the object has both `token` and `resolveFn`, and is probably a [[ResolveLiteral]] */
2772 var isResolveLiteral = function (obj) { return !!(obj.token && obj.resolveFn); };
2773 /** true if the object looks like a provide literal, or a ng2 Provider */
2774 var isLikeNg2Provider = function (obj) {
2775 return !!((obj.provide || obj.token) && (obj.useValue || obj.useFactory || obj.useExisting || obj.useClass));
2776 };
2777 /** true if the object looks like a tuple from obj2Tuples */
2778 var isTupleFromObj = function (obj) {
2779 return !!(obj && obj.val && (isString(obj.val) || isArray(obj.val) || isFunction(obj.val)));
2780 };
2781 /** extracts the token from a Provider or provide literal */
2782 var getToken = function (p) { return p.provide || p.token; };
2783 // prettier-ignore: Given a literal resolve or provider object, returns a Resolvable
2784 var literal2Resolvable = pattern([
2785 [prop('resolveFn'), function (p) { return new Resolvable(getToken(p), p.resolveFn, p.deps, p.policy); }],
2786 [prop('useFactory'), function (p) { return new Resolvable(getToken(p), p.useFactory, p.deps || p.dependencies, p.policy); }],
2787 [prop('useClass'), function (p) { return new Resolvable(getToken(p), function () { return new p.useClass(); }, [], p.policy); }],
2788 [prop('useValue'), function (p) { return new Resolvable(getToken(p), function () { return p.useValue; }, [], p.policy, p.useValue); }],
2789 [prop('useExisting'), function (p) { return new Resolvable(getToken(p), identity, [p.useExisting], p.policy); }],
2790 ]);
2791 // prettier-ignore
2792 var tuple2Resolvable = pattern([
2793 [pipe(prop('val'), isString), function (tuple) { return new Resolvable(tuple.token, identity, [tuple.val], tuple.policy); }],
2794 [pipe(prop('val'), isArray), function (tuple) { return new Resolvable(tuple.token, tail(tuple.val), tuple.val.slice(0, -1), tuple.policy); }],
2795 [pipe(prop('val'), isFunction), function (tuple) { return new Resolvable(tuple.token, tuple.val, annotate(tuple.val), tuple.policy); }],
2796 ]);
2797 // prettier-ignore
2798 var item2Resolvable = pattern([
2799 [is(Resolvable), function (r) { return r; }],
2800 [isResolveLiteral, literal2Resolvable],
2801 [isLikeNg2Provider, literal2Resolvable],
2802 [isTupleFromObj, tuple2Resolvable],
2803 [val(true), function (obj) { throw new Error('Invalid resolve value: ' + stringify(obj)); },],
2804 ]);
2805 // If resolveBlock is already an array, use it as-is.
2806 // Otherwise, assume it's an object and convert to an Array of tuples
2807 var decl = state.resolve;
2808 var items = isArray(decl) ? decl : objects2Tuples(decl, state.resolvePolicy || {});
2809 return items.map(item2Resolvable);
2810 }
2811 /**
2812 * A internal global service
2813 *
2814 * StateBuilder is a factory for the internal [[StateObject]] objects.
2815 *
2816 * When you register a state with the [[StateRegistry]], you register a plain old javascript object which
2817 * conforms to the [[StateDeclaration]] interface. This factory takes that object and builds the corresponding
2818 * [[StateObject]] object, which has an API and is used internally.
2819 *
2820 * Custom properties or API may be added to the internal [[StateObject]] object by registering a decorator function
2821 * using the [[builder]] method.
2822 */
2823 var StateBuilder = /** @class */ (function () {
2824 function StateBuilder(matcher, urlMatcherFactory) {
2825 this.matcher = matcher;
2826 var self = this;
2827 var root = function () { return matcher.find(''); };
2828 var isRoot = function (state) { return state.name === ''; };
2829 function parentBuilder(state) {
2830 if (isRoot(state))
2831 return null;
2832 return matcher.find(self.parentName(state)) || root();
2833 }
2834 this.builders = {
2835 name: [nameBuilder],
2836 self: [selfBuilder],
2837 parent: [parentBuilder],
2838 data: [dataBuilder],
2839 // Build a URLMatcher if necessary, either via a relative or absolute URL
2840 url: [getUrlBuilder(urlMatcherFactory, root)],
2841 // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
2842 navigable: [getNavigableBuilder(isRoot)],
2843 params: [getParamsBuilder(urlMatcherFactory.paramFactory)],
2844 // Each framework-specific ui-router implementation should define its own `views` builder
2845 // e.g., src/ng1/statebuilders/views.ts
2846 views: [],
2847 // Keep a full path from the root down to this state as this is needed for state activation.
2848 path: [pathBuilder],
2849 // Speed up $state.includes() as it's used a lot
2850 includes: [includesBuilder],
2851 resolvables: [resolvablesBuilder],
2852 };
2853 }
2854 StateBuilder.prototype.builder = function (name, fn) {
2855 var builders = this.builders;
2856 var array = builders[name] || [];
2857 // Backwards compat: if only one builder exists, return it, else return whole arary.
2858 if (isString(name) && !isDefined(fn))
2859 return array.length > 1 ? array : array[0];
2860 if (!isString(name) || !isFunction(fn))
2861 return;
2862 builders[name] = array;
2863 builders[name].push(fn);
2864 return function () { return builders[name].splice(builders[name].indexOf(fn, 1)) && null; };
2865 };
2866 /**
2867 * Builds all of the properties on an essentially blank State object, returning a State object which has all its
2868 * properties and API built.
2869 *
2870 * @param state an uninitialized State object
2871 * @returns the built State object
2872 */
2873 StateBuilder.prototype.build = function (state) {
2874 var _a = this, matcher = _a.matcher, builders = _a.builders;
2875 var parent = this.parentName(state);
2876 if (parent && !matcher.find(parent, undefined, false)) {
2877 return null;
2878 }
2879 for (var key in builders) {
2880 if (!builders.hasOwnProperty(key))
2881 continue;
2882 var chain = builders[key].reduce(function (parentFn, step) { return function (_state) { return step(_state, parentFn); }; }, noop);
2883 state[key] = chain(state);
2884 }
2885 return state;
2886 };
2887 StateBuilder.prototype.parentName = function (state) {
2888 // name = 'foo.bar.baz.**'
2889 var name = state.name || '';
2890 // segments = ['foo', 'bar', 'baz', '.**']
2891 var segments = name.split('.');
2892 // segments = ['foo', 'bar', 'baz']
2893 var lastSegment = segments.pop();
2894 // segments = ['foo', 'bar'] (ignore .** segment for future states)
2895 if (lastSegment === '**')
2896 segments.pop();
2897 if (segments.length) {
2898 if (state.parent) {
2899 throw new Error("States that specify the 'parent:' property should not have a '.' in their name (" + name + ")");
2900 }
2901 // 'foo.bar'
2902 return segments.join('.');
2903 }
2904 if (!state.parent)
2905 return '';
2906 return isString(state.parent) ? state.parent : state.parent.name;
2907 };
2908 StateBuilder.prototype.name = function (state) {
2909 var name = state.name;
2910 if (name.indexOf('.') !== -1 || !state.parent)
2911 return name;
2912 var parentName = isString(state.parent) ? state.parent : state.parent.name;
2913 return parentName ? parentName + '.' + name : name;
2914 };
2915 return StateBuilder;
2916 }());
2917
2918 /**
2919 * Internal representation of a UI-Router state.
2920 *
2921 * Instances of this class are created when a [[StateDeclaration]] is registered with the [[StateRegistry]].
2922 *
2923 * A registered [[StateDeclaration]] is augmented with a getter ([[StateDeclaration.$$state]]) which returns the corresponding [[StateObject]] object.
2924 *
2925 * This class prototypally inherits from the corresponding [[StateDeclaration]].
2926 * Each of its own properties (i.e., `hasOwnProperty`) are built using builders from the [[StateBuilder]].
2927 */
2928 var StateObject = /** @class */ (function () {
2929 /** @deprecated use State.create() */
2930 function StateObject(config) {
2931 return StateObject.create(config || {});
2932 }
2933 /**
2934 * Create a state object to put the private/internal implementation details onto.
2935 * The object's prototype chain looks like:
2936 * (Internal State Object) -> (Copy of State.prototype) -> (State Declaration object) -> (State Declaration's prototype...)
2937 *
2938 * @param stateDecl the user-supplied State Declaration
2939 * @returns {StateObject} an internal State object
2940 */
2941 StateObject.create = function (stateDecl) {
2942 stateDecl = StateObject.isStateClass(stateDecl) ? new stateDecl() : stateDecl;
2943 var state = inherit(inherit(stateDecl, StateObject.prototype));
2944 stateDecl.$$state = function () { return state; };
2945 state.self = stateDecl;
2946 state.__stateObjectCache = {
2947 nameGlob: Glob.fromString(state.name),
2948 };
2949 return state;
2950 };
2951 /**
2952 * Returns true if the provided parameter is the same state.
2953 *
2954 * Compares the identity of the state against the passed value, which is either an object
2955 * reference to the actual `State` instance, the original definition object passed to
2956 * `$stateProvider.state()`, or the fully-qualified name.
2957 *
2958 * @param ref Can be one of (a) a `State` instance, (b) an object that was passed
2959 * into `$stateProvider.state()`, (c) the fully-qualified name of a state as a string.
2960 * @returns Returns `true` if `ref` matches the current `State` instance.
2961 */
2962 StateObject.prototype.is = function (ref) {
2963 return this === ref || this.self === ref || this.fqn() === ref;
2964 };
2965 /**
2966 * @deprecated this does not properly handle dot notation
2967 * @returns Returns a dot-separated name of the state.
2968 */
2969 StateObject.prototype.fqn = function () {
2970 if (!this.parent || !(this.parent instanceof this.constructor))
2971 return this.name;
2972 var name = this.parent.fqn();
2973 return name ? name + '.' + this.name : this.name;
2974 };
2975 /**
2976 * Returns the root node of this state's tree.
2977 *
2978 * @returns The root of this state's tree.
2979 */
2980 StateObject.prototype.root = function () {
2981 return (this.parent && this.parent.root()) || this;
2982 };
2983 /**
2984 * Gets the state's `Param` objects
2985 *
2986 * Gets the list of [[Param]] objects owned by the state.
2987 * If `opts.inherit` is true, it also includes the ancestor states' [[Param]] objects.
2988 * If `opts.matchingKeys` exists, returns only `Param`s whose `id` is a key on the `matchingKeys` object
2989 *
2990 * @param opts options
2991 */
2992 StateObject.prototype.parameters = function (opts) {
2993 opts = defaults(opts, { inherit: true, matchingKeys: null });
2994 var inherited = (opts.inherit && this.parent && this.parent.parameters()) || [];
2995 return inherited
2996 .concat(values(this.params))
2997 .filter(function (param) { return !opts.matchingKeys || opts.matchingKeys.hasOwnProperty(param.id); });
2998 };
2999 /**
3000 * Returns a single [[Param]] that is owned by the state
3001 *
3002 * If `opts.inherit` is true, it also searches the ancestor states` [[Param]]s.
3003 * @param id the name of the [[Param]] to return
3004 * @param opts options
3005 */
3006 StateObject.prototype.parameter = function (id, opts) {
3007 if (opts === void 0) { opts = {}; }
3008 return ((this.url && this.url.parameter(id, opts)) ||
3009 find(values(this.params), propEq('id', id)) ||
3010 (opts.inherit && this.parent && this.parent.parameter(id)));
3011 };
3012 StateObject.prototype.toString = function () {
3013 return this.fqn();
3014 };
3015 /** Predicate which returns true if the object is an class with @State() decorator */
3016 StateObject.isStateClass = function (stateDecl) {
3017 return isFunction(stateDecl) && stateDecl['__uiRouterState'] === true;
3018 };
3019 /** Predicate which returns true if the object is a [[StateDeclaration]] object */
3020 StateObject.isStateDeclaration = function (obj) { return isFunction(obj['$$state']); };
3021 /** Predicate which returns true if the object is an internal [[StateObject]] object */
3022 StateObject.isState = function (obj) { return isObject(obj['__stateObjectCache']); };
3023 return StateObject;
3024 }());
3025
3026 var StateMatcher = /** @class */ (function () {
3027 function StateMatcher(_states) {
3028 this._states = _states;
3029 }
3030 StateMatcher.prototype.isRelative = function (stateName) {
3031 stateName = stateName || '';
3032 return stateName.indexOf('.') === 0 || stateName.indexOf('^') === 0;
3033 };
3034 StateMatcher.prototype.find = function (stateOrName, base, matchGlob) {
3035 if (matchGlob === void 0) { matchGlob = true; }
3036 if (!stateOrName && stateOrName !== '')
3037 return undefined;
3038 var isStr = isString(stateOrName);
3039 var name = isStr ? stateOrName : stateOrName.name;
3040 if (this.isRelative(name))
3041 name = this.resolvePath(name, base);
3042 var state = this._states[name];
3043 if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
3044 return state;
3045 }
3046 else if (isStr && matchGlob) {
3047 var _states = values(this._states);
3048 var matches = _states.filter(function (_state) { return _state.__stateObjectCache.nameGlob && _state.__stateObjectCache.nameGlob.matches(name); });
3049 if (matches.length > 1) {
3050 safeConsole.error("stateMatcher.find: Found multiple matches for " + name + " using glob: ", matches.map(function (match) { return match.name; }));
3051 }
3052 return matches[0];
3053 }
3054 return undefined;
3055 };
3056 StateMatcher.prototype.resolvePath = function (name, base) {
3057 if (!base)
3058 throw new Error("No reference point given for path '" + name + "'");
3059 var baseState = this.find(base);
3060 var splitName = name.split('.');
3061 var pathLength = splitName.length;
3062 var i = 0, current = baseState;
3063 for (; i < pathLength; i++) {
3064 if (splitName[i] === '' && i === 0) {
3065 current = baseState;
3066 continue;
3067 }
3068 if (splitName[i] === '^') {
3069 if (!current.parent)
3070 throw new Error("Path '" + name + "' not valid for state '" + baseState.name + "'");
3071 current = current.parent;
3072 continue;
3073 }
3074 break;
3075 }
3076 var relName = splitName.slice(i).join('.');
3077 return current.name + (current.name && relName ? '.' : '') + relName;
3078 };
3079 return StateMatcher;
3080 }());
3081
3082 var StateQueueManager = /** @class */ (function () {
3083 function StateQueueManager(router, states, builder, listeners) {
3084 this.router = router;
3085 this.states = states;
3086 this.builder = builder;
3087 this.listeners = listeners;
3088 this.queue = [];
3089 }
3090 StateQueueManager.prototype.dispose = function () {
3091 this.queue = [];
3092 };
3093 StateQueueManager.prototype.register = function (stateDecl) {
3094 var queue = this.queue;
3095 var state = StateObject.create(stateDecl);
3096 var name = state.name;
3097 if (!isString(name))
3098 throw new Error('State must have a valid name');
3099 if (this.states.hasOwnProperty(name) || inArray(queue.map(prop('name')), name))
3100 throw new Error("State '" + name + "' is already defined");
3101 queue.push(state);
3102 this.flush();
3103 return state;
3104 };
3105 StateQueueManager.prototype.flush = function () {
3106 var _this = this;
3107 var _a = this, queue = _a.queue, states = _a.states, builder = _a.builder;
3108 var registered = [], // states that got registered
3109 orphans = [], // states that don't yet have a parent registered
3110 previousQueueLength = {}; // keep track of how long the queue when an orphan was first encountered
3111 var getState = function (name) { return _this.states.hasOwnProperty(name) && _this.states[name]; };
3112 var notifyListeners = function () {
3113 if (registered.length) {
3114 _this.listeners.forEach(function (listener) {
3115 return listener('registered', registered.map(function (s) { return s.self; }));
3116 });
3117 }
3118 };
3119 while (queue.length > 0) {
3120 var state = queue.shift();
3121 var name_1 = state.name;
3122 var result = builder.build(state);
3123 var orphanIdx = orphans.indexOf(state);
3124 if (result) {
3125 var existingState = getState(name_1);
3126 if (existingState && existingState.name === name_1) {
3127 throw new Error("State '" + name_1 + "' is already defined");
3128 }
3129 var existingFutureState = getState(name_1 + '.**');
3130 if (existingFutureState) {
3131 // Remove future state of the same name
3132 this.router.stateRegistry.deregister(existingFutureState);
3133 }
3134 states[name_1] = state;
3135 this.attachRoute(state);
3136 if (orphanIdx >= 0)
3137 orphans.splice(orphanIdx, 1);
3138 registered.push(state);
3139 continue;
3140 }
3141 var prev = previousQueueLength[name_1];
3142 previousQueueLength[name_1] = queue.length;
3143 if (orphanIdx >= 0 && prev === queue.length) {
3144 // Wait until two consecutive iterations where no additional states were dequeued successfully.
3145 // throw new Error(`Cannot register orphaned state '${name}'`);
3146 queue.push(state);
3147 notifyListeners();
3148 return states;
3149 }
3150 else if (orphanIdx < 0) {
3151 orphans.push(state);
3152 }
3153 queue.push(state);
3154 }
3155 notifyListeners();
3156 return states;
3157 };
3158 StateQueueManager.prototype.attachRoute = function (state) {
3159 if (state.abstract || !state.url)
3160 return;
3161 var rulesApi = this.router.urlService.rules;
3162 rulesApi.rule(rulesApi.urlRuleFactory.create(state));
3163 };
3164 return StateQueueManager;
3165 }());
3166
3167 /**
3168 * A registry for all of the application's [[StateDeclaration]]s
3169 *
3170 * This API is found at `router.stateRegistry` ([[UIRouter.stateRegistry]])
3171 */
3172 var StateRegistry = /** @class */ (function () {
3173 /** @internal */
3174 function StateRegistry(router) {
3175 this.router = router;
3176 this.states = {};
3177 /** @internal */
3178 this.listeners = [];
3179 this.matcher = new StateMatcher(this.states);
3180 this.builder = new StateBuilder(this.matcher, router.urlMatcherFactory);
3181 this.stateQueue = new StateQueueManager(router, this.states, this.builder, this.listeners);
3182 this._registerRoot();
3183 }
3184 /** @internal */
3185 StateRegistry.prototype._registerRoot = function () {
3186 var rootStateDef = {
3187 name: '',
3188 url: '^',
3189 views: null,
3190 params: {
3191 '#': { value: null, type: 'hash', dynamic: true },
3192 },
3193 abstract: true,
3194 };
3195 var _root = (this._root = this.stateQueue.register(rootStateDef));
3196 _root.navigable = null;
3197 };
3198 /** @internal */
3199 StateRegistry.prototype.dispose = function () {
3200 var _this = this;
3201 this.stateQueue.dispose();
3202 this.listeners = [];
3203 this.get().forEach(function (state) { return _this.get(state) && _this.deregister(state); });
3204 };
3205 /**
3206 * Listen for a State Registry events
3207 *
3208 * Adds a callback that is invoked when states are registered or deregistered with the StateRegistry.
3209 *
3210 * #### Example:
3211 * ```js
3212 * let allStates = registry.get();
3213 *
3214 * // Later, invoke deregisterFn() to remove the listener
3215 * let deregisterFn = registry.onStatesChanged((event, states) => {
3216 * switch(event) {
3217 * case: 'registered':
3218 * states.forEach(state => allStates.push(state));
3219 * break;
3220 * case: 'deregistered':
3221 * states.forEach(state => {
3222 * let idx = allStates.indexOf(state);
3223 * if (idx !== -1) allStates.splice(idx, 1);
3224 * });
3225 * break;
3226 * }
3227 * });
3228 * ```
3229 *
3230 * @param listener a callback function invoked when the registered states changes.
3231 * The function receives two parameters, `event` and `state`.
3232 * See [[StateRegistryListener]]
3233 * @return a function that deregisters the listener
3234 */
3235 StateRegistry.prototype.onStatesChanged = function (listener) {
3236 this.listeners.push(listener);
3237 return function deregisterListener() {
3238 removeFrom(this.listeners)(listener);
3239 }.bind(this);
3240 };
3241 /**
3242 * Gets the implicit root state
3243 *
3244 * Gets the root of the state tree.
3245 * The root state is implicitly created by UI-Router.
3246 * Note: this returns the internal [[StateObject]] representation, not a [[StateDeclaration]]
3247 *
3248 * @return the root [[StateObject]]
3249 */
3250 StateRegistry.prototype.root = function () {
3251 return this._root;
3252 };
3253 /**
3254 * Adds a state to the registry
3255 *
3256 * Registers a [[StateDeclaration]] or queues it for registration.
3257 *
3258 * Note: a state will be queued if the state's parent isn't yet registered.
3259 *
3260 * @param stateDefinition the definition of the state to register.
3261 * @returns the internal [[StateObject]] object.
3262 * If the state was successfully registered, then the object is fully built (See: [[StateBuilder]]).
3263 * If the state was only queued, then the object is not fully built.
3264 */
3265 StateRegistry.prototype.register = function (stateDefinition) {
3266 return this.stateQueue.register(stateDefinition);
3267 };
3268 /** @internal */
3269 StateRegistry.prototype._deregisterTree = function (state) {
3270 var _this = this;
3271 var all = this.get().map(function (s) { return s.$$state(); });
3272 var getChildren = function (states) {
3273 var _children = all.filter(function (s) { return states.indexOf(s.parent) !== -1; });
3274 return _children.length === 0 ? _children : _children.concat(getChildren(_children));
3275 };
3276 var children = getChildren([state]);
3277 var deregistered = [state].concat(children).reverse();
3278 deregistered.forEach(function (_state) {
3279 var rulesApi = _this.router.urlService.rules;
3280 // Remove URL rule
3281 rulesApi
3282 .rules()
3283 .filter(propEq('state', _state))
3284 .forEach(function (rule) { return rulesApi.removeRule(rule); });
3285 // Remove state from registry
3286 delete _this.states[_state.name];
3287 });
3288 return deregistered;
3289 };
3290 /**
3291 * Removes a state from the registry
3292 *
3293 * This removes a state from the registry.
3294 * If the state has children, they are are also removed from the registry.
3295 *
3296 * @param stateOrName the state's name or object representation
3297 * @returns {StateObject[]} a list of removed states
3298 */
3299 StateRegistry.prototype.deregister = function (stateOrName) {
3300 var _state = this.get(stateOrName);
3301 if (!_state)
3302 throw new Error("Can't deregister state; not found: " + stateOrName);
3303 var deregisteredStates = this._deregisterTree(_state.$$state());
3304 this.listeners.forEach(function (listener) {
3305 return listener('deregistered', deregisteredStates.map(function (s) { return s.self; }));
3306 });
3307 return deregisteredStates;
3308 };
3309 StateRegistry.prototype.get = function (stateOrName, base) {
3310 var _this = this;
3311 if (arguments.length === 0)
3312 return Object.keys(this.states).map(function (name) { return _this.states[name].self; });
3313 var found = this.matcher.find(stateOrName, base);
3314 return (found && found.self) || null;
3315 };
3316 /**
3317 * Registers a [[BuilderFunction]] for a specific [[StateObject]] property (e.g., `parent`, `url`, or `path`).
3318 * More than one BuilderFunction can be registered for a given property.
3319 *
3320 * The BuilderFunction(s) will be used to define the property on any subsequently built [[StateObject]] objects.
3321 *
3322 * @param property The name of the State property being registered for.
3323 * @param builderFunction The BuilderFunction which will be used to build the State property
3324 * @returns a function which deregisters the BuilderFunction
3325 */
3326 StateRegistry.prototype.decorator = function (property, builderFunction) {
3327 return this.builder.builder(property, builderFunction);
3328 };
3329 return StateRegistry;
3330 }());
3331
3332 (function (TransitionHookPhase) {
3333 TransitionHookPhase[TransitionHookPhase["CREATE"] = 0] = "CREATE";
3334 TransitionHookPhase[TransitionHookPhase["BEFORE"] = 1] = "BEFORE";
3335 TransitionHookPhase[TransitionHookPhase["RUN"] = 2] = "RUN";
3336 TransitionHookPhase[TransitionHookPhase["SUCCESS"] = 3] = "SUCCESS";
3337 TransitionHookPhase[TransitionHookPhase["ERROR"] = 4] = "ERROR";
3338 })(exports.TransitionHookPhase || (exports.TransitionHookPhase = {}));
3339
3340 (function (TransitionHookScope) {
3341 TransitionHookScope[TransitionHookScope["TRANSITION"] = 0] = "TRANSITION";
3342 TransitionHookScope[TransitionHookScope["STATE"] = 1] = "STATE";
3343 })(exports.TransitionHookScope || (exports.TransitionHookScope = {}));
3344
3345 var defaultOptions = {
3346 current: noop,
3347 transition: null,
3348 traceData: {},
3349 bind: null,
3350 };
3351 var TransitionHook = /** @class */ (function () {
3352 function TransitionHook(transition, stateContext, registeredHook, options) {
3353 var _this = this;
3354 this.transition = transition;
3355 this.stateContext = stateContext;
3356 this.registeredHook = registeredHook;
3357 this.options = options;
3358 this.isSuperseded = function () { return _this.type.hookPhase === exports.TransitionHookPhase.RUN && !_this.options.transition.isActive(); };
3359 this.options = defaults(options, defaultOptions);
3360 this.type = registeredHook.eventType;
3361 }
3362 /**
3363 * Chains together an array of TransitionHooks.
3364 *
3365 * Given a list of [[TransitionHook]] objects, chains them together.
3366 * Each hook is invoked after the previous one completes.
3367 *
3368 * #### Example:
3369 * ```js
3370 * var hooks: TransitionHook[] = getHooks();
3371 * let promise: Promise<any> = TransitionHook.chain(hooks);
3372 *
3373 * promise.then(handleSuccess, handleError);
3374 * ```
3375 *
3376 * @param hooks the list of hooks to chain together
3377 * @param waitFor if provided, the chain is `.then()`'ed off this promise
3378 * @returns a `Promise` for sequentially invoking the hooks (in order)
3379 */
3380 TransitionHook.chain = function (hooks, waitFor) {
3381 // Chain the next hook off the previous
3382 var createHookChainR = function (prev, nextHook) { return prev.then(function () { return nextHook.invokeHook(); }); };
3383 return hooks.reduce(createHookChainR, waitFor || services.$q.when());
3384 };
3385 /**
3386 * Invokes all the provided TransitionHooks, in order.
3387 * Each hook's return value is checked.
3388 * If any hook returns a promise, then the rest of the hooks are chained off that promise, and the promise is returned.
3389 * If no hook returns a promise, then all hooks are processed synchronously.
3390 *
3391 * @param hooks the list of TransitionHooks to invoke
3392 * @param doneCallback a callback that is invoked after all the hooks have successfully completed
3393 *
3394 * @returns a promise for the async result, or the result of the callback
3395 */
3396 TransitionHook.invokeHooks = function (hooks, doneCallback) {
3397 for (var idx = 0; idx < hooks.length; idx++) {
3398 var hookResult = hooks[idx].invokeHook();
3399 if (isPromise(hookResult)) {
3400 var remainingHooks = hooks.slice(idx + 1);
3401 return TransitionHook.chain(remainingHooks, hookResult).then(doneCallback);
3402 }
3403 }
3404 return doneCallback();
3405 };
3406 /**
3407 * Run all TransitionHooks, ignoring their return value.
3408 */
3409 TransitionHook.runAllHooks = function (hooks) {
3410 hooks.forEach(function (hook) { return hook.invokeHook(); });
3411 };
3412 TransitionHook.prototype.logError = function (err) {
3413 this.transition.router.stateService.defaultErrorHandler()(err);
3414 };
3415 TransitionHook.prototype.invokeHook = function () {
3416 var _this = this;
3417 var hook = this.registeredHook;
3418 if (hook._deregistered)
3419 return;
3420 var notCurrent = this.getNotCurrentRejection();
3421 if (notCurrent)
3422 return notCurrent;
3423 var options = this.options;
3424 trace.traceHookInvocation(this, this.transition, options);
3425 var invokeCallback = function () { return hook.callback.call(options.bind, _this.transition, _this.stateContext); };
3426 var normalizeErr = function (err) { return Rejection.normalize(err).toPromise(); };
3427 var handleError = function (err) { return hook.eventType.getErrorHandler(_this)(err); };
3428 var handleResult = function (result) { return hook.eventType.getResultHandler(_this)(result); };
3429 try {
3430 var result = invokeCallback();
3431 if (!this.type.synchronous && isPromise(result)) {
3432 return result.catch(normalizeErr).then(handleResult, handleError);
3433 }
3434 else {
3435 return handleResult(result);
3436 }
3437 }
3438 catch (err) {
3439 // If callback throws (synchronously)
3440 return handleError(Rejection.normalize(err));
3441 }
3442 finally {
3443 if (hook.invokeLimit && ++hook.invokeCount >= hook.invokeLimit) {
3444 hook.deregister();
3445 }
3446 }
3447 };
3448 /**
3449 * This method handles the return value of a Transition Hook.
3450 *
3451 * A hook can return false (cancel), a TargetState (redirect),
3452 * or a promise (which may later resolve to false or a redirect)
3453 *
3454 * This also handles "transition superseded" -- when a new transition
3455 * was started while the hook was still running
3456 */
3457 TransitionHook.prototype.handleHookResult = function (result) {
3458 var _this = this;
3459 var notCurrent = this.getNotCurrentRejection();
3460 if (notCurrent)
3461 return notCurrent;
3462 // Hook returned a promise
3463 if (isPromise(result)) {
3464 // Wait for the promise, then reprocess with the resulting value
3465 return result.then(function (val) { return _this.handleHookResult(val); });
3466 }
3467 trace.traceHookResult(result, this.transition, this.options);
3468 // Hook returned false
3469 if (result === false) {
3470 // Abort this Transition
3471 return Rejection.aborted('Hook aborted transition').toPromise();
3472 }
3473 var isTargetState = is(TargetState);
3474 // hook returned a TargetState
3475 if (isTargetState(result)) {
3476 // Halt the current Transition and redirect (a new Transition) to the TargetState.
3477 return Rejection.redirected(result).toPromise();
3478 }
3479 };
3480 /**
3481 * Return a Rejection promise if the transition is no longer current due
3482 * to a stopped router (disposed), or a new transition has started and superseded this one.
3483 */
3484 TransitionHook.prototype.getNotCurrentRejection = function () {
3485 var router = this.transition.router;
3486 // The router is stopped
3487 if (router._disposed) {
3488 return Rejection.aborted("UIRouter instance #" + router.$id + " has been stopped (disposed)").toPromise();
3489 }
3490 if (this.transition._aborted) {
3491 return Rejection.aborted().toPromise();
3492 }
3493 // This transition is no longer current.
3494 // Another transition started while this hook was still running.
3495 if (this.isSuperseded()) {
3496 // Abort this transition
3497 return Rejection.superseded(this.options.current()).toPromise();
3498 }
3499 };
3500 TransitionHook.prototype.toString = function () {
3501 var _a = this, options = _a.options, registeredHook = _a.registeredHook;
3502 var event = parse('traceData.hookType')(options) || 'internal', context = parse('traceData.context.state.name')(options) || parse('traceData.context')(options) || 'unknown', name = fnToString(registeredHook.callback);
3503 return event + " context: " + context + ", " + maxLength(200, name);
3504 };
3505 /**
3506 * These GetResultHandler(s) are used by [[invokeHook]] below
3507 * Each HookType chooses a GetResultHandler (See: [[TransitionService._defineCoreEvents]])
3508 */
3509 TransitionHook.HANDLE_RESULT = function (hook) { return function (result) {
3510 return hook.handleHookResult(result);
3511 }; };
3512 /**
3513 * If the result is a promise rejection, log it.
3514 * Otherwise, ignore the result.
3515 */
3516 TransitionHook.LOG_REJECTED_RESULT = function (hook) { return function (result) {
3517 isPromise(result) && result.catch(function (err) { return hook.logError(Rejection.normalize(err)); });
3518 return undefined;
3519 }; };
3520 /**
3521 * These GetErrorHandler(s) are used by [[invokeHook]] below
3522 * Each HookType chooses a GetErrorHandler (See: [[TransitionService._defineCoreEvents]])
3523 */
3524 TransitionHook.LOG_ERROR = function (hook) { return function (error) { return hook.logError(error); }; };
3525 TransitionHook.REJECT_ERROR = function (hook) { return function (error) { return silentRejection(error); }; };
3526 TransitionHook.THROW_ERROR = function (hook) { return function (error) {
3527 throw error;
3528 }; };
3529 return TransitionHook;
3530 }());
3531
3532 /**
3533 * Determines if the given state matches the matchCriteria
3534 *
3535 * @internal
3536 *
3537 * @param state a State Object to test against
3538 * @param criterion
3539 * - If a string, matchState uses the string as a glob-matcher against the state name
3540 * - If an array (of strings), matchState uses each string in the array as a glob-matchers against the state name
3541 * and returns a positive match if any of the globs match.
3542 * - If a function, matchState calls the function with the state and returns true if the function's result is truthy.
3543 * @returns {boolean}
3544 */
3545 function matchState(state, criterion, transition) {
3546 var toMatch = isString(criterion) ? [criterion] : criterion;
3547 function matchGlobs(_state) {
3548 var globStrings = toMatch;
3549 for (var i = 0; i < globStrings.length; i++) {
3550 var glob = new Glob(globStrings[i]);
3551 if ((glob && glob.matches(_state.name)) || (!glob && globStrings[i] === _state.name)) {
3552 return true;
3553 }
3554 }
3555 return false;
3556 }
3557 var matchFn = (isFunction(toMatch) ? toMatch : matchGlobs);
3558 return !!matchFn(state, transition);
3559 }
3560 /**
3561 * The registration data for a registered transition hook
3562 */
3563 var RegisteredHook = /** @class */ (function () {
3564 function RegisteredHook(tranSvc, eventType, callback, matchCriteria, removeHookFromRegistry, options) {
3565 if (options === void 0) { options = {}; }
3566 this.tranSvc = tranSvc;
3567 this.eventType = eventType;
3568 this.callback = callback;
3569 this.matchCriteria = matchCriteria;
3570 this.removeHookFromRegistry = removeHookFromRegistry;
3571 this.invokeCount = 0;
3572 this._deregistered = false;
3573 this.priority = options.priority || 0;
3574 this.bind = options.bind || null;
3575 this.invokeLimit = options.invokeLimit;
3576 }
3577 /**
3578 * Gets the matching [[PathNode]]s
3579 *
3580 * Given an array of [[PathNode]]s, and a [[HookMatchCriterion]], returns an array containing
3581 * the [[PathNode]]s that the criteria matches, or `null` if there were no matching nodes.
3582 *
3583 * Returning `null` is significant to distinguish between the default
3584 * "match-all criterion value" of `true` compared to a `() => true` function,
3585 * when the nodes is an empty array.
3586 *
3587 * This is useful to allow a transition match criteria of `entering: true`
3588 * to still match a transition, even when `entering === []`. Contrast that
3589 * with `entering: (state) => true` which only matches when a state is actually
3590 * being entered.
3591 */
3592 RegisteredHook.prototype._matchingNodes = function (nodes, criterion, transition) {
3593 if (criterion === true)
3594 return nodes;
3595 var matching = nodes.filter(function (node) { return matchState(node.state, criterion, transition); });
3596 return matching.length ? matching : null;
3597 };
3598 /**
3599 * Gets the default match criteria (all `true`)
3600 *
3601 * Returns an object which has all the criteria match paths as keys and `true` as values, i.e.:
3602 *
3603 * ```js
3604 * {
3605 * to: true,
3606 * from: true,
3607 * entering: true,
3608 * exiting: true,
3609 * retained: true,
3610 * }
3611 */
3612 RegisteredHook.prototype._getDefaultMatchCriteria = function () {
3613 return mapObj(this.tranSvc._pluginapi._getPathTypes(), function () { return true; });
3614 };
3615 /**
3616 * Gets matching nodes as [[IMatchingNodes]]
3617 *
3618 * Create a IMatchingNodes object from the TransitionHookTypes that is roughly equivalent to:
3619 *
3620 * ```js
3621 * let matches: IMatchingNodes = {
3622 * to: _matchingNodes([tail(treeChanges.to)], mc.to),
3623 * from: _matchingNodes([tail(treeChanges.from)], mc.from),
3624 * exiting: _matchingNodes(treeChanges.exiting, mc.exiting),
3625 * retained: _matchingNodes(treeChanges.retained, mc.retained),
3626 * entering: _matchingNodes(treeChanges.entering, mc.entering),
3627 * };
3628 * ```
3629 */
3630 RegisteredHook.prototype._getMatchingNodes = function (treeChanges, transition) {
3631 var _this = this;
3632 var criteria = extend(this._getDefaultMatchCriteria(), this.matchCriteria);
3633 var paths = values(this.tranSvc._pluginapi._getPathTypes());
3634 return paths.reduce(function (mn, pathtype) {
3635 // STATE scope criteria matches against every node in the path.
3636 // TRANSITION scope criteria matches against only the last node in the path
3637 var isStateHook = pathtype.scope === exports.TransitionHookScope.STATE;
3638 var path = treeChanges[pathtype.name] || [];
3639 var nodes = isStateHook ? path : [tail(path)];
3640 mn[pathtype.name] = _this._matchingNodes(nodes, criteria[pathtype.name], transition);
3641 return mn;
3642 }, {});
3643 };
3644 /**
3645 * Determines if this hook's [[matchCriteria]] match the given [[TreeChanges]]
3646 *
3647 * @returns an IMatchingNodes object, or null. If an IMatchingNodes object is returned, its values
3648 * are the matching [[PathNode]]s for each [[HookMatchCriterion]] (to, from, exiting, retained, entering)
3649 */
3650 RegisteredHook.prototype.matches = function (treeChanges, transition) {
3651 var matches = this._getMatchingNodes(treeChanges, transition);
3652 // Check if all the criteria matched the TreeChanges object
3653 var allMatched = values(matches).every(identity);
3654 return allMatched ? matches : null;
3655 };
3656 RegisteredHook.prototype.deregister = function () {
3657 this.removeHookFromRegistry(this);
3658 this._deregistered = true;
3659 };
3660 return RegisteredHook;
3661 }());
3662 /** Return a registration function of the requested type. */
3663 function makeEvent(registry, transitionService, eventType) {
3664 // Create the object which holds the registered transition hooks.
3665 var _registeredHooks = (registry._registeredHooks = registry._registeredHooks || {});
3666 var hooks = (_registeredHooks[eventType.name] = []);
3667 var removeHookFn = removeFrom(hooks);
3668 // Create hook registration function on the IHookRegistry for the event
3669 registry[eventType.name] = hookRegistrationFn;
3670 function hookRegistrationFn(matchObject, callback, options) {
3671 if (options === void 0) { options = {}; }
3672 var registeredHook = new RegisteredHook(transitionService, eventType, callback, matchObject, removeHookFn, options);
3673 hooks.push(registeredHook);
3674 return registeredHook.deregister.bind(registeredHook);
3675 }
3676 return hookRegistrationFn;
3677 }
3678
3679 /**
3680 * This class returns applicable TransitionHooks for a specific Transition instance.
3681 *
3682 * Hooks ([[RegisteredHook]]) may be registered globally, e.g., $transitions.onEnter(...), or locally, e.g.
3683 * myTransition.onEnter(...). The HookBuilder finds matching RegisteredHooks (where the match criteria is
3684 * determined by the type of hook)
3685 *
3686 * The HookBuilder also converts RegisteredHooks objects to TransitionHook objects, which are used to run a Transition.
3687 *
3688 * The HookBuilder constructor is given the $transitions service and a Transition instance. Thus, a HookBuilder
3689 * instance may only be used for one specific Transition object. (side note: the _treeChanges accessor is private
3690 * in the Transition class, so we must also provide the Transition's _treeChanges)
3691 */
3692 var HookBuilder = /** @class */ (function () {
3693 function HookBuilder(transition) {
3694 this.transition = transition;
3695 }
3696 HookBuilder.prototype.buildHooksForPhase = function (phase) {
3697 var _this = this;
3698 var $transitions = this.transition.router.transitionService;
3699 return $transitions._pluginapi
3700 ._getEvents(phase)
3701 .map(function (type) { return _this.buildHooks(type); })
3702 .reduce(unnestR, [])
3703 .filter(identity);
3704 };
3705 /**
3706 * Returns an array of newly built TransitionHook objects.
3707 *
3708 * - Finds all RegisteredHooks registered for the given `hookType` which matched the transition's [[TreeChanges]].
3709 * - Finds [[PathNode]] (or `PathNode[]`) to use as the TransitionHook context(s)
3710 * - For each of the [[PathNode]]s, creates a TransitionHook
3711 *
3712 * @param hookType the type of the hook registration function, e.g., 'onEnter', 'onFinish'.
3713 */
3714 HookBuilder.prototype.buildHooks = function (hookType) {
3715 var transition = this.transition;
3716 var treeChanges = transition.treeChanges();
3717 // Find all the matching registered hooks for a given hook type
3718 var matchingHooks = this.getMatchingHooks(hookType, treeChanges, transition);
3719 if (!matchingHooks)
3720 return [];
3721 var baseHookOptions = {
3722 transition: transition,
3723 current: transition.options().current,
3724 };
3725 var makeTransitionHooks = function (hook) {
3726 // Fetch the Nodes that caused this hook to match.
3727 var matches = hook.matches(treeChanges, transition);
3728 // Select the PathNode[] that will be used as TransitionHook context objects
3729 var matchingNodes = matches[hookType.criteriaMatchPath.name];
3730 // Return an array of HookTuples
3731 return matchingNodes.map(function (node) {
3732 var _options = extend({
3733 bind: hook.bind,
3734 traceData: { hookType: hookType.name, context: node },
3735 }, baseHookOptions);
3736 var state = hookType.criteriaMatchPath.scope === exports.TransitionHookScope.STATE ? node.state.self : null;
3737 var transitionHook = new TransitionHook(transition, state, hook, _options);
3738 return { hook: hook, node: node, transitionHook: transitionHook };
3739 });
3740 };
3741 return matchingHooks
3742 .map(makeTransitionHooks)
3743 .reduce(unnestR, [])
3744 .sort(tupleSort(hookType.reverseSort))
3745 .map(function (tuple) { return tuple.transitionHook; });
3746 };
3747 /**
3748 * Finds all RegisteredHooks from:
3749 * - The Transition object instance hook registry
3750 * - The TransitionService ($transitions) global hook registry
3751 *
3752 * which matched:
3753 * - the eventType
3754 * - the matchCriteria (to, from, exiting, retained, entering)
3755 *
3756 * @returns an array of matched [[RegisteredHook]]s
3757 */
3758 HookBuilder.prototype.getMatchingHooks = function (hookType, treeChanges, transition) {
3759 var isCreate = hookType.hookPhase === exports.TransitionHookPhase.CREATE;
3760 // Instance and Global hook registries
3761 var $transitions = this.transition.router.transitionService;
3762 var registries = isCreate ? [$transitions] : [this.transition, $transitions];
3763 return registries
3764 .map(function (reg) { return reg.getHooks(hookType.name); }) // Get named hooks from registries
3765 .filter(assertPredicate(isArray, "broken event named: " + hookType.name)) // Sanity check
3766 .reduce(unnestR, []) // Un-nest RegisteredHook[][] to RegisteredHook[] array
3767 .filter(function (hook) { return hook.matches(treeChanges, transition); }); // Only those satisfying matchCriteria
3768 };
3769 return HookBuilder;
3770 }());
3771 /**
3772 * A factory for a sort function for HookTuples.
3773 *
3774 * The sort function first compares the PathNode depth (how deep in the state tree a node is), then compares
3775 * the EventHook priority.
3776 *
3777 * @param reverseDepthSort a boolean, when true, reverses the sort order for the node depth
3778 * @returns a tuple sort function
3779 */
3780 function tupleSort(reverseDepthSort) {
3781 if (reverseDepthSort === void 0) { reverseDepthSort = false; }
3782 return function nodeDepthThenPriority(l, r) {
3783 var factor = reverseDepthSort ? -1 : 1;
3784 var depthDelta = (l.node.state.path.length - r.node.state.path.length) * factor;
3785 return depthDelta !== 0 ? depthDelta : r.hook.priority - l.hook.priority;
3786 };
3787 }
3788
3789 /** @internal */
3790 var stateSelf = prop('self');
3791 /**
3792 * Represents a transition between two states.
3793 *
3794 * When navigating to a state, we are transitioning **from** the current state **to** the new state.
3795 *
3796 * This object contains all contextual information about the to/from states, parameters, resolves.
3797 * It has information about all states being entered and exited as a result of the transition.
3798 */
3799 var Transition = /** @class */ (function () {
3800 /**
3801 * Creates a new Transition object.
3802 *
3803 * If the target state is not valid, an error is thrown.
3804 *
3805 * @internal
3806 *
3807 * @param fromPath The path of [[PathNode]]s from which the transition is leaving. The last node in the `fromPath`
3808 * encapsulates the "from state".
3809 * @param targetState The target state and parameters being transitioned to (also, the transition options)
3810 * @param router The [[UIRouter]] instance
3811 * @internal
3812 */
3813 function Transition(fromPath, targetState, router) {
3814 var _this = this;
3815 /** @internal */
3816 this._deferred = services.$q.defer();
3817 /**
3818 * This promise is resolved or rejected based on the outcome of the Transition.
3819 *
3820 * When the transition is successful, the promise is resolved
3821 * When the transition is unsuccessful, the promise is rejected with the [[Rejection]] or javascript error
3822 */
3823 this.promise = this._deferred.promise;
3824 /** @internal Holds the hook registration functions such as those passed to Transition.onStart() */
3825 this._registeredHooks = {};
3826 /** @internal */
3827 this._hookBuilder = new HookBuilder(this);
3828 /** Checks if this transition is currently active/running. */
3829 this.isActive = function () { return _this.router.globals.transition === _this; };
3830 this.router = router;
3831 this._targetState = targetState;
3832 if (!targetState.valid()) {
3833 throw new Error(targetState.error());
3834 }
3835 // current() is assumed to come from targetState.options, but provide a naive implementation otherwise.
3836 this._options = extend({ current: val(this) }, targetState.options());
3837 this.$id = router.transitionService._transitionCount++;
3838 var toPath = PathUtils.buildToPath(fromPath, targetState);
3839 this._treeChanges = PathUtils.treeChanges(fromPath, toPath, this._options.reloadState);
3840 this.createTransitionHookRegFns();
3841 var onCreateHooks = this._hookBuilder.buildHooksForPhase(exports.TransitionHookPhase.CREATE);
3842 TransitionHook.invokeHooks(onCreateHooks, function () { return null; });
3843 this.applyViewConfigs(router);
3844 }
3845 /** @internal */
3846 Transition.prototype.onBefore = function (criteria, callback, options) {
3847 return;
3848 };
3849 /** @inheritdoc */
3850 Transition.prototype.onStart = function (criteria, callback, options) {
3851 return;
3852 };
3853 /** @inheritdoc */
3854 Transition.prototype.onExit = function (criteria, callback, options) {
3855 return;
3856 };
3857 /** @inheritdoc */
3858 Transition.prototype.onRetain = function (criteria, callback, options) {
3859 return;
3860 };
3861 /** @inheritdoc */
3862 Transition.prototype.onEnter = function (criteria, callback, options) {
3863 return;
3864 };
3865 /** @inheritdoc */
3866 Transition.prototype.onFinish = function (criteria, callback, options) {
3867 return;
3868 };
3869 /** @inheritdoc */
3870 Transition.prototype.onSuccess = function (criteria, callback, options) {
3871 return;
3872 };
3873 /** @inheritdoc */
3874 Transition.prototype.onError = function (criteria, callback, options) {
3875 return;
3876 };
3877 /** @internal
3878 * Creates the transition-level hook registration functions
3879 * (which can then be used to register hooks)
3880 */
3881 Transition.prototype.createTransitionHookRegFns = function () {
3882 var _this = this;
3883 this.router.transitionService._pluginapi
3884 ._getEvents()
3885 .filter(function (type) { return type.hookPhase !== exports.TransitionHookPhase.CREATE; })
3886 .forEach(function (type) { return makeEvent(_this, _this.router.transitionService, type); });
3887 };
3888 /** @internal */
3889 Transition.prototype.getHooks = function (hookName) {
3890 return this._registeredHooks[hookName];
3891 };
3892 Transition.prototype.applyViewConfigs = function (router) {
3893 var enteringStates = this._treeChanges.entering.map(function (node) { return node.state; });
3894 PathUtils.applyViewConfigs(router.transitionService.$view, this._treeChanges.to, enteringStates);
3895 };
3896 /**
3897 * @internal
3898 * @returns the internal from [State] object
3899 */
3900 Transition.prototype.$from = function () {
3901 return tail(this._treeChanges.from).state;
3902 };
3903 /**
3904 * @internal
3905 * @returns the internal to [State] object
3906 */
3907 Transition.prototype.$to = function () {
3908 return tail(this._treeChanges.to).state;
3909 };
3910 /**
3911 * Returns the "from state"
3912 *
3913 * Returns the state that the transition is coming *from*.
3914 *
3915 * @returns The state declaration object for the Transition's ("from state").
3916 */
3917 Transition.prototype.from = function () {
3918 return this.$from().self;
3919 };
3920 /**
3921 * Returns the "to state"
3922 *
3923 * Returns the state that the transition is going *to*.
3924 *
3925 * @returns The state declaration object for the Transition's target state ("to state").
3926 */
3927 Transition.prototype.to = function () {
3928 return this.$to().self;
3929 };
3930 /**
3931 * Gets the Target State
3932 *
3933 * A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]] as a single object.
3934 *
3935 * @returns the [[TargetState]] of this Transition
3936 */
3937 Transition.prototype.targetState = function () {
3938 return this._targetState;
3939 };
3940 /**
3941 * Determines whether two transitions are equivalent.
3942 * @deprecated
3943 */
3944 Transition.prototype.is = function (compare) {
3945 if (compare instanceof Transition) {
3946 // TODO: Also compare parameters
3947 return this.is({ to: compare.$to().name, from: compare.$from().name });
3948 }
3949 return !((compare.to && !matchState(this.$to(), compare.to, this)) ||
3950 (compare.from && !matchState(this.$from(), compare.from, this)));
3951 };
3952 Transition.prototype.params = function (pathname) {
3953 if (pathname === void 0) { pathname = 'to'; }
3954 return Object.freeze(this._treeChanges[pathname].map(prop('paramValues')).reduce(mergeR, {}));
3955 };
3956 Transition.prototype.paramsChanged = function () {
3957 var fromParams = this.params('from');
3958 var toParams = this.params('to');
3959 // All the parameters declared on both the "to" and "from" paths
3960 var allParamDescriptors = []
3961 .concat(this._treeChanges.to)
3962 .concat(this._treeChanges.from)
3963 .map(function (pathNode) { return pathNode.paramSchema; })
3964 .reduce(flattenR, [])
3965 .reduce(uniqR, []);
3966 var changedParamDescriptors = Param.changed(allParamDescriptors, fromParams, toParams);
3967 return changedParamDescriptors.reduce(function (changedValues, descriptor) {
3968 changedValues[descriptor.id] = toParams[descriptor.id];
3969 return changedValues;
3970 }, {});
3971 };
3972 /**
3973 * Creates a [[UIInjector]] Dependency Injector
3974 *
3975 * Returns a Dependency Injector for the Transition's target state (to state).
3976 * The injector provides resolve values which the target state has access to.
3977 *
3978 * The `UIInjector` can also provide values from the native root/global injector (ng1/ng2).
3979 *
3980 * #### Example:
3981 * ```js
3982 * .onEnter({ entering: 'myState' }, trans => {
3983 * var myResolveValue = trans.injector().get('myResolve');
3984 * // Inject a global service from the global/native injector (if it exists)
3985 * var MyService = trans.injector().get('MyService');
3986 * })
3987 * ```
3988 *
3989 * In some cases (such as `onBefore`), you may need access to some resolve data but it has not yet been fetched.
3990 * You can use [[UIInjector.getAsync]] to get a promise for the data.
3991 * #### Example:
3992 * ```js
3993 * .onBefore({}, trans => {
3994 * return trans.injector().getAsync('myResolve').then(myResolveValue =>
3995 * return myResolveValue !== 'ABORT';
3996 * });
3997 * });
3998 * ```
3999 *
4000 * If a `state` is provided, the injector that is returned will be limited to resolve values that the provided state has access to.
4001 * This can be useful if both a parent state `foo` and a child state `foo.bar` have both defined a resolve such as `data`.
4002 * #### Example:
4003 * ```js
4004 * .onEnter({ to: 'foo.bar' }, trans => {
4005 * // returns result of `foo` state's `myResolve` resolve
4006 * // even though `foo.bar` also has a `myResolve` resolve
4007 * var fooData = trans.injector('foo').get('myResolve');
4008 * });
4009 * ```
4010 *
4011 * If you need resolve data from the exiting states, pass `'from'` as `pathName`.
4012 * The resolve data from the `from` path will be returned.
4013 * #### Example:
4014 * ```js
4015 * .onExit({ exiting: 'foo.bar' }, trans => {
4016 * // Gets the resolve value of `myResolve` from the state being exited
4017 * var fooData = trans.injector(null, 'from').get('myResolve');
4018 * });
4019 * ```
4020 *
4021 *
4022 * @param state Limits the resolves provided to only the resolves the provided state has access to.
4023 * @param pathName Default: `'to'`: Chooses the path for which to create the injector. Use this to access resolves for `exiting` states.
4024 *
4025 * @returns a [[UIInjector]]
4026 */
4027 Transition.prototype.injector = function (state, pathName) {
4028 if (pathName === void 0) { pathName = 'to'; }
4029 var path = this._treeChanges[pathName];
4030 if (state)
4031 path = PathUtils.subPath(path, function (node) { return node.state === state || node.state.name === state; });
4032 return new ResolveContext(path).injector();
4033 };
4034 /**
4035 * Gets all available resolve tokens (keys)
4036 *
4037 * This method can be used in conjunction with [[injector]] to inspect the resolve values
4038 * available to the Transition.
4039 *
4040 * This returns all the tokens defined on [[StateDeclaration.resolve]] blocks, for the states
4041 * in the Transition's [[TreeChanges.to]] path.
4042 *
4043 * #### Example:
4044 * This example logs all resolve values
4045 * ```js
4046 * let tokens = trans.getResolveTokens();
4047 * tokens.forEach(token => console.log(token + " = " + trans.injector().get(token)));
4048 * ```
4049 *
4050 * #### Example:
4051 * This example creates promises for each resolve value.
4052 * This triggers fetches of resolves (if any have not yet been fetched).
4053 * When all promises have all settled, it logs the resolve values.
4054 * ```js
4055 * let tokens = trans.getResolveTokens();
4056 * let promise = tokens.map(token => trans.injector().getAsync(token));
4057 * Promise.all(promises).then(values => console.log("Resolved values: " + values));
4058 * ```
4059 *
4060 * Note: Angular 1 users whould use `$q.all()`
4061 *
4062 * @param pathname resolve context's path name (e.g., `to` or `from`)
4063 *
4064 * @returns an array of resolve tokens (keys)
4065 */
4066 Transition.prototype.getResolveTokens = function (pathname) {
4067 if (pathname === void 0) { pathname = 'to'; }
4068 return new ResolveContext(this._treeChanges[pathname]).getTokens();
4069 };
4070 /**
4071 * Dynamically adds a new [[Resolvable]] (i.e., [[StateDeclaration.resolve]]) to this transition.
4072 *
4073 * Allows a transition hook to dynamically add a Resolvable to this Transition.
4074 *
4075 * Use the [[Transition.injector]] to retrieve the resolved data in subsequent hooks ([[UIInjector.get]]).
4076 *
4077 * If a `state` argument is provided, the Resolvable is processed when that state is being entered.
4078 * If no `state` is provided then the root state is used.
4079 * If the given `state` has already been entered, the Resolvable is processed when any child state is entered.
4080 * If no child states will be entered, the Resolvable is processed during the `onFinish` phase of the Transition.
4081 *
4082 * The `state` argument also scopes the resolved data.
4083 * The resolved data is available from the injector for that `state` and any children states.
4084 *
4085 * #### Example:
4086 * ```js
4087 * transitionService.onBefore({}, transition => {
4088 * transition.addResolvable({
4089 * token: 'myResolve',
4090 * deps: ['MyService'],
4091 * resolveFn: myService => myService.getData()
4092 * });
4093 * });
4094 * ```
4095 *
4096 * @param resolvable a [[ResolvableLiteral]] object (or a [[Resolvable]])
4097 * @param state the state in the "to path" which should receive the new resolve (otherwise, the root state)
4098 */
4099 Transition.prototype.addResolvable = function (resolvable, state) {
4100 if (state === void 0) { state = ''; }
4101 resolvable = is(Resolvable)(resolvable) ? resolvable : new Resolvable(resolvable);
4102 var stateName = typeof state === 'string' ? state : state.name;
4103 var topath = this._treeChanges.to;
4104 var targetNode = find(topath, function (node) { return node.state.name === stateName; });
4105 var resolveContext = new ResolveContext(topath);
4106 resolveContext.addResolvables([resolvable], targetNode.state);
4107 };
4108 /**
4109 * Gets the transition from which this transition was redirected.
4110 *
4111 * If the current transition is a redirect, this method returns the transition that was redirected.
4112 *
4113 * #### Example:
4114 * ```js
4115 * let transitionA = $state.go('A').transition
4116 * transitionA.onStart({}, () => $state.target('B'));
4117 * $transitions.onSuccess({ to: 'B' }, (trans) => {
4118 * trans.to().name === 'B'; // true
4119 * trans.redirectedFrom() === transitionA; // true
4120 * });
4121 * ```
4122 *
4123 * @returns The previous Transition, or null if this Transition is not the result of a redirection
4124 */
4125 Transition.prototype.redirectedFrom = function () {
4126 return this._options.redirectedFrom || null;
4127 };
4128 /**
4129 * Gets the original transition in a redirect chain
4130 *
4131 * A transition might belong to a long chain of multiple redirects.
4132 * This method walks the [[redirectedFrom]] chain back to the original (first) transition in the chain.
4133 *
4134 * #### Example:
4135 * ```js
4136 * // states
4137 * registry.register({ name: 'A', redirectTo: 'B' });
4138 * registry.register({ name: 'B', redirectTo: 'C' });
4139 * registry.register({ name: 'C', redirectTo: 'D' });
4140 * registry.register({ name: 'D' });
4141 *
4142 * let transitionA = $state.go('A').transition
4143 *
4144 * $transitions.onSuccess({ to: 'D' }, (trans) => {
4145 * trans.to().name === 'D'; // true
4146 * trans.redirectedFrom().to().name === 'C'; // true
4147 * trans.originalTransition() === transitionA; // true
4148 * trans.originalTransition().to().name === 'A'; // true
4149 * });
4150 * ```
4151 *
4152 * @returns The original Transition that started a redirect chain
4153 */
4154 Transition.prototype.originalTransition = function () {
4155 var rf = this.redirectedFrom();
4156 return (rf && rf.originalTransition()) || this;
4157 };
4158 /**
4159 * Get the transition options
4160 *
4161 * @returns the options for this Transition.
4162 */
4163 Transition.prototype.options = function () {
4164 return this._options;
4165 };
4166 /**
4167 * Gets the states being entered.
4168 *
4169 * @returns an array of states that will be entered during this transition.
4170 */
4171 Transition.prototype.entering = function () {
4172 return map(this._treeChanges.entering, prop('state')).map(stateSelf);
4173 };
4174 /**
4175 * Gets the states being exited.
4176 *
4177 * @returns an array of states that will be exited during this transition.
4178 */
4179 Transition.prototype.exiting = function () {
4180 return map(this._treeChanges.exiting, prop('state')).map(stateSelf).reverse();
4181 };
4182 /**
4183 * Gets the states being retained.
4184 *
4185 * @returns an array of states that are already entered from a previous Transition, that will not be
4186 * exited during this Transition
4187 */
4188 Transition.prototype.retained = function () {
4189 return map(this._treeChanges.retained, prop('state')).map(stateSelf);
4190 };
4191 /**
4192 * Get the [[ViewConfig]]s associated with this Transition
4193 *
4194 * Each state can define one or more views (template/controller), which are encapsulated as `ViewConfig` objects.
4195 * This method fetches the `ViewConfigs` for a given path in the Transition (e.g., "to" or "entering").
4196 *
4197 * @param pathname the name of the path to fetch views for:
4198 * (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`)
4199 * @param state If provided, only returns the `ViewConfig`s for a single state in the path
4200 *
4201 * @returns a list of ViewConfig objects for the given path.
4202 */
4203 Transition.prototype.views = function (pathname, state) {
4204 if (pathname === void 0) { pathname = 'entering'; }
4205 var path = this._treeChanges[pathname];
4206 path = !state ? path : path.filter(propEq('state', state));
4207 return path.map(prop('views')).filter(identity).reduce(unnestR, []);
4208 };
4209 Transition.prototype.treeChanges = function (pathname) {
4210 return pathname ? this._treeChanges[pathname] : this._treeChanges;
4211 };
4212 /**
4213 * Creates a new transition that is a redirection of the current one.
4214 *
4215 * This transition can be returned from a [[TransitionService]] hook to
4216 * redirect a transition to a new state and/or set of parameters.
4217 *
4218 * @internal
4219 *
4220 * @returns Returns a new [[Transition]] instance.
4221 */
4222 Transition.prototype.redirect = function (targetState) {
4223 var redirects = 1, trans = this;
4224 // tslint:disable-next-line:no-conditional-assignment
4225 while ((trans = trans.redirectedFrom()) != null) {
4226 if (++redirects > 20)
4227 throw new Error("Too many consecutive Transition redirects (20+)");
4228 }
4229 var redirectOpts = { redirectedFrom: this, source: 'redirect' };
4230 // If the original transition was caused by URL sync, then use { location: 'replace' }
4231 // on the new transition (unless the target state explicitly specifies location: false).
4232 // This causes the original url to be replaced with the url for the redirect target
4233 // so the original url disappears from the browser history.
4234 if (this.options().source === 'url' && targetState.options().location !== false) {
4235 redirectOpts.location = 'replace';
4236 }
4237 var newOptions = extend({}, this.options(), targetState.options(), redirectOpts);
4238 targetState = targetState.withOptions(newOptions, true);
4239 var newTransition = this.router.transitionService.create(this._treeChanges.from, targetState);
4240 var originalEnteringNodes = this._treeChanges.entering;
4241 var redirectEnteringNodes = newTransition._treeChanges.entering;
4242 // --- Re-use resolve data from original transition ---
4243 // When redirecting from a parent state to a child state where the parent parameter values haven't changed
4244 // (because of the redirect), the resolves fetched by the original transition are still valid in the
4245 // redirected transition.
4246 //
4247 // This allows you to define a redirect on a parent state which depends on an async resolve value.
4248 // You can wait for the resolve, then redirect to a child state based on the result.
4249 // The redirected transition does not have to re-fetch the resolve.
4250 // ---------------------------------------------------------
4251 var nodeIsReloading = function (reloadState) { return function (node) {
4252 return reloadState && node.state.includes[reloadState.name];
4253 }; };
4254 // Find any "entering" nodes in the redirect path that match the original path and aren't being reloaded
4255 var matchingEnteringNodes = PathUtils.matching(redirectEnteringNodes, originalEnteringNodes, PathUtils.nonDynamicParams).filter(not(nodeIsReloading(targetState.options().reloadState)));
4256 // Use the existing (possibly pre-resolved) resolvables for the matching entering nodes.
4257 matchingEnteringNodes.forEach(function (node, idx) {
4258 node.resolvables = originalEnteringNodes[idx].resolvables;
4259 });
4260 return newTransition;
4261 };
4262 /** @internal If a transition doesn't exit/enter any states, returns any [[Param]] whose value changed */
4263 Transition.prototype._changedParams = function () {
4264 var tc = this._treeChanges;
4265 /** Return undefined if it's not a "dynamic" transition, for the following reasons */
4266 // If user explicitly wants a reload
4267 if (this._options.reload)
4268 return undefined;
4269 // If any states are exiting or entering
4270 if (tc.exiting.length || tc.entering.length)
4271 return undefined;
4272 // If to/from path lengths differ
4273 if (tc.to.length !== tc.from.length)
4274 return undefined;
4275 // If the to/from paths are different
4276 var pathsDiffer = arrayTuples(tc.to, tc.from)
4277 .map(function (tuple) { return tuple[0].state !== tuple[1].state; })
4278 .reduce(anyTrueR, false);
4279 if (pathsDiffer)
4280 return undefined;
4281 // Find any parameter values that differ
4282 var nodeSchemas = tc.to.map(function (node) { return node.paramSchema; });
4283 var _a = [tc.to, tc.from].map(function (path) { return path.map(function (x) { return x.paramValues; }); }), toValues = _a[0], fromValues = _a[1];
4284 var tuples = arrayTuples(nodeSchemas, toValues, fromValues);
4285 return tuples.map(function (_a) {
4286 var schema = _a[0], toVals = _a[1], fromVals = _a[2];
4287 return Param.changed(schema, toVals, fromVals);
4288 }).reduce(unnestR, []);
4289 };
4290 /**
4291 * Returns true if the transition is dynamic.
4292 *
4293 * A transition is dynamic if no states are entered nor exited, but at least one dynamic parameter has changed.
4294 *
4295 * @returns true if the Transition is dynamic
4296 */
4297 Transition.prototype.dynamic = function () {
4298 var changes = this._changedParams();
4299 return !changes ? false : changes.map(function (x) { return x.dynamic; }).reduce(anyTrueR, false);
4300 };
4301 /**
4302 * Returns true if the transition is ignored.
4303 *
4304 * A transition is ignored if no states are entered nor exited, and no parameter values have changed.
4305 *
4306 * @returns true if the Transition is ignored.
4307 */
4308 Transition.prototype.ignored = function () {
4309 return !!this._ignoredReason();
4310 };
4311 /** @internal */
4312 Transition.prototype._ignoredReason = function () {
4313 var pending = this.router.globals.transition;
4314 var reloadState = this._options.reloadState;
4315 var same = function (pathA, pathB) {
4316 if (pathA.length !== pathB.length)
4317 return false;
4318 var matching = PathUtils.matching(pathA, pathB);
4319 return pathA.length === matching.filter(function (node) { return !reloadState || !node.state.includes[reloadState.name]; }).length;
4320 };
4321 var newTC = this.treeChanges();
4322 var pendTC = pending && pending.treeChanges();
4323 if (pendTC && same(pendTC.to, newTC.to) && same(pendTC.exiting, newTC.exiting))
4324 return 'SameAsPending';
4325 if (newTC.exiting.length === 0 && newTC.entering.length === 0 && same(newTC.from, newTC.to))
4326 return 'SameAsCurrent';
4327 };
4328 /**
4329 * Runs the transition
4330 *
4331 * This method is generally called from the [[StateService.transitionTo]]
4332 *
4333 * @internal
4334 *
4335 * @returns a promise for a successful transition.
4336 */
4337 Transition.prototype.run = function () {
4338 var _this = this;
4339 var runAllHooks = TransitionHook.runAllHooks;
4340 // Gets transition hooks array for the given phase
4341 var getHooksFor = function (phase) { return _this._hookBuilder.buildHooksForPhase(phase); };
4342 // When the chain is complete, then resolve or reject the deferred
4343 var transitionSuccess = function () {
4344 trace.traceSuccess(_this.$to(), _this);
4345 _this.success = true;
4346 _this._deferred.resolve(_this.to());
4347 runAllHooks(getHooksFor(exports.TransitionHookPhase.SUCCESS));
4348 };
4349 var transitionError = function (reason) {
4350 trace.traceError(reason, _this);
4351 _this.success = false;
4352 _this._deferred.reject(reason);
4353 _this._error = reason;
4354 runAllHooks(getHooksFor(exports.TransitionHookPhase.ERROR));
4355 };
4356 var runTransition = function () {
4357 // Wait to build the RUN hook chain until the BEFORE hooks are done
4358 // This allows a BEFORE hook to dynamically add additional RUN hooks via the Transition object.
4359 var allRunHooks = getHooksFor(exports.TransitionHookPhase.RUN);
4360 var done = function () { return services.$q.when(undefined); };
4361 return TransitionHook.invokeHooks(allRunHooks, done);
4362 };
4363 var startTransition = function () {
4364 var globals = _this.router.globals;
4365 globals.lastStartedTransitionId = _this.$id;
4366 globals.transition = _this;
4367 globals.transitionHistory.enqueue(_this);
4368 trace.traceTransitionStart(_this);
4369 return services.$q.when(undefined);
4370 };
4371 var allBeforeHooks = getHooksFor(exports.TransitionHookPhase.BEFORE);
4372 TransitionHook.invokeHooks(allBeforeHooks, startTransition)
4373 .then(runTransition)
4374 .then(transitionSuccess, transitionError);
4375 return this.promise;
4376 };
4377 /**
4378 * Checks if the Transition is valid
4379 *
4380 * @returns true if the Transition is valid
4381 */
4382 Transition.prototype.valid = function () {
4383 return !this.error() || this.success !== undefined;
4384 };
4385 /**
4386 * Aborts this transition
4387 *
4388 * Imperative API to abort a Transition.
4389 * This only applies to Transitions that are not yet complete.
4390 */
4391 Transition.prototype.abort = function () {
4392 // Do not set flag if the transition is already complete
4393 if (isUndefined(this.success)) {
4394 this._aborted = true;
4395 }
4396 };
4397 /**
4398 * The Transition error reason.
4399 *
4400 * If the transition is invalid (and could not be run), returns the reason the transition is invalid.
4401 * If the transition was valid and ran, but was not successful, returns the reason the transition failed.
4402 *
4403 * @returns a transition rejection explaining why the transition is invalid, or the reason the transition failed.
4404 */
4405 Transition.prototype.error = function () {
4406 var state = this.$to();
4407 if (state.self.abstract) {
4408 return Rejection.invalid("Cannot transition to abstract state '" + state.name + "'");
4409 }
4410 var paramDefs = state.parameters();
4411 var values = this.params();
4412 var invalidParams = paramDefs.filter(function (param) { return !param.validates(values[param.id]); });
4413 if (invalidParams.length) {
4414 var invalidValues = invalidParams.map(function (param) { return "[" + param.id + ":" + stringify(values[param.id]) + "]"; }).join(', ');
4415 var detail = "The following parameter values are not valid for state '" + state.name + "': " + invalidValues;
4416 return Rejection.invalid(detail);
4417 }
4418 if (this.success === false)
4419 return this._error;
4420 };
4421 /**
4422 * A string representation of the Transition
4423 *
4424 * @returns A string representation of the Transition
4425 */
4426 Transition.prototype.toString = function () {
4427 var fromStateOrName = this.from();
4428 var toStateOrName = this.to();
4429 var avoidEmptyHash = function (params) {
4430 return params['#'] !== null && params['#'] !== undefined ? params : omit(params, ['#']);
4431 };
4432 // (X) means the to state is invalid.
4433 var id = this.$id, from = isObject(fromStateOrName) ? fromStateOrName.name : fromStateOrName, fromParams = stringify(avoidEmptyHash(this._treeChanges.from.map(prop('paramValues')).reduce(mergeR, {}))), toValid = this.valid() ? '' : '(X) ', to = isObject(toStateOrName) ? toStateOrName.name : toStateOrName, toParams = stringify(avoidEmptyHash(this.params()));
4434 return "Transition#" + id + "( '" + from + "'" + fromParams + " -> " + toValid + "'" + to + "'" + toParams + " )";
4435 };
4436 /** @internal */
4437 Transition.diToken = Transition;
4438 return Transition;
4439 }());
4440
4441 function quoteRegExp(str, param) {
4442 var surroundPattern = ['', ''], result = str.replace(/[\\\[\]\^$*+?.()|{}]/g, '\\$&');
4443 if (!param)
4444 return result;
4445 switch (param.squash) {
4446 case false:
4447 surroundPattern = ['(', ')' + (param.isOptional ? '?' : '')];
4448 break;
4449 case true:
4450 result = result.replace(/\/$/, '');
4451 surroundPattern = ['(?:/(', ')|/)?'];
4452 break;
4453 default:
4454 surroundPattern = ["(" + param.squash + "|", ')?'];
4455 break;
4456 }
4457 return result + surroundPattern[0] + param.type.pattern.source + surroundPattern[1];
4458 }
4459 var memoizeTo = function (obj, _prop, fn) { return (obj[_prop] = obj[_prop] || fn()); };
4460 var splitOnSlash = splitOnDelim('/');
4461 var defaultConfig = {
4462 state: { params: {} },
4463 strict: true,
4464 caseInsensitive: true,
4465 decodeParams: true,
4466 };
4467 /**
4468 * Matches URLs against patterns.
4469 *
4470 * Matches URLs against patterns and extracts named parameters from the path or the search
4471 * part of the URL.
4472 *
4473 * A URL pattern consists of a path pattern, optionally followed by '?' and a list of search (query)
4474 * parameters. Multiple search parameter names are separated by '&'. Search parameters
4475 * do not influence whether or not a URL is matched, but their values are passed through into
4476 * the matched parameters returned by [[UrlMatcher.exec]].
4477 *
4478 * - *Path parameters* are defined using curly brace placeholders (`/somepath/{param}`)
4479 * or colon placeholders (`/somePath/:param`).
4480 *
4481 * - *A parameter RegExp* may be defined for a param after a colon
4482 * (`/somePath/{param:[a-zA-Z0-9]+}`) in a curly brace placeholder.
4483 * The regexp must match for the url to be matched.
4484 * Should the regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
4485 *
4486 * Note: a RegExp parameter will encode its value using either [[ParamTypes.path]] or [[ParamTypes.query]].
4487 *
4488 * - *Custom parameter types* may also be specified after a colon (`/somePath/{param:int}`) in curly brace parameters.
4489 * See [[UrlMatcherFactory.type]] for more information.
4490 *
4491 * - *Catch-all parameters* are defined using an asterisk placeholder (`/somepath/*catchallparam`).
4492 * A catch-all * parameter value will contain the remainder of the URL.
4493 *
4494 * ---
4495 *
4496 * Parameter names may contain only word characters (latin letters, digits, and underscore) and
4497 * must be unique within the pattern (across both path and search parameters).
4498 * A path parameter matches any number of characters other than '/'. For catch-all
4499 * placeholders the path parameter matches any number of characters.
4500 *
4501 * Examples:
4502 *
4503 * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
4504 * trailing slashes, and patterns have to match the entire path, not just a prefix.
4505 * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
4506 * '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
4507 * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
4508 * * `'/user/{id:[^/]*}'` - Same as the previous example.
4509 * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
4510 * parameter consists of 1 to 8 hex digits.
4511 * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
4512 * path into the parameter 'path'.
4513 * * `'/files/*path'` - ditto.
4514 * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
4515 * in the built-in `date` ParamType matches `2014-11-12`) and provides a Date object in $stateParams.start
4516 *
4517 */
4518 var UrlMatcher = /** @class */ (function () {
4519 /**
4520 * @param pattern The pattern to compile into a matcher.
4521 * @param paramTypes The [[ParamTypes]] registry
4522 * @param paramFactory A [[ParamFactory]] object
4523 * @param config A [[UrlMatcherCompileConfig]] configuration object
4524 */
4525 function UrlMatcher(pattern, paramTypes, paramFactory, config) {
4526 var _this = this;
4527 /** @internal */
4528 this._cache = { path: [this] };
4529 /** @internal */
4530 this._children = [];
4531 /** @internal */
4532 this._params = [];
4533 /** @internal */
4534 this._segments = [];
4535 /** @internal */
4536 this._compiled = [];
4537 this.config = config = defaults(config, defaultConfig);
4538 this.pattern = pattern;
4539 // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
4540 // '*' name
4541 // ':' name
4542 // '{' name '}'
4543 // '{' name ':' regexp '}'
4544 // The regular expression is somewhat complicated due to the need to allow curly braces
4545 // inside the regular expression. The placeholder regexp breaks down as follows:
4546 // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case)
4547 // \{([\w\[\]]+)(?:\:\s*( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
4548 // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either
4549 // [^{}\\]+ - anything other than curly braces or backslash
4550 // \\. - a backslash escape
4551 // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
4552 var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g;
4553 var searchPlaceholder = /([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g;
4554 var patterns = [];
4555 var last = 0;
4556 var matchArray;
4557 var checkParamErrors = function (id) {
4558 if (!UrlMatcher.nameValidator.test(id))
4559 throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
4560 if (find(_this._params, propEq('id', id)))
4561 throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
4562 };
4563 // Split into static segments separated by path parameter placeholders.
4564 // The number of segments is always 1 more than the number of parameters.
4565 var matchDetails = function (m, isSearch) {
4566 // IE[78] returns '' for unmatched groups instead of null
4567 var id = m[2] || m[3];
4568 var regexp = isSearch ? m[4] : m[4] || (m[1] === '*' ? '[\\s\\S]*' : null);
4569 var makeRegexpType = function (str) {
4570 return inherit(paramTypes.type(isSearch ? 'query' : 'path'), {
4571 pattern: new RegExp(str, _this.config.caseInsensitive ? 'i' : undefined),
4572 });
4573 };
4574 return {
4575 id: id,
4576 regexp: regexp,
4577 segment: pattern.substring(last, m.index),
4578 type: !regexp ? null : paramTypes.type(regexp) || makeRegexpType(regexp),
4579 };
4580 };
4581 var details;
4582 var segment;
4583 // tslint:disable-next-line:no-conditional-assignment
4584 while ((matchArray = placeholder.exec(pattern))) {
4585 details = matchDetails(matchArray, false);
4586 if (details.segment.indexOf('?') >= 0)
4587 break; // we're into the search part
4588 checkParamErrors(details.id);
4589 this._params.push(paramFactory.fromPath(details.id, details.type, config.state));
4590 this._segments.push(details.segment);
4591 patterns.push([details.segment, tail(this._params)]);
4592 last = placeholder.lastIndex;
4593 }
4594 segment = pattern.substring(last);
4595 // Find any search parameter names and remove them from the last segment
4596 var i = segment.indexOf('?');
4597 if (i >= 0) {
4598 var search = segment.substring(i);
4599 segment = segment.substring(0, i);
4600 if (search.length > 0) {
4601 last = 0;
4602 // tslint:disable-next-line:no-conditional-assignment
4603 while ((matchArray = searchPlaceholder.exec(search))) {
4604 details = matchDetails(matchArray, true);
4605 checkParamErrors(details.id);
4606 this._params.push(paramFactory.fromSearch(details.id, details.type, config.state));
4607 last = placeholder.lastIndex;
4608 // check if ?&
4609 }
4610 }
4611 }
4612 this._segments.push(segment);
4613 this._compiled = patterns.map(function (_pattern) { return quoteRegExp.apply(null, _pattern); }).concat(quoteRegExp(segment));
4614 }
4615 /** @internal */
4616 UrlMatcher.encodeDashes = function (str) {
4617 // Replace dashes with encoded "\-"
4618 return encodeURIComponent(str).replace(/-/g, function (c) { return "%5C%" + c.charCodeAt(0).toString(16).toUpperCase(); });
4619 };
4620 /** @internal Given a matcher, return an array with the matcher's path segments and path params, in order */
4621 UrlMatcher.pathSegmentsAndParams = function (matcher) {
4622 var staticSegments = matcher._segments;
4623 var pathParams = matcher._params.filter(function (p) { return p.location === exports.DefType.PATH; });
4624 return arrayTuples(staticSegments, pathParams.concat(undefined))
4625 .reduce(unnestR, [])
4626 .filter(function (x) { return x !== '' && isDefined(x); });
4627 };
4628 /** @internal Given a matcher, return an array with the matcher's query params */
4629 UrlMatcher.queryParams = function (matcher) {
4630 return matcher._params.filter(function (p) { return p.location === exports.DefType.SEARCH; });
4631 };
4632 /**
4633 * Compare two UrlMatchers
4634 *
4635 * This comparison function converts a UrlMatcher into static and dynamic path segments.
4636 * Each static path segment is a static string between a path separator (slash character).
4637 * Each dynamic segment is a path parameter.
4638 *
4639 * The comparison function sorts static segments before dynamic ones.
4640 */
4641 UrlMatcher.compare = function (a, b) {
4642 /**
4643 * Turn a UrlMatcher and all its parent matchers into an array
4644 * of slash literals '/', string literals, and Param objects
4645 *
4646 * This example matcher matches strings like "/foo/:param/tail":
4647 * var matcher = $umf.compile("/foo").append($umf.compile("/:param")).append($umf.compile("/")).append($umf.compile("tail"));
4648 * var result = segments(matcher); // [ '/', 'foo', '/', Param, '/', 'tail' ]
4649 *
4650 * Caches the result as `matcher._cache.segments`
4651 */
4652 var segments = function (matcher) {
4653 return (matcher._cache.segments =
4654 matcher._cache.segments ||
4655 matcher._cache.path
4656 .map(UrlMatcher.pathSegmentsAndParams)
4657 .reduce(unnestR, [])
4658 .reduce(joinNeighborsR, [])
4659 .map(function (x) { return (isString(x) ? splitOnSlash(x) : x); })
4660 .reduce(unnestR, []));
4661 };
4662 /**
4663 * Gets the sort weight for each segment of a UrlMatcher
4664 *
4665 * Caches the result as `matcher._cache.weights`
4666 */
4667 var weights = function (matcher) {
4668 return (matcher._cache.weights =
4669 matcher._cache.weights ||
4670 segments(matcher).map(function (segment) {
4671 // Sort slashes first, then static strings, the Params
4672 if (segment === '/')
4673 return 1;
4674 if (isString(segment))
4675 return 2;
4676 if (segment instanceof Param)
4677 return 3;
4678 }));
4679 };
4680 /**
4681 * Pads shorter array in-place (mutates)
4682 */
4683 var padArrays = function (l, r, padVal) {
4684 var len = Math.max(l.length, r.length);
4685 while (l.length < len)
4686 l.push(padVal);
4687 while (r.length < len)
4688 r.push(padVal);
4689 };
4690 var weightsA = weights(a), weightsB = weights(b);
4691 padArrays(weightsA, weightsB, 0);
4692 var _pairs = arrayTuples(weightsA, weightsB);
4693 var cmp, i;
4694 for (i = 0; i < _pairs.length; i++) {
4695 cmp = _pairs[i][0] - _pairs[i][1];
4696 if (cmp !== 0)
4697 return cmp;
4698 }
4699 return 0;
4700 };
4701 /**
4702 * Creates a new concatenated UrlMatcher
4703 *
4704 * Builds a new UrlMatcher by appending another UrlMatcher to this one.
4705 *
4706 * @param url A `UrlMatcher` instance to append as a child of the current `UrlMatcher`.
4707 */
4708 UrlMatcher.prototype.append = function (url) {
4709 this._children.push(url);
4710 url._cache = {
4711 path: this._cache.path.concat(url),
4712 parent: this,
4713 pattern: null,
4714 };
4715 return url;
4716 };
4717 /** @internal */
4718 UrlMatcher.prototype.isRoot = function () {
4719 return this._cache.path[0] === this;
4720 };
4721 /** Returns the input pattern string */
4722 UrlMatcher.prototype.toString = function () {
4723 return this.pattern;
4724 };
4725 UrlMatcher.prototype._getDecodedParamValue = function (value, param) {
4726 if (isDefined(value)) {
4727 if (this.config.decodeParams && !param.type.raw) {
4728 if (isArray(value)) {
4729 value = value.map(function (paramValue) { return decodeURIComponent(paramValue); });
4730 }
4731 else {
4732 value = decodeURIComponent(value);
4733 }
4734 }
4735 value = param.type.decode(value);
4736 }
4737 return param.value(value);
4738 };
4739 /**
4740 * Tests the specified url/path against this matcher.
4741 *
4742 * Tests if the given url matches this matcher's pattern, and returns an object containing the captured
4743 * parameter values. Returns null if the path does not match.
4744 *
4745 * The returned object contains the values
4746 * of any search parameters that are mentioned in the pattern, but their value may be null if
4747 * they are not present in `search`. This means that search parameters are always treated
4748 * as optional.
4749 *
4750 * #### Example:
4751 * ```js
4752 * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
4753 * x: '1', q: 'hello'
4754 * });
4755 * // returns { id: 'bob', q: 'hello', r: null }
4756 * ```
4757 *
4758 * @param path The URL path to match, e.g. `$location.path()`.
4759 * @param search URL search parameters, e.g. `$location.search()`.
4760 * @param hash URL hash e.g. `$location.hash()`.
4761 * @param options
4762 *
4763 * @returns The captured parameter values.
4764 */
4765 UrlMatcher.prototype.exec = function (path, search, hash, options) {
4766 var _this = this;
4767 if (search === void 0) { search = {}; }
4768 var match = memoizeTo(this._cache, 'pattern', function () {
4769 return new RegExp([
4770 '^',
4771 unnest(_this._cache.path.map(prop('_compiled'))).join(''),
4772 _this.config.strict === false ? '/?' : '',
4773 '$',
4774 ].join(''), _this.config.caseInsensitive ? 'i' : undefined);
4775 }).exec(path);
4776 if (!match)
4777 return null;
4778 // options = defaults(options, { isolate: false });
4779 var allParams = this.parameters(), pathParams = allParams.filter(function (param) { return !param.isSearch(); }), searchParams = allParams.filter(function (param) { return param.isSearch(); }), nPathSegments = this._cache.path.map(function (urlm) { return urlm._segments.length - 1; }).reduce(function (a, x) { return a + x; }), values = {};
4780 if (nPathSegments !== match.length - 1)
4781 throw new Error("Unbalanced capture group in route '" + this.pattern + "'");
4782 function decodePathArray(paramVal) {
4783 var reverseString = function (str) { return str.split('').reverse().join(''); };
4784 var unquoteDashes = function (str) { return str.replace(/\\-/g, '-'); };
4785 var split = reverseString(paramVal).split(/-(?!\\)/);
4786 var allReversed = map(split, reverseString);
4787 return map(allReversed, unquoteDashes).reverse();
4788 }
4789 for (var i = 0; i < nPathSegments; i++) {
4790 var param = pathParams[i];
4791 var value = match[i + 1];
4792 // if the param value matches a pre-replace pair, replace the value before decoding.
4793 for (var j = 0; j < param.replace.length; j++) {
4794 if (param.replace[j].from === value)
4795 value = param.replace[j].to;
4796 }
4797 if (value && param.array === true)
4798 value = decodePathArray(value);
4799 values[param.id] = this._getDecodedParamValue(value, param);
4800 }
4801 searchParams.forEach(function (param) {
4802 var value = search[param.id];
4803 for (var j = 0; j < param.replace.length; j++) {
4804 if (param.replace[j].from === value)
4805 value = param.replace[j].to;
4806 }
4807 values[param.id] = _this._getDecodedParamValue(value, param);
4808 });
4809 if (hash)
4810 values['#'] = hash;
4811 return values;
4812 };
4813 /**
4814 * @internal
4815 * Returns all the [[Param]] objects of all path and search parameters of this pattern in order of appearance.
4816 *
4817 * @returns {Array.<Param>} An array of [[Param]] objects. Must be treated as read-only. If the
4818 * pattern has no parameters, an empty array is returned.
4819 */
4820 UrlMatcher.prototype.parameters = function (opts) {
4821 if (opts === void 0) { opts = {}; }
4822 if (opts.inherit === false)
4823 return this._params;
4824 return unnest(this._cache.path.map(function (matcher) { return matcher._params; }));
4825 };
4826 /**
4827 * @internal
4828 * Returns a single parameter from this UrlMatcher by id
4829 *
4830 * @param id
4831 * @param opts
4832 * @returns {T|Param|any|boolean|UrlMatcher|null}
4833 */
4834 UrlMatcher.prototype.parameter = function (id, opts) {
4835 var _this = this;
4836 if (opts === void 0) { opts = {}; }
4837 var findParam = function () {
4838 for (var _i = 0, _a = _this._params; _i < _a.length; _i++) {
4839 var param = _a[_i];
4840 if (param.id === id)
4841 return param;
4842 }
4843 };
4844 var parent = this._cache.parent;
4845 return findParam() || (opts.inherit !== false && parent && parent.parameter(id, opts)) || null;
4846 };
4847 /**
4848 * Validates the input parameter values against this UrlMatcher
4849 *
4850 * Checks an object hash of parameters to validate their correctness according to the parameter
4851 * types of this `UrlMatcher`.
4852 *
4853 * @param params The object hash of parameters to validate.
4854 * @returns Returns `true` if `params` validates, otherwise `false`.
4855 */
4856 UrlMatcher.prototype.validates = function (params) {
4857 var validParamVal = function (param, val) { return !param || param.validates(val); };
4858 params = params || {};
4859 // I'm not sure why this checks only the param keys passed in, and not all the params known to the matcher
4860 var paramSchema = this.parameters().filter(function (paramDef) { return params.hasOwnProperty(paramDef.id); });
4861 return paramSchema.map(function (paramDef) { return validParamVal(paramDef, params[paramDef.id]); }).reduce(allTrueR, true);
4862 };
4863 /**
4864 * Given a set of parameter values, creates a URL from this UrlMatcher.
4865 *
4866 * Creates a URL that matches this pattern by substituting the specified values
4867 * for the path and search parameters.
4868 *
4869 * #### Example:
4870 * ```js
4871 * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
4872 * // returns '/user/bob?q=yes'
4873 * ```
4874 *
4875 * @param values the values to substitute for the parameters in this pattern.
4876 * @returns the formatted URL (path and optionally search part).
4877 */
4878 UrlMatcher.prototype.format = function (values) {
4879 if (values === void 0) { values = {}; }
4880 // Build the full path of UrlMatchers (including all parent UrlMatchers)
4881 var urlMatchers = this._cache.path;
4882 // Extract all the static segments and Params (processed as ParamDetails)
4883 // into an ordered array
4884 var pathSegmentsAndParams = urlMatchers
4885 .map(UrlMatcher.pathSegmentsAndParams)
4886 .reduce(unnestR, [])
4887 .map(function (x) { return (isString(x) ? x : getDetails(x)); });
4888 // Extract the query params into a separate array
4889 var queryParams = urlMatchers
4890 .map(UrlMatcher.queryParams)
4891 .reduce(unnestR, [])
4892 .map(getDetails);
4893 var isInvalid = function (param) { return param.isValid === false; };
4894 if (pathSegmentsAndParams.concat(queryParams).filter(isInvalid).length) {
4895 return null;
4896 }
4897 /**
4898 * Given a Param, applies the parameter value, then returns detailed information about it
4899 */
4900 function getDetails(param) {
4901 // Normalize to typed value
4902 var value = param.value(values[param.id]);
4903 var isValid = param.validates(value);
4904 var isDefaultValue = param.isDefaultValue(value);
4905 // Check if we're in squash mode for the parameter
4906 var squash = isDefaultValue ? param.squash : false;
4907 // Allow the Parameter's Type to encode the value
4908 var encoded = param.type.encode(value);
4909 return { param: param, value: value, isValid: isValid, isDefaultValue: isDefaultValue, squash: squash, encoded: encoded };
4910 }
4911 // Build up the path-portion from the list of static segments and parameters
4912 var pathString = pathSegmentsAndParams.reduce(function (acc, x) {
4913 // The element is a static segment (a raw string); just append it
4914 if (isString(x))
4915 return acc + x;
4916 // Otherwise, it's a ParamDetails.
4917 var squash = x.squash, encoded = x.encoded, param = x.param;
4918 // If squash is === true, try to remove a slash from the path
4919 if (squash === true)
4920 return acc.match(/\/$/) ? acc.slice(0, -1) : acc;
4921 // If squash is a string, use the string for the param value
4922 if (isString(squash))
4923 return acc + squash;
4924 if (squash !== false)
4925 return acc; // ?
4926 if (encoded == null)
4927 return acc;
4928 // If this parameter value is an array, encode the value using encodeDashes
4929 if (isArray(encoded))
4930 return acc + map(encoded, UrlMatcher.encodeDashes).join('-');
4931 // If the parameter type is "raw", then do not encodeURIComponent
4932 if (param.raw)
4933 return acc + encoded;
4934 // Encode the value
4935 return acc + encodeURIComponent(encoded);
4936 }, '');
4937 // Build the query string by applying parameter values (array or regular)
4938 // then mapping to key=value, then flattening and joining using "&"
4939 var queryString = queryParams
4940 .map(function (paramDetails) {
4941 var param = paramDetails.param, squash = paramDetails.squash, encoded = paramDetails.encoded, isDefaultValue = paramDetails.isDefaultValue;
4942 if (encoded == null || (isDefaultValue && squash !== false))
4943 return;
4944 if (!isArray(encoded))
4945 encoded = [encoded];
4946 if (encoded.length === 0)
4947 return;
4948 if (!param.raw)
4949 encoded = map(encoded, encodeURIComponent);
4950 return encoded.map(function (val) { return param.id + "=" + val; });
4951 })
4952 .filter(identity)
4953 .reduce(unnestR, [])
4954 .join('&');
4955 // Concat the pathstring with the queryString (if exists) and the hashString (if exists)
4956 return pathString + (queryString ? "?" + queryString : '') + (values['#'] ? '#' + values['#'] : '');
4957 };
4958 /** @internal */
4959 UrlMatcher.nameValidator = /^\w+([-.]+\w+)*(?:\[\])?$/;
4960 return UrlMatcher;
4961 }());
4962
4963 var __assign = (undefined && undefined.__assign) || function () {
4964 __assign = Object.assign || function(t) {
4965 for (var s, i = 1, n = arguments.length; i < n; i++) {
4966 s = arguments[i];
4967 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
4968 t[p] = s[p];
4969 }
4970 return t;
4971 };
4972 return __assign.apply(this, arguments);
4973 };
4974 var ParamFactory = /** @class */ (function () {
4975 function ParamFactory(router) {
4976 this.router = router;
4977 }
4978 ParamFactory.prototype.fromConfig = function (id, type, state) {
4979 return new Param(id, type, exports.DefType.CONFIG, this.router.urlService.config, state);
4980 };
4981 ParamFactory.prototype.fromPath = function (id, type, state) {
4982 return new Param(id, type, exports.DefType.PATH, this.router.urlService.config, state);
4983 };
4984 ParamFactory.prototype.fromSearch = function (id, type, state) {
4985 return new Param(id, type, exports.DefType.SEARCH, this.router.urlService.config, state);
4986 };
4987 return ParamFactory;
4988 }());
4989 /**
4990 * Factory for [[UrlMatcher]] instances.
4991 *
4992 * The factory is available to ng1 services as
4993 * `$urlMatcherFactory` or ng1 providers as `$urlMatcherFactoryProvider`.
4994 */
4995 var UrlMatcherFactory = /** @class */ (function () {
4996 // TODO: move implementations to UrlConfig (urlService.config)
4997 function UrlMatcherFactory(/** @internal */ router) {
4998 var _this = this;
4999 this.router = router;
5000 /** Creates a new [[Param]] for a given location (DefType) */
5001 this.paramFactory = new ParamFactory(this.router);
5002 // TODO: Check if removal of this will break anything, then remove these
5003 this.UrlMatcher = UrlMatcher;
5004 this.Param = Param;
5005 /** @deprecated use [[UrlConfig.caseInsensitive]] */
5006 this.caseInsensitive = function (value) { return _this.router.urlService.config.caseInsensitive(value); };
5007 /** @deprecated use [[UrlConfig.defaultSquashPolicy]] */
5008 this.defaultSquashPolicy = function (value) { return _this.router.urlService.config.defaultSquashPolicy(value); };
5009 /** @deprecated use [[UrlConfig.strictMode]] */
5010 this.strictMode = function (value) { return _this.router.urlService.config.strictMode(value); };
5011 /** @deprecated use [[UrlConfig.type]] */
5012 this.type = function (name, definition, definitionFn) {
5013 return _this.router.urlService.config.type(name, definition, definitionFn) || _this;
5014 };
5015 }
5016 /**
5017 * Creates a [[UrlMatcher]] for the specified pattern.
5018 *
5019 * @param pattern The URL pattern.
5020 * @param config The config object hash.
5021 * @returns The UrlMatcher.
5022 */
5023 UrlMatcherFactory.prototype.compile = function (pattern, config) {
5024 var urlConfig = this.router.urlService.config;
5025 // backward-compatible support for config.params -> config.state.params
5026 var params = config && !config.state && config.params;
5027 config = params ? __assign({ state: { params: params } }, config) : config;
5028 var globalConfig = {
5029 strict: urlConfig._isStrictMode,
5030 caseInsensitive: urlConfig._isCaseInsensitive,
5031 decodeParams: urlConfig._decodeParams,
5032 };
5033 return new UrlMatcher(pattern, urlConfig.paramTypes, this.paramFactory, extend(globalConfig, config));
5034 };
5035 /**
5036 * Returns true if the specified object is a [[UrlMatcher]], or false otherwise.
5037 *
5038 * @param object The object to perform the type check against.
5039 * @returns `true` if the object matches the `UrlMatcher` interface, by
5040 * implementing all the same methods.
5041 */
5042 UrlMatcherFactory.prototype.isMatcher = function (object) {
5043 // TODO: typeof?
5044 if (!isObject(object))
5045 return false;
5046 var result = true;
5047 forEach(UrlMatcher.prototype, function (val, name) {
5048 if (isFunction(val))
5049 result = result && isDefined(object[name]) && isFunction(object[name]);
5050 });
5051 return result;
5052 };
5053 /** @internal */
5054 UrlMatcherFactory.prototype.$get = function () {
5055 var urlConfig = this.router.urlService.config;
5056 urlConfig.paramTypes.enqueue = false;
5057 urlConfig.paramTypes._flushTypeQueue();
5058 return this;
5059 };
5060 return UrlMatcherFactory;
5061 }());
5062
5063 /**
5064 * Creates a [[UrlRule]]
5065 *
5066 * Creates a [[UrlRule]] from a:
5067 *
5068 * - `string`
5069 * - [[UrlMatcher]]
5070 * - `RegExp`
5071 * - [[StateObject]]
5072 */
5073 var UrlRuleFactory = /** @class */ (function () {
5074 function UrlRuleFactory(router) {
5075 this.router = router;
5076 }
5077 UrlRuleFactory.prototype.compile = function (str) {
5078 return this.router.urlMatcherFactory.compile(str);
5079 };
5080 UrlRuleFactory.prototype.create = function (what, handler) {
5081 var _this = this;
5082 var isState = StateObject.isState, isStateDeclaration = StateObject.isStateDeclaration;
5083 var makeRule = pattern([
5084 [isString, function (_what) { return makeRule(_this.compile(_what)); }],
5085 [is(UrlMatcher), function (_what) { return _this.fromUrlMatcher(_what, handler); }],
5086 [or(isState, isStateDeclaration), function (_what) { return _this.fromState(_what, _this.router); }],
5087 [is(RegExp), function (_what) { return _this.fromRegExp(_what, handler); }],
5088 [isFunction, function (_what) { return new BaseUrlRule(_what, handler); }],
5089 ]);
5090 var rule = makeRule(what);
5091 if (!rule)
5092 throw new Error("invalid 'what' in when()");
5093 return rule;
5094 };
5095 /**
5096 * A UrlRule which matches based on a UrlMatcher
5097 *
5098 * The `handler` may be either a `string`, a [[UrlRuleHandlerFn]] or another [[UrlMatcher]]
5099 *
5100 * ## Handler as a function
5101 *
5102 * If `handler` is a function, the function is invoked with:
5103 *
5104 * - matched parameter values ([[RawParams]] from [[UrlMatcher.exec]])
5105 * - url: the current Url ([[UrlParts]])
5106 * - router: the router object ([[UIRouter]])
5107 *
5108 * #### Example:
5109 * ```js
5110 * var urlMatcher = $umf.compile("/foo/:fooId/:barId");
5111 * var rule = factory.fromUrlMatcher(urlMatcher, match => "/home/" + match.fooId + "/" + match.barId);
5112 * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' }
5113 * var result = rule.handler(match); // '/home/123/456'
5114 * ```
5115 *
5116 * ## Handler as UrlMatcher
5117 *
5118 * If `handler` is a UrlMatcher, the handler matcher is used to create the new url.
5119 * The `handler` UrlMatcher is formatted using the matched param from the first matcher.
5120 * The url is replaced with the result.
5121 *
5122 * #### Example:
5123 * ```js
5124 * var urlMatcher = $umf.compile("/foo/:fooId/:barId");
5125 * var handler = $umf.compile("/home/:fooId/:barId");
5126 * var rule = factory.fromUrlMatcher(urlMatcher, handler);
5127 * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' }
5128 * var result = rule.handler(match); // '/home/123/456'
5129 * ```
5130 */
5131 UrlRuleFactory.prototype.fromUrlMatcher = function (urlMatcher, handler) {
5132 var _handler = handler;
5133 if (isString(handler))
5134 handler = this.router.urlMatcherFactory.compile(handler);
5135 if (is(UrlMatcher)(handler))
5136 _handler = function (match) { return handler.format(match); };
5137 function matchUrlParamters(url) {
5138 var params = urlMatcher.exec(url.path, url.search, url.hash);
5139 return urlMatcher.validates(params) && params;
5140 }
5141 // Prioritize URLs, lowest to highest:
5142 // - Some optional URL parameters, but none matched
5143 // - No optional parameters in URL
5144 // - Some optional parameters, some matched
5145 // - Some optional parameters, all matched
5146 function matchPriority(params) {
5147 var optional = urlMatcher.parameters().filter(function (param) { return param.isOptional; });
5148 if (!optional.length)
5149 return 0.000001;
5150 var matched = optional.filter(function (param) { return params[param.id]; });
5151 return matched.length / optional.length;
5152 }
5153 var details = { urlMatcher: urlMatcher, matchPriority: matchPriority, type: 'URLMATCHER' };
5154 return extend(new BaseUrlRule(matchUrlParamters, _handler), details);
5155 };
5156 /**
5157 * A UrlRule which matches a state by its url
5158 *
5159 * #### Example:
5160 * ```js
5161 * var rule = factory.fromState($state.get('foo'), router);
5162 * var match = rule.match('/foo/123/456'); // results in { fooId: '123', barId: '456' }
5163 * var result = rule.handler(match);
5164 * // Starts a transition to 'foo' with params: { fooId: '123', barId: '456' }
5165 * ```
5166 */
5167 UrlRuleFactory.prototype.fromState = function (stateOrDecl, router) {
5168 var state = StateObject.isStateDeclaration(stateOrDecl) ? stateOrDecl.$$state() : stateOrDecl;
5169 /**
5170 * Handles match by transitioning to matched state
5171 *
5172 * First checks if the router should start a new transition.
5173 * A new transition is not required if the current state's URL
5174 * and the new URL are already identical
5175 */
5176 var handler = function (match) {
5177 var $state = router.stateService;
5178 var globals = router.globals;
5179 if ($state.href(state, match) !== $state.href(globals.current, globals.params)) {
5180 $state.transitionTo(state, match, { inherit: true, source: 'url' });
5181 }
5182 };
5183 var details = { state: state, type: 'STATE' };
5184 return extend(this.fromUrlMatcher(state.url, handler), details);
5185 };
5186 /**
5187 * A UrlRule which matches based on a regular expression
5188 *
5189 * The `handler` may be either a [[UrlRuleHandlerFn]] or a string.
5190 *
5191 * ## Handler as a function
5192 *
5193 * If `handler` is a function, the function is invoked with:
5194 *
5195 * - regexp match array (from `regexp`)
5196 * - url: the current Url ([[UrlParts]])
5197 * - router: the router object ([[UIRouter]])
5198 *
5199 * #### Example:
5200 * ```js
5201 * var rule = factory.fromRegExp(/^\/foo\/(bar|baz)$/, match => "/home/" + match[1])
5202 * var match = rule.match('/foo/bar'); // results in [ '/foo/bar', 'bar' ]
5203 * var result = rule.handler(match); // '/home/bar'
5204 * ```
5205 *
5206 * ## Handler as string
5207 *
5208 * If `handler` is a string, the url is *replaced by the string* when the Rule is invoked.
5209 * The string is first interpolated using `string.replace()` style pattern.
5210 *
5211 * #### Example:
5212 * ```js
5213 * var rule = factory.fromRegExp(/^\/foo\/(bar|baz)$/, "/home/$1")
5214 * var match = rule.match('/foo/bar'); // results in [ '/foo/bar', 'bar' ]
5215 * var result = rule.handler(match); // '/home/bar'
5216 * ```
5217 */
5218 UrlRuleFactory.prototype.fromRegExp = function (regexp, handler) {
5219 if (regexp.global || regexp.sticky)
5220 throw new Error('Rule RegExp must not be global or sticky');
5221 /**
5222 * If handler is a string, the url will be replaced by the string.
5223 * If the string has any String.replace() style variables in it (like `$2`),
5224 * they will be replaced by the captures from [[match]]
5225 */
5226 var redirectUrlTo = function (match) {
5227 // Interpolates matched values into $1 $2, etc using a String.replace()-style pattern
5228 return handler.replace(/\$(\$|\d{1,2})/, function (m, what) { return match[what === '$' ? 0 : Number(what)]; });
5229 };
5230 var _handler = isString(handler) ? redirectUrlTo : handler;
5231 var matchParamsFromRegexp = function (url) { return regexp.exec(url.path); };
5232 var details = { regexp: regexp, type: 'REGEXP' };
5233 return extend(new BaseUrlRule(matchParamsFromRegexp, _handler), details);
5234 };
5235 UrlRuleFactory.isUrlRule = function (obj) { return obj && ['type', 'match', 'handler'].every(function (key) { return isDefined(obj[key]); }); };
5236 return UrlRuleFactory;
5237 }());
5238 /**
5239 * A base rule which calls `match`
5240 *
5241 * The value from the `match` function is passed through to the `handler`.
5242 * @internal
5243 */
5244 var BaseUrlRule = /** @class */ (function () {
5245 function BaseUrlRule(match, handler) {
5246 var _this = this;
5247 this.match = match;
5248 this.type = 'RAW';
5249 this.matchPriority = function (match) { return 0 - _this.$id; };
5250 this.handler = handler || identity;
5251 }
5252 return BaseUrlRule;
5253 }());
5254
5255 function appendBasePath(url, isHtml5, absolute, baseHref) {
5256 if (baseHref === '/')
5257 return url;
5258 if (isHtml5)
5259 return stripLastPathElement(baseHref) + url;
5260 if (absolute)
5261 return baseHref.slice(1) + url;
5262 return url;
5263 }
5264 /**
5265 * Updates URL and responds to URL changes
5266 *
5267 * ### Deprecation warning:
5268 * This class is now considered to be an internal API
5269 * Use the [[UrlService]] instead.
5270 * For configuring URL rules, use the [[UrlRules]] which can be found as [[UrlService.rules]].
5271 */
5272 var UrlRouter = /** @class */ (function () {
5273 /** @internal */
5274 function UrlRouter(/** @internal */ router) {
5275 var _this = this;
5276 this.router = router;
5277 // Delegate these calls to [[UrlService]]
5278 /** @deprecated use [[UrlService.sync]]*/
5279 this.sync = function (evt) { return _this.router.urlService.sync(evt); };
5280 /** @deprecated use [[UrlService.listen]]*/
5281 this.listen = function (enabled) { return _this.router.urlService.listen(enabled); };
5282 /** @deprecated use [[UrlService.deferIntercept]]*/
5283 this.deferIntercept = function (defer) { return _this.router.urlService.deferIntercept(defer); };
5284 /** @deprecated use [[UrlService.match]]*/
5285 this.match = function (urlParts) { return _this.router.urlService.match(urlParts); };
5286 // Delegate these calls to [[UrlRules]]
5287 /** @deprecated use [[UrlRules.initial]]*/
5288 this.initial = function (handler) {
5289 return _this.router.urlService.rules.initial(handler);
5290 };
5291 /** @deprecated use [[UrlRules.otherwise]]*/
5292 this.otherwise = function (handler) {
5293 return _this.router.urlService.rules.otherwise(handler);
5294 };
5295 /** @deprecated use [[UrlRules.removeRule]]*/
5296 this.removeRule = function (rule) { return _this.router.urlService.rules.removeRule(rule); };
5297 /** @deprecated use [[UrlRules.rule]]*/
5298 this.rule = function (rule) { return _this.router.urlService.rules.rule(rule); };
5299 /** @deprecated use [[UrlRules.rules]]*/
5300 this.rules = function () { return _this.router.urlService.rules.rules(); };
5301 /** @deprecated use [[UrlRules.sort]]*/
5302 this.sort = function (compareFn) { return _this.router.urlService.rules.sort(compareFn); };
5303 /** @deprecated use [[UrlRules.when]]*/
5304 this.when = function (matcher, handler, options) { return _this.router.urlService.rules.when(matcher, handler, options); };
5305 this.urlRuleFactory = new UrlRuleFactory(router);
5306 }
5307 /** Internal API. */
5308 UrlRouter.prototype.update = function (read) {
5309 var $url = this.router.locationService;
5310 if (read) {
5311 this.location = $url.url();
5312 return;
5313 }
5314 if ($url.url() === this.location)
5315 return;
5316 $url.url(this.location, true);
5317 };
5318 /**
5319 * Internal API.
5320 *
5321 * Pushes a new location to the browser history.
5322 *
5323 * @internal
5324 * @param urlMatcher
5325 * @param params
5326 * @param options
5327 */
5328 UrlRouter.prototype.push = function (urlMatcher, params, options) {
5329 var replace = options && !!options.replace;
5330 this.router.urlService.url(urlMatcher.format(params || {}), replace);
5331 };
5332 /**
5333 * Builds and returns a URL with interpolated parameters
5334 *
5335 * #### Example:
5336 * ```js
5337 * matcher = $umf.compile("/about/:person");
5338 * params = { person: "bob" };
5339 * $bob = $urlRouter.href(matcher, params);
5340 * // $bob == "/about/bob";
5341 * ```
5342 *
5343 * @param urlMatcher The [[UrlMatcher]] object which is used as the template of the URL to generate.
5344 * @param params An object of parameter values to fill the matcher's required parameters.
5345 * @param options Options object. The options are:
5346 *
5347 * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
5348 *
5349 * @returns Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
5350 */
5351 UrlRouter.prototype.href = function (urlMatcher, params, options) {
5352 var url = urlMatcher.format(params);
5353 if (url == null)
5354 return null;
5355 options = options || { absolute: false };
5356 var cfg = this.router.urlService.config;
5357 var isHtml5 = cfg.html5Mode();
5358 if (!isHtml5 && url !== null) {
5359 url = '#' + cfg.hashPrefix() + url;
5360 }
5361 url = appendBasePath(url, isHtml5, options.absolute, cfg.baseHref());
5362 if (!options.absolute || !url) {
5363 return url;
5364 }
5365 var slash = !isHtml5 && url ? '/' : '';
5366 var cfgPort = cfg.port();
5367 var port = (cfgPort === 80 || cfgPort === 443 ? '' : ':' + cfgPort);
5368 return [cfg.protocol(), '://', cfg.host(), port, slash, url].join('');
5369 };
5370 Object.defineProperty(UrlRouter.prototype, "interceptDeferred", {
5371 /** @deprecated use [[UrlService.interceptDeferred]]*/
5372 get: function () {
5373 return this.router.urlService.interceptDeferred;
5374 },
5375 enumerable: false,
5376 configurable: true
5377 });
5378 return UrlRouter;
5379 }());
5380
5381 /**
5382 * The View service
5383 *
5384 * This service pairs existing `ui-view` components (which live in the DOM)
5385 * with view configs (from the state declaration objects: [[StateDeclaration.views]]).
5386 *
5387 * - After a successful Transition, the views from the newly entered states are activated via [[activateViewConfig]].
5388 * The views from exited states are deactivated via [[deactivateViewConfig]].
5389 * (See: the [[registerActivateViews]] Transition Hook)
5390 *
5391 * - As `ui-view` components pop in and out of existence, they register themselves using [[registerUIView]].
5392 *
5393 * - When the [[sync]] function is called, the registered `ui-view`(s) ([[ActiveUIView]])
5394 * are configured with the matching [[ViewConfig]](s)
5395 *
5396 */
5397 var ViewService = /** @class */ (function () {
5398 /** @internal */
5399 function ViewService(/** @internal */ router) {
5400 var _this = this;
5401 this.router = router;
5402 /** @internal */ this._uiViews = [];
5403 /** @internal */ this._viewConfigs = [];
5404 /** @internal */ this._viewConfigFactories = {};
5405 /** @internal */ this._listeners = [];
5406 /** @internal */
5407 this._pluginapi = {
5408 _rootViewContext: this._rootViewContext.bind(this),
5409 _viewConfigFactory: this._viewConfigFactory.bind(this),
5410 _registeredUIView: function (id) { return find(_this._uiViews, function (view) { return _this.router.$id + "." + view.id === id; }); },
5411 _registeredUIViews: function () { return _this._uiViews; },
5412 _activeViewConfigs: function () { return _this._viewConfigs; },
5413 _onSync: function (listener) {
5414 _this._listeners.push(listener);
5415 return function () { return removeFrom(_this._listeners, listener); };
5416 },
5417 };
5418 }
5419 /**
5420 * Normalizes a view's name from a state.views configuration block.
5421 *
5422 * This should be used by a framework implementation to calculate the values for
5423 * [[_ViewDeclaration.$uiViewName]] and [[_ViewDeclaration.$uiViewContextAnchor]].
5424 *
5425 * @param context the context object (state declaration) that the view belongs to
5426 * @param rawViewName the name of the view, as declared in the [[StateDeclaration.views]]
5427 *
5428 * @returns the normalized uiViewName and uiViewContextAnchor that the view targets
5429 */
5430 ViewService.normalizeUIViewTarget = function (context, rawViewName) {
5431 if (rawViewName === void 0) { rawViewName = ''; }
5432 // TODO: Validate incoming view name with a regexp to allow:
5433 // ex: "view.name@foo.bar" , "^.^.view.name" , "view.name@^.^" , "" ,
5434 // "@" , "$default@^" , "!$default.$default" , "!foo.bar"
5435 var viewAtContext = rawViewName.split('@');
5436 var uiViewName = viewAtContext[0] || '$default'; // default to unnamed view
5437 var uiViewContextAnchor = isString(viewAtContext[1]) ? viewAtContext[1] : '^'; // default to parent context
5438 // Handle relative view-name sugar syntax.
5439 // Matches rawViewName "^.^.^.foo.bar" into array: ["^.^.^.foo.bar", "^.^.^", "foo.bar"],
5440 var relativeViewNameSugar = /^(\^(?:\.\^)*)\.(.*$)/.exec(uiViewName);
5441 if (relativeViewNameSugar) {
5442 // Clobbers existing contextAnchor (rawViewName validation will fix this)
5443 uiViewContextAnchor = relativeViewNameSugar[1]; // set anchor to "^.^.^"
5444 uiViewName = relativeViewNameSugar[2]; // set view-name to "foo.bar"
5445 }
5446 if (uiViewName.charAt(0) === '!') {
5447 uiViewName = uiViewName.substr(1);
5448 uiViewContextAnchor = ''; // target absolutely from root
5449 }
5450 // handle parent relative targeting "^.^.^"
5451 var relativeMatch = /^(\^(?:\.\^)*)$/;
5452 if (relativeMatch.exec(uiViewContextAnchor)) {
5453 var anchorState = uiViewContextAnchor.split('.').reduce(function (anchor, x) { return anchor.parent; }, context);
5454 uiViewContextAnchor = anchorState.name;
5455 }
5456 else if (uiViewContextAnchor === '.') {
5457 uiViewContextAnchor = context.name;
5458 }
5459 return { uiViewName: uiViewName, uiViewContextAnchor: uiViewContextAnchor };
5460 };
5461 /** @internal */
5462 ViewService.prototype._rootViewContext = function (context) {
5463 return (this._rootContext = context || this._rootContext);
5464 };
5465 /** @internal */
5466 ViewService.prototype._viewConfigFactory = function (viewType, factory) {
5467 this._viewConfigFactories[viewType] = factory;
5468 };
5469 ViewService.prototype.createViewConfig = function (path, decl) {
5470 var cfgFactory = this._viewConfigFactories[decl.$type];
5471 if (!cfgFactory)
5472 throw new Error('ViewService: No view config factory registered for type ' + decl.$type);
5473 var cfgs = cfgFactory(path, decl);
5474 return isArray(cfgs) ? cfgs : [cfgs];
5475 };
5476 /**
5477 * Deactivates a ViewConfig.
5478 *
5479 * This function deactivates a `ViewConfig`.
5480 * After calling [[sync]], it will un-pair from any `ui-view` with which it is currently paired.
5481 *
5482 * @param viewConfig The ViewConfig view to deregister.
5483 */
5484 ViewService.prototype.deactivateViewConfig = function (viewConfig) {
5485 trace.traceViewServiceEvent('<- Removing', viewConfig);
5486 removeFrom(this._viewConfigs, viewConfig);
5487 };
5488 ViewService.prototype.activateViewConfig = function (viewConfig) {
5489 trace.traceViewServiceEvent('-> Registering', viewConfig);
5490 this._viewConfigs.push(viewConfig);
5491 };
5492 ViewService.prototype.sync = function () {
5493 var _this = this;
5494 var uiViewsByFqn = this._uiViews.map(function (uiv) { return [uiv.fqn, uiv]; }).reduce(applyPairs, {});
5495 // Return a weighted depth value for a uiView.
5496 // The depth is the nesting depth of ui-views (based on FQN; times 10,000)
5497 // plus the depth of the state that is populating the uiView
5498 function uiViewDepth(uiView) {
5499 var stateDepth = function (context) { return (context && context.parent ? stateDepth(context.parent) + 1 : 1); };
5500 return uiView.fqn.split('.').length * 10000 + stateDepth(uiView.creationContext);
5501 }
5502 // Return the ViewConfig's context's depth in the context tree.
5503 function viewConfigDepth(config) {
5504 var context = config.viewDecl.$context, count = 0;
5505 while (++count && context.parent)
5506 context = context.parent;
5507 return count;
5508 }
5509 // Given a depth function, returns a compare function which can return either ascending or descending order
5510 var depthCompare = curry(function (depthFn, posNeg, left, right) { return posNeg * (depthFn(left) - depthFn(right)); });
5511 var matchingConfigPair = function (uiView) {
5512 var matchingConfigs = _this._viewConfigs.filter(ViewService.matches(uiViewsByFqn, uiView));
5513 if (matchingConfigs.length > 1) {
5514 // This is OK. Child states can target a ui-view that the parent state also targets (the child wins)
5515 // Sort by depth and return the match from the deepest child
5516 // console.log(`Multiple matching view configs for ${uiView.fqn}`, matchingConfigs);
5517 matchingConfigs.sort(depthCompare(viewConfigDepth, -1)); // descending
5518 }
5519 return { uiView: uiView, viewConfig: matchingConfigs[0] };
5520 };
5521 var configureUIView = function (tuple) {
5522 // If a parent ui-view is reconfigured, it could destroy child ui-views.
5523 // Before configuring a child ui-view, make sure it's still in the active uiViews array.
5524 if (_this._uiViews.indexOf(tuple.uiView) !== -1)
5525 tuple.uiView.configUpdated(tuple.viewConfig);
5526 };
5527 // Sort views by FQN and state depth. Process uiviews nearest the root first.
5528 var uiViewTuples = this._uiViews.sort(depthCompare(uiViewDepth, 1)).map(matchingConfigPair);
5529 var matchedViewConfigs = uiViewTuples.map(function (tuple) { return tuple.viewConfig; });
5530 var unmatchedConfigTuples = this._viewConfigs
5531 .filter(function (config) { return !inArray(matchedViewConfigs, config); })
5532 .map(function (viewConfig) { return ({ uiView: undefined, viewConfig: viewConfig }); });
5533 uiViewTuples.forEach(configureUIView);
5534 var allTuples = uiViewTuples.concat(unmatchedConfigTuples);
5535 this._listeners.forEach(function (cb) { return cb(allTuples); });
5536 trace.traceViewSync(allTuples);
5537 };
5538 /**
5539 * Registers a `ui-view` component
5540 *
5541 * When a `ui-view` component is created, it uses this method to register itself.
5542 * After registration the [[sync]] method is used to ensure all `ui-view` are configured with the proper [[ViewConfig]].
5543 *
5544 * Note: the `ui-view` component uses the `ViewConfig` to determine what view should be loaded inside the `ui-view`,
5545 * and what the view's state context is.
5546 *
5547 * Note: There is no corresponding `deregisterUIView`.
5548 * A `ui-view` should hang on to the return value of `registerUIView` and invoke it to deregister itself.
5549 *
5550 * @param uiView The metadata for a UIView
5551 * @return a de-registration function used when the view is destroyed.
5552 */
5553 ViewService.prototype.registerUIView = function (uiView) {
5554 trace.traceViewServiceUIViewEvent('-> Registering', uiView);
5555 var uiViews = this._uiViews;
5556 var fqnAndTypeMatches = function (uiv) { return uiv.fqn === uiView.fqn && uiv.$type === uiView.$type; };
5557 if (uiViews.filter(fqnAndTypeMatches).length)
5558 trace.traceViewServiceUIViewEvent('!!!! duplicate uiView named:', uiView);
5559 uiViews.push(uiView);
5560 this.sync();
5561 return function () {
5562 var idx = uiViews.indexOf(uiView);
5563 if (idx === -1) {
5564 trace.traceViewServiceUIViewEvent('Tried removing non-registered uiView', uiView);
5565 return;
5566 }
5567 trace.traceViewServiceUIViewEvent('<- Deregistering', uiView);
5568 removeFrom(uiViews)(uiView);
5569 };
5570 };
5571 /**
5572 * Returns the list of views currently available on the page, by fully-qualified name.
5573 *
5574 * @return {Array} Returns an array of fully-qualified view names.
5575 */
5576 ViewService.prototype.available = function () {
5577 return this._uiViews.map(prop('fqn'));
5578 };
5579 /**
5580 * Returns the list of views on the page containing loaded content.
5581 *
5582 * @return {Array} Returns an array of fully-qualified view names.
5583 */
5584 ViewService.prototype.active = function () {
5585 return this._uiViews.filter(prop('$config')).map(prop('name'));
5586 };
5587 /**
5588 * Given a ui-view and a ViewConfig, determines if they "match".
5589 *
5590 * A ui-view has a fully qualified name (fqn) and a context object. The fqn is built from its overall location in
5591 * the DOM, describing its nesting relationship to any parent ui-view tags it is nested inside of.
5592 *
5593 * A ViewConfig has a target ui-view name and a context anchor. The ui-view name can be a simple name, or
5594 * can be a segmented ui-view path, describing a portion of a ui-view fqn.
5595 *
5596 * In order for a ui-view to match ViewConfig, ui-view's $type must match the ViewConfig's $type
5597 *
5598 * If the ViewConfig's target ui-view name is a simple name (no dots), then a ui-view matches if:
5599 * - the ui-view's name matches the ViewConfig's target name
5600 * - the ui-view's context matches the ViewConfig's anchor
5601 *
5602 * If the ViewConfig's target ui-view name is a segmented name (with dots), then a ui-view matches if:
5603 * - There exists a parent ui-view where:
5604 * - the parent ui-view's name matches the first segment (index 0) of the ViewConfig's target name
5605 * - the parent ui-view's context matches the ViewConfig's anchor
5606 * - And the remaining segments (index 1..n) of the ViewConfig's target name match the tail of the ui-view's fqn
5607 *
5608 * Example:
5609 *
5610 * DOM:
5611 * <ui-view> <!-- created in the root context (name: "") -->
5612 * <ui-view name="foo"> <!-- created in the context named: "A" -->
5613 * <ui-view> <!-- created in the context named: "A.B" -->
5614 * <ui-view name="bar"> <!-- created in the context named: "A.B.C" -->
5615 * </ui-view>
5616 * </ui-view>
5617 * </ui-view>
5618 * </ui-view>
5619 *
5620 * uiViews: [
5621 * { fqn: "$default", creationContext: { name: "" } },
5622 * { fqn: "$default.foo", creationContext: { name: "A" } },
5623 * { fqn: "$default.foo.$default", creationContext: { name: "A.B" } }
5624 * { fqn: "$default.foo.$default.bar", creationContext: { name: "A.B.C" } }
5625 * ]
5626 *
5627 * These four view configs all match the ui-view with the fqn: "$default.foo.$default.bar":
5628 *
5629 * - ViewConfig1: { uiViewName: "bar", uiViewContextAnchor: "A.B.C" }
5630 * - ViewConfig2: { uiViewName: "$default.bar", uiViewContextAnchor: "A.B" }
5631 * - ViewConfig3: { uiViewName: "foo.$default.bar", uiViewContextAnchor: "A" }
5632 * - ViewConfig4: { uiViewName: "$default.foo.$default.bar", uiViewContextAnchor: "" }
5633 *
5634 * Using ViewConfig3 as an example, it matches the ui-view with fqn "$default.foo.$default.bar" because:
5635 * - The ViewConfig's segmented target name is: [ "foo", "$default", "bar" ]
5636 * - There exists a parent ui-view (which has fqn: "$default.foo") where:
5637 * - the parent ui-view's name "foo" matches the first segment "foo" of the ViewConfig's target name
5638 * - the parent ui-view's context "A" matches the ViewConfig's anchor context "A"
5639 * - And the remaining segments [ "$default", "bar" ].join("."_ of the ViewConfig's target name match
5640 * the tail of the ui-view's fqn "default.bar"
5641 *
5642 * @internal
5643 */
5644 ViewService.matches = function (uiViewsByFqn, uiView) { return function (viewConfig) {
5645 // Don't supply an ng1 ui-view with an ng2 ViewConfig, etc
5646 if (uiView.$type !== viewConfig.viewDecl.$type)
5647 return false;
5648 // Split names apart from both viewConfig and uiView into segments
5649 var vc = viewConfig.viewDecl;
5650 var vcSegments = vc.$uiViewName.split('.');
5651 var uivSegments = uiView.fqn.split('.');
5652 // Check if the tails of the segment arrays match. ex, these arrays' tails match:
5653 // vc: ["foo", "bar"], uiv fqn: ["$default", "foo", "bar"]
5654 if (!equals(vcSegments, uivSegments.slice(0 - vcSegments.length)))
5655 return false;
5656 // Now check if the fqn ending at the first segment of the viewConfig matches the context:
5657 // ["$default", "foo"].join(".") == "$default.foo", does the ui-view $default.foo context match?
5658 var negOffset = 1 - vcSegments.length || undefined;
5659 var fqnToFirstSegment = uivSegments.slice(0, negOffset).join('.');
5660 var uiViewContext = uiViewsByFqn[fqnToFirstSegment].creationContext;
5661 return vc.$uiViewContextAnchor === (uiViewContext && uiViewContext.name);
5662 }; };
5663 return ViewService;
5664 }());
5665
5666 /**
5667 * Global router state
5668 *
5669 * This is where we hold the global mutable state such as current state, current
5670 * params, current transition, etc.
5671 */
5672 var UIRouterGlobals = /** @class */ (function () {
5673 function UIRouterGlobals() {
5674 /**
5675 * Current parameter values
5676 *
5677 * The parameter values from the latest successful transition
5678 */
5679 this.params = new StateParams();
5680 /** @internal */
5681 this.lastStartedTransitionId = -1;
5682 /** @internal */
5683 this.transitionHistory = new Queue([], 1);
5684 /** @internal */
5685 this.successfulTransitions = new Queue([], 1);
5686 }
5687 UIRouterGlobals.prototype.dispose = function () {
5688 this.transitionHistory.clear();
5689 this.successfulTransitions.clear();
5690 this.transition = null;
5691 };
5692 return UIRouterGlobals;
5693 }());
5694
5695 var prioritySort = function (a, b) { return (b.priority || 0) - (a.priority || 0); };
5696 var typeSort = function (a, b) {
5697 var weights = { STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1 };
5698 return (weights[a.type] || 0) - (weights[b.type] || 0);
5699 };
5700 var urlMatcherSort = function (a, b) {
5701 return !a.urlMatcher || !b.urlMatcher ? 0 : UrlMatcher.compare(a.urlMatcher, b.urlMatcher);
5702 };
5703 var idSort = function (a, b) {
5704 // Identically sorted STATE and URLMATCHER best rule will be chosen by `matchPriority` after each rule matches the URL
5705 var useMatchPriority = { STATE: true, URLMATCHER: true };
5706 var equal = useMatchPriority[a.type] && useMatchPriority[b.type];
5707 return equal ? 0 : (a.$id || 0) - (b.$id || 0);
5708 };
5709 /**
5710 * Default rule priority sorting function.
5711 *
5712 * Sorts rules by:
5713 *
5714 * - Explicit priority (set rule priority using [[UrlRules.when]])
5715 * - Rule type (STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1)
5716 * - `UrlMatcher` specificity ([[UrlMatcher.compare]]): works for STATE and URLMATCHER types to pick the most specific rule.
5717 * - Rule registration order (for rule types other than STATE and URLMATCHER)
5718 * - Equally sorted State and UrlMatcher rules will each match the URL.
5719 * Then, the *best* match is chosen based on how many parameter values were matched.
5720 */
5721 var defaultRuleSortFn;
5722 defaultRuleSortFn = function (a, b) {
5723 var cmp = prioritySort(a, b);
5724 if (cmp !== 0)
5725 return cmp;
5726 cmp = typeSort(a, b);
5727 if (cmp !== 0)
5728 return cmp;
5729 cmp = urlMatcherSort(a, b);
5730 if (cmp !== 0)
5731 return cmp;
5732 return idSort(a, b);
5733 };
5734 function getHandlerFn(handler) {
5735 if (!isFunction(handler) && !isString(handler) && !is(TargetState)(handler) && !TargetState.isDef(handler)) {
5736 throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property");
5737 }
5738 return isFunction(handler) ? handler : val(handler);
5739 }
5740 /**
5741 * API for managing URL rules
5742 *
5743 * This API is used to create and manage URL rules.
5744 * URL rules are a mechanism to respond to specific URL patterns.
5745 *
5746 * The most commonly used methods are [[otherwise]] and [[when]].
5747 *
5748 * This API is found at `router.urlService.rules` (see: [[UIRouter.urlService]], [[URLService.rules]])
5749 */
5750 var UrlRules = /** @class */ (function () {
5751 /** @internal */
5752 function UrlRules(/** @internal */ router) {
5753 this.router = router;
5754 /** @internal */ this._sortFn = defaultRuleSortFn;
5755 /** @internal */ this._rules = [];
5756 /** @internal */ this._id = 0;
5757 this.urlRuleFactory = new UrlRuleFactory(router);
5758 }
5759 /** @internal */
5760 UrlRules.prototype.dispose = function (router) {
5761 this._rules = [];
5762 delete this._otherwiseFn;
5763 };
5764 /**
5765 * Defines the initial state, path, or behavior to use when the app starts.
5766 *
5767 * This rule defines the initial/starting state for the application.
5768 *
5769 * This rule is triggered the first time the URL is checked (when the app initially loads).
5770 * The rule is triggered only when the url matches either `""` or `"/"`.
5771 *
5772 * Note: The rule is intended to be used when the root of the application is directly linked to.
5773 * When the URL is *not* `""` or `"/"` and doesn't match other rules, the [[otherwise]] rule is triggered.
5774 * This allows 404-like behavior when an unknown URL is deep-linked.
5775 *
5776 * #### Example:
5777 * Start app at `home` state.
5778 * ```js
5779 * .initial({ state: 'home' });
5780 * ```
5781 *
5782 * #### Example:
5783 * Start app at `/home` (by url)
5784 * ```js
5785 * .initial('/home');
5786 * ```
5787 *
5788 * #### Example:
5789 * When no other url rule matches, go to `home` state
5790 * ```js
5791 * .initial((matchValue, url, router) => {
5792 * console.log('initial state');
5793 * return { state: 'home' };
5794 * })
5795 * ```
5796 *
5797 * @param handler The initial state or url path, or a function which returns the state or url path (or performs custom logic).
5798 */
5799 UrlRules.prototype.initial = function (handler) {
5800 var handlerFn = getHandlerFn(handler);
5801 var matchFn = function (urlParts, router) {
5802 return router.globals.transitionHistory.size() === 0 && !!/^\/?$/.exec(urlParts.path);
5803 };
5804 this.rule(this.urlRuleFactory.create(matchFn, handlerFn));
5805 };
5806 /**
5807 * Defines the state, url, or behavior to use when no other rule matches the URL.
5808 *
5809 * This rule is matched when *no other rule* matches.
5810 * It is generally used to handle unknown URLs (similar to "404" behavior, but on the client side).
5811 *
5812 * - If `handler` a string, it is treated as a url redirect
5813 *
5814 * #### Example:
5815 * When no other url rule matches, redirect to `/index`
5816 * ```js
5817 * .otherwise('/index');
5818 * ```
5819 *
5820 * - If `handler` is an object with a `state` property, the state is activated.
5821 *
5822 * #### Example:
5823 * When no other url rule matches, redirect to `home` and provide a `dashboard` parameter value.
5824 * ```js
5825 * .otherwise({ state: 'home', params: { dashboard: 'default' } });
5826 * ```
5827 *
5828 * - If `handler` is a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object.
5829 * The function can perform actions, and/or return a value.
5830 *
5831 * #### Example:
5832 * When no other url rule matches, manually trigger a transition to the `home` state
5833 * ```js
5834 * .otherwise((matchValue, urlParts, router) => {
5835 * router.stateService.go('home');
5836 * });
5837 * ```
5838 *
5839 * #### Example:
5840 * When no other url rule matches, go to `home` state
5841 * ```js
5842 * .otherwise((matchValue, urlParts, router) => {
5843 * return { state: 'home' };
5844 * });
5845 * ```
5846 *
5847 * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic).
5848 */
5849 UrlRules.prototype.otherwise = function (handler) {
5850 var handlerFn = getHandlerFn(handler);
5851 this._otherwiseFn = this.urlRuleFactory.create(val(true), handlerFn);
5852 this._sorted = false;
5853 };
5854 /**
5855 * Remove a rule previously registered
5856 *
5857 * @param rule the matcher rule that was previously registered using [[rule]]
5858 */
5859 UrlRules.prototype.removeRule = function (rule) {
5860 removeFrom(this._rules, rule);
5861 };
5862 /**
5863 * Manually adds a URL Rule.
5864 *
5865 * Usually, a url rule is added using [[StateDeclaration.url]] or [[when]].
5866 * This api can be used directly for more control (to register a [[BaseUrlRule]], for example).
5867 * Rules can be created using [[urlRuleFactory]], or created manually as simple objects.
5868 *
5869 * A rule should have a `match` function which returns truthy if the rule matched.
5870 * It should also have a `handler` function which is invoked if the rule is the best match.
5871 *
5872 * @return a function that deregisters the rule
5873 */
5874 UrlRules.prototype.rule = function (rule) {
5875 var _this = this;
5876 if (!UrlRuleFactory.isUrlRule(rule))
5877 throw new Error('invalid rule');
5878 rule.$id = this._id++;
5879 rule.priority = rule.priority || 0;
5880 this._rules.push(rule);
5881 this._sorted = false;
5882 return function () { return _this.removeRule(rule); };
5883 };
5884 /**
5885 * Gets all registered rules
5886 *
5887 * @returns an array of all the registered rules
5888 */
5889 UrlRules.prototype.rules = function () {
5890 this.ensureSorted();
5891 return this._rules.concat(this._otherwiseFn ? [this._otherwiseFn] : []);
5892 };
5893 /**
5894 * Defines URL Rule priorities
5895 *
5896 * More than one rule ([[UrlRule]]) might match a given URL.
5897 * This `compareFn` is used to sort the rules by priority.
5898 * Higher priority rules should sort earlier.
5899 *
5900 * The [[defaultRuleSortFn]] is used by default.
5901 *
5902 * You only need to call this function once.
5903 * The `compareFn` will be used to sort the rules as each is registered.
5904 *
5905 * If called without any parameter, it will re-sort the rules.
5906 *
5907 * ---
5908 *
5909 * Url rules may come from multiple sources: states's urls ([[StateDeclaration.url]]), [[when]], and [[rule]].
5910 * Each rule has a (user-provided) [[UrlRule.priority]], a [[UrlRule.type]], and a [[UrlRule.$id]]
5911 * The `$id` is is the order in which the rule was registered.
5912 *
5913 * The sort function should use these data, or data found on a specific type
5914 * of [[UrlRule]] (such as [[StateRule.state]]), to order the rules as desired.
5915 *
5916 * #### Example:
5917 * This compare function prioritizes rules by the order in which the rules were registered.
5918 * A rule registered earlier has higher priority.
5919 *
5920 * ```js
5921 * function compareFn(a, b) {
5922 * return a.$id - b.$id;
5923 * }
5924 * ```
5925 *
5926 * @param compareFn a function that compares to [[UrlRule]] objects.
5927 * The `compareFn` should abide by the `Array.sort` compare function rules.
5928 * Given two rules, `a` and `b`, return a negative number if `a` should be higher priority.
5929 * Return a positive number if `b` should be higher priority.
5930 * Return `0` if the rules are identical.
5931 *
5932 * See the [mozilla reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description)
5933 * for details.
5934 */
5935 UrlRules.prototype.sort = function (compareFn) {
5936 var sorted = this.stableSort(this._rules, (this._sortFn = compareFn || this._sortFn));
5937 // precompute _sortGroup values and apply to each rule
5938 var group = 0;
5939 for (var i = 0; i < sorted.length; i++) {
5940 sorted[i]._group = group;
5941 if (i < sorted.length - 1 && this._sortFn(sorted[i], sorted[i + 1]) !== 0) {
5942 group++;
5943 }
5944 }
5945 this._rules = sorted;
5946 this._sorted = true;
5947 };
5948 /** @internal */
5949 UrlRules.prototype.ensureSorted = function () {
5950 this._sorted || this.sort();
5951 };
5952 /** @internal */
5953 UrlRules.prototype.stableSort = function (arr, compareFn) {
5954 var arrOfWrapper = arr.map(function (elem, idx) { return ({ elem: elem, idx: idx }); });
5955 arrOfWrapper.sort(function (wrapperA, wrapperB) {
5956 var cmpDiff = compareFn(wrapperA.elem, wrapperB.elem);
5957 return cmpDiff === 0 ? wrapperA.idx - wrapperB.idx : cmpDiff;
5958 });
5959 return arrOfWrapper.map(function (wrapper) { return wrapper.elem; });
5960 };
5961 /**
5962 * Registers a `matcher` and `handler` for custom URLs handling.
5963 *
5964 * The `matcher` can be:
5965 *
5966 * - a [[UrlMatcher]]: See: [[UrlMatcherFactory.compile]]
5967 * - a `string`: The string is compiled to a [[UrlMatcher]]
5968 * - a `RegExp`: The regexp is used to match the url.
5969 *
5970 * The `handler` can be:
5971 *
5972 * - a string: The url is redirected to the value of the string.
5973 * - a function: The url is redirected to the return value of the function.
5974 *
5975 * ---
5976 *
5977 * When the `handler` is a `string` and the `matcher` is a `UrlMatcher` (or string), the redirect
5978 * string is interpolated with parameter values.
5979 *
5980 * #### Example:
5981 * When the URL is `/foo/123` the rule will redirect to `/bar/123`.
5982 * ```js
5983 * .when("/foo/:param1", "/bar/:param1")
5984 * ```
5985 *
5986 * ---
5987 *
5988 * When the `handler` is a string and the `matcher` is a `RegExp`, the redirect string is
5989 * interpolated with capture groups from the RegExp.
5990 *
5991 * #### Example:
5992 * When the URL is `/foo/123` the rule will redirect to `/bar/123`.
5993 * ```js
5994 * .when(new RegExp("^/foo/(.*)$"), "/bar/$1");
5995 * ```
5996 *
5997 * ---
5998 *
5999 * When the handler is a function, it receives the matched value, the current URL, and the `UIRouter` object (See [[UrlRuleHandlerFn]]).
6000 * The "matched value" differs based on the `matcher`.
6001 * For [[UrlMatcher]]s, it will be the matched state params.
6002 * For `RegExp`, it will be the match array from `regexp.exec()`.
6003 *
6004 * If the handler returns a string, the URL is redirected to the string.
6005 *
6006 * #### Example:
6007 * When the URL is `/foo/123` the rule will redirect to `/bar/123`.
6008 * ```js
6009 * .when(new RegExp("^/foo/(.*)$"), match => "/bar/" + match[1]);
6010 * ```
6011 *
6012 * Note: the `handler` may also invoke arbitrary code, such as `$state.go()`
6013 *
6014 * @param matcher A pattern `string` to match, compiled as a [[UrlMatcher]], or a `RegExp`.
6015 * @param handler The path to redirect to, or a function that returns the path.
6016 * @param options `{ priority: number }`
6017 *
6018 * @return the registered [[UrlRule]]
6019 */
6020 UrlRules.prototype.when = function (matcher, handler, options) {
6021 var rule = this.urlRuleFactory.create(matcher, handler);
6022 if (isDefined(options && options.priority))
6023 rule.priority = options.priority;
6024 this.rule(rule);
6025 return rule;
6026 };
6027 return UrlRules;
6028 }());
6029
6030 /**
6031 * An API to customize the URL behavior and retrieve URL configuration
6032 *
6033 * This API is used to customize the behavior of the URL.
6034 * This includes optional trailing slashes ([[strictMode]]), case sensitivity ([[caseInsensitive]]),
6035 * and custom parameter encoding (custom [[type]]).
6036 *
6037 * It also has information about the location (url) configuration such as [[port]] and [[baseHref]].
6038 * This information can be used to build absolute URLs, such as
6039 * `https://example.com:443/basepath/state/substate?param1=a#hashvalue`;
6040 *
6041 * This API is found at `router.urlService.config` (see: [[UIRouter.urlService]], [[URLService.config]])
6042 */
6043 var UrlConfig = /** @class */ (function () {
6044 /** @internal */ function UrlConfig(/** @internal */ router) {
6045 var _this = this;
6046 this.router = router;
6047 /** @internal */ this.paramTypes = new ParamTypes();
6048 /** @internal */ this._decodeParams = true;
6049 /** @internal */ this._isCaseInsensitive = false;
6050 /** @internal */ this._isStrictMode = true;
6051 /** @internal */ this._defaultSquashPolicy = false;
6052 /** @internal */ this.dispose = function () { return _this.paramTypes.dispose(); };
6053 // Delegate these calls to the current LocationConfig implementation
6054 /**
6055 * Gets the base Href, e.g., `http://localhost/approot/`
6056 *
6057 * @return the application's base href
6058 */
6059 this.baseHref = function () { return _this.router.locationConfig.baseHref(); };
6060 /**
6061 * Gets or sets the hashPrefix
6062 *
6063 * This only applies when not running in [[html5Mode]] (pushstate mode)
6064 *
6065 * If the current url is `http://localhost/app#!/uirouter/path/#anchor`, it returns `!` which is the prefix for the "hashbang" portion.
6066 *
6067 * @return the hash prefix
6068 */
6069 this.hashPrefix = function (newprefix) { return _this.router.locationConfig.hashPrefix(newprefix); };
6070 /**
6071 * Gets the host, e.g., `localhost`
6072 *
6073 * @return the protocol
6074 */
6075 this.host = function () { return _this.router.locationConfig.host(); };
6076 /**
6077 * Returns true when running in pushstate mode
6078 *
6079 * @return true when running in html5 mode (pushstate mode).
6080 */
6081 this.html5Mode = function () { return _this.router.locationConfig.html5Mode(); };
6082 /**
6083 * Gets the port, e.g., `80`
6084 *
6085 * @return the port number
6086 */
6087 this.port = function () { return _this.router.locationConfig.port(); };
6088 /**
6089 * Gets the protocol, e.g., `http`
6090 *
6091 * @return the protocol
6092 */
6093 this.protocol = function () { return _this.router.locationConfig.protocol(); };
6094 }
6095 /**
6096 * Defines whether URL matching should be case sensitive (the default behavior), or not.
6097 *
6098 * #### Example:
6099 * ```js
6100 * // Allow case insensitive url matches
6101 * urlService.config.caseInsensitive(true);
6102 * ```
6103 *
6104 * @param value `false` to match URL in a case sensitive manner; otherwise `true`;
6105 * @returns the current value of caseInsensitive
6106 */
6107 UrlConfig.prototype.caseInsensitive = function (value) {
6108 return (this._isCaseInsensitive = isDefined(value) ? value : this._isCaseInsensitive);
6109 };
6110 /**
6111 * Sets the default behavior when generating or matching URLs with default parameter values.
6112 *
6113 * #### Example:
6114 * ```js
6115 * // Remove default parameter values from the url
6116 * urlService.config.defaultSquashPolicy(true);
6117 * ```
6118 *
6119 * @param value A string that defines the default parameter URL squashing behavior.
6120 * - `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
6121 * - `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
6122 * parameter is surrounded by slashes, squash (remove) one slash from the URL
6123 * - any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
6124 * the parameter value from the URL and replace it with this string.
6125 * @returns the current value of defaultSquashPolicy
6126 */
6127 UrlConfig.prototype.defaultSquashPolicy = function (value) {
6128 if (isDefined(value) && value !== true && value !== false && !isString(value))
6129 throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
6130 return (this._defaultSquashPolicy = isDefined(value) ? value : this._defaultSquashPolicy);
6131 };
6132 /**
6133 * Defines whether URLs should match trailing slashes, or not (the default behavior).
6134 *
6135 * #### Example:
6136 * ```js
6137 * // Allow optional trailing slashes
6138 * urlService.config.strictMode(false);
6139 * ```
6140 *
6141 * @param value `false` to match trailing slashes in URLs, otherwise `true`.
6142 * @returns the current value of strictMode
6143 */
6144 UrlConfig.prototype.strictMode = function (value) {
6145 return (this._isStrictMode = isDefined(value) ? value : this._isStrictMode);
6146 };
6147 /**
6148 * Creates and registers a custom [[ParamType]] object
6149 *
6150 * A custom parameter type can be used to generate URLs with typed parameters or custom encoding/decoding.
6151 *
6152 * #### Note: Register custom types *before using them* in a state definition.
6153 *
6154 * #### Example:
6155 * ```js
6156 * // Encode object parameter as JSON string
6157 * urlService.config.type('myjson', {
6158 * encode: (obj) => JSON.stringify(obj),
6159 * decode: (str) => JSON.parse(str),
6160 * is: (val) => typeof(val) === 'object',
6161 * pattern: /[^/]+/,
6162 * equals: (a, b) => _.isEqual(a, b),
6163 * });
6164 * ```
6165 *
6166 * See [[ParamTypeDefinition]] for more examples
6167 *
6168 * @param name The type name.
6169 * @param definition The type definition. See [[ParamTypeDefinition]] for information on the values accepted.
6170 * @param definitionFn A function that is injected before the app runtime starts.
6171 * The result of this function should be a [[ParamTypeDefinition]].
6172 * The result is merged into the existing `definition`.
6173 * See [[ParamType]] for information on the values accepted.
6174 *
6175 * @returns if only the `name` parameter was specified: the currently registered [[ParamType]] object, or undefined
6176 */
6177 UrlConfig.prototype.type = function (name, definition, definitionFn) {
6178 var type = this.paramTypes.type(name, definition, definitionFn);
6179 return !isDefined(definition) ? type : this;
6180 };
6181 return UrlConfig;
6182 }());
6183
6184 /**
6185 * API for URL management
6186 */
6187 var UrlService = /** @class */ (function () {
6188 /** @internal */
6189 function UrlService(/** @internal */ router) {
6190 var _this = this;
6191 this.router = router;
6192 /** @internal */ this.interceptDeferred = false;
6193 /**
6194 * The nested [[UrlRules]] API for managing URL rules and rewrites
6195 *
6196 * See: [[UrlRules]] for details
6197 */
6198 this.rules = new UrlRules(this.router);
6199 /**
6200 * The nested [[UrlConfig]] API to configure the URL and retrieve URL information
6201 *
6202 * See: [[UrlConfig]] for details
6203 */
6204 this.config = new UrlConfig(this.router);
6205 // Delegate these calls to the current LocationServices implementation
6206 /**
6207 * Gets the current url, or updates the url
6208 *
6209 * ### Getting the current URL
6210 *
6211 * When no arguments are passed, returns the current URL.
6212 * The URL is normalized using the internal [[path]]/[[search]]/[[hash]] values.
6213 *
6214 * For example, the URL may be stored in the hash ([[HashLocationServices]]) or
6215 * have a base HREF prepended ([[PushStateLocationServices]]).
6216 *
6217 * The raw URL in the browser might be:
6218 *
6219 * ```
6220 * http://mysite.com/somepath/index.html#/internal/path/123?param1=foo#anchor
6221 * ```
6222 *
6223 * or
6224 *
6225 * ```
6226 * http://mysite.com/basepath/internal/path/123?param1=foo#anchor
6227 * ```
6228 *
6229 * then this method returns:
6230 *
6231 * ```
6232 * /internal/path/123?param1=foo#anchor
6233 * ```
6234 *
6235 *
6236 * #### Example:
6237 * ```js
6238 * locationServices.url(); // "/some/path?query=value#anchor"
6239 * ```
6240 *
6241 * ### Updating the URL
6242 *
6243 * When `newurl` arguments is provided, changes the URL to reflect `newurl`
6244 *
6245 * #### Example:
6246 * ```js
6247 * locationServices.url("/some/path?query=value#anchor", true);
6248 * ```
6249 *
6250 * @param newurl The new value for the URL.
6251 * This url should reflect only the new internal [[path]], [[search]], and [[hash]] values.
6252 * It should not include the protocol, site, port, or base path of an absolute HREF.
6253 * @param replace When true, replaces the current history entry (instead of appending it) with this new url
6254 * @param state The history's state object, i.e., pushState (if the LocationServices implementation supports it)
6255 *
6256 * @return the url (after potentially being processed)
6257 */
6258 this.url = function (newurl, replace, state) {
6259 return _this.router.locationService.url(newurl, replace, state);
6260 };
6261 /**
6262 * Gets the path part of the current url
6263 *
6264 * If the current URL is `/some/path?query=value#anchor`, this returns `/some/path`
6265 *
6266 * @return the path portion of the url
6267 */
6268 this.path = function () { return _this.router.locationService.path(); };
6269 /**
6270 * Gets the search part of the current url as an object
6271 *
6272 * If the current URL is `/some/path?query=value#anchor`, this returns `{ query: 'value' }`
6273 *
6274 * @return the search (query) portion of the url, as an object
6275 */
6276 this.search = function () { return _this.router.locationService.search(); };
6277 /**
6278 * Gets the hash part of the current url
6279 *
6280 * If the current URL is `/some/path?query=value#anchor`, this returns `anchor`
6281 *
6282 * @return the hash (anchor) portion of the url
6283 */
6284 this.hash = function () { return _this.router.locationService.hash(); };
6285 /**
6286 * @internal
6287 *
6288 * Registers a low level url change handler
6289 *
6290 * Note: Because this is a low level handler, it's not recommended for general use.
6291 *
6292 * #### Example:
6293 * ```js
6294 * let deregisterFn = locationServices.onChange((evt) => console.log("url change", evt));
6295 * ```
6296 *
6297 * @param callback a function that will be called when the url is changing
6298 * @return a function that de-registers the callback
6299 */
6300 this.onChange = function (callback) { return _this.router.locationService.onChange(callback); };
6301 }
6302 /** @internal */
6303 UrlService.prototype.dispose = function () {
6304 this.listen(false);
6305 this.rules.dispose();
6306 };
6307 /**
6308 * Gets the current URL parts
6309 *
6310 * This method returns the different parts of the current URL (the [[path]], [[search]], and [[hash]]) as a [[UrlParts]] object.
6311 */
6312 UrlService.prototype.parts = function () {
6313 return { path: this.path(), search: this.search(), hash: this.hash() };
6314 };
6315 /**
6316 * Activates the best rule for the current URL
6317 *
6318 * Checks the current URL for a matching [[UrlRule]], then invokes that rule's handler.
6319 * This method is called internally any time the URL has changed.
6320 *
6321 * This effectively activates the state (or redirect, etc) which matches the current URL.
6322 *
6323 * #### Example:
6324 * ```js
6325 * urlService.deferIntercept();
6326 *
6327 * fetch('/states.json').then(resp => resp.json()).then(data => {
6328 * data.forEach(state => $stateRegistry.register(state));
6329 * urlService.listen();
6330 * // Find the matching URL and invoke the handler.
6331 * urlService.sync();
6332 * });
6333 * ```
6334 */
6335 UrlService.prototype.sync = function (evt) {
6336 if (evt && evt.defaultPrevented)
6337 return;
6338 var _a = this.router, urlService = _a.urlService, stateService = _a.stateService;
6339 var url = { path: urlService.path(), search: urlService.search(), hash: urlService.hash() };
6340 var best = this.match(url);
6341 var applyResult = pattern([
6342 [isString, function (newurl) { return urlService.url(newurl, true); }],
6343 [TargetState.isDef, function (def) { return stateService.go(def.state, def.params, def.options); }],
6344 [is(TargetState), function (target) { return stateService.go(target.state(), target.params(), target.options()); }],
6345 ]);
6346 applyResult(best && best.rule.handler(best.match, url, this.router));
6347 };
6348 /**
6349 * Starts or stops listening for URL changes
6350 *
6351 * Call this sometime after calling [[deferIntercept]] to start monitoring the url.
6352 * This causes UI-Router to start listening for changes to the URL, if it wasn't already listening.
6353 *
6354 * If called with `false`, UI-Router will stop listening (call listen(true) to start listening again).
6355 *
6356 * #### Example:
6357 * ```js
6358 * urlService.deferIntercept();
6359 *
6360 * fetch('/states.json').then(resp => resp.json()).then(data => {
6361 * data.forEach(state => $stateRegistry.register(state));
6362 * // Start responding to URL changes
6363 * urlService.listen();
6364 * urlService.sync();
6365 * });
6366 * ```
6367 *
6368 * @param enabled `true` or `false` to start or stop listening to URL changes
6369 */
6370 UrlService.prototype.listen = function (enabled) {
6371 var _this = this;
6372 if (enabled === false) {
6373 this._stopListeningFn && this._stopListeningFn();
6374 delete this._stopListeningFn;
6375 }
6376 else {
6377 return (this._stopListeningFn =
6378 this._stopListeningFn || this.router.urlService.onChange(function (evt) { return _this.sync(evt); }));
6379 }
6380 };
6381 /**
6382 * Disables monitoring of the URL.
6383 *
6384 * Call this method before UI-Router has bootstrapped.
6385 * It will stop UI-Router from performing the initial url sync.
6386 *
6387 * This can be useful to perform some asynchronous initialization before the router starts.
6388 * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL.
6389 *
6390 * #### Example:
6391 * ```js
6392 * // Prevent UI-Router from automatically intercepting URL changes when it starts;
6393 * urlService.deferIntercept();
6394 *
6395 * fetch('/states.json').then(resp => resp.json()).then(data => {
6396 * data.forEach(state => $stateRegistry.register(state));
6397 * urlService.listen();
6398 * urlService.sync();
6399 * });
6400 * ```
6401 *
6402 * @param defer Indicates whether to defer location change interception.
6403 * Passing no parameter is equivalent to `true`.
6404 */
6405 UrlService.prototype.deferIntercept = function (defer) {
6406 if (defer === undefined)
6407 defer = true;
6408 this.interceptDeferred = defer;
6409 };
6410 /**
6411 * Matches a URL
6412 *
6413 * Given a URL (as a [[UrlParts]] object), check all rules and determine the best matching rule.
6414 * Return the result as a [[MatchResult]].
6415 */
6416 UrlService.prototype.match = function (url) {
6417 var _this = this;
6418 url = extend({ path: '', search: {}, hash: '' }, url);
6419 var rules = this.rules.rules();
6420 // Checks a single rule. Returns { rule: rule, match: match, weight: weight } if it matched, or undefined
6421 var checkRule = function (rule) {
6422 var match = rule.match(url, _this.router);
6423 return match && { match: match, rule: rule, weight: rule.matchPriority(match) };
6424 };
6425 // The rules are pre-sorted.
6426 // - Find the first matching rule.
6427 // - Find any other matching rule that sorted *exactly the same*, according to `.sort()`.
6428 // - Choose the rule with the highest match weight.
6429 var best;
6430 for (var i = 0; i < rules.length; i++) {
6431 // Stop when there is a 'best' rule and the next rule sorts differently than it.
6432 if (best && best.rule._group !== rules[i]._group)
6433 break;
6434 var current = checkRule(rules[i]);
6435 // Pick the best MatchResult
6436 best = !best || (current && current.weight > best.weight) ? current : best;
6437 }
6438 return best;
6439 };
6440 return UrlService;
6441 }());
6442
6443 /** @internal */
6444 var _routerInstance = 0;
6445 /** @internal */
6446 var locSvcFns = ['url', 'path', 'search', 'hash', 'onChange'];
6447 /** @internal */
6448 var locCfgFns = ['port', 'protocol', 'host', 'baseHref', 'html5Mode', 'hashPrefix'];
6449 /** @internal */
6450 var locationServiceStub = makeStub('LocationServices', locSvcFns);
6451 /** @internal */
6452 var locationConfigStub = makeStub('LocationConfig', locCfgFns);
6453 /**
6454 * An instance of UI-Router.
6455 *
6456 * This object contains references to service APIs which define your application's routing behavior.
6457 */
6458 var UIRouter = /** @class */ (function () {
6459 /**
6460 * Creates a new `UIRouter` object
6461 *
6462 * @param locationService a [[LocationServices]] implementation
6463 * @param locationConfig a [[LocationConfig]] implementation
6464 * @internal
6465 */
6466 function UIRouter(locationService, locationConfig) {
6467 if (locationService === void 0) { locationService = locationServiceStub; }
6468 if (locationConfig === void 0) { locationConfig = locationConfigStub; }
6469 this.locationService = locationService;
6470 this.locationConfig = locationConfig;
6471 /** @internal */ this.$id = _routerInstance++;
6472 /** @internal */ this._disposed = false;
6473 /** @internal */ this._disposables = [];
6474 /** Enable/disable tracing to the javascript console */
6475 this.trace = trace;
6476 /** Provides services related to ui-view synchronization */
6477 this.viewService = new ViewService(this);
6478 /** An object that contains global router state, such as the current state and params */
6479 this.globals = new UIRouterGlobals();
6480 /** A service that exposes global Transition Hooks */
6481 this.transitionService = new TransitionService(this);
6482 /**
6483 * Deprecated for public use. Use [[urlService]] instead.
6484 * @deprecated Use [[urlService]] instead
6485 */
6486 this.urlMatcherFactory = new UrlMatcherFactory(this);
6487 /**
6488 * Deprecated for public use. Use [[urlService]] instead.
6489 * @deprecated Use [[urlService]] instead
6490 */
6491 this.urlRouter = new UrlRouter(this);
6492 /** Provides services related to the URL */
6493 this.urlService = new UrlService(this);
6494 /** Provides a registry for states, and related registration services */
6495 this.stateRegistry = new StateRegistry(this);
6496 /** Provides services related to states */
6497 this.stateService = new StateService(this);
6498 /** @internal plugin instances are registered here */
6499 this._plugins = {};
6500 this.viewService._pluginapi._rootViewContext(this.stateRegistry.root());
6501 this.globals.$current = this.stateRegistry.root();
6502 this.globals.current = this.globals.$current.self;
6503 this.disposable(this.globals);
6504 this.disposable(this.stateService);
6505 this.disposable(this.stateRegistry);
6506 this.disposable(this.transitionService);
6507 this.disposable(this.urlService);
6508 this.disposable(locationService);
6509 this.disposable(locationConfig);
6510 }
6511 /** Registers an object to be notified when the router is disposed */
6512 UIRouter.prototype.disposable = function (disposable) {
6513 this._disposables.push(disposable);
6514 };
6515 /**
6516 * Disposes this router instance
6517 *
6518 * When called, clears resources retained by the router by calling `dispose(this)` on all
6519 * registered [[disposable]] objects.
6520 *
6521 * Or, if a `disposable` object is provided, calls `dispose(this)` on that object only.
6522 *
6523 * @internal
6524 * @param disposable (optional) the disposable to dispose
6525 */
6526 UIRouter.prototype.dispose = function (disposable) {
6527 var _this = this;
6528 if (disposable && isFunction(disposable.dispose)) {
6529 disposable.dispose(this);
6530 return undefined;
6531 }
6532 this._disposed = true;
6533 this._disposables.slice().forEach(function (d) {
6534 try {
6535 typeof d.dispose === 'function' && d.dispose(_this);
6536 removeFrom(_this._disposables, d);
6537 }
6538 catch (ignored) { }
6539 });
6540 };
6541 /**
6542 * Adds a plugin to UI-Router
6543 *
6544 * This method adds a UI-Router Plugin.
6545 * A plugin can enhance or change UI-Router behavior using any public API.
6546 *
6547 * #### Example:
6548 * ```js
6549 * import { MyCoolPlugin } from "ui-router-cool-plugin";
6550 *
6551 * var plugin = router.addPlugin(MyCoolPlugin);
6552 * ```
6553 *
6554 * ### Plugin authoring
6555 *
6556 * A plugin is simply a class (or constructor function) which accepts a [[UIRouter]] instance and (optionally) an options object.
6557 *
6558 * The plugin can implement its functionality using any of the public APIs of [[UIRouter]].
6559 * For example, it may configure router options or add a Transition Hook.
6560 *
6561 * The plugin can then be published as a separate module.
6562 *
6563 * #### Example:
6564 * ```js
6565 * export class MyAuthPlugin implements UIRouterPlugin {
6566 * constructor(router: UIRouter, options: any) {
6567 * this.name = "MyAuthPlugin";
6568 * let $transitions = router.transitionService;
6569 * let $state = router.stateService;
6570 *
6571 * let authCriteria = {
6572 * to: (state) => state.data && state.data.requiresAuth
6573 * };
6574 *
6575 * function authHook(transition: Transition) {
6576 * let authService = transition.injector().get('AuthService');
6577 * if (!authService.isAuthenticated()) {
6578 * return $state.target('login');
6579 * }
6580 * }
6581 *
6582 * $transitions.onStart(authCriteria, authHook);
6583 * }
6584 * }
6585 * ```
6586 *
6587 * @param plugin one of:
6588 * - a plugin class which implements [[UIRouterPlugin]]
6589 * - a constructor function for a [[UIRouterPlugin]] which accepts a [[UIRouter]] instance
6590 * - a factory function which accepts a [[UIRouter]] instance and returns a [[UIRouterPlugin]] instance
6591 * @param options options to pass to the plugin class/factory
6592 * @returns the registered plugin instance
6593 */
6594 UIRouter.prototype.plugin = function (plugin, options) {
6595 if (options === void 0) { options = {}; }
6596 var pluginInstance = new plugin(this, options);
6597 if (!pluginInstance.name)
6598 throw new Error('Required property `name` missing on plugin: ' + pluginInstance);
6599 this._disposables.push(pluginInstance);
6600 return (this._plugins[pluginInstance.name] = pluginInstance);
6601 };
6602 UIRouter.prototype.getPlugin = function (pluginName) {
6603 return pluginName ? this._plugins[pluginName] : values(this._plugins);
6604 };
6605 return UIRouter;
6606 }());
6607
6608 function addCoreResolvables(trans) {
6609 trans.addResolvable(Resolvable.fromData(UIRouter, trans.router), '');
6610 trans.addResolvable(Resolvable.fromData(Transition, trans), '');
6611 trans.addResolvable(Resolvable.fromData('$transition$', trans), '');
6612 trans.addResolvable(Resolvable.fromData('$stateParams', trans.params()), '');
6613 trans.entering().forEach(function (state) {
6614 trans.addResolvable(Resolvable.fromData('$state$', state), state);
6615 });
6616 }
6617 var registerAddCoreResolvables = function (transitionService) {
6618 return transitionService.onCreate({}, addCoreResolvables);
6619 };
6620 var TRANSITION_TOKENS = ['$transition$', Transition];
6621 var isTransition = inArray(TRANSITION_TOKENS);
6622 // References to Transition in the treeChanges pathnodes makes all
6623 // previous Transitions reachable in memory, causing a memory leak
6624 // This function removes resolves for '$transition$' and `Transition` from the treeChanges.
6625 // Do not use this on current transitions, only on old ones.
6626 var treeChangesCleanup = function (trans) {
6627 var nodes = values(trans.treeChanges()).reduce(unnestR, []).reduce(uniqR, []);
6628 // If the resolvable is a Transition, return a new resolvable with null data
6629 var replaceTransitionWithNull = function (r) {
6630 return isTransition(r.token) ? Resolvable.fromData(r.token, null) : r;
6631 };
6632 nodes.forEach(function (node) {
6633 node.resolvables = node.resolvables.map(replaceTransitionWithNull);
6634 });
6635 };
6636
6637 /**
6638 * A [[TransitionHookFn]] that redirects to a different state or params
6639 *
6640 * Registered using `transitionService.onStart({ to: (state) => !!state.redirectTo }, redirectHook);`
6641 *
6642 * See [[StateDeclaration.redirectTo]]
6643 */
6644 var redirectToHook = function (trans) {
6645 var redirect = trans.to().redirectTo;
6646 if (!redirect)
6647 return;
6648 var $state = trans.router.stateService;
6649 function handleResult(result) {
6650 if (!result)
6651 return;
6652 if (result instanceof TargetState)
6653 return result;
6654 if (isString(result))
6655 return $state.target(result, trans.params(), trans.options());
6656 if (result['state'] || result['params'])
6657 return $state.target(result['state'] || trans.to(), result['params'] || trans.params(), trans.options());
6658 }
6659 if (isFunction(redirect)) {
6660 return services.$q.when(redirect(trans)).then(handleResult);
6661 }
6662 return handleResult(redirect);
6663 };
6664 var registerRedirectToHook = function (transitionService) {
6665 return transitionService.onStart({ to: function (state) { return !!state.redirectTo; } }, redirectToHook);
6666 };
6667
6668 /**
6669 * A factory which creates an onEnter, onExit or onRetain transition hook function
6670 *
6671 * The returned function invokes the (for instance) state.onEnter hook when the
6672 * state is being entered.
6673 */
6674 function makeEnterExitRetainHook(hookName) {
6675 return function (transition, state) {
6676 var _state = state.$$state();
6677 var hookFn = _state[hookName];
6678 return hookFn(transition, state);
6679 };
6680 }
6681 /**
6682 * The [[TransitionStateHookFn]] for onExit
6683 *
6684 * When the state is being exited, the state's .onExit function is invoked.
6685 *
6686 * Registered using `transitionService.onExit({ exiting: (state) => !!state.onExit }, onExitHook);`
6687 *
6688 * See: [[IHookRegistry.onExit]]
6689 */
6690 var onExitHook = makeEnterExitRetainHook('onExit');
6691 var registerOnExitHook = function (transitionService) {
6692 return transitionService.onExit({ exiting: function (state) { return !!state.onExit; } }, onExitHook);
6693 };
6694 /**
6695 * The [[TransitionStateHookFn]] for onRetain
6696 *
6697 * When the state was already entered, and is not being exited or re-entered, the state's .onRetain function is invoked.
6698 *
6699 * Registered using `transitionService.onRetain({ retained: (state) => !!state.onRetain }, onRetainHook);`
6700 *
6701 * See: [[IHookRegistry.onRetain]]
6702 */
6703 var onRetainHook = makeEnterExitRetainHook('onRetain');
6704 var registerOnRetainHook = function (transitionService) {
6705 return transitionService.onRetain({ retained: function (state) { return !!state.onRetain; } }, onRetainHook);
6706 };
6707 /**
6708 * The [[TransitionStateHookFn]] for onEnter
6709 *
6710 * When the state is being entered, the state's .onEnter function is invoked.
6711 *
6712 * Registered using `transitionService.onEnter({ entering: (state) => !!state.onEnter }, onEnterHook);`
6713 *
6714 * See: [[IHookRegistry.onEnter]]
6715 */
6716 var onEnterHook = makeEnterExitRetainHook('onEnter');
6717 var registerOnEnterHook = function (transitionService) {
6718 return transitionService.onEnter({ entering: function (state) { return !!state.onEnter; } }, onEnterHook);
6719 };
6720
6721 var RESOLVE_HOOK_PRIORITY = 1000;
6722 /**
6723 * A [[TransitionHookFn]] which resolves all EAGER Resolvables in the To Path
6724 *
6725 * Registered using `transitionService.onStart({}, eagerResolvePath, { priority: 1000 });`
6726 *
6727 * When a Transition starts, this hook resolves all the EAGER Resolvables, which the transition then waits for.
6728 *
6729 * See [[StateDeclaration.resolve]]
6730 */
6731 var eagerResolvePath = function (trans) {
6732 return new ResolveContext(trans.treeChanges().to).resolvePath('EAGER', trans).then(noop);
6733 };
6734 var registerEagerResolvePath = function (transitionService) {
6735 return transitionService.onStart({}, eagerResolvePath, { priority: RESOLVE_HOOK_PRIORITY });
6736 };
6737 /**
6738 * A [[TransitionHookFn]] which resolves all LAZY Resolvables for the state (and all its ancestors) in the To Path
6739 *
6740 * Registered using `transitionService.onEnter({ entering: () => true }, lazyResolveState, { priority: 1000 });`
6741 *
6742 * When a State is being entered, this hook resolves all the Resolvables for this state, which the transition then waits for.
6743 *
6744 * See [[StateDeclaration.resolve]]
6745 */
6746 var lazyResolveState = function (trans, state) {
6747 return new ResolveContext(trans.treeChanges().to).subContext(state.$$state()).resolvePath('LAZY', trans).then(noop);
6748 };
6749 var registerLazyResolveState = function (transitionService) {
6750 return transitionService.onEnter({ entering: val(true) }, lazyResolveState, { priority: RESOLVE_HOOK_PRIORITY });
6751 };
6752 /**
6753 * A [[TransitionHookFn]] which resolves any dynamically added (LAZY or EAGER) Resolvables.
6754 *
6755 * Registered using `transitionService.onFinish({}, eagerResolvePath, { priority: 1000 });`
6756 *
6757 * After all entering states have been entered, this hook resolves any remaining Resolvables.
6758 * These are typically dynamic resolves which were added by some Transition Hook using [[Transition.addResolvable]].
6759 *
6760 * See [[StateDeclaration.resolve]]
6761 */
6762 var resolveRemaining = function (trans) {
6763 return new ResolveContext(trans.treeChanges().to).resolvePath('LAZY', trans).then(noop);
6764 };
6765 var registerResolveRemaining = function (transitionService) {
6766 return transitionService.onFinish({}, resolveRemaining, { priority: RESOLVE_HOOK_PRIORITY });
6767 };
6768
6769 /**
6770 * A [[TransitionHookFn]] which waits for the views to load
6771 *
6772 * Registered using `transitionService.onStart({}, loadEnteringViews);`
6773 *
6774 * Allows the views to do async work in [[ViewConfig.load]] before the transition continues.
6775 * In angular 1, this includes loading the templates.
6776 */
6777 var loadEnteringViews = function (transition) {
6778 var $q = services.$q;
6779 var enteringViews = transition.views('entering');
6780 if (!enteringViews.length)
6781 return;
6782 return $q.all(enteringViews.map(function (view) { return $q.when(view.load()); })).then(noop);
6783 };
6784 var registerLoadEnteringViews = function (transitionService) {
6785 return transitionService.onFinish({}, loadEnteringViews);
6786 };
6787 /**
6788 * A [[TransitionHookFn]] which activates the new views when a transition is successful.
6789 *
6790 * Registered using `transitionService.onSuccess({}, activateViews);`
6791 *
6792 * After a transition is complete, this hook deactivates the old views from the previous state,
6793 * and activates the new views from the destination state.
6794 *
6795 * See [[ViewService]]
6796 */
6797 var activateViews = function (transition) {
6798 var enteringViews = transition.views('entering');
6799 var exitingViews = transition.views('exiting');
6800 if (!enteringViews.length && !exitingViews.length)
6801 return;
6802 var $view = transition.router.viewService;
6803 exitingViews.forEach(function (vc) { return $view.deactivateViewConfig(vc); });
6804 enteringViews.forEach(function (vc) { return $view.activateViewConfig(vc); });
6805 $view.sync();
6806 };
6807 var registerActivateViews = function (transitionService) {
6808 return transitionService.onSuccess({}, activateViews);
6809 };
6810
6811 /**
6812 * A [[TransitionHookFn]] which updates global UI-Router state
6813 *
6814 * Registered using `transitionService.onBefore({}, updateGlobalState);`
6815 *
6816 * Before a [[Transition]] starts, updates the global value of "the current transition" ([[Globals.transition]]).
6817 * After a successful [[Transition]], updates the global values of "the current state"
6818 * ([[Globals.current]] and [[Globals.$current]]) and "the current param values" ([[Globals.params]]).
6819 *
6820 * See also the deprecated properties:
6821 * [[StateService.transition]], [[StateService.current]], [[StateService.params]]
6822 */
6823 var updateGlobalState = function (trans) {
6824 var globals = trans.router.globals;
6825 var transitionSuccessful = function () {
6826 globals.successfulTransitions.enqueue(trans);
6827 globals.$current = trans.$to();
6828 globals.current = globals.$current.self;
6829 copy(trans.params(), globals.params);
6830 };
6831 var clearCurrentTransition = function () {
6832 // Do not clear globals.transition if a different transition has started in the meantime
6833 if (globals.transition === trans)
6834 globals.transition = null;
6835 };
6836 trans.onSuccess({}, transitionSuccessful, { priority: 10000 });
6837 trans.promise.then(clearCurrentTransition, clearCurrentTransition);
6838 };
6839 var registerUpdateGlobalState = function (transitionService) {
6840 return transitionService.onCreate({}, updateGlobalState);
6841 };
6842
6843 /**
6844 * A [[TransitionHookFn]] which updates the URL after a successful transition
6845 *
6846 * Registered using `transitionService.onSuccess({}, updateUrl);`
6847 */
6848 var updateUrl = function (transition) {
6849 var options = transition.options();
6850 var $state = transition.router.stateService;
6851 var $urlRouter = transition.router.urlRouter;
6852 // Dont update the url in these situations:
6853 // The transition was triggered by a URL sync (options.source === 'url')
6854 // The user doesn't want the url to update (options.location === false)
6855 // The destination state, and all parents have no navigable url
6856 if (options.source !== 'url' && options.location && $state.$current.navigable) {
6857 var urlOptions = { replace: options.location === 'replace' };
6858 $urlRouter.push($state.$current.navigable.url, $state.params, urlOptions);
6859 }
6860 $urlRouter.update(true);
6861 };
6862 var registerUpdateUrl = function (transitionService) {
6863 return transitionService.onSuccess({}, updateUrl, { priority: 9999 });
6864 };
6865
6866 /**
6867 * A [[TransitionHookFn]] that performs lazy loading
6868 *
6869 * When entering a state "abc" which has a `lazyLoad` function defined:
6870 * - Invoke the `lazyLoad` function (unless it is already in process)
6871 * - Flag the hook function as "in process"
6872 * - The function should return a promise (that resolves when lazy loading is complete)
6873 * - Wait for the promise to settle
6874 * - If the promise resolves to a [[LazyLoadResult]], then register those states
6875 * - Flag the hook function as "not in process"
6876 * - If the hook was successful
6877 * - Remove the `lazyLoad` function from the state declaration
6878 * - If all the hooks were successful
6879 * - Retry the transition (by returning a TargetState)
6880 *
6881 * ```
6882 * .state('abc', {
6883 * component: 'fooComponent',
6884 * lazyLoad: () => import('./fooComponent')
6885 * });
6886 * ```
6887 *
6888 * See [[StateDeclaration.lazyLoad]]
6889 */
6890 var lazyLoadHook = function (transition) {
6891 var router = transition.router;
6892 function retryTransition() {
6893 if (transition.originalTransition().options().source !== 'url') {
6894 // The original transition was not triggered via url sync
6895 // The lazy state should be loaded now, so re-try the original transition
6896 var orig = transition.targetState();
6897 return router.stateService.target(orig.identifier(), orig.params(), orig.options());
6898 }
6899 // The original transition was triggered via url sync
6900 // Run the URL rules and find the best match
6901 var $url = router.urlService;
6902 var result = $url.match($url.parts());
6903 var rule = result && result.rule;
6904 // If the best match is a state, redirect the transition (instead
6905 // of calling sync() which supersedes the current transition)
6906 if (rule && rule.type === 'STATE') {
6907 var state = rule.state;
6908 var params = result.match;
6909 return router.stateService.target(state, params, transition.options());
6910 }
6911 // No matching state found, so let .sync() choose the best non-state match/otherwise
6912 router.urlService.sync();
6913 }
6914 var promises = transition
6915 .entering()
6916 .filter(function (state) { return !!state.$$state().lazyLoad; })
6917 .map(function (state) { return lazyLoadState(transition, state); });
6918 return services.$q.all(promises).then(retryTransition);
6919 };
6920 var registerLazyLoadHook = function (transitionService) {
6921 return transitionService.onBefore({ entering: function (state) { return !!state.lazyLoad; } }, lazyLoadHook);
6922 };
6923 /**
6924 * Invokes a state's lazy load function
6925 *
6926 * @param transition a Transition context
6927 * @param state the state to lazy load
6928 * @returns A promise for the lazy load result
6929 */
6930 function lazyLoadState(transition, state) {
6931 var lazyLoadFn = state.$$state().lazyLoad;
6932 // Store/get the lazy load promise on/from the hookfn so it doesn't get re-invoked
6933 var promise = lazyLoadFn['_promise'];
6934 if (!promise) {
6935 var success = function (result) {
6936 delete state.lazyLoad;
6937 delete state.$$state().lazyLoad;
6938 delete lazyLoadFn['_promise'];
6939 return result;
6940 };
6941 var error = function (err) {
6942 delete lazyLoadFn['_promise'];
6943 return services.$q.reject(err);
6944 };
6945 promise = lazyLoadFn['_promise'] = services.$q
6946 .when(lazyLoadFn(transition, state))
6947 .then(updateStateRegistry)
6948 .then(success, error);
6949 }
6950 /** Register any lazy loaded state definitions */
6951 function updateStateRegistry(result) {
6952 if (result && Array.isArray(result.states)) {
6953 result.states.forEach(function (_state) { return transition.router.stateRegistry.register(_state); });
6954 }
6955 return result;
6956 }
6957 return promise;
6958 }
6959
6960 /**
6961 * This class defines a type of hook, such as `onBefore` or `onEnter`.
6962 * Plugins can define custom hook types, such as sticky states does for `onInactive`.
6963 */
6964 var TransitionEventType = /** @class */ (function () {
6965 /* tslint:disable:no-inferrable-types */
6966 function TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous) {
6967 if (reverseSort === void 0) { reverseSort = false; }
6968 if (getResultHandler === void 0) { getResultHandler = TransitionHook.HANDLE_RESULT; }
6969 if (getErrorHandler === void 0) { getErrorHandler = TransitionHook.REJECT_ERROR; }
6970 if (synchronous === void 0) { synchronous = false; }
6971 this.name = name;
6972 this.hookPhase = hookPhase;
6973 this.hookOrder = hookOrder;
6974 this.criteriaMatchPath = criteriaMatchPath;
6975 this.reverseSort = reverseSort;
6976 this.getResultHandler = getResultHandler;
6977 this.getErrorHandler = getErrorHandler;
6978 this.synchronous = synchronous;
6979 }
6980 return TransitionEventType;
6981 }());
6982
6983 /**
6984 * A [[TransitionHookFn]] that skips a transition if it should be ignored
6985 *
6986 * This hook is invoked at the end of the onBefore phase.
6987 *
6988 * If the transition should be ignored (because no parameter or states changed)
6989 * then the transition is ignored and not processed.
6990 */
6991 function ignoredHook(trans) {
6992 var ignoredReason = trans._ignoredReason();
6993 if (!ignoredReason)
6994 return;
6995 trace.traceTransitionIgnored(trans);
6996 var pending = trans.router.globals.transition;
6997 // The user clicked a link going back to the *current state* ('A')
6998 // However, there is also a pending transition in flight (to 'B')
6999 // Abort the transition to 'B' because the user now wants to be back at 'A'.
7000 if (ignoredReason === 'SameAsCurrent' && pending) {
7001 pending.abort();
7002 }
7003 return Rejection.ignored().toPromise();
7004 }
7005 var registerIgnoredTransitionHook = function (transitionService) {
7006 return transitionService.onBefore({}, ignoredHook, { priority: -9999 });
7007 };
7008
7009 /**
7010 * A [[TransitionHookFn]] that rejects the Transition if it is invalid
7011 *
7012 * This hook is invoked at the end of the onBefore phase.
7013 * If the transition is invalid (for example, param values do not validate)
7014 * then the transition is rejected.
7015 */
7016 function invalidTransitionHook(trans) {
7017 if (!trans.valid()) {
7018 throw new Error(trans.error().toString());
7019 }
7020 }
7021 var registerInvalidTransitionHook = function (transitionService) {
7022 return transitionService.onBefore({}, invalidTransitionHook, { priority: -10000 });
7023 };
7024
7025 /**
7026 * The default [[Transition]] options.
7027 *
7028 * Include this object when applying custom defaults:
7029 * let reloadOpts = { reload: true, notify: true }
7030 * let options = defaults(theirOpts, customDefaults, defaultOptions);
7031 */
7032 var defaultTransOpts = {
7033 location: true,
7034 relative: null,
7035 inherit: false,
7036 notify: true,
7037 reload: false,
7038 supercede: true,
7039 custom: {},
7040 current: function () { return null; },
7041 source: 'unknown',
7042 };
7043 /**
7044 * This class provides services related to Transitions.
7045 *
7046 * - Most importantly, it allows global Transition Hooks to be registered.
7047 * - It allows the default transition error handler to be set.
7048 * - It also has a factory function for creating new [[Transition]] objects, (used internally by the [[StateService]]).
7049 *
7050 * At bootstrap, [[UIRouter]] creates a single instance (singleton) of this class.
7051 *
7052 * This API is located at `router.transitionService` ([[UIRouter.transitionService]])
7053 */
7054 var TransitionService = /** @class */ (function () {
7055 /** @internal */
7056 function TransitionService(_router) {
7057 /** @internal */
7058 this._transitionCount = 0;
7059 /** The transition hook types, such as `onEnter`, `onStart`, etc */
7060 this._eventTypes = [];
7061 /** @internal The registered transition hooks */
7062 this._registeredHooks = {};
7063 /** The paths on a criteria object */
7064 this._criteriaPaths = {};
7065 this._router = _router;
7066 this.$view = _router.viewService;
7067 this._deregisterHookFns = {};
7068 this._pluginapi = (createProxyFunctions(val(this), {}, val(this), [
7069 '_definePathType',
7070 '_defineEvent',
7071 '_getPathTypes',
7072 '_getEvents',
7073 'getHooks',
7074 ]));
7075 this._defineCorePaths();
7076 this._defineCoreEvents();
7077 this._registerCoreTransitionHooks();
7078 _router.globals.successfulTransitions.onEvict(treeChangesCleanup);
7079 }
7080 /**
7081 * Registers a [[TransitionHookFn]], called *while a transition is being constructed*.
7082 *
7083 * Registers a transition lifecycle hook, which is invoked during transition construction.
7084 *
7085 * This low level hook should only be used by plugins.
7086 * This can be a useful time for plugins to add resolves or mutate the transition as needed.
7087 * The Sticky States plugin uses this hook to modify the treechanges.
7088 *
7089 * ### Lifecycle
7090 *
7091 * `onCreate` hooks are invoked *while a transition is being constructed*.
7092 *
7093 * ### Return value
7094 *
7095 * The hook's return value is ignored
7096 *
7097 * @internal
7098 * @param criteria defines which Transitions the Hook should be invoked for.
7099 * @param callback the hook function which will be invoked.
7100 * @param options the registration options
7101 * @returns a function which deregisters the hook.
7102 */
7103 TransitionService.prototype.onCreate = function (criteria, callback, options) {
7104 return;
7105 };
7106 /** @inheritdoc */
7107 TransitionService.prototype.onBefore = function (criteria, callback, options) {
7108 return;
7109 };
7110 /** @inheritdoc */
7111 TransitionService.prototype.onStart = function (criteria, callback, options) {
7112 return;
7113 };
7114 /** @inheritdoc */
7115 TransitionService.prototype.onExit = function (criteria, callback, options) {
7116 return;
7117 };
7118 /** @inheritdoc */
7119 TransitionService.prototype.onRetain = function (criteria, callback, options) {
7120 return;
7121 };
7122 /** @inheritdoc */
7123 TransitionService.prototype.onEnter = function (criteria, callback, options) {
7124 return;
7125 };
7126 /** @inheritdoc */
7127 TransitionService.prototype.onFinish = function (criteria, callback, options) {
7128 return;
7129 };
7130 /** @inheritdoc */
7131 TransitionService.prototype.onSuccess = function (criteria, callback, options) {
7132 return;
7133 };
7134 /** @inheritdoc */
7135 TransitionService.prototype.onError = function (criteria, callback, options) {
7136 return;
7137 };
7138 /**
7139 * dispose
7140 * @internal
7141 */
7142 TransitionService.prototype.dispose = function (router) {
7143 values(this._registeredHooks).forEach(function (hooksArray) {
7144 return hooksArray.forEach(function (hook) {
7145 hook._deregistered = true;
7146 removeFrom(hooksArray, hook);
7147 });
7148 });
7149 };
7150 /**
7151 * Creates a new [[Transition]] object
7152 *
7153 * This is a factory function for creating new Transition objects.
7154 * It is used internally by the [[StateService]] and should generally not be called by application code.
7155 *
7156 * @internal
7157 * @param fromPath the path to the current state (the from state)
7158 * @param targetState the target state (destination)
7159 * @returns a Transition
7160 */
7161 TransitionService.prototype.create = function (fromPath, targetState) {
7162 return new Transition(fromPath, targetState, this._router);
7163 };
7164 /** @internal */
7165 TransitionService.prototype._defineCoreEvents = function () {
7166 var Phase = exports.TransitionHookPhase;
7167 var TH = TransitionHook;
7168 var paths = this._criteriaPaths;
7169 var NORMAL_SORT = false, REVERSE_SORT = true;
7170 var SYNCHRONOUS = true;
7171 this._defineEvent('onCreate', Phase.CREATE, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.THROW_ERROR, SYNCHRONOUS);
7172 this._defineEvent('onBefore', Phase.BEFORE, 0, paths.to);
7173 this._defineEvent('onStart', Phase.RUN, 0, paths.to);
7174 this._defineEvent('onExit', Phase.RUN, 100, paths.exiting, REVERSE_SORT);
7175 this._defineEvent('onRetain', Phase.RUN, 200, paths.retained);
7176 this._defineEvent('onEnter', Phase.RUN, 300, paths.entering);
7177 this._defineEvent('onFinish', Phase.RUN, 400, paths.to);
7178 this._defineEvent('onSuccess', Phase.SUCCESS, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.LOG_ERROR, SYNCHRONOUS);
7179 this._defineEvent('onError', Phase.ERROR, 0, paths.to, NORMAL_SORT, TH.LOG_REJECTED_RESULT, TH.LOG_ERROR, SYNCHRONOUS);
7180 };
7181 /** @internal */
7182 TransitionService.prototype._defineCorePaths = function () {
7183 var STATE = exports.TransitionHookScope.STATE, TRANSITION = exports.TransitionHookScope.TRANSITION;
7184 this._definePathType('to', TRANSITION);
7185 this._definePathType('from', TRANSITION);
7186 this._definePathType('exiting', STATE);
7187 this._definePathType('retained', STATE);
7188 this._definePathType('entering', STATE);
7189 };
7190 /** @internal */
7191 TransitionService.prototype._defineEvent = function (name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous) {
7192 if (reverseSort === void 0) { reverseSort = false; }
7193 if (getResultHandler === void 0) { getResultHandler = TransitionHook.HANDLE_RESULT; }
7194 if (getErrorHandler === void 0) { getErrorHandler = TransitionHook.REJECT_ERROR; }
7195 if (synchronous === void 0) { synchronous = false; }
7196 var eventType = new TransitionEventType(name, hookPhase, hookOrder, criteriaMatchPath, reverseSort, getResultHandler, getErrorHandler, synchronous);
7197 this._eventTypes.push(eventType);
7198 makeEvent(this, this, eventType);
7199 };
7200 /** @internal */
7201 TransitionService.prototype._getEvents = function (phase) {
7202 var transitionHookTypes = isDefined(phase)
7203 ? this._eventTypes.filter(function (type) { return type.hookPhase === phase; })
7204 : this._eventTypes.slice();
7205 return transitionHookTypes.sort(function (l, r) {
7206 var cmpByPhase = l.hookPhase - r.hookPhase;
7207 return cmpByPhase === 0 ? l.hookOrder - r.hookOrder : cmpByPhase;
7208 });
7209 };
7210 /**
7211 * Adds a Path to be used as a criterion against a TreeChanges path
7212 *
7213 * For example: the `exiting` path in [[HookMatchCriteria]] is a STATE scoped path.
7214 * It was defined by calling `defineTreeChangesCriterion('exiting', TransitionHookScope.STATE)`
7215 * Each state in the exiting path is checked against the criteria and returned as part of the match.
7216 *
7217 * Another example: the `to` path in [[HookMatchCriteria]] is a TRANSITION scoped path.
7218 * It was defined by calling `defineTreeChangesCriterion('to', TransitionHookScope.TRANSITION)`
7219 * Only the tail of the `to` path is checked against the criteria and returned as part of the match.
7220 *
7221 * @internal
7222 */
7223 TransitionService.prototype._definePathType = function (name, hookScope) {
7224 this._criteriaPaths[name] = { name: name, scope: hookScope };
7225 };
7226 /** @internal */
7227 // tslint:disable-next-line
7228 TransitionService.prototype._getPathTypes = function () {
7229 return this._criteriaPaths;
7230 };
7231 /** @internal */
7232 TransitionService.prototype.getHooks = function (hookName) {
7233 return this._registeredHooks[hookName];
7234 };
7235 /** @internal */
7236 TransitionService.prototype._registerCoreTransitionHooks = function () {
7237 var fns = this._deregisterHookFns;
7238 fns.addCoreResolves = registerAddCoreResolvables(this);
7239 fns.ignored = registerIgnoredTransitionHook(this);
7240 fns.invalid = registerInvalidTransitionHook(this);
7241 // Wire up redirectTo hook
7242 fns.redirectTo = registerRedirectToHook(this);
7243 // Wire up onExit/Retain/Enter state hooks
7244 fns.onExit = registerOnExitHook(this);
7245 fns.onRetain = registerOnRetainHook(this);
7246 fns.onEnter = registerOnEnterHook(this);
7247 // Wire up Resolve hooks
7248 fns.eagerResolve = registerEagerResolvePath(this);
7249 fns.lazyResolve = registerLazyResolveState(this);
7250 fns.resolveAll = registerResolveRemaining(this);
7251 // Wire up the View management hooks
7252 fns.loadViews = registerLoadEnteringViews(this);
7253 fns.activateViews = registerActivateViews(this);
7254 // Updates global state after a transition
7255 fns.updateGlobals = registerUpdateGlobalState(this);
7256 // After globals.current is updated at priority: 10000
7257 fns.updateUrl = registerUpdateUrl(this);
7258 // Lazy load state trees
7259 fns.lazyLoad = registerLazyLoadHook(this);
7260 };
7261 return TransitionService;
7262 }());
7263
7264 /**
7265 * Provides services related to ui-router states.
7266 *
7267 * This API is located at `router.stateService` ([[UIRouter.stateService]])
7268 */
7269 var StateService = /** @class */ (function () {
7270 /** @internal */
7271 function StateService(/** @internal */ router) {
7272 this.router = router;
7273 /** @internal */
7274 this.invalidCallbacks = [];
7275 /** @internal */
7276 this._defaultErrorHandler = function $defaultErrorHandler($error$) {
7277 if ($error$ instanceof Error && $error$.stack) {
7278 console.error($error$);
7279 console.error($error$.stack);
7280 }
7281 else if ($error$ instanceof Rejection) {
7282 console.error($error$.toString());
7283 if ($error$.detail && $error$.detail.stack)
7284 console.error($error$.detail.stack);
7285 }
7286 else {
7287 console.error($error$);
7288 }
7289 };
7290 var getters = ['current', '$current', 'params', 'transition'];
7291 var boundFns = Object.keys(StateService.prototype).filter(not(inArray(getters)));
7292 createProxyFunctions(val(StateService.prototype), this, val(this), boundFns);
7293 }
7294 Object.defineProperty(StateService.prototype, "transition", {
7295 /**
7296 * The [[Transition]] currently in progress (or null)
7297 *
7298 * @deprecated This is a passthrough through to [[UIRouterGlobals.transition]]
7299 */
7300 get: function () {
7301 return this.router.globals.transition;
7302 },
7303 enumerable: false,
7304 configurable: true
7305 });
7306 Object.defineProperty(StateService.prototype, "params", {
7307 /**
7308 * The latest successful state parameters
7309 *
7310 * @deprecated This is a passthrough through to [[UIRouterGlobals.params]]
7311 */
7312 get: function () {
7313 return this.router.globals.params;
7314 },
7315 enumerable: false,
7316 configurable: true
7317 });
7318 Object.defineProperty(StateService.prototype, "current", {
7319 /**
7320 * The current [[StateDeclaration]]
7321 *
7322 * @deprecated This is a passthrough through to [[UIRouterGlobals.current]]
7323 */
7324 get: function () {
7325 return this.router.globals.current;
7326 },
7327 enumerable: false,
7328 configurable: true
7329 });
7330 Object.defineProperty(StateService.prototype, "$current", {
7331 /**
7332 * The current [[StateObject]] (an internal API)
7333 *
7334 * @deprecated This is a passthrough through to [[UIRouterGlobals.$current]]
7335 */
7336 get: function () {
7337 return this.router.globals.$current;
7338 },
7339 enumerable: false,
7340 configurable: true
7341 });
7342 /** @internal */
7343 StateService.prototype.dispose = function () {
7344 this.defaultErrorHandler(noop);
7345 this.invalidCallbacks = [];
7346 };
7347 /**
7348 * Handler for when [[transitionTo]] is called with an invalid state.
7349 *
7350 * Invokes the [[onInvalid]] callbacks, in natural order.
7351 * Each callback's return value is checked in sequence until one of them returns an instance of TargetState.
7352 * The results of the callbacks are wrapped in $q.when(), so the callbacks may return promises.
7353 *
7354 * If a callback returns an TargetState, then it is used as arguments to $state.transitionTo() and the result returned.
7355 *
7356 * @internal
7357 */
7358 StateService.prototype._handleInvalidTargetState = function (fromPath, toState) {
7359 var _this = this;
7360 var fromState = PathUtils.makeTargetState(this.router.stateRegistry, fromPath);
7361 var globals = this.router.globals;
7362 var latestThing = function () { return globals.transitionHistory.peekTail(); };
7363 var latest = latestThing();
7364 var callbackQueue = new Queue(this.invalidCallbacks.slice());
7365 var injector = new ResolveContext(fromPath).injector();
7366 var checkForRedirect = function (result) {
7367 if (!(result instanceof TargetState)) {
7368 return;
7369 }
7370 var target = result;
7371 // Recreate the TargetState, in case the state is now defined.
7372 target = _this.target(target.identifier(), target.params(), target.options());
7373 if (!target.valid()) {
7374 return Rejection.invalid(target.error()).toPromise();
7375 }
7376 if (latestThing() !== latest) {
7377 return Rejection.superseded().toPromise();
7378 }
7379 return _this.transitionTo(target.identifier(), target.params(), target.options());
7380 };
7381 function invokeNextCallback() {
7382 var nextCallback = callbackQueue.dequeue();
7383 if (nextCallback === undefined)
7384 return Rejection.invalid(toState.error()).toPromise();
7385 var callbackResult = services.$q.when(nextCallback(toState, fromState, injector));
7386 return callbackResult.then(checkForRedirect).then(function (result) { return result || invokeNextCallback(); });
7387 }
7388 return invokeNextCallback();
7389 };
7390 /**
7391 * Registers an Invalid State handler
7392 *
7393 * Registers a [[OnInvalidCallback]] function to be invoked when [[StateService.transitionTo]]
7394 * has been called with an invalid state reference parameter
7395 *
7396 * Example:
7397 * ```js
7398 * stateService.onInvalid(function(to, from, injector) {
7399 * if (to.name() === 'foo') {
7400 * let lazyLoader = injector.get('LazyLoadService');
7401 * return lazyLoader.load('foo')
7402 * .then(() => stateService.target('foo'));
7403 * }
7404 * });
7405 * ```
7406 *
7407 * @param {function} callback invoked when the toState is invalid
7408 * This function receives the (invalid) toState, the fromState, and an injector.
7409 * The function may optionally return a [[TargetState]] or a Promise for a TargetState.
7410 * If one is returned, it is treated as a redirect.
7411 *
7412 * @returns a function which deregisters the callback
7413 */
7414 StateService.prototype.onInvalid = function (callback) {
7415 this.invalidCallbacks.push(callback);
7416 return function deregisterListener() {
7417 removeFrom(this.invalidCallbacks)(callback);
7418 }.bind(this);
7419 };
7420 /**
7421 * Reloads the current state
7422 *
7423 * A method that force reloads the current state, or a partial state hierarchy.
7424 * All resolves are re-resolved, and components reinstantiated.
7425 *
7426 * #### Example:
7427 * ```js
7428 * let app angular.module('app', ['ui.router']);
7429 *
7430 * app.controller('ctrl', function ($scope, $state) {
7431 * $scope.reload = function(){
7432 * $state.reload();
7433 * }
7434 * });
7435 * ```
7436 *
7437 * Note: `reload()` is just an alias for:
7438 *
7439 * ```js
7440 * $state.transitionTo($state.current, $state.params, {
7441 * reload: true, inherit: false
7442 * });
7443 * ```
7444 *
7445 * @param reloadState A state name or a state object.
7446 * If present, this state and all its children will be reloaded, but ancestors will not reload.
7447 *
7448 * #### Example:
7449 * ```js
7450 * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item'
7451 * //and current state is 'contacts.detail.item'
7452 * let app angular.module('app', ['ui.router']);
7453 *
7454 * app.controller('ctrl', function ($scope, $state) {
7455 * $scope.reload = function(){
7456 * //will reload 'contact.detail' and nested 'contact.detail.item' states
7457 * $state.reload('contact.detail');
7458 * }
7459 * });
7460 * ```
7461 *
7462 * @returns A promise representing the state of the new transition. See [[StateService.go]]
7463 */
7464 StateService.prototype.reload = function (reloadState) {
7465 return this.transitionTo(this.current, this.params, {
7466 reload: isDefined(reloadState) ? reloadState : true,
7467 inherit: false,
7468 notify: false,
7469 });
7470 };
7471 /**
7472 * Transition to a different state and/or parameters
7473 *
7474 * Convenience method for transitioning to a new state.
7475 *
7476 * `$state.go` calls `$state.transitionTo` internally but automatically sets options to
7477 * `{ location: true, inherit: true, relative: router.globals.$current, notify: true }`.
7478 * This allows you to use either an absolute or relative `to` argument (because of `relative: router.globals.$current`).
7479 * It also allows you to specify * only the parameters you'd like to update, while letting unspecified parameters
7480 * inherit from the current parameter values (because of `inherit: true`).
7481 *
7482 * #### Example:
7483 * ```js
7484 * let app = angular.module('app', ['ui.router']);
7485 *
7486 * app.controller('ctrl', function ($scope, $state) {
7487 * $scope.changeState = function () {
7488 * $state.go('contact.detail');
7489 * };
7490 * });
7491 * ```
7492 *
7493 * @param to Absolute state name, state object, or relative state path (relative to current state).
7494 *
7495 * Some examples:
7496 *
7497 * - `$state.go('contact.detail')` - will go to the `contact.detail` state
7498 * - `$state.go('^')` - will go to the parent state
7499 * - `$state.go('^.sibling')` - if current state is `home.child`, will go to the `home.sibling` state
7500 * - `$state.go('.child.grandchild')` - if current state is home, will go to the `home.child.grandchild` state
7501 *
7502 * @param params A map of the parameters that will be sent to the state, will populate $stateParams.
7503 *
7504 * Any parameters that are not specified will be inherited from current parameter values (because of `inherit: true`).
7505 * This allows, for example, going to a sibling state that shares parameters defined by a parent state.
7506 *
7507 * @param options Transition options
7508 *
7509 * @returns {promise} A promise representing the state of the new transition.
7510 */
7511 StateService.prototype.go = function (to, params, options) {
7512 var defautGoOpts = { relative: this.$current, inherit: true };
7513 var transOpts = defaults(options, defautGoOpts, defaultTransOpts);
7514 return this.transitionTo(to, params, transOpts);
7515 };
7516 /**
7517 * Creates a [[TargetState]]
7518 *
7519 * This is a factory method for creating a TargetState
7520 *
7521 * This may be returned from a Transition Hook to redirect a transition, for example.
7522 */
7523 StateService.prototype.target = function (identifier, params, options) {
7524 if (options === void 0) { options = {}; }
7525 // If we're reloading, find the state object to reload from
7526 if (isObject(options.reload) && !options.reload.name)
7527 throw new Error('Invalid reload state object');
7528 var reg = this.router.stateRegistry;
7529 options.reloadState =
7530 options.reload === true ? reg.root() : reg.matcher.find(options.reload, options.relative);
7531 if (options.reload && !options.reloadState)
7532 throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
7533 return new TargetState(this.router.stateRegistry, identifier, params, options);
7534 };
7535 /** @internal */
7536 StateService.prototype.getCurrentPath = function () {
7537 var _this = this;
7538 var globals = this.router.globals;
7539 var latestSuccess = globals.successfulTransitions.peekTail();
7540 var rootPath = function () { return [new PathNode(_this.router.stateRegistry.root())]; };
7541 return latestSuccess ? latestSuccess.treeChanges().to : rootPath();
7542 };
7543 /**
7544 * Low-level method for transitioning to a new state.
7545 *
7546 * The [[go]] method (which uses `transitionTo` internally) is recommended in most situations.
7547 *
7548 * #### Example:
7549 * ```js
7550 * let app = angular.module('app', ['ui.router']);
7551 *
7552 * app.controller('ctrl', function ($scope, $state) {
7553 * $scope.changeState = function () {
7554 * $state.transitionTo('contact.detail');
7555 * };
7556 * });
7557 * ```
7558 *
7559 * @param to State name or state object.
7560 * @param toParams A map of the parameters that will be sent to the state,
7561 * will populate $stateParams.
7562 * @param options Transition options
7563 *
7564 * @returns A promise representing the state of the new transition. See [[go]]
7565 */
7566 StateService.prototype.transitionTo = function (to, toParams, options) {
7567 var _this = this;
7568 if (toParams === void 0) { toParams = {}; }
7569 if (options === void 0) { options = {}; }
7570 var router = this.router;
7571 var globals = router.globals;
7572 options = defaults(options, defaultTransOpts);
7573 var getCurrent = function () { return globals.transition; };
7574 options = extend(options, { current: getCurrent });
7575 var ref = this.target(to, toParams, options);
7576 var currentPath = this.getCurrentPath();
7577 if (!ref.exists())
7578 return this._handleInvalidTargetState(currentPath, ref);
7579 if (!ref.valid())
7580 return silentRejection(ref.error());
7581 if (options.supercede === false && getCurrent()) {
7582 return (Rejection.ignored('Another transition is in progress and supercede has been set to false in TransitionOptions for the transition. So the transition was ignored in favour of the existing one in progress.').toPromise());
7583 }
7584 /**
7585 * Special handling for Ignored, Aborted, and Redirected transitions
7586 *
7587 * The semantics for the transition.run() promise and the StateService.transitionTo()
7588 * promise differ. For instance, the run() promise may be rejected because it was
7589 * IGNORED, but the transitionTo() promise is resolved because from the user perspective
7590 * no error occurred. Likewise, the transition.run() promise may be rejected because of
7591 * a Redirect, but the transitionTo() promise is chained to the new Transition's promise.
7592 */
7593 var rejectedTransitionHandler = function (trans) { return function (error) {
7594 if (error instanceof Rejection) {
7595 var isLatest = router.globals.lastStartedTransitionId <= trans.$id;
7596 if (error.type === exports.RejectType.IGNORED) {
7597 isLatest && router.urlRouter.update();
7598 // Consider ignored `Transition.run()` as a successful `transitionTo`
7599 return services.$q.when(globals.current);
7600 }
7601 var detail = error.detail;
7602 if (error.type === exports.RejectType.SUPERSEDED && error.redirected && detail instanceof TargetState) {
7603 // If `Transition.run()` was redirected, allow the `transitionTo()` promise to resolve successfully
7604 // by returning the promise for the new (redirect) `Transition.run()`.
7605 var redirect = trans.redirect(detail);
7606 return redirect.run().catch(rejectedTransitionHandler(redirect));
7607 }
7608 if (error.type === exports.RejectType.ABORTED) {
7609 isLatest && router.urlRouter.update();
7610 return services.$q.reject(error);
7611 }
7612 }
7613 var errorHandler = _this.defaultErrorHandler();
7614 errorHandler(error);
7615 return services.$q.reject(error);
7616 }; };
7617 var transition = this.router.transitionService.create(currentPath, ref);
7618 var transitionToPromise = transition.run().catch(rejectedTransitionHandler(transition));
7619 silenceUncaughtInPromise(transitionToPromise); // issue #2676
7620 // Return a promise for the transition, which also has the transition object on it.
7621 return extend(transitionToPromise, { transition: transition });
7622 };
7623 /**
7624 * Checks if the current state *is* the provided state
7625 *
7626 * Similar to [[includes]] but only checks for the full state name.
7627 * If params is supplied then it will be tested for strict equality against the current
7628 * active params object, so all params must match with none missing and no extras.
7629 *
7630 * #### Example:
7631 * ```js
7632 * $state.$current.name = 'contacts.details.item';
7633 *
7634 * // absolute name
7635 * $state.is('contact.details.item'); // returns true
7636 * $state.is(contactDetailItemStateObject); // returns true
7637 * ```
7638 *
7639 * // relative name (. and ^), typically from a template
7640 * // E.g. from the 'contacts.details' template
7641 * ```html
7642 * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
7643 * ```
7644 *
7645 * @param stateOrName The state name (absolute or relative) or state object you'd like to check.
7646 * @param params A param object, e.g. `{sectionId: section.id}`, that you'd like
7647 * to test against the current active state.
7648 * @param options An options object. The options are:
7649 * - `relative`: If `stateOrName` is a relative state name and `options.relative` is set, .is will
7650 * test relative to `options.relative` state (or name).
7651 *
7652 * @returns Returns true if it is the state.
7653 */
7654 StateService.prototype.is = function (stateOrName, params, options) {
7655 options = defaults(options, { relative: this.$current });
7656 var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative);
7657 if (!isDefined(state))
7658 return undefined;
7659 if (this.$current !== state)
7660 return false;
7661 if (!params)
7662 return true;
7663 var schema = state.parameters({ inherit: true, matchingKeys: params });
7664 return Param.equals(schema, Param.values(schema, params), this.params);
7665 };
7666 /**
7667 * Checks if the current state *includes* the provided state
7668 *
7669 * A method to determine if the current active state is equal to or is the child of the
7670 * state stateName. If any params are passed then they will be tested for a match as well.
7671 * Not all the parameters need to be passed, just the ones you'd like to test for equality.
7672 *
7673 * #### Example when `$state.$current.name === 'contacts.details.item'`
7674 * ```js
7675 * // Using partial names
7676 * $state.includes("contacts"); // returns true
7677 * $state.includes("contacts.details"); // returns true
7678 * $state.includes("contacts.details.item"); // returns true
7679 * $state.includes("contacts.list"); // returns false
7680 * $state.includes("about"); // returns false
7681 * ```
7682 *
7683 * #### Glob Examples when `* $state.$current.name === 'contacts.details.item.url'`:
7684 * ```js
7685 * $state.includes("*.details.*.*"); // returns true
7686 * $state.includes("*.details.**"); // returns true
7687 * $state.includes("**.item.**"); // returns true
7688 * $state.includes("*.details.item.url"); // returns true
7689 * $state.includes("*.details.*.url"); // returns true
7690 * $state.includes("*.details.*"); // returns false
7691 * $state.includes("item.**"); // returns false
7692 * ```
7693 *
7694 * @param stateOrName A partial name, relative name, glob pattern,
7695 * or state object to be searched for within the current state name.
7696 * @param params A param object, e.g. `{sectionId: section.id}`,
7697 * that you'd like to test against the current active state.
7698 * @param options An options object. The options are:
7699 * - `relative`: If `stateOrName` is a relative state name and `options.relative` is set, .is will
7700 * test relative to `options.relative` state (or name).
7701 *
7702 * @returns {boolean} Returns true if it does include the state
7703 */
7704 StateService.prototype.includes = function (stateOrName, params, options) {
7705 options = defaults(options, { relative: this.$current });
7706 var glob = isString(stateOrName) && Glob.fromString(stateOrName);
7707 if (glob) {
7708 if (!glob.matches(this.$current.name))
7709 return false;
7710 stateOrName = this.$current.name;
7711 }
7712 var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative), include = this.$current.includes;
7713 if (!isDefined(state))
7714 return undefined;
7715 if (!isDefined(include[state.name]))
7716 return false;
7717 if (!params)
7718 return true;
7719 var schema = state.parameters({ inherit: true, matchingKeys: params });
7720 return Param.equals(schema, Param.values(schema, params), this.params);
7721 };
7722 /**
7723 * Generates a URL for a state and parameters
7724 *
7725 * Returns the url for the given state populated with the given params.
7726 *
7727 * #### Example:
7728 * ```js
7729 * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
7730 * ```
7731 *
7732 * @param stateOrName The state name or state object you'd like to generate a url from.
7733 * @param params An object of parameter values to fill the state's required parameters.
7734 * @param options Options object. The options are:
7735 *
7736 * @returns {string} compiled state url
7737 */
7738 StateService.prototype.href = function (stateOrName, params, options) {
7739 var defaultHrefOpts = {
7740 lossy: true,
7741 inherit: true,
7742 absolute: false,
7743 relative: this.$current,
7744 };
7745 options = defaults(options, defaultHrefOpts);
7746 params = params || {};
7747 var state = this.router.stateRegistry.matcher.find(stateOrName, options.relative);
7748 if (!isDefined(state))
7749 return null;
7750 if (options.inherit)
7751 params = this.params.$inherit(params, this.$current, state);
7752 var nav = state && options.lossy ? state.navigable : state;
7753 if (!nav || nav.url === undefined || nav.url === null) {
7754 return null;
7755 }
7756 return this.router.urlRouter.href(nav.url, params, { absolute: options.absolute });
7757 };
7758 /**
7759 * Sets or gets the default [[transitionTo]] error handler.
7760 *
7761 * The error handler is called when a [[Transition]] is rejected or when any error occurred during the Transition.
7762 * This includes errors caused by resolves and transition hooks.
7763 *
7764 * Note:
7765 * This handler does not receive certain Transition rejections.
7766 * Redirected and Ignored Transitions are not considered to be errors by [[StateService.transitionTo]].
7767 *
7768 * The built-in default error handler logs the error to the console.
7769 *
7770 * You can provide your own custom handler.
7771 *
7772 * #### Example:
7773 * ```js
7774 * stateService.defaultErrorHandler(function() {
7775 * // Do not log transitionTo errors
7776 * });
7777 * ```
7778 *
7779 * @param handler a global error handler function
7780 * @returns the current global error handler
7781 */
7782 StateService.prototype.defaultErrorHandler = function (handler) {
7783 return (this._defaultErrorHandler = handler || this._defaultErrorHandler);
7784 };
7785 StateService.prototype.get = function (stateOrName, base) {
7786 var reg = this.router.stateRegistry;
7787 if (arguments.length === 0)
7788 return reg.get();
7789 return reg.get(stateOrName, base || this.$current);
7790 };
7791 /**
7792 * Lazy loads a state
7793 *
7794 * Explicitly runs a state's [[StateDeclaration.lazyLoad]] function.
7795 *
7796 * @param stateOrName the state that should be lazy loaded
7797 * @param transition the optional Transition context to use (if the lazyLoad function requires an injector, etc)
7798 * Note: If no transition is provided, a noop transition is created using the from the current state to the current state.
7799 * This noop transition is not actually run.
7800 *
7801 * @returns a promise to lazy load
7802 */
7803 StateService.prototype.lazyLoad = function (stateOrName, transition) {
7804 var state = this.get(stateOrName);
7805 if (!state || !state.lazyLoad)
7806 throw new Error('Can not lazy load ' + stateOrName);
7807 var currentPath = this.getCurrentPath();
7808 var target = PathUtils.makeTargetState(this.router.stateRegistry, currentPath);
7809 transition = transition || this.router.transitionService.create(currentPath, target);
7810 return lazyLoadState(transition, state);
7811 };
7812 return StateService;
7813 }());
7814
7815 /**
7816 * An angular1-like promise api
7817 *
7818 * This object implements four methods similar to the
7819 * [angular 1 promise api](https://docs.angularjs.org/api/ng/service/$q)
7820 *
7821 * UI-Router evolved from an angular 1 library to a framework agnostic library.
7822 * However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection.
7823 *
7824 * This API provides native ES6 promise support wrapped as a $q-like API.
7825 * Internally, UI-Router uses this $q object to perform promise operations.
7826 * The `angular-ui-router` (ui-router for angular 1) uses the $q API provided by angular.
7827 *
7828 * $q-like promise api
7829 */
7830 var $q = {
7831 /** Normalizes a value as a promise */
7832 when: function (val) { return new Promise(function (resolve, reject) { return resolve(val); }); },
7833 /** Normalizes a value as a promise rejection */
7834 reject: function (val) {
7835 return new Promise(function (resolve, reject) {
7836 reject(val);
7837 });
7838 },
7839 /** @returns a deferred object, which has `resolve` and `reject` functions */
7840 defer: function () {
7841 var deferred = {};
7842 deferred.promise = new Promise(function (resolve, reject) {
7843 deferred.resolve = resolve;
7844 deferred.reject = reject;
7845 });
7846 return deferred;
7847 },
7848 /** Like Promise.all(), but also supports object key/promise notation like $q */
7849 all: function (promises) {
7850 if (isArray(promises)) {
7851 return Promise.all(promises);
7852 }
7853 if (isObject(promises)) {
7854 // Convert promises map to promises array.
7855 // When each promise resolves, map it to a tuple { key: key, val: val }
7856 var chain = Object.keys(promises).map(function (key) { return promises[key].then(function (val) { return ({ key: key, val: val }); }); });
7857 // Then wait for all promises to resolve, and convert them back to an object
7858 return $q.all(chain).then(function (values) {
7859 return values.reduce(function (acc, tuple) {
7860 acc[tuple.key] = tuple.val;
7861 return acc;
7862 }, {});
7863 });
7864 }
7865 },
7866 };
7867
7868 // globally available injectables
7869 var globals = {};
7870 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
7871 var ARGUMENT_NAMES = /([^\s,]+)/g;
7872 /**
7873 * A basic angular1-like injector api
7874 *
7875 * This object implements four methods similar to the
7876 * [angular 1 dependency injector](https://docs.angularjs.org/api/auto/service/$injector)
7877 *
7878 * UI-Router evolved from an angular 1 library to a framework agnostic library.
7879 * However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection.
7880 *
7881 * This object provides a naive implementation of a globally scoped dependency injection system.
7882 * It supports the following DI approaches:
7883 *
7884 * ### Function parameter names
7885 *
7886 * A function's `.toString()` is called, and the parameter names are parsed.
7887 * This only works when the parameter names aren't "mangled" by a minifier such as UglifyJS.
7888 *
7889 * ```js
7890 * function injectedFunction(FooService, BarService) {
7891 * // FooService and BarService are injected
7892 * }
7893 * ```
7894 *
7895 * ### Function annotation
7896 *
7897 * A function may be annotated with an array of dependency names as the `$inject` property.
7898 *
7899 * ```js
7900 * injectedFunction.$inject = [ 'FooService', 'BarService' ];
7901 * function injectedFunction(fs, bs) {
7902 * // FooService and BarService are injected as fs and bs parameters
7903 * }
7904 * ```
7905 *
7906 * ### Array notation
7907 *
7908 * An array provides the names of the dependencies to inject (as strings).
7909 * The function is the last element of the array.
7910 *
7911 * ```js
7912 * [ 'FooService', 'BarService', function (fs, bs) {
7913 * // FooService and BarService are injected as fs and bs parameters
7914 * }]
7915 * ```
7916 *
7917 * @type {$InjectorLike}
7918 */
7919 var $injector = {
7920 /** Gets an object from DI based on a string token */
7921 get: function (name) { return globals[name]; },
7922 /** Returns true if an object named `name` exists in global DI */
7923 has: function (name) { return $injector.get(name) != null; },
7924 /**
7925 * Injects a function
7926 *
7927 * @param fn the function to inject
7928 * @param context the function's `this` binding
7929 * @param locals An object with additional DI tokens and values, such as `{ someToken: { foo: 1 } }`
7930 */
7931 invoke: function (fn, context, locals) {
7932 var all = extend({}, globals, locals || {});
7933 var params = $injector.annotate(fn);
7934 var ensureExist = assertPredicate(function (key) { return all.hasOwnProperty(key); }, function (key) { return "DI can't find injectable: '" + key + "'"; });
7935 var args = params.filter(ensureExist).map(function (x) { return all[x]; });
7936 if (isFunction(fn))
7937 return fn.apply(context, args);
7938 else
7939 return fn.slice(-1)[0].apply(context, args);
7940 },
7941 /**
7942 * Returns a function's dependencies
7943 *
7944 * Analyzes a function (or array) and returns an array of DI tokens that the function requires.
7945 * @return an array of `string`s
7946 */
7947 annotate: function (fn) {
7948 if (!isInjectable(fn))
7949 throw new Error("Not an injectable function: " + fn);
7950 if (fn && fn.$inject)
7951 return fn.$inject;
7952 if (isArray(fn))
7953 return fn.slice(0, -1);
7954 var fnStr = fn.toString().replace(STRIP_COMMENTS, '');
7955 var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
7956 return result || [];
7957 },
7958 };
7959
7960 var keyValsToObjectR = function (accum, _a) {
7961 var key = _a[0], val = _a[1];
7962 if (!accum.hasOwnProperty(key)) {
7963 accum[key] = val;
7964 }
7965 else if (isArray(accum[key])) {
7966 accum[key].push(val);
7967 }
7968 else {
7969 accum[key] = [accum[key], val];
7970 }
7971 return accum;
7972 };
7973 var getParams = function (queryString) {
7974 return queryString.split('&').filter(identity).map(splitEqual).reduce(keyValsToObjectR, {});
7975 };
7976 function parseUrl$1(url) {
7977 var orEmptyString = function (x) { return x || ''; };
7978 var _a = splitHash(url).map(orEmptyString), beforehash = _a[0], hash = _a[1];
7979 var _b = splitQuery(beforehash).map(orEmptyString), path = _b[0], search = _b[1];
7980 return { path: path, search: search, hash: hash, url: url };
7981 }
7982 var buildUrl = function (loc) {
7983 var path = loc.path();
7984 var searchObject = loc.search();
7985 var hash = loc.hash();
7986 var search = Object.keys(searchObject)
7987 .map(function (key) {
7988 var param = searchObject[key];
7989 var vals = isArray(param) ? param : [param];
7990 return vals.map(function (val) { return key + '=' + val; });
7991 })
7992 .reduce(unnestR, [])
7993 .join('&');
7994 return path + (search ? '?' + search : '') + (hash ? '#' + hash : '');
7995 };
7996 function locationPluginFactory(name, isHtml5, serviceClass, configurationClass) {
7997 return function (uiRouter) {
7998 var service = (uiRouter.locationService = new serviceClass(uiRouter));
7999 var configuration = (uiRouter.locationConfig = new configurationClass(uiRouter, isHtml5));
8000 function dispose(router) {
8001 router.dispose(service);
8002 router.dispose(configuration);
8003 }
8004 return { name: name, service: service, configuration: configuration, dispose: dispose };
8005 };
8006 }
8007
8008 /** A base `LocationServices` */
8009 var BaseLocationServices = /** @class */ (function () {
8010 function BaseLocationServices(router, fireAfterUpdate) {
8011 var _this = this;
8012 this.fireAfterUpdate = fireAfterUpdate;
8013 this._listeners = [];
8014 this._listener = function (evt) { return _this._listeners.forEach(function (cb) { return cb(evt); }); };
8015 this.hash = function () { return parseUrl$1(_this._get()).hash; };
8016 this.path = function () { return parseUrl$1(_this._get()).path; };
8017 this.search = function () { return getParams(parseUrl$1(_this._get()).search); };
8018 this._location = root.location;
8019 this._history = root.history;
8020 }
8021 BaseLocationServices.prototype.url = function (url, replace) {
8022 if (replace === void 0) { replace = true; }
8023 if (isDefined(url) && url !== this._get()) {
8024 this._set(null, null, url, replace);
8025 if (this.fireAfterUpdate) {
8026 this._listeners.forEach(function (cb) { return cb({ url: url }); });
8027 }
8028 }
8029 return buildUrl(this);
8030 };
8031 BaseLocationServices.prototype.onChange = function (cb) {
8032 var _this = this;
8033 this._listeners.push(cb);
8034 return function () { return removeFrom(_this._listeners, cb); };
8035 };
8036 BaseLocationServices.prototype.dispose = function (router) {
8037 deregAll(this._listeners);
8038 };
8039 return BaseLocationServices;
8040 }());
8041
8042 var __extends = (undefined && undefined.__extends) || (function () {
8043 var extendStatics = function (d, b) {
8044 extendStatics = Object.setPrototypeOf ||
8045 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
8046 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
8047 return extendStatics(d, b);
8048 };
8049 return function (d, b) {
8050 extendStatics(d, b);
8051 function __() { this.constructor = d; }
8052 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
8053 };
8054 })();
8055 /** A `LocationServices` that uses the browser hash "#" to get/set the current location */
8056 var HashLocationService = /** @class */ (function (_super) {
8057 __extends(HashLocationService, _super);
8058 function HashLocationService(router) {
8059 var _this = _super.call(this, router, false) || this;
8060 root.addEventListener('hashchange', _this._listener, false);
8061 return _this;
8062 }
8063 HashLocationService.prototype._get = function () {
8064 return trimHashVal(this._location.hash);
8065 };
8066 HashLocationService.prototype._set = function (state, title, url, replace) {
8067 this._location.hash = url;
8068 };
8069 HashLocationService.prototype.dispose = function (router) {
8070 _super.prototype.dispose.call(this, router);
8071 root.removeEventListener('hashchange', this._listener);
8072 };
8073 return HashLocationService;
8074 }(BaseLocationServices));
8075
8076 var __extends$1 = (undefined && undefined.__extends) || (function () {
8077 var extendStatics = function (d, b) {
8078 extendStatics = Object.setPrototypeOf ||
8079 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
8080 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
8081 return extendStatics(d, b);
8082 };
8083 return function (d, b) {
8084 extendStatics(d, b);
8085 function __() { this.constructor = d; }
8086 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
8087 };
8088 })();
8089 /** A `LocationServices` that gets/sets the current location from an in-memory object */
8090 var MemoryLocationService = /** @class */ (function (_super) {
8091 __extends$1(MemoryLocationService, _super);
8092 function MemoryLocationService(router) {
8093 return _super.call(this, router, true) || this;
8094 }
8095 MemoryLocationService.prototype._get = function () {
8096 return this._url;
8097 };
8098 MemoryLocationService.prototype._set = function (state, title, url, replace) {
8099 this._url = url;
8100 };
8101 return MemoryLocationService;
8102 }(BaseLocationServices));
8103
8104 var __extends$2 = (undefined && undefined.__extends) || (function () {
8105 var extendStatics = function (d, b) {
8106 extendStatics = Object.setPrototypeOf ||
8107 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
8108 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
8109 return extendStatics(d, b);
8110 };
8111 return function (d, b) {
8112 extendStatics(d, b);
8113 function __() { this.constructor = d; }
8114 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
8115 };
8116 })();
8117 /**
8118 * A `LocationServices` that gets/sets the current location using the browser's `location` and `history` apis
8119 *
8120 * Uses `history.pushState` and `history.replaceState`
8121 */
8122 var PushStateLocationService = /** @class */ (function (_super) {
8123 __extends$2(PushStateLocationService, _super);
8124 function PushStateLocationService(router) {
8125 var _this = _super.call(this, router, true) || this;
8126 _this._config = router.urlService.config;
8127 root.addEventListener('popstate', _this._listener, false);
8128 return _this;
8129 }
8130 /**
8131 * Gets the base prefix without:
8132 * - trailing slash
8133 * - trailing filename
8134 * - protocol and hostname
8135 *
8136 * If <base href='/base/'>, this returns '/base'.
8137 * If <base href='/foo/base/'>, this returns '/foo/base'.
8138 * If <base href='/base/index.html'>, this returns '/base'.
8139 * If <base href='http://localhost:8080/base/index.html'>, this returns '/base'.
8140 * If <base href='/base'>, this returns ''.
8141 * If <base href='http://localhost:8080'>, this returns ''.
8142 * If <base href='http://localhost:8080/'>, this returns ''.
8143 *
8144 * See: https://html.spec.whatwg.org/dev/semantics.html#the-base-element
8145 */
8146 PushStateLocationService.prototype._getBasePrefix = function () {
8147 return stripLastPathElement(this._config.baseHref());
8148 };
8149 PushStateLocationService.prototype._get = function () {
8150 var _a = this._location, pathname = _a.pathname, hash = _a.hash, search = _a.search;
8151 search = splitQuery(search)[1]; // strip ? if found
8152 hash = splitHash(hash)[1]; // strip # if found
8153 var basePrefix = this._getBasePrefix();
8154 var exactBaseHrefMatch = pathname === this._config.baseHref();
8155 var startsWithBase = pathname.substr(0, basePrefix.length) === basePrefix;
8156 pathname = exactBaseHrefMatch ? '/' : startsWithBase ? pathname.substring(basePrefix.length) : pathname;
8157 return pathname + (search ? '?' + search : '') + (hash ? '#' + hash : '');
8158 };
8159 PushStateLocationService.prototype._set = function (state, title, url, replace) {
8160 var basePrefix = this._getBasePrefix();
8161 var slash = url && url[0] !== '/' ? '/' : '';
8162 var fullUrl = url === '' || url === '/' ? this._config.baseHref() : basePrefix + slash + url;
8163 if (replace) {
8164 this._history.replaceState(state, title, fullUrl);
8165 }
8166 else {
8167 this._history.pushState(state, title, fullUrl);
8168 }
8169 };
8170 PushStateLocationService.prototype.dispose = function (router) {
8171 _super.prototype.dispose.call(this, router);
8172 root.removeEventListener('popstate', this._listener);
8173 };
8174 return PushStateLocationService;
8175 }(BaseLocationServices));
8176
8177 /** A `LocationConfig` mock that gets/sets all config from an in-memory object */
8178 var MemoryLocationConfig = /** @class */ (function () {
8179 function MemoryLocationConfig() {
8180 var _this = this;
8181 this.dispose = noop;
8182 this._baseHref = '';
8183 this._port = 80;
8184 this._protocol = 'http';
8185 this._host = 'localhost';
8186 this._hashPrefix = '';
8187 this.port = function () { return _this._port; };
8188 this.protocol = function () { return _this._protocol; };
8189 this.host = function () { return _this._host; };
8190 this.baseHref = function () { return _this._baseHref; };
8191 this.html5Mode = function () { return false; };
8192 this.hashPrefix = function (newval) { return (isDefined(newval) ? (_this._hashPrefix = newval) : _this._hashPrefix); };
8193 }
8194 return MemoryLocationConfig;
8195 }());
8196
8197 /** A `LocationConfig` that delegates to the browser's `location` object */
8198 var BrowserLocationConfig = /** @class */ (function () {
8199 function BrowserLocationConfig(router, _isHtml5) {
8200 if (_isHtml5 === void 0) { _isHtml5 = false; }
8201 this._isHtml5 = _isHtml5;
8202 this._baseHref = undefined;
8203 this._hashPrefix = '';
8204 }
8205 BrowserLocationConfig.prototype.port = function () {
8206 if (location.port) {
8207 return Number(location.port);
8208 }
8209 return this.protocol() === 'https' ? 443 : 80;
8210 };
8211 BrowserLocationConfig.prototype.protocol = function () {
8212 return location.protocol.replace(/:/g, '');
8213 };
8214 BrowserLocationConfig.prototype.host = function () {
8215 return location.hostname;
8216 };
8217 BrowserLocationConfig.prototype.html5Mode = function () {
8218 return this._isHtml5;
8219 };
8220 BrowserLocationConfig.prototype.hashPrefix = function (newprefix) {
8221 return isDefined(newprefix) ? (this._hashPrefix = newprefix) : this._hashPrefix;
8222 };
8223 BrowserLocationConfig.prototype.baseHref = function (href) {
8224 if (isDefined(href))
8225 this._baseHref = href;
8226 if (isUndefined(this._baseHref))
8227 this._baseHref = this.getBaseHref();
8228 return this._baseHref;
8229 };
8230 BrowserLocationConfig.prototype.getBaseHref = function () {
8231 var baseTag = document.getElementsByTagName('base')[0];
8232 if (baseTag && baseTag.href) {
8233 return baseTag.href.replace(/^([^/:]*:)?\/\/[^/]*/, '');
8234 }
8235 return this._isHtml5 ? '/' : location.pathname || '/';
8236 };
8237 BrowserLocationConfig.prototype.dispose = function () { };
8238 return BrowserLocationConfig;
8239 }());
8240
8241 function servicesPlugin(router) {
8242 services.$injector = $injector;
8243 services.$q = $q;
8244 return { name: 'vanilla.services', $q: $q, $injector: $injector, dispose: function () { return null; } };
8245 }
8246 /** A `UIRouterPlugin` uses the browser hash to get/set the current location */
8247 var hashLocationPlugin = locationPluginFactory('vanilla.hashBangLocation', false, HashLocationService, BrowserLocationConfig);
8248 /** A `UIRouterPlugin` that gets/sets the current location using the browser's `location` and `history` apis */
8249 var pushStateLocationPlugin = locationPluginFactory('vanilla.pushStateLocation', true, PushStateLocationService, BrowserLocationConfig);
8250 /** A `UIRouterPlugin` that gets/sets the current location from an in-memory object */
8251 var memoryLocationPlugin = locationPluginFactory('vanilla.memoryLocation', false, MemoryLocationService, MemoryLocationConfig);
8252
8253 var UIRouterPluginBase = /** @class */ (function () {
8254 function UIRouterPluginBase() {
8255 }
8256 UIRouterPluginBase.prototype.dispose = function (router) { };
8257 return UIRouterPluginBase;
8258 }());
8259
8260 var index = /*#__PURE__*/Object.freeze({
8261 __proto__: null,
8262 root: root,
8263 fromJson: fromJson,
8264 toJson: toJson,
8265 forEach: forEach,
8266 extend: extend,
8267 equals: equals,
8268 identity: identity,
8269 noop: noop,
8270 createProxyFunctions: createProxyFunctions,
8271 inherit: inherit,
8272 inArray: inArray,
8273 _inArray: _inArray,
8274 removeFrom: removeFrom,
8275 _removeFrom: _removeFrom,
8276 pushTo: pushTo,
8277 _pushTo: _pushTo,
8278 deregAll: deregAll,
8279 defaults: defaults,
8280 mergeR: mergeR,
8281 ancestors: ancestors,
8282 pick: pick,
8283 omit: omit,
8284 pluck: pluck,
8285 filter: filter,
8286 find: find,
8287 mapObj: mapObj,
8288 map: map,
8289 values: values,
8290 allTrueR: allTrueR,
8291 anyTrueR: anyTrueR,
8292 unnestR: unnestR,
8293 flattenR: flattenR,
8294 pushR: pushR,
8295 uniqR: uniqR,
8296 unnest: unnest,
8297 flatten: flatten,
8298 assertPredicate: assertPredicate,
8299 assertMap: assertMap,
8300 assertFn: assertFn,
8301 pairs: pairs,
8302 arrayTuples: arrayTuples,
8303 applyPairs: applyPairs,
8304 tail: tail,
8305 copy: copy,
8306 _extend: _extend,
8307 silenceUncaughtInPromise: silenceUncaughtInPromise,
8308 silentRejection: silentRejection,
8309 makeStub: makeStub,
8310 services: services,
8311 Glob: Glob,
8312 curry: curry,
8313 compose: compose,
8314 pipe: pipe,
8315 prop: prop,
8316 propEq: propEq,
8317 parse: parse,
8318 not: not,
8319 and: and,
8320 or: or,
8321 all: all,
8322 any: any,
8323 is: is,
8324 eq: eq,
8325 val: val,
8326 invoke: invoke,
8327 pattern: pattern,
8328 isUndefined: isUndefined,
8329 isDefined: isDefined,
8330 isNull: isNull,
8331 isNullOrUndefined: isNullOrUndefined,
8332 isFunction: isFunction,
8333 isNumber: isNumber,
8334 isString: isString,
8335 isObject: isObject,
8336 isArray: isArray,
8337 isDate: isDate,
8338 isRegExp: isRegExp,
8339 isInjectable: isInjectable,
8340 isPromise: isPromise,
8341 Queue: Queue,
8342 maxLength: maxLength,
8343 padString: padString,
8344 kebobString: kebobString,
8345 functionToString: functionToString,
8346 fnToString: fnToString,
8347 stringify: stringify,
8348 beforeAfterSubstr: beforeAfterSubstr,
8349 hostRegex: hostRegex,
8350 stripLastPathElement: stripLastPathElement,
8351 splitHash: splitHash,
8352 splitQuery: splitQuery,
8353 splitEqual: splitEqual,
8354 trimHashVal: trimHashVal,
8355 splitOnDelim: splitOnDelim,
8356 joinNeighborsR: joinNeighborsR,
8357 get Category () { return exports.Category; },
8358 Trace: Trace,
8359 trace: trace,
8360 get DefType () { return exports.DefType; },
8361 Param: Param,
8362 ParamTypes: ParamTypes,
8363 StateParams: StateParams,
8364 ParamType: ParamType,
8365 PathNode: PathNode,
8366 PathUtils: PathUtils,
8367 resolvePolicies: resolvePolicies,
8368 defaultResolvePolicy: defaultResolvePolicy,
8369 Resolvable: Resolvable,
8370 NATIVE_INJECTOR_TOKEN: NATIVE_INJECTOR_TOKEN,
8371 ResolveContext: ResolveContext,
8372 resolvablesBuilder: resolvablesBuilder,
8373 StateBuilder: StateBuilder,
8374 StateObject: StateObject,
8375 StateMatcher: StateMatcher,
8376 StateQueueManager: StateQueueManager,
8377 StateRegistry: StateRegistry,
8378 StateService: StateService,
8379 TargetState: TargetState,
8380 get TransitionHookPhase () { return exports.TransitionHookPhase; },
8381 get TransitionHookScope () { return exports.TransitionHookScope; },
8382 HookBuilder: HookBuilder,
8383 matchState: matchState,
8384 RegisteredHook: RegisteredHook,
8385 makeEvent: makeEvent,
8386 get RejectType () { return exports.RejectType; },
8387 Rejection: Rejection,
8388 Transition: Transition,
8389 TransitionHook: TransitionHook,
8390 TransitionEventType: TransitionEventType,
8391 defaultTransOpts: defaultTransOpts,
8392 TransitionService: TransitionService,
8393 UrlRules: UrlRules,
8394 UrlConfig: UrlConfig,
8395 UrlMatcher: UrlMatcher,
8396 ParamFactory: ParamFactory,
8397 UrlMatcherFactory: UrlMatcherFactory,
8398 UrlRouter: UrlRouter,
8399 UrlRuleFactory: UrlRuleFactory,
8400 BaseUrlRule: BaseUrlRule,
8401 UrlService: UrlService,
8402 ViewService: ViewService,
8403 UIRouterGlobals: UIRouterGlobals,
8404 UIRouter: UIRouter,
8405 $q: $q,
8406 $injector: $injector,
8407 BaseLocationServices: BaseLocationServices,
8408 HashLocationService: HashLocationService,
8409 MemoryLocationService: MemoryLocationService,
8410 PushStateLocationService: PushStateLocationService,
8411 MemoryLocationConfig: MemoryLocationConfig,
8412 BrowserLocationConfig: BrowserLocationConfig,
8413 keyValsToObjectR: keyValsToObjectR,
8414 getParams: getParams,
8415 parseUrl: parseUrl$1,
8416 buildUrl: buildUrl,
8417 locationPluginFactory: locationPluginFactory,
8418 servicesPlugin: servicesPlugin,
8419 hashLocationPlugin: hashLocationPlugin,
8420 pushStateLocationPlugin: pushStateLocationPlugin,
8421 memoryLocationPlugin: memoryLocationPlugin,
8422 UIRouterPluginBase: UIRouterPluginBase
8423 });
8424
8425 /** @publicapi @module ng1 */ /** */
8426 /** @internalapi */
8427 function getNg1ViewConfigFactory() {
8428 var templateFactory = null;
8429 return function (path, view) {
8430 templateFactory = templateFactory || services.$injector.get('$templateFactory');
8431 return [new Ng1ViewConfig(path, view, templateFactory)];
8432 };
8433 }
8434 /** @internalapi */
8435 var hasAnyKey = function (keys, obj) { return keys.reduce(function (acc, key) { return acc || isDefined(obj[key]); }, false); };
8436 /**
8437 * This is a [[StateBuilder.builder]] function for angular1 `views`.
8438 *
8439 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
8440 * handles the `views` property with logic specific to @uirouter/angularjs (ng1).
8441 *
8442 * If no `views: {}` property exists on the [[StateDeclaration]], then it creates the `views` object
8443 * and applies the state-level configuration to a view named `$default`.
8444 *
8445 * @internalapi
8446 */
8447 function ng1ViewsBuilder(state) {
8448 // Do not process root state
8449 if (!state.parent)
8450 return {};
8451 var tplKeys = ['templateProvider', 'templateUrl', 'template', 'notify', 'async'], ctrlKeys = ['controller', 'controllerProvider', 'controllerAs', 'resolveAs'], compKeys = ['component', 'bindings', 'componentProvider'], nonCompKeys = tplKeys.concat(ctrlKeys), allViewKeys = compKeys.concat(nonCompKeys);
8452 // Do not allow a state to have both state-level props and also a `views: {}` property.
8453 // A state without a `views: {}` property can declare properties for the `$default` view as properties of the state.
8454 // However, the `$default` approach should not be mixed with a separate `views: ` block.
8455 if (isDefined(state.views) && hasAnyKey(allViewKeys, state)) {
8456 throw new Error("State '" + state.name + "' has a 'views' object. " +
8457 "It cannot also have \"view properties\" at the state level. " +
8458 "Move the following properties into a view (in the 'views' object): " +
8459 (" " + allViewKeys.filter(function (key) { return isDefined(state[key]); }).join(', ')));
8460 }
8461 var views = {}, viewsObject = state.views || { $default: pick(state, allViewKeys) };
8462 forEach(viewsObject, function (config, name) {
8463 // Account for views: { "": { template... } }
8464 name = name || '$default';
8465 // Account for views: { header: "headerComponent" }
8466 if (isString(config))
8467 config = { component: config };
8468 // Make a shallow copy of the config object
8469 config = extend({}, config);
8470 // Do not allow a view to mix props for component-style view with props for template/controller-style view
8471 if (hasAnyKey(compKeys, config) && hasAnyKey(nonCompKeys, config)) {
8472 throw new Error("Cannot combine: " + compKeys.join('|') + " with: " + nonCompKeys.join('|') + " in stateview: '" + name + "@" + state.name + "'");
8473 }
8474 config.resolveAs = config.resolveAs || '$resolve';
8475 config.$type = 'ng1';
8476 config.$context = state;
8477 config.$name = name;
8478 var normalized = ViewService.normalizeUIViewTarget(config.$context, config.$name);
8479 config.$uiViewName = normalized.uiViewName;
8480 config.$uiViewContextAnchor = normalized.uiViewContextAnchor;
8481 views[name] = config;
8482 });
8483 return views;
8484 }
8485 /** @hidden */
8486 var id$1 = 0;
8487 /** @internalapi */
8488 var Ng1ViewConfig = /** @class */ (function () {
8489 function Ng1ViewConfig(path, viewDecl, factory) {
8490 var _this = this;
8491 this.path = path;
8492 this.viewDecl = viewDecl;
8493 this.factory = factory;
8494 this.$id = id$1++;
8495 this.loaded = false;
8496 this.getTemplate = function (uiView, context) {
8497 return _this.component
8498 ? _this.factory.makeComponentTemplate(uiView, context, _this.component, _this.viewDecl.bindings)
8499 : _this.template;
8500 };
8501 }
8502 Ng1ViewConfig.prototype.load = function () {
8503 var _this = this;
8504 var $q = services.$q;
8505 var context = new ResolveContext(this.path);
8506 var params = this.path.reduce(function (acc, node) { return extend(acc, node.paramValues); }, {});
8507 var promises = {
8508 template: $q.when(this.factory.fromConfig(this.viewDecl, params, context)),
8509 controller: $q.when(this.getController(context)),
8510 };
8511 return $q.all(promises).then(function (results) {
8512 trace.traceViewServiceEvent('Loaded', _this);
8513 _this.controller = results.controller;
8514 extend(_this, results.template); // Either { template: "tpl" } or { component: "cmpName" }
8515 return _this;
8516 });
8517 };
8518 /**
8519 * Gets the controller for a view configuration.
8520 *
8521 * @returns {Function|Promise.<Function>} Returns a controller, or a promise that resolves to a controller.
8522 */
8523 Ng1ViewConfig.prototype.getController = function (context) {
8524 var provider = this.viewDecl.controllerProvider;
8525 if (!isInjectable(provider))
8526 return this.viewDecl.controller;
8527 var deps = services.$injector.annotate(provider);
8528 var providerFn = isArray(provider) ? tail(provider) : provider;
8529 var resolvable = new Resolvable('', providerFn, deps);
8530 return resolvable.get(context);
8531 };
8532 return Ng1ViewConfig;
8533 }());
8534
8535 /** @publicapi @module view */ /** */
8536 /**
8537 * Service which manages loading of templates from a ViewConfig.
8538 */
8539 var TemplateFactory = /** @class */ (function () {
8540 function TemplateFactory() {
8541 var _this = this;
8542 /** @hidden */ this._useHttp = ng.version.minor < 3;
8543 /** @hidden */ this.$get = [
8544 '$http',
8545 '$templateCache',
8546 '$injector',
8547 function ($http, $templateCache, $injector) {
8548 _this.$templateRequest = $injector.has && $injector.has('$templateRequest') && $injector.get('$templateRequest');
8549 _this.$http = $http;
8550 _this.$templateCache = $templateCache;
8551 return _this;
8552 },
8553 ];
8554 }
8555 /** @hidden */
8556 TemplateFactory.prototype.useHttpService = function (value) {
8557 this._useHttp = value;
8558 };
8559 /**
8560 * Creates a template from a configuration object.
8561 *
8562 * @param config Configuration object for which to load a template.
8563 * The following properties are search in the specified order, and the first one
8564 * that is defined is used to create the template:
8565 *
8566 * @param params Parameters to pass to the template function.
8567 * @param context The resolve context associated with the template's view
8568 *
8569 * @return {string|object} The template html as a string, or a promise for
8570 * that string,or `null` if no template is configured.
8571 */
8572 TemplateFactory.prototype.fromConfig = function (config, params, context) {
8573 var defaultTemplate = '<ui-view></ui-view>';
8574 var asTemplate = function (result) { return services.$q.when(result).then(function (str) { return ({ template: str }); }); };
8575 var asComponent = function (result) { return services.$q.when(result).then(function (str) { return ({ component: str }); }); };
8576 return isDefined(config.template)
8577 ? asTemplate(this.fromString(config.template, params))
8578 : isDefined(config.templateUrl)
8579 ? asTemplate(this.fromUrl(config.templateUrl, params))
8580 : isDefined(config.templateProvider)
8581 ? asTemplate(this.fromProvider(config.templateProvider, params, context))
8582 : isDefined(config.component)
8583 ? asComponent(config.component)
8584 : isDefined(config.componentProvider)
8585 ? asComponent(this.fromComponentProvider(config.componentProvider, params, context))
8586 : asTemplate(defaultTemplate);
8587 };
8588 /**
8589 * Creates a template from a string or a function returning a string.
8590 *
8591 * @param template html template as a string or function that returns an html template as a string.
8592 * @param params Parameters to pass to the template function.
8593 *
8594 * @return {string|object} The template html as a string, or a promise for that
8595 * string.
8596 */
8597 TemplateFactory.prototype.fromString = function (template, params) {
8598 return isFunction(template) ? template(params) : template;
8599 };
8600 /**
8601 * Loads a template from the a URL via `$http` and `$templateCache`.
8602 *
8603 * @param {string|Function} url url of the template to load, or a function
8604 * that returns a url.
8605 * @param {Object} params Parameters to pass to the url function.
8606 * @return {string|Promise.<string>} The template html as a string, or a promise
8607 * for that string.
8608 */
8609 TemplateFactory.prototype.fromUrl = function (url, params) {
8610 if (isFunction(url))
8611 url = url(params);
8612 if (url == null)
8613 return null;
8614 if (this._useHttp) {
8615 return this.$http
8616 .get(url, { cache: this.$templateCache, headers: { Accept: 'text/html' } })
8617 .then(function (response) {
8618 return response.data;
8619 });
8620 }
8621 return this.$templateRequest(url);
8622 };
8623 /**
8624 * Creates a template by invoking an injectable provider function.
8625 *
8626 * @param provider Function to invoke via `locals`
8627 * @param {Function} injectFn a function used to invoke the template provider
8628 * @return {string|Promise.<string>} The template html as a string, or a promise
8629 * for that string.
8630 */
8631 TemplateFactory.prototype.fromProvider = function (provider, params, context) {
8632 var deps = services.$injector.annotate(provider);
8633 var providerFn = isArray(provider) ? tail(provider) : provider;
8634 var resolvable = new Resolvable('', providerFn, deps);
8635 return resolvable.get(context);
8636 };
8637 /**
8638 * Creates a component's template by invoking an injectable provider function.
8639 *
8640 * @param provider Function to invoke via `locals`
8641 * @param {Function} injectFn a function used to invoke the template provider
8642 * @return {string} The template html as a string: "<component-name input1='::$resolve.foo'></component-name>".
8643 */
8644 TemplateFactory.prototype.fromComponentProvider = function (provider, params, context) {
8645 var deps = services.$injector.annotate(provider);
8646 var providerFn = isArray(provider) ? tail(provider) : provider;
8647 var resolvable = new Resolvable('', providerFn, deps);
8648 return resolvable.get(context);
8649 };
8650 /**
8651 * Creates a template from a component's name
8652 *
8653 * This implements route-to-component.
8654 * It works by retrieving the component (directive) metadata from the injector.
8655 * It analyses the component's bindings, then constructs a template that instantiates the component.
8656 * The template wires input and output bindings to resolves or from the parent component.
8657 *
8658 * @param uiView {object} The parent ui-view (for binding outputs to callbacks)
8659 * @param context The ResolveContext (for binding outputs to callbacks returned from resolves)
8660 * @param component {string} Component's name in camel case.
8661 * @param bindings An object defining the component's bindings: {foo: '<'}
8662 * @return {string} The template as a string: "<component-name input1='::$resolve.foo'></component-name>".
8663 */
8664 TemplateFactory.prototype.makeComponentTemplate = function (uiView, context, component, bindings) {
8665 bindings = bindings || {};
8666 // Bind once prefix
8667 var prefix = ng.version.minor >= 3 ? '::' : '';
8668 // Convert to kebob name. Add x- prefix if the string starts with `x-` or `data-`
8669 var kebob = function (camelCase) {
8670 var kebobed = kebobString(camelCase);
8671 return /^(x|data)-/.exec(kebobed) ? "x-" + kebobed : kebobed;
8672 };
8673 var attributeTpl = function (input) {
8674 var name = input.name, type = input.type;
8675 var attrName = kebob(name);
8676 // If the ui-view has an attribute which matches a binding on the routed component
8677 // then pass that attribute through to the routed component template.
8678 // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:`
8679 if (uiView.attr(attrName) && !bindings[name])
8680 return attrName + "='" + uiView.attr(attrName) + "'";
8681 var resolveName = bindings[name] || name;
8682 // Pre-evaluate the expression for "@" bindings by enclosing in {{ }}
8683 // some-attr="{{ ::$resolve.someResolveName }}"
8684 if (type === '@')
8685 return attrName + "='{{" + prefix + "$resolve." + resolveName + "}}'";
8686 // Wire "&" callbacks to resolves that return a callback function
8687 // Get the result of the resolve (should be a function) and annotate it to get its arguments.
8688 // some-attr="$resolve.someResolveResultName(foo, bar)"
8689 if (type === '&') {
8690 var res = context.getResolvable(resolveName);
8691 var fn = res && res.data;
8692 var args = (fn && services.$injector.annotate(fn)) || [];
8693 // account for array style injection, i.e., ['foo', function(foo) {}]
8694 var arrayIdxStr = isArray(fn) ? "[" + (fn.length - 1) + "]" : '';
8695 return attrName + "='$resolve." + resolveName + arrayIdxStr + "(" + args.join(',') + ")'";
8696 }
8697 // some-attr="::$resolve.someResolveName"
8698 return attrName + "='" + prefix + "$resolve." + resolveName + "'";
8699 };
8700 var attrs = getComponentBindings(component).map(attributeTpl).join(' ');
8701 var kebobName = kebob(component);
8702 return "<" + kebobName + " " + attrs + "></" + kebobName + ">";
8703 };
8704 return TemplateFactory;
8705 }());
8706 // Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&')
8707 function getComponentBindings(name) {
8708 var cmpDefs = services.$injector.get(name + 'Directive'); // could be multiple
8709 if (!cmpDefs || !cmpDefs.length)
8710 throw new Error("Unable to find component named '" + name + "'");
8711 return cmpDefs.map(getBindings).reduce(unnestR, []);
8712 }
8713 // Given a directive definition, find its object input attributes
8714 // Use different properties, depending on the type of directive (component, bindToController, normal)
8715 var getBindings = function (def) {
8716 if (isObject(def.bindToController))
8717 return scopeBindings(def.bindToController);
8718 return scopeBindings(def.scope);
8719 };
8720 // for ng 1.2 style, process the scope: { input: "=foo" }
8721 // for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object
8722 var scopeBindings = function (bindingsObj) {
8723 return Object.keys(bindingsObj || {})
8724 // [ 'input', [ '=foo', '=', 'foo' ] ]
8725 .map(function (key) { return [key, /^([=<@&])[?]?(.*)/.exec(bindingsObj[key])]; })
8726 // skip malformed values
8727 .filter(function (tuple) { return isDefined(tuple) && isArray(tuple[1]); })
8728 // { name: ('foo' || 'input'), type: '=' }
8729 .map(function (tuple) { return ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }); });
8730 };
8731
8732 /** @publicapi @module ng1 */ /** */
8733 /**
8734 * The Angular 1 `StateProvider`
8735 *
8736 * The `$stateProvider` works similar to Angular's v1 router, but it focuses purely
8737 * on state.
8738 *
8739 * A state corresponds to a "place" in the application in terms of the overall UI and
8740 * navigation. A state describes (via the controller / template / view properties) what
8741 * the UI looks like and does at that place.
8742 *
8743 * States often have things in common, and the primary way of factoring out these
8744 * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
8745 * nested states.
8746 *
8747 * The `$stateProvider` provides interfaces to declare these states for your app.
8748 */
8749 var StateProvider = /** @class */ (function () {
8750 function StateProvider(stateRegistry, stateService) {
8751 this.stateRegistry = stateRegistry;
8752 this.stateService = stateService;
8753 createProxyFunctions(val(StateProvider.prototype), this, val(this));
8754 }
8755 /**
8756 * Decorates states when they are registered
8757 *
8758 * Allows you to extend (carefully) or override (at your own peril) the
8759 * `stateBuilder` object used internally by [[StateRegistry]].
8760 * This can be used to add custom functionality to ui-router,
8761 * for example inferring templateUrl based on the state name.
8762 *
8763 * When passing only a name, it returns the current (original or decorated) builder
8764 * function that matches `name`.
8765 *
8766 * The builder functions that can be decorated are listed below. Though not all
8767 * necessarily have a good use case for decoration, that is up to you to decide.
8768 *
8769 * In addition, users can attach custom decorators, which will generate new
8770 * properties within the state's internal definition. There is currently no clear
8771 * use-case for this beyond accessing internal states (i.e. $state.$current),
8772 * however, expect this to become increasingly relevant as we introduce additional
8773 * meta-programming features.
8774 *
8775 * **Warning**: Decorators should not be interdependent because the order of
8776 * execution of the builder functions in non-deterministic. Builder functions
8777 * should only be dependent on the state definition object and super function.
8778 *
8779 *
8780 * Existing builder functions and current return values:
8781 *
8782 * - **parent** `{object}` - returns the parent state object.
8783 * - **data** `{object}` - returns state data, including any inherited data that is not
8784 * overridden by own values (if any).
8785 * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
8786 * or `null`.
8787 * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
8788 * navigable).
8789 * - **params** `{object}` - returns an array of state params that are ensured to
8790 * be a super-set of parent's params.
8791 * - **views** `{object}` - returns a views object where each key is an absolute view
8792 * name (i.e. "viewName@stateName") and each value is the config object
8793 * (template, controller) for the view. Even when you don't use the views object
8794 * explicitly on a state config, one is still created for you internally.
8795 * So by decorating this builder function you have access to decorating template
8796 * and controller properties.
8797 * - **ownParams** `{object}` - returns an array of params that belong to the state,
8798 * not including any params defined by ancestor states.
8799 * - **path** `{string}` - returns the full path from the root down to this state.
8800 * Needed for state activation.
8801 * - **includes** `{object}` - returns an object that includes every state that
8802 * would pass a `$state.includes()` test.
8803 *
8804 * #### Example:
8805 * Override the internal 'views' builder with a function that takes the state
8806 * definition, and a reference to the internal function being overridden:
8807 * ```js
8808 * $stateProvider.decorator('views', function (state, parent) {
8809 * let result = {},
8810 * views = parent(state);
8811 *
8812 * angular.forEach(views, function (config, name) {
8813 * let autoName = (state.name + '.' + name).replace('.', '/');
8814 * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
8815 * result[name] = config;
8816 * });
8817 * return result;
8818 * });
8819 *
8820 * $stateProvider.state('home', {
8821 * views: {
8822 * 'contact.list': { controller: 'ListController' },
8823 * 'contact.item': { controller: 'ItemController' }
8824 * }
8825 * });
8826 * ```
8827 *
8828 *
8829 * ```js
8830 * // Auto-populates list and item views with /partials/home/contact/list.html,
8831 * // and /partials/home/contact/item.html, respectively.
8832 * $state.go('home');
8833 * ```
8834 *
8835 * @param {string} name The name of the builder function to decorate.
8836 * @param {object} func A function that is responsible for decorating the original
8837 * builder function. The function receives two parameters:
8838 *
8839 * - `{object}` - state - The state config object.
8840 * - `{object}` - super - The original builder function.
8841 *
8842 * @return {object} $stateProvider - $stateProvider instance
8843 */
8844 StateProvider.prototype.decorator = function (name, func) {
8845 return this.stateRegistry.decorator(name, func) || this;
8846 };
8847 StateProvider.prototype.state = function (name, definition) {
8848 if (isObject(name)) {
8849 definition = name;
8850 }
8851 else {
8852 definition.name = name;
8853 }
8854 this.stateRegistry.register(definition);
8855 return this;
8856 };
8857 /**
8858 * Registers an invalid state handler
8859 *
8860 * This is a passthrough to [[StateService.onInvalid]] for ng1.
8861 */
8862 StateProvider.prototype.onInvalid = function (callback) {
8863 return this.stateService.onInvalid(callback);
8864 };
8865 return StateProvider;
8866 }());
8867
8868 /** @publicapi @module ng1 */ /** */
8869 /**
8870 * This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`,
8871 * `onRetain` callback hooks on a [[Ng1StateDeclaration]].
8872 *
8873 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
8874 * ensures that those hooks are injectable for @uirouter/angularjs (ng1).
8875 *
8876 * @internalapi
8877 */
8878 var getStateHookBuilder = function (hookName) {
8879 return function stateHookBuilder(stateObject) {
8880 var hook = stateObject[hookName];
8881 var pathname = hookName === 'onExit' ? 'from' : 'to';
8882 function decoratedNg1Hook(trans, state) {
8883 var resolveContext = new ResolveContext(trans.treeChanges(pathname));
8884 var subContext = resolveContext.subContext(state.$$state());
8885 var locals = extend(getLocals(subContext), { $state$: state, $transition$: trans });
8886 return services.$injector.invoke(hook, this, locals);
8887 }
8888 return hook ? decoratedNg1Hook : undefined;
8889 };
8890 };
8891
8892 /** @publicapi @module ng1 */ /** */
8893 /**
8894 * Implements UI-Router LocationServices and LocationConfig using Angular 1's $location service
8895 * @internalapi
8896 */
8897 var Ng1LocationServices = /** @class */ (function () {
8898 function Ng1LocationServices($locationProvider) {
8899 // .onChange() registry
8900 this._urlListeners = [];
8901 this.$locationProvider = $locationProvider;
8902 var _lp = val($locationProvider);
8903 createProxyFunctions(_lp, this, _lp, ['hashPrefix']);
8904 }
8905 /**
8906 * Applys ng1-specific path parameter encoding
8907 *
8908 * The Angular 1 `$location` service is a bit weird.
8909 * It doesn't allow slashes to be encoded/decoded bi-directionally.
8910 *
8911 * See the writeup at https://github.com/angular-ui/ui-router/issues/2598
8912 *
8913 * This code patches the `path` parameter type so it encoded/decodes slashes as ~2F
8914 *
8915 * @param router
8916 */
8917 Ng1LocationServices.monkeyPatchPathParameterType = function (router) {
8918 var pathType = router.urlMatcherFactory.type('path');
8919 pathType.encode = function (x) {
8920 return x != null ? x.toString().replace(/(~|\/)/g, function (m) { return ({ '~': '~~', '/': '~2F' }[m]); }) : x;
8921 };
8922 pathType.decode = function (x) {
8923 return x != null ? x.toString().replace(/(~~|~2F)/g, function (m) { return ({ '~~': '~', '~2F': '/' }[m]); }) : x;
8924 };
8925 };
8926 // eslint-disable-next-line @typescript-eslint/no-empty-function
8927 Ng1LocationServices.prototype.dispose = function () { };
8928 Ng1LocationServices.prototype.onChange = function (callback) {
8929 var _this = this;
8930 this._urlListeners.push(callback);
8931 return function () { return removeFrom(_this._urlListeners)(callback); };
8932 };
8933 Ng1LocationServices.prototype.html5Mode = function () {
8934 var html5Mode = this.$locationProvider.html5Mode();
8935 html5Mode = isObject(html5Mode) ? html5Mode.enabled : html5Mode;
8936 return html5Mode && this.$sniffer.history;
8937 };
8938 Ng1LocationServices.prototype.baseHref = function () {
8939 return this._baseHref || (this._baseHref = this.$browser.baseHref() || this.$window.location.pathname);
8940 };
8941 Ng1LocationServices.prototype.url = function (newUrl, replace, state) {
8942 if (replace === void 0) { replace = false; }
8943 if (isDefined(newUrl))
8944 this.$location.url(newUrl);
8945 if (replace)
8946 this.$location.replace();
8947 if (state)
8948 this.$location.state(state);
8949 return this.$location.url();
8950 };
8951 Ng1LocationServices.prototype._runtimeServices = function ($rootScope, $location, $sniffer, $browser, $window) {
8952 var _this = this;
8953 this.$location = $location;
8954 this.$sniffer = $sniffer;
8955 this.$browser = $browser;
8956 this.$window = $window;
8957 // Bind $locationChangeSuccess to the listeners registered in LocationService.onChange
8958 $rootScope.$on('$locationChangeSuccess', function (evt) { return _this._urlListeners.forEach(function (fn) { return fn(evt); }); });
8959 var _loc = val($location);
8960 // Bind these LocationService functions to $location
8961 createProxyFunctions(_loc, this, _loc, ['replace', 'path', 'search', 'hash']);
8962 // Bind these LocationConfig functions to $location
8963 createProxyFunctions(_loc, this, _loc, ['port', 'protocol', 'host']);
8964 };
8965 return Ng1LocationServices;
8966 }());
8967
8968 /** @publicapi @module url */ /** */
8969 /**
8970 * Manages rules for client-side URL
8971 *
8972 * ### Deprecation warning:
8973 * This class is now considered to be an internal API
8974 * Use the [[UrlService]] instead.
8975 * For configuring URL rules, use the [[UrlRulesApi]] which can be found as [[UrlService.rules]].
8976 *
8977 * This class manages the router rules for what to do when the URL changes.
8978 *
8979 * This provider remains for backwards compatibility.
8980 *
8981 * @internalapi
8982 * @deprecated
8983 */
8984 var UrlRouterProvider = /** @class */ (function () {
8985 /** @hidden */
8986 function UrlRouterProvider(/** @hidden */ router) {
8987 this.router = router;
8988 }
8989 UrlRouterProvider.injectableHandler = function (router, handler) {
8990 return function (match) { return services.$injector.invoke(handler, null, { $match: match, $stateParams: router.globals.params }); };
8991 };
8992 /** @hidden */
8993 UrlRouterProvider.prototype.$get = function () {
8994 var urlService = this.router.urlService;
8995 this.router.urlRouter.update(true);
8996 if (!urlService.interceptDeferred)
8997 urlService.listen();
8998 return this.router.urlRouter;
8999 };
9000 /**
9001 * Registers a url handler function.
9002 *
9003 * Registers a low level url handler (a `rule`).
9004 * A rule detects specific URL patterns and returns a redirect, or performs some action.
9005 *
9006 * If a rule returns a string, the URL is replaced with the string, and all rules are fired again.
9007 *
9008 * #### Example:
9009 * ```js
9010 * var app = angular.module('app', ['ui.router.router']);
9011 *
9012 * app.config(function ($urlRouterProvider) {
9013 * // Here's an example of how you might allow case insensitive urls
9014 * $urlRouterProvider.rule(function ($injector, $location) {
9015 * var path = $location.path(),
9016 * normalized = path.toLowerCase();
9017 *
9018 * if (path !== normalized) {
9019 * return normalized;
9020 * }
9021 * });
9022 * });
9023 * ```
9024 *
9025 * @param ruleFn
9026 * Handler function that takes `$injector` and `$location` services as arguments.
9027 * You can use them to detect a url and return a different url as a string.
9028 *
9029 * @return [[UrlRouterProvider]] (`this`)
9030 */
9031 UrlRouterProvider.prototype.rule = function (ruleFn) {
9032 var _this = this;
9033 if (!isFunction(ruleFn))
9034 throw new Error("'rule' must be a function");
9035 var match = function () { return ruleFn(services.$injector, _this.router.locationService); };
9036 var rule = new BaseUrlRule(match, identity);
9037 this.router.urlService.rules.rule(rule);
9038 return this;
9039 };
9040 /**
9041 * Defines the path or behavior to use when no url can be matched.
9042 *
9043 * #### Example:
9044 * ```js
9045 * var app = angular.module('app', ['ui.router.router']);
9046 *
9047 * app.config(function ($urlRouterProvider) {
9048 * // if the path doesn't match any of the urls you configured
9049 * // otherwise will take care of routing the user to the
9050 * // specified url
9051 * $urlRouterProvider.otherwise('/index');
9052 *
9053 * // Example of using function rule as param
9054 * $urlRouterProvider.otherwise(function ($injector, $location) {
9055 * return '/a/valid/url';
9056 * });
9057 * });
9058 * ```
9059 *
9060 * @param rule
9061 * The url path you want to redirect to or a function rule that returns the url path or performs a `$state.go()`.
9062 * The function version is passed two params: `$injector` and `$location` services, and should return a url string.
9063 *
9064 * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
9065 */
9066 UrlRouterProvider.prototype.otherwise = function (rule) {
9067 var _this = this;
9068 var urlRules = this.router.urlService.rules;
9069 if (isString(rule)) {
9070 urlRules.otherwise(rule);
9071 }
9072 else if (isFunction(rule)) {
9073 urlRules.otherwise(function () { return rule(services.$injector, _this.router.locationService); });
9074 }
9075 else {
9076 throw new Error("'rule' must be a string or function");
9077 }
9078 return this;
9079 };
9080 /**
9081 * Registers a handler for a given url matching.
9082 *
9083 * If the handler is a string, it is
9084 * treated as a redirect, and is interpolated according to the syntax of match
9085 * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
9086 *
9087 * If the handler is a function, it is injectable.
9088 * It gets invoked if `$location` matches.
9089 * You have the option of inject the match object as `$match`.
9090 *
9091 * The handler can return
9092 *
9093 * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
9094 * will continue trying to find another one that matches.
9095 * - **string** which is treated as a redirect and passed to `$location.url()`
9096 * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
9097 *
9098 * #### Example:
9099 * ```js
9100 * var app = angular.module('app', ['ui.router.router']);
9101 *
9102 * app.config(function ($urlRouterProvider) {
9103 * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
9104 * if ($state.$current.navigable !== state ||
9105 * !equalForKeys($match, $stateParams) {
9106 * $state.transitionTo(state, $match, false);
9107 * }
9108 * });
9109 * });
9110 * ```
9111 *
9112 * @param what A pattern string to match, compiled as a [[UrlMatcher]].
9113 * @param handler The path (or function that returns a path) that you want to redirect your user to.
9114 * @param ruleCallback [optional] A callback that receives the `rule` registered with [[UrlMatcher.rule]]
9115 *
9116 * Note: the handler may also invoke arbitrary code, such as `$state.go()`
9117 */
9118 UrlRouterProvider.prototype.when = function (what, handler) {
9119 if (isArray(handler) || isFunction(handler)) {
9120 handler = UrlRouterProvider.injectableHandler(this.router, handler);
9121 }
9122 this.router.urlService.rules.when(what, handler);
9123 return this;
9124 };
9125 /**
9126 * Disables monitoring of the URL.
9127 *
9128 * Call this method before UI-Router has bootstrapped.
9129 * It will stop UI-Router from performing the initial url sync.
9130 *
9131 * This can be useful to perform some asynchronous initialization before the router starts.
9132 * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL.
9133 *
9134 * #### Example:
9135 * ```js
9136 * var app = angular.module('app', ['ui.router']);
9137 *
9138 * app.config(function ($urlRouterProvider) {
9139 * // Prevent $urlRouter from automatically intercepting URL changes;
9140 * $urlRouterProvider.deferIntercept();
9141 * })
9142 *
9143 * app.run(function (MyService, $urlRouter, $http) {
9144 * $http.get("/stuff").then(function(resp) {
9145 * MyService.doStuff(resp.data);
9146 * $urlRouter.listen();
9147 * $urlRouter.sync();
9148 * });
9149 * });
9150 * ```
9151 *
9152 * @param defer Indicates whether to defer location change interception.
9153 * Passing no parameter is equivalent to `true`.
9154 */
9155 UrlRouterProvider.prototype.deferIntercept = function (defer) {
9156 this.router.urlService.deferIntercept(defer);
9157 };
9158 return UrlRouterProvider;
9159 }());
9160
9161 /* eslint-disable @typescript-eslint/no-empty-function */
9162 ng.module('ui.router.angular1', []);
9163 var mod_init = ng.module('ui.router.init', ['ng']);
9164 var mod_util = ng.module('ui.router.util', ['ui.router.init']);
9165 var mod_rtr = ng.module('ui.router.router', ['ui.router.util']);
9166 var mod_state = ng.module('ui.router.state', ['ui.router.router', 'ui.router.util', 'ui.router.angular1']);
9167 var mod_main = ng.module('ui.router', ['ui.router.init', 'ui.router.state', 'ui.router.angular1']);
9168 var mod_cmpt = ng.module('ui.router.compat', ['ui.router']);
9169 var router = null;
9170 $uiRouterProvider.$inject = ['$locationProvider'];
9171 /** This angular 1 provider instantiates a Router and exposes its services via the angular injector */
9172 function $uiRouterProvider($locationProvider) {
9173 // Create a new instance of the Router when the $uiRouterProvider is initialized
9174 router = this.router = new UIRouter();
9175 router.stateProvider = new StateProvider(router.stateRegistry, router.stateService);
9176 // Apply ng1 specific StateBuilder code for `views`, `resolve`, and `onExit/Retain/Enter` properties
9177 router.stateRegistry.decorator('views', ng1ViewsBuilder);
9178 router.stateRegistry.decorator('onExit', getStateHookBuilder('onExit'));
9179 router.stateRegistry.decorator('onRetain', getStateHookBuilder('onRetain'));
9180 router.stateRegistry.decorator('onEnter', getStateHookBuilder('onEnter'));
9181 router.viewService._pluginapi._viewConfigFactory('ng1', getNg1ViewConfigFactory());
9182 // Disable decoding of params by UrlMatcherFactory because $location already handles this
9183 router.urlService.config._decodeParams = false;
9184 var ng1LocationService = (router.locationService = router.locationConfig = new Ng1LocationServices($locationProvider));
9185 Ng1LocationServices.monkeyPatchPathParameterType(router);
9186 // backwards compat: also expose router instance as $uiRouterProvider.router
9187 router['router'] = router;
9188 router['$get'] = $get;
9189 $get.$inject = ['$location', '$browser', '$window', '$sniffer', '$rootScope', '$http', '$templateCache'];
9190 function $get($location, $browser, $window, $sniffer, $rootScope, $http, $templateCache) {
9191 ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser, $window);
9192 delete router['router'];
9193 delete router['$get'];
9194 return router;
9195 }
9196 return router;
9197 }
9198 var getProviderFor = function (serviceName) { return [
9199 '$uiRouterProvider',
9200 function ($urp) {
9201 var service = $urp.router[serviceName];
9202 service['$get'] = function () { return service; };
9203 return service;
9204 },
9205 ]; };
9206 // This effectively calls $get() on `$uiRouterProvider` to trigger init (when ng enters runtime)
9207 runBlock.$inject = ['$injector', '$q', '$uiRouter'];
9208 function runBlock($injector, $q, $uiRouter) {
9209 services.$injector = $injector;
9210 services.$q = $q;
9211 // https://github.com/angular-ui/ui-router/issues/3678
9212 if (!Object.prototype.hasOwnProperty.call($injector, 'strictDi')) {
9213 try {
9214 $injector.invoke(function (checkStrictDi) { });
9215 }
9216 catch (error) {
9217 $injector.strictDi = !!/strict mode/.exec(error && error.toString());
9218 }
9219 }
9220 // The $injector is now available.
9221 // Find any resolvables that had dependency annotation deferred
9222 $uiRouter.stateRegistry
9223 .get()
9224 .map(function (x) { return x.$$state().resolvables; })
9225 .reduce(unnestR, [])
9226 .filter(function (x) { return x.deps === 'deferred'; })
9227 .forEach(function (resolvable) { return (resolvable.deps = $injector.annotate(resolvable.resolveFn, $injector.strictDi)); });
9228 }
9229 // $urlRouter service and $urlRouterProvider
9230 var getUrlRouterProvider = function (uiRouter) { return (uiRouter.urlRouterProvider = new UrlRouterProvider(uiRouter)); };
9231 // $state service and $stateProvider
9232 // $urlRouter service and $urlRouterProvider
9233 var getStateProvider = function () { return extend(router.stateProvider, { $get: function () { return router.stateService; } }); };
9234 watchDigests.$inject = ['$rootScope'];
9235 function watchDigests($rootScope) {
9236 $rootScope.$watch(function () {
9237 trace.approximateDigests++;
9238 });
9239 }
9240 mod_init.provider('$uiRouter', $uiRouterProvider);
9241 mod_rtr.provider('$urlRouter', ['$uiRouterProvider', getUrlRouterProvider]);
9242 mod_util.provider('$urlService', getProviderFor('urlService'));
9243 mod_util.provider('$urlMatcherFactory', ['$uiRouterProvider', function () { return router.urlMatcherFactory; }]);
9244 mod_util.provider('$templateFactory', function () { return new TemplateFactory(); });
9245 mod_state.provider('$stateRegistry', getProviderFor('stateRegistry'));
9246 mod_state.provider('$uiRouterGlobals', getProviderFor('globals'));
9247 mod_state.provider('$transitions', getProviderFor('transitionService'));
9248 mod_state.provider('$state', ['$uiRouterProvider', getStateProvider]);
9249 mod_state.factory('$stateParams', ['$uiRouter', function ($uiRouter) { return $uiRouter.globals.params; }]);
9250 mod_main.factory('$view', function () { return router.viewService; });
9251 mod_main.service('$trace', function () { return trace; });
9252 mod_main.run(watchDigests);
9253 mod_util.run(['$urlMatcherFactory', function ($urlMatcherFactory) { }]);
9254 mod_state.run(['$state', function ($state) { }]);
9255 mod_rtr.run(['$urlRouter', function ($urlRouter) { }]);
9256 mod_init.run(runBlock);
9257 /** @hidden TODO: find a place to move this */
9258 var getLocals = function (ctx) {
9259 var tokens = ctx.getTokens().filter(isString);
9260 var tuples = tokens.map(function (key) {
9261 var resolvable = ctx.getResolvable(key);
9262 var waitPolicy = ctx.getPolicy(resolvable).async;
9263 return [key, waitPolicy === 'NOWAIT' ? resolvable.promise : resolvable.data];
9264 });
9265 return tuples.reduce(applyPairs, {});
9266 };
9267
9268 /* eslint-disable @typescript-eslint/no-empty-interface */
9269 /** @hidden */
9270 function parseStateRef(ref) {
9271 var paramsOnly = ref.match(/^\s*({[^}]*})\s*$/);
9272 if (paramsOnly)
9273 ref = '(' + paramsOnly[1] + ')';
9274 var parsed = ref.replace(/\n/g, ' ').match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/);
9275 if (!parsed || parsed.length !== 4)
9276 throw new Error("Invalid state ref '" + ref + "'");
9277 return { state: parsed[1] || null, paramExpr: parsed[3] || null };
9278 }
9279 /** @hidden */
9280 function stateContext(el) {
9281 var $uiView = el.parent().inheritedData('$uiView');
9282 var path = parse('$cfg.path')($uiView);
9283 return path ? tail(path).state.name : undefined;
9284 }
9285 /** @hidden */
9286 function processedDef($state, $element, def) {
9287 var uiState = def.uiState || $state.current.name;
9288 var uiStateOpts = extend(defaultOpts($element, $state), def.uiStateOpts || {});
9289 var href = $state.href(uiState, def.uiStateParams, uiStateOpts);
9290 return { uiState: uiState, uiStateParams: def.uiStateParams, uiStateOpts: uiStateOpts, href: href };
9291 }
9292 /** @hidden */
9293 function getTypeInfo(el) {
9294 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
9295 var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]';
9296 var isForm = el[0].nodeName === 'FORM';
9297 return {
9298 attr: isForm ? 'action' : isSvg ? 'xlink:href' : 'href',
9299 isAnchor: el.prop('tagName').toUpperCase() === 'A',
9300 clickable: !isForm,
9301 };
9302 }
9303 /** @hidden */
9304 function clickHook(el, $state, $timeout, type, getDef) {
9305 return function (e) {
9306 var button = e.which || e.button, target = getDef();
9307 if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || el.attr('target'))) {
9308 // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
9309 var transition_1 = $timeout(function () {
9310 if (!el.attr('disabled')) {
9311 $state.go(target.uiState, target.uiStateParams, target.uiStateOpts);
9312 }
9313 });
9314 e.preventDefault();
9315 // if the state has no URL, ignore one preventDefault from the <a> directive.
9316 var ignorePreventDefaultCount_1 = type.isAnchor && !target.href ? 1 : 0;
9317 e.preventDefault = function () {
9318 if (ignorePreventDefaultCount_1-- <= 0)
9319 $timeout.cancel(transition_1);
9320 };
9321 }
9322 };
9323 }
9324 /** @hidden */
9325 function defaultOpts(el, $state) {
9326 return {
9327 relative: stateContext(el) || $state.$current,
9328 inherit: true,
9329 source: 'sref',
9330 };
9331 }
9332 /** @hidden */
9333 function bindEvents(element, scope, hookFn, uiStateOpts) {
9334 var events;
9335 if (uiStateOpts) {
9336 events = uiStateOpts.events;
9337 }
9338 if (!isArray(events)) {
9339 events = ['click'];
9340 }
9341 var on = element.on ? 'on' : 'bind';
9342 for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
9343 var event_1 = events_1[_i];
9344 element[on](event_1, hookFn);
9345 }
9346 scope.$on('$destroy', function () {
9347 var off = element.off ? 'off' : 'unbind';
9348 for (var _i = 0, events_2 = events; _i < events_2.length; _i++) {
9349 var event_2 = events_2[_i];
9350 element[off](event_2, hookFn);
9351 }
9352 });
9353 }
9354 /**
9355 * `ui-sref`: A directive for linking to a state
9356 *
9357 * A directive which links to a state (and optionally, parameters).
9358 * When clicked, this directive activates the linked state with the supplied parameter values.
9359 *
9360 * ### Linked State
9361 * The attribute value of the `ui-sref` is the name of the state to link to.
9362 *
9363 * #### Example:
9364 * This will activate the `home` state when the link is clicked.
9365 * ```html
9366 * <a ui-sref="home">Home</a>
9367 * ```
9368 *
9369 * ### Relative Links
9370 * You can also use relative state paths within `ui-sref`, just like a relative path passed to `$state.go()` ([[StateService.go]]).
9371 * You just need to be aware that the path is relative to the state that *created* the link.
9372 * This allows a state to create a relative `ui-sref` which always targets the same destination.
9373 *
9374 * #### Example:
9375 * Both these links are relative to the parent state, even when a child state is currently active.
9376 * ```html
9377 * <a ui-sref=".child1">child 1 state</a>
9378 * <a ui-sref=".child2">child 2 state</a>
9379 * ```
9380 *
9381 * This link activates the parent state.
9382 * ```html
9383 * <a ui-sref="^">Return</a>
9384 * ```
9385 *
9386 * ### hrefs
9387 * If the linked state has a URL, the directive will automatically generate and
9388 * update the `href` attribute (using the [[StateService.href]] method).
9389 *
9390 * #### Example:
9391 * Assuming the `users` state has a url of `/users/`
9392 * ```html
9393 * <a ui-sref="users" href="/users/">Users</a>
9394 * ```
9395 *
9396 * ### Parameter Values
9397 * In addition to the state name, a `ui-sref` can include parameter values which are applied when activating the state.
9398 * Param values can be provided in the `ui-sref` value after the state name, enclosed by parentheses.
9399 * The content inside the parentheses is an expression, evaluated to the parameter values.
9400 *
9401 * #### Example:
9402 * This example renders a list of links to users.
9403 * The state's `userId` parameter value comes from each user's `user.id` property.
9404 * ```html
9405 * <li ng-repeat="user in users">
9406 * <a ui-sref="users.detail({ userId: user.id })">{{ user.displayName }}</a>
9407 * </li>
9408 * ```
9409 *
9410 * Note:
9411 * The parameter values expression is `$watch`ed for updates.
9412 *
9413 * ### Transition Options
9414 * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute.
9415 * Options are restricted to `location`, `inherit`, and `reload`.
9416 *
9417 * #### Example:
9418 * ```html
9419 * <a ui-sref="home" ui-sref-opts="{ reload: true }">Home</a>
9420 * ```
9421 *
9422 * ### Other DOM Events
9423 *
9424 * You can also customize which DOM events to respond to (instead of `click`) by
9425 * providing an `events` array in the `ui-sref-opts` attribute.
9426 *
9427 * #### Example:
9428 * ```html
9429 * <input type="text" ui-sref="contacts" ui-sref-opts="{ events: ['change', 'blur'] }">
9430 * ```
9431 *
9432 * ### Highlighting the active link
9433 * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
9434 *
9435 * ### Examples
9436 * If you have the following template:
9437 *
9438 * ```html
9439 * <a ui-sref="home">Home</a>
9440 * <a ui-sref="about">About</a>
9441 * <a ui-sref="{page: 2}">Next page</a>
9442 *
9443 * <ul>
9444 * <li ng-repeat="contact in contacts">
9445 * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
9446 * </li>
9447 * </ul>
9448 * ```
9449 *
9450 * Then (assuming the current state is `contacts`) the rendered html including hrefs would be:
9451 *
9452 * ```html
9453 * <a href="#/home" ui-sref="home">Home</a>
9454 * <a href="#/about" ui-sref="about">About</a>
9455 * <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
9456 *
9457 * <ul>
9458 * <li ng-repeat="contact in contacts">
9459 * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
9460 * </li>
9461 * <li ng-repeat="contact in contacts">
9462 * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
9463 * </li>
9464 * <li ng-repeat="contact in contacts">
9465 * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
9466 * </li>
9467 * </ul>
9468 *
9469 * <a href="#/home" ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
9470 * ```
9471 *
9472 * ### Notes
9473 *
9474 * - You can use `ui-sref` to change **only the parameter values** by omitting the state name and parentheses.
9475 * #### Example:
9476 * Sets the `lang` parameter to `en` and remains on the same state.
9477 *
9478 * ```html
9479 * <a ui-sref="{ lang: 'en' }">English</a>
9480 * ```
9481 *
9482 * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
9483 *
9484 * - Unlike the parameter values expression, the state name is not `$watch`ed (for performance reasons).
9485 * If you need to dynamically update the state being linked to, use the fully dynamic [[uiState]] directive.
9486 */
9487 var uiSrefDirective;
9488 uiSrefDirective = [
9489 '$uiRouter',
9490 '$timeout',
9491 function $StateRefDirective($uiRouter, $timeout) {
9492 var $state = $uiRouter.stateService;
9493 return {
9494 restrict: 'A',
9495 require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
9496 link: function (scope, element, attrs, uiSrefActive) {
9497 var type = getTypeInfo(element);
9498 var active = uiSrefActive[1] || uiSrefActive[0];
9499 var unlinkInfoFn = null;
9500 var rawDef = {};
9501 var getDef = function () { return processedDef($state, element, rawDef); };
9502 var ref = parseStateRef(attrs.uiSref);
9503 rawDef.uiState = ref.state;
9504 rawDef.uiStateOpts = attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {};
9505 function update() {
9506 var def = getDef();
9507 if (unlinkInfoFn)
9508 unlinkInfoFn();
9509 if (active)
9510 unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams);
9511 if (def.href != null)
9512 attrs.$set(type.attr, def.href);
9513 }
9514 if (ref.paramExpr) {
9515 scope.$watch(ref.paramExpr, function (val) {
9516 rawDef.uiStateParams = extend({}, val);
9517 update();
9518 }, true);
9519 rawDef.uiStateParams = extend({}, scope.$eval(ref.paramExpr));
9520 }
9521 update();
9522 scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update));
9523 scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update));
9524 if (!type.clickable)
9525 return;
9526 var hookFn = clickHook(element, $state, $timeout, type, getDef);
9527 bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
9528 },
9529 };
9530 },
9531 ];
9532 /**
9533 * `ui-state`: A fully dynamic directive for linking to a state
9534 *
9535 * A directive which links to a state (and optionally, parameters).
9536 * When clicked, this directive activates the linked state with the supplied parameter values.
9537 *
9538 * **This directive is very similar to [[uiSref]], but it `$observe`s and `$watch`es/evaluates all its inputs.**
9539 *
9540 * A directive which links to a state (and optionally, parameters).
9541 * When clicked, this directive activates the linked state with the supplied parameter values.
9542 *
9543 * ### Linked State
9544 * The attribute value of `ui-state` is an expression which is `$watch`ed and evaluated as the state to link to.
9545 * **This is in contrast with `ui-sref`, which takes a state name as a string literal.**
9546 *
9547 * #### Example:
9548 * Create a list of links.
9549 * ```html
9550 * <li ng-repeat="link in navlinks">
9551 * <a ui-state="link.state">{{ link.displayName }}</a>
9552 * </li>
9553 * ```
9554 *
9555 * ### Relative Links
9556 * If the expression evaluates to a relative path, it is processed like [[uiSref]].
9557 * You just need to be aware that the path is relative to the state that *created* the link.
9558 * This allows a state to create relative `ui-state` which always targets the same destination.
9559 *
9560 * ### hrefs
9561 * If the linked state has a URL, the directive will automatically generate and
9562 * update the `href` attribute (using the [[StateService.href]] method).
9563 *
9564 * ### Parameter Values
9565 * In addition to the state name expression, a `ui-state` can include parameter values which are applied when activating the state.
9566 * Param values should be provided using the `ui-state-params` attribute.
9567 * The `ui-state-params` attribute value is `$watch`ed and evaluated as an expression.
9568 *
9569 * #### Example:
9570 * This example renders a list of links with param values.
9571 * The state's `userId` parameter value comes from each user's `user.id` property.
9572 * ```html
9573 * <li ng-repeat="link in navlinks">
9574 * <a ui-state="link.state" ui-state-params="link.params">{{ link.displayName }}</a>
9575 * </li>
9576 * ```
9577 *
9578 * ### Transition Options
9579 * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute.
9580 * Options are restricted to `location`, `inherit`, and `reload`.
9581 * The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression.
9582 *
9583 * #### Example:
9584 * ```html
9585 * <a ui-state="returnto.state" ui-state-opts="{ reload: true }">Home</a>
9586 * ```
9587 *
9588 * ### Other DOM Events
9589 *
9590 * You can also customize which DOM events to respond to (instead of `click`) by
9591 * providing an `events` array in the `ui-state-opts` attribute.
9592 *
9593 * #### Example:
9594 * ```html
9595 * <input type="text" ui-state="contacts" ui-state-opts="{ events: ['change', 'blur'] }">
9596 * ```
9597 *
9598 * ### Highlighting the active link
9599 * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
9600 *
9601 * ### Notes
9602 *
9603 * - You can use `ui-params` to change **only the parameter values** by omitting the state name and supplying only `ui-state-params`.
9604 * However, it might be simpler to use [[uiSref]] parameter-only links.
9605 *
9606 * #### Example:
9607 * Sets the `lang` parameter to `en` and remains on the same state.
9608 *
9609 * ```html
9610 * <a ui-state="" ui-state-params="{ lang: 'en' }">English</a>
9611 * ```
9612 *
9613 * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
9614 * ```
9615 */
9616 var uiStateDirective;
9617 uiStateDirective = [
9618 '$uiRouter',
9619 '$timeout',
9620 function $StateRefDynamicDirective($uiRouter, $timeout) {
9621 var $state = $uiRouter.stateService;
9622 return {
9623 restrict: 'A',
9624 require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
9625 link: function (scope, element, attrs, uiSrefActive) {
9626 var type = getTypeInfo(element);
9627 var active = uiSrefActive[1] || uiSrefActive[0];
9628 var unlinkInfoFn = null;
9629 var hookFn;
9630 var rawDef = {};
9631 var getDef = function () { return processedDef($state, element, rawDef); };
9632 var inputAttrs = ['uiState', 'uiStateParams', 'uiStateOpts'];
9633 var watchDeregFns = inputAttrs.reduce(function (acc, attr) { return ((acc[attr] = noop), acc); }, {});
9634 function update() {
9635 var def = getDef();
9636 if (unlinkInfoFn)
9637 unlinkInfoFn();
9638 if (active)
9639 unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams);
9640 if (def.href != null)
9641 attrs.$set(type.attr, def.href);
9642 }
9643 inputAttrs.forEach(function (field) {
9644 rawDef[field] = attrs[field] ? scope.$eval(attrs[field]) : null;
9645 attrs.$observe(field, function (expr) {
9646 watchDeregFns[field]();
9647 watchDeregFns[field] = scope.$watch(expr, function (newval) {
9648 rawDef[field] = newval;
9649 update();
9650 }, true);
9651 });
9652 });
9653 update();
9654 scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update));
9655 scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update));
9656 if (!type.clickable)
9657 return;
9658 hookFn = clickHook(element, $state, $timeout, type, getDef);
9659 bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
9660 },
9661 };
9662 },
9663 ];
9664 /**
9665 * `ui-sref-active` and `ui-sref-active-eq`: A directive that adds a CSS class when a `ui-sref` is active
9666 *
9667 * A directive working alongside [[uiSref]] and [[uiState]] to add classes to an element when the
9668 * related directive's state is active (and remove them when it is inactive).
9669 *
9670 * The primary use-case is to highlight the active link in navigation menus,
9671 * distinguishing it from the inactive menu items.
9672 *
9673 * ### Linking to a `ui-sref` or `ui-state`
9674 * `ui-sref-active` can live on the same element as `ui-sref`/`ui-state`, or it can be on a parent element.
9675 * If a `ui-sref-active` is a parent to more than one `ui-sref`/`ui-state`, it will apply the CSS class when **any of the links are active**.
9676 *
9677 * ### Matching
9678 *
9679 * The `ui-sref-active` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state **or any child state is active**.
9680 * This is a "fuzzy match" which uses [[StateService.includes]].
9681 *
9682 * The `ui-sref-active-eq` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state is directly active (not when child states are active).
9683 * This is an "exact match" which uses [[StateService.is]].
9684 *
9685 * ### Parameter values
9686 * If the `ui-sref`/`ui-state` includes parameter values, the current parameter values must match the link's values for the link to be highlighted.
9687 * This allows a list of links to the same state with different parameters to be rendered, and the correct one highlighted.
9688 *
9689 * #### Example:
9690 * ```html
9691 * <li ng-repeat="user in users" ui-sref-active="active">
9692 * <a ui-sref="user.details({ userId: user.id })">{{ user.lastName }}</a>
9693 * </li>
9694 * ```
9695 *
9696 * ### Examples
9697 *
9698 * Given the following template:
9699 * #### Example:
9700 * ```html
9701 * <ul>
9702 * <li ui-sref-active="active" class="item">
9703 * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
9704 * </li>
9705 * </ul>
9706 * ```
9707 *
9708 * When the app state is `app.user` (or any child state),
9709 * and contains the state parameter "user" with value "bilbobaggins",
9710 * the resulting HTML will appear as (note the 'active' class):
9711 *
9712 * ```html
9713 * <ul>
9714 * <li ui-sref-active="active" class="item active">
9715 * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
9716 * </li>
9717 * </ul>
9718 * ```
9719 *
9720 * ### Glob mode
9721 *
9722 * It is possible to pass `ui-sref-active` an expression that evaluates to an object.
9723 * The objects keys represent active class names and values represent the respective state names/globs.
9724 * `ui-sref-active` will match if the current active state **includes** any of
9725 * the specified state names/globs, even the abstract ones.
9726 *
9727 * #### Example:
9728 * Given the following template, with "admin" being an abstract state:
9729 * ```html
9730 * <div ui-sref-active="{'active': 'admin.**'}">
9731 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
9732 * </div>
9733 * ```
9734 *
9735 * Arrays are also supported as values in the `ngClass`-like interface.
9736 * This allows multiple states to add `active` class.
9737 *
9738 * #### Example:
9739 * Given the following template, with "admin.roles" being the current state, the class will be added too:
9740 * ```html
9741 * <div ui-sref-active="{'active': ['owner.**', 'admin.**']}">
9742 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
9743 * </div>
9744 * ```
9745 *
9746 * When the current state is "admin.roles" the "active" class will be applied to both the `<div>` and `<a>` elements.
9747 * It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`.
9748 *
9749 * ### Notes:
9750 *
9751 * - The class name is interpolated **once** during the directives link time (any further changes to the
9752 * interpolated value are ignored).
9753 *
9754 * - Multiple classes may be specified in a space-separated format: `ui-sref-active='class1 class2 class3'`
9755 */
9756 var uiSrefActiveDirective;
9757 uiSrefActiveDirective = [
9758 '$state',
9759 '$stateParams',
9760 '$interpolate',
9761 '$uiRouter',
9762 function $StateRefActiveDirective($state, $stateParams, $interpolate, $uiRouter) {
9763 return {
9764 restrict: 'A',
9765 controller: [
9766 '$scope',
9767 '$element',
9768 '$attrs',
9769 function ($scope, $element, $attrs) {
9770 var states = [];
9771 var activeEqClass;
9772 var uiSrefActive;
9773 // There probably isn't much point in $observing this
9774 // uiSrefActive and uiSrefActiveEq share the same directive object with some
9775 // slight difference in logic routing
9776 activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
9777 try {
9778 uiSrefActive = $scope.$eval($attrs.uiSrefActive);
9779 }
9780 catch (e) {
9781 // Do nothing. uiSrefActive is not a valid expression.
9782 // Fall back to using $interpolate below
9783 }
9784 uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
9785 setStatesFromDefinitionObject(uiSrefActive);
9786 // Allow uiSref to communicate with uiSrefActive[Equals]
9787 this.$$addStateInfo = function (newState, newParams) {
9788 // we already got an explicit state provided by ui-sref-active, so we
9789 // shadow the one that comes from ui-sref
9790 if (isObject(uiSrefActive) && states.length > 0) {
9791 return;
9792 }
9793 var deregister = addState(newState, newParams, uiSrefActive);
9794 update();
9795 return deregister;
9796 };
9797 function updateAfterTransition(trans) {
9798 trans.promise.then(update, noop);
9799 }
9800 $scope.$on('$destroy', setupEventListeners());
9801 if ($uiRouter.globals.transition) {
9802 updateAfterTransition($uiRouter.globals.transition);
9803 }
9804 function setupEventListeners() {
9805 var deregisterStatesChangedListener = $uiRouter.stateRegistry.onStatesChanged(handleStatesChanged);
9806 var deregisterOnStartListener = $uiRouter.transitionService.onStart({}, updateAfterTransition);
9807 var deregisterStateChangeSuccessListener = $scope.$on('$stateChangeSuccess', update);
9808 return function cleanUp() {
9809 deregisterStatesChangedListener();
9810 deregisterOnStartListener();
9811 deregisterStateChangeSuccessListener();
9812 };
9813 }
9814 function handleStatesChanged() {
9815 setStatesFromDefinitionObject(uiSrefActive);
9816 }
9817 function setStatesFromDefinitionObject(statesDefinition) {
9818 if (isObject(statesDefinition)) {
9819 states = [];
9820 forEach(statesDefinition, function (stateOrName, activeClass) {
9821 // Helper function to abstract adding state.
9822 var addStateForClass = function (stateOrName, activeClass) {
9823 var ref = parseStateRef(stateOrName);
9824 addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
9825 };
9826 if (isString(stateOrName)) {
9827 // If state is string, just add it.
9828 addStateForClass(stateOrName, activeClass);
9829 }
9830 else if (isArray(stateOrName)) {
9831 // If state is an array, iterate over it and add each array item individually.
9832 forEach(stateOrName, function (stateOrName) {
9833 addStateForClass(stateOrName, activeClass);
9834 });
9835 }
9836 });
9837 }
9838 }
9839 function addState(stateName, stateParams, activeClass) {
9840 var state = $state.get(stateName, stateContext($element));
9841 var stateInfo = {
9842 state: state || { name: stateName },
9843 params: stateParams,
9844 activeClass: activeClass,
9845 };
9846 states.push(stateInfo);
9847 return function removeState() {
9848 removeFrom(states)(stateInfo);
9849 };
9850 }
9851 // Update route state
9852 function update() {
9853 var splitClasses = function (str) { return str.split(/\s/).filter(identity); };
9854 var getClasses = function (stateList) {
9855 return stateList
9856 .map(function (x) { return x.activeClass; })
9857 .map(splitClasses)
9858 .reduce(unnestR, []);
9859 };
9860 var allClasses = getClasses(states).concat(splitClasses(activeEqClass)).reduce(uniqR, []);
9861 var fuzzyClasses = getClasses(states.filter(function (x) { return $state.includes(x.state.name, x.params); }));
9862 var exactlyMatchesAny = !!states.filter(function (x) { return $state.is(x.state.name, x.params); }).length;
9863 var exactClasses = exactlyMatchesAny ? splitClasses(activeEqClass) : [];
9864 var addClasses = fuzzyClasses.concat(exactClasses).reduce(uniqR, []);
9865 var removeClasses = allClasses.filter(function (cls) { return !inArray(addClasses, cls); });
9866 $scope.$evalAsync(function () {
9867 addClasses.forEach(function (className) { return $element.addClass(className); });
9868 removeClasses.forEach(function (className) { return $element.removeClass(className); });
9869 });
9870 }
9871 update();
9872 },
9873 ],
9874 };
9875 },
9876 ];
9877 ng
9878 .module('ui.router.state')
9879 .directive('uiSref', uiSrefDirective)
9880 .directive('uiSrefActive', uiSrefActiveDirective)
9881 .directive('uiSrefActiveEq', uiSrefActiveDirective)
9882 .directive('uiState', uiStateDirective);
9883
9884 /** @publicapi @module ng1 */ /** */
9885 /**
9886 * `isState` Filter: truthy if the current state is the parameter
9887 *
9888 * Translates to [[StateService.is]] `$state.is("stateName")`.
9889 *
9890 * #### Example:
9891 * ```html
9892 * <div ng-if="'stateName' | isState">show if state is 'stateName'</div>
9893 * ```
9894 */
9895 $IsStateFilter.$inject = ['$state'];
9896 function $IsStateFilter($state) {
9897 var isFilter = function (state, params, options) {
9898 return $state.is(state, params, options);
9899 };
9900 isFilter.$stateful = true;
9901 return isFilter;
9902 }
9903 /**
9904 * `includedByState` Filter: truthy if the current state includes the parameter
9905 *
9906 * Translates to [[StateService.includes]]` $state.is("fullOrPartialStateName")`.
9907 *
9908 * #### Example:
9909 * ```html
9910 * <div ng-if="'fullOrPartialStateName' | includedByState">show if state includes 'fullOrPartialStateName'</div>
9911 * ```
9912 */
9913 $IncludedByStateFilter.$inject = ['$state'];
9914 function $IncludedByStateFilter($state) {
9915 var includesFilter = function (state, params, options) {
9916 return $state.includes(state, params, options);
9917 };
9918 includesFilter.$stateful = true;
9919 return includesFilter;
9920 }
9921 ng.module('ui.router.state').filter('isState', $IsStateFilter).filter('includedByState', $IncludedByStateFilter);
9922
9923 /** @publicapi @module directives */ /** */
9924 /**
9925 * `ui-view`: A viewport directive which is filled in by a view from the active state.
9926 *
9927 * ### Attributes
9928 *
9929 * - `name`: (Optional) A view name.
9930 * The name should be unique amongst the other views in the same state.
9931 * You can have views of the same name that live in different states.
9932 * The ui-view can be targeted in a View using the name ([[Ng1StateDeclaration.views]]).
9933 *
9934 * - `autoscroll`: an expression. When it evaluates to true, the `ui-view` will be scrolled into view when it is activated.
9935 * Uses [[$uiViewScroll]] to do the scrolling.
9936 *
9937 * - `onload`: Expression to evaluate whenever the view updates.
9938 *
9939 * #### Example:
9940 * A view can be unnamed or named.
9941 * ```html
9942 * <!-- Unnamed -->
9943 * <div ui-view></div>
9944 *
9945 * <!-- Named -->
9946 * <div ui-view="viewName"></div>
9947 *
9948 * <!-- Named (different style) -->
9949 * <ui-view name="viewName"></ui-view>
9950 * ```
9951 *
9952 * You can only have one unnamed view within any template (or root html). If you are only using a
9953 * single view and it is unnamed then you can populate it like so:
9954 *
9955 * ```html
9956 * <div ui-view></div>
9957 * $stateProvider.state("home", {
9958 * template: "<h1>HELLO!</h1>"
9959 * })
9960 * ```
9961 *
9962 * The above is a convenient shortcut equivalent to specifying your view explicitly with the
9963 * [[Ng1StateDeclaration.views]] config property, by name, in this case an empty name:
9964 *
9965 * ```js
9966 * $stateProvider.state("home", {
9967 * views: {
9968 * "": {
9969 * template: "<h1>HELLO!</h1>"
9970 * }
9971 * }
9972 * })
9973 * ```
9974 *
9975 * But typically you'll only use the views property if you name your view or have more than one view
9976 * in the same template. There's not really a compelling reason to name a view if its the only one,
9977 * but you could if you wanted, like so:
9978 *
9979 * ```html
9980 * <div ui-view="main"></div>
9981 * ```
9982 *
9983 * ```js
9984 * $stateProvider.state("home", {
9985 * views: {
9986 * "main": {
9987 * template: "<h1>HELLO!</h1>"
9988 * }
9989 * }
9990 * })
9991 * ```
9992 *
9993 * Really though, you'll use views to set up multiple views:
9994 *
9995 * ```html
9996 * <div ui-view></div>
9997 * <div ui-view="chart"></div>
9998 * <div ui-view="data"></div>
9999 * ```
10000 *
10001 * ```js
10002 * $stateProvider.state("home", {
10003 * views: {
10004 * "": {
10005 * template: "<h1>HELLO!</h1>"
10006 * },
10007 * "chart": {
10008 * template: "<chart_thing/>"
10009 * },
10010 * "data": {
10011 * template: "<data_thing/>"
10012 * }
10013 * }
10014 * })
10015 * ```
10016 *
10017 * #### Examples for `autoscroll`:
10018 * ```html
10019 * <!-- If autoscroll present with no expression,
10020 * then scroll ui-view into view -->
10021 * <ui-view autoscroll/>
10022 *
10023 * <!-- If autoscroll present with valid expression,
10024 * then scroll ui-view into view if expression evaluates to true -->
10025 * <ui-view autoscroll='true'/>
10026 * <ui-view autoscroll='false'/>
10027 * <ui-view autoscroll='scopeVariable'/>
10028 * ```
10029 *
10030 * Resolve data:
10031 *
10032 * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
10033 * can be customized using [[Ng1ViewDeclaration.resolveAs]]). This can be then accessed from the template.
10034 *
10035 * Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
10036 * controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
10037 * depends on `$resolve` data.
10038 *
10039 * #### Example:
10040 * ```js
10041 * $stateProvider.state('home', {
10042 * template: '<my-component user="$resolve.user"></my-component>',
10043 * resolve: {
10044 * user: function(UserService) { return UserService.fetchUser(); }
10045 * }
10046 * });
10047 * ```
10048 */
10049 var uiView;
10050 // eslint-disable-next-line prefer-const
10051 uiView = [
10052 '$view',
10053 '$animate',
10054 '$uiViewScroll',
10055 '$interpolate',
10056 '$q',
10057 function $ViewDirective($view, $animate, $uiViewScroll, $interpolate, $q) {
10058 function getRenderer() {
10059 return {
10060 enter: function (element, target, cb) {
10061 if (ng.version.minor > 2) {
10062 $animate.enter(element, null, target).then(cb);
10063 }
10064 else {
10065 $animate.enter(element, null, target, cb);
10066 }
10067 },
10068 leave: function (element, cb) {
10069 if (ng.version.minor > 2) {
10070 $animate.leave(element).then(cb);
10071 }
10072 else {
10073 $animate.leave(element, cb);
10074 }
10075 },
10076 };
10077 }
10078 function configsEqual(config1, config2) {
10079 return config1 === config2;
10080 }
10081 var rootData = {
10082 $cfg: { viewDecl: { $context: $view._pluginapi._rootViewContext() } },
10083 $uiView: {},
10084 };
10085 var directive = {
10086 count: 0,
10087 restrict: 'ECA',
10088 terminal: true,
10089 priority: 400,
10090 transclude: 'element',
10091 compile: function (tElement, tAttrs, $transclude) {
10092 return function (scope, $element, attrs) {
10093 var onloadExp = attrs['onload'] || '', autoScrollExp = attrs['autoscroll'], renderer = getRenderer(), inherited = $element.inheritedData('$uiView') || rootData, name = $interpolate(attrs['uiView'] || attrs['name'] || '')(scope) || '$default';
10094 var previousEl, currentEl, currentScope, viewConfig;
10095 var activeUIView = {
10096 $type: 'ng1',
10097 id: directive.count++,
10098 name: name,
10099 fqn: inherited.$uiView.fqn ? inherited.$uiView.fqn + '.' + name : name,
10100 config: null,
10101 configUpdated: configUpdatedCallback,
10102 get creationContext() {
10103 // The context in which this ui-view "tag" was created
10104 var fromParentTagConfig = parse('$cfg.viewDecl.$context')(inherited);
10105 // Allow <ui-view name="foo"><ui-view name="bar"></ui-view></ui-view>
10106 // See https://github.com/angular-ui/ui-router/issues/3355
10107 var fromParentTag = parse('$uiView.creationContext')(inherited);
10108 return fromParentTagConfig || fromParentTag;
10109 },
10110 };
10111 trace.traceUIViewEvent('Linking', activeUIView);
10112 function configUpdatedCallback(config) {
10113 if (config && !(config instanceof Ng1ViewConfig))
10114 return;
10115 if (configsEqual(viewConfig, config))
10116 return;
10117 trace.traceUIViewConfigUpdated(activeUIView, config && config.viewDecl && config.viewDecl.$context);
10118 viewConfig = config;
10119 updateView(config);
10120 }
10121 $element.data('$uiView', { $uiView: activeUIView });
10122 updateView();
10123 var unregister = $view.registerUIView(activeUIView);
10124 scope.$on('$destroy', function () {
10125 trace.traceUIViewEvent('Destroying/Unregistering', activeUIView);
10126 unregister();
10127 });
10128 function cleanupLastView() {
10129 if (previousEl) {
10130 trace.traceUIViewEvent('Removing (previous) el', previousEl.data('$uiView'));
10131 previousEl.remove();
10132 previousEl = null;
10133 }
10134 if (currentScope) {
10135 trace.traceUIViewEvent('Destroying scope', activeUIView);
10136 currentScope.$destroy();
10137 currentScope = null;
10138 }
10139 if (currentEl) {
10140 var _viewData_1 = currentEl.data('$uiViewAnim');
10141 trace.traceUIViewEvent('Animate out', _viewData_1);
10142 renderer.leave(currentEl, function () {
10143 _viewData_1.$$animLeave.resolve();
10144 previousEl = null;
10145 });
10146 previousEl = currentEl;
10147 currentEl = null;
10148 }
10149 }
10150 function updateView(config) {
10151 var newScope = scope.$new();
10152 var animEnter = $q.defer(), animLeave = $q.defer();
10153 var $uiViewData = {
10154 $cfg: config,
10155 $uiView: activeUIView,
10156 };
10157 var $uiViewAnim = {
10158 $animEnter: animEnter.promise,
10159 $animLeave: animLeave.promise,
10160 $$animLeave: animLeave,
10161 };
10162 /**
10163 * @ngdoc event
10164 * @name ui.router.state.directive:ui-view#$viewContentLoading
10165 * @eventOf ui.router.state.directive:ui-view
10166 * @eventType emits on ui-view directive scope
10167 * @description
10168 *
10169 * Fired once the view **begins loading**, *before* the DOM is rendered.
10170 *
10171 * @param {Object} event Event object.
10172 * @param {string} viewName Name of the view.
10173 */
10174 newScope.$emit('$viewContentLoading', name);
10175 var cloned = $transclude(newScope, function (clone) {
10176 clone.data('$uiViewAnim', $uiViewAnim);
10177 clone.data('$uiView', $uiViewData);
10178 renderer.enter(clone, $element, function onUIViewEnter() {
10179 animEnter.resolve();
10180 if (currentScope)
10181 currentScope.$emit('$viewContentAnimationEnded');
10182 if ((isDefined(autoScrollExp) && !autoScrollExp) || scope.$eval(autoScrollExp)) {
10183 $uiViewScroll(clone);
10184 }
10185 });
10186 cleanupLastView();
10187 });
10188 currentEl = cloned;
10189 currentScope = newScope;
10190 /**
10191 * @ngdoc event
10192 * @name ui.router.state.directive:ui-view#$viewContentLoaded
10193 * @eventOf ui.router.state.directive:ui-view
10194 * @eventType emits on ui-view directive scope
10195 * @description *
10196 * Fired once the view is **loaded**, *after* the DOM is rendered.
10197 *
10198 * @param {Object} event Event object.
10199 */
10200 currentScope.$emit('$viewContentLoaded', config || viewConfig);
10201 currentScope.$eval(onloadExp);
10202 }
10203 };
10204 },
10205 };
10206 return directive;
10207 },
10208 ];
10209 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$transitions', '$view', '$q'];
10210 /** @hidden */
10211 function $ViewDirectiveFill($compile, $controller, $transitions, $view, $q) {
10212 var getControllerAs = parse('viewDecl.controllerAs');
10213 var getResolveAs = parse('viewDecl.resolveAs');
10214 return {
10215 restrict: 'ECA',
10216 priority: -400,
10217 compile: function (tElement) {
10218 var initial = tElement.html();
10219 tElement.empty();
10220 return function (scope, $element) {
10221 var data = $element.data('$uiView');
10222 if (!data) {
10223 $element.html(initial);
10224 $compile($element.contents())(scope);
10225 return;
10226 }
10227 var cfg = data.$cfg || { viewDecl: {}, getTemplate: noop };
10228 var resolveCtx = cfg.path && new ResolveContext(cfg.path);
10229 $element.html(cfg.getTemplate($element, resolveCtx) || initial);
10230 trace.traceUIViewFill(data.$uiView, $element.html());
10231 var link = $compile($element.contents());
10232 var controller = cfg.controller;
10233 var controllerAs = getControllerAs(cfg);
10234 var resolveAs = getResolveAs(cfg);
10235 var locals = resolveCtx && getLocals(resolveCtx);
10236 scope[resolveAs] = locals;
10237 if (controller) {
10238 var controllerInstance = ($controller(controller, extend({}, locals, { $scope: scope, $element: $element })));
10239 if (controllerAs) {
10240 scope[controllerAs] = controllerInstance;
10241 scope[controllerAs][resolveAs] = locals;
10242 }
10243 // TODO: Use $view service as a central point for registering component-level hooks
10244 // Then, when a component is created, tell the $view service, so it can invoke hooks
10245 // $view.componentLoaded(controllerInstance, { $scope: scope, $element: $element });
10246 // scope.$on('$destroy', () => $view.componentUnloaded(controllerInstance, { $scope: scope, $element: $element }));
10247 $element.data('$ngControllerController', controllerInstance);
10248 $element.children().data('$ngControllerController', controllerInstance);
10249 registerControllerCallbacks($q, $transitions, controllerInstance, scope, cfg);
10250 }
10251 // Wait for the component to appear in the DOM
10252 if (isString(cfg.component)) {
10253 var kebobName = kebobString(cfg.component);
10254 var tagRegexp_1 = new RegExp("^(x-|data-)?" + kebobName + "$", 'i');
10255 var getComponentController = function () {
10256 var directiveEl = [].slice
10257 .call($element[0].children)
10258 .filter(function (el) { return el && el.tagName && tagRegexp_1.exec(el.tagName); });
10259 return directiveEl && ng.element(directiveEl).data("$" + cfg.component + "Controller");
10260 };
10261 var deregisterWatch_1 = scope.$watch(getComponentController, function (ctrlInstance) {
10262 if (!ctrlInstance)
10263 return;
10264 registerControllerCallbacks($q, $transitions, ctrlInstance, scope, cfg);
10265 deregisterWatch_1();
10266 });
10267 }
10268 link(scope);
10269 };
10270 },
10271 };
10272 }
10273 /** @hidden */
10274 var hasComponentImpl = typeof ng.module('ui.router')['component'] === 'function';
10275 /** @hidden incrementing id */
10276 var _uiCanExitId = 0;
10277 /** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */
10278 function registerControllerCallbacks($q, $transitions, controllerInstance, $scope, cfg) {
10279 // Call $onInit() ASAP
10280 if (isFunction(controllerInstance.$onInit) &&
10281 !((cfg.viewDecl.component || cfg.viewDecl.componentProvider) && hasComponentImpl)) {
10282 controllerInstance.$onInit();
10283 }
10284 var viewState = tail(cfg.path).state.self;
10285 var hookOptions = { bind: controllerInstance };
10286 // Add component-level hook for onUiParamsChanged
10287 if (isFunction(controllerInstance.uiOnParamsChanged)) {
10288 var resolveContext = new ResolveContext(cfg.path);
10289 var viewCreationTrans_1 = resolveContext.getResolvable('$transition$').data;
10290 // Fire callback on any successful transition
10291 var paramsUpdated = function ($transition$) {
10292 // Exit early if the $transition$ is the same as the view was created within.
10293 // Exit early if the $transition$ will exit the state the view is for.
10294 if ($transition$ === viewCreationTrans_1 || $transition$.exiting().indexOf(viewState) !== -1)
10295 return;
10296 var toParams = $transition$.params('to');
10297 var fromParams = $transition$.params('from');
10298 var getNodeSchema = function (node) { return node.paramSchema; };
10299 var toSchema = $transition$.treeChanges('to').map(getNodeSchema).reduce(unnestR, []);
10300 var fromSchema = $transition$.treeChanges('from').map(getNodeSchema).reduce(unnestR, []);
10301 // Find the to params that have different values than the from params
10302 var changedToParams = toSchema.filter(function (param) {
10303 var idx = fromSchema.indexOf(param);
10304 return idx === -1 || !fromSchema[idx].type.equals(toParams[param.id], fromParams[param.id]);
10305 });
10306 // Only trigger callback if a to param has changed or is new
10307 if (changedToParams.length) {
10308 var changedKeys_1 = changedToParams.map(function (x) { return x.id; });
10309 // Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params.
10310 var newValues = filter(toParams, function (val, key) { return changedKeys_1.indexOf(key) !== -1; });
10311 controllerInstance.uiOnParamsChanged(newValues, $transition$);
10312 }
10313 };
10314 $scope.$on('$destroy', $transitions.onSuccess({}, paramsUpdated, hookOptions));
10315 }
10316 // Add component-level hook for uiCanExit
10317 if (isFunction(controllerInstance.uiCanExit)) {
10318 var id_1 = _uiCanExitId++;
10319 var cacheProp_1 = '_uiCanExitIds';
10320 // Returns true if a redirect transition already answered truthy
10321 var prevTruthyAnswer_1 = function (trans) {
10322 return !!trans && ((trans[cacheProp_1] && trans[cacheProp_1][id_1] === true) || prevTruthyAnswer_1(trans.redirectedFrom()));
10323 };
10324 // If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition
10325 var wrappedHook = function (trans) {
10326 var promise;
10327 var ids = (trans[cacheProp_1] = trans[cacheProp_1] || {});
10328 if (!prevTruthyAnswer_1(trans)) {
10329 promise = $q.when(controllerInstance.uiCanExit(trans));
10330 promise.then(function (val) { return (ids[id_1] = val !== false); });
10331 }
10332 return promise;
10333 };
10334 var criteria = { exiting: viewState.name };
10335 $scope.$on('$destroy', $transitions.onBefore(criteria, wrappedHook, hookOptions));
10336 }
10337 }
10338 ng.module('ui.router.state').directive('uiView', uiView);
10339 ng.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
10340
10341 /** @publicapi @module ng1 */ /** */
10342 /** @hidden */
10343 function $ViewScrollProvider() {
10344 var useAnchorScroll = false;
10345 this.useAnchorScroll = function () {
10346 useAnchorScroll = true;
10347 };
10348 this.$get = [
10349 '$anchorScroll',
10350 '$timeout',
10351 function ($anchorScroll, $timeout) {
10352 if (useAnchorScroll) {
10353 return $anchorScroll;
10354 }
10355 return function ($element) {
10356 return $timeout(function () {
10357 $element[0].scrollIntoView();
10358 }, 0, false);
10359 };
10360 },
10361 ];
10362 }
10363 ng.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
10364
10365 /**
10366 * Main entry point for angular 1.x build
10367 * @publicapi @module ng1
10368 */ /** */
10369 var index$1 = 'ui.router';
10370
10371 exports.$injector = $injector;
10372 exports.$q = $q;
10373 exports.BaseLocationServices = BaseLocationServices;
10374 exports.BaseUrlRule = BaseUrlRule;
10375 exports.BrowserLocationConfig = BrowserLocationConfig;
10376 exports.Glob = Glob;
10377 exports.HashLocationService = HashLocationService;
10378 exports.HookBuilder = HookBuilder;
10379 exports.MemoryLocationConfig = MemoryLocationConfig;
10380 exports.MemoryLocationService = MemoryLocationService;
10381 exports.NATIVE_INJECTOR_TOKEN = NATIVE_INJECTOR_TOKEN;
10382 exports.Ng1ViewConfig = Ng1ViewConfig;
10383 exports.Param = Param;
10384 exports.ParamFactory = ParamFactory;
10385 exports.ParamType = ParamType;
10386 exports.ParamTypes = ParamTypes;
10387 exports.PathNode = PathNode;
10388 exports.PathUtils = PathUtils;
10389 exports.PushStateLocationService = PushStateLocationService;
10390 exports.Queue = Queue;
10391 exports.RegisteredHook = RegisteredHook;
10392 exports.Rejection = Rejection;
10393 exports.Resolvable = Resolvable;
10394 exports.ResolveContext = ResolveContext;
10395 exports.StateBuilder = StateBuilder;
10396 exports.StateMatcher = StateMatcher;
10397 exports.StateObject = StateObject;
10398 exports.StateParams = StateParams;
10399 exports.StateProvider = StateProvider;
10400 exports.StateQueueManager = StateQueueManager;
10401 exports.StateRegistry = StateRegistry;
10402 exports.StateService = StateService;
10403 exports.TargetState = TargetState;
10404 exports.Trace = Trace;
10405 exports.Transition = Transition;
10406 exports.TransitionEventType = TransitionEventType;
10407 exports.TransitionHook = TransitionHook;
10408 exports.TransitionService = TransitionService;
10409 exports.UIRouter = UIRouter;
10410 exports.UIRouterGlobals = UIRouterGlobals;
10411 exports.UIRouterPluginBase = UIRouterPluginBase;
10412 exports.UrlConfig = UrlConfig;
10413 exports.UrlMatcher = UrlMatcher;
10414 exports.UrlMatcherFactory = UrlMatcherFactory;
10415 exports.UrlRouter = UrlRouter;
10416 exports.UrlRouterProvider = UrlRouterProvider;
10417 exports.UrlRuleFactory = UrlRuleFactory;
10418 exports.UrlRules = UrlRules;
10419 exports.UrlService = UrlService;
10420 exports.ViewService = ViewService;
10421 exports._extend = _extend;
10422 exports._inArray = _inArray;
10423 exports._pushTo = _pushTo;
10424 exports._removeFrom = _removeFrom;
10425 exports.all = all;
10426 exports.allTrueR = allTrueR;
10427 exports.ancestors = ancestors;
10428 exports.and = and;
10429 exports.any = any;
10430 exports.anyTrueR = anyTrueR;
10431 exports.applyPairs = applyPairs;
10432 exports.arrayTuples = arrayTuples;
10433 exports.assertFn = assertFn;
10434 exports.assertMap = assertMap;
10435 exports.assertPredicate = assertPredicate;
10436 exports.beforeAfterSubstr = beforeAfterSubstr;
10437 exports.buildUrl = buildUrl;
10438 exports.compose = compose;
10439 exports.copy = copy;
10440 exports.core = index;
10441 exports.createProxyFunctions = createProxyFunctions;
10442 exports.curry = curry;
10443 exports.default = index$1;
10444 exports.defaultResolvePolicy = defaultResolvePolicy;
10445 exports.defaultTransOpts = defaultTransOpts;
10446 exports.defaults = defaults;
10447 exports.deregAll = deregAll;
10448 exports.eq = eq;
10449 exports.equals = equals;
10450 exports.extend = extend;
10451 exports.filter = filter;
10452 exports.find = find;
10453 exports.flatten = flatten;
10454 exports.flattenR = flattenR;
10455 exports.fnToString = fnToString;
10456 exports.forEach = forEach;
10457 exports.fromJson = fromJson;
10458 exports.functionToString = functionToString;
10459 exports.getLocals = getLocals;
10460 exports.getNg1ViewConfigFactory = getNg1ViewConfigFactory;
10461 exports.getParams = getParams;
10462 exports.hashLocationPlugin = hashLocationPlugin;
10463 exports.hostRegex = hostRegex;
10464 exports.identity = identity;
10465 exports.inArray = inArray;
10466 exports.inherit = inherit;
10467 exports.invoke = invoke;
10468 exports.is = is;
10469 exports.isArray = isArray;
10470 exports.isDate = isDate;
10471 exports.isDefined = isDefined;
10472 exports.isFunction = isFunction;
10473 exports.isInjectable = isInjectable;
10474 exports.isNull = isNull;
10475 exports.isNullOrUndefined = isNullOrUndefined;
10476 exports.isNumber = isNumber;
10477 exports.isObject = isObject;
10478 exports.isPromise = isPromise;
10479 exports.isRegExp = isRegExp;
10480 exports.isString = isString;
10481 exports.isUndefined = isUndefined;
10482 exports.joinNeighborsR = joinNeighborsR;
10483 exports.kebobString = kebobString;
10484 exports.keyValsToObjectR = keyValsToObjectR;
10485 exports.locationPluginFactory = locationPluginFactory;
10486 exports.makeEvent = makeEvent;
10487 exports.makeStub = makeStub;
10488 exports.map = map;
10489 exports.mapObj = mapObj;
10490 exports.matchState = matchState;
10491 exports.maxLength = maxLength;
10492 exports.memoryLocationPlugin = memoryLocationPlugin;
10493 exports.mergeR = mergeR;
10494 exports.ng1ViewsBuilder = ng1ViewsBuilder;
10495 exports.noop = noop;
10496 exports.not = not;
10497 exports.omit = omit;
10498 exports.or = or;
10499 exports.padString = padString;
10500 exports.pairs = pairs;
10501 exports.parse = parse;
10502 exports.parseUrl = parseUrl$1;
10503 exports.pattern = pattern;
10504 exports.pick = pick;
10505 exports.pipe = pipe;
10506 exports.pluck = pluck;
10507 exports.prop = prop;
10508 exports.propEq = propEq;
10509 exports.pushR = pushR;
10510 exports.pushStateLocationPlugin = pushStateLocationPlugin;
10511 exports.pushTo = pushTo;
10512 exports.removeFrom = removeFrom;
10513 exports.resolvablesBuilder = resolvablesBuilder;
10514 exports.resolvePolicies = resolvePolicies;
10515 exports.root = root;
10516 exports.services = services;
10517 exports.servicesPlugin = servicesPlugin;
10518 exports.silenceUncaughtInPromise = silenceUncaughtInPromise;
10519 exports.silentRejection = silentRejection;
10520 exports.splitEqual = splitEqual;
10521 exports.splitHash = splitHash;
10522 exports.splitOnDelim = splitOnDelim;
10523 exports.splitQuery = splitQuery;
10524 exports.stringify = stringify;
10525 exports.stripLastPathElement = stripLastPathElement;
10526 exports.tail = tail;
10527 exports.toJson = toJson;
10528 exports.trace = trace;
10529 exports.trimHashVal = trimHashVal;
10530 exports.uniqR = uniqR;
10531 exports.unnest = unnest;
10532 exports.unnestR = unnestR;
10533 exports.val = val;
10534 exports.values = values;
10535 exports.watchDigests = watchDigests;
10536
10537 Object.defineProperty(exports, '__esModule', { value: true });
10538
10539})));
10540//# sourceMappingURL=angular-ui-router.js.map