text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  /** @publicapi @module transition */ /** */
  var trace_1 = require("../common/trace");
  var coreservices_1 = require("../common/coreservices");
  var strings_1 = require("../common/strings");
  var common_1 = require("../common/common");
  var predicates_1 = require("../common/predicates");
  var hof_1 = require("../common/hof");
  var interface_1 = require("./interface"); // has or is using
  var transitionHook_1 = require("./transitionHook");
  var hookRegistry_1 = require("./hookRegistry");
  var hookBuilder_1 = require("./hookBuilder");
  var pathUtils_1 = require("../path/pathUtils");
  var param_1 = require("../params/param");
  var resolvable_1 = require("../resolve/resolvable");
  var resolveContext_1 = require("../resolve/resolveContext");
  var rejectFactory_1 = require("./rejectFactory");
  var common_2 = require("../common");
  /** @hidden */
  var stateSelf = hof_1.prop('self');
  /**
   * Represents a transition between two states.
   *
   * When navigating to a state, we are transitioning **from** the current state **to** the new state.
   *
   * This object contains all contextual information about the to/from states, parameters, resolves.
   * It has information about all states being entered and exited as a result of the transition.
   */
  var Transition = /** @class */ (function () {
      /**
       * Creates a new Transition object.
       *
       * If the target state is not valid, an error is thrown.
       *
       * @internalapi
       *
       * @param fromPath The path of [[PathNode]]s from which the transition is leaving.  The last node in the `fromPath`
       *        encapsulates the "from state".
       * @param targetState The target state and parameters being transitioned to (also, the transition options)
       * @param router The [[UIRouter]] instance
       */
      function Transition(fromPath, targetState, router) {
          var _this = this;
          /** @hidden */
          this._deferred = coreservices_1.services.$q.defer();
          /**
           * This promise is resolved or rejected based on the outcome of the Transition.
           *
           * When the transition is successful, the promise is resolved
           * When the transition is unsuccessful, the promise is rejected with the [[Rejection]] or javascript error
           */
          this.promise = this._deferred.promise;
          /** @hidden Holds the hook registration functions such as those passed to Transition.onStart() */
          this._registeredHooks = {};
          /** @hidden */
          this._hookBuilder = new hookBuilder_1.HookBuilder(this);
          /** Checks if this transition is currently active/running. */
          this.isActive = function () { return _this.router.globals.transition === _this; };
          this.router = router;
          this._targetState = targetState;
          if (!targetState.valid()) {
              throw new Error(targetState.error());
          }
          // current() is assumed to come from targetState.options, but provide a naive implementation otherwise.
          this._options = common_1.extend({ current: hof_1.val(this) }, targetState.options());
          this.$id = router.transitionService._transitionCount++;
          var toPath = pathUtils_1.PathUtils.buildToPath(fromPath, targetState);
          this._treeChanges = pathUtils_1.PathUtils.treeChanges(fromPath, toPath, this._options.reloadState);
          this.createTransitionHookRegFns();
          var onCreateHooks = this._hookBuilder.buildHooksForPhase(interface_1.TransitionHookPhase.CREATE);
          transitionHook_1.TransitionHook.invokeHooks(onCreateHooks, function () { return null; });
          this.applyViewConfigs(router);
      }
      /** @hidden */
      Transition.prototype.onBefore = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onStart = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onExit = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onRetain = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onEnter = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onFinish = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onSuccess = function (criteria, callback, options) {
          return;
      };
      /** @inheritdoc */
      Transition.prototype.onError = function (criteria, callback, options) {
          return;
      };
      /** @hidden
       * Creates the transition-level hook registration functions
       * (which can then be used to register hooks)
       */
      Transition.prototype.createTransitionHookRegFns = function () {
          var _this = this;
          this.router.transitionService._pluginapi
              ._getEvents()
              .filter(function (type) { return type.hookPhase !== interface_1.TransitionHookPhase.CREATE; })
              .forEach(function (type) { return hookRegistry_1.makeEvent(_this, _this.router.transitionService, type); });
      };
      /** @internalapi */
      Transition.prototype.getHooks = function (hookName) {
          return this._registeredHooks[hookName];
      };
      Transition.prototype.applyViewConfigs = function (router) {
          var enteringStates = this._treeChanges.entering.map(function (node) { return node.state; });
          pathUtils_1.PathUtils.applyViewConfigs(router.transitionService.$view, this._treeChanges.to, enteringStates);
      };
      /**
       * @internalapi
       *
       * @returns the internal from [State] object
       */
      Transition.prototype.$from = function () {
          return common_1.tail(this._treeChanges.from).state;
      };
      /**
       * @internalapi
       *
       * @returns the internal to [State] object
       */
      Transition.prototype.$to = function () {
          return common_1.tail(this._treeChanges.to).state;
      };
      /**
       * Returns the "from state"
       *
       * Returns the state that the transition is coming *from*.
       *
       * @returns The state declaration object for the Transition's ("from state").
       */
      Transition.prototype.from = function () {
          return this.$from().self;
      };
      /**
       * Returns the "to state"
       *
       * Returns the state that the transition is going *to*.
       *
       * @returns The state declaration object for the Transition's target state ("to state").
       */
      Transition.prototype.to = function () {
          return this.$to().self;
      };
      /**
       * Gets the Target State
       *
       * A transition's [[TargetState]] encapsulates the [[to]] state, the [[params]], and the [[options]] as a single object.
       *
       * @returns the [[TargetState]] of this Transition
       */
      Transition.prototype.targetState = function () {
          return this._targetState;
      };
      /**
       * Determines whether two transitions are equivalent.
       * @deprecated
       */
      Transition.prototype.is = function (compare) {
          if (compare instanceof Transition) {
              // TODO: Also compare parameters
              return this.is({ to: compare.$to().name, from: compare.$from().name });
          }
          return !((compare.to && !hookRegistry_1.matchState(this.$to(), compare.to)) ||
              (compare.from && !hookRegistry_1.matchState(this.$from(), compare.from)));
      };
      Transition.prototype.params = function (pathname) {
          if (pathname === void 0) { pathname = 'to'; }
          return Object.freeze(this._treeChanges[pathname].map(hof_1.prop('paramValues')).reduce(common_1.mergeR, {}));
      };
      Transition.prototype.paramsChanged = function () {
          var fromParams = this.params('from');
          var toParams = this.params('to');
          // All the parameters declared on both the "to" and "from" paths
          var allParamDescriptors = []
              .concat(this._treeChanges.to)
              .concat(this._treeChanges.from)
              .map(function (pathNode) { return pathNode.paramSchema; })
              .reduce(common_2.flattenR, [])
              .reduce(common_2.uniqR, []);
          var changedParamDescriptors = param_1.Param.changed(allParamDescriptors, fromParams, toParams);
          return changedParamDescriptors.reduce(function (changedValues, descriptor) {
              changedValues[descriptor.id] = toParams[descriptor.id];
              return changedValues;
          }, {});
      };
      /**
       * Creates a [[UIInjector]] Dependency Injector
       *
       * Returns a Dependency Injector for the Transition's target state (to state).
       * The injector provides resolve values which the target state has access to.
       *
       * The `UIInjector` can also provide values from the native root/global injector (ng1/ng2).
       *
       * #### Example:
       * ```js
       * .onEnter({ entering: 'myState' }, trans => {
       *   var myResolveValue = trans.injector().get('myResolve');
       *   // Inject a global service from the global/native injector (if it exists)
       *   var MyService = trans.injector().get('MyService');
       * })
       * ```
       *
       * In some cases (such as `onBefore`), you may need access to some resolve data but it has not yet been fetched.
       * You can use [[UIInjector.getAsync]] to get a promise for the data.
       * #### Example:
       * ```js
       * .onBefore({}, trans => {
       *   return trans.injector().getAsync('myResolve').then(myResolveValue =>
       *     return myResolveValue !== 'ABORT';
       *   });
       * });
       * ```
       *
       * If a `state` is provided, the injector that is returned will be limited to resolve values that the provided state has access to.
       * This can be useful if both a parent state `foo` and a child state `foo.bar` have both defined a resolve such as `data`.
       * #### Example:
       * ```js
       * .onEnter({ to: 'foo.bar' }, trans => {
       *   // returns result of `foo` state's `myResolve` resolve
       *   // even though `foo.bar` also has a `myResolve` resolve
       *   var fooData = trans.injector('foo').get('myResolve');
       * });
       * ```
       *
       * If you need resolve data from the exiting states, pass `'from'` as `pathName`.
       * The resolve data from the `from` path will be returned.
       * #### Example:
       * ```js
       * .onExit({ exiting: 'foo.bar' }, trans => {
       *   // Gets the resolve value of `myResolve` from the state being exited
       *   var fooData = trans.injector(null, 'from').get('myResolve');
       * });
       * ```
       *
       *
       * @param state Limits the resolves provided to only the resolves the provided state has access to.
       * @param pathName Default: `'to'`: Chooses the path for which to create the injector. Use this to access resolves for `exiting` states.
       *
       * @returns a [[UIInjector]]
       */
      Transition.prototype.injector = function (state, pathName) {
          if (pathName === void 0) { pathName = 'to'; }
          var path = this._treeChanges[pathName];
          if (state)
              path = pathUtils_1.PathUtils.subPath(path, function (node) { return node.state === state || node.state.name === state; });
          return new resolveContext_1.ResolveContext(path).injector();
      };
      /**
       * Gets all available resolve tokens (keys)
       *
       * This method can be used in conjunction with [[injector]] to inspect the resolve values
       * available to the Transition.
       *
       * This returns all the tokens defined on [[StateDeclaration.resolve]] blocks, for the states
       * in the Transition's [[TreeChanges.to]] path.
       *
       * #### Example:
       * This example logs all resolve values
       * ```js
       * let tokens = trans.getResolveTokens();
       * tokens.forEach(token => console.log(token + " = " + trans.injector().get(token)));
       * ```
       *
       * #### Example:
       * This example creates promises for each resolve value.
       * This triggers fetches of resolves (if any have not yet been fetched).
       * When all promises have all settled, it logs the resolve values.
       * ```js
       * let tokens = trans.getResolveTokens();
       * let promise = tokens.map(token => trans.injector().getAsync(token));
       * Promise.all(promises).then(values => console.log("Resolved values: " + values));
       * ```
       *
       * Note: Angular 1 users whould use `$q.all()`
       *
       * @param pathname resolve context's path name (e.g., `to` or `from`)
       *
       * @returns an array of resolve tokens (keys)
       */
      Transition.prototype.getResolveTokens = function (pathname) {
          if (pathname === void 0) { pathname = 'to'; }
          return new resolveContext_1.ResolveContext(this._treeChanges[pathname]).getTokens();
      };
      /**
       * Dynamically adds a new [[Resolvable]] (i.e., [[StateDeclaration.resolve]]) to this transition.
       *
       * Allows a transition hook to dynamically add a Resolvable to this Transition.
       *
       * Use the [[Transition.injector]] to retrieve the resolved data in subsequent hooks ([[UIInjector.get]]).
       *
       * If a `state` argument is provided, the Resolvable is processed when that state is being entered.
       * If no `state` is provided then the root state is used.
       * If the given `state` has already been entered, the Resolvable is processed when any child state is entered.
       * If no child states will be entered, the Resolvable is processed during the `onFinish` phase of the Transition.
       *
       * The `state` argument also scopes the resolved data.
       * The resolved data is available from the injector for that `state` and any children states.
       *
       * #### Example:
       * ```js
       * transitionService.onBefore({}, transition => {
       *   transition.addResolvable({
       *     token: 'myResolve',
       *     deps: ['MyService'],
       *     resolveFn: myService => myService.getData()
       *   });
       * });
       * ```
       *
       * @param resolvable a [[ResolvableLiteral]] object (or a [[Resolvable]])
       * @param state the state in the "to path" which should receive the new resolve (otherwise, the root state)
       */
      Transition.prototype.addResolvable = function (resolvable, state) {
          if (state === void 0) { state = ''; }
          resolvable = hof_1.is(resolvable_1.Resolvable)(resolvable) ? resolvable : new resolvable_1.Resolvable(resolvable);
          var stateName = typeof state === 'string' ? state : state.name;
          var topath = this._treeChanges.to;
          var targetNode = common_1.find(topath, function (node) { return node.state.name === stateName; });
          var resolveContext = new resolveContext_1.ResolveContext(topath);
          resolveContext.addResolvables([resolvable], targetNode.state);
      };
      /**
       * Gets the transition from which this transition was redirected.
       *
       * If the current transition is a redirect, this method returns the transition that was redirected.
       *
       * #### Example:
       * ```js
       * let transitionA = $state.go('A').transition
       * transitionA.onStart({}, () => $state.target('B'));
       * $transitions.onSuccess({ to: 'B' }, (trans) => {
       *   trans.to().name === 'B'; // true
       *   trans.redirectedFrom() === transitionA; // true
       * });
       * ```
       *
       * @returns The previous Transition, or null if this Transition is not the result of a redirection
       */
      Transition.prototype.redirectedFrom = function () {
          return this._options.redirectedFrom || null;
      };
      /**
       * Gets the original transition in a redirect chain
       *
       * A transition might belong to a long chain of multiple redirects.
       * This method walks the [[redirectedFrom]] chain back to the original (first) transition in the chain.
       *
       * #### Example:
       * ```js
       * // states
       * registry.register({ name: 'A', redirectTo: 'B' });
       * registry.register({ name: 'B', redirectTo: 'C' });
       * registry.register({ name: 'C', redirectTo: 'D' });
       * registry.register({ name: 'D' });
       *
       * let transitionA = $state.go('A').transition
       *
       * $transitions.onSuccess({ to: 'D' }, (trans) => {
       *   trans.to().name === 'D'; // true
       *   trans.redirectedFrom().to().name === 'C'; // true
       *   trans.originalTransition() === transitionA; // true
       *   trans.originalTransition().to().name === 'A'; // true
       * });
       * ```
       *
       * @returns The original Transition that started a redirect chain
       */
      Transition.prototype.originalTransition = function () {
          var rf = this.redirectedFrom();
          return (rf && rf.originalTransition()) || this;
      };
      /**
       * Get the transition options
       *
       * @returns the options for this Transition.
       */
      Transition.prototype.options = function () {
          return this._options;
      };
      /**
       * Gets the states being entered.
       *
       * @returns an array of states that will be entered during this transition.
       */
      Transition.prototype.entering = function () {
          return common_1.map(this._treeChanges.entering, hof_1.prop('state')).map(stateSelf);
      };
      /**
       * Gets the states being exited.
       *
       * @returns an array of states that will be exited during this transition.
       */
      Transition.prototype.exiting = function () {
          return common_1.map(this._treeChanges.exiting, hof_1.prop('state'))
              .map(stateSelf)
              .reverse();
      };
      /**
       * Gets the states being retained.
       *
       * @returns an array of states that are already entered from a previous Transition, that will not be
       *    exited during this Transition
       */
      Transition.prototype.retained = function () {
          return common_1.map(this._treeChanges.retained, hof_1.prop('state')).map(stateSelf);
      };
      /**
       * Get the [[ViewConfig]]s associated with this Transition
       *
       * Each state can define one or more views (template/controller), which are encapsulated as `ViewConfig` objects.
       * This method fetches the `ViewConfigs` for a given path in the Transition (e.g., "to" or "entering").
       *
       * @param pathname the name of the path to fetch views for:
       *   (`'to'`, `'from'`, `'entering'`, `'exiting'`, `'retained'`)
       * @param state If provided, only returns the `ViewConfig`s for a single state in the path
       *
       * @returns a list of ViewConfig objects for the given path.
       */
      Transition.prototype.views = function (pathname, state) {
          if (pathname === void 0) { pathname = 'entering'; }
          var path = this._treeChanges[pathname];
          path = !state ? path : path.filter(hof_1.propEq('state', state));
          return path
              .map(hof_1.prop('views'))
              .filter(common_1.identity)
              .reduce(common_1.unnestR, []);
      };
      Transition.prototype.treeChanges = function (pathname) {
          return pathname ? this._treeChanges[pathname] : this._treeChanges;
      };
      /**
       * Creates a new transition that is a redirection of the current one.
       *
       * This transition can be returned from a [[TransitionService]] hook to
       * redirect a transition to a new state and/or set of parameters.
       *
       * @internalapi
       *
       * @returns Returns a new [[Transition]] instance.
       */
      Transition.prototype.redirect = function (targetState) {
          var redirects = 1, trans = this;
          // tslint:disable-next-line:no-conditional-assignment
          while ((trans = trans.redirectedFrom()) != null) {
              if (++redirects > 20)
                  throw new Error("Too many consecutive Transition redirects (20+)");
          }
          var redirectOpts = { redirectedFrom: this, source: 'redirect' };
          // If the original transition was caused by URL sync, then use { location: 'replace' }
          // on the new transition (unless the target state explicitly specifies location: false).
          // This causes the original url to be replaced with the url for the redirect target
          // so the original url disappears from the browser history.
          if (this.options().source === 'url' && targetState.options().location !== false) {
              redirectOpts.location = 'replace';
          }
          var newOptions = common_1.extend({}, this.options(), targetState.options(), redirectOpts);
          targetState = targetState.withOptions(newOptions, true);
          var newTransition = this.router.transitionService.create(this._treeChanges.from, targetState);
          var originalEnteringNodes = this._treeChanges.entering;
          var redirectEnteringNodes = newTransition._treeChanges.entering;
          // --- Re-use resolve data from original transition ---
          // When redirecting from a parent state to a child state where the parent parameter values haven't changed
          // (because of the redirect), the resolves fetched by the original transition are still valid in the
          // redirected transition.
          //
          // This allows you to define a redirect on a parent state which depends on an async resolve value.
          // You can wait for the resolve, then redirect to a child state based on the result.
          // The redirected transition does not have to re-fetch the resolve.
          // ---------------------------------------------------------
          var nodeIsReloading = function (reloadState) { return function (node) {
              return reloadState && node.state.includes[reloadState.name];
          }; };
          // Find any "entering" nodes in the redirect path that match the original path and aren't being reloaded
          var matchingEnteringNodes = pathUtils_1.PathUtils.matching(redirectEnteringNodes, originalEnteringNodes, pathUtils_1.PathUtils.nonDynamicParams).filter(hof_1.not(nodeIsReloading(targetState.options().reloadState)));
          // Use the existing (possibly pre-resolved) resolvables for the matching entering nodes.
          matchingEnteringNodes.forEach(function (node, idx) {
              node.resolvables = originalEnteringNodes[idx].resolvables;
          });
          return newTransition;
      };
      /** @hidden If a transition doesn't exit/enter any states, returns any [[Param]] whose value changed */
      Transition.prototype._changedParams = function () {
          var tc = this._treeChanges;
          /** Return undefined if it's not a "dynamic" transition, for the following reasons */
          // If user explicitly wants a reload
          if (this._options.reload)
              return undefined;
          // If any states are exiting or entering
          if (tc.exiting.length || tc.entering.length)
              return undefined;
          // If to/from path lengths differ
          if (tc.to.length !== tc.from.length)
              return undefined;
          // If the to/from paths are different
          var pathsDiffer = common_1.arrayTuples(tc.to, tc.from)
              .map(function (tuple) { return tuple[0].state !== tuple[1].state; })
              .reduce(common_1.anyTrueR, false);
          if (pathsDiffer)
              return undefined;
          // Find any parameter values that differ
          var nodeSchemas = tc.to.map(function (node) { return node.paramSchema; });
          var _a = [tc.to, tc.from].map(function (path) { return path.map(function (x) { return x.paramValues; }); }), toValues = _a[0], fromValues = _a[1];
          var tuples = common_1.arrayTuples(nodeSchemas, toValues, fromValues);
          return tuples.map(function (_a) {
              var schema = _a[0], toVals = _a[1], fromVals = _a[2];
              return param_1.Param.changed(schema, toVals, fromVals);
          }).reduce(common_1.unnestR, []);
      };
      /**
       * Returns true if the transition is dynamic.
       *
       * A transition is dynamic if no states are entered nor exited, but at least one dynamic parameter has changed.
       *
       * @returns true if the Transition is dynamic
       */
      Transition.prototype.dynamic = function () {
          var changes = this._changedParams();
          return !changes ? false : changes.map(function (x) { return x.dynamic; }).reduce(common_1.anyTrueR, false);
      };
      /**
       * Returns true if the transition is ignored.
       *
       * A transition is ignored if no states are entered nor exited, and no parameter values have changed.
       *
       * @returns true if the Transition is ignored.
       */
      Transition.prototype.ignored = function () {
          return !!this._ignoredReason();
      };
      /** @hidden */
      Transition.prototype._ignoredReason = function () {
          var pending = this.router.globals.transition;
          var reloadState = this._options.reloadState;
          var same = function (pathA, pathB) {
              if (pathA.length !== pathB.length)
                  return false;
              var matching = pathUtils_1.PathUtils.matching(pathA, pathB);
              return pathA.length === matching.filter(function (node) { return !reloadState || !node.state.includes[reloadState.name]; }).length;
          };
          var newTC = this.treeChanges();
          var pendTC = pending && pending.treeChanges();
          if (pendTC && same(pendTC.to, newTC.to) && same(pendTC.exiting, newTC.exiting))
              return 'SameAsPending';
          if (newTC.exiting.length === 0 && newTC.entering.length === 0 && same(newTC.from, newTC.to))
              return 'SameAsCurrent';
      };
      /**
       * Runs the transition
       *
       * This method is generally called from the [[StateService.transitionTo]]
       *
       * @internalapi
       *
       * @returns a promise for a successful transition.
       */
      Transition.prototype.run = function () {
          var _this = this;
          var runAllHooks = transitionHook_1.TransitionHook.runAllHooks;
          // Gets transition hooks array for the given phase
          var getHooksFor = function (phase) { return _this._hookBuilder.buildHooksForPhase(phase); };
          // When the chain is complete, then resolve or reject the deferred
          var transitionSuccess = function () {
              trace_1.trace.traceSuccess(_this.$to(), _this);
              _this.success = true;
              _this._deferred.resolve(_this.to());
              runAllHooks(getHooksFor(interface_1.TransitionHookPhase.SUCCESS));
          };
          var transitionError = function (reason) {
              trace_1.trace.traceError(reason, _this);
              _this.success = false;
              _this._deferred.reject(reason);
              _this._error = reason;
              runAllHooks(getHooksFor(interface_1.TransitionHookPhase.ERROR));
          };
          var runTransition = function () {
              // Wait to build the RUN hook chain until the BEFORE hooks are done
              // This allows a BEFORE hook to dynamically add additional RUN hooks via the Transition object.
              var allRunHooks = getHooksFor(interface_1.TransitionHookPhase.RUN);
              var done = function () { return coreservices_1.services.$q.when(undefined); };
              return transitionHook_1.TransitionHook.invokeHooks(allRunHooks, done);
          };
          var startTransition = function () {
              var globals = _this.router.globals;
              globals.lastStartedTransitionId = _this.$id;
              globals.transition = _this;
              globals.transitionHistory.enqueue(_this);
              trace_1.trace.traceTransitionStart(_this);
              return coreservices_1.services.$q.when(undefined);
          };
          var allBeforeHooks = getHooksFor(interface_1.TransitionHookPhase.BEFORE);
          transitionHook_1.TransitionHook.invokeHooks(allBeforeHooks, startTransition)
              .then(runTransition)
              .then(transitionSuccess, transitionError);
          return this.promise;
      };
      /**
       * Checks if the Transition is valid
       *
       * @returns true if the Transition is valid
       */
      Transition.prototype.valid = function () {
          return !this.error() || this.success !== undefined;
      };
      /**
       * Aborts this transition
       *
       * Imperative API to abort a Transition.
       * This only applies to Transitions that are not yet complete.
       */
      Transition.prototype.abort = function () {
          // Do not set flag if the transition is already complete
          if (predicates_1.isUndefined(this.success)) {
              this._aborted = true;
          }
      };
      /**
       * The Transition error reason.
       *
       * If the transition is invalid (and could not be run), returns the reason the transition is invalid.
       * If the transition was valid and ran, but was not successful, returns the reason the transition failed.
       *
       * @returns a transition rejection explaining why the transition is invalid, or the reason the transition failed.
       */
      Transition.prototype.error = function () {
          var state = this.$to();
          if (state.self.abstract) {
              return rejectFactory_1.Rejection.invalid("Cannot transition to abstract state '" + state.name + "'");
          }
          var paramDefs = state.parameters();
          var values = this.params();
          var invalidParams = paramDefs.filter(function (param) { return !param.validates(values[param.id]); });
          if (invalidParams.length) {
              var invalidValues = invalidParams.map(function (param) { return "[" + param.id + ":" + strings_1.stringify(values[param.id]) + "]"; }).join(', ');
              var detail = "The following parameter values are not valid for state '" + state.name + "': " + invalidValues;
              return rejectFactory_1.Rejection.invalid(detail);
          }
          if (this.success === false)
              return this._error;
      };
      /**
       * A string representation of the Transition
       *
       * @returns A string representation of the Transition
       */
      Transition.prototype.toString = function () {
          var fromStateOrName = this.from();
          var toStateOrName = this.to();
          var avoidEmptyHash = function (params) {
              return params['#'] !== null && params['#'] !== undefined ? params : common_1.omit(params, ['#']);
          };
          // (X) means the to state is invalid.
          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()));
          return "Transition#" + id + "( '" + from + "'" + fromParams + " -> " + toValid + "'" + to + "'" + toParams + " )";
      };
      /** @hidden */
      Transition.diToken = Transition;
      return Transition;
  }());
  exports.Transition = Transition;
  //# sourceMappingURL=transition.js.map