UNPKG

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