diff --git a/src/common/coreservices.ts b/src/common/coreservices.ts index 9d8a5ca9..26da6906 100644 --- a/src/common/coreservices.ts +++ b/src/common/coreservices.ts @@ -169,9 +169,9 @@ export interface LocationConfig extends Disposable { hashPrefix(newprefix: string): string; } +/** @hidden */ export interface TemplateServices { get(url: string): Promise; } - export {services}; diff --git a/src/params/interface.ts b/src/params/interface.ts index 57269f5f..2d9ff5ad 100644 --- a/src/params/interface.ts +++ b/src/params/interface.ts @@ -2,6 +2,7 @@ * @coreapi * @module params */ /** for typedoc */ + import {ParamType} from "./type"; /** @@ -16,11 +17,14 @@ import {ParamType} from "./type"; * folderId: 'inbox' * } * ``` + * + * @coreapi */ export interface RawParams { [key: string]: any; } +/** @internalapi */ export type ParamsOrArray = (RawParams|RawParams[]); /** @@ -53,6 +57,7 @@ export type ParamsOrArray = (RawParams|RawParams[]); * } * } * ``` + * @coreapi */ export interface ParamDeclaration { /** @@ -291,6 +296,7 @@ export interface ParamDeclaration { raw: boolean; } +/** @internalapi */ export interface Replace { from: string; to: string; @@ -402,7 +408,8 @@ export interface Replace { * $state.go('list', { item: "Ringo" }); * ``` * - * See: [[UrlMatcherFactory.type]] + * See: [[UrlConfig.type]] + * @coreapi */ export interface ParamTypeDefinition { /** diff --git a/src/url/index.ts b/src/url/index.ts index 54acfd06..5806ad4d 100644 --- a/src/url/index.ts +++ b/src/url/index.ts @@ -7,3 +7,4 @@ export * from "./urlMatcher"; export * from "./urlMatcherFactory"; export * from "./urlRouter"; export * from "./urlRule"; +export * from "./urlService"; diff --git a/src/url/interface.ts b/src/url/interface.ts index 3ed11d63..f3204437 100644 --- a/src/url/interface.ts +++ b/src/url/interface.ts @@ -1,3 +1,12 @@ +/** + * Contains code related to managing the URL + * + * The primary API is found in [[UrlService]], [[UrlService.config]], and [[UrlService.rules]]. + * + * @preferred + * @coreapi + * @module url + */ /** */ import { LocationConfig } from "../common/coreservices"; import { ParamType } from "../params/type"; import { Param } from "../params/param"; @@ -6,7 +15,9 @@ import { TargetState } from "../state/targetState"; import { TargetStateDef } from "../state/interface"; import { UrlMatcher } from "./urlMatcher"; import { State } from "../state/stateObject"; +import { ParamTypeDefinition } from "../params/interface"; +/** @internalapi */ export interface ParamFactory { /** Creates a new [[Param]] from a CONFIG block */ fromConfig(id: string, type: ParamType, config: any): Param; @@ -16,15 +27,353 @@ export interface ParamFactory { fromSearch(id: string, type: ParamType, config: any): Param; } -export interface UrlConfig extends LocationConfig, UrlMatcherConfig {}; +/** + * An API to customize the URL behavior and retrieve URL configuration + * + * + * This API can customize the behavior of the URL. + * This includes optional trailing slashes ([[strictMode]]), case sensitivity ([[caseInsensitive]]), + * and custom parameter encoding (custom [[type]]). + * + * It also has information about the location (url) configuration such as [[port]] and [[baseHref]]. + * This information can be used to build absolute URLs, such as + * `https://example.com:443/basepath/state/substate?param1=a#hashvalue`; + * + * This API is found on [[UrlService.config]]. + */ +export interface UrlConfig extends LocationConfig, UrlMatcherConfig {} export interface UrlMatcherConfig { + /** + * Defines whether URL matching should be case sensitive (the default behavior), or not. + * + * @param value `false` to match URL in a case sensitive manner; otherwise `true`; + * @returns the current value of caseInsensitive + * + * #### Example: + * ```js + * // Allow case insensitive url matches + * urlService.config.caseInsensitive(true); + * ``` + */ caseInsensitive(value?: boolean): boolean; + + /** + * Defines whether URLs should match trailing slashes, or not (the default behavior). + * + * @param value `false` to match trailing slashes in URLs, otherwise `true`. + * @returns the current value of strictMode + * + * #### Example: + * ```js + * // Allow optional trailing slashes + * urlService.config.strictMode(false); + * ``` + */ strictMode(value?: boolean): boolean; + + /** + * Sets the default behavior when generating or matching URLs with default parameter values. + * + * @param value A string that defines the default parameter URL squashing behavior. + * - `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL + * - `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the + * parameter is surrounded by slashes, squash (remove) one slash from the URL + * - any other string, e.g. "~": When generating an href with a default parameter value, squash (remove) + * the parameter value from the URL and replace it with this string. + * @returns the current value of defaultSquashPolicy + * + * #### Example: + * ```js + * // Remove default parameter values from the url + * urlService.config.defaultSquashPolicy(true); + * ``` + */ defaultSquashPolicy(value?: (boolean|string)): (boolean|string); - paramType(name, type?) + + + /** + * Creates and registers a custom [[ParamTypeDefinition]] object + * + * A custom parameter type can be used to generate URLs with typed parameters or custom encoding/decoding. + * + * @param name The type name. + * @param type The type definition. See [[ParamTypeDefinition]] for examples and information. + * + * @returns if only the `name` parameter was specified: the currently registered [[ParamType]] object, or undefined + * + * #### Note: Register custom types *before using them* in a state definition. + * + * #### Example: + * ```js + * // Encode object parameter as JSON string + * urlService.config.type('myjson', { + * encode: (obj) => JSON.stringify(obj), + * decode: (str) => JSON.parse(str), + * is: (val) => typeof(val) === 'object', + * pattern: /[^/]+/, + * equals: (a, b) => _.isEqual(a, b), + * }); + * ``` + */ + type(name: string, type?: ParamTypeDefinition): ParamType; } +/** @internalapi */ +export interface UrlSync { + /** + * Checks the URL for a matching [[UrlRule]] + * + * Checks the current URL for a matching url rule, then invokes that rule's handler. + * This method is called internally any time the URL has changed. + * + * This effectively activates the state which matches the current URL. + * + * #### Example: + * ```js + * urlService.deferIntercept(); + * + * $http.get('/states.json').then(function(resp) { + * resp.data.forEach(state => $stateRegistry.register(state)); + * urlService.listen(); + * // Find the matching URL and invoke the handler. + * urlService.sync(); + * }); + * ``` + */ + sync(evt?): void; +} + +/** @internalapi */ +export interface UrlListen { + /** + * Starts or stops listening for URL changes + * + * Call this sometime after calling [[deferIntercept]] to start monitoring the url. + * This causes [[UrlRouter]] to start listening for changes to the URL, if it wasn't already listening. + * + * If called with `false`, will stop listening. Call listen() again to start listening + * + * #### Example: + * ```js + * urlService.deferIntercept(); + * + * $http.get('/states.json').then(function(resp) { + * resp.data.forEach(state => $stateRegistry.register(state)); + * // Start responding to URL changes + * urlService.listen(); + * urlService.sync(); + * }); + * ``` + */ + listen(enabled?: boolean): Function; +} + +/** @internalapi */ +export interface UrlDeferIntercept { + /** + * Disables monitoring of the URL. + * + * Call this method before UI-Router has bootstrapped. + * It will stop UI-Router from performing the initial url sync. + * + * This can be useful to perform some asynchronous initialization before the router starts. + * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL. + * + * #### Example: + * ```js + * // Prevent $urlRouter from automatically intercepting URL changes when it starts; + * urlService.deferIntercept(); + * + * $http.get('/states.json').then(function(resp) { + * resp.data.forEach(state => $stateRegistry.register(state)); + * urlService.listen(); + * urlService.sync(); + * }); + * ``` + * + * @param defer Indicates whether to defer location change interception. + * Passing no parameter is equivalent to `true`. + */ + deferIntercept(defer?: boolean); +} + +/** + * API for managing URL rules + * + * This API can be 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]]. + */ +export interface UrlRules { + /** + * 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 [[StateUrlRule.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. + */ + sort(compareFn?: (a: UrlRule, b: UrlRule) => number); + + /** + * 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]); + * ``` + * + * @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]] + * + * Note: the `handler` may also invoke arbitrary code, such as `$state.go()` + */ + when(matcher: (RegExp|UrlMatcher|string), handler: string|UrlRuleHandlerFn, options?: { priority: number }): UrlRule; + + /** + * Defines the path or behavior to use when no url can be matched. + * + * - If a string, it is treated as a url redirect + * + * #### Example: + * When no other url rule matches, redirect to `/index` + * ```js + * .otherwise('/index'); + * ``` + * + * - If a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object. + * If the function returns a string, the url is redirected to the return value. + * + * #### Example: + * When no other url rule matches, redirect to `/index` + * ```js + * .otherwise(() => '/index'); + * ``` + * + * #### Example: + * When no other url rule matches, go to `home` state + * ```js + * .otherwise((url, router) => { + * router.stateService.go('home'); + * return; + * } + * ``` + * + * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic). + */ + otherwise(handler: string|UrlRuleHandlerFn|TargetState|TargetStateDef): void; + + /** + * Gets all registered rules + * + * @returns an array of all the registered rules + */ + rules(): UrlRule[]; + + /** + * 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 [[RawUrlRule]], for example). + * Rules can be created using [[UrlRouter.urlRuleFactory]], or create manually as simple objects. + * + * @return a function that deregisters the rule + */ + rule(rule: UrlRule): Function; + + /** + * Remove a rule previously registered + * + * @param rule the matcher rule that was previously registered using [[rule]] + */ + removeRule(rule: UrlRule): void; +} + +/** + * An object containing the three parts of a URL + */ export interface UrlParts { path: string; search: { [key: string]: any }; @@ -53,8 +402,14 @@ export interface UrlRuleHandlerFn { (matchValue?: any, url?: UrlParts, router?: UIRouter): (string|TargetState|TargetStateDef|void); } +/** @internalapi */ export type UrlRuleType = "STATE" | "URLMATCHER" | "REGEXP" | "RAW" | "OTHER"; +/** + * The interface for a URL Rule + * + * If you are creating a rule for use with [[UrlRules.rule]], it should implement this interface. + */ export interface UrlRule { /** * The rule's ID. @@ -97,59 +452,20 @@ export interface UrlRule { handler: UrlRuleHandlerFn; } +/** @internalapi */ export interface MatcherUrlRule extends UrlRule { type: "URLMATCHER"|"STATE"; urlMatcher: UrlMatcher; } +/** @internalapi */ export interface StateRule extends MatcherUrlRule { type: "STATE"; state: State; } +/** @internalapi */ export interface RegExpRule extends UrlRule { type: "REGEXP"; regexp: RegExp; } - - -// export interface UrlService { -// // LocationServices -// // todo: switch back to url() -// setUrl(newurl: string, replace?: boolean): void; -// path(): string; -// search(): { [key: string]: any }; -// hash(): string; -// onChange(callback: Function): Function; -// -// config: { -// // LocationConfig -// port(): number; -// protocol(): string; -// host(): string; -// baseHref(): string; -// html5Mode(): boolean; -// hashPrefix(): string; -// -// hashPrefix(newprefix: string): string; -// -// // MatcherConfig -// caseInsensitive(value?: boolean): boolean; -// strictMode(value?: boolean): boolean; -// defaultSquashPolicy(value?: (boolean|string)): (boolean|string); -// paramType(name, type?) -// } -// -// rules: { -// // UrlRouterProvider -// rule(rule: UrlRule): UrlRouterProvider; -// otherwise(rule: string | (($injector: $InjectorLike, $location: LocationServices) => string)): UrlRouterProvider ; -// when(what: (RegExp|UrlMatcher|string), handler: string|IInjectable, ruleCallback?) ; -// } -// -// deferIntercept(defer?: boolean); -// -// // UrlRouter -// sync(evt?); -// listen(): Function; -// } diff --git a/src/url/urlMatcher.ts b/src/url/urlMatcher.ts index 63910fc9..923f4b86 100644 --- a/src/url/urlMatcher.ts +++ b/src/url/urlMatcher.ts @@ -35,6 +35,7 @@ function quoteRegExp(string: any, param?: any) { const memoizeTo = (obj: Obj, prop: string, fn: Function) => obj[prop] = obj[prop] || fn(); +/** @hidden */ interface UrlMatcherCache { path: UrlMatcher[]; parent: UrlMatcher; diff --git a/src/url/urlMatcherFactory.ts b/src/url/urlMatcherFactory.ts index 4ae62d9b..492402ad 100644 --- a/src/url/urlMatcherFactory.ts +++ b/src/url/urlMatcherFactory.ts @@ -1,5 +1,5 @@ /** - * @coreapi + * @internalapi * @module url */ /** for typedoc */ import { forEach, extend } from "../common/common"; @@ -10,7 +10,7 @@ import { ParamTypes } from "../params/paramTypes"; import { ParamTypeDefinition } from "../params/interface"; import { Disposable } from "../interface"; import { ParamType } from "../params/type"; -import { ParamFactory } from "./interface"; +import { ParamFactory, UrlMatcherConfig } from "./interface"; /** * Factory for [[UrlMatcher]] instances. @@ -18,7 +18,7 @@ import { ParamFactory } from "./interface"; * The factory is available to ng1 services as * `$urlMatcherFactor` or ng1 providers as `$urlMatcherFactoryProvider`. */ -export class UrlMatcherFactory implements Disposable { +export class UrlMatcherFactory implements Disposable, UrlMatcherConfig { /** @hidden */ paramTypes = new ParamTypes(); /** @hidden */ _isCaseInsensitive: boolean = false; /** @hidden */ _isStrictMode: boolean = true; @@ -28,37 +28,17 @@ export class UrlMatcherFactory implements Disposable { extend(this, { UrlMatcher, Param }); } - /** - * Defines whether URL matching should be case sensitive (the default behavior), or not. - * - * @param value `false` to match URL in a case sensitive manner; otherwise `true`; - * @returns the current value of caseInsensitive - */ + /** @inheritdoc */ caseInsensitive(value?: boolean): boolean { return this._isCaseInsensitive = isDefined(value) ? value : this._isCaseInsensitive; } - /** - * Defines whether URLs should match trailing slashes, or not (the default behavior). - * - * @param value `false` to match trailing slashes in URLs, otherwise `true`. - * @returns the current value of strictMode - */ + /** @inheritdoc */ strictMode(value?: boolean): boolean { return this._isStrictMode = isDefined(value) ? value : this._isStrictMode; } - /** - * Sets the default behavior when generating or matching URLs with default parameter values. - * - * @param value A string that defines the default parameter URL squashing behavior. - * - `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL - * - `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the - * parameter is surrounded by slashes, squash (remove) one slash from the URL - * - any other string, e.g. "~": When generating an href with a default parameter value, squash (remove) - * the parameter value from the URL and replace it with this string. - * @returns the current value of defaultSquashPolicy - */ + /** @inheritdoc */ defaultSquashPolicy(value?: (boolean|string)) { if (isDefined(value) && value !== true && value !== false && !isString(value)) throw new Error(`Invalid squash policy: ${value}. Valid policies: false, true, arbitrary-string`); diff --git a/src/url/urlRouter.ts b/src/url/urlRouter.ts index 3a3389d0..e68de426 100644 --- a/src/url/urlRouter.ts +++ b/src/url/urlRouter.ts @@ -1,5 +1,5 @@ /** - * @coreapi + * @internalapi * @module url */ /** for typedoc */ import { removeFrom, createProxyFunctions, inArray, composeSort, sortBy } from "../common/common"; @@ -11,7 +11,7 @@ import { UIRouter } from "../router"; import { val, is, pattern, prop, pipe } from "../common/hof"; import { UrlRuleFactory } from "./urlRule"; import { TargetState } from "../state/targetState"; -import { UrlRule, UrlRuleHandlerFn, UrlParts } from "./interface"; +import { UrlRule, UrlRuleHandlerFn, UrlParts, UrlRules, UrlSync, UrlListen, UrlDeferIntercept } from "./interface"; import { TargetStateDef } from "../state/interface"; /** @hidden */ @@ -25,19 +25,41 @@ function appendBasePath(url: string, isHtml5: boolean, absolute: boolean, baseHr /** @hidden */ const getMatcher = prop("urlMatcher"); + + +/** + * Default rule priority sorting function. + * + * Sorts rules by: + * + * - Explicit priority (set rule priority using [[UrlService.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. + * - Registration order (for rule types other than STATE and URLMATCHER) + * + * @coreapi + */ +let defaultRuleSortFn: (a: UrlRule, b: UrlRule) => number; +defaultRuleSortFn = composeSort( + sortBy(pipe(prop("priority"), x => -x)), + sortBy(pipe(prop("type"), type => ({ "STATE": 4, "URLMATCHER": 4, "REGEXP": 3, "RAW": 2, "OTHER": 1 })[type])), + (a,b) => (getMatcher(a) && getMatcher(b)) ? UrlMatcher.compare(getMatcher(a), getMatcher(b)) : 0, + sortBy(prop("$id"), inArray([ "REGEXP", "RAW", "OTHER" ])), +); + /** * Updates URL and responds to URL changes * * This class updates the URL when the state changes. * It also responds to changes in the URL. */ -export class UrlRouter implements Disposable { +export class UrlRouter implements UrlRules, UrlSync, UrlListen, UrlDeferIntercept, Disposable { /** used to create [[UrlRule]] objects for common cases */ public urlRuleFactory: UrlRuleFactory; /** @hidden */ private _router: UIRouter; /** @hidden */ private location: string; - /** @hidden */ private _sortFn = UrlRouter.defaultRuleSortFn; + /** @hidden */ private _sortFn = defaultRuleSortFn; /** @hidden */ private _stopFn: Function; /** @hidden */ _rules: UrlRule[] = []; /** @hidden */ private _otherwiseFn: UrlRule; @@ -58,75 +80,12 @@ export class UrlRouter implements Disposable { delete 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 [[StateUrlRule.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. - */ + /** @inheritdoc */ sort(compareFn?: (a: UrlRule, b: UrlRule) => number) { this._rules.sort(this._sortFn = compareFn || this._sortFn); } - /** - * Checks the current URL for a matching rule - * - * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`. - * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event, - * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed - * with the transition by calling `$urlRouter.sync()`. - * - * #### Example: - * ```js - * angular.module('app', ['ui.router']) - * .run(function($rootScope, $urlRouter) { - * $rootScope.$on('$locationChangeSuccess', function(evt) { - * // Halt state change from even starting - * evt.preventDefault(); - * // Perform custom logic - * var meetsRequirement = ... - * // Continue with the update and state transition if logic allows - * if (meetsRequirement) $urlRouter.sync(); - * }); - * }); - * ``` - */ + /** @inheritdoc */ sync(evt?) { if (evt && evt.defaultPrevented) return; @@ -171,14 +130,7 @@ export class UrlRouter implements Disposable { applyResult(best && best.rule.handler(best.match, url, router)); } - /** - * Starts or stops listening for URL changes - * - * Call this sometime after calling [[deferIntercept]] to start monitoring the url. - * This causes [[UrlRouter]] to start listening for changes to the URL, if it wasn't already listening. - * - * If called with `false`, will stop listening. Call listen() again to start listening - */ + /** @inheritdoc */ listen(enabled?: boolean): Function { if (enabled === false) { this._stopFn && this._stopFn(); @@ -279,54 +231,16 @@ export class UrlRouter implements Disposable { return () => this.removeRule(rule); } - /** - * Remove a rule previously registered - * - * @param rule the matcher rule that was previously registered using [[rule]] - */ + /** @inheritdoc */ removeRule(rule): void { removeFrom(this._rules, rule); this.sort(); } - /** - * Gets all registered rules - * - * @returns an array of all the registered rules - */ - rules = (): UrlRule[] => this._rules.slice(); + /** @inheritdoc */ + rules(): UrlRule[] { return this._rules.slice(); } - /** - * Defines the path or behavior to use when no url can be matched. - * - * - If a string, it is treated as a url redirect - * - * #### Example: - * When no other url rule matches, redirect to `/index` - * ```js - * .otherwise('/index'); - * ``` - * - * - If a function, the function receives the current url ([[UrlParts]]) and the [[UIRouter]] object. - * If the function returns a string, the url is redirected to the return value. - * - * #### Example: - * When no other url rule matches, redirect to `/index` - * ```js - * .otherwise(() => '/index'); - * ``` - * - * #### Example: - * When no other url rule matches, go to `home` state - * ```js - * .otherwise((url, router) => { - * router.stateService.go('home'); - * return; - * } - * ``` - * - * @param handler The url path to redirect to, or a function which returns the url path (or performs custom logic). - */ + /** @inheritdoc */ otherwise(handler: string|UrlRuleHandlerFn|TargetState|TargetStateDef) { if (!isFunction(handler) && !isString(handler) && !is(TargetState)(handler) && !TargetState.isDef(handler)) { throw new Error("'redirectTo' must be a string, function, TargetState, or have a state: 'newtarget' property"); @@ -337,65 +251,7 @@ export class UrlRouter implements Disposable { this.sort(); }; - /** - * 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]); - * ``` - * - * @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]] - * - * Note: the `handler` may also invoke arbitrary code, such as `$state.go()` - */ + /** @inheritdoc */ when(matcher: (RegExp|UrlMatcher|string), handler: string|UrlRuleHandlerFn, options?: { priority: number }): UrlRule { let rule = this.urlRuleFactory.create(matcher, handler); if (isDefined(options && options.priority)) rule.priority = options.priority; @@ -403,55 +259,10 @@ export class UrlRouter implements Disposable { return rule; }; - /** - * Disables monitoring of the URL. - * - * Call this method before UI-Router has bootstrapped. - * It will stop UI-Router from performing the initial url sync. - * - * This can be useful to perform some asynchronous initialization before the router starts. - * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL. - * - * #### Example: - * ```js - * var app = angular.module('app', ['ui.router']); - * - * app.config(function ($urlRouterProvider) { - * // Prevent $urlRouter from automatically intercepting URL changes; - * $urlRouterProvider.deferIntercept(); - * }) - * - * app.run(function (MyService, $urlRouter, $http) { - * $http.get("/stuff").then(function(resp) { - * MyService.doStuff(resp.data); - * $urlRouter.listen(); - * $urlRouter.sync(); - * }); - * }); - * ``` - * - * @param defer Indicates whether to defer location change interception. - * Passing no parameter is equivalent to `true`. - */ + /** @inheritdoc */ deferIntercept(defer?: boolean) { if (defer === undefined) defer = true; this.interceptDeferred = defer; }; - - /** - * Default rule priority sorting function. - * - * Sorts rules by: - * - * - Explicit priority (set rule priority using [[UrlRouter.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. - * - Registration order (for rule types other than STATE and URLMATCHER) - */ - static defaultRuleSortFn = composeSort( - sortBy(pipe(prop("priority"), x => -x)), - sortBy(pipe(prop("type"), type => ({ "STATE": 4, "URLMATCHER": 4, "REGEXP": 3, "RAW": 2, "OTHER": 1 })[type])), - (a,b) => (getMatcher(a) && getMatcher(b)) ? UrlMatcher.compare(getMatcher(a), getMatcher(b)) : 0, - sortBy(prop("$id"), inArray([ "REGEXP", "RAW", "OTHER" ])), - ); } + diff --git a/src/url/urlRule.ts b/src/url/urlRule.ts index 8ca76f39..42c76bc9 100644 --- a/src/url/urlRule.ts +++ b/src/url/urlRule.ts @@ -1,5 +1,5 @@ /** - * @internalapi + * @coreapi * @module url */ /** */ import { UrlMatcher } from "./urlMatcher"; @@ -22,6 +22,7 @@ import { * - [[UrlMatcher]] * - `RegExp` * - [[State]] + * @internalapi */ export class UrlRuleFactory { constructor(public router: UIRouter) { } @@ -200,6 +201,7 @@ export class UrlRuleFactory { * A base rule which calls `match` * * The value from the `match` function is passed through to the `handler`. + * @internalapi */ export class BaseUrlRule implements UrlRule { $id: number; diff --git a/src/url/urlService.ts b/src/url/urlService.ts index 4f178058..e2195dbf 100644 --- a/src/url/urlService.ts +++ b/src/url/urlService.ts @@ -1,30 +1,27 @@ -/** @module url */ /** */ +/** + * @coreapi + * @module url + */ /** */ import { UIRouter } from "../router"; import { LocationServices, notImplemented, LocationConfig } from "../common/coreservices"; import { noop, createProxyFunctions } from "../common/common"; -import { UrlConfig } from "./interface"; +import { UrlConfig, UrlSync, UrlListen, UrlRules, UrlDeferIntercept } from "./interface"; /** @hidden */ const makeStub = (keys: string[]): any => keys.reduce((acc, key) => (acc[key] = notImplemented(key), acc), { dispose: noop }); -/** @hidden */ -const locationServicesFns = ["url", "path", "search", "hash", "onChange"]; -/** @hidden */ -const locationConfigFns = ["port", "protocol", "host", "baseHref", "html5Mode", "hashPrefix"]; +/** @hidden */ const locationServicesFns = ["url", "path", "search", "hash", "onChange"]; +/** @hidden */ const locationConfigFns = ["port", "protocol", "host", "baseHref", "html5Mode", "hashPrefix"]; +/** @hidden */ const umfFns = ["type", "caseInsensitive", "strictMode", "defaultSquashPolicy"]; +/** @hidden */ const rulesFns = ["sort", "when", "otherwise", "rules", "rule", "removeRule"]; +/** @hidden */ const syncFns = ["deferIntercept", "listen", "sync"]; /** - * Service methods related to URL management - * - * This class delegates to other URL services. - * - * - [[LocationService]]: Framework specific code to interact with the browser URL - * - [[LocationConfig]]: Framework specific code to interact with the browser URL - * - [[UrlMatcherFactory]]: - * - [[UrlRouter]]: + * API for URL management */ -export class UrlService implements LocationServices { +export class UrlService implements LocationServices, UrlSync, UrlListen, UrlDeferIntercept { /** @hidden */ static locationServiceStub: LocationServices = makeStub(locationServicesFns); /** @hidden */ @@ -46,22 +43,48 @@ export class UrlService implements LocationServices { dispose() { } + /** @inheritdoc */ + sync(evt?) { return } + /** @inheritdoc */ + listen(enabled?: boolean): Function { return }; + /** @inheritdoc */ + deferIntercept(defer?: boolean) { return } + /** - * The [[LocationConfig]] service + * A nested API for managing URL rules and rewrites * - * This object returns information about the location (url) configuration. - * This information can be used to build absolute URLs, such as - * `https://example.com:443/basepath/state/substate?param1=a#hashvalue`; + * See: [[UrlRules]] for details + */ + rules: UrlRules; + + /** + * A nested API to configure the URL and retrieve URL information + * + * See: [[UrlConfig]] for details */ config: UrlConfig; - constructor(private router: UIRouter) { + /** @hidden */ + private router: UIRouter; + + /** @hidden */ + constructor(router: UIRouter) { + this.router = router; + this.rules = {} as any; this.config = {} as any; + // proxy function calls from UrlService to the LocationService/LocationConfig const locationServices = () => router.locationService; createProxyFunctions(locationServices, this, locationServices, locationServicesFns, true); const locationConfig = () => router.locationConfig; createProxyFunctions(locationConfig, this.config, locationConfig, locationConfigFns, true); + + const umf = () => router.urlMatcherFactory; + createProxyFunctions(umf, this.config, umf, umfFns); + + const urlRouter = () => router.urlRouter; + createProxyFunctions(urlRouter, this.rules, urlRouter, rulesFns); + createProxyFunctions(urlRouter, this, urlRouter, syncFns); } }