UNPKG

plotly-icons

Version:
793 lines (616 loc) 24.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _extends2 = require('babel-runtime/helpers/extends'); var _extends3 = _interopRequireDefault(_extends2); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); var _set = require('babel-runtime/core-js/set'); var _set2 = _interopRequireDefault(_set); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _url2 = require('url'); var _EventEmitter = require('../EventEmitter'); var _EventEmitter2 = _interopRequireDefault(_EventEmitter); var _shallowEquals = require('../shallow-equals'); var _shallowEquals2 = _interopRequireDefault(_shallowEquals); var _pQueue = require('../p-queue'); var _pQueue2 = _interopRequireDefault(_pQueue); var _utils = require('../utils'); var _ = require('./'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* global __NEXT_DATA__ */ var historyUnavailableWarning = (0, _utils.execOnce)(function () { (0, _utils.warn)('Warning: window.history is not available.'); }); var historyMethodWarning = (0, _utils.execOnce)(function (method) { (0, _utils.warn)('Warning: window.history.' + method + ' is not available'); }); var Router = function () { function Router(pathname, query, as) { var _ref = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}, pageLoader = _ref.pageLoader, Component = _ref.Component, ErrorComponent = _ref.ErrorComponent, err = _ref.err; (0, _classCallCheck3.default)(this, Router); // represents the current component key this.route = toRoute(pathname); // set up the component cache (by route keys) this.components = {}; // We should not keep the cache, if there's an error // Otherwise, this cause issues when when going back and // come again to the errored page. if (Component !== ErrorComponent) { this.components[this.route] = { Component: Component, err: err }; } // Handling Router Events this.events = new _EventEmitter2.default(); this.pageLoader = pageLoader; this.prefetchQueue = new _pQueue2.default({ concurrency: 2 }); this.ErrorComponent = ErrorComponent; this.pathname = pathname; this.query = query; this.asPath = as; this.subscriptions = new _set2.default(); this.componentLoadCancel = null; this.onPopState = this.onPopState.bind(this); if (typeof window !== 'undefined') { // in order for `e.state` to work on the `onpopstate` event // we have to register the initial route upon initialization this.changeState('replaceState', (0, _url2.format)({ pathname: pathname, query: query }), (0, _utils.getURL)()); window.addEventListener('popstate', this.onPopState); } } (0, _createClass3.default)(Router, [{ key: 'onPopState', value: function () { var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(e) { var pathname, query, _e$state, url, as, options; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: if (e.state) { _context.next = 4; break; } // We get state as undefined for two reasons. // 1. With older safari (< 8) and older chrome (< 34) // 2. When the URL changed with # // // In the both cases, we don't need to proceed and change the route. // (as it's already changed) // But we can simply replace the state with the new changes. // Actually, for (1) we don't need to nothing. But it's hard to detect that event. // So, doing the following for (1) does no harm. pathname = this.pathname, query = this.query; this.changeState('replaceState', (0, _url2.format)({ pathname: pathname, query: query }), (0, _utils.getURL)()); return _context.abrupt('return'); case 4: _e$state = e.state, url = _e$state.url, as = _e$state.as, options = _e$state.options; this.replace(url, as, options); case 6: case 'end': return _context.stop(); } } }, _callee, this); })); function onPopState(_x2) { return _ref2.apply(this, arguments); } return onPopState; }() }, { key: 'update', value: function update(route, Component) { var data = this.components[route]; if (!data) { throw new Error('Cannot update unavailable route: ' + route); } var newData = (0, _extends3.default)({}, data, { Component: Component }); this.components[route] = newData; if (route === this.route) { this.notify(newData); } } }, { key: 'reload', value: function () { var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(route) { var pathname, query, url, routeInfo, error; return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: delete this.components[route]; this.pageLoader.clearCache(route); if (!(route !== this.route)) { _context2.next = 4; break; } return _context2.abrupt('return'); case 4: pathname = this.pathname, query = this.query; url = window.location.href; this.events.emit('routeChangeStart', url); _context2.next = 9; return this.getRouteInfo(route, pathname, query, url); case 9: routeInfo = _context2.sent; error = routeInfo.error; if (!(error && error.cancelled)) { _context2.next = 13; break; } return _context2.abrupt('return'); case 13: this.notify(routeInfo); if (!error) { _context2.next = 17; break; } this.events.emit('routeChangeError', error, url); throw error; case 17: this.events.emit('routeChangeComplete', url); case 18: case 'end': return _context2.stop(); } } }, _callee2, this); })); function reload(_x3) { return _ref3.apply(this, arguments); } return reload; }() }, { key: 'back', value: function back() { window.history.back(); } }, { key: 'push', value: function push(url) { var as = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : url; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return this.change('pushState', url, as, options); } }, { key: 'replace', value: function replace(url) { var as = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : url; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return this.change('replaceState', url, as, options); } }, { key: 'change', value: function () { var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(method, _url, _as, options) { var url, as, _parse, pathname, query, route, _options$shallow, shallow, routeInfo, _routeInfo, error, hash; return _regenerator2.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: // If url and as provided as an object representation, // we'll format them into the string version here. url = (typeof _url === 'undefined' ? 'undefined' : (0, _typeof3.default)(_url)) === 'object' ? (0, _url2.format)(_url) : _url; as = (typeof _as === 'undefined' ? 'undefined' : (0, _typeof3.default)(_as)) === 'object' ? (0, _url2.format)(_as) : _as; // Add the ending slash to the paths. So, we can serve the // "<page>/index.html" directly for the SSR page. if (__NEXT_DATA__.nextExport) { as = (0, _._rewriteUrlForNextExport)(as); } this.abortComponentLoad(as); // If the url change is only related to a hash change // We should not proceed. We should only change the state. if (!this.onlyAHashChange(as)) { _context3.next = 8; break; } this.changeState(method, url, as); this.scrollToHash(as); return _context3.abrupt('return', true); case 8: _parse = (0, _url2.parse)(url, true), pathname = _parse.pathname, query = _parse.query; // If asked to change the current URL we should reload the current page // (not location.reload() but reload getInitalProps and other Next.js stuffs) // We also need to set the method = replaceState always // as this should not go into the history (That's how browsers work) if (!this.urlIsNew(pathname, query)) { method = 'replaceState'; } route = toRoute(pathname); _options$shallow = options.shallow, shallow = _options$shallow === undefined ? false : _options$shallow; routeInfo = null; this.events.emit('routeChangeStart', as); // If shallow === false and other conditions met, we reuse the // existing routeInfo for this route. // Because of this, getInitialProps would not run. if (!(shallow && this.isShallowRoutingPossible(route))) { _context3.next = 18; break; } routeInfo = this.components[route]; _context3.next = 21; break; case 18: _context3.next = 20; return this.getRouteInfo(route, pathname, query, as); case 20: routeInfo = _context3.sent; case 21: _routeInfo = routeInfo, error = _routeInfo.error; if (!(error && error.cancelled)) { _context3.next = 24; break; } return _context3.abrupt('return', false); case 24: this.events.emit('beforeHistoryChange', as); this.changeState(method, url, as, options); hash = window.location.hash.substring(1); this.set(route, pathname, query, as, (0, _extends3.default)({}, routeInfo, { hash: hash })); if (!error) { _context3.next = 31; break; } this.events.emit('routeChangeError', error, as); throw error; case 31: this.events.emit('routeChangeComplete', as); return _context3.abrupt('return', true); case 33: case 'end': return _context3.stop(); } } }, _callee3, this); })); function change(_x8, _x9, _x10, _x11) { return _ref4.apply(this, arguments); } return change; }() }, { key: 'changeState', value: function changeState(method, url, as) { var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; if (typeof window.history === 'undefined') { historyUnavailableWarning(); return; } if (typeof window.history[method] === 'undefined') { historyMethodWarning(method); return; } if (method !== 'pushState' || (0, _utils.getURL)() !== as) { window.history[method]({ url: url, as: as, options: options }, null, as); } } }, { key: 'getRouteInfo', value: function () { var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(route, pathname, query, as) { var routeInfo, _routeInfo2, Component, ctx, _Component, _ctx; return _regenerator2.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: routeInfo = null; _context4.prev = 1; routeInfo = this.components[route]; if (routeInfo) { _context4.next = 8; break; } _context4.next = 6; return this.fetchComponent(route, as); case 6: _context4.t0 = _context4.sent; routeInfo = { Component: _context4.t0 }; case 8: _routeInfo2 = routeInfo, Component = _routeInfo2.Component; if (!(typeof Component !== 'function')) { _context4.next = 11; break; } throw new Error('The default export is not a React Component in page: "' + pathname + '"'); case 11: ctx = { pathname: pathname, query: query, asPath: as }; _context4.next = 14; return this.getInitialProps(Component, ctx); case 14: routeInfo.props = _context4.sent; this.components[route] = routeInfo; _context4.next = 33; break; case 18: _context4.prev = 18; _context4.t1 = _context4['catch'](1); if (!(_context4.t1.code === 'PAGE_LOAD_ERROR')) { _context4.next = 24; break; } // If we can't load the page it could be one of following reasons // 1. Page doesn't exists // 2. Page does exist in a different zone // 3. Internal error while loading the page // So, doing a hard reload is the proper way to deal with this. window.location.href = as; // Changing the URL doesn't block executing the current code path. // So, we need to mark it as a cancelled error and stop the routing logic. _context4.t1.cancelled = true; return _context4.abrupt('return', { error: _context4.t1 }); case 24: if (!_context4.t1.cancelled) { _context4.next = 26; break; } return _context4.abrupt('return', { error: _context4.t1 }); case 26: _Component = this.ErrorComponent; routeInfo = { Component: _Component, err: _context4.t1 }; _ctx = { err: _context4.t1, pathname: pathname, query: query }; _context4.next = 31; return this.getInitialProps(_Component, _ctx); case 31: routeInfo.props = _context4.sent; routeInfo.error = _context4.t1; case 33: return _context4.abrupt('return', routeInfo); case 34: case 'end': return _context4.stop(); } } }, _callee4, this, [[1, 18]]); })); function getRouteInfo(_x13, _x14, _x15, _x16) { return _ref5.apply(this, arguments); } return getRouteInfo; }() }, { key: 'set', value: function set(route, pathname, query, as, data) { this.route = route; this.pathname = pathname; this.query = query; this.asPath = as; this.notify(data); } }, { key: 'onlyAHashChange', value: function onlyAHashChange(as) { if (!this.asPath) return false; var _asPath$split = this.asPath.split('#'), _asPath$split2 = (0, _slicedToArray3.default)(_asPath$split, 2), oldUrlNoHash = _asPath$split2[0], oldHash = _asPath$split2[1]; var _as$split = as.split('#'), _as$split2 = (0, _slicedToArray3.default)(_as$split, 2), newUrlNoHash = _as$split2[0], newHash = _as$split2[1]; // If the urls are change, there's more than a hash change if (oldUrlNoHash !== newUrlNoHash) { return false; } // If the hash has changed, then it's a hash only change. // This check is necessary to handle both the enter and // leave hash === '' cases. The identity case falls through // and is treated as a next reload. return oldHash !== newHash; } }, { key: 'scrollToHash', value: function scrollToHash(as) { var _as$split3 = as.split('#'), _as$split4 = (0, _slicedToArray3.default)(_as$split3, 2), hash = _as$split4[1]; var el = document.getElementById(hash); if (el) { el.scrollIntoView(); } } }, { key: 'urlIsNew', value: function urlIsNew(pathname, query) { return this.pathname !== pathname || !(0, _shallowEquals2.default)(query, this.query); } }, { key: 'isShallowRoutingPossible', value: function isShallowRoutingPossible(route) { return ( // If there's cached routeInfo for the route. Boolean(this.components[route]) && // If the route is already rendered on the screen. this.route === route ); } }, { key: 'prefetch', value: function () { var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(url) { var _this = this; var _parse2, pathname, route; return _regenerator2.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: if (!(process.env.NODE_ENV === 'development')) { _context5.next = 2; break; } return _context5.abrupt('return'); case 2: _parse2 = (0, _url2.parse)(url), pathname = _parse2.pathname; route = toRoute(pathname); return _context5.abrupt('return', this.prefetchQueue.add(function () { return _this.fetchRoute(route); })); case 5: case 'end': return _context5.stop(); } } }, _callee5, this); })); function prefetch(_x17) { return _ref6.apply(this, arguments); } return prefetch; }() }, { key: 'fetchComponent', value: function () { var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(route, as) { var cancelled, cancel, Component, error; return _regenerator2.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: cancelled = false; cancel = this.componentLoadCancel = function () { cancelled = true; }; _context6.next = 4; return this.fetchRoute(route); case 4: Component = _context6.sent; if (!cancelled) { _context6.next = 9; break; } error = new Error('Abort fetching component for route: "' + route + '"'); error.cancelled = true; throw error; case 9: if (cancel === this.componentLoadCancel) { this.componentLoadCancel = null; } return _context6.abrupt('return', Component); case 11: case 'end': return _context6.stop(); } } }, _callee6, this); })); function fetchComponent(_x18, _x19) { return _ref7.apply(this, arguments); } return fetchComponent; }() }, { key: 'getInitialProps', value: function () { var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7(Component, ctx) { var cancelled, cancel, props, err; return _regenerator2.default.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: cancelled = false; cancel = function cancel() { cancelled = true; }; this.componentLoadCancel = cancel; _context7.next = 5; return (0, _utils.loadGetInitialProps)(Component, ctx); case 5: props = _context7.sent; if (cancel === this.componentLoadCancel) { this.componentLoadCancel = null; } if (!cancelled) { _context7.next = 11; break; } err = new Error('Loading initial props cancelled'); err.cancelled = true; throw err; case 11: return _context7.abrupt('return', props); case 12: case 'end': return _context7.stop(); } } }, _callee7, this); })); function getInitialProps(_x20, _x21) { return _ref8.apply(this, arguments); } return getInitialProps; }() }, { key: 'fetchRoute', value: function () { var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8(route) { return _regenerator2.default.wrap(function _callee8$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: _context8.next = 2; return this.pageLoader.loadPage(route); case 2: return _context8.abrupt('return', _context8.sent); case 3: case 'end': return _context8.stop(); } } }, _callee8, this); })); function fetchRoute(_x22) { return _ref9.apply(this, arguments); } return fetchRoute; }() }, { key: 'abortComponentLoad', value: function abortComponentLoad(as) { if (this.componentLoadCancel) { this.events.emit('routeChangeError', new Error('Route Cancelled'), as); this.componentLoadCancel(); this.componentLoadCancel = null; } } }, { key: 'notify', value: function notify(data) { this.subscriptions.forEach(function (fn) { return fn(data); }); } }, { key: 'subscribe', value: function subscribe(fn) { var _this2 = this; this.subscriptions.add(fn); return function () { return _this2.subscriptions.delete(fn); }; } }]); return Router; }(); exports.default = Router; function toRoute(path) { return path.replace(/\/$/, '') || '/'; }