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/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 = [ singleAttrAssign ],
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
- + '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')'
91
- + '\\s*(?:' + singleAttrValues.join('|') + '))?';
89
+ var pattern = singleAttrIdentifier.source +
90
+ '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')' +
91
+ '\\s*(?:' + singleAttrValues.join('|') + '))?';
92
92
  if (handler.customAttrSurround) {
93
93
  var attrClauses = [];
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
- + ')';
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( handler ) {
107
+ function joinSingleAttrAssigns(handler) {
108
108
  return singleAttrAssigns.concat(
109
109
  handler.customAttrAssign || []
110
- ).map(function (assign) {
110
+ ).map(function(assign) {
111
111
  return '(?:' + assign.source + ')';
112
112
  }).join('|');
113
113
  }
114
114
 
115
- function HTMLParser( html, handler ) {
115
+ function HTMLParser(html, handler) {
116
116
  var stack = [], lastTag;
117
117
  var attribute = attrForHandler(handler);
118
118
  var last, prevTag, nextTag;
119
- while ( html ) {
119
+ while (html) {
120
120
  last = html;
121
121
  // Make sure we're not in a script or style element
122
- if ( !lastTag || !special(lastTag) ) {
122
+ if (!lastTag || !special(lastTag)) {
123
123
  var textEnd = html.indexOf('<');
124
124
  if (textEnd === 0) {
125
125
  // Comment:
126
- if ( /^<!--/.test( html ) ) {
126
+ if (/^<!--/.test(html)) {
127
127
  var commentEnd = html.indexOf('-->');
128
128
 
129
- if ( commentEnd >= 0 ) {
130
- if ( handler.comment ) {
131
- handler.comment( html.substring( 4, commentEnd ) );
129
+ if (commentEnd >= 0) {
130
+ if (handler.comment) {
131
+ handler.comment(html.substring(4, commentEnd));
132
132
  }
133
- html = html.substring( commentEnd + 3 );
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 ( /^<!\[/.test( html ) ) {
140
+ if (/^<!\[/.test(html)) {
141
141
  var conditionalEnd = html.indexOf(']>');
142
142
 
143
143
  if (conditionalEnd >= 0) {
144
- if ( handler.comment ) {
145
- handler.comment( html.substring(2, conditionalEnd + 1 ), true /* non-standard */ );
144
+ if (handler.comment) {
145
+ handler.comment(html.substring(2, conditionalEnd + 1), true /* non-standard */);
146
146
  }
147
- html = html.substring( conditionalEnd + 2 );
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( doctype );
155
- if ( doctypeMatch ) {
156
- if ( handler.doctype ) {
157
- handler.doctype( doctypeMatch[0] );
154
+ var doctypeMatch = html.match(doctype);
155
+ if (doctypeMatch) {
156
+ if (handler.doctype) {
157
+ handler.doctype(doctypeMatch[0]);
158
158
  }
159
- html = html.substring( doctypeMatch[0].length );
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( endTag );
166
- if ( endTagMatch ) {
167
- html = html.substring( endTagMatch[0].length );
168
- endTagMatch[0].replace( endTag, parseEndTag );
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 ( startTagMatch ) {
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( 0, textEnd );
186
- html = html.substring( textEnd );
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( endTag );
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 ( handler.chars ) {
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]*?)<\/' + stackedTag + '[^>]*>', 'i'));
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 ( handler.chars ) {
226
- handler.chars( text );
225
+ if (handler.chars) {
226
+ handler.chars(text);
227
227
  }
228
228
 
229
229
  return '';
230
230
  });
231
231
 
232
- parseEndTag( '</' + stackedTag + '>', stackedTag );
232
+ parseEndTag('</' + stackedTag + '>', stackedTag);
233
233
  }
234
234
 
235
- if ( html === last ) {
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( '', lastTag );
271
+ parseEndTag('', lastTag);
272
272
  }
273
273
 
274
274
  if (!handler.html5) {
275
275
  while (lastTag && inline(lastTag)) {
276
- parseEndTag( '', lastTag );
276
+ parseEndTag('', lastTag);
277
277
  }
278
278
  }
279
279
 
280
- if ( closeSelf(tagName) && lastTag === tagName ) {
281
- parseEndTag( '', tagName );
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, fallbackValue, customOpen, customClose, customAssign, quote;
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] = undefined; }
293
- if (args[4] === '') { args[4] = undefined; }
294
- if (args[5] === '') { args[5] = undefined; }
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
- fallbackValue = args[j + 3];
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
- customAssign = args[j + 1];
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 ( !unary ) {
335
- stack.push( { tag: tagName, attrs: attrs } );
341
+ if (!unary) {
342
+ stack.push({ tag: tagName, attrs: attrs });
336
343
  lastTag = tagName;
337
344
  unarySlash = '';
338
345
  }
339
346
 
340
- if ( handler.start ) {
341
- handler.start( tagName, attrs, unary, unarySlash );
347
+ if (handler.start) {
348
+ handler.start(tagName, attrs, unary, unarySlash);
342
349
  }
343
350
  }
344
351
 
345
- function parseEndTag( tag, tagName ) {
352
+ function parseEndTag(tag, tagName) {
346
353
  var pos;
347
354
 
348
- // If no tag name is provided, clean shop
349
- if ( !tagName ) {
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 ( pos = stack.length - 1; pos >= 0; pos-- ) {
356
- if ( stack[ pos ].tag.toLowerCase() === needle ) {
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 ( pos >= 0 ) {
369
+ if (pos >= 0) {
363
370
  // Close all the open elements, up the stack
364
- for ( var i = stack.length - 1; i >= pos; i-- ) {
365
- if ( handler.end ) {
366
- handler.end( stack[ i ].tag, stack[ i ].attrs, i > pos || !tag );
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[ pos - 1 ].tag;
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( html ) {
398
+ exports.HTMLtoXML = function(html) {
392
399
  var results = '';
393
400
 
394
401
  new HTMLParser(html, {
395
- start: function( tag, attrs, unary ) {
402
+ start: function(tag, attrs, unary) {
396
403
  results += '<' + tag;
397
404
 
398
- for ( var i = 0; i < attrs.length; i++ ) {
405
+ for (var i = 0, len = attrs.length; i < len; i++) {
399
406
  results += ' ' + attrs[i].name + '="' + (attrs[i].value || '').replace(/"/g, '&#34;') + '"';
400
407
  }
401
408
 
402
409
  results += (unary ? '/' : '') + '>';
403
410
  },
404
- end: function( tag ) {
411
+ end: function(tag) {
405
412
  results += '</' + tag + '>';
406
413
  },
407
- chars: function( text ) {
414
+ chars: function(text) {
408
415
  results += text;
409
416
  },
410
- comment: function( text ) {
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( html, doc ) {
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 ( !doc ) {
437
- if ( typeof DOMDocument !== 'undefined' ) {
438
- doc = new DOMDocument();
439
- }
440
- else if ( typeof document !== 'undefined' && document.implementation && document.implementation.createDocument ) {
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 = doc.ownerDocument ||
450
- doc.getOwnerDocument && doc.getOwnerDocument() ||
451
- doc;
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 ( !documentElement && doc.createElement ) {
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( doc.createElement('title') );
465
- html.appendChild( head );
466
- html.appendChild( doc.createElement('body') );
467
- doc.appendChild( html );
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 ( doc.getElementsByTagName ) {
473
- for ( var i in one ) {
474
- one[ i ] = doc.getElementsByTagName( i )[0];
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( html, {
483
- start: function( tagName, attrs, unary ) {
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 ( one[ tagName ] ) {
487
- curParentNode = one[ tagName ];
488
+ if (one[tagName]) {
489
+ curParentNode = one[tagName];
488
490
  return;
489
491
  }
490
492
 
491
- var elem = doc.createElement( tagName );
493
+ var elem = doc.createElement(tagName);
492
494
 
493
- for ( var attr in attrs ) {
494
- elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
495
+ for (var attr in attrs) {
496
+ elem.setAttribute(attrs[attr].name, attrs[attr].value);
495
497
  }
496
498
 
497
- if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] !== 'boolean' ) {
498
- one[ structure[ tagName ] ].appendChild( elem );
499
+ if (structure[tagName] && typeof one[structure[tagName]] !== 'boolean') {
500
+ one[structure[tagName]].appendChild(elem);
499
501
  }
500
- else if ( curParentNode && curParentNode.appendChild ) {
501
- curParentNode.appendChild( elem );
502
+ else if (curParentNode && curParentNode.appendChild) {
503
+ curParentNode.appendChild(elem);
502
504
  }
503
505
 
504
- if ( !unary ) {
505
- elems.push( elem );
506
+ if (!unary) {
507
+ elems.push(elem);
506
508
  curParentNode = elem;
507
509
  }
508
510
  },
509
- end: function( /* tag */ ) {
511
+ end: function(/* tag */) {
510
512
  elems.length -= 1;
511
513
 
512
514
  // Init the new parentNode
513
- curParentNode = elems[ elems.length - 1 ];
515
+ curParentNode = elems[elems.length - 1];
514
516
  },
515
- chars: function( text ) {
516
- curParentNode.appendChild( doc.createTextNode( text ) );
517
+ chars: function(text) {
518
+ curParentNode.appendChild(doc.createTextNode(text));
517
519
  },
518
- comment: function( /* text */ ) {
520
+ comment: function(/* text */) {
519
521
  // create comment node
520
522
  },
521
- ignore: function( /* text */ ) {
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;