From 32b02f044d35f89be9b6ac407c95705502e3b54e Mon Sep 17 00:00:00 2001 From: Juriy Zaytsev Date: Tue, 9 Feb 2010 13:17:18 -0500 Subject: [PATCH] =?utf8?q?Refactor=20`minify`=20function=20slightly.=20Mor?= =?utf8?q?e=20tests=20=E2=80=94=20case=20normalization,=20space=20normaliz?= =?utf8?q?ation=20and=20options.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- src/htmlminifier.js | 98 ++++++++++++++++++++++----------------------- tests/index.html | 51 +++++++++++++++++++---- 2 files changed, 91 insertions(+), 58 deletions(-) diff --git a/src/htmlminifier.js b/src/htmlminifier.js index 6a4bda9..788c6c3 100644 --- a/src/htmlminifier.js +++ b/src/htmlminifier.js @@ -31,8 +31,6 @@ } function isAttributeRedundant(tag, attrName, attrValue, attrs) { - tag = tag.toLowerCase(); - attrName = attrName.toLowerCase(); attrValue = attrValue.toLowerCase(); return ( (tag === 'script' && @@ -78,14 +76,8 @@ function canDeleteEmptyAttribute(tag, attrName, attrValue) { var isValueEmpty = /^(["'])?\s*\1$/.test(attrValue); - if (isValueEmpty) { - log('empty attribute: ' + tag + ' :: ' + attrName); - - tag = tag.toLowerCase(); - attrName = attrName.toLowerCase(); - return ( (tag === 'input' && attrName === 'value') || reEmptyAttribute.test(attrName)); @@ -93,63 +85,66 @@ return false; } + function normalizeAttribute(attr, attrs, tag, options) { + + var attrName = attr.name.toLowerCase(); + var attrValue = attr.escaped; + var attrFragment; + + if (options.shouldRemoveRedundantAttributes && + isAttributeRedundant(tag, attrName, attrValue, attrs)) { + return ''; + } + + attrValue = cleanAttributeValue(tag, attrName, attrValue); + + if (!options.shouldRemoveAttributeQuotes || + !canRemoveAttributeQuotes(attrValue)) { + attrValue = '"' + attrValue + '"'; + } + + if (options.shouldRemoveEmptyAttributes && + canDeleteEmptyAttribute(tag, attrName, attrValue)) { + return ''; + } + + if (options.shouldCollapseBooleanAttributes && + isBooleanAttribute(attrName)) { + attrFragment = attrName; + } + else { + attrFragment = attrName + '=' + attrValue; + } + + return (' ' + attrFragment); + } + function minify(value, options) { + options = options || { }; + value = trimWhitespace(value); + var results = []; var t = new Date(); HTMLParser(value, { start: function( tag, attrs, unary ) { + + tag = tag.toLowerCase(); + results.push('<', tag); + for ( var i = 0, len = attrs.length; i < len; i++ ) { - - if (options.shouldRemoveRedundantAttributes && - isAttributeRedundant(tag, attrs[i].name, attrs[i].value, attrs)) { - continue; - } - - var escaped = attrs[i].escaped, - attrName = attrs[i].name, - attrValue = escaped; - - attrValue = cleanAttributeValue(tag, attrName, attrValue); - - if (options.shouldRemoveAttributeQuotes && - canRemoveAttributeQuotes(attrValue)) { - // noop - } - else { - attrValue = '"' + attrValue + '"'; - } - - if (options.shouldRemoveEmptyAttributes && - canDeleteEmptyAttribute(tag, attrName, attrValue)) { - continue; - } - - var attributeFragment; - if (options.shouldCollapseBooleanAttributes && - isBooleanAttribute(attrName)) { - attributeFragment = attrName; - } - else { - attributeFragment = attrName + '=' + attrValue; - } - - results.push(' ', attributeFragment); + results.push(normalizeAttribute(attrs[i], attrs, tag, options)); } + results.push('>'); }, end: function( tag ) { - results.push(''); + results.push(''); }, chars: function( text ) { - if (options.shouldCollapseWhitespace) { - results.push(trimWhitespace(text)); - } - else { - results.push(text); - } + results.push(options.shouldCollapseWhitespace ? trimWhitespace(text) : text); }, comment: function( text ) { results.push(options.shouldRemoveComments ? '' : ('')); @@ -166,6 +161,7 @@ return str; } - this.minify = minify; + // export + global.minify = minify; })(this); \ No newline at end of file diff --git a/tests/index.html b/tests/index.html index 0c1480e..92b5e3a 100644 --- a/tests/index.html +++ b/tests/index.html @@ -12,7 +12,7 @@

HTML Minifier

- +

    @@ -26,7 +26,33 @@ ok(minify); }); - test('Short doctype with html 4.01 doctype', function() { + test('options', function() { + var input = '

    blahblah 2blah 3

    '; + equals(minify(input), input); + equals(minify(input, {}), input); + }); + + test('case normalization', function() { + equals(minify('

    foo

    '), '

    foo

    '); + equals(minify('
    boo
    '), '
    boo
    '); + equals(minify('
    boo
    '), '
    boo
    '); + equals(minify('
    boo
    '), '
    boo
    '); + equals(minify('
    boo
    '), '
    boo
    '); + equals(minify('
    boo
    '), '
    boo
    '); + }); + + test('space normalization between attributes', function() { + equals(minify('

    foo

    '), '

    foo

    '); + equals(minify('

    foo

    '), '

    foo

    '); + equals(minify('

    foo

    '), '

    foo

    '); + equals(minify(''), ''); + }); + + test('space normalization around text', function(){ + equals(minify('

    blah

    \n\n\n '), '

    blah

    '); + }); + + test('doctype normalization', function() { var html401doctype = ''; var actual = minify(html401doctype, { shouldUseShortDoctype: true }); var expected = ''; @@ -41,7 +67,7 @@ equals(actual, expected); }); - test('Remove comments', function(){ + test('removing comments', function(){ var input = ''; equals(minify(input, { shouldRemoveComments: true }), ''); @@ -56,7 +82,7 @@ equals(minify(input, { shouldRemoveComments: true }), input); var input = '