single-file-core 1.5.94 → 1.5.96

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
@@ -91,6 +91,8 @@ const CustomEvent = globalThis.CustomEvent;
91
91
  const MutationObserver = globalThis.MutationObserver;
92
92
  const URL = globalThis.URL;
93
93
  const DOMParser = globalThis.DOMParser;
94
+ const Uint8Array = globalThis.Uint8Array;
95
+ const btoa = globalThis.btoa;
94
96
 
95
97
  export {
96
98
  initUserScriptHandler,
@@ -105,6 +107,7 @@ export {
105
107
  getShadowRoot,
106
108
  appendInfobar,
107
109
  getContentSize,
110
+ getDataURI,
108
111
  digest,
109
112
  getValidFilename,
110
113
  parseDocContent,
@@ -297,7 +300,10 @@ function markInvalidNesting(doc) {
297
300
 
298
301
  function addTrackIds(element, index = 0, parentTrackId = "") {
299
302
  const trackId = parentTrackId ? `${parentTrackId}.${index + 1}` : `${index + 1}`;
300
- element.setAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME, trackId);
303
+ const tagName = element.tagName.toUpperCase();
304
+ if (!(parentTrackId && (tagName == "BODY" || tagName == "HEAD" || tagName == "HTML"))) {
305
+ element.setAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME, trackId);
306
+ }
301
307
  Array.from(element.children).forEach((child, indexChild) => addTrackIds(child, indexChild, trackId));
302
308
  }
303
309
 
@@ -338,7 +344,7 @@ function fixInvalidNesting(document, NESTING_TRACK_ID_ATTRIBUTE_NAME, preventCle
338
344
  if (idParts.length > 1) {
339
345
  const parentId = idParts.slice(0, -1).join(".");
340
346
  const expectedParent = trackIds[parentId];
341
- if (expectedParent && element.parentElement !== expectedParent) {
347
+ if (expectedParent && element.parentElement !== expectedParent && !element.contains(expectedParent)) {
342
348
  expectedParent.appendChild(element);
343
349
  }
344
350
  }
@@ -793,6 +799,15 @@ function getContentSize(content) {
793
799
  return new Blob([content]).size;
794
800
  }
795
801
 
802
+ async function getDataURI(blob) {
803
+ const bytes = new Uint8Array(await blob.arrayBuffer());
804
+ let content = "";
805
+ for (let offset = 0; offset < bytes.length; offset += 8192) {
806
+ content += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
807
+ }
808
+ return "data:" + (blob.type || "application/octet-stream") + ";base64," + btoa(content);
809
+ }
810
+
796
811
  async function digest(algo, text) {
797
812
  try {
798
813
  const data = new TextEncoder("utf-8").encode(text);
@@ -23,7 +23,8 @@
23
23
 
24
24
  import {
25
25
  normalizeFontFamily,
26
- getFontWeight
26
+ getFontWeight,
27
+ getDataURI
27
28
  } from "./../helper.js";
28
29
 
29
30
  const DATA_URI_PREFIX = "data:";
@@ -60,7 +61,6 @@ const FONT_STRETCHES = {
60
61
  "ultra-expanded": "200%"
61
62
  };
62
63
  const Blob = globalThis.Blob;
63
- const FileReader = globalThis.FileReader;
64
64
  const Image = globalThis.Image;
65
65
  const OffscreenCanvas = globalThis.OffscreenCanvas;
66
66
 
@@ -710,10 +710,5 @@ async function resizeImage(doc, dataURI, { imageReductionFactor }) {
710
710
 
711
711
  function toDataURI(content, contentType, charset) {
712
712
  const blob = content instanceof Blob ? content : new Blob([content], { type: (contentType || "") + (charset ? ";charset=" + charset : "") });
713
- return new Promise((resolve, reject) => {
714
- const reader = new FileReader();
715
- reader.onload = () => resolve(reader.result);
716
- reader.onerror = () => reject(new Error(reader.error));
717
- reader.readAsDataURL(blob);
718
- });
713
+ return getDataURI(blob);
719
714
  }
package/core/util.js CHANGED
@@ -66,7 +66,6 @@ const CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";
66
66
  const URL = globalThis.URL;
67
67
  const DOMParser = globalThis.DOMParser;
68
68
  const Blob = globalThis.Blob;
69
- const FileReader = globalThis.FileReader;
70
69
  const fetch = (url, options) => {
71
70
  options.cache = "force-cache";
72
71
  options.referrerPolicy = "strict-origin-when-cross-origin";
@@ -351,12 +350,7 @@ async function getFetchResponse(resourceURL, options, data, charset, contentType
351
350
  if (data) {
352
351
  if (options.asBinary) {
353
352
  if (options.inline) {
354
- const reader = new FileReader();
355
- reader.readAsDataURL(new Blob([data], { type: contentType + (options.charset ? ";charset=" + options.charset : "") }));
356
- data = await new Promise((resolve, reject) => {
357
- reader.addEventListener("load", () => resolve(reader.result), false);
358
- reader.addEventListener("error", reject, false);
359
- });
353
+ data = await helper.getDataURI(new Blob([data], { type: contentType + (options.charset ? ";charset=" + options.charset : "") }));
360
354
  } else {
361
355
  data = new Uint8Array(data);
362
356
  }
@@ -4,7 +4,6 @@ import { build } from "esbuild";
4
4
 
5
5
  const require = createRequire(import.meta.url);
6
6
  const packagePath = require.resolve("css-tree/package.json");
7
- const tokenStreamPath = packagePath.replace(/package\.json$/, "lib/tokenizer/TokenStream.js");
8
7
  const licensePath = packagePath.replace(/package\.json$/, "LICENSE");
9
8
  const { version } = JSON.parse(readFileSync(packagePath));
10
9
 
package/eslint.config.mjs CHANGED
@@ -1,6 +1,12 @@
1
1
  import js from "@eslint/js";
2
2
 
3
3
  export default [
4
+ {
5
+ ignores: [
6
+ "vendor/**",
7
+ "zip-build/lib/**"
8
+ ]
9
+ },
4
10
  js.configs.recommended,
5
11
  {
6
12
  languageOptions: {
@@ -27,5 +33,22 @@ export default [
27
33
  "warn"
28
34
  ]
29
35
  }
36
+ },
37
+ {
38
+ files: ["test/sfz-harness/**"],
39
+ languageOptions: {
40
+ globals: {
41
+ Deno: "readonly",
42
+ setTimeout: "readonly",
43
+ Blob: "readonly",
44
+ TextDecoder: "readonly",
45
+ TextEncoder: "readonly",
46
+ URL: "readonly",
47
+ performance: "readonly"
48
+ }
49
+ },
50
+ rules: {
51
+ "no-console": "off"
52
+ }
30
53
  }
31
54
  ];
@@ -27,7 +27,6 @@ import { parse } from "./template-parser.js";
27
27
  import { getContentSize, digest, getValidFilename } from "./../core/helper.js";
28
28
 
29
29
  const Blob = globalThis.Blob;
30
- const FileReader = globalThis.FileReader;
31
30
  const URL = globalThis.URL;
32
31
  const Intl = globalThis.Intl;
33
32
  const URLSearchParams = globalThis.URLSearchParams;
@@ -17187,6 +17186,10 @@ async function evalTemplate(template = "", options, content, doc, context = {})
17187
17186
  variables[prefix + "minutes-utc"] = { getter: () => String(date.getUTCMinutes()).padStart(2, "0") };
17188
17187
  variables[prefix + "seconds-utc"] = { getter: () => String(date.getUTCSeconds()).padStart(2, "0") };
17189
17188
  variables[prefix + "time-ms"] = { getter: () => String(date.getTime()) };
17189
+ variables[prefix + "weekday-locale"] = { getter: () => date.toLocaleDateString(undefined, { weekday: "long" }) };
17190
+ variables[prefix + "weekday-short-locale"] = { getter: () => date.toLocaleDateString(undefined, { weekday: "short" }) };
17191
+ variables[prefix + "weekday-utc"] = { getter: () => date.toLocaleDateString(undefined, { weekday: "long", timeZone: "UTC" }) };
17192
+ variables[prefix + "weekday-short-utc"] = { getter: () => date.toLocaleDateString(undefined, { weekday: "short", timeZone: "UTC" }) };
17190
17193
  }
17191
17194
  }
17192
17195
 
@@ -17259,26 +17262,14 @@ function getLastSegment(url, replacementCharacter) {
17259
17262
  return lastSegment;
17260
17263
  }
17261
17264
 
17262
- function truncateText(content, maxSize) {
17265
+ async function truncateText(content, maxSize) {
17263
17266
  const blob = new Blob([content]);
17264
- const reader = new FileReader();
17265
- reader.readAsText(blob.slice(0, maxSize));
17266
- return new Promise((resolve, reject) => {
17267
- reader.addEventListener(
17268
- "load",
17269
- () => {
17270
- if (content.startsWith(reader.result)) {
17271
- resolve(reader.result);
17272
- } else {
17273
- truncateText(content, maxSize - 1)
17274
- .then(resolve)
17275
- .catch(reject);
17276
- }
17277
- },
17278
- false
17279
- );
17280
- reader.addEventListener("error", reject, false);
17281
- });
17267
+ const result = await blob.slice(0, maxSize).text();
17268
+ if (content.startsWith(result)) {
17269
+ return result;
17270
+ } else {
17271
+ return truncateText(content, maxSize - 1);
17272
+ }
17282
17273
  }
17283
17274
 
17284
17275
  function getFilenameExtension(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.5.94",
3
+ "version": "1.5.96",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -20,6 +20,7 @@
20
20
  },
21
21
  "homepage": "https://github.com/gildas-lormeau/single-file-core#readme",
22
22
  "devDependencies": {
23
- "eslint": "^9.39.1"
23
+ "@eslint/js": "^9.39.5",
24
+ "eslint": "^10.9.1"
24
25
  }
25
26
  }
@@ -21,7 +21,7 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global zip, Blob, FileReader, URL */
24
+ /* global zip, Blob, btoa, URL */
25
25
 
26
26
  export {
27
27
  extract
@@ -220,12 +220,13 @@ async function extract(content, { password, prompt = () => { }, zipOptions = { u
220
220
  }
221
221
 
222
222
  async function getDataURI(textContent, mimeType) {
223
- const reader = new FileReader();
224
- reader.readAsDataURL(new Blob([textContent], { type: mimeType }));
225
- return new Promise((resolve, reject) => {
226
- reader.onload = () => resolve(reader.result.replace(CHARSET_UTF8, ""));
227
- reader.onerror = reject;
228
- });
223
+ const blob = new Blob([textContent], { type: mimeType });
224
+ const bytes = new Uint8Array(await blob.arrayBuffer());
225
+ let content = "";
226
+ for (let offset = 0; offset < bytes.length; offset += 8192) {
227
+ content += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
228
+ }
229
+ return ("data:" + (blob.type || "application/octet-stream") + ";base64," + btoa(content)).replace(CHARSET_UTF8, "");
229
230
  }
230
231
 
231
232
  function replaceAll(string, search, replacement) {
@@ -69,6 +69,7 @@ async function router(content, { extract, display }) {
69
69
  const manifest = JSON.parse(await pagesEntry.getData(new zip.TextWriter()));
70
70
  const tocEntry = entries.find(entry => entry.filename == TOC_FILENAME);
71
71
  const { pages } = manifest;
72
+ const pageTransitions = manifest.pageTransitions || "auto";
72
73
  const aliases = new Map(Object.entries(manifest.aliases || {}));
73
74
  pages.forEach(page => {
74
75
  urlToPath.set(stripFragment(page.url), page.path);
@@ -141,7 +142,7 @@ async function router(content, { extract, display }) {
141
142
  const willRender = routed && path != currentPath && isRenderablePath(path);
142
143
  // the whole render and scroll sequence runs inside the view transition
143
144
  // so that the crossfade ends on the final scroll position
144
- if (willRender && document.startViewTransition && !prefersReducedMotion()) {
145
+ if (willRender && document.startViewTransition && pageTransitionEnabled() && !prefersReducedMotion()) {
145
146
  await document.startViewTransition(update).updateCallbackDone;
146
147
  } else {
147
148
  await update();
@@ -324,6 +325,30 @@ async function router(content, { extract, display }) {
324
325
  return node;
325
326
  }
326
327
 
328
+ // "auto" approximates the cross-document opt-in of live sites: the transition
329
+ // runs when the displayed page itself contains an @view-transition rule set
330
+ // to navigation: auto, so pages without transitions stay instant
331
+ function pageTransitionEnabled() {
332
+ if (pageTransitions == "fade") {
333
+ return true;
334
+ }
335
+ if (pageTransitions == "none") {
336
+ return false;
337
+ }
338
+ return Array.from(document.styleSheets).some(styleSheet => {
339
+ try {
340
+ return containsViewTransitionRule(styleSheet.cssRules);
341
+ } catch {
342
+ return false;
343
+ }
344
+ });
345
+ }
346
+
347
+ function containsViewTransitionRule(cssRules) {
348
+ return Array.from(cssRules).some(cssRule => cssRule.navigation == "auto" ||
349
+ (cssRule.cssRules && cssRule.cssRules.length && containsViewTransitionRule(cssRule.cssRules)));
350
+ }
351
+
327
352
  function prefersReducedMotion() {
328
353
  return Boolean(globalThis.matchMedia && globalThis.matchMedia("(prefers-reduced-motion: reduce)").matches);
329
354
  }
@@ -606,6 +606,7 @@ async function getContent() {
606
606
  displayMessage("sfz-wait-message", 2);
607
607
  resolve(pageData);
608
608
  } catch (error) {
609
+ // eslint-disable-next-line no-console
609
610
  console.error(error);
610
611
  displayMessage("sfz-error-message", 2);
611
612
  reject(error);
@@ -397,8 +397,8 @@ function sendMessage(targetWindow, message, useChannel) {
397
397
  }
398
398
  }
399
399
 
400
- function getFrameData(document, globalThis, windowId, options, scrolling) {
401
- const docData = helper.preProcessDoc(document, globalThis, options);
400
+ function getFrameData(document, win, windowId, options, scrolling) {
401
+ const docData = helper.preProcessDoc(document, win, options);
402
402
  const content = helper.serialize(document);
403
403
  helper.postProcessDoc(document, docData.markedElements, docData.invalidElements);
404
404
  const baseURI = document.baseURI.split("#")[0];
@@ -21,7 +21,7 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- (globalThis => {
24
+ (() => {
25
25
 
26
26
  const LOAD_DEFERRED_IMAGES_START_EVENT = "single-file-load-deferred-images-start";
27
27
  const LOAD_DEFERRED_IMAGES_END_EVENT = "single-file-load-deferred-images-end";
@@ -71,8 +71,8 @@
71
71
  const Element = globalThis.Element;
72
72
  const UIEvent = globalThis.UIEvent;
73
73
  const Event = globalThis.Event;
74
- const FileReader = globalThis.FileReader;
75
- const Blob = globalThis.Blob;
74
+ const Uint8Array = globalThis.Uint8Array;
75
+ const btoa = globalThis.btoa;
76
76
  const JSON = globalThis.JSON;
77
77
  const MutationObserver = globalThis.MutationObserver;
78
78
  const URL = globalThis.URL;
@@ -88,83 +88,126 @@
88
88
  new MutationObserver(init).observe(document, { childList: true });
89
89
 
90
90
  function init() {
91
- document.addEventListener(LOAD_DEFERRED_IMAGES_START_EVENT, () => loadDeferredImagesStart());
92
- document.addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT, () => loadDeferredImagesStart(true));
93
- document.addEventListener(LOAD_DEFERRED_IMAGES_END_EVENT, () => loadDeferredImagesEnd());
94
- document.addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT, () => loadDeferredImagesEnd(true));
91
+ document.addEventListener(LOAD_DEFERRED_IMAGES_START_EVENT, onLoadDeferredImagesStart);
92
+ document.addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT, onLoadDeferredImagesKeepZoomLevelStart);
93
+ document.addEventListener(LOAD_DEFERRED_IMAGES_END_EVENT, onLoadDeferredImagesEnd);
94
+ document.addEventListener(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT, onLoadDeferredImagesKeepZoomLevelEnd);
95
95
  document.addEventListener(LOAD_DEFERRED_IMAGES_RESET_EVENT, resetScreenSize);
96
- document.addEventListener(LOAD_DEFERRED_IMAGES_RESET_ZOOM_LEVEL_EVENT, () => {
97
- const transform = document.documentElement.style.getPropertyValue("-sf-transform");
98
- const transformPriority = document.documentElement.style.getPropertyPriority("-sf-transform");
99
- const transformOrigin = document.documentElement.style.getPropertyValue("-sf-transform-origin");
100
- const transformOriginPriority = document.documentElement.style.getPropertyPriority("-sf-transform-origin");
101
- const minHeight = document.documentElement.style.getPropertyValue("-sf-min-height");
102
- const minHeightPriority = document.documentElement.style.getPropertyPriority("-sf-min-height");
103
- document.documentElement.style.setProperty("transform", transform, transformPriority);
104
- document.documentElement.style.setProperty("transform-origin", transformOrigin, transformOriginPriority);
105
- document.documentElement.style.setProperty("min-height", minHeight, minHeightPriority);
106
- document.documentElement.style.removeProperty("-sf-transform");
107
- document.documentElement.style.removeProperty("-sf-transform-origin");
108
- document.documentElement.style.removeProperty("-sf-min-height");
109
- resetScreenSize();
110
- });
111
- document.addEventListener(DISPATCH_SCROLL_START_EVENT, () => { dispatchScrollEvent = true; });
112
- document.addEventListener(DISPATCH_SCROLL_END_EVENT, () => { dispatchScrollEvent = false; });
113
- document.addEventListener(BLOCK_COOKIES_START_EVENT, () => {
114
- try {
115
- document.__defineGetter__("cookie", () => { throw new Error("document.cookie temporary blocked by SingleFile"); });
116
- // eslint-disable-next-line no-unused-vars
117
- } catch (error) {
118
- // ignored
119
- }
120
- });
121
- document.addEventListener(BLOCK_COOKIES_END_EVENT, () => { delete document.cookie; });
122
- document.addEventListener(BLOCK_STORAGE_START_EVENT, () => {
123
- if (!globalThis._singleFile_localStorage) {
124
- globalThis._singleFile_localStorage = globalThis.localStorage;
125
- globalThis.__defineGetter__("localStorage", () => { throw new Error("localStorage temporary blocked by SingleFile"); });
126
- }
127
- if (!globalThis._singleFile_indexedDB) {
128
- globalThis._singleFile_indexedDB = globalThis.indexedDB;
129
- globalThis.__defineGetter__("indexedDB", () => { throw new Error("indexedDB temporary blocked by SingleFile"); });
130
- }
131
- });
132
- document.addEventListener(BLOCK_STORAGE_END_EVENT, () => {
133
- if (globalThis._singleFile_localStorage) {
134
- delete globalThis.localStorage;
135
- globalThis.localStorage = globalThis._singleFile_localStorage;
136
- delete globalThis._singleFile_localStorage;
137
- }
138
- if (!globalThis._singleFile_indexedDB) {
139
- delete globalThis.indexedDB;
140
- globalThis.indexedDB = globalThis._singleFile_indexedDB;
141
- delete globalThis._singleFile_indexedDB;
142
- }
143
- });
144
- document.addEventListener(FETCH_SUPPORTED_REQUEST_EVENT, () =>
145
- document.dispatchEvent(new CustomEvent(FETCH_SUPPORTED_RESPONSE_EVENT)));
146
- document.addEventListener(FETCH_REQUEST_EVENT, async event => {
147
- const { url, options } = JSON.parse(event.detail);
148
- let detail;
149
- try {
150
- const response = await fetch(url, options);
151
- detail = { url, response: await response.arrayBuffer(), headers: [...response.headers], status: response.status };
152
- } catch (error) {
153
- detail = { url, error: error && (error.message || error.toString()) };
154
- }
155
- document.dispatchEvent(new CustomEvent(FETCH_RESPONSE_EVENT, { detail }));
156
- });
96
+ document.addEventListener(LOAD_DEFERRED_IMAGES_RESET_ZOOM_LEVEL_EVENT, onLoadDeferredImagesResetZoomLevel);
97
+ document.addEventListener(DISPATCH_SCROLL_START_EVENT, onDispatchScrollStart);
98
+ document.addEventListener(DISPATCH_SCROLL_END_EVENT, onDispatchScrollEnd);
99
+ document.addEventListener(BLOCK_COOKIES_START_EVENT, onBlockCookiesStart);
100
+ document.addEventListener(BLOCK_COOKIES_END_EVENT, onBlockCookiesEnd);
101
+ document.addEventListener(BLOCK_STORAGE_START_EVENT, onBlockStorageStart);
102
+ document.addEventListener(BLOCK_STORAGE_END_EVENT, onBlockStorageEnd);
103
+ document.addEventListener(FETCH_SUPPORTED_REQUEST_EVENT, onFetchSupportedRequest);
104
+ document.addEventListener(FETCH_REQUEST_EVENT, onFetchRequest);
157
105
  document.addEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener);
158
- document.addEventListener(BOOTSTRAP_EVENT, event => {
159
- try {
160
- if (globalThis.bootstrap && event.detail.data) {
161
- globalThis.bootstrap(event.detail.data);
162
- }
163
- // eslint-disable-next-line no-unused-vars
164
- } catch (error) {
165
- // ignored
106
+ document.addEventListener(BOOTSTRAP_EVENT, onBootstrap);
107
+ }
108
+
109
+ function onLoadDeferredImagesStart() {
110
+ loadDeferredImagesStart();
111
+ }
112
+
113
+ function onLoadDeferredImagesKeepZoomLevelStart() {
114
+ loadDeferredImagesStart(true);
115
+ }
116
+
117
+ function onLoadDeferredImagesEnd() {
118
+ loadDeferredImagesEnd();
119
+ }
120
+
121
+ function onLoadDeferredImagesKeepZoomLevelEnd() {
122
+ loadDeferredImagesEnd(true);
123
+ }
124
+
125
+ function onLoadDeferredImagesResetZoomLevel() {
126
+ const transform = document.documentElement.style.getPropertyValue("-sf-transform");
127
+ const transformPriority = document.documentElement.style.getPropertyPriority("-sf-transform");
128
+ const transformOrigin = document.documentElement.style.getPropertyValue("-sf-transform-origin");
129
+ const transformOriginPriority = document.documentElement.style.getPropertyPriority("-sf-transform-origin");
130
+ const minHeight = document.documentElement.style.getPropertyValue("-sf-min-height");
131
+ const minHeightPriority = document.documentElement.style.getPropertyPriority("-sf-min-height");
132
+ document.documentElement.style.setProperty("transform", transform, transformPriority);
133
+ document.documentElement.style.setProperty("transform-origin", transformOrigin, transformOriginPriority);
134
+ document.documentElement.style.setProperty("min-height", minHeight, minHeightPriority);
135
+ document.documentElement.style.removeProperty("-sf-transform");
136
+ document.documentElement.style.removeProperty("-sf-transform-origin");
137
+ document.documentElement.style.removeProperty("-sf-min-height");
138
+ resetScreenSize();
139
+ }
140
+
141
+ function onDispatchScrollStart() {
142
+ dispatchScrollEvent = true;
143
+ }
144
+
145
+ function onDispatchScrollEnd() {
146
+ dispatchScrollEvent = false;
147
+ }
148
+
149
+ function onBlockCookiesStart() {
150
+ try {
151
+ document.__defineGetter__("cookie", () => { throw new Error("document.cookie temporary blocked by SingleFile"); });
152
+ // eslint-disable-next-line no-unused-vars
153
+ } catch (error) {
154
+ // ignored
155
+ }
156
+ }
157
+
158
+ function onBlockCookiesEnd() {
159
+ delete document.cookie;
160
+ }
161
+
162
+ function onBlockStorageStart() {
163
+ if (!globalThis._singleFile_localStorage) {
164
+ globalThis._singleFile_localStorage = globalThis.localStorage;
165
+ globalThis.__defineGetter__("localStorage", () => { throw new Error("localStorage temporary blocked by SingleFile"); });
166
+ }
167
+ if (!globalThis._singleFile_indexedDB) {
168
+ globalThis._singleFile_indexedDB = globalThis.indexedDB;
169
+ globalThis.__defineGetter__("indexedDB", () => { throw new Error("indexedDB temporary blocked by SingleFile"); });
170
+ }
171
+ }
172
+
173
+ function onBlockStorageEnd() {
174
+ if (globalThis._singleFile_localStorage) {
175
+ delete globalThis.localStorage;
176
+ globalThis.localStorage = globalThis._singleFile_localStorage;
177
+ delete globalThis._singleFile_localStorage;
178
+ }
179
+ if (!globalThis._singleFile_indexedDB) {
180
+ delete globalThis.indexedDB;
181
+ globalThis.indexedDB = globalThis._singleFile_indexedDB;
182
+ delete globalThis._singleFile_indexedDB;
183
+ }
184
+ }
185
+
186
+ function onFetchSupportedRequest() {
187
+ document.dispatchEvent(new CustomEvent(FETCH_SUPPORTED_RESPONSE_EVENT));
188
+ }
189
+
190
+ async function onFetchRequest(event) {
191
+ const { url, options } = JSON.parse(event.detail);
192
+ let detail;
193
+ try {
194
+ const response = await fetch(url, options);
195
+ detail = { url, response: await response.arrayBuffer(), headers: [...response.headers], status: response.status };
196
+ } catch (error) {
197
+ detail = { url, error: error && (error.message || error.toString()) };
198
+ }
199
+ document.dispatchEvent(new CustomEvent(FETCH_RESPONSE_EVENT, { detail }));
200
+ }
201
+
202
+ function onBootstrap(event) {
203
+ try {
204
+ if (globalThis.bootstrap && event.detail.data) {
205
+ globalThis.bootstrap(event.detail.data);
166
206
  }
167
- });
207
+ // eslint-disable-next-line no-unused-vars
208
+ } catch (error) {
209
+ // ignored
210
+ }
168
211
  }
169
212
 
170
213
  function loadDeferredImagesStart(keepZoomLevel) {
@@ -521,18 +564,15 @@
521
564
  }
522
565
  });
523
566
  }
524
- return new Promise(resolve => {
525
- if (detail.src instanceof ArrayBuffer) {
526
- const reader = new FileReader();
527
- reader.readAsDataURL(new Blob([detail.src]));
528
- reader.addEventListener("load", () => {
529
- detail.src = "url(" + reader.result + ")";
530
- resolve(detail);
531
- });
532
- } else {
533
- resolve(detail);
567
+ if (detail.src instanceof ArrayBuffer) {
568
+ const bytes = new Uint8Array(detail.src);
569
+ let content = "";
570
+ for (let offset = 0; offset < bytes.length; offset += 8192) {
571
+ content += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
534
572
  }
535
- });
573
+ detail.src = "url(data:application/octet-stream;base64," + btoa(content) + ")";
574
+ }
575
+ return detail;
536
576
  }
537
577
 
538
578
  function dispatchResizeEvent() {
@@ -547,4 +587,4 @@
547
587
  }
548
588
  }
549
589
 
550
- })(typeof globalThis == "object" ? globalThis : globalThis.window);
590
+ })();
@@ -65,26 +65,36 @@ new MutationObserver(init).observe(document, { childList: true });
65
65
 
66
66
  function init() {
67
67
  if (document instanceof Document) {
68
- document.addEventListener(NEW_FONT_FACE_EVENT, event => {
69
- const detail = event.detail;
70
- const key = Object.assign({}, detail);
71
- delete key.src;
72
- fontFaces.set(JSON.stringify(key), detail);
73
- });
74
- document.addEventListener(DELETE_FONT_EVENT, event => {
75
- const detail = event.detail;
76
- const key = Object.assign({}, detail);
77
- delete key.src;
78
- fontFaces.delete(JSON.stringify(key));
79
- });
80
- document.addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
81
- document.addEventListener(NEW_WORKLET_EVENT, event => {
82
- const detail = event.detail;
83
- worklets.set(detail.moduleURL, detail);
84
- });
68
+ document.addEventListener(NEW_FONT_FACE_EVENT, onNewFontFace);
69
+ document.addEventListener(DELETE_FONT_EVENT, onDeleteFont);
70
+ document.addEventListener(CLEAR_FONTS_EVENT, onClearFonts);
71
+ document.addEventListener(NEW_WORKLET_EVENT, onNewWorklet);
85
72
  }
86
73
  }
87
74
 
75
+ function onNewFontFace(event) {
76
+ const detail = event.detail;
77
+ const key = Object.assign({}, detail);
78
+ delete key.src;
79
+ fontFaces.set(JSON.stringify(key), detail);
80
+ }
81
+
82
+ function onDeleteFont(event) {
83
+ const detail = event.detail;
84
+ const key = Object.assign({}, detail);
85
+ delete key.src;
86
+ fontFaces.delete(JSON.stringify(key));
87
+ }
88
+
89
+ function onClearFonts() {
90
+ fontFaces.clear();
91
+ }
92
+
93
+ function onNewWorklet(event) {
94
+ const detail = event.detail;
95
+ worklets.set(detail.moduleURL, detail);
96
+ }
97
+
88
98
  export {
89
99
  getFontsData,
90
100
  getWorkletsData,
@@ -21,11 +21,11 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global window, document */
24
+ /* global document */
25
25
 
26
26
  import { appendInfobar, refreshInfobarInfo, extractInfobarData } from "./core/infobar.js";
27
27
 
28
- (globalThis => {
28
+ (() => {
29
29
 
30
30
  const browser = globalThis.browser;
31
31
  const MutationObserver = globalThis.MutationObserver;
@@ -36,7 +36,8 @@ import { appendInfobar, refreshInfobarInfo, extractInfobarData } from "./core/in
36
36
  if (globalThis.window == globalThis.top) {
37
37
  document.addEventListener("single-file-display-infobar", displayIcon, false);
38
38
  if (document.documentElement.getAttribute("data-sfz") == "" && !mutationObserver) {
39
- mutationObserver = new MutationObserver(init).observe(document, { childList: true });
39
+ mutationObserver = new MutationObserver(init);
40
+ mutationObserver.observe(document, { childList: true });
40
41
  } else {
41
42
  if (document.readyState == "loading") {
42
43
  document.addEventListener("DOMContentLoaded", displayIcon, false);
@@ -77,4 +78,4 @@ import { appendInfobar, refreshInfobarInfo, extractInfobarData } from "./core/in
77
78
  }
78
79
  }
79
80
 
80
- })(typeof globalThis == "object" ? globalThis : window);
81
+ })();