-
Notifications
You must be signed in to change notification settings - Fork 25.3k
/
location.ts
115 lines (99 loc) · 3.37 KB
/
location.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DOCUMENT, LocationChangeEvent, LocationChangeListener, PlatformLocation, ɵgetDOM as getDOM} from '@angular/common';
import {Inject, Injectable, Optional} from '@angular/core';
import {Subject} from 'rxjs';
import * as url from 'url';
import {INITIAL_CONFIG, PlatformConfig} from './tokens';
function parseUrl(urlStr: string) {
const parsedUrl = url.parse(urlStr);
return {
hostname: parsedUrl.hostname || '',
protocol: parsedUrl.protocol || '',
port: parsedUrl.port || '',
pathname: parsedUrl.pathname || '',
search: parsedUrl.search || '',
hash: parsedUrl.hash || '',
};
}
/**
* Server-side implementation of URL state. Implements `pathname`, `search`, and `hash`
* but not the state stack.
*/
@Injectable()
export class ServerPlatformLocation implements PlatformLocation {
public readonly href: string = '/';
public readonly hostname: string = '/';
public readonly protocol: string = '/';
public readonly port: string = '/';
public readonly pathname: string = '/';
public readonly search: string = '';
public readonly hash: string = '';
private _hashUpdate = new Subject<LocationChangeEvent>();
constructor(
@Inject(DOCUMENT) private _doc: any, @Optional() @Inject(INITIAL_CONFIG) _config: any) {
const config = _config as PlatformConfig | null;
if (!!config && !!config.url) {
const parsedUrl = parseUrl(config.url);
this.hostname = parsedUrl.hostname;
this.protocol = parsedUrl.protocol;
this.port = parsedUrl.port;
this.pathname = parsedUrl.pathname;
this.search = parsedUrl.search;
this.hash = parsedUrl.hash;
this.href = _doc.location.href;
}
}
getBaseHrefFromDOM(): string {
return getDOM().getBaseHref(this._doc)!;
}
onPopState(fn: LocationChangeListener): void {
// No-op: a state stack is not implemented, so
// no events will ever come.
}
onHashChange(fn: LocationChangeListener): void {
this._hashUpdate.subscribe(fn);
}
get url(): string {
return `${this.pathname}${this.search}${this.hash}`;
}
private setHash(value: string, oldUrl: string) {
if (this.hash === value) {
// Don't fire events if the hash has not changed.
return;
}
(this as {hash: string}).hash = value;
const newUrl = this.url;
scheduleMicroTask(
() => this._hashUpdate.next(
{type: 'hashchange', state: null, oldUrl, newUrl} as LocationChangeEvent));
}
replaceState(state: any, title: string, newUrl: string): void {
const oldUrl = this.url;
const parsedUrl = parseUrl(newUrl);
(this as {pathname: string}).pathname = parsedUrl.pathname;
(this as {search: string}).search = parsedUrl.search;
this.setHash(parsedUrl.hash, oldUrl);
}
pushState(state: any, title: string, newUrl: string): void {
this.replaceState(state, title, newUrl);
}
forward(): void {
throw new Error('Not implemented');
}
back(): void {
throw new Error('Not implemented');
}
// History API isn't available on server, therefore return undefined
getState(): unknown {
return undefined;
}
}
export function scheduleMicroTask(fn: Function) {
Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
}