single-file-core 1.2.23 → 1.2.25

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.
@@ -21,7 +21,7 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global globalThis, FileReader, TextDecoder, Node */
24
+ /* global globalThis, FileReader, Node */
25
25
 
26
26
  import {
27
27
  configure,
@@ -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;
@@ -148,10 +85,11 @@ async function process(pageData, options, lastModDate = new Date()) {
148
85
  const substitutionsLF = [];
149
86
  if (options.extractDataFromPage) {
150
87
  if (!options.extractDataFromPageTags) {
151
- const textContent = new TextDecoder().decode(data);
88
+ let textContent = "";
89
+ data.slice(startOffset).forEach(charCode => textContent += String.fromCharCode(charCode));
152
90
  const matchEndTagComment = textContent.match(/-->/i);
153
91
  if (matchEndTagComment) {
154
- return findExtraDataTags(data, pageData, options, lastModDate);
92
+ return findExtraDataTags(textContent, pageData, options, lastModDate);
155
93
  }
156
94
  }
157
95
  for (let index = 0; index < data.length; index++) {
@@ -165,19 +103,22 @@ async function process(pageData, options, lastModDate = new Date()) {
165
103
  }
166
104
  }
167
105
  let pageContent = "";
168
- if (options.extractDataFromPageTags) {
169
- pageContent += options.extractDataFromPageTags[1];
170
- } else {
171
- pageContent += "-->";
106
+ if (!options.preventAppendedData) {
107
+ if (options.extractDataFromPageTags) {
108
+ pageContent += options.extractDataFromPageTags[1];
109
+ } else {
110
+ pageContent += "-->";
111
+ }
172
112
  }
173
- const endTags = "</body></html>";
113
+ const endTags = options.preventAppendedData ? "" : "</body></html>";
174
114
  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) {
115
+ const payload = await Promise.all([
116
+ arrayToBase64(insertionsCRLF),
117
+ arrayToBase64(substitutionsLF),
118
+ arrayToBase64([startOffset])
119
+ ]);
120
+ extraData = "<sfz-extra-data>" + payload.join(",") + "</sfz-extra-data>";
121
+ if (options.preventAppendedData || extraData.length > 65535 - endTags.length) {
181
122
  if (!options.extraDataSize) {
182
123
  options.extraDataSize = Math.floor(extraData.length * 1.001);
183
124
  return process(pageData, options, lastModDate);
@@ -192,7 +133,9 @@ async function process(pageData, options, lastModDate = new Date()) {
192
133
  }
193
134
  }
194
135
  pageContent += endTags;
195
- await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
136
+ if (pageContent) {
137
+ await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
138
+ }
196
139
  }
197
140
  await zipDataWriter.writable.close();
198
141
  const pageContent = await zipDataWriter.getData();
@@ -208,11 +151,83 @@ async function process(pageData, options, lastModDate = new Date()) {
208
151
  return new Blob([pageContent], { type: "application/octet-stream" });
209
152
  }
210
153
 
211
- function findExtraDataTags(data, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
212
- const textContent = new TextDecoder().decode(data);
154
+ async function prependHTMLData(pageData, zipDataWriter, script, options) {
155
+ let pageContent = "";
156
+ if (options.includeBOM && !options.extractDataFromPage) {
157
+ pageContent += "\ufeff";
158
+ }
159
+ const charset = options.extractDataFromPage ? "windows-1252" : "utf-8";
160
+ const pageDataTitle = pageData.title.replace(/</g, "&lt;").replace(/>/g, "&gt;") || "";
161
+ const title = options.extractDataFromPage ? "" : pageDataTitle;
162
+ pageContent += pageData.doctype + "<html data-sfz><meta charset=" + charset + "><title>" + title + "</title>";
163
+ if (options.insertCanonicalLink) {
164
+ pageContent += "<link rel=canonical href=\"" + options.url + "\">";
165
+ }
166
+ if (options.insertMetaNoIndex) {
167
+ pageContent += "<meta name=robots content=noindex>";
168
+ }
169
+ if (pageData.viewport) {
170
+ pageContent += "<meta name=\"viewport\" content=" + JSON.stringify(pageData.viewport) + ">";
171
+ }
172
+ pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}}</style>";
173
+ pageContent += "<body hidden>";
174
+ pageContent += "<div id='sfz-wait-message'>Please wait...</div>";
175
+ pageContent += "<div id='sfz-error-message'><strong>Error</strong>: Cannot open the page from the filesystem.";
176
+ pageContent += "<ul style='line-height:20px;'>";
177
+ 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>";
178
+ 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>";
179
+ pageContent += "<li><strong>Safari</strong>: Select \"Security > Disable Local File Restrictions\" in the \"Develop > Developer settings\" menu.</li></ul></div>";
180
+ if (options.insertTextBody) {
181
+ const doc = (new DOMParser()).parseFromString(pageData.content, "text/html");
182
+ doc.body.querySelectorAll("style, script, noscript").forEach(element => element.remove());
183
+ let textBody = "";
184
+ if (options.extractDataFromPage) {
185
+ textBody += pageDataTitle + "\n\n";
186
+ }
187
+ textBody += doc.body.innerText;
188
+ doc.body.querySelectorAll("single-file-note").forEach(node => {
189
+ const template = node.querySelector("template");
190
+ if (template) {
191
+ const docTemplate = (new DOMParser()).parseFromString(template.innerHTML, "text/html");
192
+ textBody += "\n" + docTemplate.body.querySelector("textarea").value;
193
+ }
194
+ });
195
+ textBody = textBody.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n +/g, "\n").replace(/\n\n\n+/g, "\n\n").trim();
196
+ pageContent += "\n<main hidden>\n" + textBody + "\n</main>\n";
197
+ }
198
+ script = "<script>" +
199
+ script +
200
+ "document.currentScript.remove();" +
201
+ "globalThis.addEventListener('load', () => {" +
202
+ "globalThis.bootstrap=(()=>{let bootstrapStarted;return async content=>{if (bootstrapStarted) return bootstrapStarted; bootstrapStarted = (" +
203
+ extract.toString().replace(/\n|\t/g, "") + ")(content,{prompt}).then(({docContent}) => " +
204
+ display.toString().replace(/\n|\t/g, "") + "(document,docContent));return bootstrapStarted;}})();(" +
205
+ getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(()=>{});" +
206
+ "});" +
207
+ "</script>";
208
+ pageContent += script;
209
+ let extraData = "";
210
+ if (options.extractDataFromPage && options.extraDataSize) {
211
+ const extraTags = "<sfz-extra-data></sfz-extra-data>";
212
+ extraData += extraTags + new Array(options.extraDataSize - extraTags.length).fill(" ").join("");
213
+ }
214
+ pageContent += extraData;
215
+ const startTag = options.extractDataFromPageTags ? options.extractDataFromPageTags[0] : "<!--";
216
+ pageContent += startTag;
217
+ const extraDataOffset = startTag.length + extraData.length;
218
+ await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
219
+ return extraDataOffset;
220
+ }
221
+
222
+ function findExtraDataTags(textContent, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
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(textContent, 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) {
@@ -106,6 +111,7 @@ async function getPageData(options = {}, initOptions, doc = globalThis.document,
106
111
  createRootDirectory: options.createRootDirectory,
107
112
  selfExtractingArchive: options.selfExtractingArchive,
108
113
  extractDataFromPage: options.extractDataFromPage,
114
+ preventAppendedData: options.preventAppendedData,
109
115
  insertCanonicalLink: options.insertCanonicalLink,
110
116
  insertMetaNoIndex: options.insertMetaNoIndex,
111
117
  password: options.password,