single-file-core 1.5.39 → 1.5.41
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 +18 -0
- package/core/index.js +2 -1
- package/core/util.js +4 -15
- package/modules/template-formatter.js +4 -18
- package/package.json +1 -1
package/core/helper.js
CHANGED
|
@@ -76,6 +76,9 @@ const COMMENT_HEADER_LEGACY = "Archive processed by SingleFile";
|
|
|
76
76
|
const SINGLE_FILE_UI_ELEMENT_CLASS = "single-file-ui-element";
|
|
77
77
|
const INFOBAR_TAGNAME = infobar.INFOBAR_TAGNAME;
|
|
78
78
|
const EMPTY_RESOURCE = "data:,";
|
|
79
|
+
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", "\"", "<", ">", "\\\\", "\x00-\x1f", "\x7F"];
|
|
80
|
+
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
81
|
+
const DEFAULT_REPLACEMENT_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", """, "<", ">", "\"];
|
|
79
82
|
const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
|
|
80
83
|
const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
|
|
81
84
|
const JSON = globalThis.JSON;
|
|
@@ -100,6 +103,7 @@ export {
|
|
|
100
103
|
appendInfobar,
|
|
101
104
|
getContentSize,
|
|
102
105
|
digest,
|
|
106
|
+
getValidFilename,
|
|
103
107
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
104
108
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
105
109
|
WIN_ID_ATTRIBUTE_NAME,
|
|
@@ -718,4 +722,18 @@ function getComputedStyle(win, element, pseudoElement) {
|
|
|
718
722
|
} catch (error) {
|
|
719
723
|
// ignored
|
|
720
724
|
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function getValidFilename(filename, replacedCharacters = DEFAULT_REPLACED_CHARACTERS, replacementCharacter = DEFAULT_REPLACEMENT_CHARACTER, replacementCharacters = DEFAULT_REPLACEMENT_CHARACTERS) {
|
|
728
|
+
replacementCharacters.forEach((_, index) => filename = filename.replace(new RegExp("[" + replacedCharacters[index] + "]+", "g"), replacementCharacters[index]));
|
|
729
|
+
replacedCharacters.forEach(replacedCharacter => filename = filename.replace(new RegExp("[" + replacedCharacter + "]+", "g"), replacementCharacter));
|
|
730
|
+
filename = filename
|
|
731
|
+
.replace(/\.\.\//g, "")
|
|
732
|
+
.replace(/^\/+/, "")
|
|
733
|
+
.replace(/\/+/g, "/")
|
|
734
|
+
.replace(/\/$/, "")
|
|
735
|
+
.replace(/\.$/, "")
|
|
736
|
+
.replace(/\.\//g, "." + replacementCharacter)
|
|
737
|
+
.replace(/\/\./g, "/" + replacementCharacter);
|
|
738
|
+
return filename;
|
|
721
739
|
}
|
package/core/index.js
CHANGED
|
@@ -383,7 +383,7 @@ class BatchRequest {
|
|
|
383
383
|
});
|
|
384
384
|
await onloadListener({ url: resourceURL });
|
|
385
385
|
if (!this.cancelled) {
|
|
386
|
-
const extension = util.getContentTypeExtension(content.contentType) || util.getFilenameExtension(resourceURL, options.filenameReplacedCharacters, options.filenameReplacementCharacter);
|
|
386
|
+
const extension = util.getContentTypeExtension(content.contentType) || util.getFilenameExtension(resourceURL, options.filenameReplacedCharacters, options.filenameReplacementCharacter, options.filenameReplacementCharacters);
|
|
387
387
|
resourceRequests.forEach(callbacks => {
|
|
388
388
|
const duplicateCallbacks = this.duplicates.get(requestKey);
|
|
389
389
|
const duplicate = duplicateCallbacks && duplicateCallbacks.length > 1 && duplicateCallbacks.includes(callbacks);
|
|
@@ -678,6 +678,7 @@ class Processor {
|
|
|
678
678
|
filenameTemplate: this.options.filenameTemplate,
|
|
679
679
|
filenameReplacedCharacters: this.options.filenameReplacedCharacters,
|
|
680
680
|
filenameReplacementCharacter: this.options.filenameReplacementCharacter,
|
|
681
|
+
filenameReplacementCharacters: this.options.filenameReplacementCharacters,
|
|
681
682
|
filenameMaxLengthUnit: this.options.filenameMaxLengthUnit,
|
|
682
683
|
filenameMaxLength: this.options.filenameMaxLength,
|
|
683
684
|
replaceEmojisInFilename: this.options.replaceEmojisInFilename,
|
package/core/util.js
CHANGED
|
@@ -30,8 +30,6 @@ import * as helper from "./helper.js";
|
|
|
30
30
|
const DEBUG = false;
|
|
31
31
|
const ONE_MB = 1024 * 1024;
|
|
32
32
|
const PREFIX_CONTENT_TYPE_TEXT = "text/";
|
|
33
|
-
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "\\\\", "?", "%", "*", ":", "|", "\"", "<", ">", "\x00-\x1f", "\x7F"];
|
|
34
|
-
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
35
33
|
const CONTENT_TYPE_EXTENSIONS = {
|
|
36
34
|
"image/svg+xml": ".svg",
|
|
37
35
|
"image/png": ".png",
|
|
@@ -89,14 +87,14 @@ function getInstance(utilOptions) {
|
|
|
89
87
|
utilOptions.frameFetch = utilOptions.frameFetch || utilOptions.fetch || fetch;
|
|
90
88
|
return {
|
|
91
89
|
getDoctypeString,
|
|
92
|
-
getFilenameExtension(resourceURL, replacedCharacters, replacementCharacter) {
|
|
90
|
+
getFilenameExtension(resourceURL, replacedCharacters, replacementCharacter, replacementCharacters) {
|
|
93
91
|
let matchExtension;
|
|
94
92
|
try {
|
|
95
93
|
matchExtension = new URL(resourceURL).pathname.match(/(\.[^\\/.]*)$/);
|
|
96
94
|
} catch (error) {
|
|
97
95
|
// ignored
|
|
98
96
|
}
|
|
99
|
-
return ((matchExtension && matchExtension[1] && this.getValidFilename(matchExtension[1], replacedCharacters, replacementCharacter)) || "").toLowerCase();
|
|
97
|
+
return ((matchExtension && matchExtension[1] && this.getValidFilename(matchExtension[1], replacedCharacters, replacementCharacter, replacementCharacters)) || "").toLowerCase();
|
|
100
98
|
},
|
|
101
99
|
getContentTypeExtension(contentType) {
|
|
102
100
|
return CONTENT_TYPE_EXTENSIONS[contentType] || "";
|
|
@@ -115,17 +113,8 @@ function getInstance(utilOptions) {
|
|
|
115
113
|
getSearchParams(searchParams) {
|
|
116
114
|
return Array.from(new URLSearchParams(searchParams));
|
|
117
115
|
},
|
|
118
|
-
getValidFilename(filename, replacedCharacters
|
|
119
|
-
|
|
120
|
-
filename = filename
|
|
121
|
-
.replace(/\.\.\//g, "")
|
|
122
|
-
.replace(/^\/+/, "")
|
|
123
|
-
.replace(/\/+/g, "/")
|
|
124
|
-
.replace(/\/$/, "")
|
|
125
|
-
.replace(/\.$/, "")
|
|
126
|
-
.replace(/\.\//g, "." + replacementCharacter)
|
|
127
|
-
.replace(/\/\./g, "/" + replacementCharacter);
|
|
128
|
-
return filename;
|
|
116
|
+
getValidFilename(filename, replacedCharacters, replacementCharacter, replacementCharacters) {
|
|
117
|
+
return helper.getValidFilename(filename, replacedCharacters, replacementCharacter, replacementCharacters);
|
|
129
118
|
},
|
|
130
119
|
parseDocContent(content, baseURI) {
|
|
131
120
|
const doc = (new DOMParser()).parseFromString(content, "text/html");
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
/* global globalThis */
|
|
25
25
|
|
|
26
26
|
import { parse } from "./template-parser.js";
|
|
27
|
-
import { getContentSize, digest } from "./../core/helper.js";
|
|
27
|
+
import { getContentSize, digest, getValidFilename } from "./../core/helper.js";
|
|
28
28
|
|
|
29
29
|
const Blob = globalThis.Blob;
|
|
30
30
|
const FileReader = globalThis.FileReader;
|
|
@@ -34,8 +34,6 @@ const URLSearchParams = globalThis.URLSearchParams;
|
|
|
34
34
|
const navigator = globalThis.navigator;
|
|
35
35
|
|
|
36
36
|
// eslint-disable-next-line quotes
|
|
37
|
-
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "\\\\", "?", "%", "*", ":", "|", '"', "<", ">", "\x00-\x1f", "\x7F"];
|
|
38
|
-
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
39
37
|
const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
|
|
40
38
|
|
|
41
39
|
const EMOJI_NAMES = {
|
|
@@ -16879,8 +16877,8 @@ async function formatFilename(content, doc, options) {
|
|
|
16879
16877
|
if (options.replaceEmojisInFilename) {
|
|
16880
16878
|
EMOJIS.forEach(emoji => (filename = replaceAll(filename, emoji, " _" + EMOJI_NAMES[emoji] + "_ ")));
|
|
16881
16879
|
}
|
|
16882
|
-
const replacementCharacter = options.filenameReplacementCharacter;
|
|
16883
|
-
filename = getValidFilename(filename,
|
|
16880
|
+
const { replacementCharacter, filenameReplacedCharacters, replacementCharacters } = options.filenameReplacementCharacter;
|
|
16881
|
+
filename = getValidFilename(filename, filenameReplacedCharacters, replacementCharacter, replacementCharacters);
|
|
16884
16882
|
if (!options.backgroundSave) {
|
|
16885
16883
|
filename = filename.replace(/\//g, replacementCharacter);
|
|
16886
16884
|
}
|
|
@@ -16946,6 +16944,7 @@ async function evalTemplate(template = "", options, content, doc, context = {})
|
|
|
16946
16944
|
"url-protocol": { getter: () => url.protocol },
|
|
16947
16945
|
"url-search": { getter: () => url.search.substring(1) },
|
|
16948
16946
|
"url-username": { getter: () => url.username },
|
|
16947
|
+
"url-original": { getter: () => options.originalUrl },
|
|
16949
16948
|
"tab-id": { getter: () => String(options.tabId) },
|
|
16950
16949
|
"tab-index": { getter: () => String(options.tabIndex) },
|
|
16951
16950
|
"url-last-segment": { getter: () => decode(getLastSegment(url, options.filenameReplacementCharacter)) },
|
|
@@ -17212,19 +17211,6 @@ function getLastSegment(url, replacementCharacter) {
|
|
|
17212
17211
|
return lastSegment;
|
|
17213
17212
|
}
|
|
17214
17213
|
|
|
17215
|
-
function getValidFilename(filename, replacedCharacters = DEFAULT_REPLACED_CHARACTERS, replacementCharacter = DEFAULT_REPLACEMENT_CHARACTER) {
|
|
17216
|
-
replacedCharacters.forEach(replacedCharacter => (filename = filename.replace(new RegExp("[" + replacedCharacter + "]+", "g"), replacementCharacter)));
|
|
17217
|
-
filename = filename
|
|
17218
|
-
.replace(/\.\.\//g, "")
|
|
17219
|
-
.replace(/^\/+/, "")
|
|
17220
|
-
.replace(/\/+/g, "/")
|
|
17221
|
-
.replace(/\/$/, "")
|
|
17222
|
-
.replace(/\.$/, "")
|
|
17223
|
-
.replace(/\.\//g, "." + replacementCharacter)
|
|
17224
|
-
.replace(/\/\./g, "/" + replacementCharacter);
|
|
17225
|
-
return filename;
|
|
17226
|
-
}
|
|
17227
|
-
|
|
17228
17214
|
function truncateText(content, maxSize) {
|
|
17229
17215
|
const blob = new Blob([content]);
|
|
17230
17216
|
const reader = new FileReader();
|