@angular-wave/angular.ts 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/angular-ts.esm.js +1 -1
  2. package/dist/angular-ts.umd.js +1 -1
  3. package/package.json +4 -1
  4. package/src/exts/messages.md +30 -30
  5. package/src/index.js +1 -0
  6. package/src/public.js +2 -0
  7. package/src/router/adapter/directives/stateDirectives.js +695 -0
  8. package/src/router/adapter/directives/viewDirective.js +514 -0
  9. package/src/router/adapter/injectables.js +314 -0
  10. package/src/router/adapter/interface.js +1 -0
  11. package/src/router/adapter/locationServices.js +84 -0
  12. package/src/router/adapter/services.js +126 -0
  13. package/src/router/adapter/stateFilters.js +43 -0
  14. package/src/router/adapter/stateProvider.js +137 -0
  15. package/src/router/adapter/statebuilders/onEnterExitRetain.js +30 -0
  16. package/src/router/adapter/statebuilders/views.js +146 -0
  17. package/src/router/adapter/templateFactory.js +218 -0
  18. package/src/router/adapter/viewScroll.js +31 -0
  19. package/src/router/core/common/common.js +496 -0
  20. package/src/router/core/common/coreservices.js +15 -0
  21. package/src/router/core/common/glob.js +75 -0
  22. package/src/router/core/common/hof.js +194 -0
  23. package/src/router/core/common/predicates.js +44 -0
  24. package/src/router/core/common/queue.js +41 -0
  25. package/src/router/core/common/safeConsole.js +38 -0
  26. package/src/router/core/common/strings.js +141 -0
  27. package/src/router/core/common/trace.js +232 -0
  28. package/src/router/core/globals.js +29 -0
  29. package/src/router/core/hooks/coreResolvables.js +33 -0
  30. package/src/router/core/hooks/ignoredTransition.js +25 -0
  31. package/src/router/core/hooks/invalidTransition.js +14 -0
  32. package/src/router/core/hooks/lazyLoad.js +102 -0
  33. package/src/router/core/hooks/onEnterExitRetain.js +55 -0
  34. package/src/router/core/hooks/redirectTo.js +36 -0
  35. package/src/router/core/hooks/resolve.js +57 -0
  36. package/src/router/core/hooks/updateGlobals.js +30 -0
  37. package/src/router/core/hooks/url.js +25 -0
  38. package/src/router/core/hooks/views.js +39 -0
  39. package/src/router/core/interface.js +3 -0
  40. package/src/router/core/params/README.md +8 -0
  41. package/src/router/core/params/param.js +232 -0
  42. package/src/router/core/params/paramType.js +139 -0
  43. package/src/router/core/params/paramTypes.js +163 -0
  44. package/src/router/core/params/stateParams.js +35 -0
  45. package/src/router/core/path/pathNode.js +77 -0
  46. package/src/router/core/path/pathUtils.js +199 -0
  47. package/src/router/core/resolve/interface.js +10 -0
  48. package/src/router/core/resolve/resolvable.js +124 -0
  49. package/src/router/core/resolve/resolveContext.js +211 -0
  50. package/src/router/core/router.js +203 -0
  51. package/src/router/core/state/README.md +21 -0
  52. package/src/router/core/state/stateBuilder.js +332 -0
  53. package/src/router/core/state/stateMatcher.js +65 -0
  54. package/src/router/core/state/stateObject.js +117 -0
  55. package/src/router/core/state/stateQueueManager.js +89 -0
  56. package/src/router/core/state/stateRegistry.js +175 -0
  57. package/src/router/core/state/stateService.js +592 -0
  58. package/src/router/core/state/targetState.js +159 -0
  59. package/src/router/core/transition/hookBuilder.js +127 -0
  60. package/src/router/core/transition/hookRegistry.js +175 -0
  61. package/src/router/core/transition/interface.js +14 -0
  62. package/src/router/core/transition/rejectFactory.js +122 -0
  63. package/src/router/core/transition/transition.js +739 -0
  64. package/src/router/core/transition/transitionEventType.js +27 -0
  65. package/src/router/core/transition/transitionHook.js +199 -0
  66. package/src/router/core/transition/transitionService.js +311 -0
  67. package/src/router/core/url/interface.js +1 -0
  68. package/src/router/core/url/urlConfig.js +165 -0
  69. package/src/router/core/url/urlMatcher.js +548 -0
  70. package/src/router/core/url/urlMatcherFactory.js +123 -0
  71. package/src/router/core/url/urlRouter.js +115 -0
  72. package/src/router/core/url/urlRule.js +202 -0
  73. package/src/router/core/url/urlRules.js +348 -0
  74. package/src/router/core/url/urlService.js +268 -0
  75. package/src/router/core/view/interface.js +1 -0
  76. package/src/router/core/view/view.js +312 -0
  77. package/src/router/router.js +58 -0
  78. package/test/module-test.html +6 -2
  79. package/test/module-test.js +0 -0
@@ -0,0 +1,739 @@
1
+ import { trace } from "../common/trace";
2
+ import { services } from "../common/coreservices";
3
+ import { stringify } from "../common/strings";
4
+ import {
5
+ map,
6
+ find,
7
+ extend,
8
+ mergeR,
9
+ tail,
10
+ omit,
11
+ arrayTuples,
12
+ unnestR,
13
+ identity,
14
+ anyTrueR,
15
+ flattenR,
16
+ uniqR,
17
+ } from "../common/common";
18
+ import { isObject, isUndefined } from "../common/predicates";
19
+ import { prop, propEq, val, not, is } from "../common/hof";
20
+ import { TransitionHookPhase } from "./interface"; // has or is using
21
+ import { TransitionHook } from "./transitionHook";
22
+ import { matchState, makeEvent } from "./hookRegistry";
23
+ import { HookBuilder } from "./hookBuilder";
24
+ import { PathUtils } from "../path/pathUtils";
25
+ import { Param } from "../params/param";
26
+ import { Resolvable } from "../resolve/resolvable";
27
+ import { ResolveContext } from "../resolve/resolveContext";
28
+ import { Rejection } from "./rejectFactory";
29
+ /** @internal */
30
+ const stateSelf = prop("self");
31
+ /**
32
+ * Represents a transition between two states.
33
+ *
34
+ * When navigating to a state, we are transitioning **from** the current state **to** the new state.
35
+ *
36
+ * This object contains all contextual information about the to/from states, parameters, resolves.
37
+ * It has information about all states being entered and exited as a result of the transition.
38
+ */
39
+ export class Transition {
40
+ /** @internal */
41
+ onBefore(criteria, callback, options) {
42
+ return;
43
+ }
44
+ /** @inheritdoc */
45
+ onStart(criteria, callback, options) {
46
+ return;
47
+ }
48
+ /** @inheritdoc */
49
+ onExit(criteria, callback, options) {
50
+ return;
51
+ }
52
+ /** @inheritdoc */
53
+ onRetain(criteria, callback, options) {
54
+ return;
55
+ }
56
+ /** @inheritdoc */
57
+ onEnter(criteria, callback, options) {
58
+ return;
59
+ }
60
+ /** @inheritdoc */
61
+ onFinish(criteria, callback, options) {
62
+ return;
63
+ }
64
+ /** @inheritdoc */
65
+ onSuccess(criteria, callback, options) {
66
+ return;
67
+ }
68
+ /** @inheritdoc */
69
+ onError(criteria, callback, options) {
70
+ return;
71
+ }
72
+ /** @internal
73
+ * Creates the transition-level hook registration functions
74
+ * (which can then be used to register hooks)
75
+ */
76
+ createTransitionHookRegFns() {
77
+ this.router.transitionService._pluginapi
78
+ ._getEvents()
79
+ .filter((type) => type.hookPhase !== TransitionHookPhase.CREATE)
80
+ .forEach((type) => makeEvent(this, this.router.transitionService, type));
81
+ }
82
+ /** @internal */
83
+ getHooks(hookName) {
84
+ return this._registeredHooks[hookName];
85
+ }
86
+ /**
87
+ * Creates a new Transition object.
88
+ *
89
+ * If the target state is not valid, an error is thrown.
90
+ *
91
+ * @internal
92
+ *
93
+ * @param fromPath The path of [[PathNode]]s from which the transition is leaving. The last node in the `fromPath`
94
+ * encapsulates the "from state".
95
+ * @param targetState The target state and parameters being transitioned to (also, the transition options)
96
+ * @param router The [[UIRouter]] instance
97
+ * @internal
98
+ */
99
+ constructor(fromPath, targetState, router) {
100
+ /** @internal */
101
+ this._deferred = services.$q.defer();
102
+ /**
103
+ * This promise is resolved or rejected based on the outcome of the Transition.
104
+ *
105
+ * When the transition is successful, the promise is resolved
106
+ * When the transition is unsuccessful, the promise is rejected with the [[Rejection]] or javascript error
107
+ */
108
+ this.promise = this._deferred.promise;
109
+ /** @internal Holds the hook registration functions such as those passed to Transition.onStart() */
110
+ this._registeredHooks = {};
111
+ /** @internal */
112
+ this._hookBuilder = new HookBuilder(this);
113
+ /** Checks if this transition is currently active/running. */
114
+ this.isActive = () => this.router.globals.transition === this;
115
+ this.router = router;
116
+ this._targetState = targetState;
117
+ if (!targetState.valid()) {
118
+ throw new Error(targetState.error());
119
+ }
120
+ // current() is assumed to come from targetState.options, but provide a naive implementation otherwise.
121
+ this._options = extend({ current: val(this) }, targetState.options());
122
+ this.$id = router.transitionService._transitionCount++;
123
+ const toPath = PathUtils.buildToPath(fromPath, targetState);
124
+ this._treeChanges = PathUtils.treeChanges(
125
+ fromPath,
126
+ toPath,
127
+ this._options.reloadState,
128
+ );
129
+ this.createTransitionHookRegFns();
130
+ const onCreateHooks = this._hookBuilder.buildHooksForPhase(
131
+ TransitionHookPhase.CREATE,
132
+ );
133
+ TransitionHook.invokeHooks(onCreateHooks, () => null);
134
+ this.applyViewConfigs(router);
135
+ }
136
+ applyViewConfigs(router) {
137
+ const enteringStates = this._treeChanges.entering.map((node) => node.state);
138
+ PathUtils.applyViewConfigs(
139
+ router.transitionService.$view,
140
+ this._treeChanges.to,
141
+ enteringStates,
142
+ );
143
+ }
144
+ /**
145
+ * @internal
146
+ * @returns the internal from [State] object
147
+ */
148
+ $from() {
149
+ return tail(this._treeChanges.from).state;
150
+ }
151
+ /**
152
+ * @internal
153
+ * @returns the internal to [State] object
154
+ */
155
+ $to() {
156
+ return tail(this._treeChanges.to).state;
157
+ }
158
+ /**
159
+ * Returns the "from state"
160
+ *
161
+ * Returns the state that the transition is coming *from*.
162
+ *
163
+ * @returns The state declaration object for the Transition's ("from state").
164
+ */
165
+ from() {
166
+ return this.$from().self;
167
+ }
168
+ /**
169
+ * Returns the "to state"
170
+ *
171
+ * Returns the state that the transition is going *to*.
172
+ *
173
+ * @returns The state declaration object for the Transition's target state ("to state").
174
+ */
175
+ to() {
176
+ return this.$to().self;
177
+ }
178
+ /**
179
+ * Gets the Target State
180
+ *
181
+ * A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]] as a single object.
182
+ *
183
+ * @returns the [[TargetState]] of this Transition
184
+ */
185
+ targetState() {
186
+ return this._targetState;
187
+ }
188
+ /**
189
+ * Determines whether two transitions are equivalent.
190
+ * @deprecated
191
+ */
192
+ is(compare) {
193
+ if (compare instanceof Transition) {
194
+ // TODO: Also compare parameters
195
+ return this.is({ to: compare.$to().name, from: compare.$from().name });
196
+ }
197
+ return !(
198
+ (compare.to && !matchState(this.$to(), compare.to, this)) ||
199
+ (compare.from && !matchState(this.$from(), compare.from, this))
200
+ );
201
+ }
202
+ params(pathname = "to") {
203
+ return Object.freeze(
204
+ this._treeChanges[pathname].map(prop("paramValues")).reduce(mergeR, {}),
205
+ );
206
+ }
207
+ paramsChanged() {
208
+ const fromParams = this.params("from");
209
+ const toParams = this.params("to");
210
+ // All the parameters declared on both the "to" and "from" paths
211
+ const allParamDescriptors = []
212
+ .concat(this._treeChanges.to)
213
+ .concat(this._treeChanges.from)
214
+ .map((pathNode) => pathNode.paramSchema)
215
+ .reduce(flattenR, [])
216
+ .reduce(uniqR, []);
217
+ const changedParamDescriptors = Param.changed(
218
+ allParamDescriptors,
219
+ fromParams,
220
+ toParams,
221
+ );
222
+ return changedParamDescriptors.reduce((changedValues, descriptor) => {
223
+ changedValues[descriptor.id] = toParams[descriptor.id];
224
+ return changedValues;
225
+ }, {});
226
+ }
227
+ /**
228
+ * Creates a [[UIInjector]] Dependency Injector
229
+ *
230
+ * Returns a Dependency Injector for the Transition's target state (to state).
231
+ * The injector provides resolve values which the target state has access to.
232
+ *
233
+ * The `UIInjector` can also provide values from the native root/global injector (ng1/ng2).
234
+ *
235
+ * #### Example:
236
+ * ```js
237
+ * .onEnter({ entering: 'myState' }, trans => {
238
+ * var myResolveValue = trans.injector().get('myResolve');
239
+ * // Inject a global service from the global/native injector (if it exists)
240
+ * var MyService = trans.injector().get('MyService');
241
+ * })
242
+ * ```
243
+ *
244
+ * In some cases (such as `onBefore`), you may need access to some resolve data but it has not yet been fetched.
245
+ * You can use [[UIInjector.getAsync]] to get a promise for the data.
246
+ * #### Example:
247
+ * ```js
248
+ * .onBefore({}, trans => {
249
+ * return trans.injector().getAsync('myResolve').then(myResolveValue =>
250
+ * return myResolveValue !== 'ABORT';
251
+ * });
252
+ * });
253
+ * ```
254
+ *
255
+ * If a `state` is provided, the injector that is returned will be limited to resolve values that the provided state has access to.
256
+ * This can be useful if both a parent state `foo` and a child state `foo.bar` have both defined a resolve such as `data`.
257
+ * #### Example:
258
+ * ```js
259
+ * .onEnter({ to: 'foo.bar' }, trans => {
260
+ * // returns result of `foo` state's `myResolve` resolve
261
+ * // even though `foo.bar` also has a `myResolve` resolve
262
+ * var fooData = trans.injector('foo').get('myResolve');
263
+ * });
264
+ * ```
265
+ *
266
+ * If you need resolve data from the exiting states, pass `'from'` as `pathName`.
267
+ * The resolve data from the `from` path will be returned.
268
+ * #### Example:
269
+ * ```js
270
+ * .onExit({ exiting: 'foo.bar' }, trans => {
271
+ * // Gets the resolve value of `myResolve` from the state being exited
272
+ * var fooData = trans.injector(null, 'from').get('myResolve');
273
+ * });
274
+ * ```
275
+ *
276
+ *
277
+ * @param state Limits the resolves provided to only the resolves the provided state has access to.
278
+ * @param pathName Default: `'to'`: Chooses the path for which to create the injector. Use this to access resolves for `exiting` states.
279
+ *
280
+ * @returns a [[UIInjector]]
281
+ */
282
+ injector(state, pathName = "to") {
283
+ let path = this._treeChanges[pathName];
284
+ if (state)
285
+ path = PathUtils.subPath(
286
+ path,
287
+ (node) => node.state === state || node.state.name === state,
288
+ );
289
+ return new ResolveContext(path).injector();
290
+ }
291
+ /**
292
+ * Gets all available resolve tokens (keys)
293
+ *
294
+ * This method can be used in conjunction with [[injector]] to inspect the resolve values
295
+ * available to the Transition.
296
+ *
297
+ * This returns all the tokens defined on [[StateDeclaration.resolve]] blocks, for the states
298
+ * in the Transition's [[TreeChanges.to]] path.
299
+ *
300
+ * #### Example:
301
+ * This example logs all resolve values
302
+ * ```js
303
+ * let tokens = trans.getResolveTokens();
304
+ * tokens.forEach(token => console.log(token + " = " + trans.injector().get(token)));
305
+ * ```
306
+ *
307
+ * #### Example:
308
+ * This example creates promises for each resolve value.
309
+ * This triggers fetches of resolves (if any have not yet been fetched).
310
+ * When all promises have all settled, it logs the resolve values.
311
+ * ```js
312
+ * let tokens = trans.getResolveTokens();
313
+ * let promise = tokens.map(token => trans.injector().getAsync(token));
314
+ * Promise.all(promises).then(values => console.log("Resolved values: " + values));
315
+ * ```
316
+ *
317
+ * Note: Angular 1 users whould use `$q.all()`
318
+ *
319
+ * @param pathname resolve context's path name (e.g., `to` or `from`)
320
+ *
321
+ * @returns an array of resolve tokens (keys)
322
+ */
323
+ getResolveTokens(pathname = "to") {
324
+ return new ResolveContext(this._treeChanges[pathname]).getTokens();
325
+ }
326
+ /**
327
+ * Dynamically adds a new [[Resolvable]] (i.e., [[StateDeclaration.resolve]]) to this transition.
328
+ *
329
+ * Allows a transition hook to dynamically add a Resolvable to this Transition.
330
+ *
331
+ * Use the [[Transition.injector]] to retrieve the resolved data in subsequent hooks ([[UIInjector.get]]).
332
+ *
333
+ * If a `state` argument is provided, the Resolvable is processed when that state is being entered.
334
+ * If no `state` is provided then the root state is used.
335
+ * If the given `state` has already been entered, the Resolvable is processed when any child state is entered.
336
+ * If no child states will be entered, the Resolvable is processed during the `onFinish` phase of the Transition.
337
+ *
338
+ * The `state` argument also scopes the resolved data.
339
+ * The resolved data is available from the injector for that `state` and any children states.
340
+ *
341
+ * #### Example:
342
+ * ```js
343
+ * transitionService.onBefore({}, transition => {
344
+ * transition.addResolvable({
345
+ * token: 'myResolve',
346
+ * deps: ['MyService'],
347
+ * resolveFn: myService => myService.getData()
348
+ * });
349
+ * });
350
+ * ```
351
+ *
352
+ * @param resolvable a [[ResolvableLiteral]] object (or a [[Resolvable]])
353
+ * @param state the state in the "to path" which should receive the new resolve (otherwise, the root state)
354
+ */
355
+ addResolvable(resolvable, state = "") {
356
+ resolvable = is(Resolvable)(resolvable)
357
+ ? resolvable
358
+ : new Resolvable(resolvable);
359
+ const stateName = typeof state === "string" ? state : state.name;
360
+ const topath = this._treeChanges.to;
361
+ const targetNode = find(topath, (node) => node.state.name === stateName);
362
+ const resolveContext = new ResolveContext(topath);
363
+ resolveContext.addResolvables([resolvable], targetNode.state);
364
+ }
365
+ /**
366
+ * Gets the transition from which this transition was redirected.
367
+ *
368
+ * If the current transition is a redirect, this method returns the transition that was redirected.
369
+ *
370
+ * #### Example:
371
+ * ```js
372
+ * let transitionA = $state.go('A').transition
373
+ * transitionA.onStart({}, () => $state.target('B'));
374
+ * $transitions.onSuccess({ to: 'B' }, (trans) => {
375
+ * trans.to().name === 'B'; // true
376
+ * trans.redirectedFrom() === transitionA; // true
377
+ * });
378
+ * ```
379
+ *
380
+ * @returns The previous Transition, or null if this Transition is not the result of a redirection
381
+ */
382
+ redirectedFrom() {
383
+ return this._options.redirectedFrom || null;
384
+ }
385
+ /**
386
+ * Gets the original transition in a redirect chain
387
+ *
388
+ * A transition might belong to a long chain of multiple redirects.
389
+ * This method walks the [[redirectedFrom]] chain back to the original (first) transition in the chain.
390
+ *
391
+ * #### Example:
392
+ * ```js
393
+ * // states
394
+ * registry.register({ name: 'A', redirectTo: 'B' });
395
+ * registry.register({ name: 'B', redirectTo: 'C' });
396
+ * registry.register({ name: 'C', redirectTo: 'D' });
397
+ * registry.register({ name: 'D' });
398
+ *
399
+ * let transitionA = $state.go('A').transition
400
+ *
401
+ * $transitions.onSuccess({ to: 'D' }, (trans) => {
402
+ * trans.to().name === 'D'; // true
403
+ * trans.redirectedFrom().to().name === 'C'; // true
404
+ * trans.originalTransition() === transitionA; // true
405
+ * trans.originalTransition().to().name === 'A'; // true
406
+ * });
407
+ * ```
408
+ *
409
+ * @returns The original Transition that started a redirect chain
410
+ */
411
+ originalTransition() {
412
+ const rf = this.redirectedFrom();
413
+ return (rf && rf.originalTransition()) || this;
414
+ }
415
+ /**
416
+ * Get the transition options
417
+ *
418
+ * @returns the options for this Transition.
419
+ */
420
+ options() {
421
+ return this._options;
422
+ }
423
+ /**
424
+ * Gets the states being entered.
425
+ *
426
+ * @returns an array of states that will be entered during this transition.
427
+ */
428
+ entering() {
429
+ return map(this._treeChanges.entering, prop("state")).map(stateSelf);
430
+ }
431
+ /**
432
+ * Gets the states being exited.
433
+ *
434
+ * @returns an array of states that will be exited during this transition.
435
+ */
436
+ exiting() {
437
+ return map(this._treeChanges.exiting, prop("state"))
438
+ .map(stateSelf)
439
+ .reverse();
440
+ }
441
+ /**
442
+ * Gets the states being retained.
443
+ *
444
+ * @returns an array of states that are already entered from a previous Transition, that will not be
445
+ * exited during this Transition
446
+ */
447
+ retained() {
448
+ return map(this._treeChanges.retained, prop("state")).map(stateSelf);
449
+ }
450
+ /**
451
+ * Get the [[ViewConfig]]s associated with this Transition
452
+ *
453
+ * Each state can define one or more views (template/controller), which are encapsulated as `ViewConfig` objects.
454
+ * This method fetches the `ViewConfigs` for a given path in the Transition (e.g., "to" or "entering").
455
+ *
456
+ * @param pathname the name of the path to fetch views for:
457
+ * (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`)
458
+ * @param state If provided, only returns the `ViewConfig`s for a single state in the path
459
+ *
460
+ * @returns a list of ViewConfig objects for the given path.
461
+ */
462
+ views(pathname = "entering", state) {
463
+ let path = this._treeChanges[pathname];
464
+ path = !state ? path : path.filter(propEq("state", state));
465
+ return path.map(prop("views")).filter(identity).reduce(unnestR, []);
466
+ }
467
+ treeChanges(pathname) {
468
+ return pathname ? this._treeChanges[pathname] : this._treeChanges;
469
+ }
470
+ /**
471
+ * Creates a new transition that is a redirection of the current one.
472
+ *
473
+ * This transition can be returned from a [[TransitionService]] hook to
474
+ * redirect a transition to a new state and/or set of parameters.
475
+ *
476
+ * @internal
477
+ *
478
+ * @returns Returns a new [[Transition]] instance.
479
+ */
480
+ redirect(targetState) {
481
+ let redirects = 1,
482
+ trans = this;
483
+ // tslint:disable-next-line:no-conditional-assignment
484
+ while ((trans = trans.redirectedFrom()) != null) {
485
+ if (++redirects > 20)
486
+ throw new Error(`Too many consecutive Transition redirects (20+)`);
487
+ }
488
+ const redirectOpts = { redirectedFrom: this, source: "redirect" };
489
+ // If the original transition was caused by URL sync, then use { location: 'replace' }
490
+ // on the new transition (unless the target state explicitly specifies location: false).
491
+ // This causes the original url to be replaced with the url for the redirect target
492
+ // so the original url disappears from the browser history.
493
+ if (
494
+ this.options().source === "url" &&
495
+ targetState.options().location !== false
496
+ ) {
497
+ redirectOpts.location = "replace";
498
+ }
499
+ const newOptions = extend(
500
+ {},
501
+ this.options(),
502
+ targetState.options(),
503
+ redirectOpts,
504
+ );
505
+ targetState = targetState.withOptions(newOptions, true);
506
+ const newTransition = this.router.transitionService.create(
507
+ this._treeChanges.from,
508
+ targetState,
509
+ );
510
+ const originalEnteringNodes = this._treeChanges.entering;
511
+ const redirectEnteringNodes = newTransition._treeChanges.entering;
512
+ // --- Re-use resolve data from original transition ---
513
+ // When redirecting from a parent state to a child state where the parent parameter values haven't changed
514
+ // (because of the redirect), the resolves fetched by the original transition are still valid in the
515
+ // redirected transition.
516
+ //
517
+ // This allows you to define a redirect on a parent state which depends on an async resolve value.
518
+ // You can wait for the resolve, then redirect to a child state based on the result.
519
+ // The redirected transition does not have to re-fetch the resolve.
520
+ // ---------------------------------------------------------
521
+ const nodeIsReloading = (reloadState) => (node) => {
522
+ return reloadState && node.state.includes[reloadState.name];
523
+ };
524
+ // Find any "entering" nodes in the redirect path that match the original path and aren't being reloaded
525
+ const matchingEnteringNodes = PathUtils.matching(
526
+ redirectEnteringNodes,
527
+ originalEnteringNodes,
528
+ PathUtils.nonDynamicParams,
529
+ ).filter(not(nodeIsReloading(targetState.options().reloadState)));
530
+ // Use the existing (possibly pre-resolved) resolvables for the matching entering nodes.
531
+ matchingEnteringNodes.forEach((node, idx) => {
532
+ node.resolvables = originalEnteringNodes[idx].resolvables;
533
+ });
534
+ return newTransition;
535
+ }
536
+ /** @internal If a transition doesn't exit/enter any states, returns any [[Param]] whose value changed */
537
+ _changedParams() {
538
+ const tc = this._treeChanges;
539
+ /** Return undefined if it's not a "dynamic" transition, for the following reasons */
540
+ // If user explicitly wants a reload
541
+ if (this._options.reload) return undefined;
542
+ // If any states are exiting or entering
543
+ if (tc.exiting.length || tc.entering.length) return undefined;
544
+ // If to/from path lengths differ
545
+ if (tc.to.length !== tc.from.length) return undefined;
546
+ // If the to/from paths are different
547
+ const pathsDiffer = arrayTuples(tc.to, tc.from)
548
+ .map((tuple) => tuple[0].state !== tuple[1].state)
549
+ .reduce(anyTrueR, false);
550
+ if (pathsDiffer) return undefined;
551
+ // Find any parameter values that differ
552
+ const nodeSchemas = tc.to.map((node) => node.paramSchema);
553
+ const [toValues, fromValues] = [tc.to, tc.from].map((path) =>
554
+ path.map((x) => x.paramValues),
555
+ );
556
+ const tuples = arrayTuples(nodeSchemas, toValues, fromValues);
557
+ return tuples
558
+ .map(([schema, toVals, fromVals]) =>
559
+ Param.changed(schema, toVals, fromVals),
560
+ )
561
+ .reduce(unnestR, []);
562
+ }
563
+ /**
564
+ * Returns true if the transition is dynamic.
565
+ *
566
+ * A transition is dynamic if no states are entered nor exited, but at least one dynamic parameter has changed.
567
+ *
568
+ * @returns true if the Transition is dynamic
569
+ */
570
+ dynamic() {
571
+ const changes = this._changedParams();
572
+ return !changes
573
+ ? false
574
+ : changes.map((x) => x.dynamic).reduce(anyTrueR, false);
575
+ }
576
+ /**
577
+ * Returns true if the transition is ignored.
578
+ *
579
+ * A transition is ignored if no states are entered nor exited, and no parameter values have changed.
580
+ *
581
+ * @returns true if the Transition is ignored.
582
+ */
583
+ ignored() {
584
+ return !!this._ignoredReason();
585
+ }
586
+ /** @internal */
587
+ _ignoredReason() {
588
+ const pending = this.router.globals.transition;
589
+ const reloadState = this._options.reloadState;
590
+ const same = (pathA, pathB) => {
591
+ if (pathA.length !== pathB.length) return false;
592
+ const matching = PathUtils.matching(pathA, pathB);
593
+ return (
594
+ pathA.length ===
595
+ matching.filter(
596
+ (node) => !reloadState || !node.state.includes[reloadState.name],
597
+ ).length
598
+ );
599
+ };
600
+ const newTC = this.treeChanges();
601
+ const pendTC = pending && pending.treeChanges();
602
+ if (
603
+ pendTC &&
604
+ same(pendTC.to, newTC.to) &&
605
+ same(pendTC.exiting, newTC.exiting)
606
+ )
607
+ return "SameAsPending";
608
+ if (
609
+ newTC.exiting.length === 0 &&
610
+ newTC.entering.length === 0 &&
611
+ same(newTC.from, newTC.to)
612
+ )
613
+ return "SameAsCurrent";
614
+ }
615
+ /**
616
+ * Runs the transition
617
+ *
618
+ * This method is generally called from the [[StateService.transitionTo]]
619
+ *
620
+ * @internal
621
+ *
622
+ * @returns a promise for a successful transition.
623
+ */
624
+ run() {
625
+ const runAllHooks = TransitionHook.runAllHooks;
626
+ // Gets transition hooks array for the given phase
627
+ const getHooksFor = (phase) => this._hookBuilder.buildHooksForPhase(phase);
628
+ // When the chain is complete, then resolve or reject the deferred
629
+ const transitionSuccess = () => {
630
+ trace.traceSuccess(this.$to(), this);
631
+ this.success = true;
632
+ this._deferred.resolve(this.to());
633
+ runAllHooks(getHooksFor(TransitionHookPhase.SUCCESS));
634
+ };
635
+ const transitionError = (reason) => {
636
+ trace.traceError(reason, this);
637
+ this.success = false;
638
+ this._deferred.reject(reason);
639
+ this._error = reason;
640
+ runAllHooks(getHooksFor(TransitionHookPhase.ERROR));
641
+ };
642
+ const runTransition = () => {
643
+ // Wait to build the RUN hook chain until the BEFORE hooks are done
644
+ // This allows a BEFORE hook to dynamically add additional RUN hooks via the Transition object.
645
+ const allRunHooks = getHooksFor(TransitionHookPhase.RUN);
646
+ const done = () => services.$q.when(undefined);
647
+ return TransitionHook.invokeHooks(allRunHooks, done);
648
+ };
649
+ const startTransition = () => {
650
+ const globals = this.router.globals;
651
+ globals.lastStartedTransitionId = this.$id;
652
+ globals.transition = this;
653
+ globals.transitionHistory.enqueue(this);
654
+ trace.traceTransitionStart(this);
655
+ return services.$q.when(undefined);
656
+ };
657
+ const allBeforeHooks = getHooksFor(TransitionHookPhase.BEFORE);
658
+ TransitionHook.invokeHooks(allBeforeHooks, startTransition)
659
+ .then(runTransition)
660
+ .then(transitionSuccess, transitionError);
661
+ return this.promise;
662
+ }
663
+ /**
664
+ * Checks if the Transition is valid
665
+ *
666
+ * @returns true if the Transition is valid
667
+ */
668
+ valid() {
669
+ return !this.error() || this.success !== undefined;
670
+ }
671
+ /**
672
+ * Aborts this transition
673
+ *
674
+ * Imperative API to abort a Transition.
675
+ * This only applies to Transitions that are not yet complete.
676
+ */
677
+ abort() {
678
+ // Do not set flag if the transition is already complete
679
+ if (isUndefined(this.success)) {
680
+ this._aborted = true;
681
+ }
682
+ }
683
+ /**
684
+ * The Transition error reason.
685
+ *
686
+ * If the transition is invalid (and could not be run), returns the reason the transition is invalid.
687
+ * If the transition was valid and ran, but was not successful, returns the reason the transition failed.
688
+ *
689
+ * @returns a transition rejection explaining why the transition is invalid, or the reason the transition failed.
690
+ */
691
+ error() {
692
+ const state = this.$to();
693
+ if (state.self.abstract) {
694
+ return Rejection.invalid(
695
+ `Cannot transition to abstract state '${state.name}'`,
696
+ );
697
+ }
698
+ const paramDefs = state.parameters();
699
+ const values = this.params();
700
+ const invalidParams = paramDefs.filter(
701
+ (param) => !param.validates(values[param.id]),
702
+ );
703
+ if (invalidParams.length) {
704
+ const invalidValues = invalidParams
705
+ .map((param) => `[${param.id}:${stringify(values[param.id])}]`)
706
+ .join(", ");
707
+ const detail = `The following parameter values are not valid for state '${state.name}': ${invalidValues}`;
708
+ return Rejection.invalid(detail);
709
+ }
710
+ if (this.success === false) return this._error;
711
+ }
712
+ /**
713
+ * A string representation of the Transition
714
+ *
715
+ * @returns A string representation of the Transition
716
+ */
717
+ toString() {
718
+ const fromStateOrName = this.from();
719
+ const toStateOrName = this.to();
720
+ const avoidEmptyHash = (params) =>
721
+ params["#"] !== null && params["#"] !== undefined
722
+ ? params
723
+ : omit(params, ["#"]);
724
+ // (X) means the to state is invalid.
725
+ const id = this.$id,
726
+ from = isObject(fromStateOrName) ? fromStateOrName.name : fromStateOrName,
727
+ fromParams = stringify(
728
+ avoidEmptyHash(
729
+ this._treeChanges.from.map(prop("paramValues")).reduce(mergeR, {}),
730
+ ),
731
+ ),
732
+ toValid = this.valid() ? "" : "(X) ",
733
+ to = isObject(toStateOrName) ? toStateOrName.name : toStateOrName,
734
+ toParams = stringify(avoidEmptyHash(this.params()));
735
+ return `Transition#${id}( '${from}'${fromParams} -> ${toValid}'${to}'${toParams} )`;
736
+ }
737
+ }
738
+ /** @internal */
739
+ Transition.diToken = Transition;