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/README.md +6 -7
- package/cli.js +72 -76
- package/dist/htmlminifier.js +32448 -1587
- package/dist/htmlminifier.min.js +14 -2
- package/package.json +4 -9
- package/sample-cli-config-file.conf +6 -4
- package/src/htmllint.js +139 -142
- package/src/htmlminifier.js +933 -1004
- package/src/htmlparser.js +414 -425
- package/src/utils.js +18 -0
package/src/htmlparser.js
CHANGED
|
@@ -28,511 +28,500 @@
|
|
|
28
28
|
|
|
29
29
|
/* global ActiveXObject, DOMDocument */
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
],
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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(
|
|
147
|
+
html = html.substring( conditionalEnd + 2 );
|
|
167
148
|
prevTag = '';
|
|
168
149
|
continue;
|
|
169
150
|
}
|
|
151
|
+
}
|
|
170
152
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
html = html.substring(
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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
|
|
201
|
+
nextTag = '/' + nextTagMatch[1];
|
|
204
202
|
}
|
|
205
203
|
else {
|
|
206
|
-
|
|
207
|
-
if (nextTagMatch) {
|
|
208
|
-
nextTag = '/' + nextTagMatch[1];
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
nextTag = '';
|
|
212
|
-
}
|
|
204
|
+
nextTag = '';
|
|
213
205
|
}
|
|
206
|
+
}
|
|
214
207
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
prevTag = '';
|
|
219
|
-
|
|
208
|
+
if ( handler.chars ) {
|
|
209
|
+
handler.chars(text, prevTag, nextTag);
|
|
220
210
|
}
|
|
221
|
-
|
|
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
|
-
|
|
233
|
-
|
|
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
|
-
|
|
237
|
-
|
|
225
|
+
if ( handler.chars ) {
|
|
226
|
+
handler.chars( text );
|
|
227
|
+
}
|
|
238
228
|
|
|
239
|
-
|
|
240
|
-
}
|
|
229
|
+
return '';
|
|
230
|
+
});
|
|
241
231
|
|
|
242
|
-
|
|
243
|
-
throw new Error('Parse Error: ' + html);
|
|
244
|
-
}
|
|
232
|
+
parseEndTag( '</' + stackedTag + '>', stackedTag );
|
|
245
233
|
}
|
|
246
234
|
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
parseEndTag();
|
|
235
|
+
if ( html === last ) {
|
|
236
|
+
throw new Error('Parse Error: ' + html);
|
|
250
237
|
}
|
|
238
|
+
}
|
|
251
239
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
266
|
+
function handleStartTag(match) {
|
|
267
|
+
var tagName = match.tagName;
|
|
268
|
+
var unarySlash = match.unarySlash;
|
|
276
269
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
270
|
+
if (handler.html5 && lastTag === 'p' && nonPhrasing(tagName)) {
|
|
271
|
+
parseEndTag( '', lastTag );
|
|
272
|
+
}
|
|
280
273
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
274
|
+
if (!handler.html5) {
|
|
275
|
+
while (lastTag && inline(lastTag)) {
|
|
276
|
+
parseEndTag( '', lastTag );
|
|
285
277
|
}
|
|
278
|
+
}
|
|
286
279
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
280
|
+
if ( closeSelf(tagName) && lastTag === tagName ) {
|
|
281
|
+
parseEndTag( '', tagName );
|
|
282
|
+
}
|
|
290
283
|
|
|
291
|
-
|
|
284
|
+
var unary = empty(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash;
|
|
292
285
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
320
|
+
if (value === undefined) {
|
|
321
|
+
value = fillAttrs(name) ? name : fallbackValue;
|
|
322
|
+
}
|
|
330
323
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
334
|
+
if ( !unary ) {
|
|
335
|
+
stack.push( { tag: tagName, attrs: attrs } );
|
|
336
|
+
lastTag = tagName;
|
|
337
|
+
unarySlash = '';
|
|
338
|
+
}
|
|
346
339
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
340
|
+
if ( handler.start ) {
|
|
341
|
+
handler.start( tagName, attrs, unary, unarySlash );
|
|
350
342
|
}
|
|
343
|
+
}
|
|
351
344
|
|
|
352
|
-
|
|
353
|
-
|
|
345
|
+
function parseEndTag( tag, tagName ) {
|
|
346
|
+
var pos;
|
|
354
347
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
378
|
+
}
|
|
379
|
+
else if (tagName.toLowerCase() === 'p') {
|
|
380
|
+
if (handler.start) {
|
|
381
|
+
handler.start(tagName, [], false, '', true);
|
|
385
382
|
}
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
390
|
+
exports.HTMLParser = HTMLParser;
|
|
391
|
+
exports.HTMLtoXML = function( html ) {
|
|
392
|
+
var results = '';
|
|
403
393
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
394
|
+
new HTMLParser(html, {
|
|
395
|
+
start: function( tag, attrs, unary ) {
|
|
396
|
+
results += '<' + tag;
|
|
407
397
|
|
|
408
|
-
|
|
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, '"') + '"';
|
|
421
400
|
}
|
|
422
|
-
});
|
|
423
401
|
|
|
424
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
var one = makeMap('html,head,body,title');
|
|
418
|
+
return results;
|
|
419
|
+
};
|
|
430
420
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
doc = doc.ownerDocument ||
|
|
450
|
+
doc.getOwnerDocument && doc.getOwnerDocument() ||
|
|
451
|
+
doc;
|
|
452
|
+
}
|
|
478
453
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
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
|
-
|
|
495
|
-
|
|
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
|
-
|
|
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
|
-
|
|
506
|
-
|
|
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
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
return doc;
|
|
527
|
+
};
|