@jupyterlab/application
Version:
JupyterLab - Application
144 lines • 5.08 kB
JavaScript
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { URLExt } from '@jupyterlab/coreutils';
import { PromiseDelegate, Token } from '@lumino/coreutils';
import { DisposableDelegate } from '@lumino/disposable';
import { Signal } from '@lumino/signaling';
/**
* A static class that routes URLs within the application.
*/
export class Router {
/**
* Create a URL router.
*/
constructor(options) {
/**
* If a matching rule's command resolves with the `stop` token during routing,
* no further matches will execute.
*/
this.stop = new Token('@jupyterlab/application:Router#stop');
this._routed = new Signal(this);
this._rules = new Map();
this.base = options.base;
this.commands = options.commands;
}
/**
* Returns the parsed current URL of the application.
*/
get current() {
var _a, _b;
const { base } = this;
const parsed = URLExt.parse(window.location.href);
const { search, hash } = parsed;
const path = (_b = (_a = parsed.pathname) === null || _a === void 0 ? void 0 : _a.replace(base, '/')) !== null && _b !== void 0 ? _b : '';
const request = path + search + hash;
return { hash, path, request, search };
}
/**
* A signal emitted when the router routes a route.
*/
get routed() {
return this._routed;
}
/**
* Navigate to a new path within the application.
*
* @param path - The new path or empty string if redirecting to root.
*
* @param options - The navigation options.
*/
navigate(path, options = {}) {
const { base } = this;
const { history } = window;
const { hard } = options;
const old = document.location.href;
const url = path && path.indexOf(base) === 0 ? path : URLExt.join(base, path);
if (url === old) {
return hard ? this.reload() : undefined;
}
history.pushState({}, '', url);
if (hard) {
return this.reload();
}
if (!options.skipRouting) {
// Because a `route()` call may still be in the stack after having received
// a `stop` token, wait for the next stack frame before calling `route()`.
requestAnimationFrame(() => {
void this.route();
});
}
}
/**
* Register to route a path pattern to a command.
*
* @param options - The route registration options.
*
* @returns A disposable that removes the registered rule from the router.
*/
register(options) {
var _a;
const { command, pattern } = options;
const rank = (_a = options.rank) !== null && _a !== void 0 ? _a : 100;
const rules = this._rules;
rules.set(pattern, { command, rank });
return new DisposableDelegate(() => {
rules.delete(pattern);
});
}
/**
* Cause a hard reload of the document.
*/
reload() {
window.location.reload();
}
/**
* Route a specific path to an action.
*
* #### Notes
* If a pattern is matched, its command will be invoked with arguments that
* match the `IRouter.ILocation` interface.
*/
route() {
const { commands, current, stop } = this;
const { request } = current;
const routed = this._routed;
const rules = this._rules;
const matches = [];
// Collect all rules that match the URL.
rules.forEach((rule, pattern) => {
if (request === null || request === void 0 ? void 0 : request.match(pattern)) {
matches.push(rule);
}
});
// Order the matching rules by rank and enqueue them.
const queue = matches.sort((a, b) => b.rank - a.rank);
const done = new PromiseDelegate();
// Process each enqueued command sequentially and short-circuit if a promise
// resolves with the `stop` token.
const next = async () => {
if (!queue.length) {
routed.emit(current);
done.resolve(undefined);
return;
}
const { command } = queue.pop();
try {
const request = this.current.request;
const result = await commands.execute(command, current);
if (result === stop) {
queue.length = 0;
console.debug(`Routing ${request} was short-circuited by ${command}`);
}
}
catch (reason) {
console.warn(`Routing ${request} to ${command} failed`, reason);
}
void next();
};
void next();
return done.promise;
}
}
//# sourceMappingURL=router.js.map