@angular-wave/angular.ts 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular-ts.esm.js +1 -1
- package/dist/angular-ts.umd.js +1 -1
- package/package.json +4 -1
- package/src/exts/messages.md +30 -30
- package/src/index.js +1 -0
- package/src/public.js +2 -0
- package/src/router/adapter/directives/stateDirectives.js +695 -0
- package/src/router/adapter/directives/viewDirective.js +514 -0
- package/src/router/adapter/injectables.js +314 -0
- package/src/router/adapter/interface.js +1 -0
- package/src/router/adapter/locationServices.js +84 -0
- package/src/router/adapter/services.js +126 -0
- package/src/router/adapter/stateFilters.js +43 -0
- package/src/router/adapter/stateProvider.js +137 -0
- package/src/router/adapter/statebuilders/onEnterExitRetain.js +30 -0
- package/src/router/adapter/statebuilders/views.js +146 -0
- package/src/router/adapter/templateFactory.js +218 -0
- package/src/router/adapter/viewScroll.js +31 -0
- package/src/router/core/common/common.js +496 -0
- package/src/router/core/common/coreservices.js +15 -0
- package/src/router/core/common/glob.js +75 -0
- package/src/router/core/common/hof.js +194 -0
- package/src/router/core/common/predicates.js +44 -0
- package/src/router/core/common/queue.js +41 -0
- package/src/router/core/common/safeConsole.js +38 -0
- package/src/router/core/common/strings.js +141 -0
- package/src/router/core/common/trace.js +232 -0
- package/src/router/core/globals.js +29 -0
- package/src/router/core/hooks/coreResolvables.js +33 -0
- package/src/router/core/hooks/ignoredTransition.js +25 -0
- package/src/router/core/hooks/invalidTransition.js +14 -0
- package/src/router/core/hooks/lazyLoad.js +102 -0
- package/src/router/core/hooks/onEnterExitRetain.js +55 -0
- package/src/router/core/hooks/redirectTo.js +36 -0
- package/src/router/core/hooks/resolve.js +57 -0
- package/src/router/core/hooks/updateGlobals.js +30 -0
- package/src/router/core/hooks/url.js +25 -0
- package/src/router/core/hooks/views.js +39 -0
- package/src/router/core/interface.js +3 -0
- package/src/router/core/params/README.md +8 -0
- package/src/router/core/params/param.js +232 -0
- package/src/router/core/params/paramType.js +139 -0
- package/src/router/core/params/paramTypes.js +163 -0
- package/src/router/core/params/stateParams.js +35 -0
- package/src/router/core/path/pathNode.js +77 -0
- package/src/router/core/path/pathUtils.js +199 -0
- package/src/router/core/resolve/interface.js +10 -0
- package/src/router/core/resolve/resolvable.js +124 -0
- package/src/router/core/resolve/resolveContext.js +211 -0
- package/src/router/core/router.js +203 -0
- package/src/router/core/state/README.md +21 -0
- package/src/router/core/state/stateBuilder.js +332 -0
- package/src/router/core/state/stateMatcher.js +65 -0
- package/src/router/core/state/stateObject.js +117 -0
- package/src/router/core/state/stateQueueManager.js +89 -0
- package/src/router/core/state/stateRegistry.js +175 -0
- package/src/router/core/state/stateService.js +592 -0
- package/src/router/core/state/targetState.js +159 -0
- package/src/router/core/transition/hookBuilder.js +127 -0
- package/src/router/core/transition/hookRegistry.js +175 -0
- package/src/router/core/transition/interface.js +14 -0
- package/src/router/core/transition/rejectFactory.js +122 -0
- package/src/router/core/transition/transition.js +739 -0
- package/src/router/core/transition/transitionEventType.js +27 -0
- package/src/router/core/transition/transitionHook.js +199 -0
- package/src/router/core/transition/transitionService.js +311 -0
- package/src/router/core/url/interface.js +1 -0
- package/src/router/core/url/urlConfig.js +165 -0
- package/src/router/core/url/urlMatcher.js +548 -0
- package/src/router/core/url/urlMatcherFactory.js +123 -0
- package/src/router/core/url/urlRouter.js +115 -0
- package/src/router/core/url/urlRule.js +202 -0
- package/src/router/core/url/urlRules.js +348 -0
- package/src/router/core/url/urlService.js +268 -0
- package/src/router/core/view/interface.js +1 -0
- package/src/router/core/view/view.js +312 -0
- package/src/router/router.js +58 -0
- package/test/module-test.html +6 -2
- package/test/module-test.js +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { UrlMatcherFactory } from "./url/urlMatcherFactory";
|
|
2
|
+
import { UrlRouter } from "./url/urlRouter";
|
|
3
|
+
import { TransitionService } from "./transition/transitionService";
|
|
4
|
+
import { ViewService } from "./view/view";
|
|
5
|
+
import { StateRegistry } from "./state/stateRegistry";
|
|
6
|
+
import { StateService } from "./state/stateService";
|
|
7
|
+
import { UIRouterGlobals } from "./globals";
|
|
8
|
+
import { removeFrom } from "./common/common";
|
|
9
|
+
import { isFunction } from "./common/predicates";
|
|
10
|
+
import { UrlService } from "./url/urlService";
|
|
11
|
+
import { trace } from "./common/trace";
|
|
12
|
+
import { makeStub } from "./common/coreservices";
|
|
13
|
+
|
|
14
|
+
/** @internal
|
|
15
|
+
* @type {number}
|
|
16
|
+
*/
|
|
17
|
+
let _routerInstance = 0;
|
|
18
|
+
|
|
19
|
+
/** @internal
|
|
20
|
+
* @type {(keyof LocationServices)[]}
|
|
21
|
+
*/
|
|
22
|
+
const locSvcFns = ["url", "path", "search", "hash", "onChange"];
|
|
23
|
+
/** @internal
|
|
24
|
+
* @type {(keyof LocationConfig)[]}
|
|
25
|
+
*/
|
|
26
|
+
const locCfgFns = [
|
|
27
|
+
"port",
|
|
28
|
+
"protocol",
|
|
29
|
+
"host",
|
|
30
|
+
"baseHref",
|
|
31
|
+
"html5Mode",
|
|
32
|
+
"hashPrefix",
|
|
33
|
+
];
|
|
34
|
+
/** @internal
|
|
35
|
+
* @type {any}
|
|
36
|
+
*/
|
|
37
|
+
const locationServiceStub = makeStub("LocationServices", locSvcFns);
|
|
38
|
+
/** @internal
|
|
39
|
+
* @type {any}
|
|
40
|
+
*/
|
|
41
|
+
const locationConfigStub = makeStub("LocationConfig", locCfgFns);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* An instance of UI-Router.
|
|
45
|
+
*
|
|
46
|
+
* This object contains references to service APIs which define your application's routing behavior.
|
|
47
|
+
*/
|
|
48
|
+
export class UIRouter {
|
|
49
|
+
/**
|
|
50
|
+
* Creates a new `UIRouter` object
|
|
51
|
+
*
|
|
52
|
+
* @param locationService a [[LocationServices]] implementation
|
|
53
|
+
* @param locationConfig a [[LocationConfig]] implementation
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
constructor(
|
|
57
|
+
locationService = locationServiceStub,
|
|
58
|
+
locationConfig = locationConfigStub,
|
|
59
|
+
) {
|
|
60
|
+
this.locationService = locationService;
|
|
61
|
+
this.locationConfig = locationConfig;
|
|
62
|
+
/** @internal */ this.$id = _routerInstance++;
|
|
63
|
+
/** @internal */ this._disposed = false;
|
|
64
|
+
/** @internal */ this._disposables = [];
|
|
65
|
+
/** Enable/disable tracing to the javascript console */
|
|
66
|
+
this.trace = trace;
|
|
67
|
+
/** Provides services related to ui-view synchronization */
|
|
68
|
+
this.viewService = new ViewService(this);
|
|
69
|
+
/** An object that contains global router state, such as the current state and params */
|
|
70
|
+
this.globals = new UIRouterGlobals();
|
|
71
|
+
/** A service that exposes global Transition Hooks */
|
|
72
|
+
this.transitionService = new TransitionService(this);
|
|
73
|
+
/**
|
|
74
|
+
* Deprecated for public use. Use [[urlService]] instead.
|
|
75
|
+
* @deprecated Use [[urlService]] instead
|
|
76
|
+
*/
|
|
77
|
+
this.urlMatcherFactory = new UrlMatcherFactory(this);
|
|
78
|
+
/**
|
|
79
|
+
* Deprecated for public use. Use [[urlService]] instead.
|
|
80
|
+
* @deprecated Use [[urlService]] instead
|
|
81
|
+
*/
|
|
82
|
+
this.urlRouter = new UrlRouter(this);
|
|
83
|
+
/** Provides services related to the URL */
|
|
84
|
+
this.urlService = new UrlService(this);
|
|
85
|
+
/** Provides a registry for states, and related registration services */
|
|
86
|
+
this.stateRegistry = new StateRegistry(this);
|
|
87
|
+
/** Provides services related to states */
|
|
88
|
+
this.stateService = new StateService(this);
|
|
89
|
+
/** @internal plugin instances are registered here */
|
|
90
|
+
this._plugins = {};
|
|
91
|
+
this.viewService._pluginapi._rootViewContext(this.stateRegistry.root());
|
|
92
|
+
this.globals.$current = this.stateRegistry.root();
|
|
93
|
+
this.globals.current = this.globals.$current.self;
|
|
94
|
+
this.disposable(this.globals);
|
|
95
|
+
this.disposable(this.stateService);
|
|
96
|
+
this.disposable(this.stateRegistry);
|
|
97
|
+
this.disposable(this.transitionService);
|
|
98
|
+
this.disposable(this.urlService);
|
|
99
|
+
this.disposable(locationService);
|
|
100
|
+
this.disposable(locationConfig);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Registers an object to be notified when the router is disposed
|
|
105
|
+
* @param {Disposable} disposable
|
|
106
|
+
* @returns {void}
|
|
107
|
+
*/
|
|
108
|
+
disposable(disposable) {
|
|
109
|
+
this._disposables.push(disposable);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Disposes this router instance
|
|
113
|
+
*
|
|
114
|
+
* When called, clears resources retained by the router by calling `dispose(this)` on all
|
|
115
|
+
* registered [[disposable]] objects.
|
|
116
|
+
*
|
|
117
|
+
* Or, if a `disposable` object is provided, calls `dispose(this)` on that object only.
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
* @param disposable (optional) the disposable to dispose
|
|
121
|
+
*/
|
|
122
|
+
dispose(disposable) {
|
|
123
|
+
if (disposable && isFunction(disposable.dispose)) {
|
|
124
|
+
disposable.dispose(this);
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
this._disposed = true;
|
|
128
|
+
this._disposables.slice().forEach((d) => {
|
|
129
|
+
try {
|
|
130
|
+
typeof d.dispose === "function" && d.dispose(this);
|
|
131
|
+
removeFrom(this._disposables, d);
|
|
132
|
+
} catch (ignored) {}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Adds a plugin to UI-Router
|
|
138
|
+
*
|
|
139
|
+
* This method adds a UI-Router Plugin.
|
|
140
|
+
* A plugin can enhance or change UI-Router behavior using any public API.
|
|
141
|
+
*
|
|
142
|
+
* #### Example:
|
|
143
|
+
* ```js
|
|
144
|
+
* import { MyCoolPlugin } from "ui-router-cool-plugin";
|
|
145
|
+
*
|
|
146
|
+
* var plugin = router.addPlugin(MyCoolPlugin);
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* ### Plugin authoring
|
|
150
|
+
*
|
|
151
|
+
* A plugin is simply a class (or constructor function) which accepts a [[UIRouter]] instance and (optionally) an options object.
|
|
152
|
+
*
|
|
153
|
+
* The plugin can implement its functionality using any of the public APIs of [[UIRouter]].
|
|
154
|
+
* For example, it may configure router options or add a Transition Hook.
|
|
155
|
+
*
|
|
156
|
+
* The plugin can then be published as a separate module.
|
|
157
|
+
*
|
|
158
|
+
* #### Example:
|
|
159
|
+
* ```js
|
|
160
|
+
* export class MyAuthPlugin implements UIRouterPlugin {
|
|
161
|
+
* constructor(router: UIRouter, options: any) {
|
|
162
|
+
* this.name = "MyAuthPlugin";
|
|
163
|
+
* let $transitions = router.transitionService;
|
|
164
|
+
* let $state = router.stateService;
|
|
165
|
+
*
|
|
166
|
+
* let authCriteria = {
|
|
167
|
+
* to: (state) => state.data && state.data.requiresAuth
|
|
168
|
+
* };
|
|
169
|
+
*
|
|
170
|
+
* function authHook(transition: Transition) {
|
|
171
|
+
* let authService = transition.injector().get('AuthService');
|
|
172
|
+
* if (!authService.isAuthenticated()) {
|
|
173
|
+
* return $state.target('login');
|
|
174
|
+
* }
|
|
175
|
+
* }
|
|
176
|
+
*
|
|
177
|
+
* $transitions.onStart(authCriteria, authHook);
|
|
178
|
+
* }
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @param plugin one of:
|
|
183
|
+
* - a plugin class which implements [[UIRouterPlugin]]
|
|
184
|
+
* - a constructor function for a [[UIRouterPlugin]] which accepts a [[UIRouter]] instance
|
|
185
|
+
* - a factory function which accepts a [[UIRouter]] instance and returns a [[UIRouterPlugin]] instance
|
|
186
|
+
* @param options options to pass to the plugin class/factory
|
|
187
|
+
* @returns the registered plugin instance
|
|
188
|
+
*/
|
|
189
|
+
plugin(plugin, options = {}) {
|
|
190
|
+
const pluginInstance = new plugin(this, options);
|
|
191
|
+
if (!pluginInstance.name)
|
|
192
|
+
throw new Error(
|
|
193
|
+
"Required property `name` missing on plugin: " + pluginInstance,
|
|
194
|
+
);
|
|
195
|
+
this._disposables.push(pluginInstance);
|
|
196
|
+
return (this._plugins[pluginInstance.name] = pluginInstance);
|
|
197
|
+
}
|
|
198
|
+
getPlugin(pluginName) {
|
|
199
|
+
return pluginName
|
|
200
|
+
? this._plugins[pluginName]
|
|
201
|
+
: Object.values(this._plugins);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/\*\*
|
|
2
|
+
|
|
3
|
+
- # The state subsystem
|
|
4
|
+
-
|
|
5
|
+
- This subsystem implements the ui-router state tree
|
|
6
|
+
-
|
|
7
|
+
- - The [[StateService]] has state-related service methods such as:
|
|
8
|
+
- - [[StateService.get]]: Get a registered [[StateDeclaration]] object
|
|
9
|
+
- - [[StateService.go]]: Transition from the current state to a new state
|
|
10
|
+
- - [[StateService.reload]]: Reload the current state
|
|
11
|
+
- - [[StateService.target]]: Get a [[TargetState]] (useful when redirecting from a Transition Hook)
|
|
12
|
+
- - [[StateService.onInvalid]]: Register a callback for when a transition to an invalid state is started
|
|
13
|
+
- - [[StateService.defaultErrorHandler]]: Register a global callback for when a transition errors
|
|
14
|
+
- - The [[StateDeclaration]] interface defines the shape of a state declaration
|
|
15
|
+
- - The [[StateRegistry]] contains all the registered states
|
|
16
|
+
- - States can be added/removed using the [[StateRegistry.register]] and [[StateRegistry.deregister]]
|
|
17
|
+
- - Note: Bootstrap state registration differs by front-end framework.
|
|
18
|
+
- - Get notified of state registration/deregistration using [[StateRegistry.onStatesChanged]].
|
|
19
|
+
-
|
|
20
|
+
- @packageDocumentation
|
|
21
|
+
\*/
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyPairs,
|
|
3
|
+
extend,
|
|
4
|
+
identity,
|
|
5
|
+
inherit,
|
|
6
|
+
mapObj,
|
|
7
|
+
noop,
|
|
8
|
+
omit,
|
|
9
|
+
tail,
|
|
10
|
+
copy,
|
|
11
|
+
} from "../common/common";
|
|
12
|
+
import { isArray, isDefined, isFunction, isString } from "../common/predicates";
|
|
13
|
+
import { stringify } from "../common/strings";
|
|
14
|
+
import { is, pattern, pipe, prop, val } from "../common/hof";
|
|
15
|
+
import { Resolvable } from "../resolve/resolvable";
|
|
16
|
+
import { services } from "../common/coreservices";
|
|
17
|
+
const parseUrl = (url) => {
|
|
18
|
+
if (!isString(url)) return false;
|
|
19
|
+
const root = url.charAt(0) === "^";
|
|
20
|
+
return { val: root ? url.substring(1) : url, root };
|
|
21
|
+
};
|
|
22
|
+
function nameBuilder(state) {
|
|
23
|
+
return state.name;
|
|
24
|
+
}
|
|
25
|
+
function selfBuilder(state) {
|
|
26
|
+
state.self.$$state = () => state;
|
|
27
|
+
return state.self;
|
|
28
|
+
}
|
|
29
|
+
function dataBuilder(state) {
|
|
30
|
+
if (state.parent && state.parent.data) {
|
|
31
|
+
state.data = state.self.data = inherit(state.parent.data, state.data);
|
|
32
|
+
}
|
|
33
|
+
return state.data;
|
|
34
|
+
}
|
|
35
|
+
const getUrlBuilder = ($urlMatcherFactoryProvider, root) =>
|
|
36
|
+
function urlBuilder(stateObject) {
|
|
37
|
+
let stateDec = stateObject.self;
|
|
38
|
+
// For future states, i.e., states whose name ends with `.**`,
|
|
39
|
+
// match anything that starts with the url prefix
|
|
40
|
+
if (
|
|
41
|
+
stateDec &&
|
|
42
|
+
stateDec.url &&
|
|
43
|
+
stateDec.name &&
|
|
44
|
+
stateDec.name.match(/\.\*\*$/)
|
|
45
|
+
) {
|
|
46
|
+
const newStateDec = {};
|
|
47
|
+
copy(stateDec, newStateDec);
|
|
48
|
+
newStateDec.url += "{remainder:any}"; // match any path (.*)
|
|
49
|
+
stateDec = newStateDec;
|
|
50
|
+
}
|
|
51
|
+
const parent = stateObject.parent;
|
|
52
|
+
const parsed = parseUrl(stateDec.url);
|
|
53
|
+
const url = !parsed
|
|
54
|
+
? stateDec.url
|
|
55
|
+
: $urlMatcherFactoryProvider.compile(parsed.val, { state: stateDec });
|
|
56
|
+
if (!url) return null;
|
|
57
|
+
if (!$urlMatcherFactoryProvider.isMatcher(url))
|
|
58
|
+
throw new Error(`Invalid url '${url}' in state '${stateObject}'`);
|
|
59
|
+
return parsed && parsed.root
|
|
60
|
+
? url
|
|
61
|
+
: ((parent && parent.navigable) || root()).url.append(url);
|
|
62
|
+
};
|
|
63
|
+
const getNavigableBuilder = (isRoot) =>
|
|
64
|
+
function navigableBuilder(state) {
|
|
65
|
+
return !isRoot(state) && state.url
|
|
66
|
+
? state
|
|
67
|
+
: state.parent
|
|
68
|
+
? state.parent.navigable
|
|
69
|
+
: null;
|
|
70
|
+
};
|
|
71
|
+
const getParamsBuilder = (paramFactory) =>
|
|
72
|
+
function paramsBuilder(state) {
|
|
73
|
+
const makeConfigParam = (config, id) =>
|
|
74
|
+
paramFactory.fromConfig(id, null, state.self);
|
|
75
|
+
const urlParams =
|
|
76
|
+
(state.url && state.url.parameters({ inherit: false })) || [];
|
|
77
|
+
const nonUrlParams = Object.values(
|
|
78
|
+
mapObj(
|
|
79
|
+
omit(state.params || {}, urlParams.map(prop("id"))),
|
|
80
|
+
makeConfigParam,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
return urlParams
|
|
84
|
+
.concat(nonUrlParams)
|
|
85
|
+
.map((p) => [p.id, p])
|
|
86
|
+
.reduce(applyPairs, {});
|
|
87
|
+
};
|
|
88
|
+
function pathBuilder(state) {
|
|
89
|
+
return state.parent ? state.parent.path.concat(state) : /*root*/ [state];
|
|
90
|
+
}
|
|
91
|
+
function includesBuilder(state) {
|
|
92
|
+
const includes = state.parent ? extend({}, state.parent.includes) : {};
|
|
93
|
+
includes[state.name] = true;
|
|
94
|
+
return includes;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* This is a [[StateBuilder.builder]] function for the `resolve:` block on a [[StateDeclaration]].
|
|
98
|
+
*
|
|
99
|
+
* When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
|
|
100
|
+
* validates the `resolve` property and converts it to a [[Resolvable]] array.
|
|
101
|
+
*
|
|
102
|
+
* resolve: input value can be:
|
|
103
|
+
*
|
|
104
|
+
* {
|
|
105
|
+
* // analyzed but not injected
|
|
106
|
+
* myFooResolve: function() { return "myFooData"; },
|
|
107
|
+
*
|
|
108
|
+
* // function.toString() parsed, "DependencyName" dep as string (not min-safe)
|
|
109
|
+
* myBarResolve: function(DependencyName) { return DependencyName.fetchSomethingAsPromise() },
|
|
110
|
+
*
|
|
111
|
+
* // Array split; "DependencyName" dep as string
|
|
112
|
+
* myBazResolve: [ "DependencyName", function(dep) { return dep.fetchSomethingAsPromise() },
|
|
113
|
+
*
|
|
114
|
+
* // Array split; DependencyType dep as token (compared using ===)
|
|
115
|
+
* myQuxResolve: [ DependencyType, function(dep) { return dep.fetchSometingAsPromise() },
|
|
116
|
+
*
|
|
117
|
+
* // val.$inject used as deps
|
|
118
|
+
* // where:
|
|
119
|
+
* // corgeResolve.$inject = ["DependencyName"];
|
|
120
|
+
* // function corgeResolve(dep) { dep.fetchSometingAsPromise() }
|
|
121
|
+
* // then "DependencyName" dep as string
|
|
122
|
+
* myCorgeResolve: corgeResolve,
|
|
123
|
+
*
|
|
124
|
+
* // inject service by name
|
|
125
|
+
* // When a string is found, desugar creating a resolve that injects the named service
|
|
126
|
+
* myGraultResolve: "SomeService"
|
|
127
|
+
* }
|
|
128
|
+
*
|
|
129
|
+
* or:
|
|
130
|
+
*
|
|
131
|
+
* [
|
|
132
|
+
* new Resolvable("myFooResolve", function() { return "myFooData" }),
|
|
133
|
+
* new Resolvable("myBarResolve", function(dep) { return dep.fetchSomethingAsPromise() }, [ "DependencyName" ]),
|
|
134
|
+
* { provide: "myBazResolve", useFactory: function(dep) { dep.fetchSomethingAsPromise() }, deps: [ "DependencyName" ] }
|
|
135
|
+
* ]
|
|
136
|
+
*/
|
|
137
|
+
export function resolvablesBuilder(state) {
|
|
138
|
+
/** convert resolve: {} and resolvePolicy: {} objects to an array of tuples */
|
|
139
|
+
const objects2Tuples = (resolveObj, resolvePolicies) =>
|
|
140
|
+
Object.keys(resolveObj || {}).map((token) => ({
|
|
141
|
+
token,
|
|
142
|
+
val: resolveObj[token],
|
|
143
|
+
deps: undefined,
|
|
144
|
+
policy: resolvePolicies[token],
|
|
145
|
+
}));
|
|
146
|
+
/** fetch DI annotations from a function or ng1-style array */
|
|
147
|
+
const annotate = (fn) => {
|
|
148
|
+
const $injector = services.$injector;
|
|
149
|
+
// ng1 doesn't have an $injector until runtime.
|
|
150
|
+
// If the $injector doesn't exist, use "deferred" literal as a
|
|
151
|
+
// marker indicating they should be annotated when runtime starts
|
|
152
|
+
return (
|
|
153
|
+
fn["$inject"] ||
|
|
154
|
+
($injector && $injector.annotate(fn, $injector.strictDi)) ||
|
|
155
|
+
"deferred"
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
/** true if the object has both `token` and `resolveFn`, and is probably a [[ResolveLiteral]] */
|
|
159
|
+
const isResolveLiteral = (obj) => !!(obj.token && obj.resolveFn);
|
|
160
|
+
/** true if the object looks like a provide literal, or a ng2 Provider */
|
|
161
|
+
const isLikeNg2Provider = (obj) =>
|
|
162
|
+
!!(
|
|
163
|
+
(obj.provide || obj.token) &&
|
|
164
|
+
(obj.useValue || obj.useFactory || obj.useExisting || obj.useClass)
|
|
165
|
+
);
|
|
166
|
+
/** true if the object looks like a tuple from obj2Tuples */
|
|
167
|
+
const isTupleFromObj = (obj) =>
|
|
168
|
+
!!(
|
|
169
|
+
obj &&
|
|
170
|
+
obj.val &&
|
|
171
|
+
(isString(obj.val) || isArray(obj.val) || isFunction(obj.val))
|
|
172
|
+
);
|
|
173
|
+
/** extracts the token from a Provider or provide literal */
|
|
174
|
+
const getToken = (p) => p.provide || p.token;
|
|
175
|
+
// prettier-ignore: Given a literal resolve or provider object, returns a Resolvable
|
|
176
|
+
const literal2Resolvable = pattern([
|
|
177
|
+
[
|
|
178
|
+
prop("resolveFn"),
|
|
179
|
+
(p) => new Resolvable(getToken(p), p.resolveFn, p.deps, p.policy),
|
|
180
|
+
],
|
|
181
|
+
[
|
|
182
|
+
prop("useFactory"),
|
|
183
|
+
(p) =>
|
|
184
|
+
new Resolvable(
|
|
185
|
+
getToken(p),
|
|
186
|
+
p.useFactory,
|
|
187
|
+
p.deps || p.dependencies,
|
|
188
|
+
p.policy,
|
|
189
|
+
),
|
|
190
|
+
],
|
|
191
|
+
[
|
|
192
|
+
prop("useClass"),
|
|
193
|
+
(p) => new Resolvable(getToken(p), () => new p.useClass(), [], p.policy),
|
|
194
|
+
],
|
|
195
|
+
[
|
|
196
|
+
prop("useValue"),
|
|
197
|
+
(p) =>
|
|
198
|
+
new Resolvable(getToken(p), () => p.useValue, [], p.policy, p.useValue),
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
prop("useExisting"),
|
|
202
|
+
(p) => new Resolvable(getToken(p), identity, [p.useExisting], p.policy),
|
|
203
|
+
],
|
|
204
|
+
]);
|
|
205
|
+
// prettier-ignore
|
|
206
|
+
const tuple2Resolvable = pattern([
|
|
207
|
+
[pipe(prop('val'), isString), (tuple) => new Resolvable(tuple.token, identity, [tuple.val], tuple.policy)],
|
|
208
|
+
[pipe(prop('val'), isArray), (tuple) => new Resolvable(tuple.token, tail(tuple.val), tuple.val.slice(0, -1), tuple.policy)],
|
|
209
|
+
[pipe(prop('val'), isFunction), (tuple) => new Resolvable(tuple.token, tuple.val, annotate(tuple.val), tuple.policy)],
|
|
210
|
+
]);
|
|
211
|
+
// prettier-ignore
|
|
212
|
+
const item2Resolvable = pattern([
|
|
213
|
+
[is(Resolvable), (r) => r],
|
|
214
|
+
[isResolveLiteral, literal2Resolvable],
|
|
215
|
+
[isLikeNg2Provider, literal2Resolvable],
|
|
216
|
+
[isTupleFromObj, tuple2Resolvable],
|
|
217
|
+
[val(true), (obj) => { throw new Error('Invalid resolve value: ' + stringify(obj)); },],
|
|
218
|
+
]);
|
|
219
|
+
// If resolveBlock is already an array, use it as-is.
|
|
220
|
+
// Otherwise, assume it's an object and convert to an Array of tuples
|
|
221
|
+
const decl = state.resolve;
|
|
222
|
+
const items = isArray(decl)
|
|
223
|
+
? decl
|
|
224
|
+
: objects2Tuples(decl, state.resolvePolicy || {});
|
|
225
|
+
return items.map(item2Resolvable);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* A internal global service
|
|
229
|
+
*
|
|
230
|
+
* StateBuilder is a factory for the internal [[StateObject]] objects.
|
|
231
|
+
*
|
|
232
|
+
* When you register a state with the [[StateRegistry]], you register a plain old javascript object which
|
|
233
|
+
* conforms to the [[StateDeclaration]] interface. This factory takes that object and builds the corresponding
|
|
234
|
+
* [[StateObject]] object, which has an API and is used internally.
|
|
235
|
+
*
|
|
236
|
+
* Custom properties or API may be added to the internal [[StateObject]] object by registering a decorator function
|
|
237
|
+
* using the [[builder]] method.
|
|
238
|
+
*/
|
|
239
|
+
export class StateBuilder {
|
|
240
|
+
constructor(matcher, urlMatcherFactory) {
|
|
241
|
+
this.matcher = matcher;
|
|
242
|
+
const self = this;
|
|
243
|
+
const root = () => matcher.find("");
|
|
244
|
+
const isRoot = (state) => state.name === "";
|
|
245
|
+
function parentBuilder(state) {
|
|
246
|
+
if (isRoot(state)) return null;
|
|
247
|
+
return matcher.find(self.parentName(state)) || root();
|
|
248
|
+
}
|
|
249
|
+
this.builders = {
|
|
250
|
+
name: [nameBuilder],
|
|
251
|
+
self: [selfBuilder],
|
|
252
|
+
parent: [parentBuilder],
|
|
253
|
+
data: [dataBuilder],
|
|
254
|
+
// Build a URLMatcher if necessary, either via a relative or absolute URL
|
|
255
|
+
url: [getUrlBuilder(urlMatcherFactory, root)],
|
|
256
|
+
// Keep track of the closest ancestor state that has a URL (i.e. is navigable)
|
|
257
|
+
navigable: [getNavigableBuilder(isRoot)],
|
|
258
|
+
params: [getParamsBuilder(urlMatcherFactory.paramFactory)],
|
|
259
|
+
// Each framework-specific ui-router implementation should define its own `views` builder
|
|
260
|
+
// e.g., src/ng1/statebuilders/views.ts
|
|
261
|
+
views: [],
|
|
262
|
+
// Keep a full path from the root down to this state as this is needed for state activation.
|
|
263
|
+
path: [pathBuilder],
|
|
264
|
+
// Speed up $state.includes() as it's used a lot
|
|
265
|
+
includes: [includesBuilder],
|
|
266
|
+
resolvables: [resolvablesBuilder],
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
builder(name, fn) {
|
|
270
|
+
const builders = this.builders;
|
|
271
|
+
const array = builders[name] || [];
|
|
272
|
+
// Backwards compat: if only one builder exists, return it, else return whole arary.
|
|
273
|
+
if (isString(name) && !isDefined(fn))
|
|
274
|
+
return array.length > 1 ? array : array[0];
|
|
275
|
+
if (!isString(name) || !isFunction(fn)) return;
|
|
276
|
+
builders[name] = array;
|
|
277
|
+
builders[name].push(fn);
|
|
278
|
+
return () => builders[name].splice(builders[name].indexOf(fn, 1)) && null;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Builds all of the properties on an essentially blank State object, returning a State object which has all its
|
|
282
|
+
* properties and API built.
|
|
283
|
+
*
|
|
284
|
+
* @param state an uninitialized State object
|
|
285
|
+
* @returns the built State object
|
|
286
|
+
*/
|
|
287
|
+
build(state) {
|
|
288
|
+
const { matcher, builders } = this;
|
|
289
|
+
const parent = this.parentName(state);
|
|
290
|
+
if (parent && !matcher.find(parent, undefined, false)) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
for (const key in builders) {
|
|
294
|
+
if (!Object.prototype.hasOwnProperty.call(builders, key)) continue;
|
|
295
|
+
const chain = builders[key].reduce(
|
|
296
|
+
(parentFn, step) => (_state) => step(_state, parentFn),
|
|
297
|
+
noop,
|
|
298
|
+
);
|
|
299
|
+
state[key] = chain(state);
|
|
300
|
+
}
|
|
301
|
+
return state;
|
|
302
|
+
}
|
|
303
|
+
parentName(state) {
|
|
304
|
+
// name = 'foo.bar.baz.**'
|
|
305
|
+
const name = state.name || "";
|
|
306
|
+
// segments = ['foo', 'bar', 'baz', '.**']
|
|
307
|
+
const segments = name.split(".");
|
|
308
|
+
// segments = ['foo', 'bar', 'baz']
|
|
309
|
+
const lastSegment = segments.pop();
|
|
310
|
+
// segments = ['foo', 'bar'] (ignore .** segment for future states)
|
|
311
|
+
if (lastSegment === "**") segments.pop();
|
|
312
|
+
if (segments.length) {
|
|
313
|
+
if (state.parent) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`States that specify the 'parent:' property should not have a '.' in their name (${name})`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
// 'foo.bar'
|
|
319
|
+
return segments.join(".");
|
|
320
|
+
}
|
|
321
|
+
if (!state.parent) return "";
|
|
322
|
+
return isString(state.parent) ? state.parent : state.parent.name;
|
|
323
|
+
}
|
|
324
|
+
name(state) {
|
|
325
|
+
const name = state.name;
|
|
326
|
+
if (name.indexOf(".") !== -1 || !state.parent) return name;
|
|
327
|
+
const parentName = isString(state.parent)
|
|
328
|
+
? state.parent
|
|
329
|
+
: state.parent.name;
|
|
330
|
+
return parentName ? parentName + "." + name : name;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { isString } from "../common/predicates";
|
|
2
|
+
import { safeConsole } from "../common/safeConsole";
|
|
3
|
+
export class StateMatcher {
|
|
4
|
+
constructor(_states) {
|
|
5
|
+
this._states = _states;
|
|
6
|
+
}
|
|
7
|
+
isRelative(stateName) {
|
|
8
|
+
stateName = stateName || "";
|
|
9
|
+
return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
|
|
10
|
+
}
|
|
11
|
+
find(stateOrName, base, matchGlob = true) {
|
|
12
|
+
if (!stateOrName && stateOrName !== "") return undefined;
|
|
13
|
+
const isStr = isString(stateOrName);
|
|
14
|
+
let name = isStr ? stateOrName : stateOrName.name;
|
|
15
|
+
if (this.isRelative(name)) name = this.resolvePath(name, base);
|
|
16
|
+
const state = this._states[name];
|
|
17
|
+
if (
|
|
18
|
+
state &&
|
|
19
|
+
(isStr ||
|
|
20
|
+
(!isStr && (state === stateOrName || state.self === stateOrName)))
|
|
21
|
+
) {
|
|
22
|
+
return state;
|
|
23
|
+
} else if (isStr && matchGlob) {
|
|
24
|
+
const _states = Object.values(this._states);
|
|
25
|
+
const matches = _states.filter(
|
|
26
|
+
(_state) =>
|
|
27
|
+
_state.__stateObjectCache.nameGlob &&
|
|
28
|
+
_state.__stateObjectCache.nameGlob.matches(name),
|
|
29
|
+
);
|
|
30
|
+
if (matches.length > 1) {
|
|
31
|
+
safeConsole.error(
|
|
32
|
+
`stateMatcher.find: Found multiple matches for ${name} using glob: `,
|
|
33
|
+
matches.map((match) => match.name),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return matches[0];
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
resolvePath(name, base) {
|
|
41
|
+
if (!base) throw new Error(`No reference point given for path '${name}'`);
|
|
42
|
+
const baseState = this.find(base);
|
|
43
|
+
const splitName = name.split(".");
|
|
44
|
+
const pathLength = splitName.length;
|
|
45
|
+
let i = 0,
|
|
46
|
+
current = baseState;
|
|
47
|
+
for (; i < pathLength; i++) {
|
|
48
|
+
if (splitName[i] === "" && i === 0) {
|
|
49
|
+
current = baseState;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (splitName[i] === "^") {
|
|
53
|
+
if (!current.parent)
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Path '${name}' not valid for state '${baseState.name}'`,
|
|
56
|
+
);
|
|
57
|
+
current = current.parent;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
const relName = splitName.slice(i).join(".");
|
|
63
|
+
return current.name + (current.name && relName ? "." : "") + relName;
|
|
64
|
+
}
|
|
65
|
+
}
|