@angular/common 21.0.0-next.9 → 21.0.0-rc.1

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v21.0.0-next.9
2
+ * @license Angular v21.0.0-rc.1
3
3
  * (c) 2010-2025 Google LLC. https://angular.dev/
4
4
  * License: MIT
5
5
  */
@@ -12,890 +12,653 @@ import { CommonModule, HashLocationStrategy } from './_common_module-chunk.mjs';
12
12
  import { Location, LocationStrategy, APP_BASE_HREF, PlatformLocation, PathLocationStrategy } from './_location-chunk.mjs';
13
13
 
14
14
  function deepEqual(a, b) {
15
- if (a === b) {
16
- return true;
17
- }
18
- else if (!a || !b) {
15
+ if (a === b) {
16
+ return true;
17
+ } else if (!a || !b) {
18
+ return false;
19
+ } else {
20
+ try {
21
+ if (a.prototype !== b.prototype || Array.isArray(a) && Array.isArray(b)) {
19
22
  return false;
23
+ }
24
+ return JSON.stringify(a) === JSON.stringify(b);
25
+ } catch (e) {
26
+ return false;
20
27
  }
21
- else {
22
- try {
23
- if (a.prototype !== b.prototype || (Array.isArray(a) && Array.isArray(b))) {
24
- return false;
25
- }
26
- return JSON.stringify(a) === JSON.stringify(b);
27
- }
28
- catch (e) {
29
- return false;
30
- }
31
- }
28
+ }
32
29
  }
33
30
  function isAnchor(el) {
34
- return el.href !== undefined;
31
+ return el.href !== undefined;
35
32
  }
36
33
 
37
34
  const PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/;
38
35
  const DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
39
36
  const IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
40
37
  const DEFAULT_PORTS = {
41
- 'http:': 80,
42
- 'https:': 443,
43
- 'ftp:': 21,
38
+ 'http:': 80,
39
+ 'https:': 443,
40
+ 'ftp:': 21
44
41
  };
45
- /**
46
- * Location service that provides a drop-in replacement for the $location service
47
- * provided in AngularJS.
48
- *
49
- * @see [Using the Angular Unified Location Service](guide/upgrade#using-the-unified-angular-location-service)
50
- *
51
- * @publicApi
52
- */
53
42
  class $locationShim {
54
- location;
55
- platformLocation;
56
- urlCodec;
57
- locationStrategy;
58
- initializing = true;
59
- updateBrowser = false;
60
- $$absUrl = '';
61
- $$url = '';
62
- $$protocol;
63
- $$host = '';
64
- $$port;
65
- $$replace = false;
66
- $$path = '';
67
- $$search = '';
68
- $$hash = '';
69
- $$state;
70
- $$changeListeners = [];
71
- cachedState = null;
72
- urlChanges = new ReplaySubject(1);
73
- removeOnUrlChangeFn;
74
- constructor($injector, location, platformLocation, urlCodec, locationStrategy) {
75
- this.location = location;
76
- this.platformLocation = platformLocation;
77
- this.urlCodec = urlCodec;
78
- this.locationStrategy = locationStrategy;
79
- const initialUrl = this.browserUrl();
80
- let parsedUrl = this.urlCodec.parse(initialUrl);
81
- if (typeof parsedUrl === 'string') {
82
- throw 'Invalid URL';
83
- }
84
- this.$$protocol = parsedUrl.protocol;
85
- this.$$host = parsedUrl.hostname;
86
- this.$$port = parseInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
87
- this.$$parseLinkUrl(initialUrl, initialUrl);
88
- this.cacheState();
89
- this.$$state = this.browserState();
90
- this.removeOnUrlChangeFn = this.location.onUrlChange((newUrl, newState) => {
91
- this.urlChanges.next({ newUrl, newState });
92
- });
93
- if (_isPromise($injector)) {
94
- $injector.then(($i) => this.initialize($i));
95
- }
96
- else {
97
- this.initialize($injector);
98
- }
99
- }
100
- initialize($injector) {
101
- const $rootScope = $injector.get('$rootScope');
102
- const $rootElement = $injector.get('$rootElement');
103
- $rootElement.on('click', (event) => {
104
- if (event.ctrlKey ||
105
- event.metaKey ||
106
- event.shiftKey ||
107
- event.which === 2 ||
108
- event.button === 2) {
109
- return;
110
- }
111
- let elm = event.target;
112
- // traverse the DOM up to find first A tag
113
- while (elm && elm.nodeName.toLowerCase() !== 'a') {
114
- // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
115
- if (elm === $rootElement[0] || !(elm = elm.parentNode)) {
116
- return;
117
- }
118
- }
119
- if (!isAnchor(elm)) {
120
- return;
121
- }
122
- const absHref = elm.href;
123
- const relHref = elm.getAttribute('href');
124
- // Ignore when url is started with javascript: or mailto:
125
- if (IGNORE_URI_REGEXP.test(absHref)) {
126
- return;
127
- }
128
- if (absHref && !elm.getAttribute('target') && !event.isDefaultPrevented()) {
129
- if (this.$$parseLinkUrl(absHref, relHref)) {
130
- // We do a preventDefault for all urls that are part of the AngularJS application,
131
- // in html5mode and also without, so that we are able to abort navigation without
132
- // getting double entries in the location history.
133
- event.preventDefault();
134
- // update location manually
135
- if (this.absUrl() !== this.browserUrl()) {
136
- $rootScope.$apply();
137
- }
138
- }
139
- }
140
- });
141
- this.urlChanges.subscribe(({ newUrl, newState }) => {
142
- const oldUrl = this.absUrl();
143
- const oldState = this.$$state;
144
- this.$$parse(newUrl);
145
- newUrl = this.absUrl();
146
- this.$$state = newState;
147
- const defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, newState, oldState).defaultPrevented;
148
- // if the location was changed by a `$locationChangeStart` handler then stop
149
- // processing this location change
150
- if (this.absUrl() !== newUrl)
151
- return;
152
- // If default was prevented, set back to old state. This is the state that was locally
153
- // cached in the $location service.
43
+ location;
44
+ platformLocation;
45
+ urlCodec;
46
+ locationStrategy;
47
+ initializing = true;
48
+ updateBrowser = false;
49
+ $$absUrl = '';
50
+ $$url = '';
51
+ $$protocol;
52
+ $$host = '';
53
+ $$port;
54
+ $$replace = false;
55
+ $$path = '';
56
+ $$search = '';
57
+ $$hash = '';
58
+ $$state;
59
+ $$changeListeners = [];
60
+ cachedState = null;
61
+ urlChanges = new ReplaySubject(1);
62
+ removeOnUrlChangeFn;
63
+ constructor($injector, location, platformLocation, urlCodec, locationStrategy) {
64
+ this.location = location;
65
+ this.platformLocation = platformLocation;
66
+ this.urlCodec = urlCodec;
67
+ this.locationStrategy = locationStrategy;
68
+ const initialUrl = this.browserUrl();
69
+ let parsedUrl = this.urlCodec.parse(initialUrl);
70
+ if (typeof parsedUrl === 'string') {
71
+ throw 'Invalid URL';
72
+ }
73
+ this.$$protocol = parsedUrl.protocol;
74
+ this.$$host = parsedUrl.hostname;
75
+ this.$$port = parseInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
76
+ this.$$parseLinkUrl(initialUrl, initialUrl);
77
+ this.cacheState();
78
+ this.$$state = this.browserState();
79
+ this.removeOnUrlChangeFn = this.location.onUrlChange((newUrl, newState) => {
80
+ this.urlChanges.next({
81
+ newUrl,
82
+ newState
83
+ });
84
+ });
85
+ if (_isPromise($injector)) {
86
+ $injector.then($i => this.initialize($i));
87
+ } else {
88
+ this.initialize($injector);
89
+ }
90
+ }
91
+ initialize($injector) {
92
+ const $rootScope = $injector.get('$rootScope');
93
+ const $rootElement = $injector.get('$rootElement');
94
+ $rootElement.on('click', event => {
95
+ if (event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) {
96
+ return;
97
+ }
98
+ let elm = event.target;
99
+ while (elm && elm.nodeName.toLowerCase() !== 'a') {
100
+ if (elm === $rootElement[0] || !(elm = elm.parentNode)) {
101
+ return;
102
+ }
103
+ }
104
+ if (!isAnchor(elm)) {
105
+ return;
106
+ }
107
+ const absHref = elm.href;
108
+ const relHref = elm.getAttribute('href');
109
+ if (IGNORE_URI_REGEXP.test(absHref)) {
110
+ return;
111
+ }
112
+ if (absHref && !elm.getAttribute('target') && !event.isDefaultPrevented()) {
113
+ if (this.$$parseLinkUrl(absHref, relHref)) {
114
+ event.preventDefault();
115
+ if (this.absUrl() !== this.browserUrl()) {
116
+ $rootScope.$apply();
117
+ }
118
+ }
119
+ }
120
+ });
121
+ this.urlChanges.subscribe(({
122
+ newUrl,
123
+ newState
124
+ }) => {
125
+ const oldUrl = this.absUrl();
126
+ const oldState = this.$$state;
127
+ this.$$parse(newUrl);
128
+ newUrl = this.absUrl();
129
+ this.$$state = newState;
130
+ const defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, newState, oldState).defaultPrevented;
131
+ if (this.absUrl() !== newUrl) return;
132
+ if (defaultPrevented) {
133
+ this.$$parse(oldUrl);
134
+ this.state(oldState);
135
+ this.setBrowserUrlWithFallback(oldUrl, false, oldState);
136
+ this.$$notifyChangeListeners(this.url(), this.$$state, oldUrl, oldState);
137
+ } else {
138
+ this.initializing = false;
139
+ $rootScope.$broadcast('$locationChangeSuccess', newUrl, oldUrl, newState, oldState);
140
+ this.resetBrowserUpdate();
141
+ }
142
+ if (!$rootScope.$$phase) {
143
+ $rootScope.$digest();
144
+ }
145
+ });
146
+ $rootScope.$watch(() => {
147
+ if (this.initializing || this.updateBrowser) {
148
+ this.updateBrowser = false;
149
+ const oldUrl = this.browserUrl();
150
+ const newUrl = this.absUrl();
151
+ const oldState = this.browserState();
152
+ let currentReplace = this.$$replace;
153
+ const urlOrStateChanged = !this.urlCodec.areEqual(oldUrl, newUrl) || oldState !== this.$$state;
154
+ if (this.initializing || urlOrStateChanged) {
155
+ this.initializing = false;
156
+ $rootScope.$evalAsync(() => {
157
+ const newUrl = this.absUrl();
158
+ const defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, this.$$state, oldState).defaultPrevented;
159
+ if (this.absUrl() !== newUrl) return;
154
160
  if (defaultPrevented) {
155
- this.$$parse(oldUrl);
156
- this.state(oldState);
157
- this.setBrowserUrlWithFallback(oldUrl, false, oldState);
161
+ this.$$parse(oldUrl);
162
+ this.$$state = oldState;
163
+ } else {
164
+ if (urlOrStateChanged) {
165
+ this.setBrowserUrlWithFallback(newUrl, currentReplace, oldState === this.$$state ? null : this.$$state);
166
+ this.$$replace = false;
167
+ }
168
+ $rootScope.$broadcast('$locationChangeSuccess', newUrl, oldUrl, this.$$state, oldState);
169
+ if (urlOrStateChanged) {
158
170
  this.$$notifyChangeListeners(this.url(), this.$$state, oldUrl, oldState);
171
+ }
159
172
  }
160
- else {
161
- this.initializing = false;
162
- $rootScope.$broadcast('$locationChangeSuccess', newUrl, oldUrl, newState, oldState);
163
- this.resetBrowserUpdate();
164
- }
165
- if (!$rootScope.$$phase) {
166
- $rootScope.$digest();
167
- }
168
- });
169
- // Synchronize the browser's URL and state with the application.
170
- // Note: There is no need to save the `$watch` return value (deregister listener)
171
- // into a variable because `$scope.$$watchers` is automatically cleaned up when
172
- // the root scope is destroyed.
173
- $rootScope.$watch(() => {
174
- if (this.initializing || this.updateBrowser) {
175
- this.updateBrowser = false;
176
- const oldUrl = this.browserUrl();
177
- const newUrl = this.absUrl();
178
- const oldState = this.browserState();
179
- let currentReplace = this.$$replace;
180
- const urlOrStateChanged = !this.urlCodec.areEqual(oldUrl, newUrl) || oldState !== this.$$state;
181
- // Fire location changes one time to on initialization. This must be done on the
182
- // next tick (thus inside $evalAsync()) in order for listeners to be registered
183
- // before the event fires. Mimicing behavior from $locationWatch:
184
- // https://github.com/angular/angular.js/blob/master/src/ng/location.js#L983
185
- if (this.initializing || urlOrStateChanged) {
186
- this.initializing = false;
187
- $rootScope.$evalAsync(() => {
188
- // Get the new URL again since it could have changed due to async update
189
- const newUrl = this.absUrl();
190
- const defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, this.$$state, oldState).defaultPrevented;
191
- // if the location was changed by a `$locationChangeStart` handler then stop
192
- // processing this location change
193
- if (this.absUrl() !== newUrl)
194
- return;
195
- if (defaultPrevented) {
196
- this.$$parse(oldUrl);
197
- this.$$state = oldState;
198
- }
199
- else {
200
- // This block doesn't run when initializing because it's going to perform the update
201
- // to the URL which shouldn't be needed when initializing.
202
- if (urlOrStateChanged) {
203
- this.setBrowserUrlWithFallback(newUrl, currentReplace, oldState === this.$$state ? null : this.$$state);
204
- this.$$replace = false;
205
- }
206
- $rootScope.$broadcast('$locationChangeSuccess', newUrl, oldUrl, this.$$state, oldState);
207
- if (urlOrStateChanged) {
208
- this.$$notifyChangeListeners(this.url(), this.$$state, oldUrl, oldState);
209
- }
210
- }
211
- });
212
- }
213
- }
214
- this.$$replace = false;
215
- });
216
- $rootScope.$on('$destroy', () => {
217
- this.removeOnUrlChangeFn();
218
- // Complete the subject to release all active observers when the root
219
- // scope is destroyed. Before this change, we subscribed to the `urlChanges`
220
- // subject, and the subscriber captured `this`, leading to a memory leak
221
- // after the root scope was destroyed.
222
- this.urlChanges.complete();
223
- });
224
- }
225
- resetBrowserUpdate() {
226
- this.$$replace = false;
227
- this.$$state = this.browserState();
228
- this.updateBrowser = false;
229
- this.lastBrowserUrl = this.browserUrl();
230
- }
231
- lastHistoryState;
232
- lastBrowserUrl = '';
233
- browserUrl(url, replace, state) {
234
- // In modern browsers `history.state` is `null` by default; treating it separately
235
- // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
236
- // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
237
- if (typeof state === 'undefined') {
238
- state = null;
239
- }
240
- // setter
241
- if (url) {
242
- let sameState = this.lastHistoryState === state;
243
- // Normalize the inputted URL
244
- url = this.urlCodec.parse(url).href;
245
- // Don't change anything if previous and current URLs and states match.
246
- if (this.lastBrowserUrl === url && sameState) {
247
- return this;
248
- }
249
- this.lastBrowserUrl = url;
250
- this.lastHistoryState = state;
251
- // Remove server base from URL as the Angular APIs for updating URL require
252
- // it to be the path+.
253
- url = this.stripBaseUrl(this.getServerBase(), url) || url;
254
- // Set the URL
255
- if (replace) {
256
- this.locationStrategy.replaceState(state, '', url, '');
257
- }
258
- else {
259
- this.locationStrategy.pushState(state, '', url, '');
260
- }
261
- this.cacheState();
262
- return this;
263
- // getter
264
- }
265
- else {
266
- return this.platformLocation.href;
267
- }
268
- }
269
- // This variable should be used *only* inside the cacheState function.
270
- lastCachedState = null;
271
- cacheState() {
272
- // This should be the only place in $browser where `history.state` is read.
273
- this.cachedState = this.platformLocation.getState();
274
- if (typeof this.cachedState === 'undefined') {
275
- this.cachedState = null;
276
- }
277
- // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
278
- if (deepEqual(this.cachedState, this.lastCachedState)) {
279
- this.cachedState = this.lastCachedState;
280
- }
281
- this.lastCachedState = this.cachedState;
282
- this.lastHistoryState = this.cachedState;
283
- }
284
- /**
285
- * This function emulates the $browser.state() function from AngularJS. It will cause
286
- * history.state to be cached unless changed with deep equality check.
287
- */
288
- browserState() {
289
- return this.cachedState;
290
- }
291
- stripBaseUrl(base, url) {
292
- if (url.startsWith(base)) {
293
- return url.slice(base.length);
294
- }
295
- return undefined;
296
- }
297
- getServerBase() {
298
- const { protocol, hostname, port } = this.platformLocation;
299
- const baseHref = this.locationStrategy.getBaseHref();
300
- let url = `${protocol}//${hostname}${port ? ':' + port : ''}${baseHref || '/'}`;
301
- return url.endsWith('/') ? url : url + '/';
302
- }
303
- parseAppUrl(url) {
304
- if (DOUBLE_SLASH_REGEX.test(url)) {
305
- throw new Error(`Bad Path - URL cannot start with double slashes: ${url}`);
306
- }
307
- let prefixed = url.charAt(0) !== '/';
308
- if (prefixed) {
309
- url = '/' + url;
310
- }
311
- let match = this.urlCodec.parse(url, this.getServerBase());
312
- if (typeof match === 'string') {
313
- throw new Error(`Bad URL - Cannot parse URL: ${url}`);
314
- }
315
- let path = prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname;
316
- this.$$path = this.urlCodec.decodePath(path);
317
- this.$$search = this.urlCodec.decodeSearch(match.search);
318
- this.$$hash = this.urlCodec.decodeHash(match.hash);
319
- // make sure path starts with '/';
320
- if (this.$$path && this.$$path.charAt(0) !== '/') {
321
- this.$$path = '/' + this.$$path;
322
- }
323
- }
324
- /**
325
- * Registers listeners for URL changes. This API is used to catch updates performed by the
326
- * AngularJS framework. These changes are a subset of the `$locationChangeStart` and
327
- * `$locationChangeSuccess` events which fire when AngularJS updates its internally-referenced
328
- * version of the browser URL.
329
- *
330
- * It's possible for `$locationChange` events to happen, but for the browser URL
331
- * (window.location) to remain unchanged. This `onChange` callback will fire only when AngularJS
332
- * actually updates the browser URL (window.location).
333
- *
334
- * @param fn The callback function that is triggered for the listener when the URL changes.
335
- * @param err The callback function that is triggered when an error occurs.
336
- */
337
- onChange(fn, err = (e) => { }) {
338
- this.$$changeListeners.push([fn, err]);
339
- }
340
- /** @internal */
341
- $$notifyChangeListeners(url = '', state, oldUrl = '', oldState) {
342
- this.$$changeListeners.forEach(([fn, err]) => {
343
- try {
344
- fn(url, state, oldUrl, oldState);
345
- }
346
- catch (e) {
347
- err(e);
348
- }
349
- });
350
- }
351
- /**
352
- * Parses the provided URL, and sets the current URL to the parsed result.
353
- *
354
- * @param url The URL string.
355
- */
356
- $$parse(url) {
357
- let pathUrl;
358
- if (url.startsWith('/')) {
359
- pathUrl = url;
360
- }
361
- else {
362
- // Remove protocol & hostname if URL starts with it
363
- pathUrl = this.stripBaseUrl(this.getServerBase(), url);
364
- }
365
- if (typeof pathUrl === 'undefined') {
366
- throw new Error(`Invalid url "${url}", missing path prefix "${this.getServerBase()}".`);
367
- }
368
- this.parseAppUrl(pathUrl);
369
- this.$$path ||= '/';
370
- this.composeUrls();
371
- }
372
- /**
373
- * Parses the provided URL and its relative URL.
374
- *
375
- * @param url The full URL string.
376
- * @param relHref A URL string relative to the full URL string.
377
- */
378
- $$parseLinkUrl(url, relHref) {
379
- // When relHref is passed, it should be a hash and is handled separately
380
- if (relHref && relHref[0] === '#') {
381
- this.hash(relHref.slice(1));
382
- return true;
383
- }
384
- let rewrittenUrl;
385
- let appUrl = this.stripBaseUrl(this.getServerBase(), url);
386
- if (typeof appUrl !== 'undefined') {
387
- rewrittenUrl = this.getServerBase() + appUrl;
388
- }
389
- else if (this.getServerBase() === url + '/') {
390
- rewrittenUrl = this.getServerBase();
391
- }
392
- // Set the URL
393
- if (rewrittenUrl) {
394
- this.$$parse(rewrittenUrl);
395
- }
396
- return !!rewrittenUrl;
397
- }
398
- setBrowserUrlWithFallback(url, replace, state) {
399
- const oldUrl = this.url();
400
- const oldState = this.$$state;
401
- try {
402
- this.browserUrl(url, replace, state);
403
- // Make sure $location.state() returns referentially identical (not just deeply equal)
404
- // state object; this makes possible quick checking if the state changed in the digest
405
- // loop. Checking deep equality would be too expensive.
406
- this.$$state = this.browserState();
407
- }
408
- catch (e) {
409
- // Restore old values if pushState fails
410
- this.url(oldUrl);
411
- this.$$state = oldState;
412
- throw e;
413
- }
414
- }
415
- composeUrls() {
416
- this.$$url = this.urlCodec.normalize(this.$$path, this.$$search, this.$$hash);
417
- this.$$absUrl = this.getServerBase() + this.$$url.slice(1); // remove '/' from front of URL
418
- this.updateBrowser = true;
419
- }
420
- /**
421
- * Retrieves the full URL representation with all segments encoded according to
422
- * rules specified in
423
- * [RFC 3986](https://tools.ietf.org/html/rfc3986).
424
- *
425
- *
426
- * ```js
427
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
428
- * let absUrl = $location.absUrl();
429
- * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
430
- * ```
431
- */
432
- absUrl() {
433
- return this.$$absUrl;
434
- }
435
- url(url) {
436
- if (typeof url === 'string') {
437
- if (!url.length) {
438
- url = '/';
439
- }
440
- const match = PATH_MATCH.exec(url);
441
- if (!match)
442
- return this;
443
- if (match[1] || url === '')
444
- this.path(this.urlCodec.decodePath(match[1]));
445
- if (match[2] || match[1] || url === '')
446
- this.search(match[3] || '');
447
- this.hash(match[5] || '');
448
- // Chainable method
449
- return this;
450
- }
451
- return this.$$url;
452
- }
453
- /**
454
- * Retrieves the protocol of the current URL.
455
- *
456
- * ```js
457
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
458
- * let protocol = $location.protocol();
459
- * // => "http"
460
- * ```
461
- */
462
- protocol() {
463
- return this.$$protocol;
464
- }
465
- /**
466
- * Retrieves the protocol of the current URL.
467
- *
468
- * In contrast to the non-AngularJS version `location.host` which returns `hostname:port`, this
469
- * returns the `hostname` portion only.
470
- *
471
- *
472
- * ```js
473
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
474
- * let host = $location.host();
475
- * // => "example.com"
476
- *
477
- * // given URL http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
478
- * host = $location.host();
479
- * // => "example.com"
480
- * host = location.host;
481
- * // => "example.com:8080"
482
- * ```
483
- */
484
- host() {
485
- return this.$$host;
486
- }
487
- /**
488
- * Retrieves the port of the current URL.
489
- *
490
- * ```js
491
- * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
492
- * let port = $location.port();
493
- * // => 80
494
- * ```
495
- */
496
- port() {
497
- return this.$$port;
498
- }
499
- path(path) {
500
- if (typeof path === 'undefined') {
501
- return this.$$path;
502
- }
503
- // null path converts to empty string. Prepend with "/" if needed.
504
- path = path !== null ? path.toString() : '';
505
- path = path.charAt(0) === '/' ? path : '/' + path;
506
- this.$$path = path;
507
- this.composeUrls();
508
- return this;
509
- }
510
- search(search, paramValue) {
511
- switch (arguments.length) {
512
- case 0:
513
- return this.$$search;
514
- case 1:
515
- if (typeof search === 'string' || typeof search === 'number') {
516
- this.$$search = this.urlCodec.decodeSearch(search.toString());
517
- }
518
- else if (typeof search === 'object' && search !== null) {
519
- // Copy the object so it's never mutated
520
- search = { ...search };
521
- // remove object undefined or null properties
522
- for (const key in search) {
523
- if (search[key] == null)
524
- delete search[key];
525
- }
526
- this.$$search = search;
527
- }
528
- else {
529
- throw new Error('LocationProvider.search(): First argument must be a string or an object.');
530
- }
531
- break;
532
- default:
533
- if (typeof search === 'string') {
534
- const currentSearch = this.search();
535
- if (typeof paramValue === 'undefined' || paramValue === null) {
536
- delete currentSearch[search];
537
- return this.search(currentSearch);
538
- }
539
- else {
540
- currentSearch[search] = paramValue;
541
- return this.search(currentSearch);
542
- }
543
- }
173
+ });
544
174
  }
545
- this.composeUrls();
546
- return this;
547
- }
548
- hash(hash) {
549
- if (typeof hash === 'undefined') {
550
- return this.$$hash;
551
- }
552
- this.$$hash = hash !== null ? hash.toString() : '';
553
- this.composeUrls();
554
- return this;
555
- }
556
- /**
557
- * Changes to `$location` during the current `$digest` will replace the current
558
- * history record, instead of adding a new one.
559
- */
560
- replace() {
561
- this.$$replace = true;
562
- return this;
563
- }
564
- state(state) {
565
- if (typeof state === 'undefined') {
566
- return this.$$state;
567
- }
568
- this.$$state = state;
175
+ }
176
+ this.$$replace = false;
177
+ });
178
+ $rootScope.$on('$destroy', () => {
179
+ this.removeOnUrlChangeFn();
180
+ this.urlChanges.complete();
181
+ });
182
+ }
183
+ resetBrowserUpdate() {
184
+ this.$$replace = false;
185
+ this.$$state = this.browserState();
186
+ this.updateBrowser = false;
187
+ this.lastBrowserUrl = this.browserUrl();
188
+ }
189
+ lastHistoryState;
190
+ lastBrowserUrl = '';
191
+ browserUrl(url, replace, state) {
192
+ if (typeof state === 'undefined') {
193
+ state = null;
194
+ }
195
+ if (url) {
196
+ let sameState = this.lastHistoryState === state;
197
+ url = this.urlCodec.parse(url).href;
198
+ if (this.lastBrowserUrl === url && sameState) {
569
199
  return this;
570
- }
200
+ }
201
+ this.lastBrowserUrl = url;
202
+ this.lastHistoryState = state;
203
+ url = this.stripBaseUrl(this.getServerBase(), url) || url;
204
+ if (replace) {
205
+ this.locationStrategy.replaceState(state, '', url, '');
206
+ } else {
207
+ this.locationStrategy.pushState(state, '', url, '');
208
+ }
209
+ this.cacheState();
210
+ return this;
211
+ } else {
212
+ return this.platformLocation.href;
213
+ }
214
+ }
215
+ lastCachedState = null;
216
+ cacheState() {
217
+ this.cachedState = this.platformLocation.getState();
218
+ if (typeof this.cachedState === 'undefined') {
219
+ this.cachedState = null;
220
+ }
221
+ if (deepEqual(this.cachedState, this.lastCachedState)) {
222
+ this.cachedState = this.lastCachedState;
223
+ }
224
+ this.lastCachedState = this.cachedState;
225
+ this.lastHistoryState = this.cachedState;
226
+ }
227
+ browserState() {
228
+ return this.cachedState;
229
+ }
230
+ stripBaseUrl(base, url) {
231
+ if (url.startsWith(base)) {
232
+ return url.slice(base.length);
233
+ }
234
+ return undefined;
235
+ }
236
+ getServerBase() {
237
+ const {
238
+ protocol,
239
+ hostname,
240
+ port
241
+ } = this.platformLocation;
242
+ const baseHref = this.locationStrategy.getBaseHref();
243
+ let url = `${protocol}//${hostname}${port ? ':' + port : ''}${baseHref || '/'}`;
244
+ return url.endsWith('/') ? url : url + '/';
245
+ }
246
+ parseAppUrl(url) {
247
+ if (DOUBLE_SLASH_REGEX.test(url)) {
248
+ throw new Error(`Bad Path - URL cannot start with double slashes: ${url}`);
249
+ }
250
+ let prefixed = url.charAt(0) !== '/';
251
+ if (prefixed) {
252
+ url = '/' + url;
253
+ }
254
+ let match = this.urlCodec.parse(url, this.getServerBase());
255
+ if (typeof match === 'string') {
256
+ throw new Error(`Bad URL - Cannot parse URL: ${url}`);
257
+ }
258
+ let path = prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname;
259
+ this.$$path = this.urlCodec.decodePath(path);
260
+ this.$$search = this.urlCodec.decodeSearch(match.search);
261
+ this.$$hash = this.urlCodec.decodeHash(match.hash);
262
+ if (this.$$path && this.$$path.charAt(0) !== '/') {
263
+ this.$$path = '/' + this.$$path;
264
+ }
265
+ }
266
+ onChange(fn, err = e => {}) {
267
+ this.$$changeListeners.push([fn, err]);
268
+ }
269
+ $$notifyChangeListeners(url = '', state, oldUrl = '', oldState) {
270
+ this.$$changeListeners.forEach(([fn, err]) => {
271
+ try {
272
+ fn(url, state, oldUrl, oldState);
273
+ } catch (e) {
274
+ err(e);
275
+ }
276
+ });
277
+ }
278
+ $$parse(url) {
279
+ let pathUrl;
280
+ if (url.startsWith('/')) {
281
+ pathUrl = url;
282
+ } else {
283
+ pathUrl = this.stripBaseUrl(this.getServerBase(), url);
284
+ }
285
+ if (typeof pathUrl === 'undefined') {
286
+ throw new Error(`Invalid url "${url}", missing path prefix "${this.getServerBase()}".`);
287
+ }
288
+ this.parseAppUrl(pathUrl);
289
+ this.$$path ||= '/';
290
+ this.composeUrls();
291
+ }
292
+ $$parseLinkUrl(url, relHref) {
293
+ if (relHref && relHref[0] === '#') {
294
+ this.hash(relHref.slice(1));
295
+ return true;
296
+ }
297
+ let rewrittenUrl;
298
+ let appUrl = this.stripBaseUrl(this.getServerBase(), url);
299
+ if (typeof appUrl !== 'undefined') {
300
+ rewrittenUrl = this.getServerBase() + appUrl;
301
+ } else if (this.getServerBase() === url + '/') {
302
+ rewrittenUrl = this.getServerBase();
303
+ }
304
+ if (rewrittenUrl) {
305
+ this.$$parse(rewrittenUrl);
306
+ }
307
+ return !!rewrittenUrl;
308
+ }
309
+ setBrowserUrlWithFallback(url, replace, state) {
310
+ const oldUrl = this.url();
311
+ const oldState = this.$$state;
312
+ try {
313
+ this.browserUrl(url, replace, state);
314
+ this.$$state = this.browserState();
315
+ } catch (e) {
316
+ this.url(oldUrl);
317
+ this.$$state = oldState;
318
+ throw e;
319
+ }
320
+ }
321
+ composeUrls() {
322
+ this.$$url = this.urlCodec.normalize(this.$$path, this.$$search, this.$$hash);
323
+ this.$$absUrl = this.getServerBase() + this.$$url.slice(1);
324
+ this.updateBrowser = true;
325
+ }
326
+ absUrl() {
327
+ return this.$$absUrl;
328
+ }
329
+ url(url) {
330
+ if (typeof url === 'string') {
331
+ if (!url.length) {
332
+ url = '/';
333
+ }
334
+ const match = PATH_MATCH.exec(url);
335
+ if (!match) return this;
336
+ if (match[1] || url === '') this.path(this.urlCodec.decodePath(match[1]));
337
+ if (match[2] || match[1] || url === '') this.search(match[3] || '');
338
+ this.hash(match[5] || '');
339
+ return this;
340
+ }
341
+ return this.$$url;
342
+ }
343
+ protocol() {
344
+ return this.$$protocol;
345
+ }
346
+ host() {
347
+ return this.$$host;
348
+ }
349
+ port() {
350
+ return this.$$port;
351
+ }
352
+ path(path) {
353
+ if (typeof path === 'undefined') {
354
+ return this.$$path;
355
+ }
356
+ path = path !== null ? path.toString() : '';
357
+ path = path.charAt(0) === '/' ? path : '/' + path;
358
+ this.$$path = path;
359
+ this.composeUrls();
360
+ return this;
361
+ }
362
+ search(search, paramValue) {
363
+ switch (arguments.length) {
364
+ case 0:
365
+ return this.$$search;
366
+ case 1:
367
+ if (typeof search === 'string' || typeof search === 'number') {
368
+ this.$$search = this.urlCodec.decodeSearch(search.toString());
369
+ } else if (typeof search === 'object' && search !== null) {
370
+ search = {
371
+ ...search
372
+ };
373
+ for (const key in search) {
374
+ if (search[key] == null) delete search[key];
375
+ }
376
+ this.$$search = search;
377
+ } else {
378
+ throw new Error('LocationProvider.search(): First argument must be a string or an object.');
379
+ }
380
+ break;
381
+ default:
382
+ if (typeof search === 'string') {
383
+ const currentSearch = this.search();
384
+ if (typeof paramValue === 'undefined' || paramValue === null) {
385
+ delete currentSearch[search];
386
+ return this.search(currentSearch);
387
+ } else {
388
+ currentSearch[search] = paramValue;
389
+ return this.search(currentSearch);
390
+ }
391
+ }
392
+ }
393
+ this.composeUrls();
394
+ return this;
395
+ }
396
+ hash(hash) {
397
+ if (typeof hash === 'undefined') {
398
+ return this.$$hash;
399
+ }
400
+ this.$$hash = hash !== null ? hash.toString() : '';
401
+ this.composeUrls();
402
+ return this;
403
+ }
404
+ replace() {
405
+ this.$$replace = true;
406
+ return this;
407
+ }
408
+ state(state) {
409
+ if (typeof state === 'undefined') {
410
+ return this.$$state;
411
+ }
412
+ this.$$state = state;
413
+ return this;
414
+ }
571
415
  }
572
- /**
573
- * The factory function used to create an instance of the `$locationShim` in Angular,
574
- * and provides an API-compatible `$locationProvider` for AngularJS.
575
- *
576
- * @publicApi
577
- */
578
416
  class $locationShimProvider {
579
- ngUpgrade;
580
- location;
581
- platformLocation;
582
- urlCodec;
583
- locationStrategy;
584
- constructor(ngUpgrade, location, platformLocation, urlCodec, locationStrategy) {
585
- this.ngUpgrade = ngUpgrade;
586
- this.location = location;
587
- this.platformLocation = platformLocation;
588
- this.urlCodec = urlCodec;
589
- this.locationStrategy = locationStrategy;
590
- }
591
- /**
592
- * Factory method that returns an instance of the $locationShim
593
- */
594
- $get() {
595
- return new $locationShim(this.ngUpgrade.$injector, this.location, this.platformLocation, this.urlCodec, this.locationStrategy);
596
- }
597
- /**
598
- * Stub method used to keep API compatible with AngularJS. This setting is configured through
599
- * the LocationUpgradeModule's `config` method in your Angular app.
600
- */
601
- hashPrefix(prefix) {
602
- throw new Error('Configure LocationUpgrade through LocationUpgradeModule.config method.');
603
- }
604
- /**
605
- * Stub method used to keep API compatible with AngularJS. This setting is configured through
606
- * the LocationUpgradeModule's `config` method in your Angular app.
607
- */
608
- html5Mode(mode) {
609
- throw new Error('Configure LocationUpgrade through LocationUpgradeModule.config method.');
610
- }
417
+ ngUpgrade;
418
+ location;
419
+ platformLocation;
420
+ urlCodec;
421
+ locationStrategy;
422
+ constructor(ngUpgrade, location, platformLocation, urlCodec, locationStrategy) {
423
+ this.ngUpgrade = ngUpgrade;
424
+ this.location = location;
425
+ this.platformLocation = platformLocation;
426
+ this.urlCodec = urlCodec;
427
+ this.locationStrategy = locationStrategy;
428
+ }
429
+ $get() {
430
+ return new $locationShim(this.ngUpgrade.$injector, this.location, this.platformLocation, this.urlCodec, this.locationStrategy);
431
+ }
432
+ hashPrefix(prefix) {
433
+ throw new Error('Configure LocationUpgrade through LocationUpgradeModule.config method.');
434
+ }
435
+ html5Mode(mode) {
436
+ throw new Error('Configure LocationUpgrade through LocationUpgradeModule.config method.');
437
+ }
611
438
  }
612
439
 
613
- /**
614
- * A codec for encoding and decoding URL parts.
615
- *
616
- * @publicApi
617
- **/
618
- class UrlCodec {
619
- }
620
- /**
621
- * A `UrlCodec` that uses logic from AngularJS to serialize and parse URLs
622
- * and URL parameters.
623
- *
624
- * @publicApi
625
- */
440
+ class UrlCodec {}
626
441
  class AngularJSUrlCodec {
627
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L15
628
- encodePath(path) {
629
- const segments = path.split('/');
630
- let i = segments.length;
631
- while (i--) {
632
- // decode forward slashes to prevent them from being double encoded
633
- segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, '/'));
634
- }
635
- path = segments.join('/');
636
- return _stripIndexHtml(((path && path[0] !== '/' && '/') || '') + path);
637
- }
638
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L42
639
- encodeSearch(search) {
640
- if (typeof search === 'string') {
641
- search = parseKeyValue(search);
642
- }
643
- search = toKeyValue(search);
644
- return search ? '?' + search : '';
645
- }
646
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L44
647
- encodeHash(hash) {
648
- hash = encodeUriSegment(hash);
649
- return hash ? '#' + hash : '';
650
- }
651
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L27
652
- decodePath(path, html5Mode = true) {
653
- const segments = path.split('/');
654
- let i = segments.length;
655
- while (i--) {
656
- segments[i] = decodeURIComponent(segments[i]);
657
- if (html5Mode) {
658
- // encode forward slashes to prevent them from being mistaken for path separators
659
- segments[i] = segments[i].replace(/\//g, '%2F');
660
- }
661
- }
662
- return segments.join('/');
663
- }
664
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L72
665
- decodeSearch(search) {
666
- return parseKeyValue(search);
667
- }
668
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/location.js#L73
669
- decodeHash(hash) {
670
- hash = decodeURIComponent(hash);
671
- return hash[0] === '#' ? hash.substring(1) : hash;
672
- }
673
- normalize(pathOrHref, search, hash, baseUrl) {
674
- if (arguments.length === 1) {
675
- const parsed = this.parse(pathOrHref, baseUrl);
676
- if (typeof parsed === 'string') {
677
- return parsed;
678
- }
679
- const serverUrl = `${parsed.protocol}://${parsed.hostname}${parsed.port ? ':' + parsed.port : ''}`;
680
- return this.normalize(this.decodePath(parsed.pathname), this.decodeSearch(parsed.search), this.decodeHash(parsed.hash), serverUrl);
681
- }
682
- else {
683
- const encPath = this.encodePath(pathOrHref);
684
- const encSearch = (search && this.encodeSearch(search)) || '';
685
- const encHash = (hash && this.encodeHash(hash)) || '';
686
- let joinedPath = (baseUrl || '') + encPath;
687
- if (!joinedPath.length || joinedPath[0] !== '/') {
688
- joinedPath = '/' + joinedPath;
689
- }
690
- return joinedPath + encSearch + encHash;
691
- }
692
- }
693
- areEqual(valA, valB) {
694
- return this.normalize(valA) === this.normalize(valB);
695
- }
696
- // https://github.com/angular/angular.js/blob/864c7f0/src/ng/urlUtils.js#L60
697
- parse(url, base) {
698
- try {
699
- // Safari 12 throws an error when the URL constructor is called with an undefined base.
700
- const parsed = !base ? new URL(url) : new URL(url, base);
701
- return {
702
- href: parsed.href,
703
- protocol: parsed.protocol ? parsed.protocol.replace(/:$/, '') : '',
704
- host: parsed.host,
705
- search: parsed.search ? parsed.search.replace(/^\?/, '') : '',
706
- hash: parsed.hash ? parsed.hash.replace(/^#/, '') : '',
707
- hostname: parsed.hostname,
708
- port: parsed.port,
709
- pathname: parsed.pathname.charAt(0) === '/' ? parsed.pathname : '/' + parsed.pathname,
710
- };
711
- }
712
- catch (e) {
713
- throw new Error(`Invalid URL (${url}) with base (${base})`);
714
- }
715
- }
442
+ encodePath(path) {
443
+ const segments = path.split('/');
444
+ let i = segments.length;
445
+ while (i--) {
446
+ segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, '/'));
447
+ }
448
+ path = segments.join('/');
449
+ return _stripIndexHtml((path && path[0] !== '/' && '/' || '') + path);
450
+ }
451
+ encodeSearch(search) {
452
+ if (typeof search === 'string') {
453
+ search = parseKeyValue(search);
454
+ }
455
+ search = toKeyValue(search);
456
+ return search ? '?' + search : '';
457
+ }
458
+ encodeHash(hash) {
459
+ hash = encodeUriSegment(hash);
460
+ return hash ? '#' + hash : '';
461
+ }
462
+ decodePath(path, html5Mode = true) {
463
+ const segments = path.split('/');
464
+ let i = segments.length;
465
+ while (i--) {
466
+ segments[i] = decodeURIComponent(segments[i]);
467
+ if (html5Mode) {
468
+ segments[i] = segments[i].replace(/\//g, '%2F');
469
+ }
470
+ }
471
+ return segments.join('/');
472
+ }
473
+ decodeSearch(search) {
474
+ return parseKeyValue(search);
475
+ }
476
+ decodeHash(hash) {
477
+ hash = decodeURIComponent(hash);
478
+ return hash[0] === '#' ? hash.substring(1) : hash;
479
+ }
480
+ normalize(pathOrHref, search, hash, baseUrl) {
481
+ if (arguments.length === 1) {
482
+ const parsed = this.parse(pathOrHref, baseUrl);
483
+ if (typeof parsed === 'string') {
484
+ return parsed;
485
+ }
486
+ const serverUrl = `${parsed.protocol}://${parsed.hostname}${parsed.port ? ':' + parsed.port : ''}`;
487
+ return this.normalize(this.decodePath(parsed.pathname), this.decodeSearch(parsed.search), this.decodeHash(parsed.hash), serverUrl);
488
+ } else {
489
+ const encPath = this.encodePath(pathOrHref);
490
+ const encSearch = search && this.encodeSearch(search) || '';
491
+ const encHash = hash && this.encodeHash(hash) || '';
492
+ let joinedPath = (baseUrl || '') + encPath;
493
+ if (!joinedPath.length || joinedPath[0] !== '/') {
494
+ joinedPath = '/' + joinedPath;
495
+ }
496
+ return joinedPath + encSearch + encHash;
497
+ }
498
+ }
499
+ areEqual(valA, valB) {
500
+ return this.normalize(valA) === this.normalize(valB);
501
+ }
502
+ parse(url, base) {
503
+ try {
504
+ const parsed = !base ? new URL(url) : new URL(url, base);
505
+ return {
506
+ href: parsed.href,
507
+ protocol: parsed.protocol ? parsed.protocol.replace(/:$/, '') : '',
508
+ host: parsed.host,
509
+ search: parsed.search ? parsed.search.replace(/^\?/, '') : '',
510
+ hash: parsed.hash ? parsed.hash.replace(/^#/, '') : '',
511
+ hostname: parsed.hostname,
512
+ port: parsed.port,
513
+ pathname: parsed.pathname.charAt(0) === '/' ? parsed.pathname : '/' + parsed.pathname
514
+ };
515
+ } catch (e) {
516
+ throw new Error(`Invalid URL (${url}) with base (${base})`);
517
+ }
518
+ }
716
519
  }
717
520
  function _stripIndexHtml(url) {
718
- return url.replace(/\/index.html$/, '');
521
+ return url.replace(/\/index.html$/, '');
719
522
  }
720
- /**
721
- * Tries to decode the URI component without throwing an exception.
722
- *
723
- * @param str value potential URI component to check.
724
- * @returns the decoded URI if it can be decoded or else `undefined`.
725
- */
726
523
  function tryDecodeURIComponent(value) {
727
- try {
728
- return decodeURIComponent(value);
729
- }
730
- catch (e) {
731
- // Ignore any invalid uri component.
732
- return undefined;
733
- }
524
+ try {
525
+ return decodeURIComponent(value);
526
+ } catch (e) {
527
+ return undefined;
528
+ }
734
529
  }
735
- /**
736
- * Parses an escaped url query string into key-value pairs. Logic taken from
737
- * https://github.com/angular/angular.js/blob/864c7f0/src/Angular.js#L1382
738
- */
739
530
  function parseKeyValue(keyValue) {
740
- const obj = {};
741
- (keyValue || '').split('&').forEach((keyValue) => {
742
- let splitPoint, key, val;
743
- if (keyValue) {
744
- key = keyValue = keyValue.replace(/\+/g, '%20');
745
- splitPoint = keyValue.indexOf('=');
746
- if (splitPoint !== -1) {
747
- key = keyValue.substring(0, splitPoint);
748
- val = keyValue.substring(splitPoint + 1);
749
- }
750
- key = tryDecodeURIComponent(key);
751
- if (typeof key !== 'undefined') {
752
- val = typeof val !== 'undefined' ? tryDecodeURIComponent(val) : true;
753
- if (!obj.hasOwnProperty(key)) {
754
- obj[key] = val;
755
- }
756
- else if (Array.isArray(obj[key])) {
757
- obj[key].push(val);
758
- }
759
- else {
760
- obj[key] = [obj[key], val];
761
- }
762
- }
763
- }
764
- });
765
- return obj;
531
+ const obj = {};
532
+ (keyValue || '').split('&').forEach(keyValue => {
533
+ let splitPoint, key, val;
534
+ if (keyValue) {
535
+ key = keyValue = keyValue.replace(/\+/g, '%20');
536
+ splitPoint = keyValue.indexOf('=');
537
+ if (splitPoint !== -1) {
538
+ key = keyValue.substring(0, splitPoint);
539
+ val = keyValue.substring(splitPoint + 1);
540
+ }
541
+ key = tryDecodeURIComponent(key);
542
+ if (typeof key !== 'undefined') {
543
+ val = typeof val !== 'undefined' ? tryDecodeURIComponent(val) : true;
544
+ if (!obj.hasOwnProperty(key)) {
545
+ obj[key] = val;
546
+ } else if (Array.isArray(obj[key])) {
547
+ obj[key].push(val);
548
+ } else {
549
+ obj[key] = [obj[key], val];
550
+ }
551
+ }
552
+ }
553
+ });
554
+ return obj;
766
555
  }
767
- /**
768
- * Serializes into key-value pairs. Logic taken from
769
- * https://github.com/angular/angular.js/blob/864c7f0/src/Angular.js#L1409
770
- */
771
556
  function toKeyValue(obj) {
772
- const parts = [];
773
- for (const key in obj) {
774
- let value = obj[key];
775
- if (Array.isArray(value)) {
776
- value.forEach((arrayValue) => {
777
- parts.push(encodeUriQuery(key, true) +
778
- (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
779
- });
780
- }
781
- else {
782
- parts.push(encodeUriQuery(key, true) +
783
- (value === true ? '' : '=' + encodeUriQuery(value, true)));
784
- }
785
- }
786
- return parts.length ? parts.join('&') : '';
557
+ const parts = [];
558
+ for (const key in obj) {
559
+ let value = obj[key];
560
+ if (Array.isArray(value)) {
561
+ value.forEach(arrayValue => {
562
+ parts.push(encodeUriQuery(key, true) + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
563
+ });
564
+ } else {
565
+ parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true)));
566
+ }
567
+ }
568
+ return parts.length ? parts.join('&') : '';
787
569
  }
788
- /**
789
- * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
790
- * https://tools.ietf.org/html/rfc3986 with regards to the character set (pchar) allowed in path
791
- * segments:
792
- * segment = *pchar
793
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
794
- * pct-encoded = "%" HEXDIG HEXDIG
795
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
796
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
797
- * / "*" / "+" / "," / ";" / "="
798
- *
799
- * Logic from https://github.com/angular/angular.js/blob/864c7f0/src/Angular.js#L1437
800
- */
801
570
  function encodeUriSegment(val) {
802
- return encodeUriQuery(val, true).replace(/%26/g, '&').replace(/%3D/gi, '=').replace(/%2B/gi, '+');
571
+ return encodeUriQuery(val, true).replace(/%26/g, '&').replace(/%3D/gi, '=').replace(/%2B/gi, '+');
803
572
  }
804
- /**
805
- * This method is intended for encoding *key* or *value* parts of query component. We need a custom
806
- * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
807
- * encoded per https://tools.ietf.org/html/rfc3986:
808
- * query = *( pchar / "/" / "?" )
809
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
810
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
811
- * pct-encoded = "%" HEXDIG HEXDIG
812
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
813
- * / "*" / "+" / "," / ";" / "="
814
- *
815
- * Logic from https://github.com/angular/angular.js/blob/864c7f0/src/Angular.js#L1456
816
- */
817
573
  function encodeUriQuery(val, pctEncodeSpaces = false) {
818
- return encodeURIComponent(val)
819
- .replace(/%40/g, '@')
820
- .replace(/%3A/gi, ':')
821
- .replace(/%24/g, '$')
822
- .replace(/%2C/gi, ',')
823
- .replace(/%3B/gi, ';')
824
- .replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
574
+ return encodeURIComponent(val).replace(/%40/g, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%3B/gi, ';').replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
825
575
  }
826
576
 
827
- /**
828
- * A provider token used to configure the location upgrade module.
829
- *
830
- * @publicApi
831
- */
832
577
  const LOCATION_UPGRADE_CONFIGURATION = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'LOCATION_UPGRADE_CONFIGURATION' : '');
833
578
  const APP_BASE_HREF_RESOLVED = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'APP_BASE_HREF_RESOLVED' : '');
834
- /**
835
- * `NgModule` used for providing and configuring Angular's Unified Location Service for upgrading.
836
- *
837
- * @see [Using the Unified Angular Location Service](https://angular.io/guide/upgrade#using-the-unified-angular-location-service)
838
- *
839
- * @publicApi
840
- */
841
579
  class LocationUpgradeModule {
842
- static config(config) {
843
- return {
844
- ngModule: LocationUpgradeModule,
845
- providers: [
846
- Location,
847
- {
848
- provide: $locationShim,
849
- useFactory: provide$location,
850
- },
851
- { provide: LOCATION_UPGRADE_CONFIGURATION, useValue: config ? config : {} },
852
- { provide: UrlCodec, useFactory: provideUrlCodec },
853
- {
854
- provide: APP_BASE_HREF_RESOLVED,
855
- useFactory: provideAppBaseHref,
856
- },
857
- {
858
- provide: LocationStrategy,
859
- useFactory: provideLocationStrategy,
860
- },
861
- ],
862
- };
863
- }
864
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: LocationUpgradeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
865
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.0-next.9", ngImport: i0, type: LocationUpgradeModule, imports: [CommonModule] });
866
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: LocationUpgradeModule, imports: [CommonModule] });
580
+ static config(config) {
581
+ return {
582
+ ngModule: LocationUpgradeModule,
583
+ providers: [Location, {
584
+ provide: $locationShim,
585
+ useFactory: provide$location
586
+ }, {
587
+ provide: LOCATION_UPGRADE_CONFIGURATION,
588
+ useValue: config ? config : {}
589
+ }, {
590
+ provide: UrlCodec,
591
+ useFactory: provideUrlCodec
592
+ }, {
593
+ provide: APP_BASE_HREF_RESOLVED,
594
+ useFactory: provideAppBaseHref
595
+ }, {
596
+ provide: LocationStrategy,
597
+ useFactory: provideLocationStrategy
598
+ }]
599
+ };
600
+ }
601
+ static ɵfac = i0.ɵɵngDeclareFactory({
602
+ minVersion: "12.0.0",
603
+ version: "21.0.0-rc.1",
604
+ ngImport: i0,
605
+ type: LocationUpgradeModule,
606
+ deps: [],
607
+ target: i0.ɵɵFactoryTarget.NgModule
608
+ });
609
+ static ɵmod = i0.ɵɵngDeclareNgModule({
610
+ minVersion: "14.0.0",
611
+ version: "21.0.0-rc.1",
612
+ ngImport: i0,
613
+ type: LocationUpgradeModule,
614
+ imports: [CommonModule]
615
+ });
616
+ static ɵinj = i0.ɵɵngDeclareInjector({
617
+ minVersion: "12.0.0",
618
+ version: "21.0.0-rc.1",
619
+ ngImport: i0,
620
+ type: LocationUpgradeModule,
621
+ imports: [CommonModule]
622
+ });
867
623
  }
868
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: LocationUpgradeModule, decorators: [{
869
- type: NgModule,
870
- args: [{ imports: [CommonModule] }]
871
- }] });
624
+ i0.ɵɵngDeclareClassMetadata({
625
+ minVersion: "12.0.0",
626
+ version: "21.0.0-rc.1",
627
+ ngImport: i0,
628
+ type: LocationUpgradeModule,
629
+ decorators: [{
630
+ type: NgModule,
631
+ args: [{
632
+ imports: [CommonModule]
633
+ }]
634
+ }]
635
+ });
872
636
  function provideAppBaseHref() {
873
- const config = inject(LOCATION_UPGRADE_CONFIGURATION);
874
- const appBaseHref = inject(APP_BASE_HREF, { optional: true });
875
- if (config && config.appBaseHref != null) {
876
- return config.appBaseHref;
877
- }
878
- else if (appBaseHref != null) {
879
- return appBaseHref;
880
- }
881
- return '';
637
+ const config = inject(LOCATION_UPGRADE_CONFIGURATION);
638
+ const appBaseHref = inject(APP_BASE_HREF, {
639
+ optional: true
640
+ });
641
+ if (config && config.appBaseHref != null) {
642
+ return config.appBaseHref;
643
+ } else if (appBaseHref != null) {
644
+ return appBaseHref;
645
+ }
646
+ return '';
882
647
  }
883
648
  function provideUrlCodec() {
884
- const config = inject(LOCATION_UPGRADE_CONFIGURATION);
885
- const codec = (config && config.urlCodec) || AngularJSUrlCodec;
886
- return new codec();
649
+ const config = inject(LOCATION_UPGRADE_CONFIGURATION);
650
+ const codec = config && config.urlCodec || AngularJSUrlCodec;
651
+ return new codec();
887
652
  }
888
653
  function provideLocationStrategy() {
889
- const platformLocation = inject(PlatformLocation);
890
- const baseHref = inject(APP_BASE_HREF_RESOLVED);
891
- const options = inject(LOCATION_UPGRADE_CONFIGURATION);
892
- return options.useHash
893
- ? new HashLocationStrategy(platformLocation, baseHref)
894
- : new PathLocationStrategy(platformLocation, baseHref);
654
+ const platformLocation = inject(PlatformLocation);
655
+ const baseHref = inject(APP_BASE_HREF_RESOLVED);
656
+ const options = inject(LOCATION_UPGRADE_CONFIGURATION);
657
+ return options.useHash ? new HashLocationStrategy(platformLocation, baseHref) : new PathLocationStrategy(platformLocation, baseHref);
895
658
  }
896
659
  function provide$location() {
897
- const $locationProvider = new $locationShimProvider(inject(UpgradeModule), inject(Location), inject(PlatformLocation), inject(UrlCodec), inject(LocationStrategy));
898
- return $locationProvider.$get();
660
+ const $locationProvider = new $locationShimProvider(inject(UpgradeModule), inject(Location), inject(PlatformLocation), inject(UrlCodec), inject(LocationStrategy));
661
+ return $locationProvider.$get();
899
662
  }
900
663
 
901
664
  export { $locationShim, $locationShimProvider, AngularJSUrlCodec, LOCATION_UPGRADE_CONFIGURATION, LocationUpgradeModule, UrlCodec };