UNPKG

13.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.UrlRules = void 0;
4var state_1 = require("../state");
5var urlMatcher_1 = require("./urlMatcher");
6var common_1 = require("../common");
7var urlRule_1 = require("./urlRule");
8var prioritySort = function (a, b) { return (b.priority || 0) - (a.priority || 0); };
9var typeSort = function (a, b) {
10 var weights = { STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1 };
11 return (weights[a.type] || 0) - (weights[b.type] || 0);
12};
13var urlMatcherSort = function (a, b) {
14 return !a.urlMatcher || !b.urlMatcher ? 0 : urlMatcher_1.UrlMatcher.compare(a.urlMatcher, b.urlMatcher);
15};
16var idSort = function (a, b) {
17 // Identically sorted STATE and URLMATCHER best rule will be chosen by `matchPriority` after each rule matches the URL
18 var useMatchPriority = { STATE: true, URLMATCHER: true };
19 var equal = useMatchPriority[a.type] && useMatchPriority[b.type];
20 return equal ? 0 : (a.$id || 0) - (b.$id || 0);
21};
22/**
23 * Default rule priority sorting function.
24 *
25 * Sorts rules by:
26 *
27 * - Explicit priority (set rule priority using [[UrlRules.when]])
28 * - Rule type (STATE: 4, URLMATCHER: 4, REGEXP: 3, RAW: 2, OTHER: 1)
29 * - `UrlMatcher` specificity ([[UrlMatcher.compare]]): works for STATE and URLMATCHER types to pick the most specific rule.
30 * - Rule registration order (for rule types other than STATE and URLMATCHER)
31 * - Equally sorted State and UrlMatcher rules will each match the URL.
32 * Then, the *best* match is chosen based on how many parameter values were matched.
33 */
34var defaultRuleSortFn;
35defaultRuleSortFn = function (a, b) {
36 var cmp = prioritySort(a, b);
37 if (cmp !== 0)
38 return cmp;
39 cmp = typeSort(a, b);
40 if (cmp !== 0)
41 return cmp;
42 cmp = urlMatcherSort(a, b);
43 if (cmp !== 0)
44 return cmp;
45 return idSort(a, b);
46};
47function getHandlerFn(handler) {
48 if (!common_1.isFunction(handler) && !common_1.isString(handler) && !common_1.is(state_1.TargetState)(handler) && !state_1.TargetState.isDef(handler)) {
49 throw new Error("'handler' must be a string, function, TargetState, or have a state: 'newtarget' property");
50 }
51 return common_1.isFunction(handler) ? handler : common_1.val(handler);
52}
53/**
54 * API for managing URL rules
55 *
56 * This API is used to create and manage URL rules.
57 * URL rules are a mechanism to respond to specific URL patterns.
58 *
59 * The most commonly used methods are [[otherwise]] and [[when]].
60 *
61 * This API is found at `router.urlService.rules` (see: [[UIRouter.urlService]], [[URLService.rules]])
62 */
63var UrlRules = /** @class */ (function () {
64 /** @internal */
65 function UrlRules(/** @internal */ router) {
66 this.router = router;
67 /** @internal */ this._sortFn = defaultRuleSortFn;
68 /** @internal */ this._rules = [];
69 /** @internal */ this._id = 0;
70 this.urlRuleFactory = new urlRule_1.UrlRuleFactory(router);
71 }
72 /** @internal */
73 UrlRules.prototype.dispose = function (router) {
74 this._rules = [];
75 delete this._otherwiseFn;
76 };
77 /**
78 * Defines the initial state, path, or behavior to use when the app starts.
79 *
80 * This rule defines the initial/starting state for the application.
81 *
82 * This rule is triggered the first time the URL is checked (when the app initially loads).
83 * The rule is triggered only when the url matches either `""` or `"/"`.
84 *
85 * Note: The rule is intended to be used when the root of the application is directly linked to.
86 * When the URL is *not* `""` or `"/"` and doesn't match other rules, the [[otherwise]] rule is triggered.
87 * This allows 404-like behavior when an unknown URL is deep-linked.
88 *
89 * #### Example:
90 * Start app at `home` state.
91 * ```js
92 * .initial({ state: 'home' });
93 * ```
94 *
95 * #### Example:
96 * Start app at `/home` (by url)
97 * ```js
98 * .initial('/home');
99 * ```
100 *
101 * #### Example:
102 * When no other url rule matches, go to `home` state
103 * ```js
104 * .initial((matchValue, url, router) => {
105 * console.log('initial state');
106 * return { state: 'home' };
107 * })
108 * ```
109 *
110 * @param handler The initial state or url path, or a function which returns the state or url path (or performs custom logic).
111 */
112 UrlRules.prototype.initial = function (handler) {
113 var handlerFn = getHandlerFn(handler);
114 var matchFn = function (urlParts, router) {
115 return router.globals.transitionHistory.size() === 0 && !!/^\/?$/.exec(urlParts.path);
116 };
117 this.rule(this.urlRuleFactory.create(matchFn, handlerFn));
118 };
119 /**
120 * Defines the state, url, or behavior to use when no other rule matches the URL.
121 *
122 * This rule is matched when *no other rule* matches.
123 * It is generally used to handle unknown URLs (similar to "404" behavior, but on the client side).
124 *
125 * - If `handler` a string, it is treated as a url redirect
126 *
127 * #### Example:
128 * When no other url rule matches, redirect to `/index`
129 * ```js
130 * .otherwise('/index');
131 * ```
132 *
133 * - If `handler` is an object with a `state` property, the state is activated.
134 *
135 * #### Example:
136 * When no other url rule matches, redirect to `home` and provide a `dashboard` parameter value.
137 * ```js
138 * .otherwise({ state: 'home', params: { dashboard: 'default' } });
139 * ```
140 *
141 * - If `handler` is a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object.
142 * The function can perform actions, and/or return a value.
143 *
144 * #### Example:
145 * When no other url rule matches, manually trigger a transition to the `home` state
146 * ```js
147 * .otherwise((matchValue, urlParts, router) => {
148 * router.stateService.go('home');
149 * });
150 * ```
151 *
152 * #### Example:
153 * When no other url rule matches, go to `home` state
154 * ```js
155 * .otherwise((matchValue, urlParts, router) => {
156 * return { state: 'home' };
157 * });
158 * ```
159 *
160 * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic).
161 */
162 UrlRules.prototype.otherwise = function (handler) {
163 var handlerFn = getHandlerFn(handler);
164 this._otherwiseFn = this.urlRuleFactory.create(common_1.val(true), handlerFn);
165 this._sorted = false;
166 };
167 /**
168 * Remove a rule previously registered
169 *
170 * @param rule the matcher rule that was previously registered using [[rule]]
171 */
172 UrlRules.prototype.removeRule = function (rule) {
173 common_1.removeFrom(this._rules, rule);
174 };
175 /**
176 * Manually adds a URL Rule.
177 *
178 * Usually, a url rule is added using [[StateDeclaration.url]] or [[when]].
179 * This api can be used directly for more control (to register a [[BaseUrlRule]], for example).
180 * Rules can be created using [[urlRuleFactory]], or created manually as simple objects.
181 *
182 * A rule should have a `match` function which returns truthy if the rule matched.
183 * It should also have a `handler` function which is invoked if the rule is the best match.
184 *
185 * @return a function that deregisters the rule
186 */
187 UrlRules.prototype.rule = function (rule) {
188 var _this = this;
189 if (!urlRule_1.UrlRuleFactory.isUrlRule(rule))
190 throw new Error('invalid rule');
191 rule.$id = this._id++;
192 rule.priority = rule.priority || 0;
193 this._rules.push(rule);
194 this._sorted = false;
195 return function () { return _this.removeRule(rule); };
196 };
197 /**
198 * Gets all registered rules
199 *
200 * @returns an array of all the registered rules
201 */
202 UrlRules.prototype.rules = function () {
203 this.ensureSorted();
204 return this._rules.concat(this._otherwiseFn ? [this._otherwiseFn] : []);
205 };
206 /**
207 * Defines URL Rule priorities
208 *
209 * More than one rule ([[UrlRule]]) might match a given URL.
210 * This `compareFn` is used to sort the rules by priority.
211 * Higher priority rules should sort earlier.
212 *
213 * The [[defaultRuleSortFn]] is used by default.
214 *
215 * You only need to call this function once.
216 * The `compareFn` will be used to sort the rules as each is registered.
217 *
218 * If called without any parameter, it will re-sort the rules.
219 *
220 * ---
221 *
222 * Url rules may come from multiple sources: states's urls ([[StateDeclaration.url]]), [[when]], and [[rule]].
223 * Each rule has a (user-provided) [[UrlRule.priority]], a [[UrlRule.type]], and a [[UrlRule.$id]]
224 * The `$id` is is the order in which the rule was registered.
225 *
226 * The sort function should use these data, or data found on a specific type
227 * of [[UrlRule]] (such as [[StateRule.state]]), to order the rules as desired.
228 *
229 * #### Example:
230 * This compare function prioritizes rules by the order in which the rules were registered.
231 * A rule registered earlier has higher priority.
232 *
233 * ```js
234 * function compareFn(a, b) {
235 * return a.$id - b.$id;
236 * }
237 * ```
238 *
239 * @param compareFn a function that compares to [[UrlRule]] objects.
240 * The `compareFn` should abide by the `Array.sort` compare function rules.
241 * Given two rules, `a` and `b`, return a negative number if `a` should be higher priority.
242 * Return a positive number if `b` should be higher priority.
243 * Return `0` if the rules are identical.
244 *
245 * See the [mozilla reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description)
246 * for details.
247 */
248 UrlRules.prototype.sort = function (compareFn) {
249 var sorted = this.stableSort(this._rules, (this._sortFn = compareFn || this._sortFn));
250 // precompute _sortGroup values and apply to each rule
251 var group = 0;
252 for (var i = 0; i < sorted.length; i++) {
253 sorted[i]._group = group;
254 if (i < sorted.length - 1 && this._sortFn(sorted[i], sorted[i + 1]) !== 0) {
255 group++;
256 }
257 }
258 this._rules = sorted;
259 this._sorted = true;
260 };
261 /** @internal */
262 UrlRules.prototype.ensureSorted = function () {
263 this._sorted || this.sort();
264 };
265 /** @internal */
266 UrlRules.prototype.stableSort = function (arr, compareFn) {
267 var arrOfWrapper = arr.map(function (elem, idx) { return ({ elem: elem, idx: idx }); });
268 arrOfWrapper.sort(function (wrapperA, wrapperB) {
269 var cmpDiff = compareFn(wrapperA.elem, wrapperB.elem);
270 return cmpDiff === 0 ? wrapperA.idx - wrapperB.idx : cmpDiff;
271 });
272 return arrOfWrapper.map(function (wrapper) { return wrapper.elem; });
273 };
274 /**
275 * Registers a `matcher` and `handler` for custom URLs handling.
276 *
277 * The `matcher` can be:
278 *
279 * - a [[UrlMatcher]]: See: [[UrlMatcherFactory.compile]]
280 * - a `string`: The string is compiled to a [[UrlMatcher]]
281 * - a `RegExp`: The regexp is used to match the url.
282 *
283 * The `handler` can be:
284 *
285 * - a string: The url is redirected to the value of the string.
286 * - a function: The url is redirected to the return value of the function.
287 *
288 * ---
289 *
290 * When the `handler` is a `string` and the `matcher` is a `UrlMatcher` (or string), the redirect
291 * string is interpolated with parameter values.
292 *
293 * #### Example:
294 * When the URL is `/foo/123` the rule will redirect to `/bar/123`.
295 * ```js
296 * .when("/foo/:param1", "/bar/:param1")
297 * ```
298 *
299 * ---
300 *
301 * When the `handler` is a string and the `matcher` is a `RegExp`, the redirect string is
302 * interpolated with capture groups from the RegExp.
303 *
304 * #### Example:
305 * When the URL is `/foo/123` the rule will redirect to `/bar/123`.
306 * ```js
307 * .when(new RegExp("^/foo/(.*)$"), "/bar/$1");
308 * ```
309 *
310 * ---
311 *
312 * When the handler is a function, it receives the matched value, the current URL, and the `UIRouter` object (See [[UrlRuleHandlerFn]]).
313 * The "matched value" differs based on the `matcher`.
314 * For [[UrlMatcher]]s, it will be the matched state params.
315 * For `RegExp`, it will be the match array from `regexp.exec()`.
316 *
317 * If the handler returns a string, the URL is redirected to the string.
318 *
319 * #### Example:
320 * When the URL is `/foo/123` the rule will redirect to `/bar/123`.
321 * ```js
322 * .when(new RegExp("^/foo/(.*)$"), match => "/bar/" + match[1]);
323 * ```
324 *
325 * Note: the `handler` may also invoke arbitrary code, such as `$state.go()`
326 *
327 * @param matcher A pattern `string` to match, compiled as a [[UrlMatcher]], or a `RegExp`.
328 * @param handler The path to redirect to, or a function that returns the path.
329 * @param options `{ priority: number }`
330 *
331 * @return the registered [[UrlRule]]
332 */
333 UrlRules.prototype.when = function (matcher, handler, options) {
334 var rule = this.urlRuleFactory.create(matcher, handler);
335 if (common_1.isDefined(options && options.priority))
336 rule.priority = options.priority;
337 this.rule(rule);
338 return rule;
339 };
340 return UrlRules;
341}());
342exports.UrlRules = UrlRules;
343//# sourceMappingURL=urlRules.js.map
\No newline at end of file