(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 = /^<!DOCTYPE [^>]+>/i;
-
+
// Empty Elements - HTML 4.01
var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
// 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 ];
};
// Comment
if ( html.indexOf("<!--") === 0 ) {
index = html.indexOf("-->");
-
+
if ( index >= 0 ) {
if ( handler.comment )
handler.comment( html.substring( 4, index ) );
handler.doctype( match[0] );
html = html.substring( match[0].length );
chars = false;
-
+
// end tag
} else if ( html.indexOf("</") === 0 ) {
match = html.match( endTag );
-
+
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( endTag, parseEndTag );
+ prevTag = '/'+match[1];
chars = false;
}
-
+
// start tag
} else if ( html.indexOf("<") === 0 ) {
match = html.match( startTag );
-
+
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( startTag, parseStartTag );
+ prevTag = match[1];
chars = false;
}
}
if ( chars ) {
index = html.indexOf("<");
-
+
var text = index < 0 ? html : html.substring( 0, index );
html = index < 0 ? "" : html.substring( index );
-
+
+ // next tag
+ tagMatch = html.match( startTag );
+ if (tagMatch) {
+ nextTag = tagMatch[1];
+ } else {
+ tagMatch = html.match( endTag );
+ if (tagMatch) {
+ nextTag = '/'+tagMatch[1];
+ } else {
+ nextTag = '';
+ }
+ }
+
if ( handler.chars )
- handler.chars( text );
+ handler.chars(text, prevTag, nextTag);
+
}
} else {
-
+
stackedTag = stack.last().toLowerCase();
reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp("([\\s\\S]*?)<\/" + stackedTag + "[^>]*>", "i"));
-
+
html = html.replace(reStackedTag, function(all, text) {
if (stackedTag !== 'script' && stackedTag !== 'style') {
text = text
throw "Parse Error: " + html;
last = html;
}
-
+
// Clean up any remaining tags
parseEndTag();
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] :
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
});
});
-
+
if ( handler.start )
handler.start( tagName, attrs, unary );
}
// 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 ) {
results += "<!--" + text + "-->";
}
});
-
+
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();
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(){
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
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;
},
end: function( tag ) {
elems.length -= 1;
-
+
// Init the new parentNode
curParentNode = elems[ elems.length - 1 ];
},
// create comment node
}
});
-
+
return doc;
};
}
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.
*
*/
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();
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*(<!\[endif\])$/).test(text));
}
function isEventAttribute(attrName) {
(tag === 'textarea' && (/^(?:rows|cols|tabindex)$/).test(attrName)) ||
(tag === 'colgroup' && attrName === 'span') ||
(tag === 'col' && attrName === 'span') ||
- ((tag === 'th' || tag == 'td') && (attrName === 'rowspan' || attrName === 'colspan'))
+ ((tag === 'th' || tag === 'td') && (attrName === 'rowspan' || attrName === 'colspan'))
);
}
if (!options[defaultTesters[i]]) {
options[defaultTesters[i]] = function() {
return false;
- }
+ };
}
}
}
stackNoTrimWhitespace = [],
stackNoCollapseWhitespace = [],
lint = options.lint,
- t = new Date()
+ t = new Date();
function _canCollapseWhitespace(tag, attrs) {
return canCollapseWhitespace(tag) || options.canTrimWhitespace(tag, attrs);
}
HTMLParser(value, {
- start: function( tag, attrs, unary ) {
+ start: function( tag, attrs ) {
tag = tag.toLowerCase();
currentTag = tag;
currentChars = '';
// check if current tag is in a whitespace stack
if (options.collapseWhitespace) {
if (stackNoTrimWhitespace.length &&
- tag == stackNoTrimWhitespace[stackNoTrimWhitespace.length - 1]) {
+ tag === stackNoTrimWhitespace[stackNoTrimWhitespace.length - 1]) {
stackNoTrimWhitespace.pop();
}
if (stackNoCollapseWhitespace.length &&
- tag == stackNoCollapseWhitespace[stackNoCollapseWhitespace.length - 1]) {
+ tag === stackNoCollapseWhitespace[stackNoCollapseWhitespace.length - 1]) {
stackNoCollapseWhitespace.pop();
}
}
buffer.length = 0;
currentChars = '';
},
- chars: function( text ) {
+ chars: function( text, prevTag, nextTag ) {
if (currentTag === 'script' || currentTag === 'style') {
if (options.removeCommentsFromCDATA) {
text = removeComments(text, currentTag);
}
if (options.collapseWhitespace) {
if (!stackNoTrimWhitespace.length && _canTrimWhitespace(currentTag, currentAttrs)) {
- text = trimWhitespace(text);
+ text = (prevTag || nextTag) ? collapseWhitespaceSmart(text, prevTag, nextTag) : trimWhitespace(text);
}
if (!stackNoCollapseWhitespace.length && _canCollapseWhitespace(currentTag, currentAttrs)) {
text = collapseWhitespace(text);
}
});
- results.push.apply(results, buffer)
+ results.push.apply(results, buffer);
var str = results.join('');
log('minified in: ' + (new Date() - t) + 'ms');
return str;
}
- // export
- global.minify = minify;
+ // for CommonJS enviroments, export everything
+ if ( typeof exports !== "undefined" ) {
+ exports.minify = minify;
+ } else {
+ global.minify = minify;
+ }
-})(typeof exports === 'undefined' ? window : exports);/*!
+}(this));
+/*!
* HTMLLint (to be used in conjunction with HTMLMinifier)
*
- * Copyright (c) 2010 Juriy "kangax" Zaytsev
+ * Copyright (c) 2010-2013 Juriy "kangax" Zaytsev
* Licensed under the MIT license.
*
*/
global.HTMLLint = Lint;
-})(typeof exports === 'undefined' ? window : exports);
+})(typeof exports === 'undefined' ? this : exports);
}
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;
}