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 foun
369d 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
1266+ ")]"; 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}
1941 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'
2507s). 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]]
4075). 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 Param
4671s 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 c
4742ontaining 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 `$urlMatcherFactoryProvi
4993der`. 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 =
5357cfg.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) ([[ActiveU
5393IView]]) 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: "[email protected]" , "^.^.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));
5517 // 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({}
6725, 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({}
6755, 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 invali
7348d 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)
8192: _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.forEac
8958h(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 s
9004ome 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
9173initialized 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})));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.