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 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 = DEFAULT_REPLACED_CHARACTERS, replacementCharacter = DEFAULT_REPLACEMENT_CHARACTER) {
119
- replacedCharacters.forEach(replacedCharacter => filename = filename.replace(new RegExp("[" + replacedCharacter + "]+", "g"), replacementCharacter));
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, options.filenameReplacedCharacters, replacementCharacter);
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.5.39",
3
+ "version": "1.5.41",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",