UNPKG

react-virtualized

Version:

React components for efficiently rendering large, scrollable lists and tabular data

307 lines (220 loc) 9.75 kB
'use strict'; exports.__esModule = true; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; 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; }; var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _LocationUtils = require('./LocationUtils'); var _PathUtils = require('./PathUtils'); var _createTransitionManager = require('./createTransitionManager'); var _createTransitionManager2 = _interopRequireDefault(_createTransitionManager); var _ExecutionEnvironment = require('./ExecutionEnvironment'); var _DOMUtils = require('./DOMUtils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var PopStateEvent = 'popstate'; var HashChangeEvent = 'hashchange'; var getHistoryState = function getHistoryState() { try { return window.history.state || {}; } catch (e) { // IE 11 sometimes throws when accessing window.history.state // See https://github.com/mjackson/history/pull/289 return {}; } }; /** * Creates a history object that uses the HTML5 history API including * pushState, replaceState, and the popstate event. */ var createBrowserHistory = function createBrowserHistory() { var props = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? (0, _invariant2.default)(false, 'Browser history needs a DOM') : (0, _invariant2.default)(false) : void 0; var globalHistory = window.history; var canUseHistory = (0, _DOMUtils.supportsHistory)(); var needsHashChangeListener = !(0, _DOMUtils.supportsPopStateOnHashChange)(); var _props$basename = props.basename; var basename = _props$basename === undefined ? '' : _props$basename; var _props$forceRefresh = props.forceRefresh; var forceRefresh = _props$forceRefresh === undefined ? false : _props$forceRefresh; var _props$getUserConfirm = props.getUserConfirmation; var getUserConfirmation = _props$getUserConfirm === undefined ? _DOMUtils.getConfirmation : _props$getUserConfirm; var _props$keyLength = props.keyLength; var keyLength = _props$keyLength === undefined ? 6 : _props$keyLength; var getDOMLocation = function getDOMLocation(historyState) { var _ref = historyState || {}; var key = _ref.key; var state = _ref.state; var _window$location = window.location; var pathname = _window$location.pathname; var search = _window$location.search; var hash = _window$location.hash; var path = pathname + search + hash; if (basename) path = (0, _PathUtils.stripPrefix)(path, basename); return _extends({}, (0, _PathUtils.parsePath)(path), { state: state, key: key }); }; var createKey = function createKey() { return Math.random().toString(36).substr(2, keyLength); }; var transitionManager = (0, _createTransitionManager2.default)(); var setState = function setState(nextState) { _extends(history, nextState); history.length = globalHistory.length; transitionManager.notifyListeners(history.location, history.action); }; var handlePopState = function handlePopState(event) { if (event.state === undefined) return; // Ignore extraneous popstate events in WebKit. handlePop(getDOMLocation(event.state)); }; var handleHashChange = function handleHashChange() { handlePop(getDOMLocation(getHistoryState())); }; var forceNextPop = false; var handlePop = function handlePop(location) { if (forceNextPop) { forceNextPop = false; setState(); } else { (function () { var action = 'POP'; transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) { if (ok) { setState({ action: action, location: location }); } else { revertPop(location); } }); })(); } }; var revertPop = 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 var push = function push(path, state) { process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(!((typeof path === 'undefined' ? 'undefined' : _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') : void 0; var action = 'PUSH'; var location = (0, _LocationUtils.createLocation)(path, state, createKey()); transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) { if (!ok) return; var url = basename + (0, _PathUtils.createPath)(location); var key = location.key; var state = location.state; if (canUseHistory) { globalHistory.pushState({ key: key, state: state }, null, url); if (forceRefresh) { window.location.href = url; } else { var prevIndex = allKeys.indexOf(history.location.key); var nextKeys = allKeys.slice(0, prevIndex === -1 ? 0 : prevIndex + 1); nextKeys.push(location.key); allKeys = nextKeys; setState({ action: action, location: location }); } } else { process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(state === undefined, 'Browser history cannot push state in browsers that do not support HTML5 history') : void 0; window.location.href = url; } }); }; var replace = function replace(path, state) { process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(!((typeof path === 'undefined' ? 'undefined' : _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') : void 0; var action = 'REPLACE'; var location = (0, _LocationUtils.createLocation)(path, state, createKey()); transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) { if (!ok) return; var url = basename + (0, _PathUtils.createPath)(location); var key = location.key; var state = location.state; if (canUseHistory) { globalHistory.replaceState({ key: key, state: state }, null, url); if (forceRefresh) { window.location.replace(url); } else { var prevIndex = allKeys.indexOf(history.location.key); if (prevIndex !== -1) allKeys[prevIndex] = location.key; setState({ action: action, location: location }); } } else { process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(state === undefined, 'Browser history cannot replace state in browsers that do not support HTML5 history') : void 0; window.location.replace(url); } }); }; var go = function go(n) { globalHistory.go(n); }; var goBack = function goBack() { return go(-1); }; var goForward = function goForward() { return go(1); }; var listenerCount = 0; var checkDOMListeners = function checkDOMListeners(delta) { listenerCount += delta; if (listenerCount === 1) { (0, _DOMUtils.addEventListener)(window, PopStateEvent, handlePopState); if (needsHashChangeListener) (0, _DOMUtils.addEventListener)(window, HashChangeEvent, handleHashChange); } else if (listenerCount === 0) { (0, _DOMUtils.removeEventListener)(window, PopStateEvent, handlePopState); if (needsHashChangeListener) (0, _DOMUtils.removeEventListener)(window, HashChangeEvent, handleHashChange); } }; var isBlocked = false; var block = function block() { var prompt = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; var unblock = transitionManager.setPrompt(prompt); if (!isBlocked) { checkDOMListeners(1); isBlocked = true; } return function () { if (isBlocked) { isBlocked = false; checkDOMListeners(-1); } return unblock(); }; }; var listen = function listen(listener) { var unlisten = transitionManager.appendListener(listener); checkDOMListeners(1); return function () { checkDOMListeners(-1); return unlisten(); }; }; var history = { length: globalHistory.length, action: 'POP', location: initialLocation, push: push, replace: replace, go: go, goBack: goBack, goForward: goForward, block: block, listen: listen }; return history; }; exports.default = createBrowserHistory;