single-file-core 1.0.0
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/.eslintrc.js +43 -0
- package/LICENSE +661 -0
- package/index.js +86 -0
- package/modules/css-fonts-alt-minifier.js +340 -0
- package/modules/css-fonts-minifier.js +371 -0
- package/modules/css-matched-rules.js +350 -0
- package/modules/css-medias-alt-minifier.js +91 -0
- package/modules/css-rules-minifier.js +146 -0
- package/modules/html-images-alt-minifier.js +109 -0
- package/modules/html-minifier.js +262 -0
- package/modules/html-serializer.js +180 -0
- package/modules/index.js +42 -0
- package/package.json +18 -0
- package/processors/frame-tree/content/content-frame-tree.js +418 -0
- package/processors/hooks/content/content-hooks-frames-web.js +404 -0
- package/processors/hooks/content/content-hooks-frames.js +214 -0
- package/processors/hooks/content/content-hooks-web.js +48 -0
- package/processors/hooks/content/content-hooks.js +39 -0
- package/processors/index.js +34 -0
- package/processors/lazy/content/content-lazy-loader.js +229 -0
- package/single-file-bootstrap.js +33 -0
- package/single-file-core.js +2471 -0
- package/single-file-frames.js +1 -0
- package/single-file-helper.js +557 -0
- package/single-file-util.js +412 -0
- package/vendor/css-font-property-parser.js +343 -0
- package/vendor/css-media-query-parser.js +474 -0
- package/vendor/css-minifier.js +789 -0
- package/vendor/css-tree.js +35 -0
- package/vendor/css-unescape.js +72 -0
- package/vendor/html-srcset-parser.js +339 -0
- package/vendor/index.js +40 -0
- package/vendor/mime-type-parser.js +446 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
+
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
+
*
|
|
5
|
+
* This file is part of SingleFile.
|
|
6
|
+
*
|
|
7
|
+
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
+
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
+
* of the License, or (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
+
* General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
+
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
+
* AGPL normally required by section 4, provided you include this license
|
|
20
|
+
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
+
* Source.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* global globalThis */
|
|
25
|
+
|
|
26
|
+
const browser = globalThis.browser;
|
|
27
|
+
const document = globalThis.document;
|
|
28
|
+
const Document = globalThis.Document;
|
|
29
|
+
|
|
30
|
+
if (document instanceof Document) {
|
|
31
|
+
const scriptElement = document.createElement("script");
|
|
32
|
+
scriptElement.async = false;
|
|
33
|
+
if (browser && browser.runtime && browser.runtime.getURL) {
|
|
34
|
+
scriptElement.src = browser.runtime.getURL("/lib/web/hooks/hooks-web.js");
|
|
35
|
+
scriptElement.async = false;
|
|
36
|
+
}
|
|
37
|
+
(document.documentElement || document).appendChild(scriptElement);
|
|
38
|
+
scriptElement.remove();
|
|
39
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
+
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
+
*
|
|
5
|
+
* This file is part of SingleFile.
|
|
6
|
+
*
|
|
7
|
+
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
+
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
+
* of the License, or (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
+
* General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
+
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
+
* AGPL normally required by section 4, provided you include this license
|
|
20
|
+
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
+
* Source.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as frameTree from "./frame-tree/content/content-frame-tree.js";
|
|
25
|
+
import * as hooks from "./hooks/content/content-hooks.js";
|
|
26
|
+
import * as hooksFrames from "./hooks/content/content-hooks-frames.js";
|
|
27
|
+
import * as lazy from "./lazy/content/content-lazy-loader.js";
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
frameTree,
|
|
31
|
+
hooks,
|
|
32
|
+
hooksFrames,
|
|
33
|
+
lazy
|
|
34
|
+
};
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2020 Gildas Lormeau
|
|
3
|
+
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
+
*
|
|
5
|
+
* This file is part of SingleFile.
|
|
6
|
+
*
|
|
7
|
+
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
+
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
+
* of the License, or (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
+
* General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
+
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
+
* AGPL normally required by section 4, provided you include this license
|
|
20
|
+
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
+
* Source.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* global globalThis */
|
|
25
|
+
|
|
26
|
+
import * as hooksFrames from "./../../hooks/content/content-hooks-frames";
|
|
27
|
+
import {
|
|
28
|
+
LAZY_SRC_ATTRIBUTE_NAME,
|
|
29
|
+
SINGLE_FILE_UI_ELEMENT_CLASS
|
|
30
|
+
} from "./../../../single-file-helper.js";
|
|
31
|
+
const helper = {
|
|
32
|
+
LAZY_SRC_ATTRIBUTE_NAME,
|
|
33
|
+
SINGLE_FILE_UI_ELEMENT_CLASS
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const MAX_IDLE_TIMEOUT_CALLS = 10;
|
|
37
|
+
const ATTRIBUTES_MUTATION_TYPE = "attributes";
|
|
38
|
+
|
|
39
|
+
const browser = globalThis.browser;
|
|
40
|
+
const document = globalThis.document;
|
|
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
|
+
const timeouts = new Map();
|
|
45
|
+
|
|
46
|
+
let idleTimeoutCalls;
|
|
47
|
+
|
|
48
|
+
if (browser && browser.runtime && browser.runtime.onMessage && browser.runtime.onMessage.addListener) {
|
|
49
|
+
browser.runtime.onMessage.addListener(message => {
|
|
50
|
+
if (message.method == "singlefile.lazyTimeout.onTimeout") {
|
|
51
|
+
const timeoutData = timeouts.get(message.type);
|
|
52
|
+
if (timeoutData) {
|
|
53
|
+
timeouts.delete(message.type);
|
|
54
|
+
try {
|
|
55
|
+
timeoutData.callback();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
clearRegularTimeout(message.type);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
process,
|
|
66
|
+
resetZoomLevel
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
async function process(options) {
|
|
70
|
+
if (document.documentElement) {
|
|
71
|
+
timeouts.clear();
|
|
72
|
+
const maxScrollY = Math.max(document.documentElement.scrollHeight - (document.documentElement.clientHeight * 1.5), 0);
|
|
73
|
+
const maxScrollX = Math.max(document.documentElement.scrollWidth - (document.documentElement.clientWidth * 1.5), 0);
|
|
74
|
+
if (globalThis.scrollY <= maxScrollY && globalThis.scrollX <= maxScrollX) {
|
|
75
|
+
return triggerLazyLoading(options);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resetZoomLevel(options) {
|
|
81
|
+
hooksFrames.loadDeferredImagesResetZoomLevel(options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function triggerLazyLoading(options) {
|
|
85
|
+
idleTimeoutCalls = 0;
|
|
86
|
+
return new Promise(async resolve => { // eslint-disable-line no-async-promise-executor
|
|
87
|
+
let loadingImages;
|
|
88
|
+
const pendingImages = new Set();
|
|
89
|
+
const observer = new MutationObserver(async mutations => {
|
|
90
|
+
mutations = mutations.filter(mutation => mutation.type == ATTRIBUTES_MUTATION_TYPE);
|
|
91
|
+
if (mutations.length) {
|
|
92
|
+
const updated = mutations.filter(mutation => {
|
|
93
|
+
if (mutation.attributeName == "src") {
|
|
94
|
+
mutation.target.setAttribute(helper.LAZY_SRC_ATTRIBUTE_NAME, mutation.target.src);
|
|
95
|
+
mutation.target.addEventListener("load", onResourceLoad);
|
|
96
|
+
}
|
|
97
|
+
if (mutation.attributeName == "src" || mutation.attributeName == "srcset" || mutation.target.tagName == "SOURCE") {
|
|
98
|
+
return !mutation.target.classList || !mutation.target.classList.contains(helper.SINGLE_FILE_UI_ELEMENT_CLASS);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
if (updated.length) {
|
|
102
|
+
loadingImages = true;
|
|
103
|
+
await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
104
|
+
if (!pendingImages.size) {
|
|
105
|
+
await deferLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
await setIdleTimeout(options.loadDeferredImagesMaxIdleTime * 2);
|
|
111
|
+
await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
112
|
+
observer.observe(document, { subtree: true, childList: true, attributes: true });
|
|
113
|
+
addEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
|
|
114
|
+
addEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
|
|
115
|
+
hooksFrames.loadDeferredImagesStart(options);
|
|
116
|
+
|
|
117
|
+
async function setIdleTimeout(delay) {
|
|
118
|
+
await setAsyncTimeout("idleTimeout", async () => {
|
|
119
|
+
if (!loadingImages) {
|
|
120
|
+
clearAsyncTimeout("loadTimeout");
|
|
121
|
+
clearAsyncTimeout("maxTimeout");
|
|
122
|
+
lazyLoadEnd(observer, options, cleanupAndResolve);
|
|
123
|
+
} else if (idleTimeoutCalls < MAX_IDLE_TIMEOUT_CALLS) {
|
|
124
|
+
idleTimeoutCalls++;
|
|
125
|
+
clearAsyncTimeout("idleTimeout");
|
|
126
|
+
await setIdleTimeout(Math.max(500, delay / 2));
|
|
127
|
+
}
|
|
128
|
+
}, delay);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function onResourceLoad(event) {
|
|
132
|
+
const element = event.target;
|
|
133
|
+
element.removeAttribute(helper.LAZY_SRC_ATTRIBUTE_NAME);
|
|
134
|
+
element.removeEventListener("load", onResourceLoad);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function onImageLoadEvent(event) {
|
|
138
|
+
loadingImages = true;
|
|
139
|
+
await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
140
|
+
await deferLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
141
|
+
if (event.detail) {
|
|
142
|
+
pendingImages.add(event.detail);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function onImageLoadedEvent(event) {
|
|
147
|
+
await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
148
|
+
await deferLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
149
|
+
pendingImages.delete(event.detail);
|
|
150
|
+
if (!pendingImages.size) {
|
|
151
|
+
await deferLazyLoadEnd(observer, options, cleanupAndResolve);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function cleanupAndResolve(value) {
|
|
156
|
+
observer.disconnect();
|
|
157
|
+
removeEventListener(hooksFrames.LOAD_IMAGE_EVENT, onImageLoadEvent);
|
|
158
|
+
removeEventListener(hooksFrames.IMAGE_LOADED_EVENT, onImageLoadedEvent);
|
|
159
|
+
resolve(value);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function deferLazyLoadEnd(observer, options, resolve) {
|
|
165
|
+
await setAsyncTimeout("loadTimeout", () => lazyLoadEnd(observer, options, resolve), options.loadDeferredImagesMaxIdleTime);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function deferForceLazyLoadEnd(observer, options, resolve) {
|
|
169
|
+
await setAsyncTimeout("maxTimeout", async () => {
|
|
170
|
+
await clearAsyncTimeout("loadTimeout");
|
|
171
|
+
await lazyLoadEnd(observer, options, resolve);
|
|
172
|
+
}, options.loadDeferredImagesMaxIdleTime * 10);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function lazyLoadEnd(observer, options, resolve) {
|
|
176
|
+
await clearAsyncTimeout("idleTimeout");
|
|
177
|
+
hooksFrames.loadDeferredImagesEnd(options);
|
|
178
|
+
await setAsyncTimeout("endTimeout", async () => {
|
|
179
|
+
await clearAsyncTimeout("maxTimeout");
|
|
180
|
+
resolve();
|
|
181
|
+
}, options.loadDeferredImagesMaxIdleTime / 2);
|
|
182
|
+
observer.disconnect();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function setAsyncTimeout(type, callback, delay) {
|
|
186
|
+
if (browser && browser.runtime && browser.runtime.sendMessage) {
|
|
187
|
+
if (!timeouts.get(type) || !timeouts.get(type).pending) {
|
|
188
|
+
const timeoutData = { callback, pending: true };
|
|
189
|
+
timeouts.set(type, timeoutData);
|
|
190
|
+
try {
|
|
191
|
+
await browser.runtime.sendMessage({ method: "singlefile.lazyTimeout.setTimeout", type, delay });
|
|
192
|
+
} catch (error) {
|
|
193
|
+
setRegularTimeout(type, callback, delay);
|
|
194
|
+
}
|
|
195
|
+
timeoutData.pending = false;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
setRegularTimeout(type, callback, delay);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function setRegularTimeout(type, callback, delay) {
|
|
203
|
+
const timeoutId = timeouts.get(type);
|
|
204
|
+
if (timeoutId) {
|
|
205
|
+
globalThis.clearTimeout(timeoutId);
|
|
206
|
+
}
|
|
207
|
+
timeouts.set(type, callback);
|
|
208
|
+
globalThis.setTimeout(callback, delay);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function clearAsyncTimeout(type) {
|
|
212
|
+
if (browser && browser.runtime && browser.runtime.sendMessage) {
|
|
213
|
+
try {
|
|
214
|
+
await browser.runtime.sendMessage({ method: "singlefile.lazyTimeout.clearTimeout", type });
|
|
215
|
+
} catch (error) {
|
|
216
|
+
clearRegularTimeout(type);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
clearRegularTimeout(type);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function clearRegularTimeout(type) {
|
|
224
|
+
const previousTimeoutId = timeouts.get(type);
|
|
225
|
+
timeouts.delete(type);
|
|
226
|
+
if (previousTimeoutId) {
|
|
227
|
+
globalThis.clearTimeout(previousTimeoutId);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as frameTree from "./processors/frame-tree/content/content-frame-tree.js";
|
|
2
|
+
import * as serializer from "./modules/html-serializer.js";
|
|
3
|
+
import {
|
|
4
|
+
COMMENT_HEADER,
|
|
5
|
+
COMMENT_HEADER_LEGACY,
|
|
6
|
+
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
7
|
+
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
8
|
+
initUserScriptHandler,
|
|
9
|
+
preProcessDoc,
|
|
10
|
+
postProcessDoc,
|
|
11
|
+
getShadowRoot
|
|
12
|
+
} from "./single-file-helper.js";
|
|
13
|
+
|
|
14
|
+
const processors = { frameTree };
|
|
15
|
+
const helper = {
|
|
16
|
+
COMMENT_HEADER,
|
|
17
|
+
COMMENT_HEADER_LEGACY,
|
|
18
|
+
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
19
|
+
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
20
|
+
preProcessDoc,
|
|
21
|
+
postProcessDoc,
|
|
22
|
+
serialize(doc, compressHTML) {
|
|
23
|
+
return serializer.process(doc, compressHTML);
|
|
24
|
+
},
|
|
25
|
+
getShadowRoot
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
initUserScriptHandler();
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
helper,
|
|
32
|
+
processors
|
|
33
|
+
};
|