start: function( tag, attrs, unary, unarySlash ) {
if (isIgnoring) {
- buffer.push('<', tag, attrsToMarkup(attrs), unarySlash ? '/' : '', '>');
+ buffer.push('<' + tag, attrsToMarkup(attrs), unarySlash ? '/' : '', '>');
return;
}
}
}
- buffer.push('<', tag);
+ var openTag = '<' + tag;
+ var closeTag = ((unarySlash && options.keepClosingSlash) ? '/' : '') + '>';
+ if ( attrs.length === 0) {
+ openTag += closeTag;
+ }
+
+ buffer.push(openTag);
lint && lint.testElement(tag);
+ var token;
for ( var i = 0, len = attrs.length; i < len; i++ ) {
lint && lint.testAttribute(tag, attrs[i].name.toLowerCase(), attrs[i].escaped);
- buffer.push(normalizeAttribute(attrs[i], attrs, tag, options));
+ token = normalizeAttribute(attrs[i], attrs, tag, options);
+ if ( i === len - 1 ) {
+ token += closeTag;
+ }
+ buffer.push(token);
}
- buffer.push(((unarySlash && options.keepClosingSlash) ? '/' : '') + '>');
},
end: function( tag ) {
if (isIgnoring) {
- buffer.push('</', tag, '>');
+ buffer.push('</' + tag + '>');
return;
}
var isElementEmpty = currentChars === '' && tag === currentTag;
if ((options.removeEmptyElements && isElementEmpty && canRemoveElement(tag))) {
// remove last "element" from buffer, return
- buffer.splice(buffer.lastIndexOf('<'));
+ for ( var i = buffer.length - 1; i >= 0; i-- ) {
+ if ( /^<[^\/!]/.test(buffer[i]) ) {
+ buffer.splice(i);
+ break;
+ }
+ }
return;
}
else if (options.removeOptionalTags && isOptionalTag(tag)) {
}
else {
// push end tag to buffer
- buffer.push('</', options.caseSensitive ? tag : tag.toLowerCase(), '>');
+ buffer.push('</' + (options.caseSensitive ? tag : tag.toLowerCase()) + '>');
results.push.apply(results, buffer);
}
// flush buffer
});
results.push.apply(results, buffer);
- var str = results.join('');
+ var str = joinResultSegments(results, options);
log('minified in: ' + (new Date() - t) + 'ms');
return str;
}
+ function joinResultSegments( results, options ) {
+ var str;
+ var maxLineLength = options.maxLineLength;
+ if ( maxLineLength ) {
+ var token;
+ var lines = [];
+ var line = '';
+ for ( var i = 0, len = results.length; i < len; i++ ) {
+ token = results[i];
+ if ( line.length + token.length < maxLineLength ) {
+ line += token;
+ }
+ else {
+ lines.push(line.replace(/^\n/, ''));
+ line = token;
+ }
+ }
+ lines.push(line);
+
+ str = lines.join('\n');
+ }
+ else {
+ str = results.join('');
+ }
+
+ return str;
+ }
+
// for CommonJS enviroments, export everything
if ( typeof exports !== 'undefined' ) {
exports.minify = minify;
equal(minify(input), '<script src="x"></script><noscript>x</noscript>');
});
+ test('max line length', function() {
+ var options = { maxLineLength: 25 };
+
+ input = '<div data-attr="foo"></div>';
+ equal(minify(input, options), '<div data-attr="foo">\n</div>');
+
+ input = '<code> hello world \n world hello </code>';
+ equal(minify(input, options), '<code>\n hello world \n world hello \n</code>');
+
+ equal(minify('<p title="</p>">x</p>'), '<p title="</p>">x</p>');
+ equal(minify('<p title=" <!-- hello world --> ">x</p>'), '<p title=" <!-- hello world --> ">x</p>');
+ equal(minify('<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>'), '<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>');
+ equal(minify('<p foo-bar=baz>xxx</p>'), '<p foo-bar="baz">xxx</p>');
+ equal(minify('<p foo:bar=baz>xxx</p>'), '<p foo:bar="baz">xxx</p>');
+
+ input = '<div><div><div><div><div><div><div><div><div><div>' +
+ 'i\'m 10 levels deep' +
+ '</div></div></div></div></div></div></div></div></div></div>';
+
+ equal(minify(input), input);
+
+ equal(minify('<script>alert(\'<!--\')<\/script>', options), '<script>alert(\'<!--\')\n<\/script>');
+ equal(minify('<script>alert(\'<!-- foo -->\')<\/script>', options), '<script>\nalert(\'<!-- foo -->\')\n<\/script>');
+ equal(minify('<script>alert(\'-->\')<\/script>', options), '<script>alert(\'-->\')\n<\/script>');
+
+ equal(minify('<a title="x"href=" ">foo</a>', options), '<a title="x" href="">foo\n</a>');
+ equal(minify('<p id=""class=""title="">x', options), '<p id="" class=""\n title="">x</p>');
+ equal(minify('<p x="x\'"">x</p>', options), '<p x="x\'">x</p>', 'trailing quote should be ignored');
+ equal(minify('<a href="#"><p>Click me</p></a>', options), '<a href="#"><p>Click me\n</p></a>');
+ equal(minify('<span><button>Hit me</button></span>', options), '<span><button>Hit me\n</button></span>');
+ equal(minify('<object type="image/svg+xml" data="image.svg"><div>[fallback image]</div></object>', options),
+ '<object\n type="image/svg+xml"\n data="image.svg"><div>\n[fallback image]</div>\n</object>'
+ );
+
+ equal(minify('<ng-include src="x"></ng-include>', options), '<ng-include src="x">\n</ng-include>');
+ equal(minify('<ng:include src="x"></ng:include>', options), '<ng:include src="x">\n</ng:include>');
+ equal(minify('<ng-include src="\'views/partial-notification.html\'"></ng-include><div ng-view=""></div>', options),
+ '<ng-include\n src="\'views/partial-notification.html\'">\n</ng-include><div\n ng-view=""></div>'
+ );
+ equal(minify('<some-tag-1></some-tag-1><some-tag-2></some-tag-2>', options),
+ '<some-tag-1>\n</some-tag-1>\n<some-tag-2>\n</some-tag-2>'
+ );
+ equal(minify('[\']["]', options), '[\']["]');
+ equal(minify('<a href="test.html"><div>hey</div></a>', options), '<a href="test.html">\n<div>hey</div></a>');
+ equal(minify(':) <a href="http://example.com">link</a>', options), ':) <a\n href="http://example.com">\nlink</a>');
+
+ equal(minify('<a href>ok</a>', options), '<a href>ok</a>');
+ });
+
})(typeof exports === 'undefined' ? window : exports);