1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.UrlRules = void 0;
|
4 | var state_1 = require("../state");
|
5 | var urlMatcher_1 = require("./urlMatcher");
|
6 | var common_1 = require("../common");
|
7 | var urlRule_1 = require("./urlRule");
|
8 | var prioritySort = function (a, b) { return (b.priority || 0) - (a.priority || 0); };
|
9 | var 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 | };
|
13 | var urlMatcherSort = function (a, b) {
|
14 | return !a.urlMatcher || !b.urlMatcher ? 0 : urlMatcher_1.UrlMatcher.compare(a.urlMatcher, b.urlMatcher);
|
15 | };
|
16 | var 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 | */
|
34 | var defaultRuleSortFn;
|
35 | defaultRuleSortFn = 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 | };
|
47 | function 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 | */
|
63 | var 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 | }());
|
342 | exports.UrlRules = UrlRules;
|
343 | //# sourceMappingURL=urlRules.js.map |
\ | No newline at end of file |