@angular-wave/angular.ts 0.0.11 → 0.0.12
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/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 +86 -0
- package/src/router/adapter/services.js +132 -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/urlRouterProvider.js +196 -0
- package/src/router/adapter/viewScroll.js +31 -0
- package/src/router/core/common/common.js +506 -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 +200 -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 +201 -0
- package/src/router/core/state/README.md +21 -0
- package/src/router/core/state/stateBuilder.js +333 -0
- package/src/router/core/state/stateMatcher.js +66 -0
- package/src/router/core/state/stateObject.js +116 -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 +182 -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/vanilla/baseLocationService.js +31 -0
- package/src/router/core/vanilla/browserLocationConfig.js +42 -0
- package/src/router/core/vanilla/hashLocationService.js +19 -0
- package/src/router/core/vanilla/injector.js +98 -0
- package/src/router/core/vanilla/interface.js +1 -0
- package/src/router/core/vanilla/memoryLocationConfig.js +20 -0
- package/src/router/core/vanilla/memoryLocationService.js +13 -0
- package/src/router/core/vanilla/plugins.js +35 -0
- package/src/router/core/vanilla/pushStateLocationService.js +69 -0
- package/src/router/core/vanilla/q.js +54 -0
- package/src/router/core/vanilla/utils.js +63 -0
- package/src/router/core/view/interface.js +1 -0
- package/src/router/core/view/view.js +312 -0
- package/src/router/router.js +52 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { extend, isString } from "../../../core/utils";
|
|
2
|
+
import { is, pattern } from "../common/hof";
|
|
3
|
+
import { UrlRules } from "./urlRules";
|
|
4
|
+
import { UrlConfig } from "./urlConfig";
|
|
5
|
+
import { TargetState } from "../state/targetState";
|
|
6
|
+
/**
|
|
7
|
+
* API for URL management
|
|
8
|
+
*/
|
|
9
|
+
export class UrlService {
|
|
10
|
+
/** @internal */
|
|
11
|
+
constructor(/** @internal */ router) {
|
|
12
|
+
this.router = router;
|
|
13
|
+
/** @internal */ this.interceptDeferred = false;
|
|
14
|
+
/**
|
|
15
|
+
* The nested [[UrlRules]] API for managing URL rules and rewrites
|
|
16
|
+
*
|
|
17
|
+
* See: [[UrlRules]] for details
|
|
18
|
+
*/
|
|
19
|
+
this.rules = new UrlRules(this.router);
|
|
20
|
+
/**
|
|
21
|
+
* The nested [[UrlConfig]] API to configure the URL and retrieve URL information
|
|
22
|
+
*
|
|
23
|
+
* See: [[UrlConfig]] for details
|
|
24
|
+
*/
|
|
25
|
+
this.config = new UrlConfig(this.router);
|
|
26
|
+
// Delegate these calls to the current LocationServices implementation
|
|
27
|
+
/**
|
|
28
|
+
* Gets the current url, or updates the url
|
|
29
|
+
*
|
|
30
|
+
* ### Getting the current URL
|
|
31
|
+
*
|
|
32
|
+
* When no arguments are passed, returns the current URL.
|
|
33
|
+
* The URL is normalized using the internal [[path]]/[[search]]/[[hash]] values.
|
|
34
|
+
*
|
|
35
|
+
* For example, the URL may be stored in the hash ([[HashLocationServices]]) or
|
|
36
|
+
* have a base HREF prepended ([[PushStateLocationServices]]).
|
|
37
|
+
*
|
|
38
|
+
* The raw URL in the browser might be:
|
|
39
|
+
*
|
|
40
|
+
* ```
|
|
41
|
+
* http://mysite.com/somepath/index.html#/internal/path/123?param1=foo#anchor
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* or
|
|
45
|
+
*
|
|
46
|
+
* ```
|
|
47
|
+
* http://mysite.com/basepath/internal/path/123?param1=foo#anchor
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* then this method returns:
|
|
51
|
+
*
|
|
52
|
+
* ```
|
|
53
|
+
* /internal/path/123?param1=foo#anchor
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
*
|
|
57
|
+
* #### Example:
|
|
58
|
+
* ```js
|
|
59
|
+
* locationServices.url(); // "/some/path?query=value#anchor"
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* ### Updating the URL
|
|
63
|
+
*
|
|
64
|
+
* When `newurl` arguments is provided, changes the URL to reflect `newurl`
|
|
65
|
+
*
|
|
66
|
+
* #### Example:
|
|
67
|
+
* ```js
|
|
68
|
+
* locationServices.url("/some/path?query=value#anchor", true);
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @param newurl The new value for the URL.
|
|
72
|
+
* This url should reflect only the new internal [[path]], [[search]], and [[hash]] values.
|
|
73
|
+
* It should not include the protocol, site, port, or base path of an absolute HREF.
|
|
74
|
+
* @param replace When true, replaces the current history entry (instead of appending it) with this new url
|
|
75
|
+
* @param state The history's state object, i.e., pushState (if the LocationServices implementation supports it)
|
|
76
|
+
*
|
|
77
|
+
* @return the url (after potentially being processed)
|
|
78
|
+
*/
|
|
79
|
+
this.url = (newurl, replace, state) =>
|
|
80
|
+
this.router.locationService.url(newurl, replace, state);
|
|
81
|
+
/**
|
|
82
|
+
* Gets the path part of the current url
|
|
83
|
+
*
|
|
84
|
+
* If the current URL is `/some/path?query=value#anchor`, this returns `/some/path`
|
|
85
|
+
*
|
|
86
|
+
* @return the path portion of the url
|
|
87
|
+
*/
|
|
88
|
+
this.path = () => this.router.locationService.path();
|
|
89
|
+
/**
|
|
90
|
+
* Gets the search part of the current url as an object
|
|
91
|
+
*
|
|
92
|
+
* If the current URL is `/some/path?query=value#anchor`, this returns `{ query: 'value' }`
|
|
93
|
+
*
|
|
94
|
+
* @return the search (query) portion of the url, as an object
|
|
95
|
+
*/
|
|
96
|
+
this.search = () => this.router.locationService.search();
|
|
97
|
+
/**
|
|
98
|
+
* Gets the hash part of the current url
|
|
99
|
+
*
|
|
100
|
+
* If the current URL is `/some/path?query=value#anchor`, this returns `anchor`
|
|
101
|
+
*
|
|
102
|
+
* @return the hash (anchor) portion of the url
|
|
103
|
+
*/
|
|
104
|
+
this.hash = () => this.router.locationService.hash();
|
|
105
|
+
/**
|
|
106
|
+
* @internal
|
|
107
|
+
*
|
|
108
|
+
* Registers a low level url change handler
|
|
109
|
+
*
|
|
110
|
+
* Note: Because this is a low level handler, it's not recommended for general use.
|
|
111
|
+
*
|
|
112
|
+
* #### Example:
|
|
113
|
+
* ```js
|
|
114
|
+
* let deregisterFn = locationServices.onChange((evt) => console.log("url change", evt));
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @param callback a function that will be called when the url is changing
|
|
118
|
+
* @return a function that de-registers the callback
|
|
119
|
+
*/
|
|
120
|
+
this.onChange = (callback) =>
|
|
121
|
+
this.router.locationService.onChange(callback);
|
|
122
|
+
}
|
|
123
|
+
/** @internal */
|
|
124
|
+
dispose() {
|
|
125
|
+
this.listen(false);
|
|
126
|
+
this.rules.dispose();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Gets the current URL parts
|
|
130
|
+
*
|
|
131
|
+
* This method returns the different parts of the current URL (the [[path]], [[search]], and [[hash]]) as a [[UrlParts]] object.
|
|
132
|
+
*/
|
|
133
|
+
parts() {
|
|
134
|
+
return { path: this.path(), search: this.search(), hash: this.hash() };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Activates the best rule for the current URL
|
|
138
|
+
*
|
|
139
|
+
* Checks the current URL for a matching [[UrlRule]], then invokes that rule's handler.
|
|
140
|
+
* This method is called internally any time the URL has changed.
|
|
141
|
+
*
|
|
142
|
+
* This effectively activates the state (or redirect, etc) which matches the current URL.
|
|
143
|
+
*
|
|
144
|
+
* #### Example:
|
|
145
|
+
* ```js
|
|
146
|
+
* urlService.deferIntercept();
|
|
147
|
+
*
|
|
148
|
+
* fetch('/states.json').then(resp => resp.json()).then(data => {
|
|
149
|
+
* data.forEach(state => $stateRegistry.register(state));
|
|
150
|
+
* urlService.listen();
|
|
151
|
+
* // Find the matching URL and invoke the handler.
|
|
152
|
+
* urlService.sync();
|
|
153
|
+
* });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
sync(evt) {
|
|
157
|
+
if (evt && evt.defaultPrevented) return;
|
|
158
|
+
const { urlService, stateService } = this.router;
|
|
159
|
+
const url = {
|
|
160
|
+
path: urlService.path(),
|
|
161
|
+
search: urlService.search(),
|
|
162
|
+
hash: urlService.hash(),
|
|
163
|
+
};
|
|
164
|
+
const best = this.match(url);
|
|
165
|
+
const applyResult = pattern([
|
|
166
|
+
[isString, (newurl) => urlService.url(newurl, true)],
|
|
167
|
+
[
|
|
168
|
+
TargetState.isDef,
|
|
169
|
+
(def) => stateService.go(def.state, def.params, def.options),
|
|
170
|
+
],
|
|
171
|
+
[
|
|
172
|
+
is(TargetState),
|
|
173
|
+
(target) =>
|
|
174
|
+
stateService.go(target.state(), target.params(), target.options()),
|
|
175
|
+
],
|
|
176
|
+
]);
|
|
177
|
+
applyResult(best && best.rule.handler(best.match, url, this.router));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Starts or stops listening for URL changes
|
|
181
|
+
*
|
|
182
|
+
* Call this sometime after calling [[deferIntercept]] to start monitoring the url.
|
|
183
|
+
* This causes UI-Router to start listening for changes to the URL, if it wasn't already listening.
|
|
184
|
+
*
|
|
185
|
+
* If called with `false`, UI-Router will stop listening (call listen(true) to start listening again).
|
|
186
|
+
*
|
|
187
|
+
* #### Example:
|
|
188
|
+
* ```js
|
|
189
|
+
* urlService.deferIntercept();
|
|
190
|
+
*
|
|
191
|
+
* fetch('/states.json').then(resp => resp.json()).then(data => {
|
|
192
|
+
* data.forEach(state => $stateRegistry.register(state));
|
|
193
|
+
* // Start responding to URL changes
|
|
194
|
+
* urlService.listen();
|
|
195
|
+
* urlService.sync();
|
|
196
|
+
* });
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* @param enabled `true` or `false` to start or stop listening to URL changes
|
|
200
|
+
*/
|
|
201
|
+
listen(enabled) {
|
|
202
|
+
if (enabled === false) {
|
|
203
|
+
this._stopListeningFn && this._stopListeningFn();
|
|
204
|
+
delete this._stopListeningFn;
|
|
205
|
+
} else {
|
|
206
|
+
return (this._stopListeningFn =
|
|
207
|
+
this._stopListeningFn ||
|
|
208
|
+
this.router.urlService.onChange((evt) => this.sync(evt)));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Disables monitoring of the URL.
|
|
213
|
+
*
|
|
214
|
+
* Call this method before UI-Router has bootstrapped.
|
|
215
|
+
* It will stop UI-Router from performing the initial url sync.
|
|
216
|
+
*
|
|
217
|
+
* This can be useful to perform some asynchronous initialization before the router starts.
|
|
218
|
+
* Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL.
|
|
219
|
+
*
|
|
220
|
+
* #### Example:
|
|
221
|
+
* ```js
|
|
222
|
+
* // Prevent UI-Router from automatically intercepting URL changes when it starts;
|
|
223
|
+
* urlService.deferIntercept();
|
|
224
|
+
*
|
|
225
|
+
* fetch('/states.json').then(resp => resp.json()).then(data => {
|
|
226
|
+
* data.forEach(state => $stateRegistry.register(state));
|
|
227
|
+
* urlService.listen();
|
|
228
|
+
* urlService.sync();
|
|
229
|
+
* });
|
|
230
|
+
* ```
|
|
231
|
+
*
|
|
232
|
+
* @param defer Indicates whether to defer location change interception.
|
|
233
|
+
* Passing no parameter is equivalent to `true`.
|
|
234
|
+
*/
|
|
235
|
+
deferIntercept(defer) {
|
|
236
|
+
if (defer === undefined) defer = true;
|
|
237
|
+
this.interceptDeferred = defer;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Matches a URL
|
|
241
|
+
*
|
|
242
|
+
* Given a URL (as a [[UrlParts]] object), check all rules and determine the best matching rule.
|
|
243
|
+
* Return the result as a [[MatchResult]].
|
|
244
|
+
*/
|
|
245
|
+
match(url) {
|
|
246
|
+
url = extend({ path: "", search: {}, hash: "" }, url);
|
|
247
|
+
const rules = this.rules.rules();
|
|
248
|
+
// Checks a single rule. Returns { rule: rule, match: match, weight: weight } if it matched, or undefined
|
|
249
|
+
const checkRule = (rule) => {
|
|
250
|
+
const match = rule.match(url, this.router);
|
|
251
|
+
return match && { match, rule, weight: rule.matchPriority(match) };
|
|
252
|
+
};
|
|
253
|
+
// The rules are pre-sorted.
|
|
254
|
+
// - Find the first matching rule.
|
|
255
|
+
// - Find any other matching rule that sorted *exactly the same*, according to `.sort()`.
|
|
256
|
+
// - Choose the rule with the highest match weight.
|
|
257
|
+
let best;
|
|
258
|
+
for (let i = 0; i < rules.length; i++) {
|
|
259
|
+
// Stop when there is a 'best' rule and the next rule sorts differently than it.
|
|
260
|
+
if (best && best.rule._group !== rules[i]._group) break;
|
|
261
|
+
const current = checkRule(rules[i]);
|
|
262
|
+
// Pick the best MatchResult
|
|
263
|
+
best =
|
|
264
|
+
!best || (current && current.weight > best.weight) ? current : best;
|
|
265
|
+
}
|
|
266
|
+
return best;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { deregAll, isDefined, removeFrom, root } from "../common/index";
|
|
2
|
+
import { buildUrl, getParams, parseUrl } from "./utils";
|
|
3
|
+
/** A base `LocationServices` */
|
|
4
|
+
export class BaseLocationServices {
|
|
5
|
+
constructor(router, fireAfterUpdate) {
|
|
6
|
+
this.fireAfterUpdate = fireAfterUpdate;
|
|
7
|
+
this._listeners = [];
|
|
8
|
+
this._listener = (evt) => this._listeners.forEach((cb) => cb(evt));
|
|
9
|
+
this.hash = () => parseUrl(this._get()).hash;
|
|
10
|
+
this.path = () => parseUrl(this._get()).path;
|
|
11
|
+
this.search = () => getParams(parseUrl(this._get()).search);
|
|
12
|
+
this._location = root.location;
|
|
13
|
+
this._history = root.history;
|
|
14
|
+
}
|
|
15
|
+
url(url, replace = true) {
|
|
16
|
+
if (isDefined(url) && url !== this._get()) {
|
|
17
|
+
this._set(null, null, url, replace);
|
|
18
|
+
if (this.fireAfterUpdate) {
|
|
19
|
+
this._listeners.forEach((cb) => cb({ url }));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return buildUrl(this);
|
|
23
|
+
}
|
|
24
|
+
onChange(cb) {
|
|
25
|
+
this._listeners.push(cb);
|
|
26
|
+
return () => removeFrom(this._listeners, cb);
|
|
27
|
+
}
|
|
28
|
+
dispose(router) {
|
|
29
|
+
deregAll(this._listeners);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { isDefined, isUndefined } from "../common/predicates";
|
|
2
|
+
/** A `LocationConfig` that delegates to the browser's `location` object */
|
|
3
|
+
export class BrowserLocationConfig {
|
|
4
|
+
constructor(router, _isHtml5 = false) {
|
|
5
|
+
this._isHtml5 = _isHtml5;
|
|
6
|
+
this._baseHref = undefined;
|
|
7
|
+
this._hashPrefix = "";
|
|
8
|
+
}
|
|
9
|
+
port() {
|
|
10
|
+
if (location.port) {
|
|
11
|
+
return Number(location.port);
|
|
12
|
+
}
|
|
13
|
+
return this.protocol() === "https" ? 443 : 80;
|
|
14
|
+
}
|
|
15
|
+
protocol() {
|
|
16
|
+
return location.protocol.replace(/:/g, "");
|
|
17
|
+
}
|
|
18
|
+
host() {
|
|
19
|
+
return location.hostname;
|
|
20
|
+
}
|
|
21
|
+
html5Mode() {
|
|
22
|
+
return this._isHtml5;
|
|
23
|
+
}
|
|
24
|
+
hashPrefix(newprefix) {
|
|
25
|
+
return isDefined(newprefix)
|
|
26
|
+
? (this._hashPrefix = newprefix)
|
|
27
|
+
: this._hashPrefix;
|
|
28
|
+
}
|
|
29
|
+
baseHref(href) {
|
|
30
|
+
if (isDefined(href)) this._baseHref = href;
|
|
31
|
+
if (isUndefined(this._baseHref)) this._baseHref = this.getBaseHref();
|
|
32
|
+
return this._baseHref;
|
|
33
|
+
}
|
|
34
|
+
getBaseHref() {
|
|
35
|
+
const baseTag = document.getElementsByTagName("base")[0];
|
|
36
|
+
if (baseTag && baseTag.href) {
|
|
37
|
+
return baseTag.href.replace(/^([^/:]*:)?\/\/[^/]*/, "");
|
|
38
|
+
}
|
|
39
|
+
return this._isHtml5 ? "/" : location.pathname || "/";
|
|
40
|
+
}
|
|
41
|
+
dispose() {}
|
|
42
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { root, trimHashVal } from "../common/index";
|
|
2
|
+
import { BaseLocationServices } from "./baseLocationService";
|
|
3
|
+
/** A `LocationServices` that uses the browser hash "#" to get/set the current location */
|
|
4
|
+
export class HashLocationService extends BaseLocationServices {
|
|
5
|
+
constructor(router) {
|
|
6
|
+
super(router, false);
|
|
7
|
+
root.addEventListener("hashchange", this._listener, false);
|
|
8
|
+
}
|
|
9
|
+
_get() {
|
|
10
|
+
return trimHashVal(this._location.hash);
|
|
11
|
+
}
|
|
12
|
+
_set(state, title, url, replace) {
|
|
13
|
+
this._location.hash = url;
|
|
14
|
+
}
|
|
15
|
+
dispose(router) {
|
|
16
|
+
super.dispose(router);
|
|
17
|
+
root.removeEventListener("hashchange", this._listener);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extend,
|
|
3
|
+
assertPredicate,
|
|
4
|
+
isFunction,
|
|
5
|
+
isArray,
|
|
6
|
+
isInjectable,
|
|
7
|
+
} from "../common/index";
|
|
8
|
+
// globally available injectables
|
|
9
|
+
const globals = {};
|
|
10
|
+
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
|
|
11
|
+
const ARGUMENT_NAMES = /([^\s,]+)/g;
|
|
12
|
+
/**
|
|
13
|
+
* A basic angular1-like injector api
|
|
14
|
+
*
|
|
15
|
+
* This object implements four methods similar to the
|
|
16
|
+
* [angular 1 dependency injector](https://docs.angularjs.org/api/auto/service/$injector)
|
|
17
|
+
*
|
|
18
|
+
* UI-Router evolved from an angular 1 library to a framework agnostic library.
|
|
19
|
+
* However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection.
|
|
20
|
+
*
|
|
21
|
+
* This object provides a naive implementation of a globally scoped dependency injection system.
|
|
22
|
+
* It supports the following DI approaches:
|
|
23
|
+
*
|
|
24
|
+
* ### Function parameter names
|
|
25
|
+
*
|
|
26
|
+
* A function's `.toString()` is called, and the parameter names are parsed.
|
|
27
|
+
* This only works when the parameter names aren't "mangled" by a minifier such as UglifyJS.
|
|
28
|
+
*
|
|
29
|
+
* ```js
|
|
30
|
+
* function injectedFunction(FooService, BarService) {
|
|
31
|
+
* // FooService and BarService are injected
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ### Function annotation
|
|
36
|
+
*
|
|
37
|
+
* A function may be annotated with an array of dependency names as the `$inject` property.
|
|
38
|
+
*
|
|
39
|
+
* ```js
|
|
40
|
+
* injectedFunction.$inject = [ 'FooService', 'BarService' ];
|
|
41
|
+
* function injectedFunction(fs, bs) {
|
|
42
|
+
* // FooService and BarService are injected as fs and bs parameters
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* ### Array notation
|
|
47
|
+
*
|
|
48
|
+
* An array provides the names of the dependencies to inject (as strings).
|
|
49
|
+
* The function is the last element of the array.
|
|
50
|
+
*
|
|
51
|
+
* ```js
|
|
52
|
+
* [ 'FooService', 'BarService', function (fs, bs) {
|
|
53
|
+
* // FooService and BarService are injected as fs and bs parameters
|
|
54
|
+
* }]
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @type {$InjectorLike}
|
|
58
|
+
*/
|
|
59
|
+
export const $injector = {
|
|
60
|
+
/** Gets an object from DI based on a string token */
|
|
61
|
+
get: (name) => globals[name],
|
|
62
|
+
/** Returns true if an object named `name` exists in global DI */
|
|
63
|
+
has: (name) => $injector.get(name) != null,
|
|
64
|
+
/**
|
|
65
|
+
* Injects a function
|
|
66
|
+
*
|
|
67
|
+
* @param fn the function to inject
|
|
68
|
+
* @param context the function's `this` binding
|
|
69
|
+
* @param locals An object with additional DI tokens and values, such as `{ someToken: { foo: 1 } }`
|
|
70
|
+
*/
|
|
71
|
+
invoke: (fn, context, locals) => {
|
|
72
|
+
const all = extend({}, globals, locals || {});
|
|
73
|
+
const params = $injector.annotate(fn);
|
|
74
|
+
const ensureExist = assertPredicate(
|
|
75
|
+
(key) => all.hasOwnProperty(key),
|
|
76
|
+
(key) => `DI can't find injectable: '${key}'`,
|
|
77
|
+
);
|
|
78
|
+
const args = params.filter(ensureExist).map((x) => all[x]);
|
|
79
|
+
if (isFunction(fn)) return fn.apply(context, args);
|
|
80
|
+
else return fn.slice(-1)[0].apply(context, args);
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Returns a function's dependencies
|
|
84
|
+
*
|
|
85
|
+
* Analyzes a function (or array) and returns an array of DI tokens that the function requires.
|
|
86
|
+
* @return an array of `string`s
|
|
87
|
+
*/
|
|
88
|
+
annotate: (fn) => {
|
|
89
|
+
if (!isInjectable(fn)) throw new Error(`Not an injectable function: ${fn}`);
|
|
90
|
+
if (fn && fn.$inject) return fn.$inject;
|
|
91
|
+
if (isArray(fn)) return fn.slice(0, -1);
|
|
92
|
+
const fnStr = fn.toString().replace(STRIP_COMMENTS, "");
|
|
93
|
+
const result = fnStr
|
|
94
|
+
.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")"))
|
|
95
|
+
.match(ARGUMENT_NAMES);
|
|
96
|
+
return result || [];
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isDefined } from "../common/predicates";
|
|
2
|
+
import { noop } from "../common/index";
|
|
3
|
+
/** A `LocationConfig` mock that gets/sets all config from an in-memory object */
|
|
4
|
+
export class MemoryLocationConfig {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.dispose = noop;
|
|
7
|
+
this._baseHref = "";
|
|
8
|
+
this._port = 80;
|
|
9
|
+
this._protocol = "http";
|
|
10
|
+
this._host = "localhost";
|
|
11
|
+
this._hashPrefix = "";
|
|
12
|
+
this.port = () => this._port;
|
|
13
|
+
this.protocol = () => this._protocol;
|
|
14
|
+
this.host = () => this._host;
|
|
15
|
+
this.baseHref = () => this._baseHref;
|
|
16
|
+
this.html5Mode = () => false;
|
|
17
|
+
this.hashPrefix = (newval) =>
|
|
18
|
+
isDefined(newval) ? (this._hashPrefix = newval) : this._hashPrefix;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseLocationServices } from "./baseLocationService";
|
|
2
|
+
/** A `LocationServices` that gets/sets the current location from an in-memory object */
|
|
3
|
+
export class MemoryLocationService extends BaseLocationServices {
|
|
4
|
+
constructor(router) {
|
|
5
|
+
super(router, true);
|
|
6
|
+
}
|
|
7
|
+
_get() {
|
|
8
|
+
return this._url;
|
|
9
|
+
}
|
|
10
|
+
_set(state, title, url, replace) {
|
|
11
|
+
this._url = url;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { BrowserLocationConfig } from "./browserLocationConfig";
|
|
2
|
+
import { HashLocationService } from "./hashLocationService";
|
|
3
|
+
import { locationPluginFactory } from "./utils";
|
|
4
|
+
import { PushStateLocationService } from "./pushStateLocationService";
|
|
5
|
+
import { MemoryLocationService } from "./memoryLocationService";
|
|
6
|
+
import { MemoryLocationConfig } from "./memoryLocationConfig";
|
|
7
|
+
import { $injector } from "./injector";
|
|
8
|
+
import { $q } from "./q";
|
|
9
|
+
import { services } from "../common/coreservices";
|
|
10
|
+
export function servicesPlugin(router) {
|
|
11
|
+
services.$injector = $injector;
|
|
12
|
+
services.$q = $q;
|
|
13
|
+
return { name: "vanilla.services", $q, $injector, dispose: () => null };
|
|
14
|
+
}
|
|
15
|
+
/** A `UIRouterPlugin` uses the browser hash to get/set the current location */
|
|
16
|
+
export const hashLocationPlugin = locationPluginFactory(
|
|
17
|
+
"vanilla.hashBangLocation",
|
|
18
|
+
false,
|
|
19
|
+
HashLocationService,
|
|
20
|
+
BrowserLocationConfig,
|
|
21
|
+
);
|
|
22
|
+
/** A `UIRouterPlugin` that gets/sets the current location using the browser's `location` and `history` apis */
|
|
23
|
+
export const pushStateLocationPlugin = locationPluginFactory(
|
|
24
|
+
"vanilla.pushStateLocation",
|
|
25
|
+
true,
|
|
26
|
+
PushStateLocationService,
|
|
27
|
+
BrowserLocationConfig,
|
|
28
|
+
);
|
|
29
|
+
/** A `UIRouterPlugin` that gets/sets the current location from an in-memory object */
|
|
30
|
+
export const memoryLocationPlugin = locationPluginFactory(
|
|
31
|
+
"vanilla.memoryLocation",
|
|
32
|
+
false,
|
|
33
|
+
MemoryLocationService,
|
|
34
|
+
MemoryLocationConfig,
|
|
35
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { BaseLocationServices } from "./baseLocationService";
|
|
2
|
+
import {
|
|
3
|
+
root,
|
|
4
|
+
splitHash,
|
|
5
|
+
splitQuery,
|
|
6
|
+
stripLastPathElement,
|
|
7
|
+
} from "../common/index";
|
|
8
|
+
/**
|
|
9
|
+
* A `LocationServices` that gets/sets the current location using the browser's `location` and `history` apis
|
|
10
|
+
*
|
|
11
|
+
* Uses `history.pushState` and `history.replaceState`
|
|
12
|
+
*/
|
|
13
|
+
export class PushStateLocationService extends BaseLocationServices {
|
|
14
|
+
constructor(router) {
|
|
15
|
+
super(router, true);
|
|
16
|
+
this._config = router.urlService.config;
|
|
17
|
+
root.addEventListener("popstate", this._listener, false);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Gets the base prefix without:
|
|
21
|
+
* - trailing slash
|
|
22
|
+
* - trailing filename
|
|
23
|
+
* - protocol and hostname
|
|
24
|
+
*
|
|
25
|
+
* If <base href='/base/'>, this returns '/base'.
|
|
26
|
+
* If <base href='/foo/base/'>, this returns '/foo/base'.
|
|
27
|
+
* If <base href='/base/index.html'>, this returns '/base'.
|
|
28
|
+
* If <base href='http://localhost:8080/base/index.html'>, this returns '/base'.
|
|
29
|
+
* If <base href='/base'>, this returns ''.
|
|
30
|
+
* If <base href='http://localhost:8080'>, this returns ''.
|
|
31
|
+
* If <base href='http://localhost:8080/'>, this returns ''.
|
|
32
|
+
*
|
|
33
|
+
* See: https://html.spec.whatwg.org/dev/semantics.html#the-base-element
|
|
34
|
+
*/
|
|
35
|
+
_getBasePrefix() {
|
|
36
|
+
return stripLastPathElement(this._config.baseHref());
|
|
37
|
+
}
|
|
38
|
+
_get() {
|
|
39
|
+
let { pathname, hash, search } = this._location;
|
|
40
|
+
search = splitQuery(search)[1]; // strip ? if found
|
|
41
|
+
hash = splitHash(hash)[1]; // strip # if found
|
|
42
|
+
const basePrefix = this._getBasePrefix();
|
|
43
|
+
const exactBaseHrefMatch = pathname === this._config.baseHref();
|
|
44
|
+
const startsWithBase = pathname.substr(0, basePrefix.length) === basePrefix;
|
|
45
|
+
pathname = exactBaseHrefMatch
|
|
46
|
+
? "/"
|
|
47
|
+
: startsWithBase
|
|
48
|
+
? pathname.substring(basePrefix.length)
|
|
49
|
+
: pathname;
|
|
50
|
+
return pathname + (search ? "?" + search : "") + (hash ? "#" + hash : "");
|
|
51
|
+
}
|
|
52
|
+
_set(state, title, url, replace) {
|
|
53
|
+
const basePrefix = this._getBasePrefix();
|
|
54
|
+
const slash = url && url[0] !== "/" ? "/" : "";
|
|
55
|
+
const fullUrl =
|
|
56
|
+
url === "" || url === "/"
|
|
57
|
+
? this._config.baseHref()
|
|
58
|
+
: basePrefix + slash + url;
|
|
59
|
+
if (replace) {
|
|
60
|
+
this._history.replaceState(state, title, fullUrl);
|
|
61
|
+
} else {
|
|
62
|
+
this._history.pushState(state, title, fullUrl);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
dispose(router) {
|
|
66
|
+
super.dispose(router);
|
|
67
|
+
root.removeEventListener("popstate", this._listener);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isArray, isObject } from "../common/index";
|
|
2
|
+
/**
|
|
3
|
+
* An angular1-like promise api
|
|
4
|
+
*
|
|
5
|
+
* This object implements four methods similar to the
|
|
6
|
+
* [angular 1 promise api](https://docs.angularjs.org/api/ng/service/$q)
|
|
7
|
+
*
|
|
8
|
+
* UI-Router evolved from an angular 1 library to a framework agnostic library.
|
|
9
|
+
* However, some of the `@uirouter/core` code uses these ng1 style APIs to support ng1 style dependency injection.
|
|
10
|
+
*
|
|
11
|
+
* This API provides native ES6 promise support wrapped as a $q-like API.
|
|
12
|
+
* Internally, UI-Router uses this $q object to perform promise operations.
|
|
13
|
+
* The `angular-ui-router` (ui-router for angular 1) uses the $q API provided by angular.
|
|
14
|
+
*
|
|
15
|
+
* $q-like promise api
|
|
16
|
+
*/
|
|
17
|
+
export const $q = {
|
|
18
|
+
/** Normalizes a value as a promise */
|
|
19
|
+
when: (val) => new Promise((resolve, reject) => resolve(val)),
|
|
20
|
+
/** Normalizes a value as a promise rejection */
|
|
21
|
+
reject: (val) =>
|
|
22
|
+
new Promise((resolve, reject) => {
|
|
23
|
+
reject(val);
|
|
24
|
+
}),
|
|
25
|
+
/** @returns a deferred object, which has `resolve` and `reject` functions */
|
|
26
|
+
defer: () => {
|
|
27
|
+
const deferred = {};
|
|
28
|
+
deferred.promise = new Promise((resolve, reject) => {
|
|
29
|
+
deferred.resolve = resolve;
|
|
30
|
+
deferred.reject = reject;
|
|
31
|
+
});
|
|
32
|
+
return deferred;
|
|
33
|
+
},
|
|
34
|
+
/** Like Promise.all(), but also supports object key/promise notation like $q */
|
|
35
|
+
all: (promises) => {
|
|
36
|
+
if (isArray(promises)) {
|
|
37
|
+
return Promise.all(promises);
|
|
38
|
+
}
|
|
39
|
+
if (isObject(promises)) {
|
|
40
|
+
// Convert promises map to promises array.
|
|
41
|
+
// When each promise resolves, map it to a tuple { key: key, val: val }
|
|
42
|
+
const chain = Object.keys(promises).map((key) =>
|
|
43
|
+
promises[key].then((val) => ({ key, val })),
|
|
44
|
+
);
|
|
45
|
+
// Then wait for all promises to resolve, and convert them back to an object
|
|
46
|
+
return $q.all(chain).then((values) =>
|
|
47
|
+
values.reduce((acc, tuple) => {
|
|
48
|
+
acc[tuple.key] = tuple.val;
|
|
49
|
+
return acc;
|
|
50
|
+
}, {}),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|