html-minifier 1.4.0 → 1.5.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/README.md +42 -34
- package/cli.js +218 -282
- package/package.json +4 -8
- package/sample-cli-config-file.conf +27 -18
- package/src/htmllint.js +67 -55
- package/src/htmlminifier.js +223 -114
- package/src/htmlparser.js +132 -130
- package/src/tokenchain.js +58 -0
- package/CHANGELOG.md +0 -120
- package/dist/htmlminifier.js +0 -32753
- package/dist/htmlminifier.min.js +0 -18
package/src/htmlparser.js
CHANGED
|
@@ -39,7 +39,7 @@ function makeMap(values) {
|
|
|
39
39
|
// Regular Expressions for parsing tags and attributes
|
|
40
40
|
var singleAttrIdentifier = /([^\s"'<>\/=]+)/,
|
|
41
41
|
singleAttrAssign = /=/,
|
|
42
|
-
singleAttrAssigns = [
|
|
42
|
+
singleAttrAssigns = [singleAttrAssign],
|
|
43
43
|
singleAttrValues = [
|
|
44
44
|
// attr value double quotes
|
|
45
45
|
/"([^"]*)"+/.source,
|
|
@@ -86,17 +86,17 @@ var nonPhrasing = makeMap('address,article,aside,base,blockquote,body,caption,co
|
|
|
86
86
|
var reCache = {};
|
|
87
87
|
|
|
88
88
|
function attrForHandler(handler) {
|
|
89
|
-
var pattern = singleAttrIdentifier.source
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
var pattern = singleAttrIdentifier.source +
|
|
90
|
+
'(?:\\s*(' + joinSingleAttrAssigns(handler) + ')' +
|
|
91
|
+
'\\s*(?:' + singleAttrValues.join('|') + '))?';
|
|
92
92
|
if (handler.customAttrSurround) {
|
|
93
93
|
var attrClauses = [];
|
|
94
|
-
for (
|
|
95
|
-
attrClauses[i] = '(?:'
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
for (var i = handler.customAttrSurround.length - 1; i >= 0; i--) {
|
|
95
|
+
attrClauses[i] = '(?:' +
|
|
96
|
+
'(' + handler.customAttrSurround[i][0].source + ')\\s*' +
|
|
97
|
+
pattern +
|
|
98
|
+
'\\s*(' + handler.customAttrSurround[i][1].source + ')' +
|
|
99
|
+
')';
|
|
100
100
|
}
|
|
101
101
|
attrClauses.push('(?:' + pattern + ')');
|
|
102
102
|
pattern = '(?:' + attrClauses.join('|') + ')';
|
|
@@ -104,75 +104,75 @@ function attrForHandler(handler) {
|
|
|
104
104
|
return new RegExp('^\\s*' + pattern);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function joinSingleAttrAssigns(
|
|
107
|
+
function joinSingleAttrAssigns(handler) {
|
|
108
108
|
return singleAttrAssigns.concat(
|
|
109
109
|
handler.customAttrAssign || []
|
|
110
|
-
).map(function
|
|
110
|
+
).map(function(assign) {
|
|
111
111
|
return '(?:' + assign.source + ')';
|
|
112
112
|
}).join('|');
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
function HTMLParser(
|
|
115
|
+
function HTMLParser(html, handler) {
|
|
116
116
|
var stack = [], lastTag;
|
|
117
117
|
var attribute = attrForHandler(handler);
|
|
118
118
|
var last, prevTag, nextTag;
|
|
119
|
-
while (
|
|
119
|
+
while (html) {
|
|
120
120
|
last = html;
|
|
121
121
|
// Make sure we're not in a script or style element
|
|
122
|
-
if (
|
|
122
|
+
if (!lastTag || !special(lastTag)) {
|
|
123
123
|
var textEnd = html.indexOf('<');
|
|
124
124
|
if (textEnd === 0) {
|
|
125
125
|
// Comment:
|
|
126
|
-
if (
|
|
126
|
+
if (/^<!--/.test(html)) {
|
|
127
127
|
var commentEnd = html.indexOf('-->');
|
|
128
128
|
|
|
129
|
-
if (
|
|
130
|
-
if (
|
|
131
|
-
handler.comment(
|
|
129
|
+
if (commentEnd >= 0) {
|
|
130
|
+
if (handler.comment) {
|
|
131
|
+
handler.comment(html.substring(4, commentEnd));
|
|
132
132
|
}
|
|
133
|
-
html = html.substring(
|
|
133
|
+
html = html.substring(commentEnd + 3);
|
|
134
134
|
prevTag = '';
|
|
135
135
|
continue;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
|
|
140
|
-
if (
|
|
140
|
+
if (/^<!\[/.test(html)) {
|
|
141
141
|
var conditionalEnd = html.indexOf(']>');
|
|
142
142
|
|
|
143
143
|
if (conditionalEnd >= 0) {
|
|
144
|
-
if (
|
|
145
|
-
handler.comment(
|
|
144
|
+
if (handler.comment) {
|
|
145
|
+
handler.comment(html.substring(2, conditionalEnd + 1), true /* non-standard */);
|
|
146
146
|
}
|
|
147
|
-
html = html.substring(
|
|
147
|
+
html = html.substring(conditionalEnd + 2);
|
|
148
148
|
prevTag = '';
|
|
149
149
|
continue;
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// Doctype:
|
|
154
|
-
var doctypeMatch = html.match(
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
157
|
-
handler.doctype(
|
|
154
|
+
var doctypeMatch = html.match(doctype);
|
|
155
|
+
if (doctypeMatch) {
|
|
156
|
+
if (handler.doctype) {
|
|
157
|
+
handler.doctype(doctypeMatch[0]);
|
|
158
158
|
}
|
|
159
|
-
html = html.substring(
|
|
159
|
+
html = html.substring(doctypeMatch[0].length);
|
|
160
160
|
prevTag = '';
|
|
161
161
|
continue;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
// End tag:
|
|
165
|
-
var endTagMatch = html.match(
|
|
166
|
-
if (
|
|
167
|
-
html = html.substring(
|
|
168
|
-
endTagMatch[0].replace(
|
|
165
|
+
var endTagMatch = html.match(endTag);
|
|
166
|
+
if (endTagMatch) {
|
|
167
|
+
html = html.substring(endTagMatch[0].length);
|
|
168
|
+
endTagMatch[0].replace(endTag, parseEndTag);
|
|
169
169
|
prevTag = '/' + endTagMatch[1].toLowerCase();
|
|
170
170
|
continue;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
// Start tag:
|
|
174
174
|
var startTagMatch = parseStartTag(html);
|
|
175
|
-
if (
|
|
175
|
+
if (startTagMatch) {
|
|
176
176
|
html = startTagMatch.rest;
|
|
177
177
|
handleStartTag(startTagMatch);
|
|
178
178
|
prevTag = startTagMatch.tagName.toLowerCase();
|
|
@@ -182,8 +182,8 @@ function HTMLParser( html, handler ) {
|
|
|
182
182
|
|
|
183
183
|
var text;
|
|
184
184
|
if (textEnd >= 0) {
|
|
185
|
-
text = html.substring(
|
|
186
|
-
html = html.substring(
|
|
185
|
+
text = html.substring(0, textEnd);
|
|
186
|
+
html = html.substring(textEnd);
|
|
187
187
|
}
|
|
188
188
|
else {
|
|
189
189
|
text = html;
|
|
@@ -196,7 +196,7 @@ function HTMLParser( html, handler ) {
|
|
|
196
196
|
nextTag = nextTagMatch.tagName;
|
|
197
197
|
}
|
|
198
198
|
else {
|
|
199
|
-
nextTagMatch = html.match(
|
|
199
|
+
nextTagMatch = html.match(endTag);
|
|
200
200
|
if (nextTagMatch) {
|
|
201
201
|
nextTag = '/' + nextTagMatch[1];
|
|
202
202
|
}
|
|
@@ -205,7 +205,7 @@ function HTMLParser( html, handler ) {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
if (
|
|
208
|
+
if (handler.chars) {
|
|
209
209
|
handler.chars(text, prevTag, nextTag);
|
|
210
210
|
}
|
|
211
211
|
prevTag = '';
|
|
@@ -213,7 +213,7 @@ function HTMLParser( html, handler ) {
|
|
|
213
213
|
}
|
|
214
214
|
else {
|
|
215
215
|
var stackedTag = lastTag.toLowerCase();
|
|
216
|
-
var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)
|
|
216
|
+
var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)</' + stackedTag + '[^>]*>', 'i'));
|
|
217
217
|
|
|
218
218
|
html = html.replace(reStackedTag, function(all, text) {
|
|
219
219
|
if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
|
|
@@ -222,17 +222,17 @@ function HTMLParser( html, handler ) {
|
|
|
222
222
|
.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1');
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
if (
|
|
226
|
-
handler.chars(
|
|
225
|
+
if (handler.chars) {
|
|
226
|
+
handler.chars(text);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
return '';
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
-
parseEndTag(
|
|
232
|
+
parseEndTag('</' + stackedTag + '>', stackedTag);
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
if (
|
|
235
|
+
if (html === last) {
|
|
236
236
|
throw new Error('Parse Error: ' + html);
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -268,41 +268,55 @@ function HTMLParser( html, handler ) {
|
|
|
268
268
|
var unarySlash = match.unarySlash;
|
|
269
269
|
|
|
270
270
|
if (handler.html5 && lastTag === 'p' && nonPhrasing(tagName)) {
|
|
271
|
-
parseEndTag(
|
|
271
|
+
parseEndTag('', lastTag);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
if (!handler.html5) {
|
|
275
275
|
while (lastTag && inline(lastTag)) {
|
|
276
|
-
parseEndTag(
|
|
276
|
+
parseEndTag('', lastTag);
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
if (
|
|
281
|
-
parseEndTag(
|
|
280
|
+
if (closeSelf(tagName) && lastTag === tagName) {
|
|
281
|
+
parseEndTag('', tagName);
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
var unary = empty(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash;
|
|
285
285
|
|
|
286
286
|
var attrs = match.attrs.map(function(args) {
|
|
287
|
-
var name, value,
|
|
287
|
+
var name, value, customOpen, customClose, customAssign, quote;
|
|
288
288
|
var ncp = 7; // number of captured parts, scalar
|
|
289
289
|
|
|
290
290
|
// hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
|
|
291
291
|
if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {
|
|
292
|
-
if (args[3] === '') { args[3]
|
|
293
|
-
if (args[4] === '') { args[4]
|
|
294
|
-
if (args[5] === '') { args[5]
|
|
292
|
+
if (args[3] === '') { delete args[3]; }
|
|
293
|
+
if (args[4] === '') { delete args[4]; }
|
|
294
|
+
if (args[5] === '') { delete args[5]; }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function populate(index) {
|
|
298
|
+
customAssign = args[index];
|
|
299
|
+
value = args[index + 1];
|
|
300
|
+
if (typeof value !== 'undefined') {
|
|
301
|
+
return '"';
|
|
302
|
+
}
|
|
303
|
+
value = args[index + 2];
|
|
304
|
+
if (typeof value !== 'undefined') {
|
|
305
|
+
return '\'';
|
|
306
|
+
}
|
|
307
|
+
value = args[index + 3];
|
|
308
|
+
if (typeof value === 'undefined' && fillAttrs(name)) {
|
|
309
|
+
value = name;
|
|
310
|
+
}
|
|
311
|
+
return '';
|
|
295
312
|
}
|
|
296
313
|
|
|
297
314
|
var j = 1;
|
|
298
315
|
if (handler.customAttrSurround) {
|
|
299
316
|
for (var i = 0, l = handler.customAttrSurround.length; i < l; i++, j += ncp) {
|
|
300
317
|
name = args[j + 1];
|
|
301
|
-
customAssign = args[j + 2];
|
|
302
318
|
if (name) {
|
|
303
|
-
|
|
304
|
-
value = fallbackValue || args[j + 4] || args[j + 5];
|
|
305
|
-
quote = fallbackValue ? '"' : value ? '\'' : '';
|
|
319
|
+
quote = populate(j + 2);
|
|
306
320
|
customOpen = args[j];
|
|
307
321
|
customClose = args[j + 6];
|
|
308
322
|
break;
|
|
@@ -311,14 +325,7 @@ function HTMLParser( html, handler ) {
|
|
|
311
325
|
}
|
|
312
326
|
|
|
313
327
|
if (!name && (name = args[j])) {
|
|
314
|
-
|
|
315
|
-
fallbackValue = args[j + 2];
|
|
316
|
-
value = fallbackValue || args[j + 3] || args[j + 4];
|
|
317
|
-
quote = fallbackValue ? '"' : value ? '\'' : '';
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (value === undefined) {
|
|
321
|
-
value = fillAttrs(name) ? name : fallbackValue;
|
|
328
|
+
quote = populate(j + 1);
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
return {
|
|
@@ -331,45 +338,45 @@ function HTMLParser( html, handler ) {
|
|
|
331
338
|
};
|
|
332
339
|
});
|
|
333
340
|
|
|
334
|
-
if (
|
|
335
|
-
stack.push(
|
|
341
|
+
if (!unary) {
|
|
342
|
+
stack.push({ tag: tagName, attrs: attrs });
|
|
336
343
|
lastTag = tagName;
|
|
337
344
|
unarySlash = '';
|
|
338
345
|
}
|
|
339
346
|
|
|
340
|
-
if (
|
|
341
|
-
handler.start(
|
|
347
|
+
if (handler.start) {
|
|
348
|
+
handler.start(tagName, attrs, unary, unarySlash);
|
|
342
349
|
}
|
|
343
350
|
}
|
|
344
351
|
|
|
345
|
-
function parseEndTag(
|
|
352
|
+
function parseEndTag(tag, tagName) {
|
|
346
353
|
var pos;
|
|
347
354
|
|
|
348
|
-
//
|
|
349
|
-
if (
|
|
350
|
-
pos = 0;
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
// Find the closest opened tag of the same type
|
|
355
|
+
// Find the closest opened tag of the same type
|
|
356
|
+
if (tagName) {
|
|
354
357
|
var needle = tagName.toLowerCase();
|
|
355
|
-
for (
|
|
356
|
-
if (
|
|
358
|
+
for (pos = stack.length - 1; pos >= 0; pos--) {
|
|
359
|
+
if (stack[pos].tag.toLowerCase() === needle) {
|
|
357
360
|
break;
|
|
358
361
|
}
|
|
359
362
|
}
|
|
360
363
|
}
|
|
364
|
+
// If no tag name is provided, clean shop
|
|
365
|
+
else {
|
|
366
|
+
pos = 0;
|
|
367
|
+
}
|
|
361
368
|
|
|
362
|
-
if (
|
|
369
|
+
if (pos >= 0) {
|
|
363
370
|
// Close all the open elements, up the stack
|
|
364
|
-
for (
|
|
365
|
-
if (
|
|
366
|
-
handler.end(
|
|
371
|
+
for (var i = stack.length - 1; i >= pos; i--) {
|
|
372
|
+
if (handler.end) {
|
|
373
|
+
handler.end(stack[i].tag, stack[i].attrs, i > pos || !tag);
|
|
367
374
|
}
|
|
368
375
|
}
|
|
369
376
|
|
|
370
377
|
// Remove the open elements from the stack
|
|
371
378
|
stack.length = pos;
|
|
372
|
-
lastTag = pos && stack[
|
|
379
|
+
lastTag = pos && stack[pos - 1].tag;
|
|
373
380
|
}
|
|
374
381
|
else if (tagName.toLowerCase() === 'br') {
|
|
375
382
|
if (handler.start) {
|
|
@@ -388,26 +395,26 @@ function HTMLParser( html, handler ) {
|
|
|
388
395
|
}
|
|
389
396
|
|
|
390
397
|
exports.HTMLParser = HTMLParser;
|
|
391
|
-
exports.HTMLtoXML = function(
|
|
398
|
+
exports.HTMLtoXML = function(html) {
|
|
392
399
|
var results = '';
|
|
393
400
|
|
|
394
401
|
new HTMLParser(html, {
|
|
395
|
-
start: function(
|
|
402
|
+
start: function(tag, attrs, unary) {
|
|
396
403
|
results += '<' + tag;
|
|
397
404
|
|
|
398
|
-
for (
|
|
405
|
+
for (var i = 0, len = attrs.length; i < len; i++) {
|
|
399
406
|
results += ' ' + attrs[i].name + '="' + (attrs[i].value || '').replace(/"/g, '"') + '"';
|
|
400
407
|
}
|
|
401
408
|
|
|
402
409
|
results += (unary ? '/' : '') + '>';
|
|
403
410
|
},
|
|
404
|
-
end: function(
|
|
411
|
+
end: function(tag) {
|
|
405
412
|
results += '</' + tag + '>';
|
|
406
413
|
},
|
|
407
|
-
chars: function(
|
|
414
|
+
chars: function(text) {
|
|
408
415
|
results += text;
|
|
409
416
|
},
|
|
410
|
-
comment: function(
|
|
417
|
+
comment: function(text) {
|
|
411
418
|
results += '<!--' + text + '-->';
|
|
412
419
|
},
|
|
413
420
|
ignore: function(text) {
|
|
@@ -418,7 +425,7 @@ exports.HTMLtoXML = function( html ) {
|
|
|
418
425
|
return results;
|
|
419
426
|
};
|
|
420
427
|
|
|
421
|
-
exports.HTMLtoDOM = function(
|
|
428
|
+
exports.HTMLtoDOM = function(html, doc) {
|
|
422
429
|
// There can be only one of these elements
|
|
423
430
|
var one = {
|
|
424
431
|
html: true,
|
|
@@ -433,22 +440,17 @@ exports.HTMLtoDOM = function( html, doc ) {
|
|
|
433
440
|
base: 'head'
|
|
434
441
|
};
|
|
435
442
|
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
doc = document.implementation.createDocument('', '', null);
|
|
442
|
-
}
|
|
443
|
-
else if ( typeof ActiveX !== 'undefined' ) {
|
|
444
|
-
doc = new ActiveXObject('Msxml.DOMDocument');
|
|
445
|
-
}
|
|
446
|
-
|
|
443
|
+
if (doc) {
|
|
444
|
+
doc = doc.ownerDocument || doc.getOwnerDocument && doc.getOwnerDocument() || doc;
|
|
445
|
+
}
|
|
446
|
+
else if (typeof DOMDocument !== 'undefined') {
|
|
447
|
+
doc = new DOMDocument();
|
|
447
448
|
}
|
|
448
|
-
else {
|
|
449
|
-
doc =
|
|
450
|
-
|
|
451
|
-
|
|
449
|
+
else if (typeof document !== 'undefined' && document.implementation && document.implementation.createDocument) {
|
|
450
|
+
doc = document.implementation.createDocument('', '', null);
|
|
451
|
+
}
|
|
452
|
+
else if (typeof ActiveX !== 'undefined') {
|
|
453
|
+
doc = new ActiveXObject('Msxml.DOMDocument');
|
|
452
454
|
}
|
|
453
455
|
|
|
454
456
|
var elems = [],
|
|
@@ -457,21 +459,21 @@ exports.HTMLtoDOM = function( html, doc ) {
|
|
|
457
459
|
|
|
458
460
|
// If we're dealing with an empty document then we
|
|
459
461
|
// need to pre-populate it with the HTML document structure
|
|
460
|
-
if (
|
|
462
|
+
if (!documentElement && doc.createElement) {
|
|
461
463
|
(function() {
|
|
462
464
|
var html = doc.createElement('html');
|
|
463
465
|
var head = doc.createElement('head');
|
|
464
|
-
head.appendChild(
|
|
465
|
-
html.appendChild(
|
|
466
|
-
html.appendChild(
|
|
467
|
-
doc.appendChild(
|
|
466
|
+
head.appendChild(doc.createElement('title'));
|
|
467
|
+
html.appendChild(head);
|
|
468
|
+
html.appendChild(doc.createElement('body'));
|
|
469
|
+
doc.appendChild(html);
|
|
468
470
|
})();
|
|
469
471
|
}
|
|
470
472
|
|
|
471
473
|
// Find all the unique elements
|
|
472
|
-
if (
|
|
473
|
-
for (
|
|
474
|
-
one[
|
|
474
|
+
if (doc.getElementsByTagName) {
|
|
475
|
+
for (var i in one) {
|
|
476
|
+
one[i] = doc.getElementsByTagName(i)[0];
|
|
475
477
|
}
|
|
476
478
|
}
|
|
477
479
|
|
|
@@ -479,46 +481,46 @@ exports.HTMLtoDOM = function( html, doc ) {
|
|
|
479
481
|
// the body element
|
|
480
482
|
var curParentNode = one.body;
|
|
481
483
|
|
|
482
|
-
new HTMLParser(
|
|
483
|
-
start: function(
|
|
484
|
+
new HTMLParser(html, {
|
|
485
|
+
start: function(tagName, attrs, unary) {
|
|
484
486
|
// If it's a pre-built element, then we can ignore
|
|
485
487
|
// its construction
|
|
486
|
-
if (
|
|
487
|
-
curParentNode = one[
|
|
488
|
+
if (one[tagName]) {
|
|
489
|
+
curParentNode = one[tagName];
|
|
488
490
|
return;
|
|
489
491
|
}
|
|
490
492
|
|
|
491
|
-
var elem = doc.createElement(
|
|
493
|
+
var elem = doc.createElement(tagName);
|
|
492
494
|
|
|
493
|
-
for (
|
|
494
|
-
elem.setAttribute(
|
|
495
|
+
for (var attr in attrs) {
|
|
496
|
+
elem.setAttribute(attrs[attr].name, attrs[attr].value);
|
|
495
497
|
}
|
|
496
498
|
|
|
497
|
-
if (
|
|
498
|
-
one[
|
|
499
|
+
if (structure[tagName] && typeof one[structure[tagName]] !== 'boolean') {
|
|
500
|
+
one[structure[tagName]].appendChild(elem);
|
|
499
501
|
}
|
|
500
|
-
else if (
|
|
501
|
-
curParentNode.appendChild(
|
|
502
|
+
else if (curParentNode && curParentNode.appendChild) {
|
|
503
|
+
curParentNode.appendChild(elem);
|
|
502
504
|
}
|
|
503
505
|
|
|
504
|
-
if (
|
|
505
|
-
elems.push(
|
|
506
|
+
if (!unary) {
|
|
507
|
+
elems.push(elem);
|
|
506
508
|
curParentNode = elem;
|
|
507
509
|
}
|
|
508
510
|
},
|
|
509
|
-
end: function(
|
|
511
|
+
end: function(/* tag */) {
|
|
510
512
|
elems.length -= 1;
|
|
511
513
|
|
|
512
514
|
// Init the new parentNode
|
|
513
|
-
curParentNode = elems[
|
|
515
|
+
curParentNode = elems[elems.length - 1];
|
|
514
516
|
},
|
|
515
|
-
chars: function(
|
|
516
|
-
curParentNode.appendChild(
|
|
517
|
+
chars: function(text) {
|
|
518
|
+
curParentNode.appendChild(doc.createTextNode(text));
|
|
517
519
|
},
|
|
518
|
-
comment: function(
|
|
520
|
+
comment: function(/* text */) {
|
|
519
521
|
// create comment node
|
|
520
522
|
},
|
|
521
|
-
ignore: function(
|
|
523
|
+
ignore: function(/* text */) {
|
|
522
524
|
// What to do here?
|
|
523
525
|
}
|
|
524
526
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function Sorter(tokens) {
|
|
4
|
+
this.tokens = tokens;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
Sorter.prototype.sort = function(tokens, fromIndex) {
|
|
8
|
+
fromIndex = fromIndex || 0;
|
|
9
|
+
for (var i = 0, len = this.tokens.length; i < len; i++) {
|
|
10
|
+
var token = this.tokens[i];
|
|
11
|
+
var index = tokens.indexOf(token, fromIndex);
|
|
12
|
+
if (index !== -1) {
|
|
13
|
+
do {
|
|
14
|
+
if (index !== fromIndex) {
|
|
15
|
+
tokens.splice(index, 1);
|
|
16
|
+
tokens.splice(fromIndex, 0, token);
|
|
17
|
+
}
|
|
18
|
+
fromIndex++;
|
|
19
|
+
} while ((index = tokens.indexOf(token, fromIndex)) !== -1);
|
|
20
|
+
return this[token].sort(tokens, fromIndex);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return tokens;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function TokenChain() {
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
TokenChain.prototype = {
|
|
30
|
+
add: function(tokens) {
|
|
31
|
+
var self = this;
|
|
32
|
+
tokens.forEach(function(token) {
|
|
33
|
+
(self[token] || (self[token] = [])).push(tokens);
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
createSorter: function() {
|
|
37
|
+
var self = this;
|
|
38
|
+
var sorter = new Sorter(Object.keys(this).sort(function(j, k) {
|
|
39
|
+
var m = self[j].length;
|
|
40
|
+
var n = self[k].length;
|
|
41
|
+
return m < n ? 1 : m > n ? -1 : j < k ? -1 : j > k ? 1 : 0;
|
|
42
|
+
}));
|
|
43
|
+
sorter.tokens.forEach(function(token) {
|
|
44
|
+
var chain = new TokenChain();
|
|
45
|
+
self[token].forEach(function(tokens) {
|
|
46
|
+
var index;
|
|
47
|
+
while ((index = tokens.indexOf(token)) !== -1) {
|
|
48
|
+
tokens.splice(index, 1);
|
|
49
|
+
}
|
|
50
|
+
chain.add(tokens.slice(0));
|
|
51
|
+
});
|
|
52
|
+
sorter[token] = chain.createSorter();
|
|
53
|
+
});
|
|
54
|
+
return sorter;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = TokenChain;
|