From cddb3fad049b79720b6a2973ebe507b9f00631b0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Even=20Andre=CC=81=20Fiskvik?= Date: Thu, 18 Apr 2013 11:43:53 +0200 Subject: [PATCH] Fix minifier issue with prefixed tags (closing) --- dist/all.js | 170 ++++++++++++++++++++++++++++---------------- package.json | 2 +- src/htmlminifier.js | 36 +++++----- src/htmlparser.js | 2 +- tests/minifier.js | 4 ++ 5 files changed, 131 insertions(+), 83 deletions(-) diff --git a/dist/all.js b/dist/all.js index 674b649..f0cc986 100644 --- a/dist/all.js +++ b/dist/all.js @@ -27,11 +27,11 @@ (function(global){ // Regular Expressions for parsing tags and attributes - var startTag = /^<(\w+)((?:\s*[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, - endTag = /^<\/(\w+)[^>]*>/, + var startTag = /^<([\w:-]+)((?:\s*[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, + endTag = /^<\/([\w:-]+)[^>]*>/, attr = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g, doctype = /^]+>/i; - + // Empty Elements - HTML 4.01 var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"); @@ -50,11 +50,11 @@ // Special Elements (can contain anything) var special = makeMap("script,style"); - + var reCache = { }, stackedTag, re; var HTMLParser = global.HTMLParser = function( html, handler ) { - var index, chars, match, stack = [], last = html; + var index, chars, match, stack = [], last = html, prevTag, nextTag; stack.last = function(){ return this[ this.length - 1 ]; }; @@ -68,7 +68,7 @@ // Comment if ( html.indexOf(""); - + if ( index >= 0 ) { if ( handler.comment ) handler.comment( html.substring( 4, index ) ); @@ -81,43 +81,59 @@ handler.doctype( match[0] ); html = html.substring( match[0].length ); chars = false; - + // end tag } else if ( html.indexOf("]*>", "i")); - + html = html.replace(reStackedTag, function(all, text) { if (stackedTag !== 'script' && stackedTag !== 'style') { text = text @@ -138,7 +154,7 @@ throw "Parse Error: " + html; last = html; } - + // Clean up any remaining tags parseEndTag(); @@ -157,10 +173,10 @@ if ( !unary ) stack.push( tagName ); - + if ( handler.start ) { var attrs = []; - + rest.replace(attr, function(match, name) { var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : @@ -172,7 +188,7 @@ escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //" }); }); - + if ( handler.start ) handler.start( tagName, attrs, unary ); } @@ -182,35 +198,35 @@ // If no tag name is provided, clean shop if ( !tagName ) var pos = 0; - + // Find the closest opened tag of the same type else for ( var pos = stack.length - 1; pos >= 0; pos-- ) if ( stack[ pos ] == tagName ) break; - + if ( pos >= 0 ) { // Close all the open elements, up the stack for ( var i = stack.length - 1; i >= pos; i-- ) if ( handler.end ) handler.end( stack[ i ] ); - + // Remove the open elements from the stack stack.length = pos; } } }; - + global.HTMLtoXML = function( html ) { var results = ""; - + HTMLParser(html, { start: function( tag, attrs, unary ) { results += "<" + tag; - + for ( var i = 0; i < attrs.length; i++ ) results += " " + attrs[i].name + '="' + attrs[i].escaped + '"'; - + results += (unary ? "/" : "") + ">"; }, end: function( tag ) { @@ -223,20 +239,20 @@ results += ""; } }); - + return results; }; - + global.HTMLtoDOM = function( html, doc ) { // There can be only one of these elements var one = makeMap("html,head,body,title"); - + // Enforce a structure for the document var structure = { link: "head", base: "head" }; - + if ( !doc ) { if ( typeof DOMDocument != "undefined" ) doc = new DOMDocument(); @@ -244,16 +260,16 @@ doc = document.implementation.createDocument("", "", null); else if ( typeof ActiveX != "undefined" ) doc = new ActiveXObject("Msxml.DOMDocument"); - + } else doc = doc.ownerDocument || doc.getOwnerDocument && doc.getOwnerDocument() || doc; - + var elems = [], documentElement = doc.documentElement || doc.getDocumentElement && doc.getDocumentElement(); - + // If we're dealing with an empty document then we // need to pre-populate it with the HTML document structure if ( !documentElement && doc.createElement ) (function(){ @@ -264,16 +280,16 @@ html.appendChild( doc.createElement("body") ); doc.appendChild( html ); })(); - + // Find all the unique elements if ( doc.getElementsByTagName ) for ( var i in one ) one[ i ] = doc.getElementsByTagName( i )[0]; - + // If we're working with a document, inject contents into // the body element var curParentNode = one.body; - + HTMLParser( html, { start: function( tagName, attrs, unary ) { // If it's a pre-built element, then we can ignore @@ -282,18 +298,18 @@ curParentNode = one[ tagName ]; return; } - + var elem = doc.createElement( tagName ); - + for ( var attr in attrs ) elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value ); - + if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] != "boolean" ) one[ structure[ tagName ] ].appendChild( elem ); - + else if ( curParentNode && curParentNode.appendChild ) curParentNode.appendChild( elem ); - + if ( !unary ) { elems.push( elem ); curParentNode = elem; @@ -301,7 +317,7 @@ }, end: function( tag ) { elems.length -= 1; - + // Init the new parentNode curParentNode = elems[ elems.length - 1 ]; }, @@ -312,7 +328,7 @@ // create comment node } }); - + return doc; }; @@ -324,11 +340,12 @@ } return obj; } -})(typeof exports === 'undefined' ? window : exports);/*! - * HTMLMinifier v0.4.4 +})(typeof exports === 'undefined' ? this : exports); +/*! + * HTMLMinifier v0.5.0 * http://kangax.github.com/html-minifier/ * - * Copyright (c) 2010 Juriy "kangax" Zaytsev + * Copyright (c) 2010-2013 Juriy "kangax" Zaytsev * Licensed under the MIT license. * */ @@ -353,9 +370,9 @@ HTMLParser = require('./htmlparser').HTMLParser; } - function trimWhitespace(str) { + var trimWhitespace = function(str) { return str.replace(/^\s+/, '').replace(/\s+$/, ''); - } + }; if (String.prototype.trim) { trimWhitespace = function(str) { return str.trim(); @@ -365,9 +382,31 @@ function collapseWhitespace(str) { return str.replace(/\s+/g, ' '); } + + function collapseWhitespaceSmart(str, prevTag, nextTag) { + // array of tags that will maintain a single space outside of them + var tags = ['a', 'b', 'big', 'button', 'em', 'font','i', 'img', 'mark', 's', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'tt', 'u']; + + if (prevTag && (prevTag.substr(0,1) !== '/' + || ( prevTag.substr(0,1) === '/' && tags.indexOf(prevTag.substr(1)) === -1))) { + str = str.replace(/^\s+/, ''); + } + + if (nextTag && (nextTag.substr(0,1) === '/' + || ( nextTag.substr(0,1) !== '/' && tags.indexOf(nextTag) === -1))) { + str = str.replace(/\s+$/, ''); + } + + if (prevTag && nextTag) { + // strip non space whitespace then compress spaces to one + return str.replace(/[\t\n\r]+/g, '').replace(/[ ]+/g, ' '); + } + + return str; + } function isConditionalComment(text) { - return (/\[if[^\]]+\]/).test(text); + return ((/\[if[^\]]+\]/).test(text) || (/\s*( dist/all.js && node test.js" }, "devDependencies": { "qunit": "0.5.x", diff --git a/src/htmlminifier.js b/src/htmlminifier.js index e172b70..ff4fe7d 100644 --- a/src/htmlminifier.js +++ b/src/htmlminifier.js @@ -41,24 +41,24 @@ } function collapseWhitespaceSmart(str, prevTag, nextTag) { - // array of tags that will maintain a single space outside of them - var tags = ['a', 'b', 'big', 'button', 'em', 'font','i', 'img', 'mark', 's', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'tt', 'u']; - - if (prevTag && (prevTag.substr(0,1) !== '/' - || ( prevTag.substr(0,1) === '/' && tags.indexOf(prevTag.substr(1)) === -1))) { - str = str.replace(/^\s+/, ''); - } - - if (nextTag && (nextTag.substr(0,1) === '/' - || ( nextTag.substr(0,1) !== '/' && tags.indexOf(nextTag) === -1))) { - str = str.replace(/\s+$/, ''); - } - - if (prevTag && nextTag) { - // strip non space whitespace then compress spaces to one - return str.replace(/[\t\n\r]+/g, '').replace(/[ ]+/g, ' '); - } - + // array of tags that will maintain a single space outside of them + var tags = ['a', 'b', 'big', 'button', 'em', 'font','i', 'img', 'mark', 's', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'tt', 'u']; + + if (prevTag && (prevTag.substr(0,1) !== '/' + || ( prevTag.substr(0,1) === '/' && tags.indexOf(prevTag.substr(1)) === -1))) { + str = str.replace(/^\s+/, ''); + } + + if (nextTag && (nextTag.substr(0,1) === '/' + || ( nextTag.substr(0,1) !== '/' && tags.indexOf(nextTag) === -1))) { + str = str.replace(/\s+$/, ''); + } + + if (prevTag && nextTag) { + // strip non space whitespace then compress spaces to one + return str.replace(/[\t\n\r]+/g, '').replace(/[ ]+/g, ' '); + } + return str; } diff --git a/src/htmlparser.js b/src/htmlparser.js index 0559f59..69803b4 100644 --- a/src/htmlparser.js +++ b/src/htmlparser.js @@ -28,7 +28,7 @@ // Regular Expressions for parsing tags and attributes var startTag = /^<([\w:-]+)((?:\s*[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, - endTag = /^<\/(\w+)[^>]*>/, + endTag = /^<\/([\w:-]+)[^>]*>/, attr = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g, doctype = /^]+>/i; diff --git a/tests/minifier.js b/tests/minifier.js index 3ab1baf..a56a686 100644 --- a/tests/minifier.js +++ b/tests/minifier.js @@ -24,6 +24,10 @@ equal(minify('foo'), 'foo'); equal(minify('

x'), '

x

'); equal(minify('

x

'), '

x

', 'trailing quote should be ignored'); + + equal(minify(''), ''); + equal(minify(''), ''); + equal(minify('
'), '
'); }); test('`minifiy` exists', function() { -- 2.34.1