(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
        typeof define === 'function' && define.amd ? define(['exports'], factory) :
            (factory((global.History = {})));
}(this, (function (exports) { 'use strict';

    function _extends() {
        _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;
        };

        return _extends.apply(this, arguments);
    }

    function isAbsolute(pathname) {
        return pathname.charAt(0) === '/';
    }

    // About 1.5x faster than the two-arg version of Array#splice()
    function spliceOne(list, index) {
        for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) {
            list[i] = list[k];
        }

        list.pop();
    }

    // This implementation is based heavily on node's url.parse
    function resolvePathname(to, from) {
        if (from === undefined) from = '';

        var toParts = (to && to.split('/')) || [];
        var fromParts = (from && from.split('/')) || [];

        var isToAbs = to && isAbsolute(to);
        var isFromAbs = from && isAbsolute(from);
        var mustEndAbs = isToAbs || isFromAbs;

        if (to && isAbsolute(to)) {
            // to is absolute
            fromParts = toParts;
        } else if (toParts.length) {
            // to is relative, drop the filename
            fromParts.pop();
            fromParts = fromParts.concat(toParts);
        }

        if (!fromParts.length) return '/';

        var hasTrailingSlash;
        if (fromParts.length) {
            var last = fromParts[fromParts.length - 1];
            hasTrailingSlash = last === '.' || last === '..' || last === '';
        } else {
            hasTrailingSlash = false;
        }

        var up = 0;
        for (var i = fromParts.length; i >= 0; i--) {
            var part = fromParts[i];

            if (part === '.') {
                spliceOne(fromParts, i);
            } else if (part === '..') {
                spliceOne(fromParts, i);
                up++;
            } else if (up) {
                spliceOne(fromParts, i);
                up--;
            }
        }

        if (!mustEndAbs) for (; up--; up) fromParts.unshift('..');

        if (
            mustEndAbs &&
            fromParts[0] !== '' &&
            (!fromParts[0] || !isAbsolute(fromParts[0]))
        )
            fromParts.unshift('');

        var result = fromParts.join('/');

        if (hasTrailingSlash && result.substr(-1) !== '/') result += '/';

        return result;
    }

    function valueOf(obj) {
        return obj.valueOf ? obj.valueOf() : Object.prototype.valueOf.call(obj);
    }

    function valueEqual(a, b) {
        // Test for strict equality first.
        if (a === b) return true;

        // Otherwise, if either of them == null they are not equal.
        if (a == null || b == null) return false;

        if (Array.isArray(a)) {
            return (
                Array.isArray(b) &&
                a.length === b.length &&
                a.every(function(item, index) {
                    return valueEqual(item, b[index]);
                })
            );
        }

        if (typeof a === 'object' || typeof b === 'object') {
            var aValue = valueOf(a);
            var bValue = valueOf(b);

            if (aValue !== a || bValue !== b) return valueEqual(aValue, bValue);

            return Object.keys(Object.assign({}, a, b)).every(function(key) {
                return valueEqual(a[key], b[key]);
            });
        }

        return false;
    }

    function addLeadingSlash(path) {
        return path.charAt(0) === '/' ? path : '/' + path;
    }
    function stripLeadingSlash(path) {
        return path.charAt(0) === '/' ? path.substr(1) : path;
    }
    function hasBasename(path, prefix) {
        return path.toLowerCase().indexOf(prefix.toLowerCase()) === 0 && '/?#'.indexOf(path.charAt(prefix.length)) !== -1;
    }
    function stripBasename(path, prefix) {
        return hasBasename(path, prefix) ? path.substr(prefix.length) : path;
    }
    function stripTrailingSlash(path) {
        return path.charAt(path.length - 1) === '/' ? path.slice(0, -1) : path;
    }
    function parsePath(path) {
        var pathname = path || '/';
        var search = '';
        var hash = '';
        var hashIndex = pathname.indexOf('#');

        if (hashIndex !== -1) {
            hash = pathname.substr(hashIndex);
            pathname = pathname.substr(0, hashIndex);
        }

        var searchIndex = pathname.indexOf('?');

        if (searchIndex !== -1) {
            search = pathname.substr(searchIndex);
            pathname = pathname.substr(0, searchIndex);
        }

        return {
            pathname: pathname,
            search: search === '?' ? '' : search,
            hash: hash === '#' ? '' : hash
        };
    }
    function createPath(location) {
        var pathname = location.pathname,
            search = location.search,
            hash = location.hash;
        var path = pathname || '/';
        if (search && search !== '?') path += search.charAt(0) === '?' ? search : "?" + search;
        if (hash && hash !== '#') path += hash.charAt(0) === '#' ? hash : "#" + hash;
        return path;
    }

    function createLocation(path, state, key, currentLocation) {
        var location;

        if (typeof path === 'string') {
            // Two-arg form: push(path, state)
            location = parsePath(path);
            location.state = state;
        } else {
            // One-arg form: push(location)
            location = _extends({}, path);
            if (location.pathname === undefined) location.pathname = '';

            if (location.search) {
                if (location.search.charAt(0) !== '?') location.search = '?' + location.search;
            } else {
                location.search = '';
            }

            if (location.hash) {
                if (location.hash.charAt(0) !== '#') location.hash = '#' + location.hash;
            } else {
                location.hash = '';
            }

            if (state !== undefined && location.state === undefined) location.state = state;
        }

        try {
            location.pathname = decodeURI(location.pathname);
        } catch (e) {
            if (e instanceof URIError) {
                throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' + 'This is likely caused by an invalid percent-encoding.');
            } else {
                throw e;
            }
        }

        if (key) location.key = key;

        if (currentLocation) {
            // Resolve incomplete/relative pathname relative to current location.
            if (!location.pathname) {
                location.pathname = currentLocation.pathname;
            } else if (location.pathname.charAt(0) !== '/') {
                location.pathname = resolvePathname(location.pathname, currentLocation.pathname);
            }
        } else {
            // When there is no prior location and pathname is empty, set it to /
            if (!location.pathname) {
                location.pathname = '/';
            }
        }

        return location;
    }
    function locationsAreEqual(a, b) {
        return a.pathname === b.pathname && a.search === b.search && a.hash === b.hash && a.key === b.key && valueEqual(a.state, b.state);
    }

    function warning(condition, message) {
        {
            if (condition) {
                return;
            }

            var text = "Warning: " + message;

            if (typeof console !== 'undefined') {
                console.warn(text);
            }

            try {
                throw Error(text);
            } catch (x) {}
        }
    }

    function createTransitionManager() {
        var prompt = null;

        function setPrompt(nextPrompt) {
            warning(prompt == null, 'A history supports only one prompt at a time');
            prompt = nextPrompt;
            return function () {
                if (prompt === nextPrompt) prompt = null;
            };
        }

        function confirmTransitionTo(location, action, getUserConfirmation, callback) {
            // TODO: If another transition starts while we're still confirming
            // the previous one, we may end up in a weird state. Figure out the
            // best way to handle this.
            if (prompt != null) {
                var result = typeof prompt === 'function' ? prompt(location, action) : prompt;

                if (typeof result === 'string') {
                    if (typeof getUserConfirmation === 'function') {
                        getUserConfirmation(result, callback);
                    } else {
                        warning(false, 'A history needs a getUserConfirmation function in order to use a prompt message');
                        callback(true);
                    }
                } else {
                    // Return false from a transition hook to cancel the transition.
                    callback(result !== false);
                }
            } else {
                callback(true);
            }
        }

        var listeners = [];

        function appendListener(fn) {
            var isActive = true;

            function listener() {
                if (isActive) fn.apply(void 0, arguments);
            }

            listeners.push(listener);
            return function () {
                isActive = false;
                listeners = listeners.filter(function (item) {
                    return item !== listener;
                });
            };
        }

        function notifyListeners() {
            for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
                args[_key] = arguments[_key];
            }

            listeners.forEach(function (listener) {
                return listener.apply(void 0, args);
            });
        }

        return {
            setPrompt: setPrompt,
            confirmTransitionTo: confirmTransitionTo,
            appendListener: appendListener,
            notifyListeners: notifyListeners
        };
    }

    var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
    function getConfirmation(message, callback) {
        callback(window.confirm(message)); // eslint-disable-line no-alert
    }
    /**
     * Returns true if the HTML5 history API is supported. Taken from Modernizr.
     *
     * https://github.com/Modernizr/Modernizr/blob/master/LICENSE
     * https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
     * changed to avoid false negatives for Windows Phones: https://github.com/reactjs/react-router/issues/586
     */

    function supportsHistory() {
        var ua = window.navigator.userAgent;
        if ((ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) && ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('Chrome') === -1 && ua.indexOf('Windows Phone') === -1) return false;
        return window.history && 'pushState' in window.history;
    }
    /**
     * Returns true if browser fires popstate on hash change.
     * IE10 and IE11 do not.
     */

    function supportsPopStateOnHashChange() {
        return window.navigator.userAgent.indexOf('Trident') === -1;
    }
    /**
     * Returns false if using go(n) with hash history causes a full page reload.
     */

    function supportsGoWithoutReloadUsingHash() {
        return window.navigator.userAgent.indexOf('Firefox') === -1;
    }
    /**
     * Returns true if a given popstate event is an extraneous WebKit event.
     * Accounts for the fact that Chrome on iOS fires real popstate events
     * containing undefined state when pressing the back button.
     */

    function isExtraneousPopstateEvent(event) {
        return event.state === undefined && navigator.userAgent.indexOf('CriOS') === -1;
    }

    var prefix = 'Invariant failed';
    function invariant(condition, message) {
        if (condition) {
            return;
        }

        {
            throw new Error(prefix + ": " + (message || ''));
        }
    }

    var PopStateEvent = 'popstate';
    var HashChangeEvent = 'hashchange';

    function getHistoryState() {
        try {
            return window.history.state || {};
        } catch (e) {
            // IE 11 sometimes throws when accessing window.history.state
            // See https://github.com/ReactTraining/history/pull/289
            return {};
        }
    }
    /**
     * Creates a history object that uses the HTML5 history API including
     * pushState, replaceState, and the popstate event.
     */


    function createBrowserHistory(props) {
        if (props === void 0) {
            props = {};
        }

        !canUseDOM ? invariant(false, 'Browser history needs a DOM') : void 0;
        var globalHistory = window.history;
        var canUseHistory = supportsHistory();
        var needsHashChangeListener = !supportsPopStateOnHashChange();
        var _props = props,
            _props$forceRefresh = _props.forceRefresh,
            forceRefresh = _props$forceRefresh === void 0 ? false : _props$forceRefresh,
            _props$getUserConfirm = _props.getUserConfirmation,
            getUserConfirmation = _props$getUserConfirm === void 0 ? getConfirmation : _props$getUserConfirm,
            _props$keyLength = _props.keyLength,
            keyLength = _props$keyLength === void 0 ? 6 : _props$keyLength;
        var basename = props.basename ? stripTrailingSlash(addLeadingSlash(props.basename)) : '';

        function getDOMLocation(historyState) {
            var _ref = historyState || {},
                key = _ref.key,
                state = _ref.state;

            var _window$location = window.location,
                pathname = _window$location.pathname,
                search = _window$location.search,
                hash = _window$location.hash;
            var path = pathname + search + hash;
            warning(!basename || hasBasename(path, basename), 'You are attempting to use a basename on a page whose URL path does not begin ' + 'with the basename. Expected path "' + path + '" to begin with "' + basename + '".');
            if (basename) path = stripBasename(path, basename);
            return createLocation(path, state, key);
        }

        function createKey() {
            return Math.random().toString(36).substr(2, keyLength);
        }

        var transitionManager = createTransitionManager();

        function setState(nextState) {
            _extends(history, nextState);

            history.length = globalHistory.length;
            transitionManager.notifyListeners(history.location, history.action);
        }

        function handlePopState(event) {
            // Ignore extraneous popstate events in WebKit.
            if (isExtraneousPopstateEvent(event)) return;
            handlePop(getDOMLocation(event.state));
        }

        function handleHashChange() {
            handlePop(getDOMLocation(getHistoryState()));
        }

        var forceNextPop = false;

        function handlePop(location) {
            if (forceNextPop) {
                forceNextPop = false;
                setState();
            } else {
                var action = 'POP';
                transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                    if (ok) {
                        setState({
                            action: action,
                            location: location
                        });
                    } else {
                        revertPop(location);
                    }
                });
            }
        }

        function revertPop(fromLocation) {
            var toLocation = history.location; // TODO: We could probably make this more reliable by
            // keeping a list of keys we've seen in sessionStorage.
            // Instead, we just default to 0 for keys we don't know.

            var toIndex = allKeys.indexOf(toLocation.key);
            if (toIndex === -1) toIndex = 0;
            var fromIndex = allKeys.indexOf(fromLocation.key);
            if (fromIndex === -1) fromIndex = 0;
            var delta = toIndex - fromIndex;

            if (delta) {
                forceNextPop = true;
                go(delta);
            }
        }

        var initialLocation = getDOMLocation(getHistoryState());
        var allKeys = [initialLocation.key]; // Public interface

        function createHref(location) {
            return basename + createPath(location);
        }

        function push(path, state) {
            warning(!(typeof path === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to push when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
            var action = 'PUSH';
            var location = createLocation(path, state, createKey(), history.location);
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (!ok) return;
                var href = createHref(location);
                var key = location.key,
                    state = location.state;

                if (canUseHistory) {
                    globalHistory.pushState({
                        key: key,
                        state: state
                    }, null, href);

                    if (forceRefresh) {
                        window.location.href = href;
                    } else {
                        var prevIndex = allKeys.indexOf(history.location.key);
                        var nextKeys = allKeys.slice(0, prevIndex + 1);
                        nextKeys.push(location.key);
                        allKeys = nextKeys;
                        setState({
                            action: action,
                            location: location
                        });
                    }
                } else {
                    warning(state === undefined, 'Browser history cannot push state in browsers that do not support HTML5 history');
                    window.location.href = href;
                }
            });
        }

        function replace(path, state) {
            warning(!(typeof path === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to replace when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
            var action = 'REPLACE';
            var location = createLocation(path, state, createKey(), history.location);
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (!ok) return;
                var href = createHref(location);
                var key = location.key,
                    state = location.state;

                if (canUseHistory) {
                    globalHistory.replaceState({
                        key: key,
                        state: state
                    }, null, href);

                    if (forceRefresh) {
                        window.location.replace(href);
                    } else {
                        var prevIndex = allKeys.indexOf(history.location.key);
                        if (prevIndex !== -1) allKeys[prevIndex] = location.key;
                        setState({
                            action: action,
                            location: location
                        });
                    }
                } else {
                    warning(state === undefined, 'Browser history cannot replace state in browsers that do not support HTML5 history');
                    window.location.replace(href);
                }
            });
        }

        function go(n) {
            globalHistory.go(n);
        }

        function goBack() {
            go(-1);
        }

        function goForward() {
            go(1);
        }

        var listenerCount = 0;

        function checkDOMListeners(delta) {
            listenerCount += delta;

            if (listenerCount === 1 && delta === 1) {
                window.addEventListener(PopStateEvent, handlePopState);
                if (needsHashChangeListener) window.addEventListener(HashChangeEvent, handleHashChange);
            } else if (listenerCount === 0) {
                window.removeEventListener(PopStateEvent, handlePopState);
                if (needsHashChangeListener) window.removeEventListener(HashChangeEvent, handleHashChange);
            }
        }

        var isBlocked = false;

        function block(prompt) {
            if (prompt === void 0) {
                prompt = false;
            }

            var unblock = transitionManager.setPrompt(prompt);

            if (!isBlocked) {
                checkDOMListeners(1);
                isBlocked = true;
            }

            return function () {
                if (isBlocked) {
                    isBlocked = false;
                    checkDOMListeners(-1);
                }

                return unblock();
            };
        }

        function listen(listener) {
            var unlisten = transitionManager.appendListener(listener);
            checkDOMListeners(1);
            return function () {
                checkDOMListeners(-1);
                unlisten();
            };
        }

        var history = {
            length: globalHistory.length,
            action: 'POP',
            location: initialLocation,
            createHref: createHref,
            push: push,
            replace: replace,
            go: go,
            goBack: goBack,
            goForward: goForward,
            block: block,
            listen: listen
        };
        return history;
    }

    var HashChangeEvent$1 = 'hashchange';
    var HashPathCoders = {
        hashbang: {
            encodePath: function encodePath(path) {
                return path.charAt(0) === '!' ? path : '!/' + stripLeadingSlash(path);
            },
            decodePath: function decodePath(path) {
                return path.charAt(0) === '!' ? path.substr(1) : path;
            }
        },
        noslash: {
            encodePath: stripLeadingSlash,
            decodePath: addLeadingSlash
        },
        slash: {
            encodePath: addLeadingSlash,
            decodePath: addLeadingSlash
        }
    };

    function stripHash(url) {
        var hashIndex = url.indexOf('#');
        return hashIndex === -1 ? url : url.slice(0, hashIndex);
    }

    function getHashPath() {
        // We can't use window.location.hash here because it's not
        // consistent across browsers - Firefox will pre-decode it!
        var href = window.location.href;
        var hashIndex = href.indexOf('#');
        return hashIndex === -1 ? '' : href.substring(hashIndex + 1);
    }

    function pushHashPath(path) {
        window.location.hash = path;
    }

    function replaceHashPath(path) {
        window.location.replace(stripHash(window.location.href) + '#' + path);
    }

    function createHashHistory(props) {
        if (props === void 0) {
            props = {};
        }

        !canUseDOM ? invariant(false, 'Hash history needs a DOM') : void 0;
        var globalHistory = window.history;
        var canGoWithoutReload = supportsGoWithoutReloadUsingHash();
        var _props = props,
            _props$getUserConfirm = _props.getUserConfirmation,
            getUserConfirmation = _props$getUserConfirm === void 0 ? getConfirmation : _props$getUserConfirm,
            _props$hashType = _props.hashType,
            hashType = _props$hashType === void 0 ? 'slash' : _props$hashType;
        var basename = props.basename ? stripTrailingSlash(addLeadingSlash(props.basename)) : '';
        var _HashPathCoders$hashT = HashPathCoders[hashType],
            encodePath = _HashPathCoders$hashT.encodePath,
            decodePath = _HashPathCoders$hashT.decodePath;

        function getDOMLocation() {
            var path = decodePath(getHashPath());
            warning(!basename || hasBasename(path, basename), 'You are attempting to use a basename on a page whose URL path does not begin ' + 'with the basename. Expected path "' + path + '" to begin with "' + basename + '".');
            if (basename) path = stripBasename(path, basename);
            return createLocation(path);
        }

        var transitionManager = createTransitionManager();

        function setState(nextState) {
            _extends(history, nextState);

            history.length = globalHistory.length;
            transitionManager.notifyListeners(history.location, history.action);
        }

        var forceNextPop = false;
        var ignorePath = null;

        function locationsAreEqual$$1(a, b) {
            return a.pathname === b.pathname && a.search === b.search && a.hash === b.hash;
        }

        function handleHashChange() {
            var path = getHashPath();
            var encodedPath = encodePath(path);

            if (path !== encodedPath) {
                // Ensure we always have a properly-encoded hash.
                replaceHashPath(encodedPath);
            } else {
                var location = getDOMLocation();
                var prevLocation = history.location;
                if (!forceNextPop && locationsAreEqual$$1(prevLocation, location)) return; // A hashchange doesn't always == location change.

                if (ignorePath === createPath(location)) return; // Ignore this change; we already setState in push/replace.

                ignorePath = null;
                handlePop(location);
            }
        }

        function handlePop(location) {
            if (forceNextPop) {
                forceNextPop = false;
                setState();
            } else {
                var action = 'POP';
                transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                    if (ok) {
                        setState({
                            action: action,
                            location: location
                        });
                    } else {
                        revertPop(location);
                    }
                });
            }
        }

        function revertPop(fromLocation) {
            var toLocation = history.location; // TODO: We could probably make this more reliable by
            // keeping a list of paths we've seen in sessionStorage.
            // Instead, we just default to 0 for paths we don't know.

            var toIndex = allPaths.lastIndexOf(createPath(toLocation));
            if (toIndex === -1) toIndex = 0;
            var fromIndex = allPaths.lastIndexOf(createPath(fromLocation));
            if (fromIndex === -1) fromIndex = 0;
            var delta = toIndex - fromIndex;

            if (delta) {
                forceNextPop = true;
                go(delta);
            }
        } // Ensure the hash is encoded properly before doing anything else.


        var path = getHashPath();
        var encodedPath = encodePath(path);
        if (path !== encodedPath) replaceHashPath(encodedPath);
        var initialLocation = getDOMLocation();
        var allPaths = [createPath(initialLocation)]; // Public interface

        function createHref(location) {
            var baseTag = document.querySelector('base');
            var href = '';

            if (baseTag && baseTag.getAttribute('href')) {
                href = stripHash(window.location.href);
            }

            return href + '#' + encodePath(basename + createPath(location));
        }

        function push(path, state) {
            warning(state === undefined, 'Hash history cannot push state; it is ignored');
            var action = 'PUSH';
            var location = createLocation(path, undefined, undefined, history.location);
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (!ok) return;
                var path = createPath(location);
                var encodedPath = encodePath(basename + path);
                var hashChanged = getHashPath() !== encodedPath;

                if (hashChanged) {
                    // We cannot tell if a hashchange was caused by a PUSH, so we'd
                    // rather setState here and ignore the hashchange. The caveat here
                    // is that other hash histories in the page will consider it a POP.
                    ignorePath = path;
                    pushHashPath(encodedPath);
                    var prevIndex = allPaths.lastIndexOf(createPath(history.location));
                    var nextPaths = allPaths.slice(0, prevIndex + 1);
                    nextPaths.push(path);
                    allPaths = nextPaths;
                    setState({
                        action: action,
                        location: location
                    });
                } else {
                    warning(false, 'Hash history cannot PUSH the same path; a new entry will not be added to the history stack');
                    setState();
                }
            });
        }

        function replace(path, state) {
            warning(state === undefined, 'Hash history cannot replace state; it is ignored');
            var action = 'REPLACE';
            var location = createLocation(path, undefined, undefined, history.location);
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (!ok) return;
                var path = createPath(location);
                var encodedPath = encodePath(basename + path);
                var hashChanged = getHashPath() !== encodedPath;

                if (hashChanged) {
                    // We cannot tell if a hashchange was caused by a REPLACE, so we'd
                    // rather setState here and ignore the hashchange. The caveat here
                    // is that other hash histories in the page will consider it a POP.
                    ignorePath = path;
                    replaceHashPath(encodedPath);
                }

                var prevIndex = allPaths.indexOf(createPath(history.location));
                if (prevIndex !== -1) allPaths[prevIndex] = path;
                setState({
                    action: action,
                    location: location
                });
            });
        }

        function go(n) {
            warning(canGoWithoutReload, 'Hash history go(n) causes a full page reload in this browser');
            globalHistory.go(n);
        }

        function goBack() {
            go(-1);
        }

        function goForward() {
            go(1);
        }

        var listenerCount = 0;

        function checkDOMListeners(delta) {
            listenerCount += delta;

            if (listenerCount === 1 && delta === 1) {
                window.addEventListener(HashChangeEvent$1, handleHashChange);
            } else if (listenerCount === 0) {
                window.removeEventListener(HashChangeEvent$1, handleHashChange);
            }
        }

        var isBlocked = false;

        function block(prompt) {
            if (prompt === void 0) {
                prompt = false;
            }

            var unblock = transitionManager.setPrompt(prompt);

            if (!isBlocked) {
                checkDOMListeners(1);
                isBlocked = true;
            }

            return function () {
                if (isBlocked) {
                    isBlocked = false;
                    checkDOMListeners(-1);
                }

                return unblock();
            };
        }

        function listen(listener) {
            var unlisten = transitionManager.appendListener(listener);
            checkDOMListeners(1);
            return function () {
                checkDOMListeners(-1);
                unlisten();
            };
        }

        var history = {
            length: globalHistory.length,
            action: 'POP',
            location: initialLocation,
            createHref: createHref,
            push: push,
            replace: replace,
            go: go,
            goBack: goBack,
            goForward: goForward,
            block: block,
            listen: listen
        };
        return history;
    }

    function clamp(n, lowerBound, upperBound) {
        return Math.min(Math.max(n, lowerBound), upperBound);
    }
    /**
     * Creates a history object that stores locations in memory.
     */


    function createMemoryHistory(props) {
        if (props === void 0) {
            props = {};
        }

        var _props = props,
            getUserConfirmation = _props.getUserConfirmation,
            _props$initialEntries = _props.initialEntries,
            initialEntries = _props$initialEntries === void 0 ? ['/'] : _props$initialEntries,
            _props$initialIndex = _props.initialIndex,
            initialIndex = _props$initialIndex === void 0 ? 0 : _props$initialIndex,
            _props$keyLength = _props.keyLength,
            keyLength = _props$keyLength === void 0 ? 6 : _props$keyLength;
        var transitionManager = createTransitionManager();

        function setState(nextState) {
            _extends(history, nextState);

            history.length = history.entries.length;
            transitionManager.notifyListeners(history.location, history.action);
        }

        function createKey() {
            return Math.random().toString(36).substr(2, keyLength);
        }

        var index = clamp(initialIndex, 0, initialEntries.length - 1);
        var entries = initialEntries.map(function (entry) {
            return typeof entry === 'string' ? createLocation(entry, undefined, createKey()) : createLocation(entry, undefined, entry.key || createKey());
        }); // Public interface

        var createHref = createPath;

        function push(path, state) {
            warning(!(typeof path === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to push when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
            var action = 'PUSH';
            var location = createLocation(path, state, createKey(), history.location);
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (!ok) return;
                var prevIndex = history.index;
                var nextIndex = prevIndex + 1;
                var nextEntries = history.entries.slice(0);

                if (nextEntries.length > nextIndex) {
                    nextEntries.splice(nextIndex, nextEntries.length - nextIndex, location);
                } else {
                    nextEntries.push(location);
                }

                setState({
                    action: action,
                    location: location,
                    index: nextIndex,
                    entries: nextEntries
                });
            });
        }

        function replace(path, state) {
            warning(!(typeof path === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to replace when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
            var action = 'REPLACE';
            var location = createLocation(path, state, createKey(), history.location);
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (!ok) return;
                history.entries[history.index] = location;
                setState({
                    action: action,
                    location: location
                });
            });
        }

        function go(n) {
            var nextIndex = clamp(history.index + n, 0, history.entries.length - 1);
            var action = 'POP';
            var location = history.entries[nextIndex];
            transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
                if (ok) {
                    setState({
                        action: action,
                        location: location,
                        index: nextIndex
                    });
                } else {
                    // Mimic the behavior of DOM histories by
                    // causing a render after a cancelled POP.
                    setState();
                }
            });
        }

        function goBack() {
            go(-1);
        }

        function goForward() {
            go(1);
        }

        function canGo(n) {
            var nextIndex = history.index + n;
            return nextIndex >= 0 && nextIndex < history.entries.length;
        }

        function block(prompt) {
            if (prompt === void 0) {
                prompt = false;
            }

            return transitionManager.setPrompt(prompt);
        }

        function listen(listener) {
            return transitionManager.appendListener(listener);
        }

        var history = {
            length: entries.length,
            action: 'POP',
            location: entries[index],
            index: index,
            entries: entries,
            createHref: createHref,
            push: push,
            replace: replace,
            go: go,
            goBack: goBack,
            goForward: goForward,
            canGo: canGo,
            block: block,
            listen: listen
        };
        return history;
    }

    exports.createBrowserHistory = createBrowserHistory;
    exports.createHashHistory = createHashHistory;
    exports.createMemoryHistory = createMemoryHistory;
    exports.createLocation = createLocation;
    exports.locationsAreEqual = locationsAreEqual;
    exports.parsePath = parsePath;
    exports.createPath = createPath;

    Object.defineProperty(exports, '__esModule', { value: true });

})));