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.
- package/core/index.js +1 -1
- package/core/lib/processor-helper-common.js +373 -42
- package/core/lib/processor-helper-inline.js +101 -412
- package/core/lib/processor-helper.js +102 -412
- package/modules/template-formatter.js +7 -2
- package/package.json +1 -1
- package/processors/compression/compression.js +97 -82
- package/single-file.js +6 -0
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
/* global globalThis, FileReader,
|
|
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
|
-
|
|
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, "<").replace(/>/g, ">") || "";
|
|
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, "<").replace(/>/g, ">").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
|
-
|
|
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(
|
|
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.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
|
212
|
-
|
|
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, "<").replace(/>/g, ">") || "";
|
|
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, "<").replace(/>/g, ">").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
|
-
|
|
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,
|