text: 'use strict';
  
  exports.__esModule = true;
  
  var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  exports.default = createTransitionManager;
  
  var _routerWarning = require('./routerWarning');
  
  var _routerWarning2 = _interopRequireDefault(_routerWarning);
  
  var _computeChangedRoutes2 = require('./computeChangedRoutes');
  
  var _computeChangedRoutes3 = _interopRequireDefault(_computeChangedRoutes2);
  
  var _TransitionUtils = require('./TransitionUtils');
  
  var _isActive2 = require('./isActive');
  
  var _isActive3 = _interopRequireDefault(_isActive2);
  
  var _getComponents = require('./getComponents');
  
  var _getComponents2 = _interopRequireDefault(_getComponents);
  
  var _matchRoutes = require('./matchRoutes');
  
  var _matchRoutes2 = _interopRequireDefault(_matchRoutes);
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  function hasAnyProperties(object) {
    for (var p in object) {
      if (Object.prototype.hasOwnProperty.call(object, p)) return true;
    }return false;
  }
  
  function createTransitionManager(history, routes) {
    var state = {};
  
    // Signature should be (location, indexOnly), but needs to support (path,
    // query, indexOnly)
    function isActive(location, indexOnly) {
      location = history.createLocation(location);
  
      return (0, _isActive3.default)(location, indexOnly, state.location, state.routes, state.params);
    }
  
    var partialNextState = void 0;
  
    function match(location, callback) {
      if (partialNextState && partialNextState.location === location) {
        // Continue from where we left off.
        finishMatch(partialNextState, callback);
      } else {
        (0, _matchRoutes2.default)(routes, location, function (error, nextState) {
          if (error) {
            callback(error);
          } else if (nextState) {
            finishMatch(_extends({}, nextState, { location: location }), callback);
          } else {
            callback();
          }
        });
      }
    }
  
    function finishMatch(nextState, callback) {
      var _computeChangedRoutes = (0, _computeChangedRoutes3.default)(state, nextState),
          leaveRoutes = _computeChangedRoutes.leaveRoutes,
          changeRoutes = _computeChangedRoutes.changeRoutes,
          enterRoutes = _computeChangedRoutes.enterRoutes;
  
      (0, _TransitionUtils.runLeaveHooks)(leaveRoutes, state);
  
      // Tear down confirmation hooks for left routes
      leaveRoutes.filter(function (route) {
        return enterRoutes.indexOf(route) === -1;
      }).forEach(removeListenBeforeHooksForRoute);
  
      // change and enter hooks are run in series
      (0, _TransitionUtils.runChangeHooks)(changeRoutes, state, nextState, function (error, redirectInfo) {
        if (error || redirectInfo) return handleErrorOrRedirect(error, redirectInfo);
  
        (0, _TransitionUtils.runEnterHooks)(enterRoutes, nextState, finishEnterHooks);
      });
  
      function finishEnterHooks(error, redirectInfo) {
        if (error || redirectInfo) return handleErrorOrRedirect(error, redirectInfo);
  
        // TODO: Fetch components after state is updated.
        (0, _getComponents2.default)(nextState, function (error, components) {
          if (error) {
            callback(error);
          } else {
            // TODO: Make match a pure function and have some other API
            // for "match and update state".
            callback(null, null, state = _extends({}, nextState, { components: components }));
          }
        });
      }
  
      function handleErrorOrRedirect(error, redirectInfo) {
        if (error) callback(error);else callback(null, redirectInfo);
      }
    }
  
    var RouteGuid = 1;
  
    function getRouteID(route) {
      var create = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  
      return route.__id__ || create && (route.__id__ = RouteGuid++);
    }
  
    var RouteHooks = Object.create(null);
  
    function getRouteHooksForRoutes(routes) {
      return routes.map(function (route) {
        return RouteHooks[getRouteID(route)];
      }).filter(function (hook) {
        return hook;
      });
    }
  
    function transitionHook(location, callback) {
      (0, _matchRoutes2.default)(routes, location, function (error, nextState) {
        if (nextState == null) {
          // TODO: We didn't actually match anything, but hang
          // onto error/nextState so we don't have to matchRoutes
          // again in the listen callback.
          callback();
          return;
        }
  
        // Cache some state here so we don't have to
        // matchRoutes() again in the listen callback.
        partialNextState = _extends({}, nextState, { location: location });
  
        var hooks = getRouteHooksForRoutes((0, _computeChangedRoutes3.default)(state, partialNextState).leaveRoutes);
  
        var result = void 0;
        for (var i = 0, len = hooks.length; result == null && i < len; ++i) {
          // Passing the location arg here indicates to
          // the user that this is a transition hook.
          result = hooks[i](location);
        }
  
        callback(result);
      });
    }
  
    /* istanbul ignore next: untestable with Karma */
    function beforeUnloadHook() {
      // Synchronously check to see if any route hooks want
      // to prevent the current window/tab from closing.
      if (state.routes) {
        var hooks = getRouteHooksForRoutes(state.routes);
  
        var message = void 0;
        for (var i = 0, len = hooks.length; typeof message !== 'string' && i < len; ++i) {
          // Passing no args indicates to the user that this is a
          // beforeunload hook. We don't know the next location.
          message = hooks[i]();
        }
  
        return message;
      }
    }
  
    var unlistenBefore = void 0,
        unlistenBeforeUnload = void 0;
  
    function removeListenBeforeHooksForRoute(route) {
      var routeID = getRouteID(route);
      if (!routeID) {
        return;
      }
  
      delete RouteHooks[routeID];
  
      if (!hasAnyProperties(RouteHooks)) {
        // teardown transition & beforeunload hooks
        if (unlistenBefore) {
          unlistenBefore();
          unlistenBefore = null;
        }
  
        if (unlistenBeforeUnload) {
          unlistenBeforeUnload();
          unlistenBeforeUnload = null;
        }
      }
    }
  
    /**
     * Registers the given hook function to run before leaving the given route.
     *
     * During a normal transition, the hook function receives the next location
     * as its only argument and can return either a prompt message (string) to show the user,
     * to make sure they want to leave the page; or `false`, to prevent the transition.
     * Any other return value will have no effect.
     *
     * During the beforeunload event (in browsers) the hook receives no arguments.
     * In this case it must return a prompt message to prevent the transition.
     *
     * Returns a function that may be used to unbind the listener.
     */
    function listenBeforeLeavingRoute(route, hook) {
      var thereWereNoRouteHooks = !hasAnyProperties(RouteHooks);
      var routeID = getRouteID(route, true);
  
      RouteHooks[routeID] = hook;
  
      if (thereWereNoRouteHooks) {
        // setup transition & beforeunload hooks
        unlistenBefore = history.listenBefore(transitionHook);
  
        if (history.listenBeforeUnload) unlistenBeforeUnload = history.listenBeforeUnload(beforeUnloadHook);
      }
  
      return function () {
        removeListenBeforeHooksForRoute(route);
      };
    }
  
    /**
     * This is the API for stateful environments. As the location
     * changes, we update state and call the listener. We can also
     * gracefully handle errors and redirects.
     */
    function listen(listener) {
      function historyListener(location) {
        if (state.location === location) {
          listener(null, state);
        } else {
          match(location, function (error, redirectLocation, nextState) {
            if (error) {
              listener(error);
            } else if (redirectLocation) {
              history.replace(redirectLocation);
            } else if (nextState) {
              listener(null, nextState);
            } else {
              process.env.NODE_ENV !== 'production' ? (0, _routerWarning2.default)(false, 'Location "%s" did not match any routes', location.pathname + location.search + location.hash) : void 0;
            }
          });
        }
      }
  
      // TODO: Only use a single history listener. Otherwise we'll end up with
      // multiple concurrent calls to match.
  
      // Set up the history listener first in case the initial match redirects.
      var unsubscribe = history.listen(historyListener);
  
      if (state.location) {
        // Picking up on a matchContext.
        listener(null, state);
      } else {
        historyListener(history.getCurrentLocation());
      }
  
      return unsubscribe;
    }
  
    return {
      isActive: isActive,
      match: match,
      listenBeforeLeavingRoute: listenBeforeLeavingRoute,
      listen: listen
    };
  }
  module.exports = exports['default'];