single-file-core 1.1.77 → 1.1.78
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/constants.js +13 -0
- package/core/helper.js +38 -30
- package/core/index.js +10 -7
- package/core/infobar.js +5 -1
- package/core/util.js +2 -1
- package/package.json +1 -1
- package/processors/frame-tree/content/content-frame-tree.js +5 -3
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const SINGLE_FILE_PREFIX = "single-file-";
|
|
2
|
+
const COMMENT_HEADER = "Page saved with SingleFile";
|
|
3
|
+
const SINGLE_FILE_SIGNATURE = "SingleFile";
|
|
4
|
+
const WAIT_FOR_USERSCRIPT_PROPERTY_NAME = "_singleFile_waitForUserScript";
|
|
5
|
+
const MESSAGE_PREFIX = "__frameTree__::";
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
SINGLE_FILE_PREFIX,
|
|
9
|
+
COMMENT_HEADER,
|
|
10
|
+
SINGLE_FILE_SIGNATURE,
|
|
11
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
12
|
+
MESSAGE_PREFIX
|
|
13
|
+
};
|
package/core/helper.js
CHANGED
|
@@ -26,31 +26,38 @@
|
|
|
26
26
|
import * as cssUnescape from "./../vendor/css-unescape.js";
|
|
27
27
|
import * as hooksFrames from "./../processors/hooks/content/content-hooks-frames.js";
|
|
28
28
|
import * as infobar from "./infobar.js";
|
|
29
|
+
import {
|
|
30
|
+
SINGLE_FILE_PREFIX,
|
|
31
|
+
COMMENT_HEADER,
|
|
32
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
33
|
+
MESSAGE_PREFIX
|
|
34
|
+
} from "./constants.js";
|
|
29
35
|
|
|
30
|
-
const ON_BEFORE_CAPTURE_EVENT_NAME = "
|
|
31
|
-
const ON_AFTER_CAPTURE_EVENT_NAME = "
|
|
32
|
-
const GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = "
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
36
|
+
const ON_BEFORE_CAPTURE_EVENT_NAME = SINGLE_FILE_PREFIX + "on-before-capture";
|
|
37
|
+
const ON_AFTER_CAPTURE_EVENT_NAME = SINGLE_FILE_PREFIX + "on-after-capture";
|
|
38
|
+
const GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = SINGLE_FILE_PREFIX + "request-get-adopted-stylesheets";
|
|
39
|
+
const GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT = SINGLE_FILE_PREFIX + "response-get-adopted-stylesheets";
|
|
40
|
+
const UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = SINGLE_FILE_PREFIX + "unregister-request-get-adopted-stylesheets";
|
|
41
|
+
const ON_INIT_USERSCRIPT_EVENT = SINGLE_FILE_PREFIX + "user-script-init";
|
|
42
|
+
const REMOVED_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "removed-content";
|
|
43
|
+
const HIDDEN_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "hidden-content";
|
|
44
|
+
const KEPT_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "kept-content";
|
|
45
|
+
const HIDDEN_FRAME_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "hidden-frame";
|
|
46
|
+
const PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "preserved-space-element";
|
|
47
|
+
const SHADOW_ROOT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "shadow-root-element";
|
|
48
|
+
const WIN_ID_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "win-id";
|
|
49
|
+
const IMAGE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "image";
|
|
50
|
+
const POSTER_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "poster";
|
|
51
|
+
const VIDEO_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "video";
|
|
52
|
+
const CANVAS_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "canvas";
|
|
53
|
+
const STYLE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "movable-style";
|
|
54
|
+
const INPUT_VALUE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "input-value";
|
|
55
|
+
const LAZY_SRC_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "lazy-loaded-src";
|
|
56
|
+
const STYLESHEET_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "stylesheet";
|
|
57
|
+
const DISABLED_NOSCRIPT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "disabled-noscript";
|
|
58
|
+
const SELECTED_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "selected-content";
|
|
59
|
+
const INVALID_ELEMENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "invalid-element";
|
|
60
|
+
const ASYNC_SCRIPT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "async-script";
|
|
54
61
|
const FLOW_ELEMENTS_SELECTOR = "*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)";
|
|
55
62
|
const KEPT_TAG_NAMES = ["NOSCRIPT", "DISABLED-NOSCRIPT", "META", "LINK", "STYLE", "TITLE", "TEMPLATE", "SOURCE", "OBJECT", "SCRIPT", "HEAD", "BODY"];
|
|
56
63
|
const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
|
|
@@ -62,7 +69,6 @@ const FONT_WEIGHTS = {
|
|
|
62
69
|
bolder: "700",
|
|
63
70
|
lighter: "100"
|
|
64
71
|
};
|
|
65
|
-
const COMMENT_HEADER = "Page saved with SingleFile";
|
|
66
72
|
const COMMENT_HEADER_LEGACY = "Archive processed by SingleFile";
|
|
67
73
|
const SINGLE_FILE_UI_ELEMENT_CLASS = "single-file-ui-element";
|
|
68
74
|
const INFOBAR_TAGNAME = infobar.INFOBAR_TAGNAME;
|
|
@@ -106,11 +112,13 @@ export {
|
|
|
106
112
|
COMMENT_HEADER_LEGACY,
|
|
107
113
|
SINGLE_FILE_UI_ELEMENT_CLASS,
|
|
108
114
|
EMPTY_RESOURCE,
|
|
109
|
-
INFOBAR_TAGNAME
|
|
115
|
+
INFOBAR_TAGNAME,
|
|
116
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
117
|
+
MESSAGE_PREFIX
|
|
110
118
|
};
|
|
111
119
|
|
|
112
120
|
function initUserScriptHandler() {
|
|
113
|
-
addEventListener(
|
|
121
|
+
addEventListener(ON_INIT_USERSCRIPT_EVENT, () => globalThis[WAIT_FOR_USERSCRIPT_PROPERTY_NAME] = async eventPrefixName => {
|
|
114
122
|
const event = new CustomEvent(eventPrefixName + "-request", { cancelable: true });
|
|
115
123
|
const promiseResponse = new Promise(resolve => addEventListener(eventPrefixName + "-response", resolve));
|
|
116
124
|
dispatchEvent(event);
|
|
@@ -283,9 +291,9 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
283
291
|
const tagName = element.tagName && element.tagName.toUpperCase();
|
|
284
292
|
if (tagName == "CANVAS") {
|
|
285
293
|
try {
|
|
286
|
-
data.canvases.push({
|
|
287
|
-
dataURI: element.toDataURL("image/png", ""),
|
|
288
|
-
backgroundColor: computedStyle.getPropertyValue("background-color")
|
|
294
|
+
data.canvases.push({
|
|
295
|
+
dataURI: element.toDataURL("image/png", ""),
|
|
296
|
+
backgroundColor: computedStyle.getPropertyValue("background-color")
|
|
289
297
|
});
|
|
290
298
|
element.setAttribute(CANVAS_ATTRIBUTE_NAME, data.canvases.length - 1);
|
|
291
299
|
data.markedElements.push(element);
|
package/core/index.js
CHANGED
|
@@ -34,7 +34,7 @@ const Image = globalThis.Image;
|
|
|
34
34
|
|
|
35
35
|
let util, cssTree;
|
|
36
36
|
|
|
37
|
-
export {
|
|
37
|
+
export {
|
|
38
38
|
getClass
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -48,7 +48,7 @@ class SingleFileClass {
|
|
|
48
48
|
this.options = options;
|
|
49
49
|
}
|
|
50
50
|
async run() {
|
|
51
|
-
const waitForUserScript = globalThis.
|
|
51
|
+
const waitForUserScript = globalThis[util.WAIT_FOR_USERSCRIPT_PROPERTY_NAME];
|
|
52
52
|
if (this.options.userScriptEnabled && waitForUserScript) {
|
|
53
53
|
await waitForUserScript(util.ON_BEFORE_CAPTURE_EVENT_NAME);
|
|
54
54
|
}
|
|
@@ -430,7 +430,7 @@ class Processor {
|
|
|
430
430
|
}
|
|
431
431
|
this.maxResources = this.batchRequest.getMaxResources();
|
|
432
432
|
if (!this.options.saveRawPage && !this.options.removeFrames && this.options.frames) {
|
|
433
|
-
this.options.frames.forEach(frameData =>
|
|
433
|
+
this.options.frames.forEach(frameData => this.maxResources += frameData.maxResources || 0);
|
|
434
434
|
}
|
|
435
435
|
this.stats.set("processed", "resources", this.maxResources);
|
|
436
436
|
}
|
|
@@ -448,7 +448,7 @@ class Processor {
|
|
|
448
448
|
acceptHeaders: this.options.acceptHeaders,
|
|
449
449
|
networkTimeout: this.options.networkTimeout
|
|
450
450
|
});
|
|
451
|
-
pageContent = content.data;
|
|
451
|
+
pageContent = content.data || "";
|
|
452
452
|
}
|
|
453
453
|
this.doc = util.parseDocContent(pageContent, this.baseURI);
|
|
454
454
|
if (this.options.saveRawPage) {
|
|
@@ -1277,9 +1277,12 @@ class Processor {
|
|
|
1277
1277
|
let resourcePromises = processAttributeArgs.map(([selector, attributeName, processDuplicates, removeElementIfMissing]) =>
|
|
1278
1278
|
ProcessorHelper.processAttribute(this.doc.querySelectorAll(selector), attributeName, this.baseURI, this.options, "image", this.cssVariables, this.styles, this.batchRequest, processDuplicates, removeElementIfMissing)
|
|
1279
1279
|
);
|
|
1280
|
+
resourcePromises = resourcePromises.concat([
|
|
1281
|
+
ProcessorHelper.processXLinks(this.doc.querySelectorAll("use"), this.doc, this.baseURI, this.options, this.batchRequest),
|
|
1282
|
+
ProcessorHelper.processSrcset(this.doc.querySelectorAll("img[srcset], source[srcset]"), this.baseURI, this.options, this.batchRequest)
|
|
1283
|
+
]);
|
|
1280
1284
|
resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("object[data*=\".pdf\"]"), "data", this.baseURI, this.options, null, this.cssVariables, this.styles, this.batchRequest));
|
|
1281
1285
|
resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("embed[src*=\".pdf\"]"), "src", this.baseURI, this.options, null, this.cssVariables, this.styles, this.batchRequest));
|
|
1282
|
-
resourcePromises = resourcePromises.concat([ProcessorHelper.processXLinks(this.doc.querySelectorAll("use"), this.doc, this.baseURI, this.options, this.batchRequest), ProcessorHelper.processSrcset(this.doc.querySelectorAll("img[srcset], source[srcset]"), this.baseURI, this.options, this.batchRequest)]);
|
|
1283
1286
|
resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("audio[src], audio > source[src]"), "src", this.baseURI, this.options, "audio", this.cssVariables, this.styles, this.batchRequest));
|
|
1284
1287
|
resourcePromises.push(ProcessorHelper.processAttribute(this.doc.querySelectorAll("video[src], video > source[src]"), "src", this.baseURI, this.options, "video", this.cssVariables, this.styles, this.batchRequest));
|
|
1285
1288
|
await Promise.all(resourcePromises);
|
|
@@ -2087,7 +2090,7 @@ function normalizeURL(url) {
|
|
|
2087
2090
|
}
|
|
2088
2091
|
}
|
|
2089
2092
|
|
|
2090
|
-
function matchCharsetEquals(stylesheetContent, charset = UTF8_CHARSET) {
|
|
2093
|
+
function matchCharsetEquals(stylesheetContent = "", charset = UTF8_CHARSET) {
|
|
2091
2094
|
const stylesheetCharset = getCharset(stylesheetContent);
|
|
2092
2095
|
if (stylesheetCharset) {
|
|
2093
2096
|
return stylesheetCharset == charset.toLowerCase();
|
|
@@ -2096,7 +2099,7 @@ function matchCharsetEquals(stylesheetContent, charset = UTF8_CHARSET) {
|
|
|
2096
2099
|
}
|
|
2097
2100
|
}
|
|
2098
2101
|
|
|
2099
|
-
function getCharset(stylesheetContent) {
|
|
2102
|
+
function getCharset(stylesheetContent = "") {
|
|
2100
2103
|
const match = stylesheetContent.match(/^@charset\s+"([^"]*)";/i);
|
|
2101
2104
|
if (match && match[1]) {
|
|
2102
2105
|
return match[1].toLowerCase().trim();
|
package/core/infobar.js
CHANGED
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
|
|
24
24
|
/* global getComputedStyle, XPathResult, Node */
|
|
25
25
|
|
|
26
|
+
import {
|
|
27
|
+
SINGLE_FILE_SIGNATURE,
|
|
28
|
+
} from "./constants.js";
|
|
29
|
+
|
|
26
30
|
const INFOBAR_TAGNAME = "single-file-infobar";
|
|
27
31
|
const INFOBAR_STYLES = `
|
|
28
32
|
.infobar,
|
|
@@ -213,7 +217,7 @@ function appendInfobar(doc, options, useShadowRoot) {
|
|
|
213
217
|
function extractInfobarData(doc) {
|
|
214
218
|
const result = doc.evaluate("//comment()", doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
|
|
215
219
|
let singleFileComment = result && result.singleNodeValue;
|
|
216
|
-
if (singleFileComment && singleFileComment.nodeType == Node.COMMENT_NODE && singleFileComment.textContent.includes(
|
|
220
|
+
if (singleFileComment && singleFileComment.nodeType == Node.COMMENT_NODE && singleFileComment.textContent.includes(SINGLE_FILE_SIGNATURE)) {
|
|
217
221
|
const info = singleFileComment.textContent.split("\n");
|
|
218
222
|
const [, , urlData, ...optionalData] = info;
|
|
219
223
|
const urlMatch = urlData.match(/^ url: (.*) ?$/);
|
package/core/util.js
CHANGED
|
@@ -184,7 +184,8 @@ function getInstance(utilOptions) {
|
|
|
184
184
|
COMMENT_HEADER_LEGACY: helper.COMMENT_HEADER_LEGACY,
|
|
185
185
|
SINGLE_FILE_UI_ELEMENT_CLASS: helper.SINGLE_FILE_UI_ELEMENT_CLASS,
|
|
186
186
|
EMPTY_RESOURCE: helper.EMPTY_RESOURCE,
|
|
187
|
-
INFOBAR_TAGNAME: helper.INFOBAR_TAGNAME
|
|
187
|
+
INFOBAR_TAGNAME: helper.INFOBAR_TAGNAME,
|
|
188
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME
|
|
188
189
|
};
|
|
189
190
|
|
|
190
191
|
async function getContent(resourceURL, options) {
|
package/package.json
CHANGED
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
29
29
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
30
30
|
WIN_ID_ATTRIBUTE_NAME,
|
|
31
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
32
|
+
MESSAGE_PREFIX,
|
|
31
33
|
preProcessDoc,
|
|
32
34
|
serialize,
|
|
33
35
|
postProcessDoc,
|
|
@@ -38,13 +40,13 @@ const helper = {
|
|
|
38
40
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
39
41
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
40
42
|
WIN_ID_ATTRIBUTE_NAME,
|
|
43
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
41
44
|
preProcessDoc,
|
|
42
45
|
serialize,
|
|
43
46
|
postProcessDoc,
|
|
44
47
|
getShadowRoot
|
|
45
48
|
};
|
|
46
49
|
|
|
47
|
-
const MESSAGE_PREFIX = "__frameTree__::";
|
|
48
50
|
const FRAMES_CSS_SELECTOR = "iframe, frame, object[type=\"text/html\"][data]";
|
|
49
51
|
const ALL_ELEMENTS_CSS_SELECTOR = "*";
|
|
50
52
|
const INIT_REQUEST_MESSAGE = "singlefile.frameTree.initRequest";
|
|
@@ -163,7 +165,7 @@ function getNewSessionId() {
|
|
|
163
165
|
|
|
164
166
|
function initRequestSync(message) {
|
|
165
167
|
const sessionId = message.sessionId;
|
|
166
|
-
const waitForUserScript = globalThis.
|
|
168
|
+
const waitForUserScript = globalThis[helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME];
|
|
167
169
|
delete globalThis._singleFile_cleaningUp;
|
|
168
170
|
if (!TOP_WINDOW) {
|
|
169
171
|
windowId = globalThis.frameId = message.windowId;
|
|
@@ -183,7 +185,7 @@ function initRequestSync(message) {
|
|
|
183
185
|
|
|
184
186
|
async function initRequestAsync(message) {
|
|
185
187
|
const sessionId = message.sessionId;
|
|
186
|
-
const waitForUserScript = globalThis.
|
|
188
|
+
const waitForUserScript = globalThis[helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME];
|
|
187
189
|
delete globalThis._singleFile_cleaningUp;
|
|
188
190
|
if (!TOP_WINDOW) {
|
|
189
191
|
windowId = globalThis.frameId = message.windowId;
|