single-file-core 1.0.0

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.
@@ -0,0 +1,339 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Author: Gildas Lormeau
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ // derived from https://github.com/albell/parse-srcset
26
+
27
+ /**
28
+ * Srcset Parser
29
+ *
30
+ * By Alex Bell | MIT License
31
+ *
32
+ * JS Parser for the string value that appears in markup <img srcset="here">
33
+ *
34
+ * @returns Array [{url: _, d: _, w: _, h:_}, ...]
35
+ *
36
+ * Based super duper closely on the reference algorithm at:
37
+ * https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-srcset-attribute
38
+ *
39
+ * Most comments are copied in directly from the spec
40
+ * (except for comments in parens).
41
+ */
42
+
43
+ export {
44
+ process
45
+ };
46
+
47
+ // 1. Let input be the value passed to this algorithm.
48
+ function process(input) {
49
+
50
+ // UTILITY FUNCTIONS
51
+
52
+ // Manual is faster than RegEx
53
+ // http://bjorn.tipling.com/state-and-regular-expressions-in-javascript
54
+ // http://jsperf.com/whitespace-character/5
55
+ function isSpace(c) {
56
+ return (c === "\u0020" || // space
57
+ c === "\u0009" || // horizontal tab
58
+ c === "\u000A" || // new line
59
+ c === "\u000C" || // form feed
60
+ c === "\u000D"); // carriage return
61
+ }
62
+
63
+ function collectCharacters(regEx) {
64
+ let chars;
65
+ const match = regEx.exec(input.substring(pos));
66
+ if (match) {
67
+ chars = match[0];
68
+ pos += chars.length;
69
+ return chars;
70
+ }
71
+ }
72
+
73
+ const inputLength = input.length;
74
+
75
+ // (Don"t use \s, to avoid matching non-breaking space)
76
+ /* eslint-disable no-control-regex */
77
+ const regexLeadingSpaces = /^[ \t\n\r\u000c]+/;
78
+ const regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/;
79
+ const regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/;
80
+ const regexTrailingCommas = /[,]+$/;
81
+ const regexNonNegativeInteger = /^\d+$/;
82
+ /* eslint-enable no-control-regex */
83
+
84
+ // ( Positive or negative or unsigned integers or decimals, without or without exponents.
85
+ // Must include at least one digit.
86
+ // According to spec tests any decimal point must be followed by a digit.
87
+ // No leading plus sign is allowed.)
88
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-floating-point-number
89
+ const regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
90
+
91
+ let url, descriptors, currentDescriptor, state, c,
92
+ // 2. Let position be a pointer into input, initially pointing at the start
93
+ // of the string.
94
+ pos = 0;
95
+ // 3. Let candidates be an initially empty source set.
96
+ const candidates = [];
97
+
98
+ // 4. Splitting loop: Collect a sequence of characters that are space
99
+ // characters or U+002C COMMA characters. If any U+002C COMMA characters
100
+ // were collected, that is a parse error.
101
+ while (true) { // eslint-disable-line no-constant-condition
102
+ collectCharacters(regexLeadingCommasOrSpaces);
103
+
104
+ // 5. If position is past the end of input, return candidates and abort these steps.
105
+ if (pos >= inputLength) {
106
+ return candidates; // (we"re done, this is the sole return path)
107
+ }
108
+
109
+ // 6. Collect a sequence of characters that are not space characters,
110
+ // and let that be url.
111
+ url = collectCharacters(regexLeadingNotSpaces);
112
+
113
+ // 7. Let descriptors be a new empty list.
114
+ descriptors = [];
115
+
116
+ // 8. If url ends with a U+002C COMMA character (,), follow these substeps:
117
+ // (1). Remove all trailing U+002C COMMA characters from url. If this removed
118
+ // more than one character, that is a parse error.
119
+ if (url.slice(-1) === ",") {
120
+ url = url.replace(regexTrailingCommas, "");
121
+ // (Jump ahead to step 9 to skip tokenization and just push the candidate).
122
+ parseDescriptors();
123
+
124
+ // Otherwise, follow these substeps:
125
+ } else {
126
+ tokenize();
127
+ } // (close else of step 8)
128
+
129
+ // 16. Return to the step labeled splitting loop.
130
+ } // (Close of big while loop.)
131
+
132
+ /**
133
+ * Tokenizes descriptor properties prior to parsing
134
+ * Returns undefined.
135
+ */
136
+ function tokenize() {
137
+
138
+ // 8.1. Descriptor tokeniser: Skip whitespace
139
+ collectCharacters(regexLeadingSpaces);
140
+
141
+ // 8.2. Let current descriptor be the empty string.
142
+ currentDescriptor = "";
143
+
144
+ // 8.3. Let state be in descriptor.
145
+ state = "in descriptor";
146
+
147
+ while (true) { // eslint-disable-line no-constant-condition
148
+
149
+ // 8.4. Let c be the character at position.
150
+ c = input.charAt(pos);
151
+
152
+ // Do the following depending on the value of state.
153
+ // For the purpose of this step, "EOF" is a special character representing
154
+ // that position is past the end of input.
155
+
156
+ // In descriptor
157
+ if (state === "in descriptor") {
158
+ // Do the following, depending on the value of c:
159
+
160
+ // Space character
161
+ // If current descriptor is not empty, append current descriptor to
162
+ // descriptors and let current descriptor be the empty string.
163
+ // Set state to after descriptor.
164
+ if (isSpace(c)) {
165
+ if (currentDescriptor) {
166
+ descriptors.push(currentDescriptor);
167
+ currentDescriptor = "";
168
+ state = "after descriptor";
169
+ }
170
+
171
+ // U+002C COMMA (,)
172
+ // Advance position to the next character in input. If current descriptor
173
+ // is not empty, append current descriptor to descriptors. Jump to the step
174
+ // labeled descriptor parser.
175
+ } else if (c === ",") {
176
+ pos += 1;
177
+ if (currentDescriptor) {
178
+ descriptors.push(currentDescriptor);
179
+ }
180
+ parseDescriptors();
181
+ return;
182
+
183
+ // U+0028 LEFT PARENTHESIS (()
184
+ // Append c to current descriptor. Set state to in parens.
185
+ } else if (c === "\u0028") {
186
+ currentDescriptor = currentDescriptor + c;
187
+ state = "in parens";
188
+
189
+ // EOF
190
+ // If current descriptor is not empty, append current descriptor to
191
+ // descriptors. Jump to the step labeled descriptor parser.
192
+ } else if (c === "") {
193
+ if (currentDescriptor) {
194
+ descriptors.push(currentDescriptor);
195
+ }
196
+ parseDescriptors();
197
+ return;
198
+
199
+ // Anything else
200
+ // Append c to current descriptor.
201
+ } else {
202
+ currentDescriptor = currentDescriptor + c;
203
+ }
204
+ // (end "in descriptor"
205
+
206
+ // In parens
207
+ } else if (state === "in parens") {
208
+
209
+ // U+0029 RIGHT PARENTHESIS ())
210
+ // Append c to current descriptor. Set state to in descriptor.
211
+ if (c === ")") {
212
+ currentDescriptor = currentDescriptor + c;
213
+ state = "in descriptor";
214
+
215
+ // EOF
216
+ // Append current descriptor to descriptors. Jump to the step labeled
217
+ // descriptor parser.
218
+ } else if (c === "") {
219
+ descriptors.push(currentDescriptor);
220
+ parseDescriptors();
221
+ return;
222
+
223
+ // Anything else
224
+ // Append c to current descriptor.
225
+ } else {
226
+ currentDescriptor = currentDescriptor + c;
227
+ }
228
+
229
+ // After descriptor
230
+ } else if (state === "after descriptor") {
231
+
232
+ // Do the following, depending on the value of c:
233
+ // Space character: Stay in this state.
234
+ if (isSpace(c)) {
235
+
236
+ // EOF: Jump to the step labeled descriptor parser.
237
+ } else if (c === "") {
238
+ parseDescriptors();
239
+ return;
240
+
241
+ // Anything else
242
+ // Set state to in descriptor. Set position to the previous character in input.
243
+ } else {
244
+ state = "in descriptor";
245
+ pos -= 1;
246
+
247
+ }
248
+ }
249
+
250
+ // Advance position to the next character in input.
251
+ pos += 1;
252
+
253
+ // Repeat this step.
254
+ } // (close while true loop)
255
+ }
256
+
257
+ /**
258
+ * Adds descriptor properties to a candidate, pushes to the candidates array
259
+ * @return undefined
260
+ */
261
+ // Declared outside of the while loop so that it"s only created once.
262
+ function parseDescriptors() {
263
+
264
+ // 9. Descriptor parser: Let error be no.
265
+ let pError = false,
266
+
267
+ // 10. Let width be absent.
268
+ // 11. Let density be absent.
269
+ // 12. Let future-compat-h be absent. (We"re implementing it now as h)
270
+ w, d, h, i,
271
+ desc, lastChar, value, intVal, floatVal;
272
+ const candidate = {};
273
+
274
+ // 13. For each descriptor in descriptors, run the appropriate set of steps
275
+ // from the following list:
276
+ for (i = 0; i < descriptors.length; i++) {
277
+ desc = descriptors[i];
278
+
279
+ lastChar = desc[desc.length - 1];
280
+ value = desc.substring(0, desc.length - 1);
281
+ intVal = parseInt(value, 10);
282
+ floatVal = parseFloat(value);
283
+
284
+ // If the descriptor consists of a valid non-negative integer followed by
285
+ // a U+0077 LATIN SMALL LETTER W character
286
+ if (regexNonNegativeInteger.test(value) && (lastChar === "w")) {
287
+
288
+ // If width and density are not both absent, then let error be yes.
289
+ if (w || d) { pError = true; }
290
+
291
+ // Apply the rules for parsing non-negative integers to the descriptor.
292
+ // If the result is zero, let error be yes.
293
+ // Otherwise, let width be the result.
294
+ if (intVal === 0) { pError = true; } else { w = intVal; }
295
+
296
+ // If the descriptor consists of a valid floating-point number followed by
297
+ // a U+0078 LATIN SMALL LETTER X character
298
+ } else if (regexFloatingPoint.test(value) && (lastChar === "x")) {
299
+
300
+ // If width, density and future-compat-h are not all absent, then let error
301
+ // be yes.
302
+ if (w || d || h) { pError = true; }
303
+
304
+ // Apply the rules for parsing floating-point number values to the descriptor.
305
+ // If the result is less than zero, let error be yes. Otherwise, let density
306
+ // be the result.
307
+ if (floatVal < 0) { pError = true; } else { d = floatVal; }
308
+
309
+ // If the descriptor consists of a valid non-negative integer followed by
310
+ // a U+0068 LATIN SMALL LETTER H character
311
+ } else if (regexNonNegativeInteger.test(value) && (lastChar === "h")) {
312
+
313
+ // If height and density are not both absent, then let error be yes.
314
+ if (h || d) { pError = true; }
315
+
316
+ // Apply the rules for parsing non-negative integers to the descriptor.
317
+ // If the result is zero, let error be yes. Otherwise, let future-compat-h
318
+ // be the result.
319
+ if (intVal === 0) { pError = true; } else { h = intVal; }
320
+
321
+ // Anything else, Let error be yes.
322
+ } else { pError = true; }
323
+ } // (close step 13 for loop)
324
+
325
+ // 15. If error is still no, then append a new image source to candidates whose
326
+ // URL is url, associated with a width width if not absent and a pixel
327
+ // density density if not absent. Otherwise, there is a parse error.
328
+ if (!pError) {
329
+ candidate.url = url;
330
+ if (w) { candidate.w = w; }
331
+ if (d) { candidate.d = d; }
332
+ if (h) { candidate.h = h; }
333
+ candidates.push(candidate);
334
+ } else if (console && console.log) { // eslint-disable-line no-console
335
+ console.log("Invalid srcset descriptor found in \"" + input + "\" at \"" + desc + "\"."); // eslint-disable-line no-console
336
+ }
337
+ } // (close parseDescriptors fn)
338
+
339
+ }
@@ -0,0 +1,40 @@
1
+ /*
2
+ * Copyright 2010-2020 Gildas Lormeau
3
+ * contact : gildas.lormeau <at> gmail.com
4
+ *
5
+ * This file is part of SingleFile.
6
+ *
7
+ * The code in this file is free software: you can redistribute it and/or
8
+ * modify it under the terms of the GNU Affero General Public License
9
+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
10
+ * of the License, or (at your option) any later version.
11
+ *
12
+ * The code in this file is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
15
+ * General Public License for more details.
16
+ *
17
+ * As additional permission under GNU AGPL version 3 section 7, you may
18
+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
19
+ * AGPL normally required by section 4, provided you include this license
20
+ * notice and a URL through which recipients can access the Corresponding
21
+ * Source.
22
+ */
23
+
24
+ import * as fontPropertyParser from "./css-font-property-parser.js";
25
+ import * as mediaQueryParser from "./css-media-query-parser.js";
26
+ import * as cssMinifier from "./css-minifier.js";
27
+ import * as cssTree from "./css-tree.js";
28
+ import * as cssUnescape from "./css-unescape.js";
29
+ import * as srcsetParser from "./html-srcset-parser";
30
+ import { MIMEType } from "./mime-type-parser";
31
+
32
+ export {
33
+ fontPropertyParser,
34
+ mediaQueryParser,
35
+ cssMinifier,
36
+ cssTree,
37
+ cssUnescape,
38
+ srcsetParser,
39
+ MIMEType
40
+ };