UNPKG

@uirouter/core

Version:

UI-Router Core: Framework agnostic, State-based routing for JavaScript Single Page Apps

343 lines 13.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UrlRules = void 0; var state_1 = require("../state"); var urlMatcher_1 = require("./urlMatcher"); var common_1 = require("../common"); var urlRule_1 = require("./urlRule"); var prioritySort = function (a, b) { return (b.priority || 0) - (a.priority || 0); }; var typeSort = function (a, b) { var weights = { STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1 }; return (weights[a.type] || 0) - (weights[b.type] || 0); }; var urlMatcherSort = function (a, b) { return !a.urlMatcher || !b.urlMatcher ? 0 : urlMatcher_1.UrlMatcher.compare(a.urlMatcher, b.urlMatcher); }; var idSort = function (a, b) { // Identically sorted STATE and URLMATCHER best rule will be chosen by `matchPriority` after each rule matches the URL var useMatchPriority = { STATE: true, URLMATCHER: true }; var equal = useMatchPriority[a.type] && useMatchPriority[b.type]; return equal ? 0 : (a.$id || 0) - (b.$id || 0); }; /** * Default rule priority sorting function. * * Sorts rules by: * * - Explicit priority (set rule priority using [[UrlRules.when]]) * - Rule type (STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1) * - `UrlMatcher` specificity ([[UrlMatcher.compare]]): works for STATE and URLMATCHER types to pick the most specific rule. * - Rule registration order (for rule types other than STATE and URLMATCHER) * - Equally sorted State and UrlMatcher rules will each match the URL. * Then, the *best* match is chosen based on how many parameter values were matched. */ var defaultRuleSortFn; defaultRuleSortFn = function (a, b) { var cmp = prioritySort(a, b); if (cmp !== 0) return cmp; cmp = typeSort(a, b); if (cmp !== 0) return cmp; cmp = urlMatcherSort(a, b); if (cmp !== 0) return cmp; return idSort(a, b); }; function getHandlerFn(handler) { if (!common_1.isFunction(handler) && !common_1.isString(handler) && !common_1.is(state_1.TargetState)(handler) && !state_1.TargetState.isDef(handler)) { throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property"); } return common_1.isFunction(handler) ? handler : common_1.val(handler); } /** * API for managing URL rules * * This API is used to create and manage URL rules. * URL rules are a mechanism to respond to specific URL patterns. * * The most commonly used methods are [[otherwise]] and [[when]]. * * This API is found at `router.urlService.rules` (see: [[UIRouter.urlService]], [[URLService.rules]]) */ var UrlRules = /** @class */ (function () { /** @internal */ function UrlRules(/** @internal */ router) { this.router = router; /** @internal */ this._sortFn = defaultRuleSortFn; /** @internal */ this._rules = []; /** @internal */ this._id = 0; this.urlRuleFactory = new urlRule_1.UrlRuleFactory(router); } /** @internal */ UrlRules.prototype.dispose = function (router) { this._rules = []; delete this._otherwiseFn; }; /** * Defines the initial state, path, or behavior to use when the app starts. * * This rule defines the initial/starting state for the application. * * This rule is triggered the first time the URL is checked (when the app initially loads). * The rule is triggered only when the url matches either `""` or `"/"`. * * Note: The rule is intended to be used when the root of the application is directly linked to. * When the URL is *not* `""` or `"/"` and doesn't match other rules, the [[otherwise]] rule is triggered. * This allows 404-like behavior when an unknown URL is deep-linked. * * #### Example: * Start app at `home` state. * ```js * .initial({ state: 'home' }); * ``` * * #### Example: * Start app at `/home` (by url) * ```js * .initial('/home'); * ``` * * #### Example: * When no other url rule matches, go to `home` state * ```js * .initial((matchValue, url, router) => { * console.log('initial state'); * return { state: 'home' }; * }) * ``` * * @param handler The initial state or url path, or a function which returns the state or url path (or performs custom logic). */ UrlRules.prototype.initial = function (handler) { var handlerFn = getHandlerFn(handler); var matchFn = function (urlParts, router) { return router.globals.transitionHistory.size() === 0 && !!/^\/?$/.exec(urlParts.path); }; this.rule(this.urlRuleFactory.create(matchFn, handlerFn)); }; /** * Defines the state, url, or behavior to use when no other rule matches the URL. * * This rule is matched when *no other rule* matches. * It is generally used to handle unknown URLs (similar to "404" behavior, but on the client side). * * - If `handler` a string, it is treated as a url redirect * * #### Example: * When no other url rule matches, redirect to `/index` * ```js * .otherwise('/index'); * ``` * * - If `handler` is an object with a `state` property, the state is activated. * * #### Example: * When no other url rule matches, redirect to `home` and provide a `dashboard` parameter value. * ```js * .otherwise({ state: 'home', params: { dashboard: 'default' } }); * ``` * * - If `handler` is a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object. * The function can perform actions, and/or return a value. * * #### Example: * When no other url rule matches, manually trigger a transition to the `home` state * ```js * .otherwise((matchValue, urlParts, router) => { * router.stateService.go('home'); * }); * ``` * * #### Example: * When no other url rule matches, go to `home` state * ```js * .otherwise((matchValue, urlParts, router) => { * return { state: 'home' }; * }); * ``` * * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic). */ UrlRules.prototype.otherwise = function (handler) { var handlerFn = getHandlerFn(handler); this._otherwiseFn = this.urlRuleFactory.create(common_1.val(true), handlerFn); this._sorted = false; }; /** * Remove a rule previously registered * * @param rule the matcher rule that was previously registered using [[rule]] */ UrlRules.prototype.removeRule = function (rule) { common_1.removeFrom(this._rules, rule); }; /** * Manually adds a URL Rule. * * Usually, a url rule is added using [[StateDeclaration.url]] or [[when]]. * This api can be used directly for more control (to register a [[BaseUrlRule]], for example). * Rules can be created using [[urlRuleFactory]], or created manually as simple objects. * * A rule should have a `match` function which returns truthy if the rule matched. * It should also have a `handler` function which is invoked if the rule is the best match. * * @return a function that deregisters the rule */ UrlRules.prototype.rule = function (rule) { var _this = this; if (!urlRule_1.UrlRuleFactory.isUrlRule(rule)) throw new Error('invalid rule'); rule.$id = this._id++; rule.priority = rule.priority || 0; this._rules.push(rule); this._sorted = false; return function () { return _this.removeRule(rule); }; }; /** * Gets all registered rules * * @returns an array of all the registered rules */ UrlRules.prototype.rules = function () { this.ensureSorted(); return this._rules.concat(this._otherwiseFn ? [this._otherwiseFn] : []); }; /** * Defines URL Rule priorities * * More than one rule ([[UrlRule]]) might match a given URL. * This `compareFn` is used to sort the rules by priority. * Higher priority rules should sort earlier. * * The [[defaultRuleSortFn]] is used by default. * * You only need to call this function once. * The `compareFn` will be used to sort the rules as each is registered. * * If called without any parameter, it will re-sort the rules. * * --- * * Url rules may come from multiple sources: states's urls ([[StateDeclaration.url]]), [[when]], and [[rule]]. * Each rule has a (user-provided) [[UrlRule.priority]], a [[UrlRule.type]], and a [[UrlRule.$id]] * The `$id` is is the order in which the rule was registered. * * The sort function should use these data, or data found on a specific type * of [[UrlRule]] (such as [[StateRule.state]]), to order the rules as desired. * * #### Example: * This compare function prioritizes rules by the order in which the rules were registered. * A rule registered earlier has higher priority. * * ```js * function compareFn(a, b) { * return a.$id - b.$id; * } * ``` * * @param compareFn a function that compares to [[UrlRule]] objects. * The `compareFn` should abide by the `Array.sort` compare function rules. * Given two rules, `a` and `b`, return a negative number if `a` should be higher priority. * Return a positive number if `b` should be higher priority. * Return `0` if the rules are identical. * * See the [mozilla reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description) * for details. */ UrlRules.prototype.sort = function (compareFn) { var sorted = this.stableSort(this._rules, (this._sortFn = compareFn || this._sortFn)); // precompute _sortGroup values and apply to each rule var group = 0; for (var i = 0; i < sorted.length; i++) { sorted[i]._group = group; if (i < sorted.length - 1 && this._sortFn(sorted[i], sorted[i + 1]) !== 0) { group++; } } this._rules = sorted; this._sorted = true; }; /** @internal */ UrlRules.prototype.ensureSorted = function () { this._sorted || this.sort(); }; /** @internal */ UrlRules.prototype.stableSort = function (arr, compareFn) { var arrOfWrapper = arr.map(function (elem, idx) { return ({ elem: elem, idx: idx }); }); arrOfWrapper.sort(function (wrapperA, wrapperB) { var cmpDiff = compareFn(wrapperA.elem, wrapperB.elem); return cmpDiff === 0 ? wrapperA.idx - wrapperB.idx : cmpDiff; }); return arrOfWrapper.map(function (wrapper) { return wrapper.elem; }); }; /** * Registers a `matcher` and `handler` for custom URLs handling. * * The `matcher` can be: * * - a [[UrlMatcher]]: See: [[UrlMatcherFactory.compile]] * - a `string`: The string is compiled to a [[UrlMatcher]] * - a `RegExp`: The regexp is used to match the url. * * The `handler` can be: * * - a string: The url is redirected to the value of the string. * - a function: The url is redirected to the return value of the function. * * --- * * When the `handler` is a `string` and the `matcher` is a `UrlMatcher` (or string), the redirect * string is interpolated with parameter values. * * #### Example: * When the URL is `/foo/123` the rule will redirect to `/bar/123`. * ```js * .when("/foo/:param1", "/bar/:param1") * ``` * * --- * * When the `handler` is a string and the `matcher` is a `RegExp`, the redirect string is * interpolated with capture groups from the RegExp. * * #### Example: * When the URL is `/foo/123` the rule will redirect to `/bar/123`. * ```js * .when(new RegExp("^/foo/(.*)$"), "/bar/$1"); * ``` * * --- * * When the handler is a function, it receives the matched value, the current URL, and the `UIRouter` object (See [[UrlRuleHandlerFn]]). * The "matched value" differs based on the `matcher`. * For [[UrlMatcher]]s, it will be the matched state params. * For `RegExp`, it will be the match array from `regexp.exec()`. * * If the handler returns a string, the URL is redirected to the string. * * #### Example: * When the URL is `/foo/123` the rule will redirect to `/bar/123`. * ```js * .when(new RegExp("^/foo/(.*)$"), match => "/bar/" + match[1]); * ``` * * Note: the `handler` may also invoke arbitrary code, such as `$state.go()` * * @param matcher A pattern `string` to match, compiled as a [[UrlMatcher]], or a `RegExp`. * @param handler The path to redirect to, or a function that returns the path. * @param options `{ priority: number }` * * @return the registered [[UrlRule]] */ UrlRules.prototype.when = function (matcher, handler, options) { var rule = this.urlRuleFactory.create(matcher, handler); if (common_1.isDefined(options && options.priority)) rule.priority = options.priority; this.rule(rule); return rule; }; return UrlRules; }()); exports.UrlRules = UrlRules; //# sourceMappingURL=urlRules.js.map