<label for="remove-comments">Remove comments (</label>
<input type="checkbox" id="remove-comments-from-cdata" checked>
<label for="remove-comments-from-cdata">also from scripts and styles )</label>
+ <span class="quiet short" style="margin-left:1.5em">
+ Conditional comments are left intact.
+ </span>
</li>
<li>
<input type="checkbox" id="remove-cdata-sections-from-cdata" checked>
<div id="todo">
TODO:
<ul>
- <li>Write more unit tests (~60 so far)</li>
+ <li>Write more unit tests (~70 so far)</li>
<li>Detect repeating attributes (e.g. multiple styles, classes, etc.)</li>
<li>Strip whitespace from attributes where allowed</li>
<li>Report deprecated (or presentational) attributes (e.g.: <code><td width="..." height="..."></code>)</li>
<li>Add option to collapse all whitespace to 1 character, instead of completely removing it (to preserve empty text nodes)</li>
<li>Figure out when it is safe to remove optional closing tags, so that it doesn't affect document tree</li>
- <li>Do not strip IE conditional comments</li>
+ <li>Support IE "Downlevel-revealed Conditional Comments"</li>
<li>Remove as many empty/blank attributes as possible (not just core ones)</li>
<li>Parser trips over xml declarations (need to ignore or strip them)</li>
<li>Generate a report of all applied transformations</li>
function trimWhitespace(str) {
return (str.trim ? str.trim() : str.replace(/^\s+/, '').replace(/\s+$/, ''));
}
+
function collapseWhitespace(str) {
return str.replace(/\s+/g, ' ');
}
+ function isConditionalComment(text) {
+ return /\[if[^]+\]/.test(text);
+ }
+
function canRemoveAttributeQuotes(value) {
// http://www.w3.org/TR/html4/intro/sgmltut.html#attributes
// avoid \w, which could match unicode in some implementations
}
if (options.removeCDATASectionsFromCDATA) {
text = text
- // /* <![[CDATA */ or // <![[CDATA
- .replace(/^(?:\s*\/\*\s*<!\[\[CDATA\[\s*\*\/|\s*\/\/\s*<!\[\[CDATA\[.*)/, '')
- // /* ]]> */ or // ]]>
+ // "/* <![CDATA[ */" or "// <![CDATA["
+ .replace(/^(?:\s*\/\*\s*<!\[CDATA\[\s*\*\/|\s*\/\/\s*<!\[CDATA\[.*)/, '')
+ // "/* ]]> */" or "// ]]>"
.replace(/(?:\/\*\s*\]\]>\s*\*\/|\/\/\s*\]\]>)\s*$/, '');
}
}
buffer.push(text);
},
comment: function( text ) {
- buffer.push(options.removeComments ? '' : ('<!--' + text + '-->'));
+ if (options.removeComments && !isConditionalComment(text)) {
+ text = '';
+ }
+ else {
+ text = '<!--' + text + '-->';
+ }
+ buffer.push(text);
},
doctype: function(doctype) {
buffer.push(options.useShortDoctype ? '<!DOCTYPE html>' : collapseWhitespace(doctype));
var input = '<!-- test -->';
equals(minify(input, { removeComments: true }), '');
- var input = '<!-- foo --><div>baz</div><!-- bar\n\n moo -->';
+ input = '<!-- foo --><div>baz</div><!-- bar\n\n moo -->';
equals(minify(input, { removeComments: true }), '<div>baz</div>');
equals(minify(input, { removeComments: false }), input);
- var input = '<p title="<!-- comment in attribute -->">foo</p>';
+ input = '<p title="<!-- comment in attribute -->">foo</p>';
equals(minify(input, { removeComments: true }), input);
- var input = '<script><!-- alert(1) --><\/script>';
+ input = '<script><!-- alert(1) --><\/script>';
equals(minify(input, { removeComments: true }), input);
- var input = '<STYLE><!-- alert(1) --><\/STYLE>';
+ input = '<STYLE><!-- alert(1) --><\/STYLE>';
equals(minify(input, { removeComments: true }), '<style><!-- alert(1) --><\/style>');
});
+ test('conditional comments', function(){
+ var input = '<!--[if IE 6]> test <![endif]-->';
+ equals(minify(input, { removeComments: true }), input);
+
+ input = '<!--[if lt IE 5.5]> test <![endif]-->';
+ equals(minify(input, { removeComments: true }), input);
+
+ input = '<!--[if (gt IE 5)&(lt IE 7)]> test <![endif]-->';
+ equals(minify(input, { removeComments: true }), input);
+ });
+
test('remove comments from scripts/styles', function(){
var input = '<script><!--alert(1)--><\/script>';
var output = '<script>alert(1)<\/script>';
});
test('remove CDATA sections from scripts/styles', function(){
- var input = '<script>/*<![[CDATA[*/alert(1)/*]]>*/<\/script>';
+ var input = '<script>/*<![CDATA[*/alert(1)/*]]>*/<\/script>';
var output = '<script>alert(1)<\/script>';
equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
- input = '<script>//<![[CDATA[\nalert(1)\n//]]><\/script>';
+ input = '<script>//<![CDATA[\nalert(1)\n//]]><\/script>';
output = '<script>\nalert(1)\n<\/script>';
equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
- input = '<script type="text/javascript"> /* \n\t <![[CDATA[ */ alert(1) /* ]]> */ \n <\/script>';
+ input = '<script type="text/javascript"> /* \n\t <![CDATA[ */ alert(1) /* ]]> */ \n <\/script>';
output = '<script type="text/javascript"> alert(1) <\/script>';
equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
- input = '<style>/* <![[CDATA[ */p { color: red } // ]]><\/style>';
- output = '<style>p { color: red } </style>';
+ input = '<style>/* <![CDATA[ */p { color: red } // ]]><\/style>';
+ output = '<style>p { color: red } <\/style>';
+ equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
+
+ input = '<script>\n\n//<![CDATA[\nalert(1)//]]><\/script>';
+ output = '<script>\nalert(1)<\/script>';
equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
});