single-file-core 1.2.23 → 1.2.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.2.23",
3
+ "version": "1.2.24",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -72,70 +72,7 @@ async function process(pageData, options, lastModDate = new Date()) {
72
72
  zipDataWriter.init();
73
73
  let extraDataOffset, extraData;
74
74
  if (options.selfExtractingArchive) {
75
- let pageContent = "";
76
- if (options.includeBOM && !options.extractDataFromPage) {
77
- pageContent += "\ufeff";
78
- }
79
- const charset = options.extractDataFromPage ? "windows-1252" : "utf-8";
80
- const pageDataTitle = pageData.title.replace(/</g, "&lt;").replace(/>/g, "&gt;") || "";
81
- const title = options.extractDataFromPage ? "" : pageDataTitle;
82
- pageContent += pageData.doctype + "<html data-sfz><meta charset=" + charset + "><title>" + title + "</title>";
83
- if (options.insertCanonicalLink) {
84
- pageContent += "<link rel=canonical href=\"" + options.url + "\">";
85
- }
86
- if (options.insertMetaNoIndex) {
87
- pageContent += "<meta name=robots content=noindex>";
88
- }
89
- if (pageData.viewport) {
90
- pageContent += "<meta name=\"viewport\" content=" + JSON.stringify(pageData.viewport) + ">";
91
- }
92
- pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}}</style>";
93
- pageContent += "<body hidden>";
94
- pageContent += "<div id='sfz-wait-message'>Please wait...</div>";
95
- pageContent += "<div id='sfz-error-message'><strong>Error</strong>: Cannot open the page from the filesystem.";
96
- pageContent += "<ul style='line-height:20px;'>";
97
- pageContent += "<li style='margin-bottom:10px'><strong>Chrome</strong>: Install <a href='https://chrome.google.com/webstore/detail/singlefile/mpiodijhokgodhhofbcjdecpffjipkle'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (chrome://extensions/?id=mpiodijhokgodhhofbcjdecpffjipkle).</li>";
98
- pageContent += "<li style='margin-bottom:10px'><strong>Microsoft Edge</strong>: Install <a href='https://microsoftedge.microsoft.com/addons/detail/singlefile/efnbkdcfmcmnhlkaijjjmhjjgladedno'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (edge://extensions/?id=efnbkdcfmcmnhlkaijjjmhjjgladedno).</li>";
99
- pageContent += "<li><strong>Safari</strong>: Select \"Security > Disable Local File Restrictions\" in the \"Develop > Developer settings\" menu.</li></ul></div>";
100
- if (options.insertTextBody) {
101
- const doc = (new DOMParser()).parseFromString(pageData.content, "text/html");
102
- doc.body.querySelectorAll("style, script, noscript").forEach(element => element.remove());
103
- let textBody = "";
104
- if (options.extractDataFromPage) {
105
- textBody += pageDataTitle + "\n\n";
106
- }
107
- textBody += doc.body.innerText;
108
- doc.body.querySelectorAll("single-file-note").forEach(node => {
109
- const template = node.querySelector("template");
110
- if (template) {
111
- const docTemplate = (new DOMParser()).parseFromString(template.innerHTML, "text/html");
112
- textBody += "\n" + docTemplate.body.querySelector("textarea").value;
113
- }
114
- });
115
- textBody = textBody.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n +/g, "\n").replace(/\n\n\n+/g, "\n\n").trim();
116
- pageContent += "\n<main hidden>\n" + textBody + "\n</main>\n";
117
- }
118
- script = "<script>" +
119
- script +
120
- "document.currentScript.remove();" +
121
- "globalThis.addEventListener('load', () => {" +
122
- "globalThis.bootstrap=(()=>{let bootstrapStarted;return async content=>{if (bootstrapStarted) return bootstrapStarted; bootstrapStarted = (" +
123
- extract.toString().replace(/\n|\t/g, "") + ")(content,{prompt}).then(({docContent}) => " +
124
- display.toString().replace(/\n|\t/g, "") + "(document,docContent));return bootstrapStarted;}})();(" +
125
- getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(()=>{});" +
126
- "});" +
127
- "</script>";
128
- pageContent += script;
129
- let extraData = "";
130
- if (options.extractDataFromPage && options.extraDataSize) {
131
- const extraTags = "<sfz-extra-data></sfz-extra-data>";
132
- extraData += extraTags + new Array(options.extraDataSize - extraTags.length).fill(" ").join("");
133
- }
134
- pageContent += extraData;
135
- const startTag = options.extractDataFromPageTags ? options.extractDataFromPageTags[0] : "<!--";
136
- pageContent += startTag;
137
- extraDataOffset = startTag.length + extraData.length;
138
- await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
75
+ extraDataOffset = await prependHTMLData(pageData, zipDataWriter, script, options);
139
76
  }
140
77
  const zipWriter = new ZipWriter(zipDataWriter, { bufferedWrite: true, keepOrder: false, lastModDate });
141
78
  let startOffset = zipDataWriter.offset;
@@ -165,19 +102,22 @@ async function process(pageData, options, lastModDate = new Date()) {
165
102
  }
166
103
  }
167
104
  let pageContent = "";
168
- if (options.extractDataFromPageTags) {
169
- pageContent += options.extractDataFromPageTags[1];
170
- } else {
171
- pageContent += "-->";
105
+ if (!options.preventAppendedData) {
106
+ if (options.extractDataFromPageTags) {
107
+ pageContent += options.extractDataFromPageTags[1];
108
+ } else {
109
+ pageContent += "-->";
110
+ }
172
111
  }
173
- const endTags = "</body></html>";
112
+ const endTags = options.preventAppendedData ? "" : "</body></html>";
174
113
  if (options.extractDataFromPage) {
175
- extraData = "<sfz-extra-data>" +
176
- await arrayToBase64(insertionsCRLF) + "," +
177
- await arrayToBase64(substitutionsLF) + "," +
178
- await arrayToBase64([startOffset]) +
179
- "</sfz-extra-data>";
180
- if (extraData.length > 65535 - endTags.length) {
114
+ const payload = await Promise.all([
115
+ arrayToBase64(insertionsCRLF),
116
+ arrayToBase64(substitutionsLF),
117
+ arrayToBase64([startOffset])
118
+ ]);
119
+ extraData = "<sfz-extra-data>" + payload.join(",") + "</sfz-extra-data>";
120
+ if (options.preventAppendedData || extraData.length > 65535 - endTags.length) {
181
121
  if (!options.extraDataSize) {
182
122
  options.extraDataSize = Math.floor(extraData.length * 1.001);
183
123
  return process(pageData, options, lastModDate);
@@ -192,7 +132,9 @@ async function process(pageData, options, lastModDate = new Date()) {
192
132
  }
193
133
  }
194
134
  pageContent += endTags;
195
- await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
135
+ if (pageContent) {
136
+ await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
137
+ }
196
138
  }
197
139
  await zipDataWriter.writable.close();
198
140
  const pageContent = await zipDataWriter.getData();
@@ -208,11 +150,84 @@ async function process(pageData, options, lastModDate = new Date()) {
208
150
  return new Blob([pageContent], { type: "application/octet-stream" });
209
151
  }
210
152
 
153
+ async function prependHTMLData(pageData, zipDataWriter, script, options) {
154
+ let pageContent = "";
155
+ if (options.includeBOM && !options.extractDataFromPage) {
156
+ pageContent += "\ufeff";
157
+ }
158
+ const charset = options.extractDataFromPage ? "windows-1252" : "utf-8";
159
+ const pageDataTitle = pageData.title.replace(/</g, "&lt;").replace(/>/g, "&gt;") || "";
160
+ const title = options.extractDataFromPage ? "" : pageDataTitle;
161
+ pageContent += pageData.doctype + "<html data-sfz><meta charset=" + charset + "><title>" + title + "</title>";
162
+ if (options.insertCanonicalLink) {
163
+ pageContent += "<link rel=canonical href=\"" + options.url + "\">";
164
+ }
165
+ if (options.insertMetaNoIndex) {
166
+ pageContent += "<meta name=robots content=noindex>";
167
+ }
168
+ if (pageData.viewport) {
169
+ pageContent += "<meta name=\"viewport\" content=" + JSON.stringify(pageData.viewport) + ">";
170
+ }
171
+ pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}}</style>";
172
+ pageContent += "<body hidden>";
173
+ pageContent += "<div id='sfz-wait-message'>Please wait...</div>";
174
+ pageContent += "<div id='sfz-error-message'><strong>Error</strong>: Cannot open the page from the filesystem.";
175
+ pageContent += "<ul style='line-height:20px;'>";
176
+ pageContent += "<li style='margin-bottom:10px'><strong>Chrome</strong>: Install <a href='https://chrome.google.com/webstore/detail/singlefile/mpiodijhokgodhhofbcjdecpffjipkle'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (chrome://extensions/?id=mpiodijhokgodhhofbcjdecpffjipkle).</li>";
177
+ pageContent += "<li style='margin-bottom:10px'><strong>Microsoft Edge</strong>: Install <a href='https://microsoftedge.microsoft.com/addons/detail/singlefile/efnbkdcfmcmnhlkaijjjmhjjgladedno'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (edge://extensions/?id=efnbkdcfmcmnhlkaijjjmhjjgladedno).</li>";
178
+ pageContent += "<li><strong>Safari</strong>: Select \"Security > Disable Local File Restrictions\" in the \"Develop > Developer settings\" menu.</li></ul></div>";
179
+ if (options.insertTextBody) {
180
+ const doc = (new DOMParser()).parseFromString(pageData.content, "text/html");
181
+ doc.body.querySelectorAll("style, script, noscript").forEach(element => element.remove());
182
+ let textBody = "";
183
+ if (options.extractDataFromPage) {
184
+ textBody += pageDataTitle + "\n\n";
185
+ }
186
+ textBody += doc.body.innerText;
187
+ doc.body.querySelectorAll("single-file-note").forEach(node => {
188
+ const template = node.querySelector("template");
189
+ if (template) {
190
+ const docTemplate = (new DOMParser()).parseFromString(template.innerHTML, "text/html");
191
+ textBody += "\n" + docTemplate.body.querySelector("textarea").value;
192
+ }
193
+ });
194
+ textBody = textBody.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n +/g, "\n").replace(/\n\n\n+/g, "\n\n").trim();
195
+ pageContent += "\n<main hidden>\n" + textBody + "\n</main>\n";
196
+ }
197
+ script = "<script>" +
198
+ script +
199
+ "document.currentScript.remove();" +
200
+ "globalThis.addEventListener('load', () => {" +
201
+ "globalThis.bootstrap=(()=>{let bootstrapStarted;return async content=>{if (bootstrapStarted) return bootstrapStarted; bootstrapStarted = (" +
202
+ extract.toString().replace(/\n|\t/g, "") + ")(content,{prompt}).then(({docContent}) => " +
203
+ display.toString().replace(/\n|\t/g, "") + "(document,docContent));return bootstrapStarted;}})();(" +
204
+ getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(()=>{});" +
205
+ "});" +
206
+ "</script>";
207
+ pageContent += script;
208
+ let extraData = "";
209
+ if (options.extractDataFromPage && options.extraDataSize) {
210
+ const extraTags = "<sfz-extra-data></sfz-extra-data>";
211
+ extraData += extraTags + new Array(options.extraDataSize - extraTags.length).fill(" ").join("");
212
+ }
213
+ pageContent += extraData;
214
+ const startTag = options.extractDataFromPageTags ? options.extractDataFromPageTags[0] : "<!--";
215
+ pageContent += startTag;
216
+ const extraDataOffset = startTag.length + extraData.length;
217
+ await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
218
+ return extraDataOffset;
219
+ }
220
+
211
221
  function findExtraDataTags(data, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
212
222
  const textContent = new TextDecoder().decode(data);
213
223
  const matchEndTag = textContent.match(EXTRA_DATA_REGEXPS[indexExtractDataFromPageTags]);
214
224
  if (matchEndTag) {
215
- return findExtraDataTags(data, pageData, options, lastModDate, indexExtractDataFromPageTags + 1);
225
+ if (indexExtractDataFromPageTags < EXTRA_DATA_TAGS.length - 1) {
226
+ return findExtraDataTags(data, pageData, options, lastModDate, indexExtractDataFromPageTags + 1);
227
+ } else {
228
+ options.extractDataFromPage = false;
229
+ return process(pageData, options, lastModDate);
230
+ }
216
231
  } else {
217
232
  options.extractDataFromPageTags = EXTRA_DATA_TAGS[indexExtractDataFromPageTags];
218
233
  return process(pageData, options, lastModDate);
package/single-file.js CHANGED
@@ -88,11 +88,16 @@ async function getPageData(options = {}, initOptions, doc = globalThis.document,
88
88
  options.doc = doc;
89
89
  options.win = win;
90
90
  options.insertCanonicalLink = true;
91
+
92
+ const externalOnProgress = options.onprogress;
91
93
  options.onprogress = event => {
92
94
  if (event.type === event.RESOURCES_INITIALIZED && doc && win && options.loadDeferredImages) {
93
95
  processors.lazy.resetZoomLevel(options);
94
96
  }
97
+
98
+ if (externalOnProgress) externalOnProgress(event);
95
99
  };
100
+
96
101
  const processor = new SingleFile(options);
97
102
  await processor.run();
98
103
  if (framesSessionId) {