single-file-core 1.2.19 → 1.2.21

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.
@@ -16966,6 +16966,7 @@ async function evalTemplate(template = "", options, util, content, doc, dontRepl
16966
16966
  "substring": (value, start, end) => value.substring(start, end),
16967
16967
  "lowercase": value => value.toLowerCase(),
16968
16968
  "uppercase": value => value.toUpperCase(),
16969
+ "capitalize": value => value.charAt(0).toUpperCase() + value.slice(1),
16969
16970
  "replace": (value, searchValue, replaceValue) => searchValue && replaceValue ? value.replaceAll(searchValue, replaceValue) : value,
16970
16971
  "trim": value => value.trim(),
16971
16972
  "trim-left": value => value.trimLeft(),
@@ -16973,8 +16974,8 @@ async function evalTemplate(template = "", options, util, content, doc, dontRepl
16973
16974
  "pad-left": (value, length, padString) => length > 0 ? value.padStart(length, padString) : value,
16974
16975
  "pad-right": (value, length, padString) => length > 0 ? value.padEnd(length, padString) : value,
16975
16976
  "repeat": (value, count) => count > 0 ? value.repeat(count) : "",
16976
- "index-of": (value, searchValue) => value.indexOf(searchValue),
16977
- "last-index-of": (value, searchValue) => value.lastIndexOf(searchValue),
16977
+ "index-of": (value, searchValue, fromIndex) => value.indexOf(searchValue, fromIndex),
16978
+ "last-index-of": (value, searchValue, fromIndex) => value.lastIndexOf(searchValue, fromIndex),
16978
16979
  "length": value => value.length,
16979
16980
  "url-search-name": (index = 0) => params[index] && params[index][0],
16980
16981
  "url-search-value": (index = 0) => params[index] && params[index][1],
@@ -16992,11 +16993,11 @@ async function evalTemplate(template = "", options, util, content, doc, dontRepl
16992
16993
  const subdomains = urlSubDomains.split(".");
16993
16994
  return subdomains[subdomains.length - index - 1];
16994
16995
  },
16995
- "stringify": value => JSON.stringify(value),
16996
- "encode-uri": value => encodeURIComponent(value),
16997
- "decode-uri": value => decodeURIComponent(value),
16998
- "encode-uri-component": value => encodeURI(value),
16999
- "decode-uri-component": value => decodeURI(value)
16996
+ "stringify": value => { try { return JSON.stringify(value); } catch (error) { return value; } },
16997
+ "encode-uri": value => { try { return encodeURI(value); } catch (error) { return value; } },
16998
+ "decode-uri": value => { try { return decodeURI(value); } catch (error) { return value; } },
16999
+ "encode-uri-component": value => { try { return encodeURIComponent(value); } catch (error) { return value; } },
17000
+ "decode-uri-component": value => { try { return decodeURIComponent(value); } catch (error) { return value; } }
17000
17001
  };
17001
17002
  if (doc) {
17002
17003
  functions["page-element-text"] = (selector) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.2.19",
3
+ "version": "1.2.21",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -38,8 +38,20 @@ import {
38
38
  } from "./compression-display.js";
39
39
 
40
40
  const { Blob, fetch, TextEncoder, DOMParser } = globalThis;
41
- const NO_COMPRESSION_EXTENSIONS = [ ".jpg", ".jpeg", ".png", ".pdf", ".woff2", ".mp4", ".mp3", ".ogg", ".webp", ".webm", ".avi", ".mpeg", ".ts", ".ogv" ];
41
+ const NO_COMPRESSION_EXTENSIONS = [".jpg", ".jpeg", ".png", ".pdf", ".woff2", ".mp4", ".mp3", ".ogg", ".webp", ".webm", ".avi", ".mpeg", ".ts", ".ogv"];
42
42
  const SCRIPT_PATH = "/lib/single-file-zip.min.js";
43
+ const EXTRA_DATA_TAGS = [
44
+ ["<noscript>", "</noscript>"],
45
+ ["<script type=sfz-data>", "</script>"],
46
+ ["<xmp>", "</xmp>"],
47
+ ["<plaintext>", "</plaintext>"]
48
+ ];
49
+ const EXTRA_DATA_REGEXPS = [
50
+ [/<\/noscript>/i],
51
+ [/<\/script>/i],
52
+ [/<\/xmp>/i],
53
+ [/<\/plaintext>/i]
54
+ ];
43
55
 
44
56
  const browser = globalThis.browser;
45
57
 
@@ -76,6 +88,7 @@ async function process(pageData, options, lastModDate = new Date()) {
76
88
  if (pageData.viewport) {
77
89
  pageContent += "<meta name=\"viewport\" content=" + JSON.stringify(pageData.viewport) + ">";
78
90
  }
91
+ pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}}</style>";
79
92
  pageContent += "<body hidden>";
80
93
  pageContent += "<div id='sfz-wait-message'>Please wait...</div>";
81
94
  pageContent += "<div id='sfz-error-message'><strong>Error</strong>: Cannot open the page from the filesystem.";
@@ -104,12 +117,11 @@ async function process(pageData, options, lastModDate = new Date()) {
104
117
  script = "<script>" +
105
118
  script +
106
119
  "document.currentScript.remove();" +
107
- "globalThis.onload = () => {" +
108
- "globalThis.bootstrap=(()=>{let bootstrapStarted;return async content=>{if (bootstrapStarted) return bootstrapStarted;bootstrapStarted = (" +
109
- extract.toString().replace(/\n|\t/g, "") + ")(content,{prompt}).then(({docContent}) => " +
110
- display.toString().replace(/\n|\t/g, "") + "(document,docContent));return bootstrapStarted;}})();(" +
120
+ "globalThis.bootstrap=(()=>{let bootstrapStarted;return content=>{if (bootstrapStarted) return bootstrapStarted;bootstrapStarted=" +
121
+ "new Promise(resolve=>{document.readyState=='complete'?resolve():globalThis.onload=resolve}).then(()=>" +
122
+ extract.toString().replace(/\n|\t/g, "") + "(content,{prompt}).then(({docContent}) => " +
123
+ display.toString().replace(/\n|\t/g, "") + "(document,docContent)));return bootstrapStarted;}})();(" +
111
124
  getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).catch(()=>{});" +
112
- "};" +
113
125
  "</script>";
114
126
  pageContent += script;
115
127
  let extraData = "";
@@ -137,20 +149,7 @@ async function process(pageData, options, lastModDate = new Date()) {
137
149
  const textContent = new TextDecoder().decode(data);
138
150
  const matchEndTagComment = textContent.match(/-->/i);
139
151
  if (matchEndTagComment) {
140
- const matchEndTagXMP = textContent.match(/<\/\s*xmp>/i);
141
- if (matchEndTagXMP) {
142
- const matchEndTagPlainText = textContent.match(/<\/\s*plaintext>/i);
143
- if (matchEndTagPlainText) {
144
- options.extractDataFromPage = false;
145
- return process(pageData, options, lastModDate);
146
- } else {
147
- options.extractDataFromPageTags = ["<plaintext>", "</plaintext>"];
148
- return process(pageData, options, lastModDate);
149
- }
150
- } else {
151
- options.extractDataFromPageTags = ["<xmp>", "</xmp>"];
152
- return process(pageData, options, lastModDate);
153
- }
152
+ return findExtraDataTags(data, pageData, options, lastModDate);
154
153
  }
155
154
  }
156
155
  for (let index = 0; index < data.length; index++) {
@@ -207,6 +206,17 @@ async function process(pageData, options, lastModDate = new Date()) {
207
206
  return new Blob([pageContent], { type: "application/octet-stream" });
208
207
  }
209
208
 
209
+ function findExtraDataTags(data, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
210
+ const textContent = new TextDecoder().decode(data);
211
+ const matchEndTag = textContent.match(EXTRA_DATA_REGEXPS[indexExtractDataFromPageTags]);
212
+ if (matchEndTag) {
213
+ return findExtraDataTags(data, pageData, options, lastModDate, indexExtractDataFromPageTags + 1);
214
+ } else {
215
+ options.extractDataFromPageTags = EXTRA_DATA_TAGS[indexExtractDataFromPageTags];
216
+ return process(pageData, options, lastModDate);
217
+ }
218
+ }
219
+
210
220
  async function arrayToBase64(data) {
211
221
  const fileReader = new FileReader();
212
222
  return await new Promise(resolve => {
@@ -267,50 +277,41 @@ async function addFile(zipWriter, prefixName, data) {
267
277
  }
268
278
 
269
279
  async function getContent() {
270
- const { Blob, XMLHttpRequest, fetch, document, setTimeout, clearTimeout, stop } = globalThis;
280
+ const { Blob, XMLHttpRequest, fetch, document, stop } = globalThis;
271
281
  const characterMap = new Map([
272
282
  [65533, 0], [8364, 128], [8218, 130], [402, 131], [8222, 132], [8230, 133], [8224, 134], [8225, 135], [710, 136], [8240, 137],
273
283
  [352, 138], [8249, 139], [338, 140], [381, 142], [8216, 145], [8217, 146], [8220, 147], [8221, 148], [8226, 149], [8211, 150],
274
284
  [8212, 151], [732, 152], [8482, 153], [353, 154], [8250, 155], [339, 156], [382, 158], [376, 159]
275
285
  ]);
276
286
  const xhr = new XMLHttpRequest();
277
- let displayTimeout;
278
- Array.from(document.documentElement.childNodes).forEach(node => {
279
- if (node != document.body && node != document.head) {
280
- node.remove();
281
- }
282
- });
283
287
  xhr.responseType = "blob";
284
288
  xhr.open("GET", "");
285
289
  return new Promise((resolve, reject) => {
286
290
  xhr.onerror = () => {
287
291
  extractPageData().then(resolve).catch(() => {
288
- displayTimeout = displayMessage("sfz-error-message");
292
+ displayMessage("sfz-error-message", 2);
289
293
  reject();
290
294
  });
291
295
  };
292
296
  xhr.send();
293
297
  xhr.onload = () => {
294
- displayTimeout = displayMessage("sfz-wait-message");
295
298
  stop();
296
- if (displayTimeout) {
297
- clearTimeout(displayTimeout);
298
- }
299
+ displayMessage("sfz-wait-message", 2);
299
300
  resolve(xhr.response);
300
301
  };
301
302
  });
302
303
 
303
- function displayMessage(elementId) {
304
- return setTimeout(() => {
305
- if (document.getElementById(elementId)) {
306
- Array.from(document.body.childNodes).forEach(node => {
307
- if (node.id != elementId) {
308
- node.remove();
309
- }
310
- });
311
- document.body.hidden = false;
312
- }
313
- }, 1500);
304
+ function displayMessage(elementId, delay = 0) {
305
+ const element = document.getElementById(elementId);
306
+ if (element) {
307
+ Array.from(document.body.childNodes).forEach(node => {
308
+ if (node.id != elementId) {
309
+ node.remove();
310
+ }
311
+ });
312
+ document.body.hidden = false;
313
+ element.style = "opacity: 0; animation: 0s linear " + delay + "s display-wait-message 1 normal forwards";
314
+ }
314
315
  }
315
316
 
316
317
  async function extractPageData() {
@@ -326,6 +327,7 @@ async function getContent() {
326
327
  }
327
328
  const zipData = [];
328
329
  let { textContent } = dataNode;
330
+ displayMessage("sfz-wait-message", 2);
329
331
  for (let index = 0; index < textContent.length; index++) {
330
332
  const charCode = textContent.charCodeAt(index);
331
333
  zipData.push(charCode > 255 ? characterMap.get(charCode) : charCode);