@blueking/bk-weweb 0.0.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.
- package/dist/collect-source.js +2139 -0
- package/dist/collect-source.js.map +1 -0
- package/dist/index.esm.js +2106 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.min.js +2 -0
- package/dist/index.min.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +82 -0
- package/readme.md +33 -0
- package/typings/base-app/base-element.d.ts +6 -0
- package/typings/base-app/collect-source.d.ts +1 -0
- package/typings/cache/app-cache.d.ts +19 -0
- package/typings/common/index.d.ts +1 -0
- package/typings/common/state.d.ts +11 -0
- package/typings/component/web-compnent.d.ts +26 -0
- package/typings/context/document.d.ts +2 -0
- package/typings/context/element.d.ts +2 -0
- package/typings/context/function.d.ts +1 -0
- package/typings/context/memory.d.ts +10 -0
- package/typings/context/sandbox.d.ts +17 -0
- package/typings/context/window.d.ts +9 -0
- package/typings/entry/entry.d.ts +28 -0
- package/typings/entry/script.d.ts +22 -0
- package/typings/entry/style.d.ts +22 -0
- package/typings/index.d.ts +19 -0
- package/typings/lifecircle/activated.d.ts +2 -0
- package/typings/lifecircle/before-load.d.ts +1 -0
- package/typings/lifecircle/deactivated.d.ts +1 -0
- package/typings/lifecircle/load.d.ts +6 -0
- package/typings/lifecircle/mount.d.ts +2 -0
- package/typings/lifecircle/unload.d.ts +1 -0
- package/typings/lifecircle/unmount.d.ts +1 -0
- package/typings/mode/app.d.ts +37 -0
- package/typings/mode/instance.d.ts +32 -0
- package/typings/preload/preload.d.ts +5 -0
- package/typings/typings/global.d.ts +17 -0
- package/typings/typings/index.d.ts +10 -0
- package/typings/typings/model.d.ts +64 -0
- package/typings/typings/sandbox.d.ts +18 -0
- package/typings/typings/source.d.ts +23 -0
- package/typings/utils/common.d.ts +18 -0
- package/typings/utils/element-event.d.ts +2 -0
- package/typings/utils/element.d.ts +6 -0
- package/typings/utils/fetch.d.ts +1 -0
- package/typings/utils/index.d.ts +4 -0
- package/typings/utils/load-source.d.ts +12 -0
|
@@ -0,0 +1,2139 @@
|
|
|
1
|
+
// app status
|
|
2
|
+
var AppState;
|
|
3
|
+
(function (AppState) {
|
|
4
|
+
AppState["UNSET"] = "UNSET";
|
|
5
|
+
AppState["LOADING"] = "LOADING";
|
|
6
|
+
AppState["LOADED"] = "LOADED";
|
|
7
|
+
AppState["ERROR"] = "ERROR";
|
|
8
|
+
AppState["MOUNTING"] = "MOUNTING";
|
|
9
|
+
AppState["MOUNTED"] = "MOUNTED";
|
|
10
|
+
AppState["ACTIVATED"] = "ACTIVATED";
|
|
11
|
+
AppState["DEACTIVATED"] = "DEACTIVATED";
|
|
12
|
+
AppState["UNMOUNT"] = "UNMOUNT";
|
|
13
|
+
})(AppState || (AppState = {}));
|
|
14
|
+
|
|
15
|
+
// is function
|
|
16
|
+
function isFunction(target) {
|
|
17
|
+
return typeof target === 'function';
|
|
18
|
+
}
|
|
19
|
+
// Promise.then might be synchronized in Zone.js context, we need to use setTimeout instead to mock next tick.
|
|
20
|
+
const nextTask = typeof window.Zone === 'function' ? setTimeout : cb => Promise.resolve().then(cb);
|
|
21
|
+
function addUrlProtocol(url) {
|
|
22
|
+
return url.startsWith('//') ? `${location.protocol}${url}` : url;
|
|
23
|
+
}
|
|
24
|
+
// Get valid address, such as https://xxx/xx/xx.html to https://xxx/xx/
|
|
25
|
+
function getUrlDir(url) {
|
|
26
|
+
const { origin, pathname } = new URL(url);
|
|
27
|
+
if (/\.(\w+)$/.test(pathname)) {
|
|
28
|
+
const fullPath = `${origin}${pathname}`;
|
|
29
|
+
const pathArr = fullPath.split('/');
|
|
30
|
+
pathArr.pop();
|
|
31
|
+
return `${pathArr.join('/')}/`;
|
|
32
|
+
}
|
|
33
|
+
return `${origin}${pathname}/`.replace(/\/\/$/, '/');
|
|
34
|
+
}
|
|
35
|
+
// 补齐url地址
|
|
36
|
+
function fillUpPath(path, baseURI) {
|
|
37
|
+
if (!path || /^((((ht|f)tps?)|file):)?\/\//.test(path) || /^(data|blob):/.test(path))
|
|
38
|
+
return path;
|
|
39
|
+
return new URL(path, getUrlDir(addUrlProtocol(baseURI))).toString();
|
|
40
|
+
}
|
|
41
|
+
// 获取url目录
|
|
42
|
+
function getFileDir(linkpath) {
|
|
43
|
+
const pathArr = linkpath.split('/');
|
|
44
|
+
pathArr.pop();
|
|
45
|
+
return addUrlProtocol(`${pathArr.join('/')}/`);
|
|
46
|
+
}
|
|
47
|
+
// is ie11
|
|
48
|
+
const isIE11 = typeof navigator !== 'undefined' && navigator.userAgent.indexOf('Trident') !== -1;
|
|
49
|
+
// 随机url
|
|
50
|
+
function randomUrl() {
|
|
51
|
+
return `inline-${random(16)}`;
|
|
52
|
+
}
|
|
53
|
+
// Array deduplication
|
|
54
|
+
function arrayUnique(array) {
|
|
55
|
+
return Array.from(new Set(array));
|
|
56
|
+
}
|
|
57
|
+
// is safari browser
|
|
58
|
+
function isSafari() {
|
|
59
|
+
return /Safari/.test(navigator.userAgent);
|
|
60
|
+
}
|
|
61
|
+
// Create pure elements
|
|
62
|
+
function createElement$1(tagName, options) {
|
|
63
|
+
const element = document.createElement(tagName, options);
|
|
64
|
+
if (element.__BK_WEWEB_APP_KEY__)
|
|
65
|
+
delete element.__BK_WEWEB_APP_KEY__;
|
|
66
|
+
return element;
|
|
67
|
+
}
|
|
68
|
+
// bodyle element
|
|
69
|
+
function isBodyElement(key) {
|
|
70
|
+
return /^(body|head|html)$/i.test(key);
|
|
71
|
+
}
|
|
72
|
+
// create random string
|
|
73
|
+
const random = (n, str = 'abcdefghijklmnopqrstuvwxyz0123456789') => {
|
|
74
|
+
// 生成n位长度的字符串
|
|
75
|
+
let result = '';
|
|
76
|
+
for (let i = 0; i < n; i++) {
|
|
77
|
+
result += str[parseInt((Math.random() * str.length).toString(), 10)];
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let currentRunningApp = null;
|
|
83
|
+
function getCurrentRunningApp() {
|
|
84
|
+
return currentRunningApp;
|
|
85
|
+
}
|
|
86
|
+
function setCurrentRunningApp(appInstance) {
|
|
87
|
+
currentRunningApp = appInstance;
|
|
88
|
+
}
|
|
89
|
+
const documentClickListMap = new Map();
|
|
90
|
+
const documentEventListenerMap = new Map();
|
|
91
|
+
const script = document.createElement('script');
|
|
92
|
+
const isSupportModuleScript = 'noModule' in script;
|
|
93
|
+
const SCOPED_CSS_STYLE_ID = 'SCOPED_CSS_STYLE_ID';
|
|
94
|
+
const templateStyle = document.createElement('style');
|
|
95
|
+
templateStyle.setAttribute('id', SCOPED_CSS_STYLE_ID);
|
|
96
|
+
document.body.appendChild(templateStyle);
|
|
97
|
+
templateStyle.sheet.disabled = true;
|
|
98
|
+
const disabledStyleDom = templateStyle;
|
|
99
|
+
|
|
100
|
+
const { document: document$1 } = window;
|
|
101
|
+
const { createElement, querySelector, querySelectorAll, getElementById, getElementsByClassName, getElementsByTagName, getElementsByName, } = Document.prototype;
|
|
102
|
+
function rewriteDocumentPrototypeMethods() {
|
|
103
|
+
Document.prototype.createElement = function (tagName, options) {
|
|
104
|
+
const element = createElement.call(this, tagName, options);
|
|
105
|
+
const app = getCurrentRunningApp();
|
|
106
|
+
// img.src = '' iframe.src = '' 均不能在setAttributes上监听 但是这里所有的src都是 全地址 无法判断是否需要添加子应用域名
|
|
107
|
+
// if (tagName.toLocaleLowerCase() === 'img') {
|
|
108
|
+
// const observer = new MutationObserver((list, observer) => {
|
|
109
|
+
// observer.disconnect();
|
|
110
|
+
// const url = new URL((element as HTMLImageElement).src)
|
|
111
|
+
// (element as HTMLImageElement).src = `${}`
|
|
112
|
+
// });
|
|
113
|
+
// observer.observe(element, { attributeFilter: ['src'], subtree: false, childList: false });
|
|
114
|
+
// }
|
|
115
|
+
if (app)
|
|
116
|
+
element.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
|
|
117
|
+
return element;
|
|
118
|
+
};
|
|
119
|
+
function querySelectorNew(selectors) {
|
|
120
|
+
const app = getCurrentRunningApp();
|
|
121
|
+
if (!app || !selectors || isBodyElement(selectors) || document$1 !== this) {
|
|
122
|
+
return querySelector.call(this, selectors);
|
|
123
|
+
}
|
|
124
|
+
return app?.container?.querySelector(selectors) ?? null;
|
|
125
|
+
}
|
|
126
|
+
function querySelectorAllNew(selectors) {
|
|
127
|
+
const app = getCurrentRunningApp();
|
|
128
|
+
if (!app || !selectors || isBodyElement(selectors) || document$1 !== this) {
|
|
129
|
+
return querySelectorAll.call(this, selectors);
|
|
130
|
+
}
|
|
131
|
+
return app?.container?.querySelectorAll(selectors) ?? [];
|
|
132
|
+
}
|
|
133
|
+
Document.prototype.querySelector = querySelectorNew;
|
|
134
|
+
Document.prototype.querySelectorAll = querySelectorAllNew;
|
|
135
|
+
Document.prototype.getElementById = function getElementByIdNew(key) {
|
|
136
|
+
if (!getCurrentRunningApp()) {
|
|
137
|
+
return getElementById.call(this, key);
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
return querySelectorNew.call(this, `#${key}`);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return getElementById.call(this, key);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
Document.prototype.getElementsByClassName = function (key) {
|
|
147
|
+
if (!getCurrentRunningApp()) {
|
|
148
|
+
return getElementsByClassName.call(this, key);
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
return querySelectorAllNew.call(this, `.${key}`);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return getElementsByClassName.call(this, key);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
// eslint-disable-next-line max-len
|
|
158
|
+
Document.prototype.getElementsByTagName = function (key) {
|
|
159
|
+
const app = getCurrentRunningApp();
|
|
160
|
+
if (!app
|
|
161
|
+
|| isBodyElement(key)
|
|
162
|
+
|| (!app?.showSourceCode && key.toLocaleLowerCase() === 'script')) {
|
|
163
|
+
return getElementsByTagName.call(this, key);
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
return querySelectorAllNew.call(this, key);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return getElementsByTagName.call(this, key);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
Document.prototype.getElementsByName = function (key) {
|
|
173
|
+
if (!getCurrentRunningApp()) {
|
|
174
|
+
return getElementsByName.call(this, key);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
return querySelectorAllNew.call(this, `[name=${key}]`);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return getElementsByName.call(this, key);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function resetDocumentPrototypeMethods() {
|
|
185
|
+
Document.prototype.createElement = createElement;
|
|
186
|
+
Document.prototype.querySelector = querySelector;
|
|
187
|
+
Document.prototype.querySelectorAll = querySelectorAll;
|
|
188
|
+
Document.prototype.getElementById = getElementById;
|
|
189
|
+
Document.prototype.getElementsByClassName = getElementsByClassName;
|
|
190
|
+
Document.prototype.getElementsByTagName = getElementsByTagName;
|
|
191
|
+
Document.prototype.getElementsByName = getElementsByName;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const CSS_ATTRIBUTE_KEY = 'id';
|
|
195
|
+
var CSS_RULE_TYPE;
|
|
196
|
+
(function (CSS_RULE_TYPE) {
|
|
197
|
+
CSS_RULE_TYPE[CSS_RULE_TYPE["STYLE_RULE"] = 1] = "STYLE_RULE";
|
|
198
|
+
CSS_RULE_TYPE[CSS_RULE_TYPE["MEDIA_RULE"] = 4] = "MEDIA_RULE";
|
|
199
|
+
CSS_RULE_TYPE[CSS_RULE_TYPE["SUPPORTS_RULE"] = 12] = "SUPPORTS_RULE";
|
|
200
|
+
})(CSS_RULE_TYPE || (CSS_RULE_TYPE = {}));
|
|
201
|
+
|
|
202
|
+
const commonEScapeKeyList = ['System', '__cjsWrapper', '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__'];
|
|
203
|
+
// 共享主应用上下文属性名
|
|
204
|
+
const escapeSetterKeyList = ['location'];
|
|
205
|
+
// 一定需要在子应用自身上下文获取的属性名
|
|
206
|
+
const scopeWindowKeyList = ['webpackJsonp', '__POWERED_BY_BK_WEWEB__', '__BK_WEWEB_APP_KEY__', '__BK_WEWEB_DATA__'];
|
|
207
|
+
// 设置了scopedLocation 后需要监听属性名
|
|
208
|
+
const scopedLocationKeyList = ['location', 'history'];
|
|
209
|
+
var DescriptorMapValue;
|
|
210
|
+
(function (DescriptorMapValue) {
|
|
211
|
+
DescriptorMapValue["TARGET"] = "TARGET";
|
|
212
|
+
DescriptorMapValue["WINDOW"] = "WINDOW";
|
|
213
|
+
})(DescriptorMapValue || (DescriptorMapValue = {}));
|
|
214
|
+
const commonRawWindowKeyMap = {
|
|
215
|
+
undefined: true,
|
|
216
|
+
Array: true,
|
|
217
|
+
Object: true,
|
|
218
|
+
String: true,
|
|
219
|
+
Boolean: true,
|
|
220
|
+
Math: true,
|
|
221
|
+
Reflect: true,
|
|
222
|
+
Function: true,
|
|
223
|
+
Number: true,
|
|
224
|
+
Symbol: true,
|
|
225
|
+
parseFloat: true,
|
|
226
|
+
parseInt: true,
|
|
227
|
+
Float32Array: true,
|
|
228
|
+
Set: true,
|
|
229
|
+
Infinity: true,
|
|
230
|
+
navigator: true,
|
|
231
|
+
devicePixelRatio: true,
|
|
232
|
+
Promise: true,
|
|
233
|
+
HTMLElement: true,
|
|
234
|
+
Map: true,
|
|
235
|
+
Element: true,
|
|
236
|
+
performance: true,
|
|
237
|
+
console: true,
|
|
238
|
+
isFinite: true,
|
|
239
|
+
RegExp: true,
|
|
240
|
+
JSON: true,
|
|
241
|
+
Date: true,
|
|
242
|
+
Proxy: true,
|
|
243
|
+
isNaN: true,
|
|
244
|
+
WeakMap: true,
|
|
245
|
+
WeakSet: true,
|
|
246
|
+
Uint8Array: true,
|
|
247
|
+
// btoa: true,
|
|
248
|
+
// unescape: true,
|
|
249
|
+
// WebSocket: true,
|
|
250
|
+
// encodeURIComponent: true,
|
|
251
|
+
// decodeURIComponent: true,
|
|
252
|
+
// ArrayBuffer: true,
|
|
253
|
+
// XMLHttpRequest: true,
|
|
254
|
+
// SVGElement: true,
|
|
255
|
+
// URLSearchParams: true,
|
|
256
|
+
// URL: true,
|
|
257
|
+
// RangeError: true,
|
|
258
|
+
// TypeError: true,
|
|
259
|
+
// Uint16Array: true,
|
|
260
|
+
// Uint32Array: true,
|
|
261
|
+
// Float64Array: true,
|
|
262
|
+
// Int32Array: true,
|
|
263
|
+
// requestAnimationFrame: true,
|
|
264
|
+
// SVGRect: true,
|
|
265
|
+
// Error: true,
|
|
266
|
+
// Buffer: true,
|
|
267
|
+
// SyntaxError: true,
|
|
268
|
+
// DataView: true,
|
|
269
|
+
// Worker: true,
|
|
270
|
+
// localStorage: true,
|
|
271
|
+
// Text: true,
|
|
272
|
+
// FormData: true,
|
|
273
|
+
// ShadowRoot: true,
|
|
274
|
+
// encodeURI: true,
|
|
275
|
+
// crypto: true,
|
|
276
|
+
// HTMLAnchorElement: true,
|
|
277
|
+
// Int8Array: true,
|
|
278
|
+
// FinalizationRegistry: true,
|
|
279
|
+
// BigInt: true,
|
|
280
|
+
// Atomics: true,
|
|
281
|
+
// Uint8ClampedArray: true,
|
|
282
|
+
// WeakRef: true,
|
|
283
|
+
// getComputedStyle,
|
|
284
|
+
// innerHeight: true,
|
|
285
|
+
// innerWidth: true,
|
|
286
|
+
// getComputedStyle: true,
|
|
287
|
+
};
|
|
288
|
+
const commonFakeWindowKeyMap = {
|
|
289
|
+
__VUE_OPTIONS_API__: true,
|
|
290
|
+
__VUE_I18N_LEGACY_API__: true,
|
|
291
|
+
__VUE_I18N_FULL_INSTALL__: true,
|
|
292
|
+
__VUE_DEVTOOLS_GLOBAL_HOOK__: true,
|
|
293
|
+
__VUE_DEVTOOLS_HOOK_REPLAY__: true,
|
|
294
|
+
__VUE_DEVTOOLS_PLUGINS__: true,
|
|
295
|
+
__DEV__: true,
|
|
296
|
+
webpackChunktrace: true,
|
|
297
|
+
i18n: true,
|
|
298
|
+
'__core-js_shared__': true,
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
function eventHandler(event, element) {
|
|
302
|
+
Object.defineProperties(event, {
|
|
303
|
+
currentTarget: {
|
|
304
|
+
get() {
|
|
305
|
+
return element;
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
srcElement: {
|
|
309
|
+
get() {
|
|
310
|
+
return element;
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
target: {
|
|
314
|
+
get() {
|
|
315
|
+
return element;
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
function dispatchLinkOrScriptLoad(element) {
|
|
321
|
+
const event = new CustomEvent('load');
|
|
322
|
+
eventHandler(event, element);
|
|
323
|
+
if (isFunction(element.onload)) {
|
|
324
|
+
element.onload(event);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
element.dispatchEvent(event);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function dispatchLinkOrScriptError(element) {
|
|
331
|
+
const event = new CustomEvent('error');
|
|
332
|
+
eventHandler(event, element);
|
|
333
|
+
if (isFunction(element.onerror)) {
|
|
334
|
+
element.onerror(event);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
element.dispatchEvent(event);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* eslint-disable no-param-reassign */
|
|
342
|
+
class Style {
|
|
343
|
+
scopedCode = '';
|
|
344
|
+
code = '';
|
|
345
|
+
prefetch = false;
|
|
346
|
+
preload = false;
|
|
347
|
+
url;
|
|
348
|
+
scoped;
|
|
349
|
+
fromHtml;
|
|
350
|
+
initial;
|
|
351
|
+
constructor({ code, prefetch, preload, url, fromHtml, initial }) {
|
|
352
|
+
this.scoped = false;
|
|
353
|
+
this.code = code;
|
|
354
|
+
this.prefetch = prefetch ?? false;
|
|
355
|
+
this.preload = preload ?? false;
|
|
356
|
+
this.url = url;
|
|
357
|
+
this.fromHtml = fromHtml;
|
|
358
|
+
this.initial = initial ?? false;
|
|
359
|
+
}
|
|
360
|
+
async getCode(app) {
|
|
361
|
+
if (this.code.length || !this.url) {
|
|
362
|
+
return this.code;
|
|
363
|
+
}
|
|
364
|
+
let code = '';
|
|
365
|
+
if (app?.source?.styles?.has(this.url)) {
|
|
366
|
+
code = app.source.styles.get(this.url)?.code || '';
|
|
367
|
+
}
|
|
368
|
+
if (!code && appCache.getCacheStyle(this.url)) {
|
|
369
|
+
const style = appCache.getCacheStyle(this.url);
|
|
370
|
+
code = style?.code || '';
|
|
371
|
+
}
|
|
372
|
+
if (!code) {
|
|
373
|
+
code = await fetchSource(this.url).catch(() => '');
|
|
374
|
+
}
|
|
375
|
+
this.code = code;
|
|
376
|
+
return code;
|
|
377
|
+
}
|
|
378
|
+
async excuteCode(app) {
|
|
379
|
+
app.registerRunningApp();
|
|
380
|
+
let styleElement = createElement$1('style');
|
|
381
|
+
styleElement.setAttribute('type', 'text/css');
|
|
382
|
+
styleElement.textContent = this.code;
|
|
383
|
+
try {
|
|
384
|
+
if (!this.code)
|
|
385
|
+
await this.getCode(app);
|
|
386
|
+
styleElement = this.scopedStyleCSS(app, styleElement);
|
|
387
|
+
this.scoped = true;
|
|
388
|
+
}
|
|
389
|
+
catch (e) {
|
|
390
|
+
console.error('scoped style error', e);
|
|
391
|
+
}
|
|
392
|
+
return styleElement;
|
|
393
|
+
}
|
|
394
|
+
scopedStyleCSS(app, styleElement) {
|
|
395
|
+
const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
396
|
+
setMarkElement(styleElement, app, needKeepAlive);
|
|
397
|
+
if (this.code || styleElement.textContent) {
|
|
398
|
+
if (styleElement.textContent) {
|
|
399
|
+
styleElement.textContent = '';
|
|
400
|
+
styleElement.innerHTML = '';
|
|
401
|
+
}
|
|
402
|
+
disabledStyleDom.textContent = this.code;
|
|
403
|
+
this.commonScoped(disabledStyleDom, styleElement, app);
|
|
404
|
+
disabledStyleDom.textContent = '';
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
const observer = new MutationObserver(() => {
|
|
408
|
+
if (!(styleElement.textContent || styleElement.sheet?.cssRules?.length))
|
|
409
|
+
return;
|
|
410
|
+
observer.disconnect();
|
|
411
|
+
this.commonScoped(styleElement, styleElement, app);
|
|
412
|
+
});
|
|
413
|
+
observer.observe(styleElement, { attributes: false, childList: true, subtree: true, characterData: true });
|
|
414
|
+
}
|
|
415
|
+
this.url && styleElement.setAttribute('origin-src', this.url);
|
|
416
|
+
return styleElement;
|
|
417
|
+
}
|
|
418
|
+
scopedLinkCSS(app, linkElement) {
|
|
419
|
+
const styleElement = createElement$1('style');
|
|
420
|
+
styleElement.setAttribute('type', 'text/css');
|
|
421
|
+
const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
422
|
+
setMarkElement(styleElement, app, needKeepAlive);
|
|
423
|
+
const container = needKeepAlive ? document.head : app.container;
|
|
424
|
+
try {
|
|
425
|
+
if (this.code) {
|
|
426
|
+
disabledStyleDom.textContent = styleElement.textContent || this.code;
|
|
427
|
+
this.commonScoped(disabledStyleDom, styleElement, app);
|
|
428
|
+
container?.prepend(styleElement);
|
|
429
|
+
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
430
|
+
disabledStyleDom.textContent = '';
|
|
431
|
+
}
|
|
432
|
+
else if (linkElement.getAttribute('href')) {
|
|
433
|
+
this.url = fillUpPath(linkElement.getAttribute('href'), app.url);
|
|
434
|
+
this.getCode(app).then(() => {
|
|
435
|
+
this.scopedStyleCSS(app, styleElement);
|
|
436
|
+
linkElement.remove();
|
|
437
|
+
container?.prepend(styleElement);
|
|
438
|
+
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
439
|
+
this.scoped = true;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
const observer = new MutationObserver(() => {
|
|
444
|
+
if (!linkElement.href)
|
|
445
|
+
return;
|
|
446
|
+
observer.disconnect();
|
|
447
|
+
this.url = fillUpPath(linkElement.getAttribute('href'), app.url);
|
|
448
|
+
this.getCode(app).then(() => {
|
|
449
|
+
this.scopedStyleCSS(app, styleElement);
|
|
450
|
+
linkElement.remove();
|
|
451
|
+
container?.prepend(styleElement);
|
|
452
|
+
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
453
|
+
this.scoped = true;
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
observer.observe(linkElement, { attributeFilter: ['href'], subtree: false, childList: false });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
linkElement && dispatchLinkOrScriptError(linkElement);
|
|
461
|
+
}
|
|
462
|
+
return styleElement;
|
|
463
|
+
}
|
|
464
|
+
commonScoped(templateStyle, styleElement, app) {
|
|
465
|
+
if (app.scopeCss && !(app.container instanceof ShadowRoot)) {
|
|
466
|
+
const rules = Array.from(templateStyle.sheet?.cssRules ?? []);
|
|
467
|
+
const cssPrefix = `#${app.name}`;
|
|
468
|
+
const scopedCss = this.scopeRule(rules, cssPrefix);
|
|
469
|
+
let cssText = this.resetUrlHost(scopedCss, app.url, this.url);
|
|
470
|
+
if (isSafari()) {
|
|
471
|
+
cssText = cssText.replace(/([;{]\s*content:\s*)([^\s"][^";}]*)/gm, (all, $1, $2) => {
|
|
472
|
+
if ($2 === 'none' || /^(url\()|(counter\()|(attr\()|(open-quote)|(close-quote)/.test($2)) {
|
|
473
|
+
return all;
|
|
474
|
+
}
|
|
475
|
+
return `${$1}"${$2}"`;
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
styleElement.textContent = cssText;
|
|
479
|
+
this.scopedCode = cssText;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
const cssText = this.resetUrlHost(styleElement.textContent || templateStyle.textContent || '', app.url, this.url);
|
|
483
|
+
styleElement.textContent = cssText;
|
|
484
|
+
}
|
|
485
|
+
this.scoped = true;
|
|
486
|
+
}
|
|
487
|
+
scopeRule(rules, cssPrefix) {
|
|
488
|
+
let result = '';
|
|
489
|
+
for (const rule of rules) {
|
|
490
|
+
switch (rule.type) {
|
|
491
|
+
case CSS_RULE_TYPE.STYLE_RULE:
|
|
492
|
+
result += this.scopeStyleRule(rule, cssPrefix);
|
|
493
|
+
break;
|
|
494
|
+
case CSS_RULE_TYPE.MEDIA_RULE:
|
|
495
|
+
result += this.resetPackRule(rule, cssPrefix, 'media');
|
|
496
|
+
break;
|
|
497
|
+
case CSS_RULE_TYPE.SUPPORTS_RULE:
|
|
498
|
+
result += this.resetPackRule(rule, cssPrefix, 'supports');
|
|
499
|
+
break;
|
|
500
|
+
default:
|
|
501
|
+
result += rule.cssText;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return result.replace(/^\s+/, '');
|
|
506
|
+
}
|
|
507
|
+
resetPackRule(rule, prefix, packName) {
|
|
508
|
+
const result = this.scopeRule(Array.from(rule.cssRules), prefix);
|
|
509
|
+
return `@${packName} ${rule.conditionText} {${result}}`;
|
|
510
|
+
}
|
|
511
|
+
resetUrlHost(cssText, baseURI, linkpath) {
|
|
512
|
+
return cssText.replace(/url\(["']?([^)"']+)["']?\)/gm, (text, $1) => {
|
|
513
|
+
if (/^(data|blob):/.test($1) || /^(https?:)?\/\//.test($1)) {
|
|
514
|
+
return text;
|
|
515
|
+
}
|
|
516
|
+
if (/^((\.\.?\/)|[^/])/.test($1) && linkpath) {
|
|
517
|
+
baseURI = getFileDir(linkpath);
|
|
518
|
+
}
|
|
519
|
+
return `url("${fillUpPath($1, baseURI)}")`;
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
scopeStyleRule(rule, prefix) {
|
|
523
|
+
const { selectorText, cssText } = rule;
|
|
524
|
+
if (/^((html[\s>~,]+body)|(html|body|:root))$/.test(selectorText)) {
|
|
525
|
+
return cssText.replace(/^((html[\s>~,]+body)|(html|body|:root))/, prefix);
|
|
526
|
+
}
|
|
527
|
+
if (selectorText === '*') {
|
|
528
|
+
return cssText.replace('*', `${prefix} *`);
|
|
529
|
+
}
|
|
530
|
+
const builtInRootSelectorRE = /(^|\s+)((html[\s>~]+body)|(html|body|:root))(?=[\s>~]+|$)/;
|
|
531
|
+
return cssText.replace(/^[\s\S]+{/, selectors => selectors.replace(/(^|,)([^,]+)/g, (all, $1, $2) => {
|
|
532
|
+
if (builtInRootSelectorRE.test($2)) {
|
|
533
|
+
return all.replace(builtInRootSelectorRE, prefix);
|
|
534
|
+
}
|
|
535
|
+
return `${$1} ${prefix} ${$2.replace(/^\s*/, '')}`;
|
|
536
|
+
}));
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async function excuteAppStyles(app, container) {
|
|
540
|
+
const styleList = Array.from(app.source.styles.values());
|
|
541
|
+
const promiseList = [];
|
|
542
|
+
styleList.forEach((style) => {
|
|
543
|
+
promiseList.push(style.excuteCode(app));
|
|
544
|
+
});
|
|
545
|
+
await Promise.all(promiseList).then((styleElementList) => {
|
|
546
|
+
const parentElemnt = container || app.container;
|
|
547
|
+
if (app.keepAlive && !(parentElemnt instanceof ShadowRoot)) {
|
|
548
|
+
document.head.append(...styleElementList);
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
parentElemnt?.prepend(...styleElementList);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const { appendChild: bodyAppendChild$1 } = HTMLBodyElement.prototype;
|
|
557
|
+
function resetNewElement(parent, child, app) {
|
|
558
|
+
if (child instanceof HTMLStyleElement) {
|
|
559
|
+
if (child.hasAttribute('exclude')) {
|
|
560
|
+
return document.createComment('【bk-weweb】style with exclude attribute is ignored');
|
|
561
|
+
}
|
|
562
|
+
if (!child.hasAttribute('ignore')) {
|
|
563
|
+
const styleInstance = new Style({
|
|
564
|
+
code: child.textContent || '',
|
|
565
|
+
url: '',
|
|
566
|
+
fromHtml: false,
|
|
567
|
+
});
|
|
568
|
+
app.source?.setStyle(randomUrl(), styleInstance);
|
|
569
|
+
styleInstance.scopedStyleCSS(app, child);
|
|
570
|
+
}
|
|
571
|
+
return child;
|
|
572
|
+
}
|
|
573
|
+
if (child instanceof HTMLLinkElement) {
|
|
574
|
+
const result = app.source?.collectLink(child, parent, true);
|
|
575
|
+
if (!result)
|
|
576
|
+
return child;
|
|
577
|
+
if (result.style) {
|
|
578
|
+
result.style.scopedLinkCSS(app, child);
|
|
579
|
+
}
|
|
580
|
+
if (result.replace !== child) {
|
|
581
|
+
return result.replace;
|
|
582
|
+
}
|
|
583
|
+
return child;
|
|
584
|
+
}
|
|
585
|
+
if (child instanceof HTMLScriptElement) {
|
|
586
|
+
const replaceInfo = app.source.collectScript(child, parent, true);
|
|
587
|
+
if (!replaceInfo) {
|
|
588
|
+
return child;
|
|
589
|
+
}
|
|
590
|
+
if (replaceInfo.script) {
|
|
591
|
+
replaceInfo.script.excuteCode(app);
|
|
592
|
+
}
|
|
593
|
+
if (replaceInfo.replace !== child) {
|
|
594
|
+
return replaceInfo.replace;
|
|
595
|
+
}
|
|
596
|
+
if (app.scopeJs && !child.getAttribute('src') && !child.textContent) {
|
|
597
|
+
const observer = new MutationObserver(() => {
|
|
598
|
+
if (child.getAttribute('src')) {
|
|
599
|
+
observer.disconnect();
|
|
600
|
+
const scriptInfo = app.source.collectScript(child, parent, true);
|
|
601
|
+
if (scriptInfo?.replace) {
|
|
602
|
+
bodyAppendChild$1.call(app.container, scriptInfo.replace);
|
|
603
|
+
}
|
|
604
|
+
if (scriptInfo?.script) {
|
|
605
|
+
scriptInfo.script.excuteCode(app);
|
|
606
|
+
}
|
|
607
|
+
child.remove();
|
|
608
|
+
}
|
|
609
|
+
else if (child.textContent) {
|
|
610
|
+
observer.disconnect();
|
|
611
|
+
const scriptInstance = new Script({
|
|
612
|
+
code: child.textContent,
|
|
613
|
+
async: false,
|
|
614
|
+
defer: child.type === 'module',
|
|
615
|
+
isModule: child.type === 'module',
|
|
616
|
+
fromHtml: false,
|
|
617
|
+
});
|
|
618
|
+
app.source.scripts.set(randomUrl(), scriptInstance);
|
|
619
|
+
try {
|
|
620
|
+
scriptInstance.excuteCode(app);
|
|
621
|
+
}
|
|
622
|
+
catch (e) {
|
|
623
|
+
console.error(e);
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
!scriptInstance.isModule && dispatchLinkOrScriptLoad(child);
|
|
627
|
+
child.remove();
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
observer.observe(child, { attributeFilter: ['src'], childList: true, subtree: false });
|
|
632
|
+
return document.createComment('【bk-weweb】dynamic script or module');
|
|
633
|
+
}
|
|
634
|
+
return child;
|
|
635
|
+
}
|
|
636
|
+
return child;
|
|
637
|
+
}
|
|
638
|
+
function isSepcailElement(node) {
|
|
639
|
+
return node instanceof HTMLScriptElement
|
|
640
|
+
|| node instanceof HTMLStyleElement
|
|
641
|
+
|| node instanceof HTMLLinkElement;
|
|
642
|
+
}
|
|
643
|
+
function elmentAppendHandler(parent, newChild, rawMethod) {
|
|
644
|
+
if (newChild.__BK_WEWEB_APP_KEY__) {
|
|
645
|
+
const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
|
|
646
|
+
if (app?.container) {
|
|
647
|
+
const targetChild = resetNewElement(parent, newChild, app);
|
|
648
|
+
const needKeepAlive = isSepcailElement(newChild) && !!app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
649
|
+
const container = needKeepAlive ? document.head : app?.container;
|
|
650
|
+
setMarkElement(targetChild, app, needKeepAlive);
|
|
651
|
+
return rawMethod.call(container, targetChild);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return rawMethod.call(parent, newChild);
|
|
655
|
+
}
|
|
656
|
+
function elementInsertHandler(parent, newChild, passiveChild, rawMethod) {
|
|
657
|
+
if (newChild.__BK_WEWEB_APP_KEY__) {
|
|
658
|
+
const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
|
|
659
|
+
if (app?.container) {
|
|
660
|
+
const needKeepAlive = isSepcailElement(newChild) && app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
661
|
+
const container = needKeepAlive ? document.head : app?.container;
|
|
662
|
+
const targetChild = resetNewElement(parent, newChild, app);
|
|
663
|
+
if (needKeepAlive) {
|
|
664
|
+
targetChild.__KEEP_ALIVE__ = app.appCacheKey;
|
|
665
|
+
targetChild.setAttribute('data-from', app.name);
|
|
666
|
+
targetChild.setAttribute('data-keep-alive', 'true');
|
|
667
|
+
}
|
|
668
|
+
if (passiveChild && !container.contains(passiveChild)) {
|
|
669
|
+
return bodyAppendChild$1.call(container, targetChild);
|
|
670
|
+
}
|
|
671
|
+
return rawMethod.call(container, targetChild, passiveChild);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
675
|
+
}
|
|
676
|
+
function setMarkElement(element, app, keepAlive) {
|
|
677
|
+
if (keepAlive) {
|
|
678
|
+
element.__KEEP_ALIVE__ = app.appCacheKey;
|
|
679
|
+
element.setAttribute('data-from', app.name);
|
|
680
|
+
element.setAttribute('data-keep-alive', 'true');
|
|
681
|
+
}
|
|
682
|
+
element.setAttribute?.('powered-by', 'bk-weweb');
|
|
683
|
+
return element;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const { setAttribute } = Element.prototype;
|
|
687
|
+
const { appendChild: bodyAppendChild, removeChild: bodyRemoveChild, append, } = HTMLBodyElement.prototype;
|
|
688
|
+
const { appendChild: headAppendChild, removeChild: headRemoveChild, insertBefore: headInsertBefore, } = HTMLHeadElement.prototype;
|
|
689
|
+
const rawHead = document.head;
|
|
690
|
+
function rewriteBodyAndHeaderMethods() {
|
|
691
|
+
Element.prototype.setAttribute = function (key, value) {
|
|
692
|
+
const tagName = this.tagName.toLocaleUpperCase();
|
|
693
|
+
if (((['src', 'srcset'].includes(key) && ['SCRIPT', 'IMG'].includes(tagName))
|
|
694
|
+
|| (key === 'href' && ['LINK'].includes(tagName)))
|
|
695
|
+
&& this.__BK_WEWEB_APP_KEY__
|
|
696
|
+
&& appCache.getApp(this.__BK_WEWEB_APP_KEY__ || '')) {
|
|
697
|
+
setAttribute.call(this, key, fillUpPath(value, appCache.getApp(this.__BK_WEWEB_APP_KEY__).url));
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
setAttribute.call(this, key, value);
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
HTMLBodyElement.prototype.appendChild = function appendChildNew(newChild) {
|
|
704
|
+
if (newChild.__KEEP_ALIVE__ && isSepcailElement(newChild))
|
|
705
|
+
return headAppendChild.call(rawHead, newChild);
|
|
706
|
+
return elmentAppendHandler(this, newChild, bodyAppendChild);
|
|
707
|
+
};
|
|
708
|
+
HTMLBodyElement.prototype.append = function (...nodes) {
|
|
709
|
+
let i = 0;
|
|
710
|
+
const { length } = nodes;
|
|
711
|
+
while (i < length) {
|
|
712
|
+
if (nodes[i].__KEEP_ALIVE__ && isSepcailElement(nodes[i])) {
|
|
713
|
+
return headAppendChild.call(rawHead, nodes[i]);
|
|
714
|
+
}
|
|
715
|
+
elmentAppendHandler(this, nodes[i], bodyAppendChild);
|
|
716
|
+
i += 1;
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
HTMLHeadElement.prototype.appendChild = HTMLBodyElement.prototype.appendChild;
|
|
720
|
+
HTMLHeadElement.prototype.insertBefore = function (newChild, refChild) {
|
|
721
|
+
return elementInsertHandler(this, newChild, refChild, headInsertBefore);
|
|
722
|
+
};
|
|
723
|
+
HTMLBodyElement.prototype.removeChild = function removeChildNew(oldChild) {
|
|
724
|
+
const app = appCache.getApp(oldChild.__BK_WEWEB_APP_KEY__);
|
|
725
|
+
if (app?.container?.contains(oldChild)) {
|
|
726
|
+
const node = bodyRemoveChild.call(app.container, oldChild);
|
|
727
|
+
return node;
|
|
728
|
+
}
|
|
729
|
+
if (this.contains(oldChild)) {
|
|
730
|
+
return bodyRemoveChild.call(this, oldChild);
|
|
731
|
+
}
|
|
732
|
+
return oldChild;
|
|
733
|
+
};
|
|
734
|
+
HTMLHeadElement.prototype.removeChild = HTMLBodyElement.prototype.removeChild;
|
|
735
|
+
}
|
|
736
|
+
function resetBodyAndHeaderMethods() {
|
|
737
|
+
setCurrentRunningApp(null);
|
|
738
|
+
Element.prototype.setAttribute = setAttribute;
|
|
739
|
+
HTMLBodyElement.prototype.appendChild = bodyAppendChild;
|
|
740
|
+
HTMLBodyElement.prototype.append = append;
|
|
741
|
+
HTMLBodyElement.prototype.removeChild = bodyRemoveChild;
|
|
742
|
+
HTMLHeadElement.prototype.appendChild = headAppendChild;
|
|
743
|
+
HTMLHeadElement.prototype.insertBefore = headInsertBefore;
|
|
744
|
+
HTMLHeadElement.prototype.removeChild = headRemoveChild;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function beforeLoad() {
|
|
748
|
+
rewriteBodyAndHeaderMethods();
|
|
749
|
+
rewriteDocumentPrototypeMethods();
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const constructorMap = new WeakMap();
|
|
753
|
+
function isConstructor(value) {
|
|
754
|
+
if (constructorMap.has(value)) {
|
|
755
|
+
return constructorMap.get(value);
|
|
756
|
+
}
|
|
757
|
+
const valueStr = value.toString();
|
|
758
|
+
const result = (value.prototype
|
|
759
|
+
&& value.prototype.constructor === value
|
|
760
|
+
&& Object.getOwnPropertyNames(value.prototype).length > 1)
|
|
761
|
+
|| /^function\s+[A-Z]/.test(valueStr)
|
|
762
|
+
|| /^class\s+/.test(valueStr);
|
|
763
|
+
constructorMap.set(value, result);
|
|
764
|
+
return result;
|
|
765
|
+
}
|
|
766
|
+
const rawWindowMethodMap = new WeakMap();
|
|
767
|
+
function bindFunctionToRawWindow(rawWindow, value) {
|
|
768
|
+
if (rawWindowMethodMap.has(value)) {
|
|
769
|
+
return rawWindowMethodMap.get(value);
|
|
770
|
+
}
|
|
771
|
+
if (isFunction(value) && !isConstructor(value)) {
|
|
772
|
+
const bindRawWindowValue = value.bind(rawWindow);
|
|
773
|
+
Object.keys(value).forEach(key => (bindRawWindowValue[key] = value[key]));
|
|
774
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
775
|
+
if (value.hasOwnProperty('prototype') && !bindRawWindowValue.hasOwnProperty('prototype')) {
|
|
776
|
+
bindRawWindowValue.prototype = value.prototype;
|
|
777
|
+
}
|
|
778
|
+
rawWindowMethodMap.set(value, bindRawWindowValue);
|
|
779
|
+
return bindRawWindowValue;
|
|
780
|
+
}
|
|
781
|
+
return value;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/* eslint-disable no-param-reassign */
|
|
785
|
+
// rewrite document and body event listener
|
|
786
|
+
function rewriteDocumentAndBodyEvent() {
|
|
787
|
+
const { addEventListener, removeEventListener } = window.document;
|
|
788
|
+
const { addEventListener: bodyAddEventListener, removeEventListener: bodyRemoveEventListener } = window.document.body;
|
|
789
|
+
document.addEventListener = function (type, listener, options) {
|
|
790
|
+
const app = getCurrentRunningApp();
|
|
791
|
+
if (app) {
|
|
792
|
+
const appListenersMap = documentEventListenerMap.get(app.appCacheKey);
|
|
793
|
+
if (appListenersMap) {
|
|
794
|
+
const appListenerList = appListenersMap.get(type);
|
|
795
|
+
if (appListenerList) {
|
|
796
|
+
appListenerList.add(listener);
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
appListenersMap.set(type, new Set([listener]));
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
documentEventListenerMap.set(app.appCacheKey, new Map([[type, new Set([listener])]]));
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
addEventListener.call(app?.container instanceof ShadowRoot ? app.container : this, type, listener, options);
|
|
807
|
+
};
|
|
808
|
+
document.body.addEventListener = document.addEventListener;
|
|
809
|
+
document.removeEventListener = function (type, listener, options) {
|
|
810
|
+
const app = getCurrentRunningApp();
|
|
811
|
+
if (app) {
|
|
812
|
+
const appListenersMap = documentEventListenerMap.get(app.appCacheKey);
|
|
813
|
+
if (appListenersMap) {
|
|
814
|
+
const appListenerList = appListenersMap.get(type);
|
|
815
|
+
if (appListenerList?.size && appListenerList.has(listener)) {
|
|
816
|
+
appListenerList.delete(listener);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
removeEventListener.call(app?.container instanceof ShadowRoot ? app.container : this, type, listener, options);
|
|
821
|
+
};
|
|
822
|
+
document.body.removeEventListener = document.removeEventListener;
|
|
823
|
+
function resetDocumentAndBodyEvent() {
|
|
824
|
+
document.addEventListener = addEventListener;
|
|
825
|
+
document.body.addEventListener = bodyAddEventListener;
|
|
826
|
+
document.removeEventListener = removeEventListener;
|
|
827
|
+
document.body.removeEventListener = bodyRemoveEventListener;
|
|
828
|
+
}
|
|
829
|
+
return {
|
|
830
|
+
resetDocumentAndBodyEvent,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
// rewrite window funtion like settimeout setinterval ...
|
|
834
|
+
function rewriteWindowFunction(fakeWindow) {
|
|
835
|
+
const app = getCurrentRunningApp();
|
|
836
|
+
const eventListenerMap = new Map();
|
|
837
|
+
const intervalIdMap = new Map();
|
|
838
|
+
const timeoutIdMap = new Map();
|
|
839
|
+
const rawWindow = window;
|
|
840
|
+
const rawDocument = window.document;
|
|
841
|
+
const { addEventListener, removeEventListener, setInterval, setTimeout, clearInterval, clearTimeout } = window;
|
|
842
|
+
const { removeEventListener: documentRemoveEventListener } = window.document;
|
|
843
|
+
// listener may be null, e.g test-passive
|
|
844
|
+
fakeWindow.addEventListener = function (type, listener, options) {
|
|
845
|
+
const listenerList = eventListenerMap.get(type);
|
|
846
|
+
if (listenerList) {
|
|
847
|
+
listenerList.add(listener);
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
eventListenerMap.set(type, new Set([listener]));
|
|
851
|
+
}
|
|
852
|
+
addEventListener.call(rawWindow, type, listener, options);
|
|
853
|
+
};
|
|
854
|
+
fakeWindow.removeEventListener = function (type, listener, options) {
|
|
855
|
+
const listenerList = eventListenerMap.get(type);
|
|
856
|
+
if (listenerList?.size && listenerList.has(listener)) {
|
|
857
|
+
listenerList.delete(listener);
|
|
858
|
+
}
|
|
859
|
+
removeEventListener.call(rawWindow, type, listener, options);
|
|
860
|
+
};
|
|
861
|
+
fakeWindow.setInterval = function (handler, timeout, ...args) {
|
|
862
|
+
const intervalId = setInterval.call(rawWindow, handler, timeout, ...args);
|
|
863
|
+
intervalIdMap.set(intervalId, { handler, timeout, args });
|
|
864
|
+
return intervalId;
|
|
865
|
+
};
|
|
866
|
+
fakeWindow.setTimeout = function (handler, timeout, ...args) {
|
|
867
|
+
const timeoutId = setTimeout.call(rawWindow, handler, timeout, ...args);
|
|
868
|
+
timeoutIdMap.set(timeoutId, { handler, timeout, args });
|
|
869
|
+
return timeoutId;
|
|
870
|
+
};
|
|
871
|
+
fakeWindow.clearInterval = function (intervalId) {
|
|
872
|
+
intervalIdMap.delete(intervalId);
|
|
873
|
+
clearInterval.call(rawWindow, intervalId);
|
|
874
|
+
};
|
|
875
|
+
fakeWindow.clearTimeout = function (timeoutId) {
|
|
876
|
+
timeoutIdMap.delete(timeoutId);
|
|
877
|
+
clearTimeout.call(rawWindow, timeoutId);
|
|
878
|
+
};
|
|
879
|
+
// reset all event listener & interval & timeout when unmount app
|
|
880
|
+
const resetWindowFunction = () => {
|
|
881
|
+
// clear window events listener
|
|
882
|
+
if (eventListenerMap.size) {
|
|
883
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
884
|
+
for (const listener of listenerList) {
|
|
885
|
+
removeEventListener.call(rawWindow, type, listener);
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
eventListenerMap.clear();
|
|
889
|
+
}
|
|
890
|
+
// clear settimeout timers
|
|
891
|
+
if (intervalIdMap.size) {
|
|
892
|
+
intervalIdMap.forEach((_, intervalId) => {
|
|
893
|
+
clearInterval.call(rawWindow, intervalId);
|
|
894
|
+
});
|
|
895
|
+
intervalIdMap.clear();
|
|
896
|
+
}
|
|
897
|
+
if (timeoutIdMap.size) {
|
|
898
|
+
timeoutIdMap.forEach((_, timeoutId) => {
|
|
899
|
+
clearTimeout.call(rawWindow, timeoutId);
|
|
900
|
+
});
|
|
901
|
+
timeoutIdMap.clear();
|
|
902
|
+
}
|
|
903
|
+
if (app) {
|
|
904
|
+
documentClickListMap.delete(app.appCacheKey);
|
|
905
|
+
const documentAppListenersMap = documentEventListenerMap.get(app.appCacheKey);
|
|
906
|
+
if (documentAppListenersMap) {
|
|
907
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
908
|
+
for (const listener of listenerList) {
|
|
909
|
+
documentRemoveEventListener.call(rawDocument, type, listener);
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
documentAppListenersMap.clear();
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
return {
|
|
917
|
+
resetWindowFunction,
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/* eslint-disable no-new-func */
|
|
922
|
+
class SandBox {
|
|
923
|
+
app;
|
|
924
|
+
resetWindowFunction;
|
|
925
|
+
resetDocumentAndBodyEvent;
|
|
926
|
+
injectedKeySet = new Set();
|
|
927
|
+
escapedKeySet = new Set();
|
|
928
|
+
active = false;
|
|
929
|
+
rawWindow;
|
|
930
|
+
rawDocument;
|
|
931
|
+
proxyWindow;
|
|
932
|
+
fakeWindow = {};
|
|
933
|
+
windowSymbolKey;
|
|
934
|
+
constructor(app) {
|
|
935
|
+
this.app = app;
|
|
936
|
+
const descriptorMap = new Map();
|
|
937
|
+
const rawWindow = window;
|
|
938
|
+
const rawDocument = window.document;
|
|
939
|
+
this.fakeWindow.__POWERED_BY_BK_WEWEB__ = true;
|
|
940
|
+
this.fakeWindow.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
|
|
941
|
+
this.rawWindow = rawWindow;
|
|
942
|
+
this.rawDocument = rawDocument;
|
|
943
|
+
this.fakeWindow.rawWindow = rawWindow;
|
|
944
|
+
this.fakeWindow.rawDocument = rawDocument;
|
|
945
|
+
const { resetWindowFunction } = rewriteWindowFunction(this.fakeWindow);
|
|
946
|
+
this.resetWindowFunction = resetWindowFunction;
|
|
947
|
+
this.windowSymbolKey = `__${(app.name || app.appCacheKey).replace(/(-|,|:|~|'|")/gmi, '_')}_${random(10)}__`;
|
|
948
|
+
this.proxyWindow = new Proxy(this.fakeWindow, {
|
|
949
|
+
get: (target, key) => {
|
|
950
|
+
if (commonRawWindowKeyMap[key] || key === Symbol.unscopables)
|
|
951
|
+
return rawWindow[key];
|
|
952
|
+
if (commonFakeWindowKeyMap[key])
|
|
953
|
+
return this.fakeWindow[key];
|
|
954
|
+
if (['window', 'self', 'globalThis'].includes(key))
|
|
955
|
+
return this.proxyWindow;
|
|
956
|
+
if (key === 'document' || key === 'eval') {
|
|
957
|
+
app.registerRunningApp();
|
|
958
|
+
// eslint-disable-next-line no-eval
|
|
959
|
+
return key === 'document' ? rawDocument : eval;
|
|
960
|
+
}
|
|
961
|
+
if (scopedLocationKeyList.includes(key)
|
|
962
|
+
&& this.app instanceof MicroAppModel
|
|
963
|
+
&& this.app.iframe
|
|
964
|
+
&& this.app.scopeLocation) {
|
|
965
|
+
return this.app.iframe.contentWindow?.[key];
|
|
966
|
+
}
|
|
967
|
+
// if (key === Symbol.unscopables) return com;
|
|
968
|
+
if (key === 'hasOwnProperty')
|
|
969
|
+
return (key) => this.fakeWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
|
|
970
|
+
if (key === 'top' || key === 'parent') {
|
|
971
|
+
if (rawWindow === rawWindow.parent) {
|
|
972
|
+
return this.proxyWindow;
|
|
973
|
+
}
|
|
974
|
+
return Reflect.get(rawWindow, key); // iframe
|
|
975
|
+
}
|
|
976
|
+
if (Reflect.has(target, key)
|
|
977
|
+
|| scopeWindowKeyList.includes(key))
|
|
978
|
+
return Reflect.get(target, key);
|
|
979
|
+
const rawValue = Reflect.get(rawWindow, key);
|
|
980
|
+
return bindFunctionToRawWindow(rawWindow, rawValue);
|
|
981
|
+
},
|
|
982
|
+
set: (target, key, value) => {
|
|
983
|
+
if (this.active) {
|
|
984
|
+
if (scopedLocationKeyList.includes(key)
|
|
985
|
+
&& this.app instanceof MicroAppModel
|
|
986
|
+
&& this.app.iframe
|
|
987
|
+
&& this.app.scopeLocation) {
|
|
988
|
+
return Reflect.set(this.app.iframe.contentWindow, key, value);
|
|
989
|
+
}
|
|
990
|
+
if (escapeSetterKeyList.includes(key)) {
|
|
991
|
+
Reflect.set(rawWindow, key, value);
|
|
992
|
+
}
|
|
993
|
+
else if (!target.hasOwnProperty(key)
|
|
994
|
+
&& rawWindow.hasOwnProperty(key)
|
|
995
|
+
&& !scopeWindowKeyList.includes(key)) {
|
|
996
|
+
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
997
|
+
const { writable, configurable, enumerable } = descriptor;
|
|
998
|
+
if (writable) {
|
|
999
|
+
Object.defineProperty(target, key, {
|
|
1000
|
+
configurable,
|
|
1001
|
+
enumerable,
|
|
1002
|
+
writable,
|
|
1003
|
+
value,
|
|
1004
|
+
});
|
|
1005
|
+
this.injectedKeySet.add(key);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
Reflect.set(target, key, value);
|
|
1010
|
+
this.injectedKeySet.add(key);
|
|
1011
|
+
}
|
|
1012
|
+
if ((commonEScapeKeyList.includes(key) && !Reflect.has(rawWindow, key))
|
|
1013
|
+
&& !scopeWindowKeyList.includes(key)) {
|
|
1014
|
+
Reflect.set(rawWindow, key, value);
|
|
1015
|
+
this.escapedKeySet.add(key);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return true;
|
|
1019
|
+
},
|
|
1020
|
+
has: (target, key) => commonRawWindowKeyMap[key]
|
|
1021
|
+
|| key in target
|
|
1022
|
+
|| key in rawWindow,
|
|
1023
|
+
getOwnPropertyDescriptor: (target, key) => {
|
|
1024
|
+
if (target.hasOwnProperty(key)) {
|
|
1025
|
+
return Object.getOwnPropertyDescriptor(target, key);
|
|
1026
|
+
}
|
|
1027
|
+
if (rawWindow.hasOwnProperty(key)) {
|
|
1028
|
+
descriptorMap.set(key, DescriptorMapValue.WINDOW);
|
|
1029
|
+
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
1030
|
+
if (descriptor && !descriptor.configurable) {
|
|
1031
|
+
descriptor.configurable = true;
|
|
1032
|
+
}
|
|
1033
|
+
return descriptor;
|
|
1034
|
+
}
|
|
1035
|
+
return undefined;
|
|
1036
|
+
},
|
|
1037
|
+
// Object.defineProperty(window, key, Descriptor)
|
|
1038
|
+
defineProperty: (target, key, value) => {
|
|
1039
|
+
const from = descriptorMap.get(key);
|
|
1040
|
+
if (from === DescriptorMapValue.WINDOW) {
|
|
1041
|
+
return Reflect.defineProperty(rawWindow, key, value);
|
|
1042
|
+
}
|
|
1043
|
+
return Reflect.defineProperty(target, key, value);
|
|
1044
|
+
},
|
|
1045
|
+
// Object.getOwnPropertyNames(window)
|
|
1046
|
+
ownKeys: (target) => arrayUnique(Reflect.ownKeys(rawWindow)
|
|
1047
|
+
.concat(Reflect.ownKeys(target))),
|
|
1048
|
+
deleteProperty: (target, key) => {
|
|
1049
|
+
if (target.hasOwnProperty(key)) {
|
|
1050
|
+
this.injectedKeySet.has(key) && this.injectedKeySet.delete(key);
|
|
1051
|
+
this.escapedKeySet.has(key) && Reflect.deleteProperty(rawWindow, key);
|
|
1052
|
+
return Reflect.deleteProperty(target, key);
|
|
1053
|
+
}
|
|
1054
|
+
return true;
|
|
1055
|
+
},
|
|
1056
|
+
});
|
|
1057
|
+
if (app.showSourceCode) {
|
|
1058
|
+
rawWindow[this.windowSymbolKey] = this.proxyWindow;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
activeated(data) {
|
|
1062
|
+
if (!this.active) {
|
|
1063
|
+
this.active = true;
|
|
1064
|
+
this.fakeWindow.__BK_WEWEB_DATA__ = data ?? {};
|
|
1065
|
+
const { resetDocumentAndBodyEvent } = rewriteDocumentAndBodyEvent();
|
|
1066
|
+
this.resetDocumentAndBodyEvent = resetDocumentAndBodyEvent;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
deactivated() {
|
|
1070
|
+
if (this.active) {
|
|
1071
|
+
this.active = false;
|
|
1072
|
+
this.resetWindowFunction();
|
|
1073
|
+
// this.injectedKeySet.forEach((key: PropertyKey) => Reflect.deleteProperty(this.fakeWindow, key));
|
|
1074
|
+
this.injectedKeySet.clear();
|
|
1075
|
+
this.escapedKeySet.forEach((key) => Reflect.deleteProperty(window, key));
|
|
1076
|
+
this.escapedKeySet.clear();
|
|
1077
|
+
this.resetDocumentAndBodyEvent?.();
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
class MicroInstanceModel {
|
|
1083
|
+
state = AppState.UNSET;
|
|
1084
|
+
isPreLoad = false;
|
|
1085
|
+
appCacheKey;
|
|
1086
|
+
url;
|
|
1087
|
+
container;
|
|
1088
|
+
scopeJs = false;
|
|
1089
|
+
source;
|
|
1090
|
+
sandBox;
|
|
1091
|
+
name;
|
|
1092
|
+
showSourceCode = true;
|
|
1093
|
+
scopeCss = true;
|
|
1094
|
+
keepAlive;
|
|
1095
|
+
initSource;
|
|
1096
|
+
data;
|
|
1097
|
+
constructor(props) {
|
|
1098
|
+
this.name = props.id !== props.url ? props.id : random(5);
|
|
1099
|
+
this.appCacheKey = props.id || this.name;
|
|
1100
|
+
this.url = props.url;
|
|
1101
|
+
this.container = props.container ?? undefined;
|
|
1102
|
+
this.scopeJs = props.scopeJs ?? true;
|
|
1103
|
+
this.showSourceCode = props.showSourceCode ?? true;
|
|
1104
|
+
this.scopeCss = props.scopeCss ?? true;
|
|
1105
|
+
this.keepAlive = props.keepAlive ?? false;
|
|
1106
|
+
this.data = props.data ?? {};
|
|
1107
|
+
this.initSource = props.initSource ?? [];
|
|
1108
|
+
// 是否启用沙盒
|
|
1109
|
+
if (this.scopeJs) {
|
|
1110
|
+
this.sandBox = new SandBox(this);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
get status() {
|
|
1114
|
+
return this.state;
|
|
1115
|
+
}
|
|
1116
|
+
set status(v) {
|
|
1117
|
+
this.state = v;
|
|
1118
|
+
}
|
|
1119
|
+
async start() {
|
|
1120
|
+
if (!this.source || [AppState.UNSET, AppState.ERROR].includes(this.status)) {
|
|
1121
|
+
this.source = new EntrySource(this.url);
|
|
1122
|
+
await this.source.importEntery(this);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
onMount() {
|
|
1126
|
+
if (this.isPreLoad)
|
|
1127
|
+
return;
|
|
1128
|
+
this.state = AppState.LOADED;
|
|
1129
|
+
this.mount();
|
|
1130
|
+
}
|
|
1131
|
+
onError() {
|
|
1132
|
+
this.state = AppState.ERROR;
|
|
1133
|
+
}
|
|
1134
|
+
mount(container, callback) {
|
|
1135
|
+
this.isPreLoad = false;
|
|
1136
|
+
this.container = container ?? this.container;
|
|
1137
|
+
this.state = AppState.MOUNTING;
|
|
1138
|
+
if (this.container instanceof HTMLElement) {
|
|
1139
|
+
this.container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1140
|
+
}
|
|
1141
|
+
this.container.innerHTML = '';
|
|
1142
|
+
const instanceWrap = document.createElement('div');
|
|
1143
|
+
const wrapId = `${this.name}-wrapper`;
|
|
1144
|
+
instanceWrap.setAttribute('id', wrapId);
|
|
1145
|
+
if (this.source?.styles.size) {
|
|
1146
|
+
excuteAppStyles(this, this.container);
|
|
1147
|
+
}
|
|
1148
|
+
this.container.appendChild(instanceWrap);
|
|
1149
|
+
this.sandBox?.activeated();
|
|
1150
|
+
execAppScripts(this).finally(() => {
|
|
1151
|
+
this.state = AppState.MOUNTED;
|
|
1152
|
+
const scriptInfo = this.source?.getScript(this.url);
|
|
1153
|
+
if (typeof scriptInfo?.exportInstance?.render === 'function') {
|
|
1154
|
+
scriptInfo.exportInstance.render(instanceWrap, this.data);
|
|
1155
|
+
}
|
|
1156
|
+
callback?.(this, scriptInfo?.exportInstance);
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
activated(container, callback) {
|
|
1160
|
+
this.isPreLoad = false;
|
|
1161
|
+
this.state = AppState.ACTIVATED;
|
|
1162
|
+
if (this.container && container) {
|
|
1163
|
+
if (container instanceof Element)
|
|
1164
|
+
container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1165
|
+
const fragment = document.createDocumentFragment();
|
|
1166
|
+
Array.from(this.container.childNodes).forEach((node) => {
|
|
1167
|
+
fragment.appendChild(node);
|
|
1168
|
+
});
|
|
1169
|
+
container.appendChild(fragment);
|
|
1170
|
+
this.container = container;
|
|
1171
|
+
this.sandBox?.activeated();
|
|
1172
|
+
const scriptInfo = this.source?.getScript(this.url);
|
|
1173
|
+
callback?.(this, scriptInfo?.exportInstance);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
deactivated() {
|
|
1177
|
+
this.state = AppState.DEACTIVATED;
|
|
1178
|
+
this.sandBox?.deactivated();
|
|
1179
|
+
}
|
|
1180
|
+
unmount(needDestroy) {
|
|
1181
|
+
this.state = AppState.UNMOUNT;
|
|
1182
|
+
this.sandBox?.deactivated();
|
|
1183
|
+
needDestroy && appCache.deleteApp(this.url);
|
|
1184
|
+
this.container.innerHTML = '';
|
|
1185
|
+
this.container = undefined;
|
|
1186
|
+
}
|
|
1187
|
+
registerRunningApp() {
|
|
1188
|
+
setCurrentRunningApp(this);
|
|
1189
|
+
Promise.resolve().then(() => setCurrentRunningApp(null));
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
async function load(props) {
|
|
1194
|
+
if (props.mode === 'app')
|
|
1195
|
+
return await loadApp(props);
|
|
1196
|
+
if (props.mode === 'js')
|
|
1197
|
+
return await loadInstance(props);
|
|
1198
|
+
return await loadApp(props);
|
|
1199
|
+
}
|
|
1200
|
+
// 加载app
|
|
1201
|
+
async function loadApp(props) {
|
|
1202
|
+
let instance = appCache.getApp(props.id);
|
|
1203
|
+
if (!instance) {
|
|
1204
|
+
instance = new MicroAppModel(props);
|
|
1205
|
+
appCache.setApp(instance);
|
|
1206
|
+
}
|
|
1207
|
+
await instance.start();
|
|
1208
|
+
return instance;
|
|
1209
|
+
}
|
|
1210
|
+
// 加载instance
|
|
1211
|
+
function loadInstance(props) {
|
|
1212
|
+
return new Promise((resolve) => {
|
|
1213
|
+
let instance = appCache.getApp(props.id);
|
|
1214
|
+
if (!instance) {
|
|
1215
|
+
instance = new MicroInstanceModel(props);
|
|
1216
|
+
appCache.setApp(instance);
|
|
1217
|
+
instance.start().then(() => resolve(instance));
|
|
1218
|
+
}
|
|
1219
|
+
else if ([AppState.UNSET, AppState.MOUNTING].includes(instance.status)) {
|
|
1220
|
+
const timer = setInterval(() => {
|
|
1221
|
+
if ([AppState.ERROR, AppState.MOUNTED].includes(instance.status)) {
|
|
1222
|
+
resolve(instance);
|
|
1223
|
+
clearInterval(timer);
|
|
1224
|
+
}
|
|
1225
|
+
}, 300);
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
resolve(instance);
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
function unmount(appKey) {
|
|
1234
|
+
const app = appCache.getApp(appKey);
|
|
1235
|
+
if (app?.status !== AppState.UNMOUNT) {
|
|
1236
|
+
app?.unmount();
|
|
1237
|
+
}
|
|
1238
|
+
if (!appCache.hasActiveApp) {
|
|
1239
|
+
resetBodyAndHeaderMethods();
|
|
1240
|
+
resetDocumentPrototypeMethods();
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
function activated(appKey, container, callback) {
|
|
1245
|
+
const app = appCache.getApp(appKey);
|
|
1246
|
+
if (app?.status === AppState.DEACTIVATED && app.keepAlive) {
|
|
1247
|
+
nextTask(() => app?.activated(container, callback));
|
|
1248
|
+
}
|
|
1249
|
+
else {
|
|
1250
|
+
nextTask(() => app?.mount(container, callback));
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
function deactivated(appKey) {
|
|
1255
|
+
const app = appCache.getApp(appKey);
|
|
1256
|
+
if (app && [AppState.ACTIVATED, AppState.MOUNTED].includes(app.status)) {
|
|
1257
|
+
app.keepAlive ? app.deactivated() : app.unmount();
|
|
1258
|
+
}
|
|
1259
|
+
if (!appCache.hasActiveApp) {
|
|
1260
|
+
resetBodyAndHeaderMethods();
|
|
1261
|
+
resetDocumentPrototypeMethods();
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// bk-weweb支持的属性配置
|
|
1266
|
+
var ObserveAttrs;
|
|
1267
|
+
(function (ObserveAttrs) {
|
|
1268
|
+
ObserveAttrs["URL"] = "url";
|
|
1269
|
+
ObserveAttrs["showSourceCode"] = "showSourceCode";
|
|
1270
|
+
ObserveAttrs["scopeLocation"] = "scopeLocation";
|
|
1271
|
+
ObserveAttrs["setShodowDom"] = "setShodowDom";
|
|
1272
|
+
ObserveAttrs["mode"] = "mode";
|
|
1273
|
+
ObserveAttrs["data"] = "data";
|
|
1274
|
+
ObserveAttrs["id"] = "id";
|
|
1275
|
+
ObserveAttrs["scopeJs"] = "scopeJs";
|
|
1276
|
+
ObserveAttrs["scopeCss"] = "scopeCss";
|
|
1277
|
+
ObserveAttrs["keepAlive"] = "keepAlive";
|
|
1278
|
+
})(ObserveAttrs || (ObserveAttrs = {}));
|
|
1279
|
+
class BkIframeElement extends HTMLElement {
|
|
1280
|
+
static get observedAttributes() {
|
|
1281
|
+
return [ObserveAttrs.URL];
|
|
1282
|
+
}
|
|
1283
|
+
connected = false;
|
|
1284
|
+
appUrl = '';
|
|
1285
|
+
// 考虑到js模式下 需要js bundle的复用性 需用户设置id属性 如果单实例下则可以不用配置
|
|
1286
|
+
get appKey() {
|
|
1287
|
+
return this.getAttribute(ObserveAttrs.id) || this.getAttribute(ObserveAttrs.URL);
|
|
1288
|
+
}
|
|
1289
|
+
get appData() {
|
|
1290
|
+
if (this.hasAttribute(ObserveAttrs.data)) {
|
|
1291
|
+
try {
|
|
1292
|
+
return JSON.parse(this.getAttribute(ObserveAttrs.data));
|
|
1293
|
+
}
|
|
1294
|
+
catch { }
|
|
1295
|
+
}
|
|
1296
|
+
return {};
|
|
1297
|
+
}
|
|
1298
|
+
get appProps() {
|
|
1299
|
+
if (this.getAttribute('mode') === 'js') {
|
|
1300
|
+
return {
|
|
1301
|
+
url: this.getAttribute(ObserveAttrs.URL),
|
|
1302
|
+
mode: 'js',
|
|
1303
|
+
id: this.appKey,
|
|
1304
|
+
data: this.appData,
|
|
1305
|
+
container: this.shadowRoot ?? this,
|
|
1306
|
+
showSourceCode: this.getBooleanAttr(ObserveAttrs.showSourceCode),
|
|
1307
|
+
scopeCss: this.getBooleanAttr(ObserveAttrs.scopeCss) && !this.getBooleanAttr(ObserveAttrs.setShodowDom),
|
|
1308
|
+
scopeJs: this.getBooleanAttr(ObserveAttrs.scopeJs),
|
|
1309
|
+
keepAlive: this.getBooleanAttr(ObserveAttrs.keepAlive),
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
return {
|
|
1313
|
+
url: this.getAttribute(ObserveAttrs.URL),
|
|
1314
|
+
mode: 'app',
|
|
1315
|
+
id: this.appKey,
|
|
1316
|
+
data: this.appData,
|
|
1317
|
+
container: this.shadowRoot ?? this,
|
|
1318
|
+
scopeLocation: this.getBooleanAttr(ObserveAttrs.scopeLocation),
|
|
1319
|
+
showSourceCode: this.getBooleanAttr(ObserveAttrs.showSourceCode),
|
|
1320
|
+
scopeCss: !this.getBooleanAttr(ObserveAttrs.setShodowDom),
|
|
1321
|
+
scopeJs: !this.getBooleanAttr(ObserveAttrs.scopeJs),
|
|
1322
|
+
keepAlive: this.getBooleanAttr(ObserveAttrs.keepAlive),
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
async connectedCallback() {
|
|
1326
|
+
beforeLoad();
|
|
1327
|
+
if (this.getBooleanAttr(ObserveAttrs.setShodowDom) && !this.shadowRoot) {
|
|
1328
|
+
this.attachShadow({ mode: 'open', delegatesFocus: true });
|
|
1329
|
+
}
|
|
1330
|
+
await load(this.appProps);
|
|
1331
|
+
activated(this.appKey, this.shadowRoot ?? this);
|
|
1332
|
+
this.connected = true;
|
|
1333
|
+
}
|
|
1334
|
+
disconnectedCallback() {
|
|
1335
|
+
this.connected = false;
|
|
1336
|
+
if (this.appProps.keepAlive) {
|
|
1337
|
+
deactivated(this.appKey);
|
|
1338
|
+
}
|
|
1339
|
+
else
|
|
1340
|
+
unmount(this.appKey);
|
|
1341
|
+
}
|
|
1342
|
+
attributeChangedCallback(attr, _oldVal, newVal) {
|
|
1343
|
+
if (attr !== ObserveAttrs.URL || this[attr] === newVal)
|
|
1344
|
+
return;
|
|
1345
|
+
this.appUrl = newVal;
|
|
1346
|
+
(this.connected || appCache.getApp(this.appKey)) && this.handleAttributeChanged();
|
|
1347
|
+
}
|
|
1348
|
+
async handleAttributeChanged() {
|
|
1349
|
+
if (!this.appKey)
|
|
1350
|
+
return;
|
|
1351
|
+
if (this.getBooleanAttr(ObserveAttrs.setShodowDom)) {
|
|
1352
|
+
this.attachShadow({ mode: 'open' });
|
|
1353
|
+
}
|
|
1354
|
+
const app = appCache.getApp(this.appKey);
|
|
1355
|
+
if (app && app.url === this.appUrl && (app.isPreLoad || app.status === AppState.UNMOUNT)) {
|
|
1356
|
+
activated(this.appKey, this.shadowRoot ?? this);
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
await load(this.appProps);
|
|
1360
|
+
}
|
|
1361
|
+
getBooleanAttr(name) {
|
|
1362
|
+
return this.hasAttribute(name) ? this.getAttribute(name) !== 'false' : undefined;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// 收集和辩证资源
|
|
1367
|
+
async function collectSource(soruceList) {
|
|
1368
|
+
let source = [];
|
|
1369
|
+
if (typeof soruceList === 'function') {
|
|
1370
|
+
source = await soruceList();
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
source = soruceList || [];
|
|
1374
|
+
}
|
|
1375
|
+
if (!source.length)
|
|
1376
|
+
return {};
|
|
1377
|
+
const collectScript = new Map();
|
|
1378
|
+
const collectStyle = new Map();
|
|
1379
|
+
source.forEach((str) => {
|
|
1380
|
+
try {
|
|
1381
|
+
const url = new URL(str);
|
|
1382
|
+
if (url.pathname.match(/\.js$/)) {
|
|
1383
|
+
const script = appCache.getCacheScript(str);
|
|
1384
|
+
collectScript.set(str, new Script({
|
|
1385
|
+
code: script?.code || '',
|
|
1386
|
+
async: false,
|
|
1387
|
+
defer: false,
|
|
1388
|
+
url: str,
|
|
1389
|
+
isModule: false,
|
|
1390
|
+
initial: true,
|
|
1391
|
+
fromHtml: false,
|
|
1392
|
+
}));
|
|
1393
|
+
}
|
|
1394
|
+
else if (url.pathname.match(/\.css$/)) {
|
|
1395
|
+
const style = appCache.getCacheStyle(str);
|
|
1396
|
+
collectStyle.set(str, new Style({
|
|
1397
|
+
code: style?.code || '',
|
|
1398
|
+
prefetch: false,
|
|
1399
|
+
preload: false,
|
|
1400
|
+
url: str,
|
|
1401
|
+
fromHtml: true,
|
|
1402
|
+
initial: true,
|
|
1403
|
+
}));
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
catch {
|
|
1407
|
+
console.error(`【bk-weweb】: ${str} is invalid URL`);
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
return {
|
|
1411
|
+
collectScript,
|
|
1412
|
+
collectStyle,
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
const CUSTOM_ELEMENT_TAG = 'bk-weweb';
|
|
1417
|
+
class IframeApp {
|
|
1418
|
+
fetchSource;
|
|
1419
|
+
webcomponentTag = CUSTOM_ELEMENT_TAG;
|
|
1420
|
+
constructor() {
|
|
1421
|
+
if (!window.customElements.get(CUSTOM_ELEMENT_TAG)) {
|
|
1422
|
+
window.customElements.define(CUSTOM_ELEMENT_TAG, BkIframeElement);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
// todo set some global start props
|
|
1426
|
+
start(option) {
|
|
1427
|
+
if (typeof option?.fetchSource === 'function') {
|
|
1428
|
+
this.fetchSource = option.fetchSource;
|
|
1429
|
+
}
|
|
1430
|
+
this.webcomponentTag = option?.webcomponentTag || CUSTOM_ELEMENT_TAG;
|
|
1431
|
+
this.setWebComponentTag();
|
|
1432
|
+
}
|
|
1433
|
+
// 设置自定义dom标签名
|
|
1434
|
+
setWebComponentTag() {
|
|
1435
|
+
if (!window.customElements.get(this.webcomponentTag)) {
|
|
1436
|
+
window.customElements.define(this.webcomponentTag, BkIframeElement);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
const iframeApp = new IframeApp();
|
|
1441
|
+
|
|
1442
|
+
function fetchSource(url, options = {}) {
|
|
1443
|
+
if (iframeApp.fetchSource) {
|
|
1444
|
+
return iframeApp.fetchSource(url, options);
|
|
1445
|
+
}
|
|
1446
|
+
return window.fetch(url, options).then(res => res.text());
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/* eslint-disable no-restricted-syntax */
|
|
1450
|
+
let firstGlobalProp;
|
|
1451
|
+
let secondGlobalProp;
|
|
1452
|
+
let lastGlobalProp;
|
|
1453
|
+
// Script脚本实例
|
|
1454
|
+
class Script {
|
|
1455
|
+
code = '';
|
|
1456
|
+
async = false;
|
|
1457
|
+
defer = false;
|
|
1458
|
+
isModule = false;
|
|
1459
|
+
url;
|
|
1460
|
+
exportInstance;
|
|
1461
|
+
scoped;
|
|
1462
|
+
fromHtml;
|
|
1463
|
+
initial;
|
|
1464
|
+
constructor({ code, async, defer, isModule, url, fromHtml, initial }) {
|
|
1465
|
+
this.code = code;
|
|
1466
|
+
this.async = async;
|
|
1467
|
+
this.defer = defer;
|
|
1468
|
+
this.isModule = isModule;
|
|
1469
|
+
this.url = url;
|
|
1470
|
+
this.scoped = false;
|
|
1471
|
+
this.fromHtml = fromHtml ?? false;
|
|
1472
|
+
this.initial = initial ?? false;
|
|
1473
|
+
}
|
|
1474
|
+
async getCode(app) {
|
|
1475
|
+
if (this.code.length || !this.url) {
|
|
1476
|
+
return this.code;
|
|
1477
|
+
}
|
|
1478
|
+
let code = '';
|
|
1479
|
+
if (app?.source?.getScript(this.url)) {
|
|
1480
|
+
code = app.source.getScript(this.url)?.code || '';
|
|
1481
|
+
}
|
|
1482
|
+
if (!code && appCache.getCacheScript(this.url)) {
|
|
1483
|
+
code = appCache.getCacheScript(this.url)?.code || '';
|
|
1484
|
+
}
|
|
1485
|
+
if (!code) {
|
|
1486
|
+
code = await fetchSource(this.url).catch((e) => {
|
|
1487
|
+
console.error(`fetch script ${this.url} error`, e);
|
|
1488
|
+
return '';
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
this.code = code;
|
|
1492
|
+
return code;
|
|
1493
|
+
}
|
|
1494
|
+
async excuteCode(app, needRelaceScriptElement = false) {
|
|
1495
|
+
try {
|
|
1496
|
+
if (!this.code)
|
|
1497
|
+
await this.getCode(app);
|
|
1498
|
+
if (app instanceof MicroInstanceModel) {
|
|
1499
|
+
noteGlobalProps(app.sandBox?.proxyWindow || window);
|
|
1500
|
+
}
|
|
1501
|
+
let scopedCode = this.code;
|
|
1502
|
+
scopedCode = this.transformCode(app);
|
|
1503
|
+
if (app.showSourceCode || this.isModule) {
|
|
1504
|
+
const scriptElement = createElement$1('script');
|
|
1505
|
+
app.registerRunningApp();
|
|
1506
|
+
this.executeSourceScript(scriptElement, scopedCode);
|
|
1507
|
+
if (needRelaceScriptElement)
|
|
1508
|
+
return scriptElement;
|
|
1509
|
+
const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
1510
|
+
const container = needKeepAlive ? document.head : app.container;
|
|
1511
|
+
setMarkElement(scriptElement, app, needKeepAlive);
|
|
1512
|
+
container.appendChild(scriptElement);
|
|
1513
|
+
}
|
|
1514
|
+
else {
|
|
1515
|
+
this.executeMemoryScript(app, scopedCode);
|
|
1516
|
+
if (needRelaceScriptElement)
|
|
1517
|
+
return document.createComment('【bk-weweb】dynamic script');
|
|
1518
|
+
}
|
|
1519
|
+
if (app instanceof MicroInstanceModel) {
|
|
1520
|
+
const exportProp = getGlobalProp(app.sandBox?.proxyWindow || window);
|
|
1521
|
+
exportProp && (this.exportInstance = (app.sandBox?.proxyWindow || window)[exportProp]);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
catch (e) {
|
|
1525
|
+
console.error('execute script code error', e);
|
|
1526
|
+
}
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
transformCode(app) {
|
|
1530
|
+
if (app.sandBox) {
|
|
1531
|
+
if (app.showSourceCode || this.isModule) {
|
|
1532
|
+
return `;(function(window, self){
|
|
1533
|
+
with(window){
|
|
1534
|
+
;${this.code}\n
|
|
1535
|
+
}
|
|
1536
|
+
}).call(window.${app.sandBox.windowSymbolKey},
|
|
1537
|
+
window.${app.sandBox.windowSymbolKey});`;
|
|
1538
|
+
}
|
|
1539
|
+
return `
|
|
1540
|
+
with(window) {
|
|
1541
|
+
try {
|
|
1542
|
+
${this.code}
|
|
1543
|
+
}
|
|
1544
|
+
catch(e) {
|
|
1545
|
+
console.error(e)
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
`;
|
|
1549
|
+
}
|
|
1550
|
+
return this.code;
|
|
1551
|
+
}
|
|
1552
|
+
executeSourceScript(scriptElement, scopedCode) {
|
|
1553
|
+
if (this.isModule) {
|
|
1554
|
+
const blob = new Blob([scopedCode], { type: 'text/javascript' });
|
|
1555
|
+
scriptElement.src = URL.createObjectURL(blob);
|
|
1556
|
+
scriptElement.setAttribute('type', 'module');
|
|
1557
|
+
}
|
|
1558
|
+
else {
|
|
1559
|
+
scriptElement.textContent = scopedCode;
|
|
1560
|
+
}
|
|
1561
|
+
this.url && scriptElement.setAttribute('origin-src', this.url);
|
|
1562
|
+
}
|
|
1563
|
+
executeMemoryScript(app, scopedCode) {
|
|
1564
|
+
try {
|
|
1565
|
+
const isScopedLocation = app instanceof MicroAppModel && app.scopeLocation;
|
|
1566
|
+
app.registerRunningApp();
|
|
1567
|
+
// eslint-disable-next-line no-new-func
|
|
1568
|
+
new Function('window', 'location', 'history', scopedCode)(app.sandBox.proxyWindow, isScopedLocation ? app.iframe?.contentWindow?.location : window.location, isScopedLocation ? app.iframe?.contentWindow?.history : window.history);
|
|
1569
|
+
}
|
|
1570
|
+
catch (e) {
|
|
1571
|
+
console.error(e);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
// 全局属性是否跳过标记
|
|
1576
|
+
function shouldSkipProperty(global, p) {
|
|
1577
|
+
return (!global.hasOwnProperty(p)
|
|
1578
|
+
|| (!isNaN(p) && p < global.length)
|
|
1579
|
+
|| (isIE11 && global[p] && typeof window !== 'undefined' && global[p].parent === window));
|
|
1580
|
+
}
|
|
1581
|
+
// 获取instance js source code 执行后 绑定的export 实例
|
|
1582
|
+
function getGlobalProp(global, useFirstGlobalProp) {
|
|
1583
|
+
let cnt = 0;
|
|
1584
|
+
let foundLastProp;
|
|
1585
|
+
let result;
|
|
1586
|
+
for (const p in global) {
|
|
1587
|
+
// do not check frames cause it could be removed during import
|
|
1588
|
+
if (shouldSkipProperty(global, p))
|
|
1589
|
+
continue;
|
|
1590
|
+
if ((cnt === 0 && p !== firstGlobalProp) || (cnt === 1 && p !== secondGlobalProp))
|
|
1591
|
+
return p;
|
|
1592
|
+
if (foundLastProp) {
|
|
1593
|
+
lastGlobalProp = p;
|
|
1594
|
+
result = (useFirstGlobalProp && result) || p;
|
|
1595
|
+
}
|
|
1596
|
+
else {
|
|
1597
|
+
foundLastProp = p === lastGlobalProp;
|
|
1598
|
+
}
|
|
1599
|
+
cnt += 1;
|
|
1600
|
+
}
|
|
1601
|
+
return result;
|
|
1602
|
+
}
|
|
1603
|
+
// 标记全局属性
|
|
1604
|
+
function noteGlobalProps(global) {
|
|
1605
|
+
secondGlobalProp = undefined;
|
|
1606
|
+
firstGlobalProp = secondGlobalProp;
|
|
1607
|
+
for (const p in global) {
|
|
1608
|
+
if (shouldSkipProperty(global, p))
|
|
1609
|
+
continue;
|
|
1610
|
+
if (!firstGlobalProp)
|
|
1611
|
+
firstGlobalProp = p;
|
|
1612
|
+
else if (!secondGlobalProp)
|
|
1613
|
+
secondGlobalProp = p;
|
|
1614
|
+
lastGlobalProp = p;
|
|
1615
|
+
}
|
|
1616
|
+
return lastGlobalProp;
|
|
1617
|
+
}
|
|
1618
|
+
// app初始化dom 脚本执行
|
|
1619
|
+
async function execAppScripts(app) {
|
|
1620
|
+
const appInitialScriptList = Array.from(app.source.scripts.values()).filter(script => script.initial);
|
|
1621
|
+
// 初始化脚本最先执行
|
|
1622
|
+
if (appInitialScriptList.length) {
|
|
1623
|
+
await Promise.all(appInitialScriptList.map(script => script.excuteCode(app)));
|
|
1624
|
+
}
|
|
1625
|
+
const appScriptList = Array.from(app.source.scripts.values()).filter(script => script.fromHtml && !script.initial);
|
|
1626
|
+
const commomList = appScriptList.filter(script => ((!script.async && !script.defer) || script.isModule));
|
|
1627
|
+
// 保证同步脚本 和 module类型 最先执行
|
|
1628
|
+
await Promise.all(commomList.map(script => script.excuteCode(app)));
|
|
1629
|
+
// 最后执行 defer 和 async 脚本
|
|
1630
|
+
const deferScriptList = [];
|
|
1631
|
+
const asyncScriptList = [];
|
|
1632
|
+
// async defer 脚本执行
|
|
1633
|
+
appScriptList.forEach((script) => {
|
|
1634
|
+
if ((script.defer || script.async)) {
|
|
1635
|
+
if (!script.code && script.defer) {
|
|
1636
|
+
deferScriptList.push(script.excuteCode(app));
|
|
1637
|
+
}
|
|
1638
|
+
else
|
|
1639
|
+
asyncScriptList.push(script.excuteCode(app));
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
await Promise.all([...asyncScriptList, ...deferScriptList])
|
|
1643
|
+
.catch((e) => {
|
|
1644
|
+
console.error(e);
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
class MicroAppModel {
|
|
1649
|
+
state = AppState.UNSET;
|
|
1650
|
+
isPreLoad = false;
|
|
1651
|
+
mode = 'app';
|
|
1652
|
+
name;
|
|
1653
|
+
url;
|
|
1654
|
+
container;
|
|
1655
|
+
showSourceCode;
|
|
1656
|
+
scopeCss;
|
|
1657
|
+
scopeJs;
|
|
1658
|
+
source;
|
|
1659
|
+
sandBox;
|
|
1660
|
+
scopeLocation;
|
|
1661
|
+
keepAlive;
|
|
1662
|
+
initSource;
|
|
1663
|
+
iframe = null;
|
|
1664
|
+
data;
|
|
1665
|
+
constructor(props) {
|
|
1666
|
+
this.name = props.id !== props.url ? props.id : random(5);
|
|
1667
|
+
this.mode = props.mode ?? 'app';
|
|
1668
|
+
this.container = props.container ?? undefined;
|
|
1669
|
+
this.showSourceCode = props.showSourceCode ?? false;
|
|
1670
|
+
this.url = props.url;
|
|
1671
|
+
this.data = props.data || {};
|
|
1672
|
+
this.scopeJs = props.scopeJs ?? true;
|
|
1673
|
+
this.scopeCss = props.scopeCss ?? true;
|
|
1674
|
+
this.scopeLocation = props.scopeLocation ?? false;
|
|
1675
|
+
this.isPreLoad = props.isPreLoad ?? false;
|
|
1676
|
+
this.keepAlive = props.keepAlive ?? false;
|
|
1677
|
+
this.initSource = props.initSource ?? [];
|
|
1678
|
+
if (this.scopeJs) {
|
|
1679
|
+
this.sandBox = new SandBox(this);
|
|
1680
|
+
}
|
|
1681
|
+
if (this.container instanceof HTMLElement) {
|
|
1682
|
+
this.container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
get appCacheKey() {
|
|
1686
|
+
return this.url;
|
|
1687
|
+
}
|
|
1688
|
+
get status() {
|
|
1689
|
+
return this.state;
|
|
1690
|
+
}
|
|
1691
|
+
set status(v) {
|
|
1692
|
+
this.state = v;
|
|
1693
|
+
}
|
|
1694
|
+
async start() {
|
|
1695
|
+
if (!this.source || [AppState.UNSET, AppState.ERROR].includes(this.status)) {
|
|
1696
|
+
this.state = AppState.LOADING;
|
|
1697
|
+
if (this.scopeLocation) {
|
|
1698
|
+
const iframe = await this.createIframe();
|
|
1699
|
+
this.iframe = iframe;
|
|
1700
|
+
}
|
|
1701
|
+
this.source = new EntrySource(this.url);
|
|
1702
|
+
await this.source.importEntery(this);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
onMount() {
|
|
1706
|
+
if (this.isPreLoad)
|
|
1707
|
+
return;
|
|
1708
|
+
this.state = AppState.LOADED;
|
|
1709
|
+
this.mount();
|
|
1710
|
+
}
|
|
1711
|
+
onError() {
|
|
1712
|
+
this.state = AppState.ERROR;
|
|
1713
|
+
}
|
|
1714
|
+
mount(container, callback) {
|
|
1715
|
+
this.isPreLoad = false;
|
|
1716
|
+
this.container = container ?? this.container;
|
|
1717
|
+
this.state = AppState.MOUNTING;
|
|
1718
|
+
if (this.container) {
|
|
1719
|
+
if (this.container instanceof Element)
|
|
1720
|
+
this.container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1721
|
+
const clonedNode = this.source.html.cloneNode(true);
|
|
1722
|
+
const fragment = document.createDocumentFragment();
|
|
1723
|
+
Array.from(clonedNode.childNodes).forEach((node) => {
|
|
1724
|
+
fragment.appendChild(node);
|
|
1725
|
+
});
|
|
1726
|
+
this.container.innerHTML = '';
|
|
1727
|
+
this.container.appendChild(fragment);
|
|
1728
|
+
this.sandBox?.activeated(this.data);
|
|
1729
|
+
execAppScripts(this).finally(() => {
|
|
1730
|
+
this.state = AppState.MOUNTED;
|
|
1731
|
+
callback?.(this);
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
activated(container, callback) {
|
|
1736
|
+
this.isPreLoad = false;
|
|
1737
|
+
this.state = AppState.ACTIVATED;
|
|
1738
|
+
if (container && this.container) {
|
|
1739
|
+
if (container instanceof Element)
|
|
1740
|
+
container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1741
|
+
const fragment = document.createDocumentFragment();
|
|
1742
|
+
Array.from(this.container.childNodes).forEach((node) => {
|
|
1743
|
+
fragment.appendChild(node);
|
|
1744
|
+
});
|
|
1745
|
+
container.innerHTML = '';
|
|
1746
|
+
container.appendChild(fragment);
|
|
1747
|
+
this.container = container;
|
|
1748
|
+
this.sandBox?.activeated(this.data);
|
|
1749
|
+
callback?.(this);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
deactivated() {
|
|
1753
|
+
this.state = AppState.DEACTIVATED;
|
|
1754
|
+
this.sandBox?.deactivated();
|
|
1755
|
+
}
|
|
1756
|
+
unmount(needDestroy = false) {
|
|
1757
|
+
this.state = AppState.UNMOUNT;
|
|
1758
|
+
this.sandBox?.deactivated();
|
|
1759
|
+
needDestroy && appCache.deleteApp(this.url);
|
|
1760
|
+
this.container.innerHTML = '';
|
|
1761
|
+
this.container = undefined;
|
|
1762
|
+
}
|
|
1763
|
+
createIframe() {
|
|
1764
|
+
return new Promise((resolve) => {
|
|
1765
|
+
const iframe = document.createElement('iframe');
|
|
1766
|
+
const url = new URL(this.url);
|
|
1767
|
+
iframe.setAttribute('src', `about:blank${url.pathname || '/'}${url.search}${url.hash}`);
|
|
1768
|
+
iframe.style.cssText = 'position: fixed; top: -9999px; width: 100%; height: 1px;';
|
|
1769
|
+
document.body.appendChild(iframe);
|
|
1770
|
+
setTimeout(() => resolve(iframe), 100);
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
registerRunningApp() {
|
|
1774
|
+
setCurrentRunningApp(this);
|
|
1775
|
+
Promise.resolve().then(() => setCurrentRunningApp(null));
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
class EntrySource {
|
|
1780
|
+
url;
|
|
1781
|
+
scripts;
|
|
1782
|
+
styles;
|
|
1783
|
+
html = null;
|
|
1784
|
+
rawHtml;
|
|
1785
|
+
constructor(url) {
|
|
1786
|
+
this.url = url;
|
|
1787
|
+
this.scripts = new Map();
|
|
1788
|
+
this.styles = new Map();
|
|
1789
|
+
}
|
|
1790
|
+
async importEntery(app) {
|
|
1791
|
+
let importEntry;
|
|
1792
|
+
if (app.initSource?.length) {
|
|
1793
|
+
// 初始化配置的公共source资源
|
|
1794
|
+
const { collectScript, collectStyle } = await collectSource(app.initSource);
|
|
1795
|
+
if (collectScript) {
|
|
1796
|
+
this.scripts = collectScript;
|
|
1797
|
+
}
|
|
1798
|
+
if (collectStyle) {
|
|
1799
|
+
this.styles = collectStyle;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
if (app instanceof MicroAppModel)
|
|
1803
|
+
importEntry = this.importHtmlEntry(app);
|
|
1804
|
+
else if (app instanceof MicroInstanceModel)
|
|
1805
|
+
importEntry = this.importInstanceEntry();
|
|
1806
|
+
importEntry && await importEntry;
|
|
1807
|
+
}
|
|
1808
|
+
async importHtmlEntry(app) {
|
|
1809
|
+
let htmlStr = appCache.getCacheHtml(this.url);
|
|
1810
|
+
if (!htmlStr) {
|
|
1811
|
+
htmlStr = await fetchSource(this.url, { cache: 'no-cache' });
|
|
1812
|
+
if (!htmlStr) {
|
|
1813
|
+
console.error('load app entry error, pleace check');
|
|
1814
|
+
return Promise.reject();
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
this.rawHtml = htmlStr;
|
|
1818
|
+
const wrapElement = createElement$1('div');
|
|
1819
|
+
wrapElement.innerHTML = htmlStr.replace(/<\/?head>/gim, '').replace(/<\/?body>/i, '');
|
|
1820
|
+
this.collectScriptAndStyle(wrapElement, app);
|
|
1821
|
+
await excuteAppStyles(app, wrapElement);
|
|
1822
|
+
this.html = wrapElement;
|
|
1823
|
+
}
|
|
1824
|
+
async importInstanceEntry() {
|
|
1825
|
+
let jsStr = appCache.getCacheScript(this.url)?.code;
|
|
1826
|
+
if (!jsStr) {
|
|
1827
|
+
jsStr = await fetchSource(this.url, { cache: 'no-cache' });
|
|
1828
|
+
}
|
|
1829
|
+
if (!jsStr) {
|
|
1830
|
+
console.error('load app entry error, pleace check');
|
|
1831
|
+
return Promise.reject();
|
|
1832
|
+
}
|
|
1833
|
+
this.scripts.set(this.url, new Script({
|
|
1834
|
+
code: jsStr,
|
|
1835
|
+
async: false,
|
|
1836
|
+
defer: false,
|
|
1837
|
+
url: this.url,
|
|
1838
|
+
isModule: false,
|
|
1839
|
+
fromHtml: true,
|
|
1840
|
+
}));
|
|
1841
|
+
}
|
|
1842
|
+
collectScriptAndStyle(parent, app) {
|
|
1843
|
+
const children = Array.from(parent.children);
|
|
1844
|
+
children.length
|
|
1845
|
+
&& children.forEach((child) => {
|
|
1846
|
+
this.collectScriptAndStyle(child, app);
|
|
1847
|
+
});
|
|
1848
|
+
for (const dom of children) {
|
|
1849
|
+
if (dom instanceof HTMLLinkElement) {
|
|
1850
|
+
this.collectLink(dom, parent);
|
|
1851
|
+
}
|
|
1852
|
+
else if (dom instanceof HTMLStyleElement) {
|
|
1853
|
+
if (dom.hasAttribute('exclude')) ;
|
|
1854
|
+
else if (!dom.hasAttribute('ignore')) {
|
|
1855
|
+
this.styles.set(randomUrl(), new Style({
|
|
1856
|
+
code: dom.textContent || '',
|
|
1857
|
+
url: '',
|
|
1858
|
+
fromHtml: true,
|
|
1859
|
+
}));
|
|
1860
|
+
dom.remove();
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
else if (dom instanceof HTMLScriptElement) {
|
|
1864
|
+
this.collectScript(dom, parent);
|
|
1865
|
+
}
|
|
1866
|
+
else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
|
|
1867
|
+
parent.removeChild(dom);
|
|
1868
|
+
}
|
|
1869
|
+
else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
|
|
1870
|
+
dom.setAttribute('src', fillUpPath(dom.getAttribute('src'), this.url));
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
collectLink(link, parent, needReplaceELement = false) {
|
|
1875
|
+
if (link.hasAttribute('exclude')) {
|
|
1876
|
+
return { replace: document.createComment('【bk-weweb】style with exclude attribute is ignored') };
|
|
1877
|
+
}
|
|
1878
|
+
if (link.hasAttribute('ignore')) {
|
|
1879
|
+
return { replace: link };
|
|
1880
|
+
}
|
|
1881
|
+
const rel = link.getAttribute('rel');
|
|
1882
|
+
let href = link.getAttribute('href');
|
|
1883
|
+
let replaceElement;
|
|
1884
|
+
if (rel === 'stylesheet' && href) {
|
|
1885
|
+
href = fillUpPath(href, this.url);
|
|
1886
|
+
replaceElement = document.createComment(`【bk-weweb】style with href=${href}`);
|
|
1887
|
+
let styleInstance = this.getStyle(href);
|
|
1888
|
+
if (!styleInstance) {
|
|
1889
|
+
styleInstance = new Style({
|
|
1890
|
+
code: '',
|
|
1891
|
+
url: href,
|
|
1892
|
+
prefetch: !!link.getAttribute('prefetch'),
|
|
1893
|
+
preload: !!link.getAttribute('preload'),
|
|
1894
|
+
fromHtml: !needReplaceELement,
|
|
1895
|
+
});
|
|
1896
|
+
this.styles.set(href, styleInstance);
|
|
1897
|
+
}
|
|
1898
|
+
!needReplaceELement && parent.replaceChild(replaceElement, link);
|
|
1899
|
+
return { replace: replaceElement, style: styleInstance };
|
|
1900
|
+
}
|
|
1901
|
+
if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
|
|
1902
|
+
// preload prefetch icon ....
|
|
1903
|
+
replaceElement = document.createComment(`【bk-weweb】style with rel=${rel}${href ? ` & href=${href}` : ''}`);
|
|
1904
|
+
!needReplaceELement && parent.removeChild(link);
|
|
1905
|
+
return { replace: replaceElement };
|
|
1906
|
+
}
|
|
1907
|
+
if (href) {
|
|
1908
|
+
// dns-prefetch preconnect modulepreload search ....
|
|
1909
|
+
link.setAttribute('href', fillUpPath(href, this.url));
|
|
1910
|
+
}
|
|
1911
|
+
return { replace: link };
|
|
1912
|
+
}
|
|
1913
|
+
collectScript(script, parent, needReplaceELement = false) {
|
|
1914
|
+
if (script.hasAttribute('ignore')
|
|
1915
|
+
|| (script.hasAttribute('type') && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type))
|
|
1916
|
+
|| (!isSupportModuleScript && script.type === 'module')) {
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
let replaceElement = null;
|
|
1920
|
+
if (script.hasAttribute('exclude')) {
|
|
1921
|
+
replaceElement = document.createComment('【bk-weweb】script element with exclude attribute is removed');
|
|
1922
|
+
!needReplaceELement && parent.replaceChild(replaceElement, script);
|
|
1923
|
+
return { replace: replaceElement };
|
|
1924
|
+
}
|
|
1925
|
+
let src = script.getAttribute('src');
|
|
1926
|
+
if (src) {
|
|
1927
|
+
src = fillUpPath(src, this.url);
|
|
1928
|
+
let scriptInstance = this.getScript(src);
|
|
1929
|
+
if (!scriptInstance) {
|
|
1930
|
+
scriptInstance = new Script({
|
|
1931
|
+
code: '',
|
|
1932
|
+
async: script.hasAttribute('async'),
|
|
1933
|
+
defer: script.defer || script.type === 'module',
|
|
1934
|
+
isModule: script.type === 'module',
|
|
1935
|
+
url: src,
|
|
1936
|
+
fromHtml: !needReplaceELement,
|
|
1937
|
+
});
|
|
1938
|
+
this.scripts.set(src, scriptInstance);
|
|
1939
|
+
}
|
|
1940
|
+
replaceElement = document.createComment(`【bk-weweb】script with src='${src}'`);
|
|
1941
|
+
!needReplaceELement && parent.replaceChild(replaceElement, script);
|
|
1942
|
+
return { replace: replaceElement, script: scriptInstance };
|
|
1943
|
+
}
|
|
1944
|
+
if (script.textContent) {
|
|
1945
|
+
const nonceStr = randomUrl();
|
|
1946
|
+
const scriptInstance = new Script({
|
|
1947
|
+
code: script.textContent,
|
|
1948
|
+
async: false,
|
|
1949
|
+
defer: script.type === 'module',
|
|
1950
|
+
isModule: script.type === 'module',
|
|
1951
|
+
url: nonceStr,
|
|
1952
|
+
fromHtml: !needReplaceELement,
|
|
1953
|
+
});
|
|
1954
|
+
this.scripts.set(nonceStr, scriptInstance);
|
|
1955
|
+
replaceElement = document.createComment('【bk-weweb】script with texcontent');
|
|
1956
|
+
!needReplaceELement && parent.replaceChild(replaceElement, script);
|
|
1957
|
+
return { replace: replaceElement, script: scriptInstance };
|
|
1958
|
+
}
|
|
1959
|
+
return { replace: script };
|
|
1960
|
+
}
|
|
1961
|
+
setScript(url, script) {
|
|
1962
|
+
this.scripts.set(url, new Script(script));
|
|
1963
|
+
}
|
|
1964
|
+
getScript(url) {
|
|
1965
|
+
return this.scripts.get(url);
|
|
1966
|
+
}
|
|
1967
|
+
setStyle(url, style) {
|
|
1968
|
+
this.styles.set(url, style);
|
|
1969
|
+
}
|
|
1970
|
+
getStyle(url) {
|
|
1971
|
+
return this.styles.get(url);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
// 所有应用缓存
|
|
1976
|
+
class AppCache {
|
|
1977
|
+
cache;
|
|
1978
|
+
// todo 主应用共享资源包
|
|
1979
|
+
baseSource;
|
|
1980
|
+
constructor() {
|
|
1981
|
+
this.cache = new Map();
|
|
1982
|
+
this.baseSource = new EntrySource(location.href);
|
|
1983
|
+
// this.baseApp = new
|
|
1984
|
+
}
|
|
1985
|
+
get hasActiveApp() {
|
|
1986
|
+
return Array.from(this.cache.values()).some((app) => app.status !== AppState.UNMOUNT);
|
|
1987
|
+
}
|
|
1988
|
+
setApp(app) {
|
|
1989
|
+
this.cache.set(app.appCacheKey, app);
|
|
1990
|
+
}
|
|
1991
|
+
getApp(name) {
|
|
1992
|
+
if (!name)
|
|
1993
|
+
return undefined;
|
|
1994
|
+
const app = this.cache.get(name);
|
|
1995
|
+
if (app)
|
|
1996
|
+
return app;
|
|
1997
|
+
return Array.from(this.cache.values()).find((item) => item.name === name);
|
|
1998
|
+
}
|
|
1999
|
+
getCacheHtml(url) {
|
|
2000
|
+
const list = Array.from(this.cache.values());
|
|
2001
|
+
const app = list.find(item => item.url === url);
|
|
2002
|
+
if (app)
|
|
2003
|
+
return app.source?.rawHtml || '';
|
|
2004
|
+
return '';
|
|
2005
|
+
}
|
|
2006
|
+
getCacheStyle(url) {
|
|
2007
|
+
let style = this.baseSource.getStyle(url);
|
|
2008
|
+
if (style)
|
|
2009
|
+
return;
|
|
2010
|
+
const list = Array.from(this.cache.values());
|
|
2011
|
+
list.some((app) => {
|
|
2012
|
+
style = app.source?.getStyle(url);
|
|
2013
|
+
return !!style;
|
|
2014
|
+
});
|
|
2015
|
+
return style;
|
|
2016
|
+
}
|
|
2017
|
+
getCacheScript(url) {
|
|
2018
|
+
let script = this.baseSource.getScript(url);
|
|
2019
|
+
if (script)
|
|
2020
|
+
return;
|
|
2021
|
+
const list = Array.from(this.cache.values());
|
|
2022
|
+
list.some((app) => {
|
|
2023
|
+
script = app.source?.getScript(url);
|
|
2024
|
+
return !!script;
|
|
2025
|
+
});
|
|
2026
|
+
return script;
|
|
2027
|
+
}
|
|
2028
|
+
deleteApp(url) {
|
|
2029
|
+
this.cache.delete(url);
|
|
2030
|
+
}
|
|
2031
|
+
setBaseAppStyle(url, style) {
|
|
2032
|
+
this.baseSource.setStyle(url, style);
|
|
2033
|
+
}
|
|
2034
|
+
setBaseAppScript(url, script) {
|
|
2035
|
+
this.baseSource.setScript(url, script);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
const appCache = new AppCache();
|
|
2039
|
+
// 注册全局获取缓存app 或者 instance
|
|
2040
|
+
window.__getAppOrInstance__ = function (id) {
|
|
2041
|
+
if (!id)
|
|
2042
|
+
return appCache;
|
|
2043
|
+
return appCache.getApp(id);
|
|
2044
|
+
};
|
|
2045
|
+
|
|
2046
|
+
function getStyleSource(url, style, originLink) {
|
|
2047
|
+
const replaceStyle = createElement$1('style');
|
|
2048
|
+
fetchSource(url)
|
|
2049
|
+
.then((data) => {
|
|
2050
|
+
style.code = data;
|
|
2051
|
+
appCache.setBaseAppStyle(url, style);
|
|
2052
|
+
replaceStyle.textContent = data;
|
|
2053
|
+
dispatchLinkOrScriptLoad(originLink);
|
|
2054
|
+
})
|
|
2055
|
+
.catch((err) => {
|
|
2056
|
+
console.error(err);
|
|
2057
|
+
dispatchLinkOrScriptError(originLink);
|
|
2058
|
+
});
|
|
2059
|
+
return replaceStyle;
|
|
2060
|
+
}
|
|
2061
|
+
function getScriptSource(url, script, originScript) {
|
|
2062
|
+
const replaceElement = createElement$1('script');
|
|
2063
|
+
fetchSource(url)
|
|
2064
|
+
.then((code) => {
|
|
2065
|
+
script.code = code;
|
|
2066
|
+
try {
|
|
2067
|
+
replaceElement.textContent = code;
|
|
2068
|
+
if (!url.startsWith('inline-')) {
|
|
2069
|
+
originScript.setAttribute('origin-src', url);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
catch (e) {
|
|
2073
|
+
console.error(e, url);
|
|
2074
|
+
}
|
|
2075
|
+
dispatchLinkOrScriptLoad(originScript);
|
|
2076
|
+
})
|
|
2077
|
+
.catch((err) => {
|
|
2078
|
+
console.error(err);
|
|
2079
|
+
dispatchLinkOrScriptError(originScript);
|
|
2080
|
+
});
|
|
2081
|
+
return replaceElement;
|
|
2082
|
+
}
|
|
2083
|
+
function rewriteNewNode(child) {
|
|
2084
|
+
if (child instanceof HTMLLinkElement) {
|
|
2085
|
+
const rel = child.getAttribute('rel');
|
|
2086
|
+
let href = child.getAttribute('href');
|
|
2087
|
+
if (rel === 'stylesheet' && href) {
|
|
2088
|
+
href = fillUpPath(href, location.href);
|
|
2089
|
+
const replaceStyle = createElement$1('style');
|
|
2090
|
+
const styleInstance = new Style({
|
|
2091
|
+
code: '',
|
|
2092
|
+
url: href,
|
|
2093
|
+
fromHtml: false,
|
|
2094
|
+
});
|
|
2095
|
+
getStyleSource(href, styleInstance, child);
|
|
2096
|
+
return replaceStyle;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
if (child instanceof HTMLScriptElement) {
|
|
2100
|
+
let src = child.getAttribute('src');
|
|
2101
|
+
if (src && child.type !== 'module') {
|
|
2102
|
+
src = fillUpPath(src, location.href);
|
|
2103
|
+
const script = new Script({
|
|
2104
|
+
code: '',
|
|
2105
|
+
async: child.hasAttribute('async'),
|
|
2106
|
+
defer: child.defer || child.type === 'module',
|
|
2107
|
+
isModule: false,
|
|
2108
|
+
fromHtml: false,
|
|
2109
|
+
});
|
|
2110
|
+
appCache.setBaseAppScript(src, script);
|
|
2111
|
+
const replaceElement = getScriptSource(src, script, child);
|
|
2112
|
+
return replaceElement || child;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return child;
|
|
2116
|
+
}
|
|
2117
|
+
function baseElementHandle(parent, newChild, passiveChild, rawMethod, baseSource) {
|
|
2118
|
+
if (baseSource) {
|
|
2119
|
+
const targetChild = rewriteNewNode(newChild);
|
|
2120
|
+
return rawMethod.call(parent, targetChild, passiveChild);
|
|
2121
|
+
}
|
|
2122
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
(function () {
|
|
2126
|
+
const rawBodyAppendChild = HTMLBodyElement.prototype.appendChild;
|
|
2127
|
+
const rawHeadAppendChild = HTMLHeadElement.prototype.appendChild;
|
|
2128
|
+
const rawHeadInsertBefore = HTMLHeadElement.prototype.appendChild;
|
|
2129
|
+
HTMLBodyElement.prototype.appendChild = function (newChild) {
|
|
2130
|
+
return baseElementHandle(this, newChild, null, rawBodyAppendChild, true);
|
|
2131
|
+
};
|
|
2132
|
+
HTMLHeadElement.prototype.appendChild = function (newChild) {
|
|
2133
|
+
return baseElementHandle(this, newChild, null, rawHeadAppendChild, true);
|
|
2134
|
+
};
|
|
2135
|
+
HTMLHeadElement.prototype.insertBefore = function (newChild, refChild) {
|
|
2136
|
+
return baseElementHandle(this, newChild, refChild, rawHeadInsertBefore, true);
|
|
2137
|
+
};
|
|
2138
|
+
}());
|
|
2139
|
+
//# sourceMappingURL=collect-source.js.map
|