single-file-core 1.3.14 → 1.3.16

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/core/helper.js CHANGED
@@ -82,6 +82,7 @@ const crypto = globalThis.crypto;
82
82
  const TextEncoder = globalThis.TextEncoder;
83
83
  const Blob = globalThis.Blob;
84
84
  const CustomEvent = globalThis.CustomEvent;
85
+ const MutationObserver = globalThis.MutationObserver;
85
86
 
86
87
  export {
87
88
  initUserScriptHandler,
@@ -135,6 +136,7 @@ function initUserScriptHandler() {
135
136
  await promiseResponse;
136
137
  }
137
138
  });
139
+ new MutationObserver(initUserScriptHandler).observe(globalThis.document, { childList: true });
138
140
  }
139
141
 
140
142
  function initDoc(doc) {
package/core/index.js CHANGED
@@ -473,13 +473,20 @@ class Processor {
473
473
  this.doc = util.parseDocContent(pageContent, this.baseURI);
474
474
  if (this.options.saveRawPage) {
475
475
  let charset;
476
- this.doc.querySelectorAll("meta[charset], meta[http-equiv=\"content-type\"]").forEach(element => {
477
- const charsetDeclaration = element.content.split(";")[1];
478
- if (charsetDeclaration && !charset) {
479
- charset = charsetDeclaration.split("=")[1].trim().toLowerCase();
476
+ this.doc.querySelectorAll("meta[charset]").forEach(element => {
477
+ if (!charset) {
478
+ charset = element.getAttribute("charset").trim().toLowerCase();
480
479
  }
481
480
  });
482
- if (charset && content.charset && charset.toLowerCase() != content.charset.toLowerCase()) {
481
+ if (!charset) {
482
+ this.doc.querySelectorAll("meta[http-equiv=\"content-type\"]").forEach(element => {
483
+ const charsetDeclaration = element.content.split(";")[1];
484
+ if (charsetDeclaration && !charset) {
485
+ charset = charsetDeclaration.split("=")[1].trim().toLowerCase();
486
+ }
487
+ });
488
+ }
489
+ if (charset && content.charset && charset != content.charset.toLowerCase()) {
483
490
  return this.loadPage(pageContent, charset);
484
491
  }
485
492
  }
@@ -857,7 +864,7 @@ class Processor {
857
864
  this.stats.set("discarded", "objects", objectElements.length);
858
865
  this.stats.set("processed", "objects", objectElements.length);
859
866
  objectElements.forEach(element => element.remove());
860
- const replacedAttributeValue = this.doc.querySelectorAll("link[rel~=preconnect], link[rel~=prerender], link[rel~=dns-prefetch], link[rel~=preload], link[rel~=manifest], link[rel~=prefetch]");
867
+ const replacedAttributeValue = this.doc.querySelectorAll("link[rel~=preconnect], link[rel~=prerender], link[rel~=dns-prefetch], link[rel~=preload], link[rel~=manifest], link[rel~=prefetch], link[rel~=modulepreload]");
861
868
  replacedAttributeValue.forEach(element => {
862
869
  const relValue = element
863
870
  .getAttribute("rel")
@@ -1223,6 +1230,7 @@ class Processor {
1223
1230
  options.includeInfobar = false;
1224
1231
  options.saveFilenameTemplateData = false;
1225
1232
  options.selected = false;
1233
+ options.embeddedImage = null;
1226
1234
  options.url = frameData.baseURI;
1227
1235
  options.windowId = frameWindowId;
1228
1236
  if (frameData.content) {
@@ -394,7 +394,7 @@ function getProcessorHelperClass(utilInstance) {
394
394
  }
395
395
 
396
396
  setMetaCSP(metaElement) {
397
- metaElement.content = "default-src 'none'; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline' data: blob:; frame-src 'self' data: blob:; media-src 'self' data: blob:; script-src 'self' 'unsafe-inline' data: blob:; object-src 'self' data: blob:;";
397
+ metaElement.content = "default-src 'none'; connect-src 'self'; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline' data: blob:; frame-src 'self' data: blob:; media-src 'self' data: blob:; script-src 'self' 'unsafe-inline' data: blob:; object-src 'self' data: blob:;";
398
398
  }
399
399
 
400
400
  removeUnusedStylesheets() {
@@ -26,7 +26,7 @@
26
26
  import { parse } from "./template-parser.js";
27
27
  import { getContentSize, digest } from "./../core/helper.js";
28
28
 
29
- const Blob = globalThis.Blob;
29
+ const Blob = globalThis.Blob;
30
30
  const FileReader = globalThis.FileReader;
31
31
  const URL = globalThis.URL;
32
32
  const URLSearchParams = globalThis.URLSearchParams;
@@ -16991,6 +16991,10 @@ async function evalTemplate(template = "", options, content, doc, dontReplaceSla
16991
16991
  "length": value => value.length,
16992
16992
  "url-search-name": (index = 0) => params[index] && params[index][0],
16993
16993
  "url-search-value": (index = 0) => params[index] && params[index][1],
16994
+ "url-search-named-value": name => {
16995
+ const param = params.find(param => param[0] == name);
16996
+ return (param && param[1]);
16997
+ },
16994
16998
  "url-search": name => {
16995
16999
  const param = params.find(param => param[0] == name);
16996
17000
  return (param && param[1]);
@@ -17030,6 +17034,9 @@ async function evalTemplate(template = "", options, content, doc, dontReplaceSla
17030
17034
  const fn = functions[name];
17031
17035
  if (fn) {
17032
17036
  argument = argument.replace(/\\\\(.)/g, "$1");
17037
+ if (!optionalArguments) {
17038
+ optionalArguments = [];
17039
+ }
17033
17040
  optionalArguments = optionalArguments
17034
17041
  .map(argument => argument.replace(/\\\\(.)/g, "$1"))
17035
17042
  .filter(argument => argument != undefined && argument != null && argument != "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.3.14",
3
+ "version": "1.3.16",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -31,15 +31,6 @@ async function display(document, docContent, { disableFramePointerEvents } = {})
31
31
  docContent = docContent.replace(/<noscript/gi, "<template disabled-noscript");
32
32
  docContent = docContent.replaceAll(/<\/noscript/gi, "</template");
33
33
  const doc = (new DOMParser()).parseFromString(docContent, "text/html");
34
- if (doc.doctype) {
35
- if (document.doctype) {
36
- document.replaceChild(doc.doctype, document.doctype);
37
- } else {
38
- document.insertBefore(doc.doctype, document.documentElement);
39
- }
40
- } else if (document.doctype) {
41
- document.doctype.remove();
42
- }
43
34
  if (disableFramePointerEvents) {
44
35
  doc.querySelectorAll("iframe").forEach(element => {
45
36
  const pointerEvents = "pointer-events";
@@ -47,7 +38,10 @@ async function display(document, docContent, { disableFramePointerEvents } = {})
47
38
  element.style.setProperty(pointerEvents, "none", "important");
48
39
  });
49
40
  }
50
- document.replaceChild(document.importNode(doc.documentElement, true), document.documentElement);
41
+ document.open();
42
+ document.write(getDoctypeString(doc));
43
+ document.write(doc.documentElement.outerHTML);
44
+ document.close();
51
45
  document.querySelectorAll("template[disabled-noscript]").forEach(element => {
52
46
  const noscriptElement = document.createElement("noscript");
53
47
  element.removeAttribute("disabled-noscript");
@@ -57,22 +51,22 @@ async function display(document, docContent, { disableFramePointerEvents } = {})
57
51
  });
58
52
  document.documentElement.setAttribute("data-sfz", "");
59
53
  document.querySelectorAll("link[rel*=icon]").forEach(element => element.parentElement.replaceChild(element, element));
60
- document.open = document.write = document.close = () => { };
61
- for (let element of Array.from(document.querySelectorAll("script"))) {
62
- await new Promise(resolve => {
63
- const scriptElement = document.createElement("script");
64
- Array.from(element.attributes).forEach(attribute => scriptElement.setAttribute(attribute.name, attribute.value));
65
- const async = element.getAttribute("async") == "" || element.getAttribute("async") == "async";
66
- if (element.textContent) {
67
- scriptElement.textContent = element.textContent;
68
- } else if (!async) {
69
- scriptElement.addEventListener("load", resolve);
70
- scriptElement.addEventListener("error", () => resolve());
71
- }
72
- element.parentElement.replaceChild(scriptElement, element);
73
- if (element.textContent || async) {
74
- resolve();
75
- }
76
- });
54
+
55
+ function getDoctypeString(doc) {
56
+ const docType = doc.doctype;
57
+ let docTypeString = "";
58
+ if (docType) {
59
+ docTypeString = "<!DOCTYPE " + docType.nodeName;
60
+ if (docType.publicId) {
61
+ docTypeString += " PUBLIC \"" + docType.publicId + "\"";
62
+ if (docType.systemId)
63
+ docTypeString += " \"" + docType.systemId + "\"";
64
+ } else if (docType.systemId)
65
+ docTypeString += " SYSTEM \"" + docType.systemId + "\"";
66
+ if (docType.internalSubset)
67
+ docTypeString += " [" + docType.internalSubset + "]";
68
+ docTypeString += "> ";
69
+ }
70
+ return docTypeString;
77
71
  }
78
- }
72
+ }
@@ -47,12 +47,30 @@ const EXTRA_DATA_TAGS = [
47
47
  ["<xmp>", "</xmp>"],
48
48
  ["<plaintext>", "</plaintext>"]
49
49
  ];
50
+ const EMBEDDED_IMAGE_DATA_TAGS = [
51
+ ["<!--", "-->"],
52
+ ...EXTRA_DATA_TAGS,
53
+ ];
50
54
  const EXTRA_DATA_REGEXPS = [
51
55
  [/<\/noscript>/i],
52
56
  [/<\/script>/i],
53
57
  [/<\/xmp>/i],
54
58
  [/<\/plaintext>/i]
55
59
  ];
60
+ const EMBEDDED_IMAGE_DATA_REGEXPS = [
61
+ /-->/i,
62
+ ...EXTRA_DATA_REGEXPS,
63
+ ];
64
+ const CRC32_TABLE = new Uint32Array(256).map((_, indexTable) => {
65
+ let crc = indexTable;
66
+ for (let indexBits = 0; indexBits < 8; indexBits++) {
67
+ crc = crc & 1 ? 0xEDB88320 ^ (crc >>> 1) : crc >>> 1;
68
+ }
69
+ return crc;
70
+ });
71
+ const PNG_IEND_LENGTH = 12;
72
+ const PNG_SIGNATURE_LENGTH = 8;
73
+ const PNG_IHDR_LENGTH = 25;
56
74
 
57
75
  const browser = globalThis.browser;
58
76
 
@@ -70,7 +88,29 @@ async function process(pageData, options, lastModDate = new Date()) {
70
88
  }
71
89
  const zipDataWriter = new Uint8ArrayWriter();
72
90
  zipDataWriter.init();
73
- let extraDataOffset, extraData;
91
+ zipDataWriter.writable.size = 0;
92
+ let extraDataOffset, extraData, embeddedImageDataOffset, endTag;
93
+ if (options.embeddedImage) {
94
+ const embeddedImageData = options.embeddedImage.slice(PNG_SIGNATURE_LENGTH + PNG_IHDR_LENGTH, options.embeddedImage.length - PNG_IEND_LENGTH);
95
+ await writeData(zipDataWriter.writable, options.embeddedImage.slice(0, PNG_SIGNATURE_LENGTH + PNG_IHDR_LENGTH));
96
+ if (options.selfExtractingArchive) {
97
+ const embeddedImageText = embeddedImageData.reduce((text, charCode) => text + String.fromCharCode(charCode), "");
98
+ const tagIndex = EMBEDDED_IMAGE_DATA_REGEXPS.findIndex(test => !embeddedImageText.match(test));
99
+ let startTag;
100
+ [startTag, endTag] = tagIndex == -1 ? ["", ""] : EMBEDDED_IMAGE_DATA_TAGS[tagIndex];
101
+ const html = getHTMLStartData(pageData, options) + startTag;
102
+ const hmtlData = new Uint8Array([...getLength(html.length + 4), ...new Uint8Array([0x74, 0x54, 0x58, 0x74, 0x50, 0x4e, 0x47, 0]), ...new TextEncoder().encode(html)]);
103
+ await writeData(zipDataWriter.writable, hmtlData);
104
+ await writeData(zipDataWriter.writable, getCRC32(hmtlData, 4));
105
+ }
106
+ await writeData(zipDataWriter.writable, embeddedImageData);
107
+ await writeData(zipDataWriter.writable, new Uint8Array(4));
108
+ embeddedImageDataOffset = zipDataWriter.offset;
109
+ await writeData(zipDataWriter.writable, new Uint8Array([0x74, 0x54, 0x58, 0x74, 0x5a, 0x49, 0x50, 0]));
110
+ if (options.selfExtractingArchive) {
111
+ await writeData(zipDataWriter.writable, new TextEncoder().encode(endTag));
112
+ }
113
+ }
74
114
  if (options.selfExtractingArchive) {
75
115
  extraDataOffset = await prependHTMLData(pageData, zipDataWriter, script, options);
76
116
  }
@@ -92,7 +132,7 @@ async function process(pageData, options, lastModDate = new Date()) {
92
132
  return findExtraDataTags(textContent, pageData, options, lastModDate);
93
133
  }
94
134
  }
95
- for (let index = 0; index < data.length; index++) {
135
+ for (let index = startOffset; index < data.length; index++) {
96
136
  if (data[index] == 13) {
97
137
  if (data[index + 1] == 10) {
98
138
  insertionsCRLF.push(index - startOffset);
@@ -110,14 +150,14 @@ async function process(pageData, options, lastModDate = new Date()) {
110
150
  pageContent += "-->";
111
151
  }
112
152
  }
113
- const endTags = options.preventAppendedData ? "" : "</body></html>";
153
+ const endTags = options.preventAppendedData || options.embeddedImage ? "" : "</body></html>";
114
154
  if (options.extractDataFromPage) {
115
155
  const payload = await Promise.all([
116
156
  arrayToBase64(insertionsCRLF),
117
157
  arrayToBase64(substitutionsLF)
118
158
  ]);
119
159
  extraData = "<sfz-extra-data>" + payload.join(",") + "</sfz-extra-data>";
120
- if (options.preventAppendedData || extraData.length > 65535 - endTags.length) {
160
+ if (options.preventAppendedData || extraData.length > 65535 - endTags.length - (options.embeddedImage ? PNG_IEND_LENGTH : 0)) {
121
161
  if (!options.extraDataSize) {
122
162
  options.extraDataSize = Math.floor(extraData.length * 1.001);
123
163
  return process(pageData, options, lastModDate);
@@ -132,9 +172,7 @@ async function process(pageData, options, lastModDate = new Date()) {
132
172
  }
133
173
  }
134
174
  pageContent += endTags;
135
- if (pageContent) {
136
- await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
137
- }
175
+ await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
138
176
  }
139
177
  await zipDataWriter.writable.close();
140
178
  const pageContent = await zipDataWriter.getData();
@@ -147,31 +185,49 @@ async function process(pageData, options, lastModDate = new Date()) {
147
185
  return process(pageData, options, lastModDate);
148
186
  }
149
187
  }
150
- return new Blob([pageContent], { type: "application/octet-stream" });
188
+ if (options.embeddedImage) {
189
+ pageContent.set(getLength(zipDataWriter.offset - embeddedImageDataOffset - 4), embeddedImageDataOffset - 4);
190
+ return new Blob([
191
+ pageContent,
192
+ getCRC32(pageContent, embeddedImageDataOffset),
193
+ options.embeddedImage.slice(options.embeddedImage.length - PNG_IEND_LENGTH)
194
+ ], { type: "application/octet-stream" });
195
+ } else {
196
+ return new Blob([pageContent], { type: "application/octet-stream" });
197
+ }
198
+ }
199
+
200
+ function getCRC32(data, indexData = 0) {
201
+ const crcArray = new Uint8Array(4);
202
+ let crc = -1;
203
+ for (; indexData < data.length; indexData++) {
204
+ crc = (crc >>> 8) ^ CRC32_TABLE[(crc ^ data[indexData]) & 0xff];
205
+ }
206
+ crc ^= -1;
207
+ setUint32(crcArray, crc);
208
+ return crcArray;
209
+ }
210
+
211
+ function getLength(length) {
212
+ const lengthArray = new Uint8Array(4);
213
+ setUint32(lengthArray, length);
214
+ return lengthArray;
215
+ }
216
+
217
+ function setUint32(data, value) {
218
+ data[0] = value >> 24;
219
+ data[1] = value >> 16;
220
+ data[2] = value >> 8;
221
+ data[3] = value;
151
222
  }
152
223
 
153
224
  async function prependHTMLData(pageData, zipDataWriter, script, options) {
154
225
  let pageContent = "";
155
- if (options.includeBOM && !options.extractDataFromPage) {
156
- pageContent += "\ufeff";
226
+ if (!options.embeddedImage) {
227
+ pageContent += getHTMLStartData(pageData, options);
157
228
  }
158
- const charset = options.extractDataFromPage ? "windows-1252" : "utf-8";
159
- const pageDataTitle = pageData.title.replace(/</g, "&lt;").replace(/>/g, "&gt;") || "";
160
- const title = options.extractDataFromPage ? "" : pageDataTitle;
161
- pageContent += pageData.doctype + "<html data-sfz><meta charset=" + charset + "><title>" + title + "</title>";
162
- if (options.insertCanonicalLink) {
163
- pageContent += "<link rel=canonical href=\"" + options.url + "\">";
164
- }
165
- if (options.insertMetaNoIndex) {
166
- pageContent += "<meta name=robots content=noindex>";
167
- }
168
- if (pageData.viewport) {
169
- pageContent += "<meta name=\"viewport\" content=" + JSON.stringify(pageData.viewport) + ">";
170
- }
171
- pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}}</style>";
172
- pageContent += "<body hidden>";
173
- pageContent += "<div id='sfz-wait-message'>Please wait...</div>";
174
- pageContent += "<div id='sfz-error-message'><strong>Error</strong>: Cannot open the page from the filesystem.";
229
+ pageContent += "<div id=sfz-wait-message>Please wait...</div>";
230
+ pageContent += "<div id=sfz-error-message><strong>Error</strong>: Cannot open the page from the filesystem.";
175
231
  pageContent += "<ul style='line-height:20px;'>";
176
232
  pageContent += "<li style='margin-bottom:10px'><strong>Chrome</strong>: Install <a href='https://chrome.google.com/webstore/detail/singlefile/mpiodijhokgodhhofbcjdecpffjipkle'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (chrome://extensions/?id=mpiodijhokgodhhofbcjdecpffjipkle).</li>";
177
233
  pageContent += "<li style='margin-bottom:10px'><strong>Microsoft Edge</strong>: Install <a href='https://microsoftedge.microsoft.com/addons/detail/singlefile/efnbkdcfmcmnhlkaijjjmhjjgladedno'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (edge://extensions/?id=efnbkdcfmcmnhlkaijjjmhjjgladedno).</li>";
@@ -181,7 +237,7 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
181
237
  doc.body.querySelectorAll("style, script, noscript").forEach(element => element.remove());
182
238
  let textBody = "";
183
239
  if (options.extractDataFromPage) {
184
- textBody += pageDataTitle + "\n\n";
240
+ textBody += getPageTitle(pageData) + "\n\n";
185
241
  }
186
242
  textBody += doc.body.innerText;
187
243
  doc.body.querySelectorAll("single-file-note").forEach(node => {
@@ -218,6 +274,32 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
218
274
  return extraDataOffset;
219
275
  }
220
276
 
277
+ function getHTMLStartData(pageData, options) {
278
+ let pageContent = "";
279
+ if (options.includeBOM && !options.extractDataFromPage && !options.embeddedImage) {
280
+ pageContent += "\ufeff";
281
+ }
282
+ const charset = options.extractDataFromPage ? "windows-1252" : "utf-8";
283
+ const title = options.extractDataFromPage ? "" : getPageTitle(pageData);
284
+ pageContent += (options.embeddedImage ? "" : pageData.doctype) + "<html data-sfz><meta charset=" + charset + "><title>" + title + "</title>";
285
+ if (options.insertCanonicalLink) {
286
+ pageContent += "<link rel=canonical href=\"" + options.url + "\">";
287
+ }
288
+ if (options.insertMetaNoIndex) {
289
+ pageContent += "<meta name=robots content=noindex>";
290
+ }
291
+ if (pageData.viewport) {
292
+ pageContent += "<meta name=viewport content=" + JSON.stringify(pageData.viewport) + ">";
293
+ }
294
+ pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}};body{color:transparent};div{color:initial}</style>";
295
+ pageContent += "<body hidden>";
296
+ return pageContent;
297
+ }
298
+
299
+ function getPageTitle(pageData) {
300
+ return pageData.title.replace(/</g, "&lt;").replace(/>/g, "&gt;") || "";
301
+ }
302
+
221
303
  function findExtraDataTags(textContent, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
222
304
  const matchEndTag = textContent.match(EXTRA_DATA_REGEXPS[indexExtractDataFromPageTags]);
223
305
  if (matchEndTag) {
@@ -244,7 +326,7 @@ async function arrayToBase64(data) {
244
326
  async function writeData(writable, array) {
245
327
  const streamWriter = writable.getWriter();
246
328
  await streamWriter.ready;
247
- writable.size = array.length;
329
+ writable.size += array.length;
248
330
  await streamWriter.write(array);
249
331
  streamWriter.releaseLock();
250
332
  }
@@ -300,6 +382,7 @@ async function getContent() {
300
382
  [8212, 151], [732, 152], [8482, 153], [353, 154], [8250, 155], [339, 156], [382, 158], [376, 159]
301
383
  ]);
302
384
  const xhr = new XMLHttpRequest();
385
+ document.body.querySelectorAll("meta, style").forEach(element => document.head.appendChild(element));
303
386
  xhr.responseType = "blob";
304
387
  xhr.open("GET", "");
305
388
  return new Promise((resolve, reject) => {
@@ -335,8 +418,10 @@ async function getContent() {
335
418
  if (zipDataElement) {
336
419
  let dataNode = zipDataElement.nextSibling;
337
420
  if (dataNode) {
338
- if (dataNode.nodeType == Node.TEXT_NODE) {
421
+ if (dataNode.nodeType == Node.TEXT_NODE && dataNode.nextSibling) {
339
422
  dataNode = dataNode.nextSibling;
423
+ } else {
424
+ dataNode = zipDataElement.previousSibling;
340
425
  }
341
426
  } else {
342
427
  dataNode = zipDataElement.previousSibling;
@@ -61,11 +61,11 @@ const WINDOW_ID_SEPARATOR = ".";
61
61
  const TOP_WINDOW = globalThis.window == globalThis.top;
62
62
 
63
63
  const browser = globalThis.browser;
64
- const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
65
64
  const top = globalThis.top;
66
65
  const MessageChannel = globalThis.MessageChannel;
67
66
  const document = globalThis.document;
68
67
  const JSON = globalThis.JSON;
68
+ const MutationObserver = globalThis.MutationObserver;
69
69
 
70
70
  let sessions = globalThis.sessions;
71
71
  if (!sessions) {
@@ -87,33 +87,8 @@ if (TOP_WINDOW) {
87
87
  });
88
88
  }
89
89
  }
90
- addEventListener("message", async event => {
91
- if (typeof event.data == "string" && event.data.startsWith(MESSAGE_PREFIX)) {
92
- event.preventDefault();
93
- event.stopPropagation();
94
- const message = JSON.parse(event.data.substring(MESSAGE_PREFIX.length));
95
- if (message.method == INIT_REQUEST_MESSAGE) {
96
- if (event.source) {
97
- sendMessage(event.source, { method: ACK_INIT_REQUEST_MESSAGE, windowId: message.windowId, sessionId: message.sessionId });
98
- }
99
- if (!TOP_WINDOW) {
100
- globalThis.stop();
101
- if (message.options.loadDeferredImages) {
102
- lazy.process(message.options);
103
- }
104
- await initRequestAsync(message);
105
- }
106
- } else if (message.method == ACK_INIT_REQUEST_MESSAGE) {
107
- clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
108
- createFrameResponseTimeout(message.sessionId, message.windowId);
109
- } else if (message.method == CLEANUP_REQUEST_MESSAGE) {
110
- cleanupRequest(message);
111
- } else if (message.method == INIT_RESPONSE_MESSAGE && sessions.get(message.sessionId)) {
112
- const port = event.ports[0];
113
- port.onmessage = event => initResponse(event.data);
114
- }
115
- }
116
- }, true);
90
+ init();
91
+ new MutationObserver(init).observe(document, { childList: true });
117
92
 
118
93
  export {
119
94
  getAsync,
@@ -123,6 +98,36 @@ export {
123
98
  TIMEOUT_INIT_REQUEST_MESSAGE
124
99
  };
125
100
 
101
+ function init() {
102
+ globalThis.addEventListener("message", async event => {
103
+ if (typeof event.data == "string" && event.data.startsWith(MESSAGE_PREFIX)) {
104
+ event.preventDefault();
105
+ event.stopPropagation();
106
+ const message = JSON.parse(event.data.substring(MESSAGE_PREFIX.length));
107
+ if (message.method == INIT_REQUEST_MESSAGE) {
108
+ if (event.source) {
109
+ sendMessage(event.source, { method: ACK_INIT_REQUEST_MESSAGE, windowId: message.windowId, sessionId: message.sessionId });
110
+ }
111
+ if (!TOP_WINDOW) {
112
+ globalThis.stop();
113
+ if (message.options.loadDeferredImages) {
114
+ lazy.process(message.options);
115
+ }
116
+ await initRequestAsync(message);
117
+ }
118
+ } else if (message.method == ACK_INIT_REQUEST_MESSAGE) {
119
+ clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
120
+ createFrameResponseTimeout(message.sessionId, message.windowId);
121
+ } else if (message.method == CLEANUP_REQUEST_MESSAGE) {
122
+ cleanupRequest(message);
123
+ } else if (message.method == INIT_RESPONSE_MESSAGE && sessions.get(message.sessionId)) {
124
+ const port = event.ports[0];
125
+ port.onmessage = event => initResponse(event.data);
126
+ }
127
+ }
128
+ }, true);
129
+ }
130
+
126
131
  function getAsync(options) {
127
132
  const sessionId = getNewSessionId();
128
133
  options = JSON.parse(JSON.stringify(options));
@@ -59,8 +59,6 @@
59
59
  featureSettings: "font-feature-settings"
60
60
  };
61
61
 
62
- const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
63
- const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
64
62
  const fetch = (url, options) => globalThis.fetch(url, options);
65
63
  const CustomEvent = globalThis.CustomEvent;
66
64
  const document = globalThis.document;
@@ -71,13 +69,83 @@
71
69
  const FileReader = globalThis.FileReader;
72
70
  const Blob = globalThis.Blob;
73
71
  const JSON = globalThis.JSON;
72
+ const MutationObserver = globalThis.MutationObserver;
74
73
 
75
74
  const observers = new Map();
76
75
  const observedElements = new Map();
77
76
 
78
77
  let dispatchScrollEvent;
79
- addEventListener(LOAD_DEFERRED_IMAGES_START_EVENT, () => loadDeferredImagesStart());
80
- addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT, () => loadDeferredImagesStart(true));
78
+
79
+ init();
80
+ new MutationObserver(init).observe(document, { childList: true });
81
+
82
+ function init() {
83
+ document.addEventListener(LOAD_DEFERRED_IMAGES_START_EVENT, () => loadDeferredImagesStart());
84
+ document.addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT, () => loadDeferredImagesStart(true));
85
+ document.addEventListener(LOAD_DEFERRED_IMAGES_END_EVENT, () => loadDeferredImagesEnd());
86
+ document.addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT, () => loadDeferredImagesEnd(true));
87
+ document.addEventListener(LOAD_DEFERRED_IMAGES_RESET_EVENT, resetScreenSize);
88
+ document.addEventListener(LOAD_DEFERRED_IMAGES_RESET_ZOOM_LEVEL_EVENT, () => {
89
+ const transform = document.documentElement.style.getPropertyValue("-sf-transform");
90
+ const transformPriority = document.documentElement.style.getPropertyPriority("-sf-transform");
91
+ const transformOrigin = document.documentElement.style.getPropertyValue("-sf-transform-origin");
92
+ const transformOriginPriority = document.documentElement.style.getPropertyPriority("-sf-transform-origin");
93
+ const minHeight = document.documentElement.style.getPropertyValue("-sf-min-height");
94
+ const minHeightPriority = document.documentElement.style.getPropertyPriority("-sf-min-height");
95
+ document.documentElement.style.setProperty("transform", transform, transformPriority);
96
+ document.documentElement.style.setProperty("transform-origin", transformOrigin, transformOriginPriority);
97
+ document.documentElement.style.setProperty("min-height", minHeight, minHeightPriority);
98
+ document.documentElement.style.removeProperty("-sf-transform");
99
+ document.documentElement.style.removeProperty("-sf-transform-origin");
100
+ document.documentElement.style.removeProperty("-sf-min-height");
101
+ resetScreenSize();
102
+ });
103
+ document.addEventListener(DISPATCH_SCROLL_START_EVENT, () => { dispatchScrollEvent = true; });
104
+ document.addEventListener(DISPATCH_SCROLL_END_EVENT, () => { dispatchScrollEvent = false; });
105
+ document.addEventListener(BLOCK_COOKIES_START_EVENT, () => {
106
+ try {
107
+ document.__defineGetter__("cookie", () => { throw new Error("document.cookie temporary blocked by SingleFile"); });
108
+ } catch (error) {
109
+ // ignored
110
+ }
111
+ });
112
+ document.addEventListener(BLOCK_COOKIES_END_EVENT, () => { delete document.cookie; });
113
+ document.addEventListener(BLOCK_STORAGE_START_EVENT, () => {
114
+ if (!globalThis._singleFile_localStorage) {
115
+ globalThis._singleFile_localStorage = globalThis.localStorage;
116
+ globalThis.__defineGetter__("localStorage", () => { throw new Error("localStorage temporary blocked by SingleFile"); });
117
+ }
118
+ if (!globalThis._singleFile_indexedDB) {
119
+ globalThis._singleFile_indexedDB = globalThis.indexedDB;
120
+ globalThis.__defineGetter__("indexedDB", () => { throw new Error("indexedDB temporary blocked by SingleFile"); });
121
+ }
122
+ });
123
+ document.addEventListener(BLOCK_STORAGE_END_EVENT, () => {
124
+ if (globalThis._singleFile_localStorage) {
125
+ delete globalThis.localStorage;
126
+ globalThis.localStorage = globalThis._singleFile_localStorage;
127
+ delete globalThis._singleFile_localStorage;
128
+ }
129
+ if (!globalThis._singleFile_indexedDB) {
130
+ delete globalThis.indexedDB;
131
+ globalThis.indexedDB = globalThis._singleFile_indexedDB;
132
+ delete globalThis._singleFile_indexedDB;
133
+ }
134
+ });
135
+ document.addEventListener(FETCH_REQUEST_EVENT, async event => {
136
+ document.dispatchEvent(new CustomEvent(FETCH_ACK_EVENT));
137
+ const { url, options } = JSON.parse(event.detail);
138
+ let detail;
139
+ try {
140
+ const response = await fetch(url, options);
141
+ detail = { url, response: await response.arrayBuffer(), headers: [...response.headers], status: response.status };
142
+ } catch (error) {
143
+ detail = { url, error: error && error.toString() };
144
+ }
145
+ document.dispatchEvent(new CustomEvent(FETCH_RESPONSE_EVENT, { detail }));
146
+ });
147
+ document.addEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener);
148
+ }
81
149
 
82
150
  function loadDeferredImagesStart(keepZoomLevel) {
83
151
  const scrollingElement = document.scrollingElement || document.documentElement;
@@ -121,11 +189,11 @@
121
189
  const result = new Image(...arguments);
122
190
  result.__defineSetter__("src", value => {
123
191
  image.src = value;
124
- dispatchEvent(new CustomEvent(LOAD_IMAGE_EVENT, { detail: image.src }));
192
+ document.dispatchEvent(new CustomEvent(LOAD_IMAGE_EVENT, { detail: image.src }));
125
193
  });
126
194
  result.__defineGetter__("src", () => image.src);
127
195
  result.__defineSetter__("srcset", value => {
128
- dispatchEvent(new CustomEvent(LOAD_IMAGE_EVENT));
196
+ document.dispatchEvent(new CustomEvent(LOAD_IMAGE_EVENT));
129
197
  image.srcset = value;
130
198
  });
131
199
  result.__defineGetter__("srcset", () => image.srcset);
@@ -137,7 +205,7 @@
137
205
  result.__defineGetter__("decode", () => () => image.decode());
138
206
  }
139
207
  image.onload = image.onloadend = image.onerror = event => {
140
- dispatchEvent(new CustomEvent(IMAGE_LOADED_EVENT, { detail: image.src }));
208
+ document.dispatchEvent(new CustomEvent(IMAGE_LOADED_EVENT, { detail: image.src }));
141
209
  result.dispatchEvent(new Event(event.type, event));
142
210
  };
143
211
  return result;
@@ -191,32 +259,13 @@
191
259
  const time = 0;
192
260
  return { target, intersectionRatio, boundingClientRect, intersectionRect: boundingClientRect, isIntersecting, rootBounds, time };
193
261
  });
194
- observer.callback(params, intersectionObserver);
262
+ observer.callback.call(intersectionObserver, params, intersectionObserver);
195
263
  }
196
264
  });
197
265
  }
198
266
  }
199
267
  }
200
268
 
201
- addEventListener(LOAD_DEFERRED_IMAGES_END_EVENT, () => loadDeferredImagesEnd());
202
- addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT, () => loadDeferredImagesEnd(true));
203
- addEventListener(LOAD_DEFERRED_IMAGES_RESET_EVENT, resetScreenSize);
204
- addEventListener(LOAD_DEFERRED_IMAGES_RESET_ZOOM_LEVEL_EVENT, () => {
205
- const transform = document.documentElement.style.getPropertyValue("-sf-transform");
206
- const transformPriority = document.documentElement.style.getPropertyPriority("-sf-transform");
207
- const transformOrigin = document.documentElement.style.getPropertyValue("-sf-transform-origin");
208
- const transformOriginPriority = document.documentElement.style.getPropertyPriority("-sf-transform-origin");
209
- const minHeight = document.documentElement.style.getPropertyValue("-sf-min-height");
210
- const minHeightPriority = document.documentElement.style.getPropertyPriority("-sf-min-height");
211
- document.documentElement.style.setProperty("transform", transform, transformPriority);
212
- document.documentElement.style.setProperty("transform-origin", transformOrigin, transformOriginPriority);
213
- document.documentElement.style.setProperty("min-height", minHeight, minHeightPriority);
214
- document.documentElement.style.removeProperty("-sf-transform");
215
- document.documentElement.style.removeProperty("-sf-transform-origin");
216
- document.documentElement.style.removeProperty("-sf-min-height");
217
- resetScreenSize();
218
- });
219
-
220
269
  function loadDeferredImagesEnd(keepZoomLevel) {
221
270
  document.querySelectorAll("[" + LAZY_LOAD_ATTRIBUTE + "]").forEach(element => {
222
271
  element.loading = "lazy";
@@ -256,82 +305,25 @@
256
305
  delete screen.width;
257
306
  }
258
307
 
259
- addEventListener(DISPATCH_SCROLL_START_EVENT, () => {
260
- dispatchScrollEvent = true;
261
- });
262
-
263
- addEventListener(DISPATCH_SCROLL_END_EVENT, () => {
264
- dispatchScrollEvent = false;
265
- });
266
-
267
- addEventListener(BLOCK_COOKIES_START_EVENT, () => {
268
- try {
269
- document.__defineGetter__("cookie", () => { throw new Error("document.cookie temporary blocked by SingleFile"); });
270
- } catch (error) {
271
- // ignored
272
- }
273
- });
274
-
275
- addEventListener(BLOCK_COOKIES_END_EVENT, () => {
276
- delete document.cookie;
277
- });
278
-
279
- addEventListener(BLOCK_STORAGE_START_EVENT, () => {
280
- if (!globalThis._singleFile_localStorage) {
281
- globalThis._singleFile_localStorage = globalThis.localStorage;
282
- globalThis.__defineGetter__("localStorage", () => { throw new Error("localStorage temporary blocked by SingleFile"); });
283
- }
284
- if (!globalThis._singleFile_indexedDB) {
285
- globalThis._singleFile_indexedDB = globalThis.indexedDB;
286
- globalThis.__defineGetter__("indexedDB", () => { throw new Error("indexedDB temporary blocked by SingleFile"); });
287
- }
288
- });
289
-
290
- addEventListener(BLOCK_STORAGE_END_EVENT, () => {
291
- if (globalThis._singleFile_localStorage) {
292
- delete globalThis.localStorage;
293
- globalThis.localStorage = globalThis._singleFile_localStorage;
294
- delete globalThis._singleFile_localStorage;
295
- }
296
- if (!globalThis._singleFile_indexedDB) {
297
- delete globalThis.indexedDB;
298
- globalThis.indexedDB = globalThis._singleFile_indexedDB;
299
- delete globalThis._singleFile_indexedDB;
300
- }
301
- });
302
-
303
- addEventListener(FETCH_REQUEST_EVENT, async event => {
304
- dispatchEvent(new CustomEvent(FETCH_ACK_EVENT));
305
- const { url, options } = JSON.parse(event.detail);
306
- let detail;
307
- try {
308
- const response = await fetch(url, options);
309
- detail = { url, response: await response.arrayBuffer(), headers: [...response.headers], status: response.status };
310
- } catch (error) {
311
- detail = { url, error: error && error.toString() };
312
- }
313
- dispatchEvent(new CustomEvent(FETCH_RESPONSE_EVENT, { detail }));
314
- });
315
308
 
316
- addEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener);
317
309
 
318
310
  if (globalThis.FontFace) {
319
311
  const FontFace = globalThis.FontFace;
320
312
  globalThis.FontFace = function () {
321
- getDetailObject(...arguments).then(detail => dispatchEvent(new CustomEvent(NEW_FONT_FACE_EVENT, { detail })));
313
+ getDetailObject(...arguments).then(detail => document.dispatchEvent(new CustomEvent(NEW_FONT_FACE_EVENT, { detail })));
322
314
  return new FontFace(...arguments);
323
315
  };
324
316
  globalThis.FontFace.prototype = FontFace.prototype;
325
317
  globalThis.FontFace.toString = function () { return "function FontFace() { [native code] }"; };
326
318
  const deleteFont = document.fonts.delete;
327
319
  document.fonts.delete = function (fontFace) {
328
- getDetailObject(fontFace.family).then(detail => dispatchEvent(new CustomEvent(DELETE_FONT_EVENT, { detail })));
320
+ getDetailObject(fontFace.family).then(detail => document.dispatchEvent(new CustomEvent(DELETE_FONT_EVENT, { detail })));
329
321
  return deleteFont.call(document.fonts, fontFace);
330
322
  };
331
323
  document.fonts.delete.toString = function () { return "function delete() { [native code] }"; };
332
324
  const clearFonts = document.fonts.clear;
333
325
  document.fonts.clear = function () {
334
- dispatchEvent(new CustomEvent(CLEAR_FONTS_EVENT));
326
+ document.dispatchEvent(new CustomEvent(CLEAR_FONTS_EVENT));
335
327
  return clearFonts.call(document.fonts);
336
328
  };
337
329
  document.fonts.clear.toString = function () { return "function clear() { [native code] }"; };
@@ -418,9 +410,9 @@
418
410
 
419
411
  function dispatchResizeEvent() {
420
412
  try {
421
- dispatchEvent(new UIEvent("resize"));
413
+ globalThis.dispatchEvent(new UIEvent("resize"));
422
414
  if (dispatchScrollEvent) {
423
- dispatchEvent(new UIEvent("scroll"));
415
+ globalThis.dispatchEvent(new UIEvent("scroll"));
424
416
  }
425
417
  } catch (error) {
426
418
  // ignored
@@ -42,12 +42,11 @@ const DELETE_FONT_EVENT = "single-file-delete-font";
42
42
  const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
43
43
  const FONT_FACE_PROPERTY_NAME = "_singleFile_fontFaces";
44
44
 
45
- const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
46
- const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
47
45
  const CustomEvent = globalThis.CustomEvent;
48
46
  const document = globalThis.document;
49
47
  const Document = globalThis.Document;
50
48
  const JSON = globalThis.JSON;
49
+ const MutationObserver = globalThis.MutationObserver;
51
50
 
52
51
  let fontFaces;
53
52
  if (window[FONT_FACE_PROPERTY_NAME]) {
@@ -56,20 +55,25 @@ if (window[FONT_FACE_PROPERTY_NAME]) {
56
55
  fontFaces = window[FONT_FACE_PROPERTY_NAME] = new Map();
57
56
  }
58
57
 
59
- if (document instanceof Document) {
60
- addEventListener(NEW_FONT_FACE_EVENT, event => {
61
- const detail = event.detail;
62
- const key = Object.assign({}, detail);
63
- delete key.src;
64
- fontFaces.set(JSON.stringify(key), detail);
65
- });
66
- addEventListener(DELETE_FONT_EVENT, event => {
67
- const detail = event.detail;
68
- const key = Object.assign({}, detail);
69
- delete key.src;
70
- fontFaces.delete(JSON.stringify(key));
71
- });
72
- addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
58
+ init();
59
+ new MutationObserver(init).observe(document, { childList: true });
60
+
61
+ function init() {
62
+ if (document instanceof Document) {
63
+ document.addEventListener(NEW_FONT_FACE_EVENT, event => {
64
+ const detail = event.detail;
65
+ const key = Object.assign({}, detail);
66
+ delete key.src;
67
+ fontFaces.set(JSON.stringify(key), detail);
68
+ });
69
+ document.addEventListener(DELETE_FONT_EVENT, event => {
70
+ const detail = event.detail;
71
+ const key = Object.assign({}, detail);
72
+ delete key.src;
73
+ fontFaces.delete(JSON.stringify(key));
74
+ });
75
+ document.addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
76
+ }
73
77
  }
74
78
 
75
79
  export {
@@ -87,42 +91,42 @@ function getFontsData() {
87
91
 
88
92
  function loadDeferredImagesStart(options) {
89
93
  if (options.loadDeferredImagesBlockCookies) {
90
- dispatchEvent(new CustomEvent(BLOCK_COOKIES_START_EVENT));
94
+ document.dispatchEvent(new CustomEvent(BLOCK_COOKIES_START_EVENT));
91
95
  }
92
96
  if (options.loadDeferredImagesBlockStorage) {
93
- dispatchEvent(new CustomEvent(BLOCK_STORAGE_START_EVENT));
97
+ document.dispatchEvent(new CustomEvent(BLOCK_STORAGE_START_EVENT));
94
98
  }
95
99
  if (options.loadDeferredImagesDispatchScrollEvent) {
96
- dispatchEvent(new CustomEvent(DISPATCH_SCROLL_START_EVENT));
100
+ document.dispatchEvent(new CustomEvent(DISPATCH_SCROLL_START_EVENT));
97
101
  }
98
102
  if (options.loadDeferredImagesKeepZoomLevel) {
99
- dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT));
103
+ document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT));
100
104
  } else {
101
- dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_START_EVENT));
105
+ document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_START_EVENT));
102
106
  }
103
107
  }
104
108
 
105
109
  function loadDeferredImagesEnd(options) {
106
110
  if (options.loadDeferredImagesBlockCookies) {
107
- dispatchEvent(new CustomEvent(BLOCK_COOKIES_END_EVENT));
111
+ document.dispatchEvent(new CustomEvent(BLOCK_COOKIES_END_EVENT));
108
112
  }
109
113
  if (options.loadDeferredImagesBlockStorage) {
110
- dispatchEvent(new CustomEvent(BLOCK_STORAGE_END_EVENT));
114
+ document.dispatchEvent(new CustomEvent(BLOCK_STORAGE_END_EVENT));
111
115
  }
112
116
  if (options.loadDeferredImagesDispatchScrollEvent) {
113
- dispatchEvent(new CustomEvent(DISPATCH_SCROLL_END_EVENT));
117
+ document.dispatchEvent(new CustomEvent(DISPATCH_SCROLL_END_EVENT));
114
118
  }
115
119
  if (options.loadDeferredImagesKeepZoomLevel) {
116
- dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT));
120
+ document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT));
117
121
  } else {
118
- dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_END_EVENT));
122
+ document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_END_EVENT));
119
123
  }
120
124
  }
121
125
 
122
126
  function loadDeferredImagesResetZoomLevel(options) {
123
127
  if (options.loadDeferredImagesKeepZoomLevel) {
124
- dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_RESET_ZOOM_LEVEL_EVENT));
128
+ document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_RESET_ZOOM_LEVEL_EVENT));
125
129
  } else {
126
- dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_RESET_EVENT));
130
+ document.dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_RESET_EVENT));
127
131
  }
128
132
  }
@@ -39,8 +39,6 @@ const ATTRIBUTES_MUTATION_TYPE = "attributes";
39
39
  const browser = globalThis.browser;
40
40
  const document = globalThis.document;
41
41
  const MutationObserver = globalThis.MutationObserver;
42
- const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
43
- const removeEventListener = (type, listener, options) => globalThis.removeEventListener(type, listener, options);
44
42
  const timeouts = new Map();
45
43
 
46
44
  let idleTimeoutCalls;
@@ -115,8 +113,8 @@ function triggerLazyLoading(options) {
115
113
  await setIdleTimeout(options.loadDeferredImagesMaxIdleTime * 2);
116
114
  await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
117
115
  observer.observe(document, { subtree: true, childList: true, attributes: true });
118
- addEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
119
- addEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
116
+ document.addEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
117
+ document.addEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
120
118
  hooksFrames.loadDeferredImagesStart(options);
121
119
 
122
120
  async function setIdleTimeout(delay) {
@@ -159,8 +157,8 @@ function triggerLazyLoading(options) {
159
157
 
160
158
  function cleanupAndResolve(value) {
161
159
  observer.disconnect();
162
- removeEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
163
- removeEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
160
+ document.removeEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
161
+ document.removeEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
164
162
  resolve(value);
165
163
  }
166
164
  });
@@ -28,19 +28,24 @@ import { appendInfobar, refreshInfobarInfo, extractInfobarData } from "./core/in
28
28
  (globalThis => {
29
29
 
30
30
  const browser = globalThis.browser;
31
+ const MutationObserver = globalThis.MutationObserver;
32
+ init();
31
33
 
32
- if (globalThis.window == globalThis.top) {
33
- if (document.readyState == "loading") {
34
- document.addEventListener("DOMContentLoaded", displayIcon, false);
35
- } else {
36
- displayIcon();
34
+ function init() {
35
+ if (globalThis.window == globalThis.top) {
36
+ if (document.readyState == "loading") {
37
+ document.addEventListener("DOMContentLoaded", displayIcon, false);
38
+ } else {
39
+ displayIcon();
40
+ }
41
+ document.addEventListener("single-file-display-infobar", displayIcon, false);
42
+ new MutationObserver(init).observe(document, { childList: true });
43
+ }
44
+ if (globalThis.singlefile) {
45
+ globalThis.singlefile.infobar = {
46
+ displayIcon
47
+ };
37
48
  }
38
- document.addEventListener("single-file-display-infobar", displayIcon, false);
39
- }
40
- if (globalThis.singlefile) {
41
- globalThis.singlefile.infobar = {
42
- displayIcon
43
- };
44
49
  }
45
50
 
46
51
  async function displayIcon() {
package/single-file.js CHANGED
@@ -110,6 +110,7 @@ async function getPageData(options = {}, initOptions, doc = globalThis.document,
110
110
  url: options.url,
111
111
  createRootDirectory: options.createRootDirectory,
112
112
  selfExtractingArchive: options.selfExtractingArchive,
113
+ insertEmbeddedImage: options.insertEmbeddedImage,
113
114
  extractDataFromPage: options.extractDataFromPage,
114
115
  preventAppendedData: options.preventAppendedData,
115
116
  insertCanonicalLink: options.insertCanonicalLink,