@dooboostore/dom-parser 1.0.0 → 1.0.2
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/README.MD +47 -94
- package/dist/cjs/DomParser.js +33 -12
- package/dist/cjs/DomParser.js.map +2 -2
- package/dist/cjs/node/DocumentBase.js +4 -0
- package/dist/cjs/node/DocumentBase.js.map +2 -2
- package/dist/cjs/node/elements/Element.js.map +1 -1
- package/dist/cjs/node/elements/ElementBase.js +12 -2
- package/dist/cjs/node/elements/ElementBase.js.map +2 -2
- package/dist/cjs/node/elements/HTMLElement.js.map +1 -1
- package/dist/cjs/node/elements/HTMLElementBase.js +154 -2
- package/dist/cjs/node/elements/HTMLElementBase.js.map +2 -2
- package/dist/cjs/window/WindowBase.js +128 -7
- package/dist/cjs/window/WindowBase.js.map +2 -2
- package/dist/esm/DomParser.js +33 -12
- package/dist/esm/DomParser.js.map +2 -2
- package/dist/esm/node/DocumentBase.js +4 -0
- package/dist/esm/node/DocumentBase.js.map +2 -2
- package/dist/esm/node/elements/ElementBase.js +12 -2
- package/dist/esm/node/elements/ElementBase.js.map +2 -2
- package/dist/esm/node/elements/HTMLElementBase.js +154 -2
- package/dist/esm/node/elements/HTMLElementBase.js.map +2 -2
- package/dist/esm/window/WindowBase.js +128 -7
- package/dist/esm/window/WindowBase.js.map +2 -2
- package/dist/esm-bundle/dooboostore-dom-parser.esm.js +504 -195
- package/dist/esm-bundle/dooboostore-dom-parser.esm.js.map +3 -3
- package/dist/types/DomParser.d.ts +4 -0
- package/dist/types/DomParser.d.ts.map +1 -1
- package/dist/types/node/DocumentBase.d.ts +2 -0
- package/dist/types/node/DocumentBase.d.ts.map +1 -1
- package/dist/types/node/elements/Element.d.ts +1 -0
- package/dist/types/node/elements/Element.d.ts.map +1 -1
- package/dist/types/node/elements/ElementBase.d.ts +2 -2
- package/dist/types/node/elements/ElementBase.d.ts.map +1 -1
- package/dist/types/node/elements/HTMLElement.d.ts +32 -1
- package/dist/types/node/elements/HTMLElement.d.ts.map +1 -1
- package/dist/types/node/elements/HTMLElementBase.d.ts +11 -2
- package/dist/types/node/elements/HTMLElementBase.d.ts.map +1 -1
- package/dist/types/window/WindowBase.d.ts +12 -2
- package/dist/types/window/WindowBase.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-dom-parser.umd.js +504 -195
- package/dist/umd-bundle/dooboostore-dom-parser.umd.js.map +3 -3
- package/package.json +3 -10
- package/src/DomParser.ts +457 -436
- package/src/node/DocumentBase.ts +7 -2
- package/src/node/elements/Element.ts +24 -23
- package/src/node/elements/ElementBase.ts +50 -41
- package/src/node/elements/HTMLElement.ts +36 -1
- package/src/node/elements/HTMLElementBase.ts +191 -5
- package/src/window/WindowBase.ts +1128 -919
package/src/window/WindowBase.ts
CHANGED
|
@@ -1,1018 +1,1227 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import {Window, Location, History, Navigator} from './Window';
|
|
2
|
+
import {Document} from '../node/Document';
|
|
3
|
+
import {DocumentBase} from '../node/DocumentBase';
|
|
4
|
+
import {NodeBase} from '../node/NodeBase';
|
|
5
|
+
import {ElementBase} from '../node/elements/ElementBase';
|
|
6
6
|
|
|
7
7
|
// Import all element classes
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
8
|
+
import {HTMLAnchorElement} from '../node/elements/HTMLAnchorElement';
|
|
9
|
+
import {HTMLBodyElement} from '../node/elements/HTMLBodyElement';
|
|
10
|
+
import {HTMLButtonElement} from '../node/elements/HTMLButtonElement';
|
|
11
|
+
import {HTMLCanvasElement} from '../node/elements/HTMLCanvasElement';
|
|
12
|
+
import {HTMLDivElement} from '../node/elements/HTMLDivElement';
|
|
13
|
+
import {HTMLH1Element} from '../node/elements/HTMLH1Element';
|
|
14
|
+
import {HTMLHeadElement} from '../node/elements/HTMLHeadElement';
|
|
15
|
+
import {HTMLHtmlElement} from '../node/elements/HTMLHtmlElement';
|
|
16
|
+
import {HTMLImgElement} from '../node/elements/HTMLImgElement';
|
|
17
|
+
import {HTMLInputElement} from '../node/elements/HTMLInputElement';
|
|
18
|
+
import {HTMLPElement} from '../node/elements/HTMLPElement';
|
|
19
|
+
import {HTMLSpanElement} from '../node/elements/HTMLSpanElement';
|
|
20
|
+
import {HTMLTitleElement} from '../node/elements/HTMLTitleElement';
|
|
21
|
+
import {HTMLLinkElement} from '../node/elements/HTMLLinkElement';
|
|
22
|
+
import {HTMLScriptElement} from '../node/elements/HTMLScriptElement';
|
|
23
|
+
import {HTMLStyleElement} from '../node/elements/HTMLStyleElement';
|
|
24
|
+
import {HTMLFormElement} from '../node/elements/HTMLFormElement';
|
|
25
|
+
import {HTMLTableElement} from '../node/elements/HTMLTableElement';
|
|
26
|
+
import {HTMLUListElement} from '../node/elements/HTMLUListElement';
|
|
27
|
+
import {HTMLOListElement} from '../node/elements/HTMLOListElement';
|
|
28
|
+
import {HTMLLIElement} from '../node/elements/HTMLLIElement';
|
|
29
|
+
import {HTMLMetaElement} from '../node/elements/HTMLMetaElement';
|
|
30
|
+
import {HTMLTemplateElement} from '../node/elements/HTMLTemplateElement';
|
|
31
|
+
import {HTMLTheadElement} from '../node/elements/HTMLTheadElement';
|
|
32
|
+
import {HTMLTfootElement} from '../node/elements/HTMLTfootElement';
|
|
33
|
+
import {HTMLTrElement} from '../node/elements/HTMLTrElement';
|
|
34
|
+
import {HTMLTdElement} from '../node/elements/HTMLTdElement';
|
|
35
|
+
import {HTMLThElement} from '../node/elements/HTMLThElement';
|
|
36
|
+
import {HTMLCaptionElement} from '../node/elements/HTMLCaptionElement';
|
|
37
|
+
import {HTMLTbodyElement} from '../node/elements/HTMLTbodyElement';
|
|
38
38
|
|
|
39
39
|
class LocationBase implements Location {
|
|
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
|
-
}
|
|
40
|
+
private _href: string = 'about:blank';
|
|
41
|
+
private _protocol: string = 'about:';
|
|
42
|
+
private _host: string = '';
|
|
43
|
+
private _hostname: string = '';
|
|
44
|
+
private _port: string = '';
|
|
45
|
+
private _pathname: string = 'blank';
|
|
46
|
+
private _search: string = '';
|
|
47
|
+
private _hash: string = '';
|
|
48
|
+
private _origin: string = 'null';
|
|
49
|
+
private urlChangeCallback?: (url: string) => void;
|
|
50
|
+
|
|
51
|
+
constructor(initialUrl: string = 'about:blank', urlChangeCallback?: (url: string) => void) {
|
|
52
|
+
this.urlChangeCallback = urlChangeCallback;
|
|
53
|
+
this.parseUrl(initialUrl);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get href(): string {
|
|
57
|
+
return this._href;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
set href(url: string) {
|
|
61
|
+
const oldHref = this._href;
|
|
62
|
+
this.parseUrl(url);
|
|
63
|
+
|
|
64
|
+
// Call URL change callback if URL actually changed
|
|
65
|
+
if (this._href !== oldHref && this.urlChangeCallback) {
|
|
66
|
+
this.urlChangeCallback(this._href);
|
|
68
67
|
}
|
|
68
|
+
}
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
set protocol(value: string) {
|
|
75
|
-
if (!value.endsWith(':')) {
|
|
76
|
-
value += ':';
|
|
77
|
-
}
|
|
78
|
-
this._protocol = value;
|
|
79
|
-
this.reconstructUrl();
|
|
80
|
-
}
|
|
70
|
+
get protocol(): string {
|
|
71
|
+
return this._protocol;
|
|
72
|
+
}
|
|
81
73
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
set host(value: string) {
|
|
87
|
-
this._host = value;
|
|
88
|
-
// Parse hostname and port from host
|
|
89
|
-
const colonIndex = value.lastIndexOf(':');
|
|
90
|
-
if (colonIndex !== -1 && colonIndex > value.lastIndexOf(']')) {
|
|
91
|
-
// IPv6 addresses are enclosed in brackets, so check if colon is after the closing bracket
|
|
92
|
-
this._hostname = value.substring(0, colonIndex);
|
|
93
|
-
this._port = value.substring(colonIndex + 1);
|
|
94
|
-
} else {
|
|
95
|
-
this._hostname = value;
|
|
96
|
-
this._port = '';
|
|
97
|
-
}
|
|
98
|
-
this.reconstructUrl();
|
|
74
|
+
set protocol(value: string) {
|
|
75
|
+
if (!value.endsWith(':')) {
|
|
76
|
+
value += ':';
|
|
99
77
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
78
|
+
this._protocol = value;
|
|
79
|
+
this.reconstructUrl();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get host(): string {
|
|
83
|
+
return this._host;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
set host(value: string) {
|
|
87
|
+
this._host = value;
|
|
88
|
+
// Parse hostname and port from host
|
|
89
|
+
const colonIndex = value.lastIndexOf(':');
|
|
90
|
+
if (colonIndex !== -1 && colonIndex > value.lastIndexOf(']')) {
|
|
91
|
+
// IPv6 addresses are enclosed in brackets, so check if colon is after the closing bracket
|
|
92
|
+
this._hostname = value.substring(0, colonIndex);
|
|
93
|
+
this._port = value.substring(colonIndex + 1);
|
|
94
|
+
} else {
|
|
95
|
+
this._hostname = value;
|
|
96
|
+
this._port = '';
|
|
103
97
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
this.reconstructUrl();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get hostname(): string {
|
|
102
|
+
return this._hostname;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
set hostname(value: string) {
|
|
106
|
+
this._hostname = value;
|
|
107
|
+
this._host = this._port ? `${value}:${this._port}` : value;
|
|
108
|
+
this.reconstructUrl();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get port(): string {
|
|
112
|
+
return this._port;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
set port(value: string) {
|
|
116
|
+
this._port = value;
|
|
117
|
+
this._host = value ? `${this._hostname}:${value}` : this._hostname;
|
|
118
|
+
this.reconstructUrl();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get pathname(): string {
|
|
122
|
+
return this._pathname;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
set pathname(value: string) {
|
|
126
|
+
if (!value.startsWith('/')) {
|
|
127
|
+
value = '/' + value;
|
|
109
128
|
}
|
|
129
|
+
this._pathname = value;
|
|
130
|
+
this.reconstructUrl();
|
|
131
|
+
}
|
|
110
132
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
133
|
+
get search(): string {
|
|
134
|
+
return this._search;
|
|
135
|
+
}
|
|
114
136
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.reconstructUrl();
|
|
137
|
+
set search(value: string) {
|
|
138
|
+
if (value && !value.startsWith('?')) {
|
|
139
|
+
value = '?' + value;
|
|
119
140
|
}
|
|
141
|
+
this._search = value;
|
|
142
|
+
this.reconstructUrl();
|
|
143
|
+
}
|
|
120
144
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
get hash(): string {
|
|
146
|
+
return this._hash;
|
|
147
|
+
}
|
|
124
148
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
this._pathname = value;
|
|
130
|
-
this.reconstructUrl();
|
|
149
|
+
set hash(value: string) {
|
|
150
|
+
if (value && !value.startsWith('#')) {
|
|
151
|
+
value = '#' + value;
|
|
131
152
|
}
|
|
153
|
+
this._hash = value;
|
|
154
|
+
this.reconstructUrl();
|
|
155
|
+
}
|
|
132
156
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
157
|
+
get origin(): string {
|
|
158
|
+
return this._origin;
|
|
159
|
+
}
|
|
136
160
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
this._search = value;
|
|
142
|
-
this.reconstructUrl();
|
|
143
|
-
}
|
|
161
|
+
assign(url: string): void {
|
|
162
|
+
const oldHref = this._href;
|
|
163
|
+
this.parseUrl(url);
|
|
144
164
|
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
// Call URL change callback if URL actually changed
|
|
166
|
+
if (this._href !== oldHref && this.urlChangeCallback) {
|
|
167
|
+
this.urlChangeCallback(this._href);
|
|
147
168
|
}
|
|
169
|
+
}
|
|
148
170
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
this._hash = value;
|
|
154
|
-
this.reconstructUrl();
|
|
155
|
-
}
|
|
171
|
+
replace(url: string): void {
|
|
172
|
+
const oldHref = this._href;
|
|
173
|
+
this.parseUrl(url);
|
|
156
174
|
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
// Call URL change callback if URL actually changed
|
|
176
|
+
if (this._href !== oldHref && this.urlChangeCallback) {
|
|
177
|
+
this.urlChangeCallback(this._href);
|
|
159
178
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
reload(forcedReload?: boolean): void {
|
|
182
|
+
// No-op in server-side environment
|
|
183
|
+
// In a real browser, this would reload the page
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
toString(): string {
|
|
187
|
+
return this._href;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private parseUrl(url: string): void {
|
|
191
|
+
try {
|
|
192
|
+
// Handle relative URLs by creating a base URL
|
|
193
|
+
let parsedUrl: URL;
|
|
194
|
+
|
|
195
|
+
if (url.startsWith('//')) {
|
|
196
|
+
// Protocol-relative URL
|
|
197
|
+
parsedUrl = new URL(this._protocol + url);
|
|
198
|
+
} else if (url.startsWith('/')) {
|
|
199
|
+
// Absolute path
|
|
200
|
+
parsedUrl = new URL(url, `${this._protocol}//${this._host}`);
|
|
201
|
+
} else if (url.includes('://')) {
|
|
202
|
+
// Absolute URL
|
|
203
|
+
parsedUrl = new URL(url);
|
|
204
|
+
} else {
|
|
205
|
+
// Relative URL
|
|
206
|
+
const base = `${this._protocol}//${this._host}${this._pathname}`;
|
|
207
|
+
parsedUrl = new URL(url, base);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this._href = parsedUrl.href;
|
|
211
|
+
this._protocol = parsedUrl.protocol;
|
|
212
|
+
this._host = parsedUrl.host;
|
|
213
|
+
this._hostname = parsedUrl.hostname;
|
|
214
|
+
this._port = parsedUrl.port;
|
|
215
|
+
this._pathname = parsedUrl.pathname;
|
|
216
|
+
this._search = parsedUrl.search;
|
|
217
|
+
this._hash = parsedUrl.hash;
|
|
218
|
+
this._origin = parsedUrl.origin;
|
|
219
|
+
} catch (e) {
|
|
220
|
+
// Invalid URL, handle special cases
|
|
221
|
+
if (url === 'about:blank') {
|
|
222
|
+
this._href = 'about:blank';
|
|
223
|
+
this._protocol = 'about:';
|
|
224
|
+
this._host = '';
|
|
225
|
+
this._hostname = '';
|
|
226
|
+
this._port = '';
|
|
227
|
+
this._pathname = 'blank';
|
|
228
|
+
this._search = '';
|
|
229
|
+
this._hash = '';
|
|
230
|
+
this._origin = 'null';
|
|
231
|
+
}
|
|
232
|
+
// For other invalid URLs, keep current values
|
|
169
233
|
}
|
|
234
|
+
}
|
|
170
235
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
236
|
+
private reconstructUrl(): void {
|
|
237
|
+
try {
|
|
238
|
+
// Reconstruct the URL from components
|
|
239
|
+
let url = this._protocol;
|
|
174
240
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
reload(forcedReload?: boolean): void {
|
|
182
|
-
// No-op in server-side environment
|
|
183
|
-
// In a real browser, this would reload the page
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
toString(): string {
|
|
187
|
-
return this._href;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private parseUrl(url: string): void {
|
|
191
|
-
try {
|
|
192
|
-
// Handle relative URLs by creating a base URL
|
|
193
|
-
let parsedUrl: URL;
|
|
194
|
-
|
|
195
|
-
if (url.startsWith('//')) {
|
|
196
|
-
// Protocol-relative URL
|
|
197
|
-
parsedUrl = new URL(this._protocol + url);
|
|
198
|
-
} else if (url.startsWith('/')) {
|
|
199
|
-
// Absolute path
|
|
200
|
-
parsedUrl = new URL(url, `${this._protocol}//${this._host}`);
|
|
201
|
-
} else if (url.includes('://')) {
|
|
202
|
-
// Absolute URL
|
|
203
|
-
parsedUrl = new URL(url);
|
|
204
|
-
} else {
|
|
205
|
-
// Relative URL
|
|
206
|
-
const base = `${this._protocol}//${this._host}${this._pathname}`;
|
|
207
|
-
parsedUrl = new URL(url, base);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
this._href = parsedUrl.href;
|
|
211
|
-
this._protocol = parsedUrl.protocol;
|
|
212
|
-
this._host = parsedUrl.host;
|
|
213
|
-
this._hostname = parsedUrl.hostname;
|
|
214
|
-
this._port = parsedUrl.port;
|
|
215
|
-
this._pathname = parsedUrl.pathname;
|
|
216
|
-
this._search = parsedUrl.search;
|
|
217
|
-
this._hash = parsedUrl.hash;
|
|
218
|
-
this._origin = parsedUrl.origin;
|
|
219
|
-
} catch (e) {
|
|
220
|
-
// Invalid URL, handle special cases
|
|
221
|
-
if (url === 'about:blank') {
|
|
222
|
-
this._href = 'about:blank';
|
|
223
|
-
this._protocol = 'about:';
|
|
224
|
-
this._host = '';
|
|
225
|
-
this._hostname = '';
|
|
226
|
-
this._port = '';
|
|
227
|
-
this._pathname = 'blank';
|
|
228
|
-
this._search = '';
|
|
229
|
-
this._hash = '';
|
|
230
|
-
this._origin = 'null';
|
|
231
|
-
}
|
|
232
|
-
// For other invalid URLs, keep current values
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
private reconstructUrl(): void {
|
|
237
|
-
try {
|
|
238
|
-
// Reconstruct the URL from components
|
|
239
|
-
let url = this._protocol;
|
|
240
|
-
|
|
241
|
-
if (this._protocol !== 'about:' && this._protocol !== 'data:') {
|
|
242
|
-
url += '//';
|
|
243
|
-
if (this._host) {
|
|
244
|
-
url += this._host;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
url += this._pathname;
|
|
249
|
-
url += this._search;
|
|
250
|
-
url += this._hash;
|
|
251
|
-
|
|
252
|
-
// Validate the reconstructed URL
|
|
253
|
-
const testUrl = new URL(url);
|
|
254
|
-
this._href = testUrl.href;
|
|
255
|
-
this._origin = testUrl.origin;
|
|
256
|
-
} catch (e) {
|
|
257
|
-
// If reconstruction fails, keep the current href
|
|
241
|
+
if (this._protocol !== 'about:' && this._protocol !== 'data:') {
|
|
242
|
+
url += '//';
|
|
243
|
+
if (this._host) {
|
|
244
|
+
url += this._host;
|
|
258
245
|
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
url += this._pathname;
|
|
249
|
+
url += this._search;
|
|
250
|
+
url += this._hash;
|
|
251
|
+
|
|
252
|
+
// Validate the reconstructed URL
|
|
253
|
+
const testUrl = new URL(url);
|
|
254
|
+
this._href = testUrl.href;
|
|
255
|
+
this._origin = testUrl.origin;
|
|
256
|
+
} catch (e) {
|
|
257
|
+
// If reconstruction fails, keep the current href
|
|
259
258
|
}
|
|
259
|
+
}
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
class HistoryBase implements History {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
263
|
+
length: number = 1;
|
|
264
|
+
state: any = null;
|
|
265
|
+
private window: WindowBase;
|
|
266
|
+
private historyStack: Array<{ state: any; title: string; url?: string }> = [];
|
|
267
|
+
private currentIndex: number = -1;
|
|
268
|
+
|
|
269
|
+
constructor(window: WindowBase) {
|
|
270
|
+
this.window = window;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
back(): void {
|
|
274
|
+
this.go(-1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
forward(): void {
|
|
278
|
+
this.go(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
go(delta?: number): void {
|
|
282
|
+
if (!delta || this.historyStack.length === 0) return;
|
|
283
|
+
|
|
284
|
+
const newIndex = this.currentIndex + delta;
|
|
285
|
+
if (newIndex >= 0 && newIndex < this.historyStack.length) {
|
|
286
|
+
this.currentIndex = newIndex;
|
|
287
|
+
const entry = this.historyStack[this.currentIndex];
|
|
288
|
+
this.state = entry.state;
|
|
289
|
+
|
|
290
|
+
// Update location if URL is provided
|
|
291
|
+
if (entry.url && this.window.location) {
|
|
292
|
+
(this.window.location as any)._href = entry.url;
|
|
293
|
+
(this.window.location as any).parseUrl(entry.url);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Dispatch popstate event
|
|
297
|
+
(this.window as any).dispatchPopStateEvent(entry.state, entry.url);
|
|
271
298
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
pushState(data: any, title: string, url?: string): void {
|
|
302
|
+
// Remove any forward history when pushing new state
|
|
303
|
+
this.historyStack = this.historyStack.slice(0, this.currentIndex + 1);
|
|
304
|
+
|
|
305
|
+
// Add new state
|
|
306
|
+
this.historyStack.push({state: data, title, url});
|
|
307
|
+
this.currentIndex = this.historyStack.length - 1;
|
|
308
|
+
this.state = data;
|
|
309
|
+
this.length = this.historyStack.length;
|
|
310
|
+
|
|
311
|
+
// Update location if URL is provided
|
|
312
|
+
if (url && this.window.location) {
|
|
313
|
+
const oldHref = (this.window.location as any)._href;
|
|
314
|
+
(this.window.location as any)._href = url;
|
|
315
|
+
(this.window.location as any).parseUrl(url);
|
|
316
|
+
|
|
317
|
+
// Trigger URL change callback for dynamic HTML loading
|
|
318
|
+
if (url !== oldHref && (this.window.location as any).urlChangeCallback) {
|
|
319
|
+
(this.window.location as any).urlChangeCallback(url);
|
|
320
|
+
}
|
|
275
321
|
}
|
|
276
322
|
|
|
277
|
-
|
|
278
|
-
|
|
323
|
+
// Note: pushState does NOT dispatch popstate event (per HTML spec)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
replaceState(data: any, title: string, url?: string): void {
|
|
327
|
+
if (this.currentIndex >= 0 && this.currentIndex < this.historyStack.length) {
|
|
328
|
+
// Replace current state
|
|
329
|
+
this.historyStack[this.currentIndex] = {state: data, title, url};
|
|
330
|
+
} else {
|
|
331
|
+
// No current state, create one
|
|
332
|
+
this.historyStack = [{state: data, title, url}];
|
|
333
|
+
this.currentIndex = 0;
|
|
334
|
+
this.length = 1;
|
|
279
335
|
}
|
|
280
336
|
|
|
281
|
-
|
|
282
|
-
if (!delta || this.historyStack.length === 0) return;
|
|
283
|
-
|
|
284
|
-
const newIndex = this.currentIndex + delta;
|
|
285
|
-
if (newIndex >= 0 && newIndex < this.historyStack.length) {
|
|
286
|
-
this.currentIndex = newIndex;
|
|
287
|
-
const entry = this.historyStack[this.currentIndex];
|
|
288
|
-
this.state = entry.state;
|
|
337
|
+
this.state = data;
|
|
289
338
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
339
|
+
// Update location if URL is provided
|
|
340
|
+
if (url && this.window.location) {
|
|
341
|
+
const oldHref = (this.window.location as any)._href;
|
|
342
|
+
(this.window.location as any)._href = url;
|
|
343
|
+
(this.window.location as any).parseUrl(url);
|
|
295
344
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
345
|
+
// Trigger URL change callback for dynamic HTML loading
|
|
346
|
+
if (url !== oldHref && (this.window.location as any).urlChangeCallback) {
|
|
347
|
+
(this.window.location as any).urlChangeCallback(url);
|
|
348
|
+
}
|
|
299
349
|
}
|
|
300
350
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
this.historyStack = this.historyStack.slice(0, this.currentIndex + 1);
|
|
304
|
-
|
|
305
|
-
// Add new state
|
|
306
|
-
this.historyStack.push({ state: data, title, url });
|
|
307
|
-
this.currentIndex = this.historyStack.length - 1;
|
|
308
|
-
this.state = data;
|
|
309
|
-
this.length = this.historyStack.length;
|
|
310
|
-
|
|
311
|
-
// Update location if URL is provided
|
|
312
|
-
if (url && this.window.location) {
|
|
313
|
-
const oldHref = (this.window.location as any)._href;
|
|
314
|
-
(this.window.location as any)._href = url;
|
|
315
|
-
(this.window.location as any).parseUrl(url);
|
|
316
|
-
|
|
317
|
-
// Trigger URL change callback for dynamic HTML loading
|
|
318
|
-
if (url !== oldHref && (this.window.location as any).urlChangeCallback) {
|
|
319
|
-
(this.window.location as any).urlChangeCallback(url);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Note: pushState does NOT dispatch popstate event (per HTML spec)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
replaceState(data: any, title: string, url?: string): void {
|
|
327
|
-
if (this.currentIndex >= 0 && this.currentIndex < this.historyStack.length) {
|
|
328
|
-
// Replace current state
|
|
329
|
-
this.historyStack[this.currentIndex] = { state: data, title, url };
|
|
330
|
-
} else {
|
|
331
|
-
// No current state, create one
|
|
332
|
-
this.historyStack = [{ state: data, title, url }];
|
|
333
|
-
this.currentIndex = 0;
|
|
334
|
-
this.length = 1;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
this.state = data;
|
|
338
|
-
|
|
339
|
-
// Update location if URL is provided
|
|
340
|
-
if (url && this.window.location) {
|
|
341
|
-
const oldHref = (this.window.location as any)._href;
|
|
342
|
-
(this.window.location as any)._href = url;
|
|
343
|
-
(this.window.location as any).parseUrl(url);
|
|
344
|
-
|
|
345
|
-
// Trigger URL change callback for dynamic HTML loading
|
|
346
|
-
if (url !== oldHref && (this.window.location as any).urlChangeCallback) {
|
|
347
|
-
(this.window.location as any).urlChangeCallback(url);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Note: replaceState does NOT dispatch popstate event (per HTML spec)
|
|
352
|
-
}
|
|
351
|
+
// Note: replaceState does NOT dispatch popstate event (per HTML spec)
|
|
352
|
+
}
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
class NavigatorBase implements Navigator {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
356
|
+
userAgent: string = 'Mozilla/5.0 (Server-Side Rendering)';
|
|
357
|
+
language: string = 'en-US';
|
|
358
|
+
languages: readonly string[] = ['en-US', 'en'];
|
|
359
|
+
platform: string = 'Server';
|
|
360
|
+
cookieEnabled: boolean = false;
|
|
361
|
+
onLine: boolean = true;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
// Simple event system for WindowBase
|
|
365
365
|
interface EventListener {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
type: string;
|
|
367
|
+
listener: (event: any) => void;
|
|
368
|
+
options?: boolean | AddEventListenerOptions;
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
interface PopStateEvent {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
372
|
+
type: 'popstate';
|
|
373
|
+
state: any;
|
|
374
|
+
url?: string;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
export class WindowBase {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
378
|
+
// Suppress type errors for complex event handler types
|
|
379
|
+
[key: string]: any;
|
|
380
|
+
|
|
381
|
+
// Event system
|
|
382
|
+
private _eventListeners: EventListener[] = [];
|
|
383
|
+
// Timers and intervals tracking
|
|
384
|
+
private _timers: Set<number> = new Set();
|
|
385
|
+
private _intervals: Set<number> = new Set();
|
|
386
|
+
private _animationFrames: Set<number> = new Set();
|
|
387
|
+
// Properties
|
|
388
|
+
readonly clientInformation: Navigator;
|
|
389
|
+
private _closed: boolean = false;
|
|
390
|
+
|
|
391
|
+
get closed(): boolean {
|
|
392
|
+
return this._closed;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
readonly cookieStore: CookieStore = {} as CookieStore;
|
|
396
|
+
readonly customElements: CustomElementRegistry = {} as CustomElementRegistry;
|
|
397
|
+
readonly devicePixelRatio: number = 1;
|
|
398
|
+
readonly document: Document;
|
|
399
|
+
readonly event: Event | undefined = undefined;
|
|
400
|
+
readonly external: External = {} as External;
|
|
401
|
+
readonly frameElement: Element | null = null;
|
|
402
|
+
readonly frames: WindowProxy = {} as WindowProxy;
|
|
403
|
+
readonly history: History;
|
|
404
|
+
readonly innerHeight: number = 768;
|
|
405
|
+
readonly innerWidth: number = 1024;
|
|
406
|
+
readonly length: number = 0;
|
|
407
|
+
readonly locationbar: BarProp = {} as BarProp;
|
|
408
|
+
readonly menubar: BarProp = {} as BarProp;
|
|
409
|
+
name: string = '';
|
|
410
|
+
readonly navigator: Navigator;
|
|
411
|
+
ondevicemotion: ((this: Window, ev: DeviceMotionEvent) => any) | null = null;
|
|
412
|
+
ondeviceorientation: ((this: Window, ev: DeviceOrientationEvent) => any) | null = null;
|
|
413
|
+
ondeviceorientationabsolute: ((this: Window, ev: DeviceOrientationEvent) => any) | null = null;
|
|
414
|
+
onorientationchange: ((this: Window, ev: Event) => any) | null = null;
|
|
415
|
+
opener: any = null;
|
|
416
|
+
readonly orientation: number = 0;
|
|
417
|
+
readonly originAgentCluster: boolean = false;
|
|
418
|
+
readonly outerHeight: number = 768;
|
|
419
|
+
readonly outerWidth: number = 1024;
|
|
420
|
+
readonly pageXOffset: number = 0;
|
|
421
|
+
readonly pageYOffset: number = 0;
|
|
422
|
+
readonly parent: WindowProxy = {} as WindowProxy;
|
|
423
|
+
readonly personalbar: BarProp = {} as BarProp;
|
|
424
|
+
readonly screen: Screen = {} as Screen;
|
|
425
|
+
readonly screenLeft: number = 0;
|
|
426
|
+
readonly screenTop: number = 0;
|
|
427
|
+
readonly screenX: number = 0;
|
|
428
|
+
readonly screenY: number = 0;
|
|
429
|
+
readonly scrollX: number = 0;
|
|
430
|
+
readonly scrollY: number = 0;
|
|
431
|
+
readonly scrollbars: BarProp = {} as BarProp;
|
|
432
|
+
readonly self: Window & typeof globalThis = this as any;
|
|
433
|
+
readonly speechSynthesis: SpeechSynthesis = {} as SpeechSynthesis;
|
|
434
|
+
status: string = '';
|
|
435
|
+
readonly statusbar: BarProp = {} as BarProp;
|
|
436
|
+
readonly toolbar: BarProp = {} as BarProp;
|
|
437
|
+
readonly top: WindowProxy | null = null;
|
|
438
|
+
readonly visualViewport: VisualViewport | null = null;
|
|
439
|
+
readonly window: Window & typeof globalThis = this as any;
|
|
440
|
+
|
|
441
|
+
// Location property with getter/setter
|
|
442
|
+
private _location: Location;
|
|
443
|
+
get location(): Location {
|
|
444
|
+
return this._location;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
set location(href: string | Location) {
|
|
448
|
+
if (typeof href === 'string') {
|
|
449
|
+
this._location.href = href;
|
|
450
|
+
} else {
|
|
451
|
+
this._location.href = href.href;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Global constructors
|
|
456
|
+
Node = NodeBase;
|
|
457
|
+
Element = ElementBase;
|
|
458
|
+
HTMLElement = ElementBase;
|
|
459
|
+
Event = class Event {
|
|
460
|
+
};
|
|
461
|
+
PopStateEvent = class PopStateEvent extends this.Event {
|
|
462
|
+
};
|
|
463
|
+
IntersectionObserver = class IntersectionObserver {
|
|
464
|
+
};
|
|
465
|
+
NodeFilter = class NodeFilter {
|
|
466
|
+
};
|
|
467
|
+
DocumentFragment = class DocumentFragment {
|
|
468
|
+
};
|
|
469
|
+
HTMLMetaElement = HTMLMetaElement;
|
|
470
|
+
HTMLCanvasElement = HTMLCanvasElement;
|
|
471
|
+
CanvasRenderingContext2D = class CanvasRenderingContext2D {
|
|
472
|
+
};
|
|
473
|
+
CanvasPattern = class CanvasPattern {
|
|
474
|
+
};
|
|
475
|
+
CanvasGradient = class CanvasGradient {
|
|
476
|
+
};
|
|
477
|
+
Path2D = class Path2D {
|
|
478
|
+
};
|
|
479
|
+
ImageData = class ImageData {
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// HTML element constructors
|
|
483
|
+
HTMLAnchorElement = HTMLAnchorElement;
|
|
484
|
+
HTMLBodyElement = HTMLBodyElement;
|
|
485
|
+
HTMLButtonElement = HTMLButtonElement;
|
|
486
|
+
HTMLDivElement = HTMLDivElement;
|
|
487
|
+
HTMLH1Element = HTMLH1Element;
|
|
488
|
+
HTMLHeadElement = HTMLHeadElement;
|
|
489
|
+
HTMLHtmlElement = HTMLHtmlElement;
|
|
490
|
+
HTMLImgElement = HTMLImgElement;
|
|
491
|
+
HTMLInputElement = HTMLInputElement;
|
|
492
|
+
HTMLPElement = HTMLPElement;
|
|
493
|
+
HTMLSpanElement = HTMLSpanElement;
|
|
494
|
+
HTMLTitleElement = HTMLTitleElement;
|
|
495
|
+
HTMLLinkElement = HTMLLinkElement;
|
|
496
|
+
HTMLScriptElement = HTMLScriptElement;
|
|
497
|
+
HTMLStyleElement = HTMLStyleElement;
|
|
498
|
+
HTMLFormElement = HTMLFormElement;
|
|
499
|
+
HTMLTableElement = HTMLTableElement;
|
|
500
|
+
HTMLUListElement = HTMLUListElement;
|
|
501
|
+
HTMLOListElement = HTMLOListElement;
|
|
502
|
+
HTMLLIElement = HTMLLIElement;
|
|
503
|
+
HTMLTemplateElement = HTMLTemplateElement;
|
|
504
|
+
HTMLTheadElement = HTMLTheadElement;
|
|
505
|
+
HTMLTfootElement = HTMLTfootElement;
|
|
506
|
+
HTMLTrElement = HTMLTrElement;
|
|
507
|
+
HTMLTdElement = HTMLTdElement;
|
|
508
|
+
HTMLThElement = HTMLThElement;
|
|
509
|
+
HTMLCaptionElement = HTMLCaptionElement;
|
|
510
|
+
HTMLTbodyElement = HTMLTbodyElement;
|
|
511
|
+
|
|
512
|
+
constructor(config?: { initialUrl?: string }) {
|
|
513
|
+
const documentBase = new DocumentBase();
|
|
514
|
+
|
|
515
|
+
// Set window reference in document for load event
|
|
516
|
+
if (this.document && (this.document as any).setWindow) {
|
|
517
|
+
(this.document as any).setWindow(this);
|
|
443
518
|
}
|
|
444
519
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
520
|
+
this._location = new LocationBase(config?.initialUrl);
|
|
521
|
+
documentBase.setLocation(this._location);
|
|
522
|
+
this.document = documentBase;
|
|
523
|
+
this.history = new HistoryBase(this);
|
|
524
|
+
this.navigator = new NavigatorBase();
|
|
525
|
+
this.clientInformation = this.navigator;
|
|
526
|
+
|
|
527
|
+
// Return a Proxy to handle the [index: number]: Window signature
|
|
528
|
+
return new Proxy(this, {
|
|
529
|
+
get(target: WindowBase, prop: string | symbol): any {
|
|
530
|
+
// Handle numeric indices - return the window itself
|
|
531
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
532
|
+
return target;
|
|
533
|
+
}
|
|
534
|
+
// Return the actual property
|
|
535
|
+
return (target as any)[prop];
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
set(target: WindowBase, prop: string | symbol, value: any): boolean {
|
|
539
|
+
// Handle numeric indices - ignore setting
|
|
540
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
// Set the actual property
|
|
544
|
+
(target as any)[prop] = value;
|
|
545
|
+
return true;
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
has(target: WindowBase, prop: string | symbol): boolean {
|
|
549
|
+
// Handle numeric indices
|
|
550
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
// Check if property exists
|
|
554
|
+
return prop in target;
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
ownKeys(target: WindowBase): ArrayLike<string | symbol> {
|
|
558
|
+
// Get all own keys plus numeric indices for length
|
|
559
|
+
const keys = Object.getOwnPropertyNames(target);
|
|
560
|
+
// Add numeric indices based on length property
|
|
561
|
+
for (let i = 0; i < target.length; i++) {
|
|
562
|
+
keys.push(i.toString());
|
|
563
|
+
}
|
|
564
|
+
return keys;
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
getOwnPropertyDescriptor(target: WindowBase, prop: string | symbol): PropertyDescriptor | undefined {
|
|
568
|
+
// Handle numeric indices
|
|
569
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
570
|
+
return {
|
|
571
|
+
enumerable: true,
|
|
572
|
+
configurable: true,
|
|
573
|
+
value: target
|
|
574
|
+
};
|
|
498
575
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
576
|
+
// Return actual property descriptor
|
|
577
|
+
return Object.getOwnPropertyDescriptor(target, prop);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Window methods - all with empty implementations for server-side
|
|
583
|
+
alert(message?: any): void {
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
blur(): void {
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
cancelIdleCallback(handle: number): void {
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
captureEvents(): void {
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
close(): void {
|
|
596
|
+
if (this._closed) return;
|
|
597
|
+
|
|
598
|
+
this._closed = true;
|
|
599
|
+
|
|
600
|
+
// Clear all timers
|
|
601
|
+
this._timers.forEach(id => clearTimeout(id));
|
|
602
|
+
this._timers.clear();
|
|
603
|
+
|
|
604
|
+
// Clear all intervals
|
|
605
|
+
this._intervals.forEach(id => clearInterval(id));
|
|
606
|
+
this._intervals.clear();
|
|
607
|
+
|
|
608
|
+
// Clear all animation frames
|
|
609
|
+
this._animationFrames.forEach(id => clearTimeout(id));
|
|
610
|
+
this._animationFrames.clear();
|
|
611
|
+
|
|
612
|
+
// Remove all event listeners (clear references to prevent memory leaks)
|
|
613
|
+
this._eventListeners.forEach(listener => {
|
|
614
|
+
// Explicitly null out the listener function reference
|
|
615
|
+
(listener as any).listener = null;
|
|
616
|
+
});
|
|
617
|
+
this._eventListeners.length = 0;
|
|
618
|
+
|
|
619
|
+
// Clear all on* event handler properties
|
|
620
|
+
this.onload = null;
|
|
621
|
+
this.onunload = null;
|
|
622
|
+
this.onbeforeunload = null;
|
|
623
|
+
this.onpopstate = null;
|
|
624
|
+
this.onerror = null;
|
|
625
|
+
this.onmessage = null;
|
|
626
|
+
this.onhashchange = null;
|
|
627
|
+
|
|
628
|
+
// Clear document references
|
|
629
|
+
if (this.document) {
|
|
630
|
+
const doc = this.document as any;
|
|
631
|
+
|
|
632
|
+
// Clear document content recursively
|
|
633
|
+
if (doc.body) {
|
|
634
|
+
this.clearNodeRecursively(doc.body);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (doc.head) {
|
|
638
|
+
this.clearNodeRecursively(doc.head);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (doc.documentElement) {
|
|
642
|
+
this.clearNodeRecursively(doc.documentElement);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Break circular references
|
|
646
|
+
if (doc.setWindow) {
|
|
647
|
+
doc.setWindow(null);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Clear document event listeners
|
|
651
|
+
if (doc._eventListeners) {
|
|
652
|
+
doc._eventListeners.forEach((listener: any) => {
|
|
653
|
+
listener.listener = null;
|
|
557
654
|
});
|
|
655
|
+
doc._eventListeners.length = 0;
|
|
656
|
+
}
|
|
558
657
|
}
|
|
559
658
|
|
|
560
|
-
//
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
confirm(message?: string): boolean {
|
|
572
|
-
return false;
|
|
659
|
+
// Clear history
|
|
660
|
+
if (this.history) {
|
|
661
|
+
const hist = this.history as any;
|
|
662
|
+
if (hist.historyStack) {
|
|
663
|
+
hist.historyStack.forEach((entry: any) => {
|
|
664
|
+
entry.state = null;
|
|
665
|
+
});
|
|
666
|
+
hist.historyStack.length = 0;
|
|
667
|
+
}
|
|
668
|
+
hist.state = null;
|
|
669
|
+
hist.window = null;
|
|
573
670
|
}
|
|
574
671
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
return {} as CSSStyleDeclaration;
|
|
672
|
+
// Clear location callback
|
|
673
|
+
if (this._location) {
|
|
674
|
+
(this._location as any).urlChangeCallback = null;
|
|
579
675
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Recursively clear a node and its children to prevent memory leaks
|
|
680
|
+
*/
|
|
681
|
+
private clearNodeRecursively(node: any): void {
|
|
682
|
+
if (!node) return;
|
|
683
|
+
|
|
684
|
+
// Clear children first
|
|
685
|
+
while (node.firstChild) {
|
|
686
|
+
const child = node.firstChild;
|
|
687
|
+
node.removeChild(child);
|
|
688
|
+
this.clearNodeRecursively(child);
|
|
583
689
|
}
|
|
584
690
|
|
|
585
|
-
|
|
586
|
-
|
|
691
|
+
// Clear event listeners on the node
|
|
692
|
+
if (node._eventListeners) {
|
|
693
|
+
node._eventListeners.forEach((listener: any) => {
|
|
694
|
+
listener.listener = null;
|
|
695
|
+
});
|
|
696
|
+
node._eventListeners.length = 0;
|
|
587
697
|
}
|
|
588
698
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
699
|
+
// Clear node properties that might hold references (safely)
|
|
700
|
+
try {
|
|
701
|
+
if (node.parentNode) {
|
|
702
|
+
node.parentNode = null;
|
|
703
|
+
}
|
|
704
|
+
} catch (e) {
|
|
705
|
+
// parentNode might be read-only
|
|
595
706
|
}
|
|
596
707
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
prompt(message?: string, _default?: string): string | null {
|
|
604
|
-
return null;
|
|
708
|
+
try {
|
|
709
|
+
if (node.ownerDocument) {
|
|
710
|
+
node.ownerDocument = null;
|
|
711
|
+
}
|
|
712
|
+
} catch (e) {
|
|
713
|
+
// ownerDocument might be read-only (getter-only)
|
|
605
714
|
}
|
|
606
715
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
716
|
+
// Clear other potential circular references
|
|
717
|
+
if (node._childNodes) {
|
|
718
|
+
if (Array.isArray(node._childNodes)) {
|
|
719
|
+
node._childNodes = [];
|
|
720
|
+
} else if (node._childNodes instanceof Map) {
|
|
721
|
+
node._childNodes.clear();
|
|
722
|
+
}
|
|
611
723
|
}
|
|
724
|
+
if (node._attributes) {
|
|
725
|
+
if (Array.isArray(node._attributes)) {
|
|
726
|
+
node._attributes = [];
|
|
727
|
+
} else if (node._attributes instanceof Map) {
|
|
728
|
+
node._attributes.clear();
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
612
732
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
733
|
+
confirm(message?: string): boolean {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
616
736
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
scroll(optionsOrX?: ScrollToOptions | number, y?: number): void { }
|
|
737
|
+
focus(): void {
|
|
738
|
+
}
|
|
620
739
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
740
|
+
getComputedStyle(elt: Element, pseudoElt?: string | null): CSSStyleDeclaration {
|
|
741
|
+
return {} as CSSStyleDeclaration;
|
|
742
|
+
}
|
|
624
743
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
744
|
+
getSelection(): Selection | null {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
628
747
|
|
|
629
|
-
|
|
748
|
+
matchMedia(query: string): MediaQueryList {
|
|
749
|
+
return {} as MediaQueryList;
|
|
750
|
+
}
|
|
630
751
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
752
|
+
moveBy(x: number, y: number): void {
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
moveTo(x: number, y: number): void {
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
open(url?: string | URL, target?: string, features?: string): WindowProxy | null {
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void;
|
|
763
|
+
postMessage(message: any, options?: WindowPostMessageOptions): void;
|
|
764
|
+
postMessage(message: any, targetOriginOrOptions?: string | WindowPostMessageOptions, transfer?: Transferable[]): void {
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
print(): void {
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
prompt(message?: string, _default?: string): string | null {
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
releaseEvents(): void {
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
requestIdleCallback(callback: IdleRequestCallback, options?: IdleRequestOptions): number {
|
|
778
|
+
return 0;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
resizeBy(x: number, y: number): void {
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
resizeTo(width: number, height: number): void {
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
scroll(options?: ScrollToOptions): void;
|
|
788
|
+
scroll(x: number, y: number): void;
|
|
789
|
+
scroll(optionsOrX?: ScrollToOptions | number, y?: number): void {
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
scrollBy(options?: ScrollToOptions): void;
|
|
793
|
+
scrollBy(x: number, y: number): void;
|
|
794
|
+
scrollBy(optionsOrX?: ScrollToOptions | number, y?: number): void {
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
scrollTo(options?: ScrollToOptions): void;
|
|
798
|
+
scrollTo(x: number, y: number): void;
|
|
799
|
+
scrollTo(optionsOrX?: ScrollToOptions | number, y?: number): void {
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
stop(): void {
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Timer methods
|
|
806
|
+
setTimeout(callback: Function, delay?: number, ...args: any[]): number {
|
|
807
|
+
if (this._closed) return 0;
|
|
808
|
+
|
|
809
|
+
const id = setTimeout(() => {
|
|
810
|
+
this._timers.delete(id);
|
|
811
|
+
callback(...args);
|
|
812
|
+
}, delay) as any;
|
|
813
|
+
|
|
814
|
+
this._timers.add(id);
|
|
815
|
+
return id;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
clearTimeout(id: number): void {
|
|
819
|
+
clearTimeout(id);
|
|
820
|
+
this._timers.delete(id);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
setInterval(callback: Function, delay?: number, ...args: any[]): number {
|
|
824
|
+
if (this._closed) return 0;
|
|
825
|
+
|
|
826
|
+
const id = setInterval(callback, delay, ...args) as any;
|
|
827
|
+
this._intervals.add(id);
|
|
828
|
+
return id;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
clearInterval(id: number): void {
|
|
832
|
+
clearInterval(id);
|
|
833
|
+
this._intervals.delete(id);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Animation methods
|
|
837
|
+
requestAnimationFrame(callback: FrameRequestCallback): number {
|
|
838
|
+
if (this._closed) return 0;
|
|
839
|
+
|
|
840
|
+
const id = this.setTimeout(callback, 16) as number;
|
|
841
|
+
this._animationFrames.add(id);
|
|
842
|
+
return id;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
cancelAnimationFrame(id: number): void {
|
|
846
|
+
this.clearTimeout(id);
|
|
847
|
+
this._animationFrames.delete(id);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Event methods
|
|
851
|
+
addEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
852
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
853
|
+
addEventListener(type: any, listener: any, options?: any): void {
|
|
854
|
+
// Support popstate, load, unload, and beforeunload events
|
|
855
|
+
if (type === 'popstate' || type === 'load' || type === 'unload' || type === 'beforeunload') {
|
|
856
|
+
this._eventListeners.push({
|
|
857
|
+
type,
|
|
858
|
+
listener: typeof listener === 'function' ? listener : listener.handleEvent,
|
|
859
|
+
options
|
|
860
|
+
});
|
|
634
861
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
removeEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
865
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
866
|
+
removeEventListener(type: any, listener: any, options?: any): void {
|
|
867
|
+
if (type === 'popstate' || type === 'load' || type === 'unload' || type === 'beforeunload') {
|
|
868
|
+
const targetListener = typeof listener === 'function' ? listener : listener.handleEvent;
|
|
869
|
+
this._eventListeners = this._eventListeners.filter(
|
|
870
|
+
eventListener => !(eventListener.type === type && eventListener.listener === targetListener)
|
|
871
|
+
);
|
|
638
872
|
}
|
|
873
|
+
}
|
|
639
874
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
875
|
+
dispatchEvent(event: any): boolean {
|
|
876
|
+
const eventListeners = this._eventListeners.filter(listener => listener.type === event.type);
|
|
643
877
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
878
|
+
for (const eventListener of eventListeners) {
|
|
879
|
+
try {
|
|
880
|
+
eventListener.listener.call(this, event);
|
|
647
881
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
cancelAnimationFrame(id: number): void {
|
|
654
|
-
this.clearTimeout(id);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// Event methods
|
|
658
|
-
addEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
659
|
-
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
660
|
-
addEventListener(type: any, listener: any, options?: any): void {
|
|
661
|
-
// Support popstate, load, unload, and beforeunload events
|
|
662
|
-
if (type === 'popstate' || type === 'load' || type === 'unload' || type === 'beforeunload') {
|
|
663
|
-
this._eventListeners.push({
|
|
664
|
-
type,
|
|
665
|
-
listener: typeof listener === 'function' ? listener : listener.handleEvent,
|
|
666
|
-
options
|
|
667
|
-
});
|
|
882
|
+
// Handle 'once' option
|
|
883
|
+
if (eventListener.options && typeof eventListener.options === 'object' && eventListener.options.once) {
|
|
884
|
+
this.removeEventListener(event.type, eventListener.listener);
|
|
668
885
|
}
|
|
886
|
+
} catch (error) {
|
|
887
|
+
console.error('Error in event listener:', error);
|
|
888
|
+
}
|
|
669
889
|
}
|
|
670
890
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Dispatch a popstate event to registered listeners
|
|
896
|
+
*/
|
|
897
|
+
private dispatchPopStateEvent(state: any, url?: string): void {
|
|
898
|
+
const popStateEvent: PopStateEvent = {
|
|
899
|
+
type: 'popstate',
|
|
900
|
+
state,
|
|
901
|
+
url
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
this.dispatchEvent(popStateEvent);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// EventTarget methods (inherited)
|
|
908
|
+
// These are already covered by addEventListener/removeEventListener/dispatchEvent
|
|
909
|
+
|
|
910
|
+
// GlobalEventHandlers - empty implementations
|
|
911
|
+
onabort: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null = null;
|
|
912
|
+
onanimationcancel: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null = null;
|
|
913
|
+
onanimationend: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null = null;
|
|
914
|
+
onanimationiteration: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null = null;
|
|
915
|
+
onanimationstart: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null = null;
|
|
916
|
+
onauxclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
917
|
+
onbeforeinput: ((this: GlobalEventHandlers, ev: InputEvent) => any) | null = null;
|
|
918
|
+
onblur: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null = null;
|
|
919
|
+
oncancel: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
920
|
+
oncanplay: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
921
|
+
oncanplaythrough: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
922
|
+
onchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
923
|
+
onclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
924
|
+
onclose: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
925
|
+
oncontextmenu: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
926
|
+
oncopy: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null = null;
|
|
927
|
+
oncuechange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
928
|
+
oncut: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null = null;
|
|
929
|
+
ondblclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
930
|
+
ondrag: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
931
|
+
ondragend: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
932
|
+
ondragenter: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
933
|
+
ondragleave: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
934
|
+
ondragover: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
935
|
+
ondragstart: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
936
|
+
ondrop: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
937
|
+
ondurationchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
938
|
+
onemptied: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
939
|
+
onended: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
940
|
+
onerror: OnErrorEventHandler = null;
|
|
941
|
+
onfocus: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null = null;
|
|
942
|
+
onformdata: ((this: GlobalEventHandlers, ev: FormDataEvent) => any) | null = null;
|
|
943
|
+
ongotpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
944
|
+
oninput: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
945
|
+
oninvalid: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
946
|
+
onkeydown: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null = null;
|
|
947
|
+
onkeypress: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null = null;
|
|
948
|
+
onkeyup: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null = null;
|
|
949
|
+
onload: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
950
|
+
onloadeddata: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
951
|
+
onloadedmetadata: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
952
|
+
onloadstart: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
953
|
+
onlostpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
954
|
+
onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
955
|
+
onmouseenter: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
956
|
+
onmouseleave: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
957
|
+
onmousemove: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
958
|
+
onmouseout: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
959
|
+
onmouseover: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
960
|
+
onmouseup: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
961
|
+
onpaste: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null = null;
|
|
962
|
+
onpause: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
963
|
+
onplay: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
964
|
+
onplaying: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
965
|
+
onpointercancel: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
966
|
+
onpointerdown: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
967
|
+
onpointerenter: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
968
|
+
onpointerleave: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
969
|
+
onpointermove: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
970
|
+
onpointerout: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
971
|
+
onpointerover: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
972
|
+
onpointerup: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
973
|
+
onprogress: ((this: GlobalEventHandlers, ev: ProgressEvent<EventTarget>) => any) | null = null;
|
|
974
|
+
onratechange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
975
|
+
onreset: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
976
|
+
onresize: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null = null;
|
|
977
|
+
onscroll: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
978
|
+
onscrollend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
979
|
+
onsecuritypolicyviolation: ((this: GlobalEventHandlers, ev: SecurityPolicyViolationEvent) => any) | null = null;
|
|
980
|
+
onseeked: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
981
|
+
onseeking: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
982
|
+
onselect: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
983
|
+
onselectionchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
984
|
+
onselectstart: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
985
|
+
onslotchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
986
|
+
onstalled: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
987
|
+
onsubmit: ((this: GlobalEventHandlers, ev: SubmitEvent) => any) | null = null;
|
|
988
|
+
onsuspend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
989
|
+
ontimeupdate: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
990
|
+
ontoggle: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
991
|
+
ontouchcancel?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
992
|
+
ontouchend?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
993
|
+
ontouchmove?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
994
|
+
ontouchstart?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
995
|
+
ontransitioncancel: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
996
|
+
ontransitionend: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
997
|
+
ontransitionrun: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
998
|
+
ontransitionstart: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
999
|
+
onvolumechange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
1000
|
+
onwaiting: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
1001
|
+
onwebkitanimationend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
1002
|
+
onwebkitanimationiteration: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
1003
|
+
onwebkitanimationstart: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
1004
|
+
onwebkittransitionend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
1005
|
+
onwheel: ((this: GlobalEventHandlers, ev: WheelEvent) => any) | null = null;
|
|
1006
|
+
|
|
1007
|
+
// WindowEventHandlers - empty implementations
|
|
1008
|
+
onafterprint: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
1009
|
+
onbeforeprint: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
1010
|
+
onbeforeunload: ((this: WindowEventHandlers, ev: BeforeUnloadEvent) => any) | null = null;
|
|
1011
|
+
onhashchange: ((this: WindowEventHandlers, ev: HashChangeEvent) => any) | null = null;
|
|
1012
|
+
onlanguagechange: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
1013
|
+
onmessage: ((this: WindowEventHandlers, ev: MessageEvent) => any) | null = null;
|
|
1014
|
+
onmessageerror: ((this: WindowEventHandlers, ev: MessageEvent) => any) | null = null;
|
|
1015
|
+
onoffline: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
1016
|
+
ononline: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
1017
|
+
onpagehide: ((this: WindowEventHandlers, ev: PageTransitionEvent) => any) | null = null;
|
|
1018
|
+
onpageshow: ((this: WindowEventHandlers, ev: PageTransitionEvent) => any) | null = null;
|
|
1019
|
+
onpopstate: ((this: WindowEventHandlers, ev: PopStateEvent) => any) | null = null;
|
|
1020
|
+
onrejectionhandled: ((this: WindowEventHandlers, ev: PromiseRejectionEvent) => any) | null = null;
|
|
1021
|
+
onstorage: ((this: WindowEventHandlers, ev: StorageEvent) => any) | null = null;
|
|
1022
|
+
onunhandledrejection: ((this: WindowEventHandlers, ev: PromiseRejectionEvent) => any) | null = null;
|
|
1023
|
+
onunload: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
1024
|
+
|
|
1025
|
+
// WindowLocalStorage
|
|
1026
|
+
readonly localStorage: Storage = {} as Storage;
|
|
1027
|
+
|
|
1028
|
+
// WindowOrWorkerGlobalScope - empty implementations
|
|
1029
|
+
readonly caches: CacheStorage = {} as CacheStorage;
|
|
1030
|
+
readonly crossOriginIsolated: boolean = false;
|
|
1031
|
+
readonly crypto: Crypto = {} as Crypto;
|
|
1032
|
+
readonly indexedDB: IDBFactory = {} as IDBFactory;
|
|
1033
|
+
readonly isSecureContext: boolean = false;
|
|
1034
|
+
readonly origin: string = 'null';
|
|
1035
|
+
readonly performance: Performance = {} as Performance;
|
|
1036
|
+
|
|
1037
|
+
atob(data: string): string {
|
|
1038
|
+
return '';
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
btoa(data: string): string {
|
|
1042
|
+
return '';
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
createImageBitmap(image: ImageBitmapSource, options?: ImageBitmapOptions): Promise<ImageBitmap>;
|
|
1046
|
+
createImageBitmap(image: ImageBitmapSource, sx: number, sy: number, sw: number, sh: number, options?: ImageBitmapOptions): Promise<ImageBitmap>;
|
|
1047
|
+
createImageBitmap(...args: any[]): Promise<ImageBitmap> {
|
|
1048
|
+
return Promise.resolve({} as ImageBitmap);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
1052
|
+
return Promise.resolve({} as Response);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
queueMicrotask(callback: VoidFunction): void {
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
reportError(e: any): void {
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
structuredClone(value: any, options?: StructuredSerializeOptions): any {
|
|
1062
|
+
return value;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// WindowSessionStorage
|
|
1066
|
+
readonly sessionStorage: Storage = {} as Storage;
|
|
1067
|
+
|
|
1068
|
+
// AnimationFrameProvider - already implemented above
|
|
1069
|
+
|
|
1070
|
+
// Index signature - getter implementation
|
|
1071
|
+
[index: number]: Window;
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* Recursive HTML parsing
|
|
1076
|
+
*/
|
|
1077
|
+
private parseHTMLRecursive(html: string, parent: any): void {
|
|
1078
|
+
let position = 0;
|
|
1079
|
+
|
|
1080
|
+
while (position < html.length) {
|
|
1081
|
+
const tagStart = html.indexOf('<', position);
|
|
1082
|
+
|
|
1083
|
+
if (tagStart === -1) {
|
|
1084
|
+
const remainingText = html.substring(position).trim();
|
|
1085
|
+
if (remainingText) {
|
|
1086
|
+
const textNode = this.document.createTextNode(remainingText);
|
|
1087
|
+
parent.appendChild(textNode);
|
|
679
1088
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
// Handle 'once' option
|
|
690
|
-
if (eventListener.options && typeof eventListener.options === 'object' && eventListener.options.once) {
|
|
691
|
-
this.removeEventListener(event.type, eventListener.listener);
|
|
692
|
-
}
|
|
693
|
-
} catch (error) {
|
|
694
|
-
console.error('Error in event listener:', error);
|
|
695
|
-
}
|
|
1089
|
+
break;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
if (tagStart > position) {
|
|
1093
|
+
const textContent = html.substring(position, tagStart).trim();
|
|
1094
|
+
if (textContent) {
|
|
1095
|
+
const textNode = this.document.createTextNode(textContent);
|
|
1096
|
+
parent.appendChild(textNode);
|
|
696
1097
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
onclose: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
732
|
-
oncontextmenu: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
733
|
-
oncopy: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null = null;
|
|
734
|
-
oncuechange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
735
|
-
oncut: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null = null;
|
|
736
|
-
ondblclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
737
|
-
ondrag: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
738
|
-
ondragend: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
739
|
-
ondragenter: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
740
|
-
ondragleave: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
741
|
-
ondragover: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
742
|
-
ondragstart: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
743
|
-
ondrop: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null = null;
|
|
744
|
-
ondurationchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
745
|
-
onemptied: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
746
|
-
onended: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
747
|
-
onerror: OnErrorEventHandler = null;
|
|
748
|
-
onfocus: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null = null;
|
|
749
|
-
onformdata: ((this: GlobalEventHandlers, ev: FormDataEvent) => any) | null = null;
|
|
750
|
-
ongotpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
751
|
-
oninput: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
752
|
-
oninvalid: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
753
|
-
onkeydown: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null = null;
|
|
754
|
-
onkeypress: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null = null;
|
|
755
|
-
onkeyup: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null = null;
|
|
756
|
-
onload: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
757
|
-
onloadeddata: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
758
|
-
onloadedmetadata: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
759
|
-
onloadstart: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
760
|
-
onlostpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
761
|
-
onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
762
|
-
onmouseenter: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
763
|
-
onmouseleave: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
764
|
-
onmousemove: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
765
|
-
onmouseout: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
766
|
-
onmouseover: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
767
|
-
onmouseup: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null = null;
|
|
768
|
-
onpaste: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null = null;
|
|
769
|
-
onpause: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
770
|
-
onplay: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
771
|
-
onplaying: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
772
|
-
onpointercancel: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
773
|
-
onpointerdown: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
774
|
-
onpointerenter: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
775
|
-
onpointerleave: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
776
|
-
onpointermove: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
777
|
-
onpointerout: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
778
|
-
onpointerover: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
779
|
-
onpointerup: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null = null;
|
|
780
|
-
onprogress: ((this: GlobalEventHandlers, ev: ProgressEvent<EventTarget>) => any) | null = null;
|
|
781
|
-
onratechange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
782
|
-
onreset: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
783
|
-
onresize: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null = null;
|
|
784
|
-
onscroll: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
785
|
-
onscrollend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
786
|
-
onsecuritypolicyviolation: ((this: GlobalEventHandlers, ev: SecurityPolicyViolationEvent) => any) | null = null;
|
|
787
|
-
onseeked: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
788
|
-
onseeking: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
789
|
-
onselect: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
790
|
-
onselectionchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
791
|
-
onselectstart: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
792
|
-
onslotchange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
793
|
-
onstalled: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
794
|
-
onsubmit: ((this: GlobalEventHandlers, ev: SubmitEvent) => any) | null = null;
|
|
795
|
-
onsuspend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
796
|
-
ontimeupdate: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
797
|
-
ontoggle: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
798
|
-
ontouchcancel?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
799
|
-
ontouchend?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
800
|
-
ontouchmove?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
801
|
-
ontouchstart?: ((this: GlobalEventHandlers, ev: TouchEvent) => any) | null = null;
|
|
802
|
-
ontransitioncancel: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
803
|
-
ontransitionend: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
804
|
-
ontransitionrun: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
805
|
-
ontransitionstart: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null = null;
|
|
806
|
-
onvolumechange: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
807
|
-
onwaiting: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
808
|
-
onwebkitanimationend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
809
|
-
onwebkitanimationiteration: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
810
|
-
onwebkitanimationstart: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
811
|
-
onwebkittransitionend: ((this: GlobalEventHandlers, ev: Event) => any) | null = null;
|
|
812
|
-
onwheel: ((this: GlobalEventHandlers, ev: WheelEvent) => any) | null = null;
|
|
813
|
-
|
|
814
|
-
// WindowEventHandlers - empty implementations
|
|
815
|
-
onafterprint: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
816
|
-
onbeforeprint: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
817
|
-
onbeforeunload: ((this: WindowEventHandlers, ev: BeforeUnloadEvent) => any) | null = null;
|
|
818
|
-
onhashchange: ((this: WindowEventHandlers, ev: HashChangeEvent) => any) | null = null;
|
|
819
|
-
onlanguagechange: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
820
|
-
onmessage: ((this: WindowEventHandlers, ev: MessageEvent) => any) | null = null;
|
|
821
|
-
onmessageerror: ((this: WindowEventHandlers, ev: MessageEvent) => any) | null = null;
|
|
822
|
-
onoffline: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
823
|
-
ononline: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
824
|
-
onpagehide: ((this: WindowEventHandlers, ev: PageTransitionEvent) => any) | null = null;
|
|
825
|
-
onpageshow: ((this: WindowEventHandlers, ev: PageTransitionEvent) => any) | null = null;
|
|
826
|
-
onpopstate: ((this: WindowEventHandlers, ev: PopStateEvent) => any) | null = null;
|
|
827
|
-
onrejectionhandled: ((this: WindowEventHandlers, ev: PromiseRejectionEvent) => any) | null = null;
|
|
828
|
-
onstorage: ((this: WindowEventHandlers, ev: StorageEvent) => any) | null = null;
|
|
829
|
-
onunhandledrejection: ((this: WindowEventHandlers, ev: PromiseRejectionEvent) => any) | null = null;
|
|
830
|
-
onunload: ((this: WindowEventHandlers, ev: Event) => any) | null = null;
|
|
831
|
-
|
|
832
|
-
// WindowLocalStorage
|
|
833
|
-
readonly localStorage: Storage = {} as Storage;
|
|
834
|
-
|
|
835
|
-
// WindowOrWorkerGlobalScope - empty implementations
|
|
836
|
-
readonly caches: CacheStorage = {} as CacheStorage;
|
|
837
|
-
readonly crossOriginIsolated: boolean = false;
|
|
838
|
-
readonly crypto: Crypto = {} as Crypto;
|
|
839
|
-
readonly indexedDB: IDBFactory = {} as IDBFactory;
|
|
840
|
-
readonly isSecureContext: boolean = false;
|
|
841
|
-
readonly origin: string = 'null';
|
|
842
|
-
readonly performance: Performance = {} as Performance;
|
|
843
|
-
|
|
844
|
-
atob(data: string): string { return ''; }
|
|
845
|
-
btoa(data: string): string { return ''; }
|
|
846
|
-
createImageBitmap(image: ImageBitmapSource, options?: ImageBitmapOptions): Promise<ImageBitmap>;
|
|
847
|
-
createImageBitmap(image: ImageBitmapSource, sx: number, sy: number, sw: number, sh: number, options?: ImageBitmapOptions): Promise<ImageBitmap>;
|
|
848
|
-
createImageBitmap(...args: any[]): Promise<ImageBitmap> { return Promise.resolve({} as ImageBitmap); }
|
|
849
|
-
|
|
850
|
-
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> { return Promise.resolve({} as Response); }
|
|
851
|
-
queueMicrotask(callback: VoidFunction): void { }
|
|
852
|
-
reportError(e: any): void { }
|
|
853
|
-
structuredClone(value: any, options?: StructuredSerializeOptions): any { return value; }
|
|
854
|
-
|
|
855
|
-
// WindowSessionStorage
|
|
856
|
-
readonly sessionStorage: Storage = {} as Storage;
|
|
857
|
-
|
|
858
|
-
// AnimationFrameProvider - already implemented above
|
|
859
|
-
|
|
860
|
-
// Index signature - getter implementation
|
|
861
|
-
[index: number]: Window;
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* Recursive HTML parsing
|
|
867
|
-
*/
|
|
868
|
-
private parseHTMLRecursive(html: string, parent: any): void {
|
|
869
|
-
let position = 0;
|
|
870
|
-
|
|
871
|
-
while (position < html.length) {
|
|
872
|
-
const tagStart = html.indexOf('<', position);
|
|
873
|
-
|
|
874
|
-
if (tagStart === -1) {
|
|
875
|
-
const remainingText = html.substring(position).trim();
|
|
876
|
-
if (remainingText) {
|
|
877
|
-
const textNode = this.document.createTextNode(remainingText);
|
|
878
|
-
parent.appendChild(textNode);
|
|
879
|
-
}
|
|
880
|
-
break;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
if (tagStart > position) {
|
|
884
|
-
const textContent = html.substring(position, tagStart).trim();
|
|
885
|
-
if (textContent) {
|
|
886
|
-
const textNode = this.document.createTextNode(textContent);
|
|
887
|
-
parent.appendChild(textNode);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
const tagEnd = html.indexOf('>', tagStart);
|
|
892
|
-
if (tagEnd === -1) break;
|
|
893
|
-
|
|
894
|
-
const tagContent = html.substring(tagStart + 1, tagEnd);
|
|
895
|
-
|
|
896
|
-
if (tagContent.startsWith('/')) {
|
|
897
|
-
position = tagEnd + 1;
|
|
898
|
-
break;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
const isSelfClosing = tagContent.endsWith('/') || this.isSelfClosingTag(tagContent.split(/\s+/)[0]);
|
|
902
|
-
const spaceIndex = tagContent.indexOf(' ');
|
|
903
|
-
const tagName = spaceIndex === -1 ? tagContent.replace('/', '') : tagContent.substring(0, spaceIndex);
|
|
904
|
-
const attributes = spaceIndex === -1 ? '' : tagContent.substring(spaceIndex + 1).replace('/', '');
|
|
905
|
-
|
|
906
|
-
const element = this.document.createElement(tagName.toLowerCase());
|
|
907
|
-
|
|
908
|
-
if (attributes.trim()) {
|
|
909
|
-
this.parseAttributes(attributes, element);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
// Handle special HTML structure tags
|
|
913
|
-
if (tagName.toLowerCase() === 'html') {
|
|
914
|
-
// Replace documentElement
|
|
915
|
-
(this.document as any).documentElement = element;
|
|
916
|
-
this.document.appendChild(element);
|
|
917
|
-
} else if (tagName.toLowerCase() === 'head') {
|
|
918
|
-
(this.document as any).head = element;
|
|
919
|
-
if (this.document.documentElement) {
|
|
920
|
-
this.document.documentElement.appendChild(element);
|
|
921
|
-
} else {
|
|
922
|
-
parent.appendChild(element);
|
|
923
|
-
}
|
|
924
|
-
} else if (tagName.toLowerCase() === 'body') {
|
|
925
|
-
(this.document as any).body = element;
|
|
926
|
-
if (this.document.documentElement) {
|
|
927
|
-
this.document.documentElement.appendChild(element);
|
|
928
|
-
} else {
|
|
929
|
-
parent.appendChild(element);
|
|
930
|
-
}
|
|
931
|
-
} else {
|
|
932
|
-
parent.appendChild(element);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
if (isSelfClosing) {
|
|
936
|
-
position = tagEnd + 1;
|
|
937
|
-
} else {
|
|
938
|
-
const closingTag = `</${tagName}>`;
|
|
939
|
-
const closingTagIndex = this.findMatchingClosingTag(html, tagEnd + 1, tagName);
|
|
940
|
-
|
|
941
|
-
if (closingTagIndex !== -1) {
|
|
942
|
-
const innerContent = html.substring(tagEnd + 1, closingTagIndex);
|
|
943
|
-
if (innerContent.trim()) {
|
|
944
|
-
this.parseHTMLRecursive(innerContent, element);
|
|
945
|
-
}
|
|
946
|
-
position = closingTagIndex + closingTag.length;
|
|
947
|
-
} else {
|
|
948
|
-
position = tagEnd + 1;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const tagEnd = html.indexOf('>', tagStart);
|
|
1101
|
+
if (tagEnd === -1) break;
|
|
1102
|
+
|
|
1103
|
+
const tagContent = html.substring(tagStart + 1, tagEnd);
|
|
1104
|
+
|
|
1105
|
+
if (tagContent.startsWith('/')) {
|
|
1106
|
+
position = tagEnd + 1;
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
const isSelfClosing = tagContent.endsWith('/') || this.isSelfClosingTag(tagContent.split(/\s+/)[0]);
|
|
1111
|
+
const spaceIndex = tagContent.indexOf(' ');
|
|
1112
|
+
const tagName = spaceIndex === -1 ? tagContent.replace('/', '') : tagContent.substring(0, spaceIndex);
|
|
1113
|
+
const attributes = spaceIndex === -1 ? '' : tagContent.substring(spaceIndex + 1).replace('/', '');
|
|
1114
|
+
|
|
1115
|
+
const element = this.document.createElement(tagName.toLowerCase());
|
|
1116
|
+
|
|
1117
|
+
if (attributes.trim()) {
|
|
1118
|
+
this.parseAttributes(attributes, element);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Handle special HTML structure tags
|
|
1122
|
+
if (tagName.toLowerCase() === 'html') {
|
|
1123
|
+
// Replace documentElement
|
|
1124
|
+
(this.document as any).documentElement = element;
|
|
1125
|
+
this.document.appendChild(element);
|
|
1126
|
+
} else if (tagName.toLowerCase() === 'head') {
|
|
1127
|
+
(this.document as any).head = element;
|
|
1128
|
+
if (this.document.documentElement) {
|
|
1129
|
+
this.document.documentElement.appendChild(element);
|
|
1130
|
+
} else {
|
|
1131
|
+
parent.appendChild(element);
|
|
951
1132
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
let pos = startPos;
|
|
959
|
-
|
|
960
|
-
while (pos < html.length && depth > 0) {
|
|
961
|
-
const nextOpen = html.indexOf(openTag, pos);
|
|
962
|
-
const nextClose = html.indexOf(closeTag, pos);
|
|
963
|
-
|
|
964
|
-
if (nextClose === -1) return -1;
|
|
965
|
-
|
|
966
|
-
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
967
|
-
depth++;
|
|
968
|
-
pos = nextOpen + openTag.length;
|
|
969
|
-
} else {
|
|
970
|
-
depth--;
|
|
971
|
-
if (depth === 0) return nextClose;
|
|
972
|
-
pos = nextClose + closeTag.length;
|
|
973
|
-
}
|
|
1133
|
+
} else if (tagName.toLowerCase() === 'body') {
|
|
1134
|
+
(this.document as any).body = element;
|
|
1135
|
+
if (this.document.documentElement) {
|
|
1136
|
+
this.document.documentElement.appendChild(element);
|
|
1137
|
+
} else {
|
|
1138
|
+
parent.appendChild(element);
|
|
974
1139
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1140
|
+
} else {
|
|
1141
|
+
parent.appendChild(element);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (isSelfClosing) {
|
|
1145
|
+
position = tagEnd + 1;
|
|
1146
|
+
} else {
|
|
1147
|
+
const closingTag = `</${tagName}>`;
|
|
1148
|
+
const closingTagIndex = this.findMatchingClosingTag(html, tagEnd + 1, tagName);
|
|
1149
|
+
|
|
1150
|
+
if (closingTagIndex !== -1) {
|
|
1151
|
+
const innerContent = html.substring(tagEnd + 1, closingTagIndex);
|
|
1152
|
+
if (innerContent.trim()) {
|
|
1153
|
+
this.parseHTMLRecursive(innerContent, element);
|
|
1154
|
+
}
|
|
1155
|
+
position = closingTagIndex + closingTag.length;
|
|
1156
|
+
} else {
|
|
1157
|
+
position = tagEnd + 1;
|
|
988
1158
|
}
|
|
1159
|
+
}
|
|
989
1160
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
private findMatchingClosingTag(html: string, startPos: number, tagName: string): number {
|
|
1164
|
+
const openTag = `<${tagName}`;
|
|
1165
|
+
const closeTag = `</${tagName}>`;
|
|
1166
|
+
let depth = 1;
|
|
1167
|
+
let pos = startPos;
|
|
1168
|
+
|
|
1169
|
+
while (pos < html.length && depth > 0) {
|
|
1170
|
+
const nextOpen = html.indexOf(openTag, pos);
|
|
1171
|
+
const nextClose = html.indexOf(closeTag, pos);
|
|
1172
|
+
|
|
1173
|
+
if (nextClose === -1) return -1;
|
|
1174
|
+
|
|
1175
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
1176
|
+
depth++;
|
|
1177
|
+
pos = nextOpen + openTag.length;
|
|
1178
|
+
} else {
|
|
1179
|
+
depth--;
|
|
1180
|
+
if (depth === 0) return nextClose;
|
|
1181
|
+
pos = nextClose + closeTag.length;
|
|
1182
|
+
}
|
|
994
1183
|
}
|
|
995
1184
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
*/
|
|
999
|
-
private clearDocumentContent(): void {
|
|
1000
|
-
const doc = this.document as any;
|
|
1185
|
+
return -1;
|
|
1186
|
+
}
|
|
1001
1187
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1188
|
+
private parseAttributes(attributeString: string, element: any): void {
|
|
1189
|
+
// Updated regex to handle hyphenated attribute names like dr-for-of, data-bind, etc.
|
|
1190
|
+
const attrRegex = /([\w:-]+)(?:\s*=\s*(['"])(.*?)\2)?/g;
|
|
1191
|
+
let match;
|
|
1008
1192
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1193
|
+
while ((match = attrRegex.exec(attributeString)) !== null) {
|
|
1194
|
+
const name = match[1];
|
|
1195
|
+
let value = match[3] || ''; // match[3] is the content between quotes
|
|
1196
|
+
element.setAttribute(name, value);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
private isSelfClosingTag(tagName: string): boolean {
|
|
1201
|
+
const selfClosingTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
|
|
1202
|
+
return selfClosingTags.includes(tagName.toLowerCase());
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Clear current document content
|
|
1207
|
+
*/
|
|
1208
|
+
private clearDocumentContent(): void {
|
|
1209
|
+
const doc = this.document as any;
|
|
1210
|
+
|
|
1211
|
+
// Clear head content
|
|
1212
|
+
if (doc.head) {
|
|
1213
|
+
while (doc.head.firstChild) {
|
|
1214
|
+
doc.head.removeChild(doc.head.firstChild);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// Clear body content
|
|
1219
|
+
if (doc.body) {
|
|
1220
|
+
while (doc.body.firstChild) {
|
|
1221
|
+
doc.body.removeChild(doc.body.firstChild);
|
|
1222
|
+
}
|
|
1015
1223
|
}
|
|
1224
|
+
}
|
|
1016
1225
|
|
|
1017
1226
|
|
|
1018
1227
|
}
|