single-file-core 1.2.8 → 1.2.10

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.
@@ -125,6 +125,7 @@ function getProcessorHelperClass(utilInstance) {
125
125
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("embed[src*=\".pdf\"]"), "src", baseURI, options, null, resources, styles, batchRequest));
126
126
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("audio[src], audio > source[src]"), "src", baseURI, options, "audio", resources, styles, batchRequest));
127
127
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("video[src], video > source[src]"), "src", baseURI, options, "video", resources, styles, batchRequest));
128
+ resourcePromises.push(this.processAttribute(doc.querySelectorAll("audio track[src], video track[src]"), "src", baseURI, options, null, resources, styles, batchRequest));
128
129
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("model[src]"), "src", baseURI, options, null, resources, styles, batchRequest));
129
130
  await Promise.all(resourcePromises);
130
131
  if (options.saveFavicon) {
@@ -382,7 +383,7 @@ function getProcessorHelperClass(utilInstance) {
382
383
  resourceElement.setAttribute("data-sf-original-" + attributeName, resourceURL);
383
384
  }
384
385
  delete resourceElement.dataset.singleFileOriginURL;
385
- if (!options["block" + expectedType.charAt(0).toUpperCase() + expectedType.substring(1) + "s"]) {
386
+ if (!expectedType || !options["block" + expectedType.charAt(0).toUpperCase() + expectedType.substring(1) + "s"]) {
386
387
  if (!testIgnoredPath(resourceURL)) {
387
388
  setAttributeEmpty(resourceElement, attributeName, expectedType);
388
389
  if (testValidPath(resourceURL)) {
@@ -114,6 +114,7 @@ function getProcessorHelperClass(utilInstance) {
114
114
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("embed[src*=\".pdf\"]"), "src", baseURI, options, null, resources, batchRequest));
115
115
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("audio[src], audio > source[src]"), "src", baseURI, options, "audio", resources, batchRequest));
116
116
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("video[src], video > source[src]"), "src", baseURI, options, "video", resources, batchRequest));
117
+ resourcePromises.push(this.processAttribute(doc.querySelectorAll("audio track[src], video track[src]"), "src", baseURI, options, null, resources, batchRequest));
117
118
  resourcePromises.push(this.processAttribute(doc.querySelectorAll("model[src]"), "src", baseURI, options, null, resources, batchRequest));
118
119
  await Promise.all(resourcePromises);
119
120
  if (options.saveFavicon) {
@@ -360,7 +361,7 @@ function getProcessorHelperClass(utilInstance) {
360
361
  resourceElement.setAttribute("data-sf-original-" + attributeName, resourceURL);
361
362
  }
362
363
  delete resourceElement.dataset.singleFileOriginURL;
363
- if (!options["block" + expectedType.charAt(0).toUpperCase() + expectedType.substring(1) + "s"]) {
364
+ if (!expectedType || !options["block" + expectedType.charAt(0).toUpperCase() + expectedType.substring(1) + "s"]) {
364
365
  if (!testIgnoredPath(resourceURL)) {
365
366
  setAttributeEmpty(resourceElement, attributeName, expectedType);
366
367
  if (testValidPath(resourceURL)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -54,93 +54,100 @@ async function extract(content, { password, prompt = () => { }, shadowRootScript
54
54
  "wav": "audio/wav",
55
55
  "weba": "audio/webm"
56
56
  };
57
+ const REGEXP_MATCH_STYLESHEET = /stylesheet_[0-9]+\.css/;
58
+ const REGEXP_MATCH_SCRIPT = /scripts\/[0-9]+\.js/;
59
+ const REGEXP_MATCH_ROOT_INDEX = /^([0-9_]+\/)?index\.html$/;
60
+ const REGEXP_MATCH_INDEX = /index\.html$/;
61
+ const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
62
+ const CHARSET_UTF8 = ";charset=utf-8";
57
63
  if (Array.isArray(content)) {
58
64
  content = new Blob([new Uint8Array(content)]);
59
65
  }
60
66
  zip.configure(zipOptions);
61
67
  const blobReader = new zip.BlobReader(content);
62
- let resources = [];
63
68
  const zipReader = new zip.ZipReader(blobReader);
64
69
  const entries = await zipReader.getEntries();
65
70
  const options = { password };
71
+ let docContent, origDocContent, url, resources = [];
66
72
  await Promise.all(entries.map(async entry => {
67
- let dataWriter, content, textContent, name, blob;
73
+ const { filename } = entry;
74
+ let dataWriter, content, textContent, mimeType;
68
75
  if (!options.password && entry.encrypted) {
69
76
  options.password = prompt("Please enter the password to view the page");
70
77
  }
71
- name = entry.filename.match(/^([0-9_]+\/)?(.*)$/)[2];
72
- let mimeType;
73
- if (entry.filename.match(/index\.html$/) || entry.filename.match(/stylesheet_[0-9]+\.css/) || entry.filename.match(/scripts\/[0-9]+\.js/)) {
78
+ if (filename.match(REGEXP_MATCH_INDEX) || filename.match(REGEXP_MATCH_STYLESHEET) || filename.match(REGEXP_MATCH_SCRIPT)) {
74
79
  dataWriter = new zip.TextWriter();
75
80
  textContent = await entry.getData(dataWriter, options);
76
- if (entry.filename.match(/index\.html$/)) {
77
- mimeType = "text/html;charset=utf-8";
81
+ if (filename.match(REGEXP_MATCH_INDEX)) {
82
+ mimeType = "text/html" + CHARSET_UTF8;
78
83
  } else {
79
- if (entry.filename.match(/stylesheet_[0-9]+\.css/)) {
80
- mimeType = "text/css;charset=utf-8";
81
- } else if (entry.filename.match(/scripts\/[0-9]+\.js/)) {
82
- mimeType = "text/javascript;charset=utf-8";
84
+ if (filename.match(REGEXP_MATCH_STYLESHEET)) {
85
+ mimeType = "text/css" + CHARSET_UTF8;
86
+ } else if (filename.match(REGEXP_MATCH_SCRIPT)) {
87
+ mimeType = "text/javascript" + CHARSET_UTF8;
83
88
  }
84
89
  if (textContent !== undefined) {
85
- content = noBlobURL ? await getDataURI(textContent, mimeType) : URL.createObjectURL(new Blob([textContent], { type: mimeType }));
90
+ content = await getDataURI(textContent, mimeType);
86
91
  } else {
87
92
  content = "data:text/plain,";
88
93
  }
89
94
  }
90
95
  } else {
91
- const extension = entry.filename.match(/\.([^.]+)/);
96
+ const extension = filename.match(/\.([^.]+)/);
92
97
  if (extension && extension[1] && KNOWN_MIMETYPES[extension[1]]) {
93
98
  mimeType = KNOWN_MIMETYPES[extension[1]];
94
99
  } else {
95
100
  mimeType = "application/octet-stream";
96
101
  }
97
- if (entry.filename.match(/frames\//) || noBlobURL) {
102
+ if (filename.match(/frames\//) || noBlobURL) {
98
103
  content = await entry.getData(new zip.Data64URIWriter(mimeType), options);
99
104
  } else {
100
- blob = await entry.getData(new zip.BlobWriter(mimeType), options);
105
+ const blob = await entry.getData(new zip.BlobWriter(mimeType), options);
101
106
  content = URL.createObjectURL(blob);
102
107
  }
103
108
  }
104
- resources.push({ filename: entry.filename, name, url: entry.comment, content, mimeType, blob, textContent, parentResources: [] });
109
+ const name = entry.filename.match(/^([0-9_]+\/)?(.*)$/)[2];
110
+ resources.push({ filename: entry.filename, name, url: entry.comment, content, mimeType, textContent, parentResources: [] });
105
111
  }));
106
112
  await zipReader.close();
107
- let docContent, origDocContent, url;
108
113
  resources = resources.sort((resourceLeft, resourceRight) => resourceRight.filename.length - resourceLeft.filename.length);
109
- const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
110
114
  for (const resource of resources) {
111
- if (resource.textContent !== undefined) {
115
+ let { textContent, mimeType, filename } = resource;
116
+ if (textContent !== undefined) {
112
117
  let prefixPath = "";
113
- const prefixPathMatch = resource.filename.match(/(.*\/)[^/]+$/);
118
+ const prefixPathMatch = filename.match(/(.*\/)[^/]+$/);
114
119
  if (prefixPathMatch && prefixPathMatch[1]) {
115
120
  prefixPath = prefixPathMatch[1];
116
121
  }
117
- if (resource.filename.match(/^([0-9_]+\/)?index\.html$/)) {
118
- origDocContent = resource.textContent;
122
+ if (filename.match(REGEXP_MATCH_ROOT_INDEX)) {
123
+ origDocContent = textContent;
119
124
  }
120
- const isScript = resource.filename.match(/scripts\/[0-9]+\.js/);
121
- if (!isScript) {
125
+ if (!filename.match(REGEXP_MATCH_SCRIPT)) {
126
+ const resourceFilename = filename;
122
127
  await Promise.all(resources.map(async innerResource => {
123
- if (innerResource.filename.startsWith(prefixPath) && innerResource.filename != resource.filename) {
124
- const filename = innerResource.filename.substring(prefixPath.length);
125
- if (!filename.match(/manifest\.json$/)) {
126
- const searchRegExp = new RegExp(filename.replace(REGEXP_ESCAPE, "\\$1"), "g");
127
- const position = resource.textContent.search(searchRegExp);
128
+ const { filename, parentResources, content } = innerResource;
129
+ if (filename.startsWith(prefixPath) && filename != resourceFilename) {
130
+ const relativeFilename = filename.substring(prefixPath.length);
131
+ if (!relativeFilename.match(/manifest\.json$/)) {
132
+ const searchRegExp = new RegExp(relativeFilename.replace(REGEXP_ESCAPE, "\\$1"), "g");
133
+ const position = textContent.search(searchRegExp);
128
134
  if (position != -1) {
129
- innerResource.parentResources.push(resource.filename);
130
- resource.textContent = resource.textContent.replace(searchRegExp, innerResource.content);
135
+ parentResources.push(resourceFilename);
136
+ textContent = textContent.replace(searchRegExp, content);
131
137
  }
132
138
  }
133
139
  }
134
140
  }));
135
- resource.content = await getDataURI(resource.textContent, resource.mimeType);
141
+ resource.content = await getDataURI(textContent, mimeType);
142
+ resource.textContent = textContent;
136
143
  }
137
- if (resource.filename.match(/index\.html$/)) {
144
+ if (filename.match(REGEXP_MATCH_INDEX)) {
138
145
  if (shadowRootScriptURL) {
139
- resource.textContent = resource.textContent.replace(/<script data-template-shadow-root.*<\/script>/g, "<script data-template-shadow-root src=" + shadowRootScriptURL + "></" + "script>");
146
+ resource.textContent = textContent.replace(/<script data-template-shadow-root.*<\/script>/g, "<script data-template-shadow-root src=" + shadowRootScriptURL + "></" + "script>");
140
147
  }
141
148
  }
142
- if (resource.filename.match(/^([0-9_]+\/)?index\.html$/)) {
143
- docContent = resource.textContent;
149
+ if (filename.match(REGEXP_MATCH_ROOT_INDEX)) {
150
+ docContent = textContent;
144
151
  url = resource.url;
145
152
  }
146
153
  }
@@ -151,8 +158,8 @@ async function extract(content, { password, prompt = () => { }, shadowRootScript
151
158
  const reader = new FileReader();
152
159
  reader.readAsDataURL(new Blob([textContent], { type: mimeType }));
153
160
  return new Promise((resolve, reject) => {
154
- reader.addEventListener("load", () => resolve(reader.result), false);
155
- reader.addEventListener("error", reject, false);
161
+ reader.onload = () => resolve(reader.result);
162
+ reader.onerror = reject;
156
163
  });
157
164
  }
158
165
  }