html-minifier 1.3.1 → 1.4.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
@@ -28,511 +28,500 @@
28
28
 
29
29
  /* global ActiveXObject, DOMDocument */
30
30
 
31
- (function(global) {
32
- 'use strict';
33
-
34
- // Regular Expressions for parsing tags and attributes
35
- var singleAttrIdentifier = /([^\s"'<>\/=]+)/,
36
- singleAttrAssign = /=/,
37
- singleAttrAssigns = [ singleAttrAssign ],
38
- singleAttrValues = [
39
- // attr value double quotes
40
- /"([^"]*)"+/.source,
41
- // attr value, single quotes
42
- /'([^']*)'+/.source,
43
- // attr value, no quotes
44
- /([^\s"'=<>`]+)/.source
45
- ],
46
- // https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
47
- qnameCapture = (function() {
48
- var ncname;
49
- if (typeof require === 'function') {
50
- ncname = require('ncname');
51
- }
52
- else {
53
- ncname = global.NCName;
54
- }
55
- if (ncname) {
56
- ncname = ncname.source.slice(1, -1);
57
- }
58
- else {
59
- ncname = '[:A-Za-z_][:\\w\\-\\.]*';
60
- }
61
- return '((?:' + ncname + '\\:)?' + ncname + ')';
62
- })(),
63
- startTagOpen = new RegExp('^<' + qnameCapture),
64
- startTagClose = /^\s*(\/?)>/,
65
- endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>'),
66
- doctype = /^<!DOCTYPE [^>]+>/i;
67
-
68
- var IS_REGEX_CAPTURING_BROKEN = false;
69
- 'x'.replace(/x(.)?/g, function(m, g) {
70
- IS_REGEX_CAPTURING_BROKEN = g === '';
71
- });
72
-
73
- // Empty Elements
74
- var empty = makeMap('area,base,basefont,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr');
75
-
76
- // Inline Elements
77
- var inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,noscript,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,svg,textarea,tt,u,var');
78
-
79
- // Elements that you can, intentionally, leave open
80
- // (and which close themselves)
81
- var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source');
82
-
83
- // Attributes that have their values filled in disabled='disabled'
84
- var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected');
85
-
86
- // Special Elements (can contain anything)
87
- var special = makeMap('script,style');
88
-
89
- // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3
90
- // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
91
- var nonPhrasing = makeMap('address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track');
92
-
93
- var reCache = {};
94
-
95
- function attrForHandler(handler) {
96
- var pattern = singleAttrIdentifier.source
97
- + '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')'
98
- + '\\s*(?:' + singleAttrValues.join('|') + '))?';
99
- if (handler.customAttrSurround) {
100
- var attrClauses = [];
101
- for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) {
102
- attrClauses[i] = '(?:'
103
- + '(' + handler.customAttrSurround[i][0].source + ')\\s*'
104
- + pattern
105
- + '\\s*(' + handler.customAttrSurround[i][1].source + ')'
106
- + ')';
107
- }
108
- attrClauses.push('(?:' + pattern + ')');
109
- pattern = '(?:' + attrClauses.join('|') + ')';
31
+ 'use strict';
32
+
33
+ var createMapFromString = require('./utils').createMapFromString;
34
+
35
+ function makeMap(values) {
36
+ return createMapFromString(values, true);
37
+ }
38
+
39
+ // Regular Expressions for parsing tags and attributes
40
+ var singleAttrIdentifier = /([^\s"'<>\/=]+)/,
41
+ singleAttrAssign = /=/,
42
+ singleAttrAssigns = [ singleAttrAssign ],
43
+ singleAttrValues = [
44
+ // attr value double quotes
45
+ /"([^"]*)"+/.source,
46
+ // attr value, single quotes
47
+ /'([^']*)'+/.source,
48
+ // attr value, no quotes
49
+ /([^\s"'=<>`]+)/.source
50
+ ],
51
+ // https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
52
+ qnameCapture = (function() {
53
+ var ncname = require('ncname').source.slice(1, -1);
54
+ return '((?:' + ncname + '\\:)?' + ncname + ')';
55
+ })(),
56
+ startTagOpen = new RegExp('^<' + qnameCapture),
57
+ startTagClose = /^\s*(\/?)>/,
58
+ endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>'),
59
+ doctype = /^<!DOCTYPE [^>]+>/i;
60
+
61
+ var IS_REGEX_CAPTURING_BROKEN = false;
62
+ 'x'.replace(/x(.)?/g, function(m, g) {
63
+ IS_REGEX_CAPTURING_BROKEN = g === '';
64
+ });
65
+
66
+ // Empty Elements
67
+ var empty = makeMap('area,base,basefont,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr');
68
+
69
+ // Inline Elements
70
+ var inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,noscript,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,svg,textarea,tt,u,var');
71
+
72
+ // Elements that you can, intentionally, leave open
73
+ // (and which close themselves)
74
+ var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source');
75
+
76
+ // Attributes that have their values filled in disabled='disabled'
77
+ var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected');
78
+
79
+ // Special Elements (can contain anything)
80
+ var special = makeMap('script,style');
81
+
82
+ // HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3
83
+ // Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
84
+ var nonPhrasing = makeMap('address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track');
85
+
86
+ var reCache = {};
87
+
88
+ function attrForHandler(handler) {
89
+ var pattern = singleAttrIdentifier.source
90
+ + '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')'
91
+ + '\\s*(?:' + singleAttrValues.join('|') + '))?';
92
+ if (handler.customAttrSurround) {
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
+ + ')';
110
100
  }
111
- return new RegExp('^\\s*' + pattern);
112
- }
113
-
114
- function joinSingleAttrAssigns( handler ) {
115
- return singleAttrAssigns.concat(
116
- handler.customAttrAssign || []
117
- ).map(function (assign) {
118
- return '(?:' + assign.source + ')';
119
- }).join('|');
101
+ attrClauses.push('(?:' + pattern + ')');
102
+ pattern = '(?:' + attrClauses.join('|') + ')';
120
103
  }
121
-
122
- var HTMLParser = global.HTMLParser = function( html, handler ) {
123
- var stack = [], lastTag;
124
- var attribute = attrForHandler(handler);
125
- var last, prevTag, nextTag;
126
- while ( html ) {
127
- last = html;
128
- // Make sure we're not in a script or style element
129
- if ( !lastTag || !special[ lastTag ] ) {
130
- var textEnd = html.indexOf('<');
131
- if (textEnd === 0) {
132
- // Comment:
133
- if ( /^<!--/.test( html ) ) {
134
- var commentEnd = html.indexOf('-->');
135
-
136
- if ( commentEnd >= 0 ) {
137
- if ( handler.comment ) {
138
- handler.comment( html.substring( 4, commentEnd ) );
139
- }
140
- html = html.substring( commentEnd + 3 );
141
- prevTag = '';
142
- continue;
104
+ return new RegExp('^\\s*' + pattern);
105
+ }
106
+
107
+ function joinSingleAttrAssigns( handler ) {
108
+ return singleAttrAssigns.concat(
109
+ handler.customAttrAssign || []
110
+ ).map(function (assign) {
111
+ return '(?:' + assign.source + ')';
112
+ }).join('|');
113
+ }
114
+
115
+ function HTMLParser( html, handler ) {
116
+ var stack = [], lastTag;
117
+ var attribute = attrForHandler(handler);
118
+ var last, prevTag, nextTag;
119
+ while ( html ) {
120
+ last = html;
121
+ // Make sure we're not in a script or style element
122
+ if ( !lastTag || !special(lastTag) ) {
123
+ var textEnd = html.indexOf('<');
124
+ if (textEnd === 0) {
125
+ // Comment:
126
+ if ( /^<!--/.test( html ) ) {
127
+ var commentEnd = html.indexOf('-->');
128
+
129
+ if ( commentEnd >= 0 ) {
130
+ if ( handler.comment ) {
131
+ handler.comment( html.substring( 4, commentEnd ) );
143
132
  }
133
+ html = html.substring( commentEnd + 3 );
134
+ prevTag = '';
135
+ continue;
144
136
  }
137
+ }
145
138
 
146
- // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
147
- if ( /^<!\[/.test( html ) ) {
148
- var conditionalEnd = html.indexOf(']>');
149
-
150
- if (conditionalEnd >= 0) {
151
- if ( handler.comment ) {
152
- handler.comment( html.substring(2, conditionalEnd + 1 ), true /* non-standard */ );
153
- }
154
- html = html.substring( conditionalEnd + 2 );
155
- prevTag = '';
156
- continue;
157
- }
158
- }
139
+ // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
140
+ if ( /^<!\[/.test( html ) ) {
141
+ var conditionalEnd = html.indexOf(']>');
159
142
 
160
- // Doctype:
161
- var doctypeMatch = html.match( doctype );
162
- if ( doctypeMatch ) {
163
- if ( handler.doctype ) {
164
- handler.doctype( doctypeMatch[0] );
143
+ if (conditionalEnd >= 0) {
144
+ if ( handler.comment ) {
145
+ handler.comment( html.substring(2, conditionalEnd + 1 ), true /* non-standard */ );
165
146
  }
166
- html = html.substring( doctypeMatch[0].length );
147
+ html = html.substring( conditionalEnd + 2 );
167
148
  prevTag = '';
168
149
  continue;
169
150
  }
151
+ }
170
152
 
171
- // End tag:
172
- var endTagMatch = html.match( endTag );
173
- if ( endTagMatch ) {
174
- html = html.substring( endTagMatch[0].length );
175
- endTagMatch[0].replace( endTag, parseEndTag );
176
- prevTag = '/' + endTagMatch[1].toLowerCase();
177
- continue;
178
- }
179
-
180
- // Start tag:
181
- var startTagMatch = parseStartTag(html);
182
- if ( startTagMatch ) {
183
- html = startTagMatch.rest;
184
- handleStartTag(startTagMatch);
185
- prevTag = startTagMatch.tagName.toLowerCase();
186
- continue;
153
+ // Doctype:
154
+ var doctypeMatch = html.match( doctype );
155
+ if ( doctypeMatch ) {
156
+ if ( handler.doctype ) {
157
+ handler.doctype( doctypeMatch[0] );
187
158
  }
159
+ html = html.substring( doctypeMatch[0].length );
160
+ prevTag = '';
161
+ continue;
188
162
  }
189
163
 
190
- var text;
191
- if (textEnd >= 0) {
192
- text = html.substring( 0, textEnd );
193
- html = html.substring( textEnd );
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 );
169
+ prevTag = '/' + endTagMatch[1].toLowerCase();
170
+ continue;
194
171
  }
195
- else {
196
- text = html;
197
- html = '';
172
+
173
+ // Start tag:
174
+ var startTagMatch = parseStartTag(html);
175
+ if ( startTagMatch ) {
176
+ html = startTagMatch.rest;
177
+ handleStartTag(startTagMatch);
178
+ prevTag = startTagMatch.tagName.toLowerCase();
179
+ continue;
198
180
  }
181
+ }
199
182
 
200
- // next tag
201
- var nextTagMatch = parseStartTag(html);
183
+ var text;
184
+ if (textEnd >= 0) {
185
+ text = html.substring( 0, textEnd );
186
+ html = html.substring( textEnd );
187
+ }
188
+ else {
189
+ text = html;
190
+ html = '';
191
+ }
192
+
193
+ // next tag
194
+ var nextTagMatch = parseStartTag(html);
195
+ if (nextTagMatch) {
196
+ nextTag = nextTagMatch.tagName;
197
+ }
198
+ else {
199
+ nextTagMatch = html.match( endTag );
202
200
  if (nextTagMatch) {
203
- nextTag = nextTagMatch.tagName;
201
+ nextTag = '/' + nextTagMatch[1];
204
202
  }
205
203
  else {
206
- nextTagMatch = html.match( endTag );
207
- if (nextTagMatch) {
208
- nextTag = '/' + nextTagMatch[1];
209
- }
210
- else {
211
- nextTag = '';
212
- }
204
+ nextTag = '';
213
205
  }
206
+ }
214
207
 
215
- if ( handler.chars ) {
216
- handler.chars(text, prevTag, nextTag);
217
- }
218
- prevTag = '';
219
-
208
+ if ( handler.chars ) {
209
+ handler.chars(text, prevTag, nextTag);
220
210
  }
221
- else {
222
- var stackedTag = lastTag.toLowerCase();
223
- var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)<\/' + stackedTag + '[^>]*>', 'i'));
224
-
225
- html = html.replace(reStackedTag, function(all, text) {
226
- if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
227
- text = text
228
- .replace(/<!--([\s\S]*?)-->/g, '$1')
229
- .replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1');
230
- }
211
+ prevTag = '';
231
212
 
232
- if ( handler.chars ) {
233
- handler.chars( text );
234
- }
213
+ }
214
+ else {
215
+ var stackedTag = lastTag.toLowerCase();
216
+ var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)<\/' + stackedTag + '[^>]*>', 'i'));
217
+
218
+ html = html.replace(reStackedTag, function(all, text) {
219
+ if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') {
220
+ text = text
221
+ .replace(/<!--([\s\S]*?)-->/g, '$1')
222
+ .replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1');
223
+ }
235
224
 
236
- return '';
237
- });
225
+ if ( handler.chars ) {
226
+ handler.chars( text );
227
+ }
238
228
 
239
- parseEndTag( '</' + stackedTag + '>', stackedTag );
240
- }
229
+ return '';
230
+ });
241
231
 
242
- if ( html === last ) {
243
- throw new Error('Parse Error: ' + html);
244
- }
232
+ parseEndTag( '</' + stackedTag + '>', stackedTag );
245
233
  }
246
234
 
247
- if (!handler.partialMarkup) {
248
- // Clean up any remaining tags
249
- parseEndTag();
235
+ if ( html === last ) {
236
+ throw new Error('Parse Error: ' + html);
250
237
  }
238
+ }
251
239
 
252
- function parseStartTag(input) {
253
- var start = input.match(startTagOpen);
254
- if (start) {
255
- var match = {
256
- tagName: start[1],
257
- attrs: []
258
- };
259
- input = input.slice(start[0].length);
260
- var end, attr;
261
- while (!(end = input.match(startTagClose)) && (attr = input.match(attribute))) {
262
- input = input.slice(attr[0].length);
263
- match.attrs.push(attr);
264
- }
265
- if (end) {
266
- match.unarySlash = end[1];
267
- match.rest = input.slice(end[0].length);
268
- return match;
269
- }
240
+ if (!handler.partialMarkup) {
241
+ // Clean up any remaining tags
242
+ parseEndTag();
243
+ }
244
+
245
+ function parseStartTag(input) {
246
+ var start = input.match(startTagOpen);
247
+ if (start) {
248
+ var match = {
249
+ tagName: start[1],
250
+ attrs: []
251
+ };
252
+ input = input.slice(start[0].length);
253
+ var end, attr;
254
+ while (!(end = input.match(startTagClose)) && (attr = input.match(attribute))) {
255
+ input = input.slice(attr[0].length);
256
+ match.attrs.push(attr);
257
+ }
258
+ if (end) {
259
+ match.unarySlash = end[1];
260
+ match.rest = input.slice(end[0].length);
261
+ return match;
270
262
  }
271
263
  }
264
+ }
272
265
 
273
- function handleStartTag(match) {
274
- var tagName = match.tagName;
275
- var unarySlash = match.unarySlash;
266
+ function handleStartTag(match) {
267
+ var tagName = match.tagName;
268
+ var unarySlash = match.unarySlash;
276
269
 
277
- if (handler.html5 && lastTag === 'p' && nonPhrasing[tagName]) {
278
- parseEndTag( '', lastTag );
279
- }
270
+ if (handler.html5 && lastTag === 'p' && nonPhrasing(tagName)) {
271
+ parseEndTag( '', lastTag );
272
+ }
280
273
 
281
- if (!handler.html5) {
282
- while (lastTag && inline[ lastTag ]) {
283
- parseEndTag( '', lastTag );
284
- }
274
+ if (!handler.html5) {
275
+ while (lastTag && inline(lastTag)) {
276
+ parseEndTag( '', lastTag );
285
277
  }
278
+ }
286
279
 
287
- if ( closeSelf[ tagName ] && lastTag === tagName ) {
288
- parseEndTag( '', tagName );
289
- }
280
+ if ( closeSelf(tagName) && lastTag === tagName ) {
281
+ parseEndTag( '', tagName );
282
+ }
290
283
 
291
- var unary = empty[ tagName ] || tagName === 'html' && lastTag === 'head' || !!unarySlash;
284
+ var unary = empty(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash;
292
285
 
293
- var attrs = match.attrs.map(function(args) {
294
- var name, value, fallbackValue, customOpen, customClose, customAssign, quote;
295
- var ncp = 7; // number of captured parts, scalar
286
+ var attrs = match.attrs.map(function(args) {
287
+ var name, value, fallbackValue, customOpen, customClose, customAssign, quote;
288
+ var ncp = 7; // number of captured parts, scalar
296
289
 
297
- // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
298
- if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) {
299
- if (args[3] === '') { args[3] = undefined; }
300
- if (args[4] === '') { args[4] = undefined; }
301
- if (args[5] === '') { args[5] = undefined; }
302
- }
290
+ // hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778
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; }
295
+ }
303
296
 
304
- var j = 1;
305
- if (handler.customAttrSurround) {
306
- for (var i = 0, l = handler.customAttrSurround.length; i < l; i++, j += ncp) {
307
- name = args[j + 1];
308
- customAssign = args[j + 2];
309
- if (name) {
310
- fallbackValue = args[j + 3];
311
- value = fallbackValue || args[j + 4] || args[j + 5];
312
- quote = fallbackValue ? '"' : value ? '\'' : '';
313
- customOpen = args[j];
314
- customClose = args[j + 6];
315
- break;
316
- }
297
+ var j = 1;
298
+ if (handler.customAttrSurround) {
299
+ for (var i = 0, l = handler.customAttrSurround.length; i < l; i++, j += ncp) {
300
+ name = args[j + 1];
301
+ customAssign = args[j + 2];
302
+ if (name) {
303
+ fallbackValue = args[j + 3];
304
+ value = fallbackValue || args[j + 4] || args[j + 5];
305
+ quote = fallbackValue ? '"' : value ? '\'' : '';
306
+ customOpen = args[j];
307
+ customClose = args[j + 6];
308
+ break;
317
309
  }
318
310
  }
311
+ }
319
312
 
320
- if (!name && (name = args[j])) {
321
- customAssign = args[j + 1];
322
- fallbackValue = args[j + 2];
323
- value = fallbackValue || args[j + 3] || args[j + 4];
324
- quote = fallbackValue ? '"' : value ? '\'' : '';
325
- }
313
+ 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
+ }
326
319
 
327
- if (value === undefined) {
328
- value = fillAttrs[name] ? name : fallbackValue;
329
- }
320
+ if (value === undefined) {
321
+ value = fillAttrs(name) ? name : fallbackValue;
322
+ }
330
323
 
331
- return {
332
- name: name,
333
- value: value,
334
- customAssign: customAssign || '=',
335
- customOpen: customOpen || '',
336
- customClose: customClose || '',
337
- quote: quote || ''
338
- };
339
- });
324
+ return {
325
+ name: name,
326
+ value: value,
327
+ customAssign: customAssign || '=',
328
+ customOpen: customOpen || '',
329
+ customClose: customClose || '',
330
+ quote: quote || ''
331
+ };
332
+ });
340
333
 
341
- if ( !unary ) {
342
- stack.push( { tag: tagName, attrs: attrs } );
343
- lastTag = tagName;
344
- unarySlash = '';
345
- }
334
+ if ( !unary ) {
335
+ stack.push( { tag: tagName, attrs: attrs } );
336
+ lastTag = tagName;
337
+ unarySlash = '';
338
+ }
346
339
 
347
- if ( handler.start ) {
348
- handler.start( tagName, attrs, unary, unarySlash );
349
- }
340
+ if ( handler.start ) {
341
+ handler.start( tagName, attrs, unary, unarySlash );
350
342
  }
343
+ }
351
344
 
352
- function parseEndTag( tag, tagName ) {
353
- var pos;
345
+ function parseEndTag( tag, tagName ) {
346
+ var pos;
354
347
 
355
- // If no tag name is provided, clean shop
356
- if ( !tagName ) {
357
- pos = 0;
358
- }
359
- else {
360
- // Find the closest opened tag of the same type
361
- var needle = tagName.toLowerCase();
362
- for ( pos = stack.length - 1; pos >= 0; pos-- ) {
363
- if ( stack[ pos ].tag.toLowerCase() === needle ) {
364
- break;
365
- }
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
354
+ var needle = tagName.toLowerCase();
355
+ for ( pos = stack.length - 1; pos >= 0; pos-- ) {
356
+ if ( stack[ pos ].tag.toLowerCase() === needle ) {
357
+ break;
366
358
  }
367
359
  }
360
+ }
368
361
 
369
- if ( pos >= 0 ) {
370
- // Close all the open elements, up the stack
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 );
374
- }
362
+ if ( pos >= 0 ) {
363
+ // 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 );
375
367
  }
368
+ }
376
369
 
377
- // Remove the open elements from the stack
378
- stack.length = pos;
379
- lastTag = pos && stack[ pos - 1 ].tag;
370
+ // Remove the open elements from the stack
371
+ stack.length = pos;
372
+ lastTag = pos && stack[ pos - 1 ].tag;
373
+ }
374
+ else if (tagName.toLowerCase() === 'br') {
375
+ if (handler.start) {
376
+ handler.start(tagName, [], true, '');
380
377
  }
381
- else if (tagName.toLowerCase() === 'br') {
382
- if (handler.start) {
383
- handler.start(tagName, [], true, '');
384
- }
378
+ }
379
+ else if (tagName.toLowerCase() === 'p') {
380
+ if (handler.start) {
381
+ handler.start(tagName, [], false, '', true);
385
382
  }
386
- else if (tagName.toLowerCase() === 'p') {
387
- if (handler.start) {
388
- handler.start(tagName, [], false, '', true);
389
- }
390
- if (handler.end) {
391
- handler.end(tagName, []);
392
- }
383
+ if (handler.end) {
384
+ handler.end(tagName, []);
393
385
  }
394
386
  }
395
- };
396
-
397
- global.HTMLtoXML = function( html ) {
398
- var results = '';
387
+ }
388
+ }
399
389
 
400
- new HTMLParser(html, {
401
- start: function( tag, attrs, unary ) {
402
- results += '<' + tag;
390
+ exports.HTMLParser = HTMLParser;
391
+ exports.HTMLtoXML = function( html ) {
392
+ var results = '';
403
393
 
404
- for ( var i = 0; i < attrs.length; i++ ) {
405
- results += ' ' + attrs[i].name + '="' + (attrs[i].value || '').replace(/"/g, '&#34;') + '"';
406
- }
394
+ new HTMLParser(html, {
395
+ start: function( tag, attrs, unary ) {
396
+ results += '<' + tag;
407
397
 
408
- results += (unary ? '/' : '') + '>';
409
- },
410
- end: function( tag ) {
411
- results += '</' + tag + '>';
412
- },
413
- chars: function( text ) {
414
- results += text;
415
- },
416
- comment: function( text ) {
417
- results += '<!--' + text + '-->';
418
- },
419
- ignore: function(text) {
420
- results += text;
398
+ for ( var i = 0; i < attrs.length; i++ ) {
399
+ results += ' ' + attrs[i].name + '="' + (attrs[i].value || '').replace(/"/g, '&#34;') + '"';
421
400
  }
422
- });
423
401
 
424
- return results;
425
- };
402
+ results += (unary ? '/' : '') + '>';
403
+ },
404
+ end: function( tag ) {
405
+ results += '</' + tag + '>';
406
+ },
407
+ chars: function( text ) {
408
+ results += text;
409
+ },
410
+ comment: function( text ) {
411
+ results += '<!--' + text + '-->';
412
+ },
413
+ ignore: function(text) {
414
+ results += text;
415
+ }
416
+ });
426
417
 
427
- global.HTMLtoDOM = function( html, doc ) {
428
- // There can be only one of these elements
429
- var one = makeMap('html,head,body,title');
418
+ return results;
419
+ };
430
420
 
431
- // Enforce a structure for the document
432
- var structure = {
433
- link: 'head',
434
- base: 'head'
435
- };
421
+ exports.HTMLtoDOM = function( html, doc ) {
422
+ // There can be only one of these elements
423
+ var one = {
424
+ html: true,
425
+ head: true,
426
+ body: true,
427
+ title: true
428
+ };
436
429
 
437
- if ( !doc ) {
438
- if ( typeof DOMDocument !== 'undefined' ) {
439
- doc = new DOMDocument();
440
- }
441
- else if ( typeof document !== 'undefined' && document.implementation && document.implementation.createDocument ) {
442
- doc = document.implementation.createDocument('', '', null);
443
- }
444
- else if ( typeof ActiveX !== 'undefined' ) {
445
- doc = new ActiveXObject('Msxml.DOMDocument');
446
- }
430
+ // Enforce a structure for the document
431
+ var structure = {
432
+ link: 'head',
433
+ base: 'head'
434
+ };
447
435
 
436
+ if ( !doc ) {
437
+ if ( typeof DOMDocument !== 'undefined' ) {
438
+ doc = new DOMDocument();
448
439
  }
449
- else {
450
- doc = doc.ownerDocument ||
451
- doc.getOwnerDocument && doc.getOwnerDocument() ||
452
- doc;
440
+ else if ( typeof document !== 'undefined' && document.implementation && document.implementation.createDocument ) {
441
+ doc = document.implementation.createDocument('', '', null);
453
442
  }
454
-
455
- var elems = [],
456
- documentElement = doc.documentElement ||
457
- doc.getDocumentElement && doc.getDocumentElement();
458
-
459
- // If we're dealing with an empty document then we
460
- // need to pre-populate it with the HTML document structure
461
- if ( !documentElement && doc.createElement ) {
462
- (function() {
463
- var html = doc.createElement('html');
464
- var head = doc.createElement('head');
465
- head.appendChild( doc.createElement('title') );
466
- html.appendChild( head );
467
- html.appendChild( doc.createElement('body') );
468
- doc.appendChild( html );
469
- })();
443
+ else if ( typeof ActiveX !== 'undefined' ) {
444
+ doc = new ActiveXObject('Msxml.DOMDocument');
470
445
  }
471
446
 
472
- // Find all the unique elements
473
- if ( doc.getElementsByTagName ) {
474
- for ( var i in one ) {
475
- one[ i ] = doc.getElementsByTagName( i )[0];
476
- }
477
- }
447
+ }
448
+ else {
449
+ doc = doc.ownerDocument ||
450
+ doc.getOwnerDocument && doc.getOwnerDocument() ||
451
+ doc;
452
+ }
478
453
 
479
- // If we're working with a document, inject contents into
480
- // the body element
481
- var curParentNode = one.body;
482
-
483
- new HTMLParser( html, {
484
- start: function( tagName, attrs, unary ) {
485
- // If it's a pre-built element, then we can ignore
486
- // its construction
487
- if ( one[ tagName ] ) {
488
- curParentNode = one[ tagName ];
489
- return;
490
- }
454
+ var elems = [],
455
+ documentElement = doc.documentElement ||
456
+ doc.getDocumentElement && doc.getDocumentElement();
457
+
458
+ // If we're dealing with an empty document then we
459
+ // need to pre-populate it with the HTML document structure
460
+ if ( !documentElement && doc.createElement ) {
461
+ (function() {
462
+ var html = doc.createElement('html');
463
+ 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 );
468
+ })();
469
+ }
491
470
 
492
- var elem = doc.createElement( tagName );
471
+ // Find all the unique elements
472
+ if ( doc.getElementsByTagName ) {
473
+ for ( var i in one ) {
474
+ one[ i ] = doc.getElementsByTagName( i )[0];
475
+ }
476
+ }
493
477
 
494
- for ( var attr in attrs ) {
495
- elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
496
- }
478
+ // If we're working with a document, inject contents into
479
+ // the body element
480
+ var curParentNode = one.body;
481
+
482
+ new HTMLParser( html, {
483
+ start: function( tagName, attrs, unary ) {
484
+ // If it's a pre-built element, then we can ignore
485
+ // its construction
486
+ if ( one[ tagName ] ) {
487
+ curParentNode = one[ tagName ];
488
+ return;
489
+ }
497
490
 
498
- if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] !== 'boolean' ) {
499
- one[ structure[ tagName ] ].appendChild( elem );
500
- }
501
- else if ( curParentNode && curParentNode.appendChild ) {
502
- curParentNode.appendChild( elem );
503
- }
491
+ var elem = doc.createElement( tagName );
504
492
 
505
- if ( !unary ) {
506
- elems.push( elem );
507
- curParentNode = elem;
508
- }
509
- },
510
- end: function( /* tag */ ) {
511
- elems.length -= 1;
512
-
513
- // Init the new parentNode
514
- curParentNode = elems[ elems.length - 1 ];
515
- },
516
- chars: function( text ) {
517
- curParentNode.appendChild( doc.createTextNode( text ) );
518
- },
519
- comment: function( /* text */ ) {
520
- // create comment node
521
- },
522
- ignore: function( /* text */ ) {
523
- // What to do here?
493
+ for ( var attr in attrs ) {
494
+ elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
524
495
  }
525
- });
526
496
 
527
- return doc;
528
- };
497
+ if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] !== 'boolean' ) {
498
+ one[ structure[ tagName ] ].appendChild( elem );
499
+ }
500
+ else if ( curParentNode && curParentNode.appendChild ) {
501
+ curParentNode.appendChild( elem );
502
+ }
529
503
 
530
- function makeMap(str) {
531
- var obj = {}, items = str.split(',');
532
- for ( var i = 0; i < items.length; i++ ) {
533
- obj[ items[i] ] = true;
534
- obj[ items[i].toUpperCase() ] = true;
504
+ if ( !unary ) {
505
+ elems.push( elem );
506
+ curParentNode = elem;
507
+ }
508
+ },
509
+ end: function( /* tag */ ) {
510
+ elems.length -= 1;
511
+
512
+ // Init the new parentNode
513
+ curParentNode = elems[ elems.length - 1 ];
514
+ },
515
+ chars: function( text ) {
516
+ curParentNode.appendChild( doc.createTextNode( text ) );
517
+ },
518
+ comment: function( /* text */ ) {
519
+ // create comment node
520
+ },
521
+ ignore: function( /* text */ ) {
522
+ // What to do here?
535
523
  }
536
- return obj;
537
- }
538
- })(typeof exports === 'undefined' ? this : exports);
524
+ });
525
+
526
+ return doc;
527
+ };