UNPKG

router5-plugin-browser

Version:
271 lines (262 loc) 11.4 kB
import { errorCodes, constants } from 'router5'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } var value = function (arg) { return function () { return arg; }; }; var noop = function () { }; var isBrowser = typeof window !== 'undefined' && window.history; var getBase = function () { return window.location.pathname; }; var supportsPopStateOnHashChange = function () { return window.navigator.userAgent.indexOf('Trident') === -1; }; var pushState = function (state, title, path) { return window.history.pushState(state, title, path); }; var replaceState = function (state, title, path) { return window.history.replaceState(state, title, path); }; var addPopstateListener = function (fn, opts) { var shouldAddHashChangeListener = opts.useHash && !supportsPopStateOnHashChange(); window.addEventListener('popstate', fn); if (shouldAddHashChangeListener) { window.addEventListener('hashchange', fn); } return function () { window.removeEventListener('popstate', fn); if (shouldAddHashChangeListener) { window.removeEventListener('hashchange', fn); } }; }; var getLocation = function (opts) { var path = opts.useHash ? window.location.hash.replace(new RegExp('^#' + opts.hashPrefix), '') : window.location.pathname.replace(new RegExp('^' + opts.base), ''); // Fix issue with browsers that don't URL encode characters (Edge) var correctedPath = safelyEncodePath(path); return (correctedPath || '/') + window.location.search; }; var safelyEncodePath = function (path) { try { return encodeURI(decodeURI(path)); } catch (_) { return path; } }; var getState = function () { return window.history.state; }; var getHash = function () { return window.location.hash; }; var browser = {}; if (isBrowser) { browser = { getBase: getBase, pushState: pushState, replaceState: replaceState, addPopstateListener: addPopstateListener, getLocation: getLocation, getState: getState, getHash: getHash }; } else { browser = { getBase: value(''), pushState: noop, replaceState: noop, addPopstateListener: noop, getLocation: value(''), getState: value(null), getHash: value('') }; } var safeBrowser = browser; var defaultOptions = { forceDeactivate: true, useHash: false, hashPrefix: '', base: '', mergeState: false, preserveHash: true }; var source = 'popstate'; function browserPluginFactory(opts, browser) { if (browser === void 0) { browser = safeBrowser; } var options = __assign(__assign({}, defaultOptions), opts); var transitionOptions = { forceDeactivate: options.forceDeactivate, source: source }; var removePopStateListener; return function browserPlugin(router) { var routerOptions = router.getOptions(); var routerStart = router.start; router.buildUrl = function (route, params) { var base = options.base || ''; var prefix = options.useHash ? "#" + options.hashPrefix : ''; var path = router.buildPath(route, params); return base + prefix + path; }; var urlToPath = function (url) { var match = url.match(/^(?:http|https):\/\/(?:[0-9a-z_\-.:]+?)(?=\/)(.*)$/); var path = match ? match[1] : url; var pathParts = path.match(/^(.+?)(#.+?)?(\?.+)?$/); if (!pathParts) throw new Error("[router5] Could not parse url " + url); var pathname = pathParts[1]; var hash = pathParts[2] || ''; var search = pathParts[3] || ''; return ((options.useHash ? hash.replace(new RegExp('^#' + options.hashPrefix), '') : options.base ? pathname.replace(new RegExp('^' + options.base), '') : pathname) + search); }; router.matchUrl = function (url) { return router.matchPath(urlToPath(url)); }; router.start = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 0 || typeof args[0] === 'function') { routerStart.apply(void 0, __spreadArrays([browser.getLocation(options)], args)); } else { routerStart.apply(void 0, args); } return router; }; router.replaceHistoryState = function (name, params, title) { if (params === void 0) { params = {}; } if (title === void 0) { title = ''; } var route = router.buildState(name, params); var state = router.makeState(route.name, route.params, router.buildPath(route.name, route.params), { params: route.meta }); var url = router.buildUrl(name, params); router.lastKnownState = state; browser.replaceState(state, title, url); }; function updateBrowserState(state, url, replace) { var trimmedState = state ? { meta: state.meta, name: state.name, params: state.params, path: state.path } : state; var finalState = options.mergeState === true ? __assign(__assign({}, browser.getState()), trimmedState) : trimmedState; if (replace) browser.replaceState(finalState, '', url); else browser.pushState(finalState, '', url); } function onPopState(evt) { var routerState = router.getState(); // Do nothing if no state or if last know state is poped state (it should never happen) var newState = !evt.state || !evt.state.name; var state = newState ? router.matchPath(browser.getLocation(options), source) : router.makeState(evt.state.name, evt.state.params, evt.state.path, __assign(__assign({}, evt.state.meta), { source: source }), evt.state.meta.id); var defaultRoute = routerOptions.defaultRoute, defaultParams = routerOptions.defaultParams; if (!state) { // If current state is already the default route, we will have a double entry // Navigating back and forth will emit SAME_STATES error defaultRoute && router.navigateToDefault(__assign(__assign({}, transitionOptions), { reload: true, replace: true })); return; } if (routerState && router.areStatesEqual(state, routerState, false)) { return; } router.transitionToState(state, routerState, transitionOptions, function (err, toState) { if (err) { if (err.redirect) { var _a = err.redirect, name_1 = _a.name, params = _a.params; router.navigate(name_1, params, __assign(__assign({}, transitionOptions), { replace: true, force: true, redirected: true })); } else if (err.code === errorCodes.CANNOT_DEACTIVATE) { var url = router.buildUrl(routerState.name, routerState.params); if (!newState) { // Keep history state unchanged but use current URL updateBrowserState(state, url, true); } // else do nothing or history will be messed up // TODO: history.back()? } else { // Force navigation to default state defaultRoute && router.navigate(defaultRoute, defaultParams, __assign(__assign({}, transitionOptions), { reload: true, replace: true })); } } else { router.invokeEventListeners(constants.TRANSITION_SUCCESS, toState, routerState, { replace: true }); } }); } function onStart() { if (options.useHash && !options.base) { // Guess base options.base = browser.getBase(); } removePopStateListener = browser.addPopstateListener(onPopState, options); } function teardown() { if (removePopStateListener) { removePopStateListener(); removePopStateListener = undefined; } } function onTransitionSuccess(toState, fromState, opts) { var historyState = browser.getState(); var hasState = historyState && historyState.meta && historyState.name && historyState.params; var statesAreEqual = fromState && router.areStatesEqual(fromState, toState, false); var replace = opts.replace || !hasState || statesAreEqual; var url = router.buildUrl(toState.name, toState.params); if (fromState === null && options.useHash === false && options.preserveHash === true) { url += browser.getHash(); } updateBrowserState(toState, url, replace); } return { onStart: onStart, onStop: teardown, teardown: teardown, onTransitionSuccess: onTransitionSuccess, onPopState: onPopState }; }; } export default browserPluginFactory;