#

API Docs for: v2.11.1
Show: Inherited Protected Private Deprecated

# File: node_modules/router_js/lib/router/transition.js

    import { Promise } from 'rsvp';
    import { trigger, slice, log, promiseLabel } from './utils';
    import TransitionAbortedError from './transition-aborted-error';

    /**
      A Transition is a thennable (a promise-like object) that represents
      an attempt to transition to another route. It can be aborted, either
      explicitly via `abort` or by attempting another transition while a
      previous one is still underway. An aborted transition can also
      be `retry()`d later.

      @class Transition
      @constructor
      @param {Object} router
      @param {Object} intent
      @param {Object} state
      @param {Object} error
      @private
     */
    function Transition(router, intent, state, error, previousTransition) {
      var transition = this;
      this.state = state || router.state;
      this.intent = intent;
      this.router = router;
      this.data = this.intent && this.intent.data || {};
      this.resolvedModels = {};
      this.queryParams = {};
      this.promise = undefined;
      this.error = undefined;
      this.params = undefined;
      this.handlerInfos = undefined;
      this.targetName = undefined;
      this.pivotHandler = undefined;
      this.sequence = undefined;
      this.isAborted = false;
      this.isActive = true;

      if (error) {
        this.promise = Promise.reject(error);
        this.error = error;
        return;
      }

      // if you're doing multiple redirects, need the new transition to know if it
      // is actually part of the first transition or not. Any further redirects
      // in the initial transition also need to know if they are part of the
      // initial transition
      this.isCausedByAbortingTransition = !!previousTransition;
      this.isCausedByInitialTransition = (
        previousTransition && (
          previousTransition.isCausedByInitialTransition ||
          previousTransition.sequence === 0
        )
      );

      if (state) {
        this.params = state.params;
        this.queryParams = state.queryParams;
        this.handlerInfos = state.handlerInfos;

        var len = state.handlerInfos.length;
        if (len) {
          this.targetName = state.handlerInfos[len-1].name;
        }

        for (var i = 0; i < len; ++i) {
          var handlerInfo = state.handlerInfos[i];

          // TODO: this all seems hacky
          if (!handlerInfo.isResolved) { break; }
          this.pivotHandler = handlerInfo.handler;
        }

        this.sequence = router.currentSequence++;
        this.promise = state.resolve(checkForAbort, this)['catch'](
          catchHandlerForTransition(transition), promiseLabel('Handle Abort'));
      } else {
        this.promise = Promise.resolve(this.state);
        this.params = {};
      }

      function checkForAbort() {
        if (transition.isAborted) {
          return Promise.reject(undefined, promiseLabel("Transition aborted - reject"));
        }
      }
    }

    function catchHandlerForTransition(transition) {
      return function(result) {
        if (result.wasAborted || transition.isAborted) {
          return Promise.reject(logAbort(transition));
        } else {
          transition.trigger('error', result.error, transition, result.handlerWithError);
          transition.abort();
          return Promise.reject(result.error);
        }
      };
    }

    Transition.prototype = {
      targetName: null,
      urlMethod: 'update',
      intent: null,
      pivotHandler: null,
      resolveIndex: 0,
      resolvedModels: null,
      state: null,
      queryParamsOnly: false,

      isTransition: true,

      isExiting: function(handler) {
        var handlerInfos = this.handlerInfos;
        for (var i = 0, len = handlerInfos.length; i < len; ++i) {
          var handlerInfo = handlerInfos[i];
          if (handlerInfo.name === handler || handlerInfo.handler === handler) {
            return false;
          }
        }
        return true;
      },

      /**
        The Transition's internal promise. Calling `.then` on this property
        is that same as calling `.then` on the Transition object itself, but
        this property is exposed for when you want to pass around a
        Transition's promise, but not the Transition object itself, since
        Transition object can be externally `abort`ed, while the promise
        cannot.

        @property promise
        @type {Object}
        @public
       */
      promise: null,

      /**
        Custom state can be stored on a Transition's `data` object.
        This can be useful for decorating a Transition within an earlier
        hook and shared with a later hook. Properties set on `data` will
        be copied to new transitions generated by calling `retry` on this
        transition.

        @property data
        @type {Object}
        @public
       */
      data: null,

      /**
        A standard promise hook that resolves if the transition
        succeeds and rejects if it fails/redirects/aborts.

        Forwards to the internal `promise` property which you can
        use in situations where you want to pass around a thennable,
        but not the Transition itself.

        @method then
        @param {Function} onFulfilled
        @param {Function} onRejected
        @param {String} label optional string for labeling the promise.
        Useful for tooling.
        @return {Promise}
        @public
       */
      then: function(onFulfilled, onRejected, label) {
        return this.promise.then(onFulfilled, onRejected, label);
      },

      /**

        Forwards to the internal `promise` property which you can
        use in situations where you want to pass around a thennable,
        but not the Transition itself.

        @method catch
        @param {Function} onRejection
        @param {String} label optional string for labeling the promise.
        Useful for tooling.
        @return {Promise}
        @public
       */
      catch: function(onRejection, label) {
        return this.promise.catch(onRejection, label);
      },

      /**

        Forwards to the internal `promise` property which you can
        use in situations where you want to pass around a thennable,
        but not the Transition itself.

        @method finally
        @param {Function} callback
        @param {String} label optional string for labeling the promise.
        Useful for tooling.
        @return {Promise}
        @public
       */
      finally: function(callback, label) {
        return this.promise.finally(callback, label);
      },

      /**
        Aborts the Transition. Note you can also implicitly abort a transition
        by initiating another transition while a previous one is underway.

        @method abort
        @return {Transition} this transition
        @public
       */
      abort: function() {
        if (this.isAborted) { return this; }
        log(this.router, this.sequence, this.targetName + ": transition was aborted");
        this.intent.preTransitionState = this.router.state;
        this.isAborted = true;
        this.isActive = false;
        this.router.activeTransition = null;
        return this;
      },

      /**

        Retries a previously-aborted transition (making sure to abort the
        transition if it's still active). Returns a new transition that
        represents the new attempt to transition.

        @method retry
        @return {Transition} new transition
        @public
       */
      retry: function() {
        // TODO: add tests for merged state retry()s
        this.abort();
        return this.router.transitionByIntent(this.intent, false);
      },

      /**

        Sets the URL-changing method to be employed at the end of a
        successful transition. By default, a new Transition will just
        use `updateURL`, but passing 'replace' to this method will
        cause the URL to update using 'replaceWith' instead. Omitting
        a parameter will disable the URL change, allowing for transitions
        that don't update the URL at completion (this is also used for
        handleURL, since the URL has already changed before the
        transition took place).

        @method method
        @param {String} method the type of URL-changing method to use
          at the end of a transition. Accepted values are 'replace',
          falsy values, or any other non-falsy value (which is
          interpreted as an updateURL transition).

        @return {Transition} this transition
        @public
       */
      method: function(method) {
        this.urlMethod = method;
        return this;
      },

      /**

        Fires an event on the current list of resolved/resolving
        handlers within this transition. Useful for firing events
        on route hierarchies that haven't fully been entered yet.

        Note: This method is also aliased as `send`

        @method trigger
        @param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error
        @param {String} name the name of the event to fire
        @public
       */
      trigger: function (ignoreFailure) {
        var args = slice.call(arguments);
        if (typeof ignoreFailure === 'boolean') {
          args.shift();
        } else {
          // Throw errors on unhandled trigger events by default
          ignoreFailure = false;
        }
        trigger(this.router, this.state.handlerInfos.slice(0, this.resolveIndex + 1), ignoreFailure, args);
      },

      /**
        Transitions are aborted and their promises rejected
        when redirects occur; this method returns a promise
        that will follow any redirects that occur and fulfill
        with the value fulfilled by any redirecting transitions
        that occur.

        @method followRedirects
        @return {Promise} a promise that fulfills with the same
          value that the final redirecting transition fulfills with
        @public
       */
      followRedirects: function() {
        var router = this.router;
        return this.promise['catch'](function(reason) {
          if (router.activeTransition) {
            return router.activeTransition.followRedirects();
          }
          return Promise.reject(reason);
        });
      },

      toString: function() {
        return "Transition (sequence " + this.sequence + ")";
      },

      /**
        @private
       */
      log: function(message) {
        log(this.router, this.sequence, message);
      }
    };

    // Alias 'trigger' as 'send'
    Transition.prototype.send = Transition.prototype.trigger;

    /**
      @private

      Logs and returns an instance of TransitionAbortedError.
     */
    function logAbort(transition) {
      log(transition.router, transition.sequence, "detected abort.");
      return new TransitionAbortedError();
    }

    export { Transition, logAbort, TransitionAbortedError as TransitionAborted };