single-file-core 1.2.7 → 1.2.9

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.7",
3
+ "version": "1.2.9",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -54,93 +54,99 @@ 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";
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";
81
- } else if (entry.filename.match(/scripts\/[0-9]+\.js/)) {
82
- mimeType = "text/javascript";
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
+ resources.push({ filename: entry.filename, url: entry.comment, content, mimeType, textContent, parentResources: [] });
105
110
  }));
106
111
  await zipReader.close();
107
- let docContent, origDocContent, url;
108
112
  resources = resources.sort((resourceLeft, resourceRight) => resourceRight.filename.length - resourceLeft.filename.length);
109
- const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
110
113
  for (const resource of resources) {
111
- if (resource.textContent !== undefined) {
114
+ let { textContent, mimeType, filename } = resource;
115
+ if (textContent !== undefined) {
112
116
  let prefixPath = "";
113
- const prefixPathMatch = resource.filename.match(/(.*\/)[^/]+$/);
117
+ const prefixPathMatch = filename.match(/(.*\/)[^/]+$/);
114
118
  if (prefixPathMatch && prefixPathMatch[1]) {
115
119
  prefixPath = prefixPathMatch[1];
116
120
  }
117
- if (resource.filename.match(/^([0-9_]+\/)?index\.html$/)) {
118
- origDocContent = resource.textContent;
121
+ if (filename.match(REGEXP_MATCH_ROOT_INDEX)) {
122
+ origDocContent = textContent;
119
123
  }
120
- const isScript = resource.filename.match(/scripts\/[0-9]+\.js/);
121
- if (!isScript) {
124
+ if (!filename.match(REGEXP_MATCH_SCRIPT)) {
125
+ const resourceFilename = filename;
122
126
  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);
127
+ const { filename, parentResources, content } = innerResource;
128
+ if (filename.startsWith(prefixPath) && filename != resourceFilename) {
129
+ const relativeFilename = filename.substring(prefixPath.length);
130
+ if (!relativeFilename.match(/manifest\.json$/)) {
131
+ const searchRegExp = new RegExp(relativeFilename.replace(REGEXP_ESCAPE, "\\$1"), "g");
132
+ const position = textContent.search(searchRegExp);
128
133
  if (position != -1) {
129
- innerResource.parentResources.push(resource.filename);
130
- resource.textContent = resource.textContent.replace(searchRegExp, innerResource.content);
134
+ parentResources.push(resourceFilename);
135
+ textContent = textContent.replace(searchRegExp, content);
131
136
  }
132
137
  }
133
138
  }
134
139
  }));
135
- resource.content = await getDataURI(resource.textContent, resource.mimeType);
140
+ resource.content = await getDataURI(textContent, mimeType);
141
+ resource.textContent = textContent;
136
142
  }
137
- if (resource.filename.match(/index\.html$/)) {
143
+ if (filename.match(REGEXP_MATCH_INDEX)) {
138
144
  if (shadowRootScriptURL) {
139
- resource.textContent = resource.textContent.replace(/<script data-template-shadow-root.*<\/script>/g, "<script data-template-shadow-root src=" + shadowRootScriptURL + "></" + "script>");
145
+ resource.textContent = textContent.replace(/<script data-template-shadow-root.*<\/script>/g, "<script data-template-shadow-root src=" + shadowRootScriptURL + "></" + "script>");
140
146
  }
141
147
  }
142
- if (resource.filename.match(/^([0-9_]+\/)?index\.html$/)) {
143
- docContent = resource.textContent;
148
+ if (filename.match(REGEXP_MATCH_ROOT_INDEX)) {
149
+ docContent = textContent;
144
150
  url = resource.url;
145
151
  }
146
152
  }
@@ -151,8 +157,8 @@ async function extract(content, { password, prompt = () => { }, shadowRootScript
151
157
  const reader = new FileReader();
152
158
  reader.readAsDataURL(new Blob([textContent], { type: mimeType }));
153
159
  return new Promise((resolve, reject) => {
154
- reader.addEventListener("load", () => resolve(reader.result), false);
155
- reader.addEventListener("error", reject, false);
160
+ reader.onload = () => resolve(reader.result);
161
+ reader.onerror = reject;
156
162
  });
157
163
  }
158
164
  }