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.
- package/.eslintrc.js +43 -0
- package/LICENSE +661 -0
- package/index.js +86 -0
- package/modules/css-fonts-alt-minifier.js +340 -0
- package/modules/css-fonts-minifier.js +371 -0
- package/modules/css-matched-rules.js +350 -0
- package/modules/css-medias-alt-minifier.js +91 -0
- package/modules/css-rules-minifier.js +146 -0
- package/modules/html-images-alt-minifier.js +109 -0
- package/modules/html-minifier.js +262 -0
- package/modules/html-serializer.js +180 -0
- package/modules/index.js +42 -0
- package/package.json +18 -0
- package/processors/frame-tree/content/content-frame-tree.js +418 -0
- package/processors/hooks/content/content-hooks-frames-web.js +404 -0
- package/processors/hooks/content/content-hooks-frames.js +214 -0
- package/processors/hooks/content/content-hooks-web.js +48 -0
- package/processors/hooks/content/content-hooks.js +39 -0
- package/processors/index.js +34 -0
- package/processors/lazy/content/content-lazy-loader.js +229 -0
- package/single-file-bootstrap.js +33 -0
- package/single-file-core.js +2471 -0
- package/single-file-frames.js +1 -0
- package/single-file-helper.js +557 -0
- package/single-file-util.js +412 -0
- package/vendor/css-font-property-parser.js +343 -0
- package/vendor/css-media-query-parser.js +474 -0
- package/vendor/css-minifier.js +789 -0
- package/vendor/css-tree.js +35 -0
- package/vendor/css-unescape.js +72 -0
- package/vendor/html-srcset-parser.js +339 -0
- package/vendor/index.js +40 -0
- package/vendor/mime-type-parser.js +446 -0
|
@@ -0,0 +1,474 @@
|
|
|
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/dryoma/postcss-media-query-parser
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* The MIT License (MIT)
|
|
29
|
+
*
|
|
30
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
32
|
+
* in the Software without restriction, including without limitation the rights
|
|
33
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
34
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
35
|
+
* furnished to do so, subject to the following conditions:
|
|
36
|
+
*
|
|
37
|
+
* The above copyright notice and this permission notice shall be included in
|
|
38
|
+
* all copies or substantial portions of the Software.
|
|
39
|
+
*
|
|
40
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
41
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
42
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
43
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
44
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
45
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
46
|
+
* THE SOFTWARE.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parses a media feature expression, e.g. `max-width: 10px`, `(color)`
|
|
51
|
+
*
|
|
52
|
+
* @param {string} string - the source expression string, can be inside parens
|
|
53
|
+
* @param {Number} index - the index of `string` in the overall input
|
|
54
|
+
*
|
|
55
|
+
* @return {Array} an array of Nodes, the first element being a media feature,
|
|
56
|
+
* the second - its value (may be missing)
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
function parseMediaFeature(string, index = 0) {
|
|
60
|
+
const modesEntered = [{
|
|
61
|
+
mode: "normal",
|
|
62
|
+
character: null,
|
|
63
|
+
}];
|
|
64
|
+
const result = [];
|
|
65
|
+
let lastModeIndex = 0, mediaFeature = "", colon = null, mediaFeatureValue = null, indexLocal = index;
|
|
66
|
+
|
|
67
|
+
let stringNormalized = string;
|
|
68
|
+
// Strip trailing parens (if any), and correct the starting index
|
|
69
|
+
if (string[0] === "(" && string[string.length - 1] === ")") {
|
|
70
|
+
stringNormalized = string.substring(1, string.length - 1);
|
|
71
|
+
indexLocal++;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < stringNormalized.length; i++) {
|
|
75
|
+
const character = stringNormalized[i];
|
|
76
|
+
|
|
77
|
+
// If entering/exiting a string
|
|
78
|
+
if (character === "'" || character === "\"") {
|
|
79
|
+
if (modesEntered[lastModeIndex].isCalculationEnabled === true) {
|
|
80
|
+
modesEntered.push({
|
|
81
|
+
mode: "string",
|
|
82
|
+
isCalculationEnabled: false,
|
|
83
|
+
character,
|
|
84
|
+
});
|
|
85
|
+
lastModeIndex++;
|
|
86
|
+
} else if (modesEntered[lastModeIndex].mode === "string" &&
|
|
87
|
+
modesEntered[lastModeIndex].character === character &&
|
|
88
|
+
stringNormalized[i - 1] !== "\\"
|
|
89
|
+
) {
|
|
90
|
+
modesEntered.pop();
|
|
91
|
+
lastModeIndex--;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// If entering/exiting interpolation
|
|
96
|
+
if (character === "{") {
|
|
97
|
+
modesEntered.push({
|
|
98
|
+
mode: "interpolation",
|
|
99
|
+
isCalculationEnabled: true,
|
|
100
|
+
});
|
|
101
|
+
lastModeIndex++;
|
|
102
|
+
} else if (character === "}") {
|
|
103
|
+
modesEntered.pop();
|
|
104
|
+
lastModeIndex--;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// If a : is met outside of a string, function call or interpolation, than
|
|
108
|
+
// this : separates a media feature and a value
|
|
109
|
+
if (modesEntered[lastModeIndex].mode === "normal" && character === ":") {
|
|
110
|
+
const mediaFeatureValueStr = stringNormalized.substring(i + 1);
|
|
111
|
+
mediaFeatureValue = {
|
|
112
|
+
type: "value",
|
|
113
|
+
before: /^(\s*)/.exec(mediaFeatureValueStr)[1],
|
|
114
|
+
after: /(\s*)$/.exec(mediaFeatureValueStr)[1],
|
|
115
|
+
value: mediaFeatureValueStr.trim(),
|
|
116
|
+
};
|
|
117
|
+
// +1 for the colon
|
|
118
|
+
mediaFeatureValue.sourceIndex =
|
|
119
|
+
mediaFeatureValue.before.length + i + 1 + indexLocal;
|
|
120
|
+
colon = {
|
|
121
|
+
type: "colon",
|
|
122
|
+
sourceIndex: i + indexLocal,
|
|
123
|
+
after: mediaFeatureValue.before,
|
|
124
|
+
value: ":", // for consistency only
|
|
125
|
+
};
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
mediaFeature += character;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Forming a media feature node
|
|
133
|
+
mediaFeature = {
|
|
134
|
+
type: "media-feature",
|
|
135
|
+
before: /^(\s*)/.exec(mediaFeature)[1],
|
|
136
|
+
after: /(\s*)$/.exec(mediaFeature)[1],
|
|
137
|
+
value: mediaFeature.trim(),
|
|
138
|
+
};
|
|
139
|
+
mediaFeature.sourceIndex = mediaFeature.before.length + indexLocal;
|
|
140
|
+
result.push(mediaFeature);
|
|
141
|
+
|
|
142
|
+
if (colon !== null) {
|
|
143
|
+
colon.before = mediaFeature.after;
|
|
144
|
+
result.push(colon);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (mediaFeatureValue !== null) {
|
|
148
|
+
result.push(mediaFeatureValue);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Parses a media query, e.g. `screen and (color)`, `only tv`
|
|
156
|
+
*
|
|
157
|
+
* @param {string} string - the source media query string
|
|
158
|
+
* @param {Number} index - the index of `string` in the overall input
|
|
159
|
+
*
|
|
160
|
+
* @return {Array} an array of Nodes and Containers
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
function parseMediaQuery(string, index = 0) {
|
|
164
|
+
const result = [];
|
|
165
|
+
|
|
166
|
+
// How many times the parser entered parens/curly braces
|
|
167
|
+
let localLevel = 0;
|
|
168
|
+
// Has any keyword, media type, media feature expression or interpolation
|
|
169
|
+
// ('element' hereafter) started
|
|
170
|
+
let insideSomeValue = false, node;
|
|
171
|
+
|
|
172
|
+
function resetNode() {
|
|
173
|
+
return {
|
|
174
|
+
before: "",
|
|
175
|
+
after: "",
|
|
176
|
+
value: "",
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
node = resetNode();
|
|
181
|
+
|
|
182
|
+
for (let i = 0; i < string.length; i++) {
|
|
183
|
+
const character = string[i];
|
|
184
|
+
// If not yet entered any element
|
|
185
|
+
if (!insideSomeValue) {
|
|
186
|
+
if (character.search(/\s/) !== -1) {
|
|
187
|
+
// A whitespace
|
|
188
|
+
// Don't form 'after' yet; will do it later
|
|
189
|
+
node.before += character;
|
|
190
|
+
} else {
|
|
191
|
+
// Not a whitespace - entering an element
|
|
192
|
+
// Expression start
|
|
193
|
+
if (character === "(") {
|
|
194
|
+
node.type = "media-feature-expression";
|
|
195
|
+
localLevel++;
|
|
196
|
+
}
|
|
197
|
+
node.value = character;
|
|
198
|
+
node.sourceIndex = index + i;
|
|
199
|
+
insideSomeValue = true;
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
// Already in the middle of some element
|
|
203
|
+
node.value += character;
|
|
204
|
+
|
|
205
|
+
// Here parens just increase localLevel and don't trigger a start of
|
|
206
|
+
// a media feature expression (since they can't be nested)
|
|
207
|
+
// Interpolation start
|
|
208
|
+
if (character === "{" || character === "(") { localLevel++; }
|
|
209
|
+
// Interpolation/function call/media feature expression end
|
|
210
|
+
if (character === ")" || character === "}") { localLevel--; }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// If exited all parens/curlies and the next symbol
|
|
214
|
+
if (insideSomeValue && localLevel === 0 &&
|
|
215
|
+
(character === ")" || i === string.length - 1 ||
|
|
216
|
+
string[i + 1].search(/\s/) !== -1)
|
|
217
|
+
) {
|
|
218
|
+
if (["not", "only", "and"].indexOf(node.value) !== -1) {
|
|
219
|
+
node.type = "keyword";
|
|
220
|
+
}
|
|
221
|
+
// if it's an expression, parse its contents
|
|
222
|
+
if (node.type === "media-feature-expression") {
|
|
223
|
+
node.nodes = parseMediaFeature(node.value, node.sourceIndex);
|
|
224
|
+
}
|
|
225
|
+
result.push(Array.isArray(node.nodes) ?
|
|
226
|
+
new Container(node) : new Node(node));
|
|
227
|
+
node = resetNode();
|
|
228
|
+
insideSomeValue = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Now process the result array - to specify undefined types of the nodes
|
|
233
|
+
// and specify the `after` prop
|
|
234
|
+
for (let i = 0; i < result.length; i++) {
|
|
235
|
+
node = result[i];
|
|
236
|
+
if (i > 0) { result[i - 1].after = node.before; }
|
|
237
|
+
|
|
238
|
+
// Node types. Might not be set because contains interpolation/function
|
|
239
|
+
// calls or fully consists of them
|
|
240
|
+
if (node.type === undefined) {
|
|
241
|
+
if (i > 0) {
|
|
242
|
+
// only `and` can follow an expression
|
|
243
|
+
if (result[i - 1].type === "media-feature-expression") {
|
|
244
|
+
node.type = "keyword";
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
// Anything after 'only|not' is a media type
|
|
248
|
+
if (result[i - 1].value === "not" || result[i - 1].value === "only") {
|
|
249
|
+
node.type = "media-type";
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
// Anything after 'and' is an expression
|
|
253
|
+
if (result[i - 1].value === "and") {
|
|
254
|
+
node.type = "media-feature-expression";
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (result[i - 1].type === "media-type") {
|
|
259
|
+
// if it is the last element - it might be an expression
|
|
260
|
+
// or 'and' depending on what is after it
|
|
261
|
+
if (!result[i + 1]) {
|
|
262
|
+
node.type = "media-feature-expression";
|
|
263
|
+
} else {
|
|
264
|
+
node.type = result[i + 1].type === "media-feature-expression" ?
|
|
265
|
+
"keyword" : "media-feature-expression";
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (i === 0) {
|
|
271
|
+
// `screen`, `fn( ... )`, `#{ ... }`. Not an expression, since then
|
|
272
|
+
// its type would have been set by now
|
|
273
|
+
if (!result[i + 1]) {
|
|
274
|
+
node.type = "media-type";
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// `screen and` or `#{...} (max-width: 10px)`
|
|
279
|
+
if (result[i + 1] &&
|
|
280
|
+
(result[i + 1].type === "media-feature-expression" ||
|
|
281
|
+
result[i + 1].type === "keyword")
|
|
282
|
+
) {
|
|
283
|
+
node.type = "media-type";
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (result[i + 2]) {
|
|
287
|
+
// `screen and (color) ...`
|
|
288
|
+
if (result[i + 2].type === "media-feature-expression") {
|
|
289
|
+
node.type = "media-type";
|
|
290
|
+
result[i + 1].type = "keyword";
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
// `only screen and ...`
|
|
294
|
+
if (result[i + 2].type === "keyword") {
|
|
295
|
+
node.type = "keyword";
|
|
296
|
+
result[i + 1].type = "media-type";
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (result[i + 3]) {
|
|
301
|
+
// `screen and (color) ...`
|
|
302
|
+
if (result[i + 3].type === "media-feature-expression") {
|
|
303
|
+
node.type = "keyword";
|
|
304
|
+
result[i + 1].type = "media-type";
|
|
305
|
+
result[i + 2].type = "keyword";
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Parses a media query list. Takes a possible `url()` at the start into
|
|
317
|
+
* account, and divides the list into media queries that are parsed separately
|
|
318
|
+
*
|
|
319
|
+
* @param {string} string - the source media query list string
|
|
320
|
+
*
|
|
321
|
+
* @return {Array} an array of Nodes/Containers
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
function parseMediaList(string) {
|
|
325
|
+
const result = [];
|
|
326
|
+
let interimIndex = 0, levelLocal = 0;
|
|
327
|
+
|
|
328
|
+
// Check for a `url(...)` part (if it is contents of an @import rule)
|
|
329
|
+
const doesHaveUrl = /^(\s*)url\s*\(/.exec(string);
|
|
330
|
+
if (doesHaveUrl !== null) {
|
|
331
|
+
let i = doesHaveUrl[0].length;
|
|
332
|
+
let parenthesesLv = 1;
|
|
333
|
+
while (parenthesesLv > 0) {
|
|
334
|
+
const character = string[i];
|
|
335
|
+
if (character === "(") { parenthesesLv++; }
|
|
336
|
+
if (character === ")") { parenthesesLv--; }
|
|
337
|
+
i++;
|
|
338
|
+
}
|
|
339
|
+
result.unshift(new Node({
|
|
340
|
+
type: "url",
|
|
341
|
+
value: string.substring(0, i).trim(),
|
|
342
|
+
sourceIndex: doesHaveUrl[1].length,
|
|
343
|
+
before: doesHaveUrl[1],
|
|
344
|
+
after: /^(\s*)/.exec(string.substring(i))[1],
|
|
345
|
+
}));
|
|
346
|
+
interimIndex = i;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Start processing the media query list
|
|
350
|
+
for (let i = interimIndex; i < string.length; i++) {
|
|
351
|
+
const character = string[i];
|
|
352
|
+
|
|
353
|
+
// Dividing the media query list into comma-separated media queries
|
|
354
|
+
// Only count commas that are outside of any parens
|
|
355
|
+
// (i.e., not part of function call params list, etc.)
|
|
356
|
+
if (character === "(") { levelLocal++; }
|
|
357
|
+
if (character === ")") { levelLocal--; }
|
|
358
|
+
if (levelLocal === 0 && character === ",") {
|
|
359
|
+
const mediaQueryString = string.substring(interimIndex, i);
|
|
360
|
+
const spaceBefore = /^(\s*)/.exec(mediaQueryString)[1];
|
|
361
|
+
result.push(new Container({
|
|
362
|
+
type: "media-query",
|
|
363
|
+
value: mediaQueryString.trim(),
|
|
364
|
+
sourceIndex: interimIndex + spaceBefore.length,
|
|
365
|
+
nodes: parseMediaQuery(mediaQueryString, interimIndex),
|
|
366
|
+
before: spaceBefore,
|
|
367
|
+
after: /(\s*)$/.exec(mediaQueryString)[1],
|
|
368
|
+
}));
|
|
369
|
+
interimIndex = i + 1;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const mediaQueryString = string.substring(interimIndex);
|
|
374
|
+
const spaceBefore = /^(\s*)/.exec(mediaQueryString)[1];
|
|
375
|
+
result.push(new Container({
|
|
376
|
+
type: "media-query",
|
|
377
|
+
value: mediaQueryString.trim(),
|
|
378
|
+
sourceIndex: interimIndex + spaceBefore.length,
|
|
379
|
+
nodes: parseMediaQuery(mediaQueryString, interimIndex),
|
|
380
|
+
before: spaceBefore,
|
|
381
|
+
after: /(\s*)$/.exec(mediaQueryString)[1],
|
|
382
|
+
}));
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function Container(opts) {
|
|
388
|
+
this.constructor(opts);
|
|
389
|
+
|
|
390
|
+
this.nodes = opts.nodes;
|
|
391
|
+
|
|
392
|
+
if (this.after === undefined) {
|
|
393
|
+
this.after = this.nodes.length > 0 ?
|
|
394
|
+
this.nodes[this.nodes.length - 1].after : "";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (this.before === undefined) {
|
|
398
|
+
this.before = this.nodes.length > 0 ?
|
|
399
|
+
this.nodes[0].before : "";
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (this.sourceIndex === undefined) {
|
|
403
|
+
this.sourceIndex = this.before.length;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
this.nodes.forEach(node => {
|
|
407
|
+
node.parent = this; // eslint-disable-line no-param-reassign
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
Container.prototype = Object.create(Node.prototype);
|
|
412
|
+
Container.constructor = Node;
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Iterate over descendant nodes of the node
|
|
416
|
+
*
|
|
417
|
+
* @param {RegExp|string} filter - Optional. Only nodes with node.type that
|
|
418
|
+
* satisfies the filter will be traversed over
|
|
419
|
+
* @param {function} cb - callback to call on each node. Takes these params:
|
|
420
|
+
* node - the node being processed, i - it's index, nodes - the array
|
|
421
|
+
* of all nodes
|
|
422
|
+
* If false is returned, the iteration breaks
|
|
423
|
+
*
|
|
424
|
+
* @return (boolean) false, if the iteration was broken
|
|
425
|
+
*/
|
|
426
|
+
Container.prototype.walk = function walk(filter, cb) {
|
|
427
|
+
const hasFilter = typeof filter === "string" || filter instanceof RegExp;
|
|
428
|
+
const callback = hasFilter ? cb : filter;
|
|
429
|
+
const filterReg = typeof filter === "string" ? new RegExp(filter) : filter;
|
|
430
|
+
|
|
431
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
432
|
+
const node = this.nodes[i];
|
|
433
|
+
const filtered = hasFilter ? filterReg.test(node.type) : true;
|
|
434
|
+
if (filtered && callback && callback(node, i, this.nodes) === false) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (node.nodes && node.walk(filter, cb) === false) { return false; }
|
|
438
|
+
}
|
|
439
|
+
return true;
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Iterate over immediate children of the node
|
|
444
|
+
*
|
|
445
|
+
* @param {function} cb - callback to call on each node. Takes these params:
|
|
446
|
+
* node - the node being processed, i - it's index, nodes - the array
|
|
447
|
+
* of all nodes
|
|
448
|
+
* If false is returned, the iteration breaks
|
|
449
|
+
*
|
|
450
|
+
* @return (boolean) false, if the iteration was broken
|
|
451
|
+
*/
|
|
452
|
+
Container.prototype.each = function each(cb = () => { }) {
|
|
453
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
454
|
+
const node = this.nodes[i];
|
|
455
|
+
if (cb(node, i, this.nodes) === false) { return false; }
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* A very generic node. Pretty much any element of a media query
|
|
462
|
+
*/
|
|
463
|
+
|
|
464
|
+
function Node(opts) {
|
|
465
|
+
this.after = opts.after;
|
|
466
|
+
this.before = opts.before;
|
|
467
|
+
this.type = opts.type;
|
|
468
|
+
this.value = opts.value;
|
|
469
|
+
this.sourceIndex = opts.sourceIndex;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export {
|
|
473
|
+
parseMediaList
|
|
474
|
+
};
|