html-minifier
Advanced tools
Comparing version 1.2.0 to 1.3.0
47
cli.js
@@ -7,3 +7,3 @@ #!/usr/bin/env node | ||
* | ||
* Copyright (c) 2014-2015 Zoltan Frombach | ||
* Copyright (c) 2014-2016 Zoltan Frombach | ||
* | ||
@@ -29,4 +29,2 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
/* globals JSON:true */ | ||
'use strict'; | ||
@@ -52,4 +50,4 @@ | ||
var usage = appName + ' [OPTIONS] [FILE(s)]\n\n'; | ||
usage += ' If no input file(s) specified then STDIN will be used for input.\n'; | ||
usage += ' If more than one input file specified those will be concatenated and minified together.\n\n'; | ||
usage += ' If no input files are specified then STDIN will be used for input.\n'; | ||
usage += ' If more than one input file is specified then those will be concatenated and minified together.\n\n'; | ||
usage += ' When you specify a config file with the --config-file option (see sample-cli-config-file.conf for format)\n'; | ||
@@ -206,16 +204,16 @@ usage += ' you can still override some of its contents by providing individual command line options, too.\n\n'; | ||
catch (e) { | ||
if (e.code === 'EEXIST') { | ||
return; | ||
if (e.code !== 'EEXIST') { | ||
cli.fatal('Can not create directory ' + path); | ||
return 3; | ||
} | ||
cli.fatal('Can not create directory ' + path); | ||
cli.exit(3); | ||
} | ||
return 0; | ||
} | ||
function processDirectory(inputDir, outputDir) { | ||
createDirectory(outputDir); | ||
var fileList = fs.readdirSync(inputDir); | ||
var status = 0; | ||
var status = createDirectory(outputDir); | ||
fileList.forEach(function(fileName) { | ||
for (var i = 0; status === 0 && i < fileList.length; i++) { | ||
var fileName = fileList[i]; | ||
var inputFilePath = inputDir + '/' + fileName; | ||
@@ -226,11 +224,11 @@ var outputFilePath = outputDir + '/' + fileName; | ||
if (stat.isDirectory()) { | ||
processDirectory(inputFilePath, outputFilePath); | ||
return; | ||
status = processDirectory(inputFilePath, outputFilePath); | ||
} | ||
else { | ||
var originalContent = fs.readFileSync(inputFilePath, 'utf8'); | ||
status = runMinify(originalContent, outputFilePath); | ||
} | ||
} | ||
var originalContent = fs.readFileSync(inputFilePath, 'utf8'); | ||
status = runMinify(originalContent, outputFilePath); | ||
}); | ||
cli.exit(status); | ||
return status; | ||
} | ||
@@ -307,3 +305,3 @@ | ||
if (!inputDir) { | ||
cli.error('The option output-dir need to be use with the option include-dir. If you are working with only 1 file, use -o.'); | ||
cli.error('The option output-dir needs to be used with the option input-dir. If you are working with a single file, use -o.'); | ||
cli.exit(2); | ||
@@ -316,3 +314,3 @@ } | ||
catch (e) { | ||
cli.error('The input directory does not exits'); | ||
cli.error('The input directory does not exist'); | ||
cli.exit(2); | ||
@@ -328,12 +326,9 @@ } | ||
try { | ||
processDirectory(inputDir, outputDir); | ||
cli.exit(processDirectory(inputDir, outputDir)); | ||
} | ||
catch (e) { | ||
cli.error('Something wrong happened'); | ||
cli.error('Error while processing input files'); | ||
cli.error(e); | ||
cli.exit(3); | ||
} | ||
cli.exit(0); | ||
return; | ||
} | ||
@@ -340,0 +335,0 @@ |
/*! | ||
* HTMLMinifier v1.2.0 (http://kangax.github.io/html-minifier/) | ||
* HTMLMinifier v1.3.0 (http://kangax.github.io/html-minifier/) | ||
* Copyright 2010-2016 Juriy "kangax" Zaytsev | ||
@@ -40,14 +40,33 @@ * Licensed under the MIT license | ||
// Regular Expressions for parsing tags and attributes | ||
var singleAttrIdentifier = /([\w:\.-]+)/, | ||
var singleAttrIdentifier = /([^\s"'<>\/=]+)/, | ||
singleAttrAssign = /=/, | ||
singleAttrAssigns = [ singleAttrAssign ], | ||
singleAttrValues = [ | ||
/"((?:\\.|[^"])*)"/.source, // attr value double quotes | ||
/'((?:\\.|[^'])*)'/.source, // attr value, single quotes | ||
/([^>\s]+)/.source // attr value, no quotes | ||
// attr value double quotes | ||
/"([^"]*)"+/.source, | ||
// attr value, single quotes | ||
/'([^']*)'+/.source, | ||
// attr value, no quotes | ||
/([^\s"'=<>`]+)/.source | ||
], | ||
startTagOpen = /^<([\w:-]+)/, | ||
startTagClose = /\s*(\/?)>/, | ||
endTag = /^<\/([\w:-]+)[^>]*>/, | ||
endingSlash = /\/>$/, | ||
// https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName | ||
qnameCapture = (function() { | ||
var ncname; | ||
if (typeof require === 'function') { | ||
ncname = require('ncname'); | ||
} | ||
else { | ||
ncname = global.NCName; | ||
} | ||
if (ncname) { | ||
ncname = ncname.source.slice(1, -1); | ||
} | ||
else { | ||
ncname = '[:A-Za-z_][:\\w\\-\\.]*'; | ||
} | ||
return '((?:' + ncname + '\\:)?' + ncname + ')'; | ||
})(), | ||
startTagOpen = new RegExp('^<' + qnameCapture), | ||
startTagClose = /^\s*(\/?)>/, | ||
endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>'), | ||
doctype = /^<!DOCTYPE [^>]+>/i; | ||
@@ -76,71 +95,25 @@ | ||
var reCache = {}, stackedTag, reStackedTag, tagMatch; | ||
// Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content | ||
var phrasingOnly = makeMap('abbr,b,bdi,bdo,button,cite,code,data,dfn,em,h1,h2,h3,h4,h5,h6,i,kbd,label,legend,mark,meter,output,p,pre,progress,q,rp,rt,s,samp,small,span,strong,sub,sup,time,u,var'); | ||
var phrasing = makeMap('a,abbr,area,audio,b,bdi,bdo,br,button,canvas,cite,code,data,datalist,del,dfn,em,embed,i,iframe,img,input,ins,kbd,keygen,label,link,main,map,mark,math,menu,meter,nav,noscript,object,ol,output,p,picture,pre,progress,q,ruby,s,samp,script,section,select,small,span,strong,sub,sup,svg,table,template,textarea,time,u,ul,var,video,wbr'); | ||
function startTagForHandler( handler ) { | ||
var customStartTagAttrs; | ||
var reCache = {}; | ||
var startTagAttrs = new RegExp( | ||
'(?:\\s*[\\w:\\.-]+' | ||
+ '(?:\\s*' | ||
+ '(?:' + joinSingleAttrAssigns(handler) + ')' | ||
+ '\\s*(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>\\s]+)' | ||
+ ')?' | ||
+ ')*' | ||
); | ||
if ( handler.customAttrSurround ) { | ||
function attrForHandler(handler) { | ||
var pattern = singleAttrIdentifier.source | ||
+ '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')' | ||
+ '\\s*(?:' + singleAttrValues.join('|') + '))?'; | ||
if (handler.customAttrSurround) { | ||
var attrClauses = []; | ||
for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { | ||
// Capture the custom attribute opening and closing markup surrounding the standard attribute rules | ||
attrClauses[i] = '(?:\\s*' | ||
+ handler.customAttrSurround[i][0].source | ||
+ '\\s*' | ||
+ startTagAttrs.source | ||
+ '\\s*' | ||
+ handler.customAttrSurround[i][1].source | ||
+ ')'; | ||
} | ||
attrClauses.unshift(startTagAttrs.source); | ||
customStartTagAttrs = new RegExp( | ||
'((?:' + attrClauses.join('|') + ')*)' | ||
); | ||
} | ||
else { | ||
// No custom attribute wrappers specified, so just capture the standard attribute rules | ||
customStartTagAttrs = new RegExp('(' + startTagAttrs.source + ')'); | ||
} | ||
return new RegExp(startTagOpen.source + customStartTagAttrs.source + startTagClose.source); | ||
} | ||
function attrForHandler( handler ) { | ||
var singleAttr = new RegExp( | ||
singleAttrIdentifier.source | ||
+ '(?:\\s*' | ||
+ '(' + joinSingleAttrAssigns( handler ) + ')' | ||
+ '\\s*' | ||
+ '(?:' | ||
+ singleAttrValues.join('|') | ||
+ ')' | ||
+ ')?' | ||
); | ||
if ( handler.customAttrSurround ) { | ||
var attrClauses = []; | ||
for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { | ||
attrClauses[i] = '(?:' | ||
+ '(' + handler.customAttrSurround[i][0].source + ')\\s*' | ||
+ singleAttr.source | ||
+ pattern | ||
+ '\\s*(' + handler.customAttrSurround[i][1].source + ')' | ||
+ ')'; | ||
} | ||
attrClauses.unshift('(?:' + singleAttr.source + ')'); | ||
return new RegExp(attrClauses.join('|'), 'g'); | ||
attrClauses.push('(?:' + pattern + ')'); | ||
pattern = '(?:' + attrClauses.join('|') + ')'; | ||
} | ||
else { | ||
return new RegExp(singleAttr.source, 'g'); | ||
} | ||
return new RegExp('^\\s*' + pattern); | ||
} | ||
@@ -157,89 +130,88 @@ | ||
var HTMLParser = global.HTMLParser = function( html, handler ) { | ||
var index, match, stack = [], last = html, prevTag, nextTag; | ||
stack.last = function() { | ||
var last = this[ this.length - 1 ]; | ||
return last && last.tag; | ||
}; | ||
var startTag = startTagForHandler(handler); | ||
var attr = attrForHandler(handler); | ||
var stack = [], lastTag; | ||
var attribute = attrForHandler(handler); | ||
var last, prevTag, nextTag; | ||
while ( html ) { | ||
last = html; | ||
// Make sure we're not in a script or style element | ||
if ( !stack.last() || !special[ stack.last() ] ) { | ||
if ( !lastTag || !special[ lastTag ] ) { | ||
var textEnd = html.indexOf('<'); | ||
if (textEnd === 0) { | ||
// Comment: | ||
if ( /^<!--/.test( html ) ) { | ||
var commentEnd = html.indexOf('-->'); | ||
// Comment: | ||
if ( /^<!--/.test( html ) ) { | ||
index = html.indexOf('-->'); | ||
if ( index >= 0 ) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring( 4, index ) ); | ||
if ( commentEnd >= 0 ) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring( 4, commentEnd ) ); | ||
} | ||
html = html.substring( commentEnd + 3 ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
html = html.substring( index + 3 ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
} | ||
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment | ||
if ( /^<!\[/.test( html ) ) { | ||
index = html.indexOf(']>'); | ||
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment | ||
if ( /^<!\[/.test( html ) ) { | ||
var conditionalEnd = html.indexOf(']>'); | ||
if (index >= 0) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring(2, index + 1 ), true /* non-standard */ ); | ||
if (conditionalEnd >= 0) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring(2, conditionalEnd + 1 ), true /* non-standard */ ); | ||
} | ||
html = html.substring( conditionalEnd + 2 ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
html = html.substring( index + 2 ); | ||
} | ||
// Doctype: | ||
var doctypeMatch = html.match( doctype ); | ||
if ( doctypeMatch ) { | ||
if ( handler.doctype ) { | ||
handler.doctype( doctypeMatch[0] ); | ||
} | ||
html = html.substring( doctypeMatch[0].length ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
} | ||
// Doctype: | ||
if ( (match = doctype.exec( html )) ) { | ||
if ( handler.doctype ) { | ||
handler.doctype( match[0] ); | ||
// End tag: | ||
var endTagMatch = html.match( endTag ); | ||
if ( endTagMatch ) { | ||
html = html.substring( endTagMatch[0].length ); | ||
endTagMatch[0].replace( endTag, parseEndTag ); | ||
prevTag = '/' + endTagMatch[1].toLowerCase(); | ||
continue; | ||
} | ||
html = html.substring( match[0].length ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
// End tag: | ||
if ( /^<\//.test( html ) ) { | ||
match = html.match( endTag ); | ||
if ( match ) { | ||
html = html.substring( match[0].length ); | ||
match[0].replace( endTag, parseEndTag ); | ||
prevTag = '/' + match[1].toLowerCase(); | ||
// Start tag: | ||
var startTagMatch = parseStartTag(html); | ||
if ( startTagMatch ) { | ||
html = startTagMatch.rest; | ||
handleStartTag(startTagMatch); | ||
prevTag = startTagMatch.tagName.toLowerCase(); | ||
continue; | ||
} | ||
} | ||
var text; | ||
if (textEnd >= 0) { | ||
text = html.substring( 0, textEnd ); | ||
html = html.substring( textEnd ); | ||
} | ||
// Start tag: | ||
if ( /^</.test( html ) ) { | ||
match = html.match( startTag ); | ||
if ( match ) { | ||
html = html.substring( match[0].length ); | ||
match[0].replace( startTag, parseStartTag ); | ||
prevTag = match[1].toLowerCase(); | ||
continue; | ||
} | ||
else { | ||
text = html; | ||
html = ''; | ||
} | ||
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]; | ||
var nextTagMatch = parseStartTag(html); | ||
if (nextTagMatch) { | ||
nextTag = nextTagMatch.tagName; | ||
} | ||
else { | ||
tagMatch = html.match( endTag ); | ||
if (tagMatch) { | ||
nextTag = '/' + tagMatch[1]; | ||
nextTagMatch = html.match( endTag ); | ||
if (nextTagMatch) { | ||
nextTag = '/' + nextTagMatch[1]; | ||
} | ||
@@ -258,6 +230,5 @@ else { | ||
else { | ||
var stackedTag = lastTag.toLowerCase(); | ||
var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)<\/' + stackedTag + '[^>]*>', 'i')); | ||
stackedTag = stack.last().toLowerCase(); | ||
reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)<\/' + stackedTag + '[^>]*>', 'i')); | ||
html = html.replace(reStackedTag, function(all, text) { | ||
@@ -277,3 +248,3 @@ if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') { | ||
parseEndTag( '', stackedTag ); | ||
parseEndTag( '</' + stackedTag + '>', stackedTag ); | ||
} | ||
@@ -284,24 +255,51 @@ | ||
} | ||
last = html; | ||
} | ||
// Clean up any remaining tags | ||
parseEndTag(); | ||
if (!handler.partialMarkup) { | ||
// Clean up any remaining tags | ||
parseEndTag(); | ||
} | ||
function parseStartTag( tag, tagName, rest, unary ) { | ||
var unarySlash = false; | ||
function parseStartTag(input) { | ||
var start = input.match(startTagOpen); | ||
if (start) { | ||
var match = { | ||
tagName: start[1], | ||
attrs: [] | ||
}; | ||
input = input.slice(start[0].length); | ||
var end, attr; | ||
while (!(end = input.match(startTagClose)) && (attr = input.match(attribute))) { | ||
input = input.slice(attr[0].length); | ||
match.attrs.push(attr); | ||
} | ||
if (end) { | ||
match.unarySlash = end[1]; | ||
match.rest = input.slice(end[0].length); | ||
return match; | ||
} | ||
} | ||
} | ||
while ( !handler.html5 && stack.last() && inline[ stack.last() ]) { | ||
parseEndTag( '', stack.last() ); | ||
function handleStartTag(match) { | ||
var tagName = match.tagName; | ||
var unarySlash = match.unarySlash; | ||
if (handler.html5 && lastTag && phrasingOnly[lastTag] && !phrasing[tagName]) { | ||
parseEndTag( '', lastTag ); | ||
} | ||
if ( closeSelf[ tagName ] && stack.last() === tagName ) { | ||
if (!handler.html5) { | ||
while (lastTag && inline[ lastTag ]) { | ||
parseEndTag( '', lastTag ); | ||
} | ||
} | ||
if ( closeSelf[ tagName ] && lastTag === tagName ) { | ||
parseEndTag( '', tagName ); | ||
} | ||
unary = empty[ tagName ] || !!unary; | ||
var unary = empty[ tagName ] || tagName === 'html' && lastTag === 'head' || !!unarySlash; | ||
var attrs = []; | ||
rest.replace(attr, function () { | ||
var attrs = match.attrs.map(function(args) { | ||
var name, value, fallbackValue, customOpen, customClose, customAssign, quote; | ||
@@ -311,31 +309,19 @@ var ncp = 7; // number of captured parts, scalar | ||
// hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 | ||
if (IS_REGEX_CAPTURING_BROKEN && arguments[0].indexOf('""') === -1) { | ||
if (arguments[3] === '') { arguments[3] = undefined; } | ||
if (arguments[4] === '') { arguments[4] = undefined; } | ||
if (arguments[5] === '') { arguments[5] = undefined; } | ||
if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { | ||
if (args[3] === '') { args[3] = undefined; } | ||
if (args[4] === '') { args[4] = undefined; } | ||
if (args[5] === '') { args[5] = undefined; } | ||
} | ||
name = arguments[1]; | ||
if ( name ) { | ||
customAssign = arguments[2]; | ||
fallbackValue = arguments[3]; | ||
value = fallbackValue || arguments[4] || arguments[5]; | ||
if (customAssign) { | ||
quote = arguments[0].charAt(name.length + customAssign.length); | ||
quote = (quote === '\'' || quote === '"') ? quote : ''; | ||
} | ||
} | ||
else if ( handler.customAttrSurround ) { | ||
for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { | ||
name = arguments[i * ncp + 7]; | ||
customAssign = arguments[i * ncp + 8]; | ||
if ( name ) { | ||
fallbackValue = arguments[i * ncp + 9]; | ||
value = fallbackValue | ||
|| arguments[i * ncp + 10] | ||
|| arguments[i * ncp + 11]; | ||
customOpen = arguments[i * ncp + 6]; | ||
customClose = arguments[i * ncp + 12]; | ||
var j = 1; | ||
if (handler.customAttrSurround) { | ||
for (var i = 0, l = handler.customAttrSurround.length; i < l; i++, j += ncp) { | ||
name = args[j + 1]; | ||
customAssign = args[j + 2]; | ||
if (name) { | ||
fallbackValue = args[j + 3]; | ||
value = fallbackValue || args[j + 4] || args[j + 5]; | ||
quote = fallbackValue ? '"' : value ? '\'' : ''; | ||
customOpen = args[j]; | ||
customClose = args[j + 6]; | ||
break; | ||
@@ -346,7 +332,14 @@ } | ||
if ( value === undefined ) { | ||
if (!name && (name = args[j])) { | ||
customAssign = args[j + 1]; | ||
fallbackValue = args[j + 2]; | ||
value = fallbackValue || args[j + 3] || args[j + 4]; | ||
quote = fallbackValue ? '"' : value ? '\'' : ''; | ||
} | ||
if (value === undefined) { | ||
value = fillAttrs[name] ? name : fallbackValue; | ||
} | ||
attrs.push({ | ||
return { | ||
name: name, | ||
@@ -358,3 +351,3 @@ value: value, | ||
quote: quote || '' | ||
}); | ||
}; | ||
}); | ||
@@ -364,8 +357,6 @@ | ||
stack.push( { tag: tagName, attrs: attrs } ); | ||
lastTag = tagName; | ||
unarySlash = ''; | ||
} | ||
else { | ||
unarySlash = tag.match( endingSlash ); | ||
} | ||
if ( handler.start ) { | ||
@@ -397,3 +388,3 @@ handler.start( tagName, attrs, unary, unarySlash ); | ||
if ( handler.end ) { | ||
handler.end( stack[ i ].tag, stack[ i ].attrs ); | ||
handler.end( stack[ i ].tag, stack[ i ].attrs, i > pos || !tag ); | ||
} | ||
@@ -404,2 +395,3 @@ } | ||
stack.length = pos; | ||
lastTag = pos && stack[ pos - 1 ].tag; | ||
} | ||
@@ -552,4 +544,2 @@ } | ||
/* global CleanCSS */ | ||
(function(global) { | ||
@@ -591,4 +581,8 @@ 'use strict'; | ||
function compressWhitespace(spaces) { | ||
return spaces === '\t' ? spaces : ' '; | ||
} | ||
function collapseWhitespaceAll(str) { | ||
return str ? str.replace(/[\t\n\r ]+/g, ' ') : str; | ||
return str ? str.replace(/[\t\n\r ]+/g, compressWhitespace) : str; | ||
} | ||
@@ -614,6 +608,6 @@ | ||
if (options.preserveLineBreaks) { | ||
str = str.replace(/^[\t ]*[\n\r]+[\t\n\r ]*/, function() { | ||
str = str.replace(/^[\t ]*[\n\r][\t\n\r ]*/, function() { | ||
lineBreakBefore = '\n'; | ||
return ''; | ||
}).replace(/[\t\n\r ]*[\n\r]+[\t ]*$/, function() { | ||
}).replace(/[\t\n\r ]*[\n\r][\t ]*$/, function() { | ||
lineBreakAfter = '\n'; | ||
@@ -625,7 +619,7 @@ return ''; | ||
if (trimLeft) { | ||
str = str.replace(/^\s+/, !lineBreakBefore && options.conservativeCollapse ? ' ' : ''); | ||
str = str.replace(/^\s+/, !lineBreakBefore && options.conservativeCollapse ? compressWhitespace : ''); | ||
} | ||
if (trimRight) { | ||
str = str.replace(/\s+$/, !lineBreakAfter && options.conservativeCollapse ? ' ' : ''); | ||
str = str.replace(/\s+$/, !lineBreakAfter && options.conservativeCollapse ? compressWhitespace : ''); | ||
} | ||
@@ -641,11 +635,18 @@ | ||
// array of non-empty element tags that will maintain a single space outside of them | ||
// non-empty tags that will maintain whitespace around them | ||
var inlineTags = createMapFromString('a,abbr,acronym,b,bdi,bdo,big,button,cite,code,del,dfn,em,font,i,ins,kbd,mark,math,q,rt,rp,s,samp,small,span,strike,strong,sub,sup,svg,time,tt,u,var'); | ||
// non-empty tags that will maintain whitespace within them | ||
var inlineTextTags = createMapFromString('a,abbr,acronym,b,big,del,em,font,i,ins,kbd,mark,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var'); | ||
// self-closing tags that will maintain whitespace around them | ||
var selfClosingInlineTags = createMapFromString('comment,img,input'); | ||
function collapseWhitespaceSmart(str, prevTag, nextTag, options) { | ||
var trimLeft = prevTag && !selfClosingInlineTags(prevTag) && | ||
(options.collapseInlineTagWhitespace || prevTag.charAt(0) !== '/' || !inlineTags(prevTag.substr(1))); | ||
var trimRight = nextTag && !selfClosingInlineTags(nextTag) && | ||
(options.collapseInlineTagWhitespace || nextTag.charAt(0) === '/' || !inlineTags(nextTag)); | ||
var trimLeft = prevTag && !selfClosingInlineTags(prevTag); | ||
if (trimLeft && !options.collapseInlineTagWhitespace) { | ||
trimLeft = prevTag.charAt(0) === '/' ? !inlineTags(prevTag.slice(1)) : !inlineTextTags(prevTag); | ||
} | ||
var trimRight = nextTag && !selfClosingInlineTags(nextTag); | ||
if (trimRight && !options.collapseInlineTagWhitespace) { | ||
trimRight = nextTag.charAt(0) === '/' ? !inlineTextTags(nextTag.slice(1)) : !inlineTags(nextTag); | ||
} | ||
return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag); | ||
@@ -655,3 +656,3 @@ } | ||
function isConditionalComment(text) { | ||
return ((/\[if[^\]]+\]/).test(text) || (/\s*((?:<!)?\[endif\])$/).test(text)); | ||
return /^\[if\s[^\]]+\]|\[endif\]$/.test(text); | ||
} | ||
@@ -734,10 +735,2 @@ | ||
function isScriptTypeAttribute(tag, attrName, attrValue) { | ||
return ( | ||
tag === 'script' && | ||
attrName === 'type' && | ||
trimWhitespace(attrValue.toLowerCase()) === 'text/javascript' | ||
); | ||
} | ||
// https://mathiasbynens.be/demo/javascript-mime-type | ||
@@ -754,2 +747,10 @@ // https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type | ||
function isScriptTypeAttribute(tag, attrName, attrValue) { | ||
return ( | ||
tag === 'script' && | ||
attrName === 'type' && | ||
executableScriptsMimetypes(trimWhitespace(attrValue.toLowerCase())) | ||
); | ||
} | ||
function isExecutableScript(tag, attrs) { | ||
@@ -762,3 +763,3 @@ if (tag !== 'script') { | ||
if (attrName === 'type') { | ||
var attrValue = trimWhitespace(attrs[i].value).split(/;/, 2)[0].toLowerCase(); | ||
var attrValue = trimWhitespace(attrs[i].value.split(/;/, 2)[0]).toLowerCase(); | ||
return attrValue === '' || executableScriptsMimetypes(attrValue); | ||
@@ -778,19 +779,7 @@ } | ||
var enumeratedAttributeValues = { | ||
draggable: ['true', 'false'] // defaults to 'auto' | ||
}; | ||
var isSimpleBoolean = createMapFromString('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible'); | ||
var isBooleanValue = createMapFromString('true,false'); | ||
function isBooleanAttribute(attrName, attrValue) { | ||
var isSimpleBoolean = (/^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|truespeed|typemustmatch|visible)$/i).test(attrName); | ||
if (isSimpleBoolean) { | ||
return true; | ||
} | ||
var attrValueEnumeration = enumeratedAttributeValues[attrName.toLowerCase()]; | ||
if (!attrValueEnumeration) { | ||
return false; | ||
} | ||
else { | ||
return (-1 === attrValueEnumeration.indexOf(attrValue.toLowerCase())); | ||
} | ||
return isSimpleBoolean(attrName) || attrName === 'draggable' && !isBooleanValue(attrValue); | ||
} | ||
@@ -882,2 +871,5 @@ | ||
} | ||
else if (tag === 'script' && attrName === 'type') { | ||
attrValue = trimWhitespace(attrValue.replace(/\s*;\s*/g, ';')); | ||
} | ||
return attrValue; | ||
@@ -913,6 +905,6 @@ } | ||
function cleanConditionalComment(comment) { | ||
return comment | ||
.replace(/^(\[[^\]]+\]>)\s*/, '$1') | ||
.replace(/\s*(<!\[endif\])$/, '$1'); | ||
function cleanConditionalComment(comment, options) { | ||
return comment.replace(/^(\[if\s[^\]]+\]>)([\s\S]*?)(<!\[endif\])$/, function(match, prefix, text, suffix) { | ||
return prefix + minify(text, options, true) + suffix; | ||
}); | ||
} | ||
@@ -955,2 +947,3 @@ | ||
// - </rb>, </rt>, </rtc>, </rp> & </tfoot> follow http://www.w3.org/TR/html5/syntax.html#optional-tags | ||
// - retain all tags which are adjacent to non-standard HTML tags | ||
var optionalStartTags = createMapFromString('html,head,body,colgroup,tbody'); | ||
@@ -971,2 +964,4 @@ var optionalEndTags = createMapFromString('html,head,body,li,dt,dd,p,rb,rt,rtc,rp,optgroup,option,colgroup,caption,thead,tbody,tfoot,tr,td,th'); | ||
var looseTags = createMapFromString('head,colgroup,caption'); | ||
var trailingTags = createMapFromString('dt,thead'); | ||
var htmlTags = createMapFromString('a,abbr,acronym,address,applet,area,article,aside,audio,b,base,basefont,bdi,bdo,bgsound,big,blink,blockquote,body,br,button,canvas,caption,center,cite,code,col,colgroup,command,content,data,datalist,dd,del,details,dfn,dialog,dir,div,dl,dt,element,em,embed,fieldset,figcaption,figure,font,footer,form,frame,frameset,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,image,img,input,ins,isindex,kbd,keygen,label,legend,li,link,listing,main,map,mark,marquee,menu,menuitem,meta,meter,multicol,nav,nobr,noembed,noframes,noscript,object,ol,optgroup,option,output,p,param,picture,plaintext,pre,progress,q,rp,rt,rtc,ruby,s,samp,script,section,select,shadow,small,source,spacer,span,strike,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,tt,u,ul,var,video,wbr,xmp'); | ||
@@ -1049,15 +1044,38 @@ function canRemoveParentTag(optionalStartTag, tag) { | ||
function canRemoveElement(tag, attrs) { | ||
if (tag === 'textarea') { | ||
return false; | ||
function hasAttrName(name, attrs) { | ||
for (var i = attrs.length - 1; i >= 0; i--) { | ||
if (attrs[i].name === name) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
if (tag === 'script') { | ||
for (var i = attrs.length - 1; i >= 0; i--) { | ||
if (attrs[i].name === 'src') { | ||
function canRemoveElement(tag, attrs) { | ||
switch (tag) { | ||
case 'textarea': | ||
return false; | ||
case 'audio': | ||
case 'script': | ||
case 'video': | ||
if (hasAttrName('src', attrs)) { | ||
return false; | ||
} | ||
} | ||
break; | ||
case 'iframe': | ||
if (hasAttrName('src', attrs) || hasAttrName('srcdoc', attrs)) { | ||
return false; | ||
} | ||
break; | ||
case 'object': | ||
if (hasAttrName('data', attrs)) { | ||
return false; | ||
} | ||
break; | ||
case 'applet': | ||
if (hasAttrName('code', attrs)) { | ||
return false; | ||
} | ||
break; | ||
} | ||
return true; | ||
@@ -1132,3 +1150,3 @@ } | ||
if (attrValue === undefined || (options.collapseBooleanAttributes && | ||
isBooleanAttribute(attrName, attrValue))) { | ||
isBooleanAttribute(attrName.toLowerCase(), attrValue.toLowerCase()))) { | ||
attrFragment = attrName; | ||
@@ -1146,6 +1164,8 @@ if (!isLast) { | ||
function setDefaultTesters(options) { | ||
function processOptions(options) { | ||
if (!('includeAutoGeneratedTags' in options)) { | ||
options.includeAutoGeneratedTags = true; | ||
} | ||
var defaultTesters = ['canCollapseWhitespace', 'canTrimWhitespace']; | ||
for (var i = 0, len = defaultTesters.length; i < len; i++) { | ||
@@ -1158,119 +1178,97 @@ if (!options[defaultTesters[i]]) { | ||
} | ||
} | ||
function minifyURLs(text, options) { | ||
if (typeof options !== 'object') { | ||
options = { }; | ||
if (options.minifyURLs && typeof options.minifyURLs !== 'object') { | ||
options.minifyURLs = { }; | ||
} | ||
try { | ||
// try to get global reference first | ||
var __RelateUrl = global.RelateUrl; | ||
if (typeof __RelateUrl === 'undefined' && typeof require === 'function') { | ||
__RelateUrl = require('relateurl'); | ||
if (options.minifyJS) { | ||
if (typeof options.minifyJS !== 'object') { | ||
options.minifyJS = { }; | ||
} | ||
options.minifyJS.fromString = true; | ||
(options.minifyJS.output || (options.minifyJS.output = { })).inline_script = true; | ||
} | ||
// noop | ||
if (!__RelateUrl) { | ||
return text; | ||
if (options.minifyCSS) { | ||
if (typeof options.minifyCSS !== 'object') { | ||
options.minifyCSS = { }; | ||
} | ||
if (__RelateUrl.relate) { | ||
return __RelateUrl.relate(text, options); | ||
if (typeof options.minifyCSS.advanced === 'undefined') { | ||
options.minifyCSS.advanced = false; | ||
} | ||
else { | ||
return text; | ||
} | ||
} | ||
catch (err) { | ||
log(err); | ||
} | ||
} | ||
function noop(text) { | ||
return text; | ||
} | ||
function minifyJS(text, options) { | ||
if (typeof options !== 'object') { | ||
options = { }; | ||
function getModule(name, path) { | ||
// try to get global reference first | ||
var result = global[name]; | ||
if (typeof result === 'undefined' && typeof require === 'function') { | ||
result = require(path); | ||
} | ||
options.fromString = true; | ||
var outputOptions = options.output || {}; | ||
outputOptions.inline_script = true; | ||
options.output = outputOptions; | ||
return result; | ||
} | ||
try { | ||
// try to get global reference first | ||
var __UglifyJS = global.UglifyJS; | ||
if (typeof __UglifyJS === 'undefined' && typeof require === 'function') { | ||
__UglifyJS = require('uglify-js'); | ||
} | ||
// noop | ||
if (!__UglifyJS) { | ||
return text; | ||
} | ||
if (__UglifyJS.minify) { | ||
return __UglifyJS.minify(text, options).code; | ||
} | ||
else if (__UglifyJS.parse) { | ||
var ast = __UglifyJS.parse(text); | ||
ast.figure_out_scope(); | ||
var compressor = __UglifyJS.Compressor(); | ||
var compressedAst = ast.transform(compressor); | ||
compressedAst.figure_out_scope(); | ||
compressedAst.compute_char_frequency(); | ||
if (options.mangle !== false) { | ||
compressedAst.mangle_names(); | ||
var minifyURLs = (function() { | ||
var RelateUrl = getModule('RelateUrl', 'relateurl'); | ||
if (RelateUrl && RelateUrl.relate) { | ||
return function(text, options) { | ||
try { | ||
return RelateUrl.relate(text, options); | ||
} | ||
var stream = __UglifyJS.OutputStream(options.output); | ||
compressedAst.print(stream); | ||
return stream.toString(); | ||
} | ||
else { | ||
return text; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
catch (err) { | ||
log(err); | ||
else { | ||
return noop; | ||
} | ||
return text; | ||
} | ||
})(); | ||
function minifyCSS(text, options, inline) { | ||
if (typeof options !== 'object') { | ||
options = { }; | ||
var minifyJS = (function() { | ||
var UglifyJS = getModule('UglifyJS', 'uglify-js'); | ||
if (UglifyJS && UglifyJS.minify) { | ||
return function(text, options) { | ||
try { | ||
return UglifyJS.minify(text, options).code; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
if (typeof options.advanced === 'undefined') { | ||
options.advanced = false; | ||
else { | ||
return noop; | ||
} | ||
try { | ||
var cleanCSS; | ||
})(); | ||
if (typeof CleanCSS !== 'undefined') { | ||
cleanCSS = new CleanCSS(options); | ||
} | ||
else if (typeof require === 'function') { | ||
var CleanCSSModule = require('clean-css'); | ||
cleanCSS = new CleanCSSModule(options); | ||
} | ||
if (inline) { | ||
return unwrapCSS(cleanCSS.minify(wrapCSS(text)).styles); | ||
} | ||
else { | ||
return cleanCSS.minify(text).styles; | ||
} | ||
var minifyCSS = (function() { | ||
var CleanCSS = getModule('CleanCSS', 'clean-css'); | ||
if (CleanCSS) { | ||
return function(text, options, inline) { | ||
try { | ||
var cleanCSS = new CleanCSS(options); | ||
if (inline) { | ||
return unwrapCSS(cleanCSS.minify(wrapCSS(text)).styles); | ||
} | ||
else { | ||
return cleanCSS.minify(text).styles; | ||
} | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
catch (err) { | ||
log(err); | ||
else { | ||
return noop; | ||
} | ||
return text; | ||
} | ||
})(); | ||
@@ -1285,14 +1283,12 @@ function uniqueId(value) { | ||
function minify(value, options) { | ||
function minify(value, options, partialMarkup) { | ||
options = options || {}; | ||
var optionsStack = []; | ||
processOptions(options); | ||
value = options.collapseWhitespace ? trimWhitespace(value) : value; | ||
value = trimWhitespace(value); | ||
setDefaultTesters(options); | ||
var results = [ ], | ||
buffer = [ ], | ||
var buffer = [ ], | ||
charsPrevTag, | ||
currentChars = '', | ||
hasChars, | ||
currentTag = '', | ||
@@ -1311,13 +1307,14 @@ currentAttrs = [], | ||
if (~value.indexOf('<!-- htmlmin:ignore -->')) { | ||
uidIgnore = '<!--!' + uniqueId(value) + '-->'; | ||
// temporarily replace ignored chunks with comments, | ||
// so that we don't have to worry what's there. | ||
// for all we care there might be | ||
// completely-horribly-broken-alien-non-html-emoj-cthulhu-filled content | ||
value = value.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g, function(match, group1) { | ||
ignoredMarkupChunks.push(group1); | ||
return uidIgnore; | ||
}); | ||
} | ||
// temporarily replace ignored chunks with comments, | ||
// so that we don't have to worry what's there. | ||
// for all we care there might be | ||
// completely-horribly-broken-alien-non-html-emoj-cthulhu-filled content | ||
value = value.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g, function(match, group1) { | ||
if (!uidIgnore) { | ||
uidIgnore = '<!--!' + uniqueId(value) + '-->'; | ||
} | ||
var token = uidIgnore + ignoredMarkupChunks.length; | ||
ignoredMarkupChunks.push(group1); | ||
return token; | ||
}); | ||
@@ -1331,3 +1328,3 @@ var customFragments = (options.ignoreCustomFragments || [ | ||
if (customFragments.length) { | ||
var reCustomIgnore = new RegExp('\\s*(?:' + customFragments.join('|') + ')\\s*', 'g'); | ||
var reCustomIgnore = new RegExp('\\s*(?:' + customFragments.join('|') + ')+\\s*', 'g'); | ||
// temporarily replace custom ignored fragments with unique attributes | ||
@@ -1338,4 +1335,5 @@ value = value.replace(reCustomIgnore, function(match) { | ||
} | ||
var token = uidAttr + ignoredCustomMarkupChunks.length; | ||
ignoredCustomMarkupChunks.push(match); | ||
return ' ' + uidAttr + ' '; | ||
return '\t' + token + '\t'; | ||
}); | ||
@@ -1365,6 +1363,26 @@ } | ||
} | ||
buffer.length = index; | ||
buffer.length = Math.max(0, index); | ||
} | ||
// look for trailing whitespaces from previously processed text | ||
// which may not be trimmed due to a following comment or an empty | ||
// element which has now been removed | ||
function squashTrailingWhitespace(nextTag) { | ||
var charsIndex; | ||
if (buffer.length > 1 && /^(?:<!|$)/.test(buffer[buffer.length - 1]) && | ||
/\s$/.test(buffer[buffer.length - 2])) { | ||
charsIndex = buffer.length - 2; | ||
} | ||
else if (buffer.length > 0 && /\s$/.test(buffer[buffer.length - 1])) { | ||
charsIndex = buffer.length - 1; | ||
} | ||
if (charsIndex > 0) { | ||
buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(text) { | ||
return collapseWhitespaceSmart(text, 'comment', nextTag, options); | ||
}); | ||
} | ||
} | ||
new HTMLParser(value, { | ||
partialMarkup: partialMarkup, | ||
html5: typeof options.html5 !== 'undefined' ? options.html5 : true, | ||
@@ -1390,3 +1408,6 @@ | ||
charsPrevTag = tag; | ||
currentChars = ''; | ||
if (!inlineTextTags(tag)) { | ||
currentChars = ''; | ||
} | ||
hasChars = false; | ||
currentAttrs = attrs; | ||
@@ -1396,2 +1417,3 @@ | ||
if (optional) { | ||
var htmlTag = htmlTags(tag); | ||
// <html> may be omitted if first thing inside is not comment | ||
@@ -1402,3 +1424,3 @@ // <head> may be omitted if first thing inside is an element | ||
// <tbody> may be omitted if first thing inside is <tr> | ||
if (canRemoveParentTag(optionalStartTag, tag)) { | ||
if (htmlTag && canRemoveParentTag(optionalStartTag, tag)) { | ||
removeStartTag(); | ||
@@ -1408,3 +1430,3 @@ } | ||
// end-tag-followed-by-start-tag omission rules | ||
if (canRemovePrecedingTag(optionalEndTag, tag)) { | ||
if (htmlTag && canRemovePrecedingTag(optionalEndTag, tag)) { | ||
removeEndTag(); | ||
@@ -1420,2 +1442,5 @@ // <colgroup> cannot be omitted if preceding </colgroup> is omitted | ||
if (options.collapseWhitespace) { | ||
if (!stackNoTrimWhitespace.length) { | ||
squashTrailingWhitespace(tag); | ||
} | ||
if (!_canTrimWhitespace(tag, attrs)) { | ||
@@ -1461,3 +1486,3 @@ stackNoTrimWhitespace.push(tag); | ||
}, | ||
end: function(tag, attrs) { | ||
end: function(tag, attrs, autoGenerated) { | ||
var lowerTag = tag.toLowerCase(); | ||
@@ -1467,16 +1492,4 @@ if (lowerTag === 'svg') { | ||
} | ||
tag = options.caseSensitive ? tag : lowerTag; | ||
if (options.removeOptionalTags) { | ||
// </html> or </body> may be omitted if not followed by comment | ||
// </head> may be omitted if not followed by space or comment | ||
// </p> may be omitted if no more content in non-</a> parent | ||
// except for </dt> or </thead>, end tags may be omitted if no more content in parent element | ||
if (optionalEndTag && optionalEndTag !== 'dt' && optionalEndTag !== 'thead' && (optionalEndTag !== 'p' || !pInlineTags(tag))) { | ||
removeEndTag(); | ||
} | ||
optionalEndTag = optionalEndTags(tag) ? tag : ''; | ||
} | ||
// check if current tag is in a whitespace stack | ||
@@ -1490,14 +1503,3 @@ if (options.collapseWhitespace) { | ||
else { | ||
var charsIndex; | ||
if (buffer.length > 1 && buffer[buffer.length - 1] === '' && /\s+$/.test(buffer[buffer.length - 2])) { | ||
charsIndex = buffer.length - 2; | ||
} | ||
else if (buffer.length > 0 && /\s+$/.test(buffer[buffer.length - 1])) { | ||
charsIndex = buffer.length - 1; | ||
} | ||
if (charsIndex > 0) { | ||
buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(text) { | ||
return collapseWhitespaceSmart(text, 'comment', '/' + tag, options); | ||
}); | ||
} | ||
squashTrailingWhitespace('/' + tag); | ||
} | ||
@@ -1513,4 +1515,21 @@ if (stackNoCollapseWhitespace.length && | ||
currentTag = ''; | ||
isElementEmpty = currentChars === ''; | ||
isElementEmpty = !hasChars; | ||
} | ||
if (options.removeOptionalTags) { | ||
// <html>, <head> or <body> may be omitted if the element is empty | ||
if (isElementEmpty && topLevelTags(optionalStartTag)) { | ||
removeStartTag(); | ||
} | ||
optionalStartTag = ''; | ||
// </html> or </body> may be omitted if not followed by comment | ||
// </head> may be omitted if not followed by space or comment | ||
// </p> may be omitted if no more content in non-</a> parent | ||
// except for </dt> or </thead>, end tags may be omitted if no more content in parent element | ||
if (htmlTags(tag) && optionalEndTag && !trailingTags(optionalEndTag) && (optionalEndTag !== 'p' || !pInlineTags(tag))) { | ||
removeEndTag(); | ||
} | ||
optionalEndTag = optionalEndTags(tag) ? tag : ''; | ||
} | ||
if (options.removeEmptyElements && isElementEmpty && canRemoveElement(tag, attrs)) { | ||
@@ -1524,6 +1543,9 @@ // remove last "element" from buffer | ||
// push out everything but the end tag | ||
results.push.apply(results, buffer); | ||
buffer = ['</' + tag + '>']; | ||
if (options.includeAutoGeneratedTags || !autoGenerated) { | ||
buffer.push('</' + tag + '>'); | ||
} | ||
charsPrevTag = '/' + tag; | ||
currentChars = ''; | ||
if (!inlineTextTags(tag)) { | ||
currentChars = ''; | ||
} | ||
} | ||
@@ -1541,3 +1563,3 @@ }, | ||
} | ||
if (buffer.length > 1 && (removed || currentChars.charAt(currentChars.length - 1) === ' ')) { | ||
if (buffer.length > 1 && (removed || / $/.test(currentChars))) { | ||
var charsIndex = buffer.length - 2; | ||
@@ -1550,3 +1572,18 @@ buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(trailingSpaces) { | ||
} | ||
if (prevTag && inlineTextTags(prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag)) { | ||
text = collapseWhitespace(text, options, /(?:^|\s)$/.test(currentChars)); | ||
} | ||
text = prevTag || nextTag ? collapseWhitespaceSmart(text, prevTag, nextTag, options) : trimWhitespace(text); | ||
if (!text && /\s$/.test(currentChars) && prevTag && prevTag.charAt(0) === '/') { | ||
for (var index = buffer.length - 2, endTag = prevTag.slice(1); index >= 0 && _canTrimWhitespace(endTag); index--) { | ||
var str = buffer[index]; | ||
var match = str.match(/^<\/([\w:-]+)>$/); | ||
if (match) { | ||
endTag = match[1]; | ||
} | ||
else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options))) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
@@ -1593,2 +1630,5 @@ if (!stackNoCollapseWhitespace.length) { | ||
currentChars += text; | ||
if (text) { | ||
hasChars = true; | ||
} | ||
if (lint) { | ||
@@ -1602,7 +1642,7 @@ lint.testChars(text); | ||
var suffix = nonStandard ? '>' : '-->'; | ||
if (options.removeComments) { | ||
if (isConditionalComment(text)) { | ||
text = prefix + cleanConditionalComment(text) + suffix; | ||
} | ||
else if (isIgnoredComment(text, options)) { | ||
if (isConditionalComment(text)) { | ||
text = prefix + cleanConditionalComment(text, options) + suffix; | ||
} | ||
else if (options.removeComments) { | ||
if (isIgnoredComment(text, options)) { | ||
text = '<!--' + text + '-->'; | ||
@@ -1638,3 +1678,3 @@ } | ||
// except for </dt> or </thead>, end tags may be omitted if no more content in parent element | ||
if (optionalEndTag && optionalEndTag !== 'dt' && optionalEndTag !== 'thead') { | ||
if (optionalEndTag && !trailingTags(optionalEndTag)) { | ||
removeEndTag(); | ||
@@ -1644,17 +1684,27 @@ } | ||
results.push.apply(results, buffer); | ||
var str = joinResultSegments(results, options); | ||
var str = joinResultSegments(buffer, options); | ||
if (uidAttr) { | ||
str = str.replace(new RegExp('(\\s*)' + uidAttr + '(\\s*)', 'g'), function(match, prefix, suffix) { | ||
var chunk = ignoredCustomMarkupChunks.shift(); | ||
return options.collapseWhitespace ? collapseWhitespace(prefix + chunk + suffix, { | ||
preserveLineBreaks: options.preserveLineBreaks, | ||
conservativeCollapse: true | ||
}, true, true) : chunk; | ||
str = str.replace(new RegExp('(\\s*)' + uidAttr + '([0-9]+)(\\s*)', 'g'), function(match, prefix, index, suffix) { | ||
var chunk = ignoredCustomMarkupChunks[+index]; | ||
if (options.collapseWhitespace) { | ||
if (prefix !== '\t') { | ||
chunk = prefix + chunk; | ||
} | ||
if (suffix !== '\t') { | ||
chunk += suffix; | ||
} | ||
return collapseWhitespace(chunk, { | ||
preserveLineBreaks: options.preserveLineBreaks, | ||
conservativeCollapse: true | ||
}, /^\s/.test(chunk), /\s$/.test(chunk)); | ||
} | ||
else { | ||
return chunk; | ||
} | ||
}); | ||
} | ||
if (uidIgnore) { | ||
str = str.replace(new RegExp(uidIgnore, 'g'), function() { | ||
return ignoredMarkupChunks.shift(); | ||
str = str.replace(new RegExp(uidIgnore + '([0-9]+)', 'g'), function(match, index) { | ||
return ignoredMarkupChunks[+index]; | ||
}); | ||
@@ -1692,12 +1742,8 @@ } | ||
return trimWhitespace(str); | ||
return options.collapseWhitespace ? trimWhitespace(str) : str; | ||
} | ||
// for CommonJS enviroments, export everything | ||
if (typeof exports !== 'undefined') { | ||
exports.minify = minify; | ||
} | ||
else { | ||
global.minify = minify; | ||
} | ||
global.minify = function(value, options) { | ||
return minify(value, options); | ||
}; | ||
@@ -1704,0 +1750,0 @@ }(typeof exports === 'undefined' ? this : exports)); |
/*! | ||
* HTMLMinifier v1.2.0 (http://kangax.github.io/html-minifier/) | ||
* HTMLMinifier v1.3.0 (http://kangax.github.io/html-minifier/) | ||
* Copyright 2010-2016 Juriy "kangax" Zaytsev | ||
* Licensed under the MIT license | ||
*/ | ||
!function(a){"use strict";function b(a){var b,c=new RegExp("(?:\\s*[\\w:\\.-]+(?:\\s*(?:"+d(a)+")\\s*(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>\\s]+))?)*");if(a.customAttrSurround){for(var e=[],f=a.customAttrSurround.length-1;f>=0;f--)e[f]="(?:\\s*"+a.customAttrSurround[f][0].source+"\\s*"+c.source+"\\s*"+a.customAttrSurround[f][1].source+")";e.unshift(c.source),b=new RegExp("((?:"+e.join("|")+")*)")}else b=new RegExp("("+c.source+")");return new RegExp(j.source+b.source+k.source)}function c(a){var b=new RegExp(f.source+"(?:\\s*("+d(a)+")\\s*(?:"+i.join("|")+"))?");if(a.customAttrSurround){for(var c=[],e=a.customAttrSurround.length-1;e>=0;e--)c[e]="(?:("+a.customAttrSurround[e][0].source+")\\s*"+b.source+"\\s*("+a.customAttrSurround[e][1].source+"))";return c.unshift("(?:"+b.source+")"),new RegExp(c.join("|"),"g")}return new RegExp(b.source,"g")}function d(a){return h.concat(a.customAttrAssign||[]).map(function(a){return"(?:"+a.source+")"}).join("|")}function e(a){for(var b={},c=a.split(","),d=0;d<c.length;d++)b[c[d]]=!0,b[c[d].toUpperCase()]=!0;return b}var f=/([\w:\.-]+)/,g=/=/,h=[g],i=[/"((?:\\.|[^"])*)"/.source,/'((?:\\.|[^'])*)'/.source,/([^>\s]+)/.source],j=/^<([\w:-]+)/,k=/\s*(\/?)>/,l=/^<\/([\w:-]+)[^>]*>/,m=/\/>$/,n=/^<!DOCTYPE [^>]+>/i,o=!1;"x".replace(/x(.)?/g,function(a,b){o=""===b});var p,q,r,s=e("area,base,basefont,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),t=e("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,noscript,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,svg,textarea,tt,u,var"),u=e("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),v=e("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"),w=e("script,style"),x={},y=a.HTMLParser=function(a,d){function e(a,b,c,e){for(var g=!1;!d.html5&&k.last()&&t[k.last()];)f("",k.last());u[b]&&k.last()===b&&f("",b),e=s[b]||!!e;var h=[];c.replace(A,function(){var a,b,c,e,f,g,i,j=7;if(o&&-1===arguments[0].indexOf('""')&&(""===arguments[3]&&(arguments[3]=void 0),""===arguments[4]&&(arguments[4]=void 0),""===arguments[5]&&(arguments[5]=void 0)),a=arguments[1])g=arguments[2],c=arguments[3],b=c||arguments[4]||arguments[5],g&&(i=arguments[0].charAt(a.length+g.length),i="'"===i||'"'===i?i:"");else if(d.customAttrSurround)for(var k=d.customAttrSurround.length-1;k>=0;k--)if(a=arguments[k*j+7],g=arguments[k*j+8],a){c=arguments[k*j+9],b=c||arguments[k*j+10]||arguments[k*j+11],e=arguments[k*j+6],f=arguments[k*j+12];break}void 0===b&&(b=v[a]?a:c),h.push({name:a,value:b,customAssign:g||"=",customOpen:e||"",customClose:f||"",quote:i||""})}),e?g=a.match(m):k.push({tag:b,attrs:h}),d.start&&d.start(b,h,e,g)}function f(a,b){var c;if(b){var e=b.toLowerCase();for(c=k.length-1;c>=0&&k[c].tag.toLowerCase()!==e;c--);}else c=0;if(c>=0){for(var f=k.length-1;f>=c;f--)d.end&&d.end(k[f].tag,k[f].attrs);k.length=c}}var g,h,i,j,k=[],y=a;k.last=function(){var a=this[this.length-1];return a&&a.tag};for(var z=b(d),A=c(d);a;){if(k.last()&&w[k.last()])p=k.last().toLowerCase(),q=x[p]||(x[p]=new RegExp("([\\s\\S]*?)</"+p+"[^>]*>","i")),a=a.replace(q,function(a,b){return"script"!==p&&"style"!==p&&"noscript"!==p&&(b=b.replace(/<!--([\s\S]*?)-->/g,"$1").replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g,"$1")),d.chars&&d.chars(b),""}),f("",p);else{if(/^<!--/.test(a)&&(g=a.indexOf("-->"),g>=0)){d.comment&&d.comment(a.substring(4,g)),a=a.substring(g+3),i="";continue}if(/^<!\[/.test(a)&&(g=a.indexOf("]>"),g>=0)){d.comment&&d.comment(a.substring(2,g+1),!0),a=a.substring(g+2),i="";continue}if(h=n.exec(a)){d.doctype&&d.doctype(h[0]),a=a.substring(h[0].length),i="";continue}if(/^<\//.test(a)&&(h=a.match(l))){a=a.substring(h[0].length),h[0].replace(l,f),i="/"+h[1].toLowerCase();continue}if(/^</.test(a)&&(h=a.match(z))){a=a.substring(h[0].length),h[0].replace(z,e),i=h[1].toLowerCase();continue}g=a.indexOf("<");var B=0>g?a:a.substring(0,g);a=0>g?"":a.substring(g),r=a.match(z),r?j=r[1]:(r=a.match(l),j=r?"/"+r[1]:""),d.chars&&d.chars(B,i,j),i=""}if(a===y)throw"Parse Error: "+a;y=a}f()};a.HTMLtoXML=function(a){var b="";return new y(a,{start:function(a,c,d){b+="<"+a;for(var e=0;e<c.length;e++)b+=" "+c[e].name+'="'+(c[e].value||"").replace(/"/g,""")+'"';b+=(d?"/":"")+">"},end:function(a){b+="</"+a+">"},chars:function(a){b+=a},comment:function(a){b+="<!--"+a+"-->"},ignore:function(a){b+=a}}),b},a.HTMLtoDOM=function(a,b){var c=e("html,head,body,title"),d={link:"head",base:"head"};b?b=b.ownerDocument||b.getOwnerDocument&&b.getOwnerDocument()||b:"undefined"!=typeof DOMDocument?b=new DOMDocument:"undefined"!=typeof document&&document.implementation&&document.implementation.createDocument?b=document.implementation.createDocument("","",null):"undefined"!=typeof ActiveX&&(b=new ActiveXObject("Msxml.DOMDocument"));var f=[],g=b.documentElement||b.getDocumentElement&&b.getDocumentElement();if(!g&&b.createElement&&!function(){var a=b.createElement("html"),c=b.createElement("head");c.appendChild(b.createElement("title")),a.appendChild(c),a.appendChild(b.createElement("body")),b.appendChild(a)}(),b.getElementsByTagName)for(var h in c)c[h]=b.getElementsByTagName(h)[0];var i=c.body;return new y(a,{start:function(a,e,g){if(c[a])return void(i=c[a]);var h=b.createElement(a);for(var j in e)h.setAttribute(e[j].name,e[j].value);d[a]&&"boolean"!=typeof c[d[a]]?c[d[a]].appendChild(h):i&&i.appendChild&&i.appendChild(h),g||(f.push(h),i=h)},end:function(){f.length-=1,i=f[f.length-1]},chars:function(a){i.appendChild(b.createTextNode(a))},comment:function(){},ignore:function(){}}),b}}("undefined"==typeof exports?this:exports),function(a){"use strict";function b(a){return a?a.replace(/[\t\n\r ]+/g," "):a}function c(a){var b={};return a.forEach(function(a){b[a]=1}),function(a){return 1===b[a]}}function d(a){return c(a.split(/,/))}function e(a,c,d,e,f){var g="",h="";return c.preserveLineBreaks&&(a=a.replace(/^[\t ]*[\n\r]+[\t\n\r ]*/,function(){return g="\n",""}).replace(/[\t\n\r ]*[\n\r]+[\t ]*$/,function(){return h="\n",""})),d&&(a=a.replace(/^\s+/,!g&&c.conservativeCollapse?" ":"")),e&&(a=a.replace(/\s+$/,!h&&c.conservativeCollapse?" ":"")),f&&(a=b(a)),g+a+h}function f(a,b,c,d){var f=b&&!U(b)&&(d.collapseInlineTagWhitespace||"/"!==b.charAt(0)||!T(b.substr(1))),g=c&&!U(c)&&(d.collapseInlineTagWhitespace||"/"===c.charAt(0)||!T(c));return e(a,d,f,g,b&&c)}function g(a){return/\[if[^\]]+\]/.test(a)||/\s*((?:<!)?\[endif\])$/.test(a)}function h(a,b){if(/^!/.test(a))return!0;if(b.ignoreCustomComments)for(var c=0,d=b.ignoreCustomComments.length;d>c;c++)if(b.ignoreCustomComments[c].test(a))return!0;return!1}function i(a,b){var c=b.customEventAttributes;if(c){for(var d=c.length;d--;)if(c[d].test(a))return!0;return!1}return/^on[a-z]{3,}$/.test(a)}function j(a){return/^[^\x20\t\n\f\r"'`=<>]+$/.test(a)}function k(a,b){for(var c=a.length;c--;)if(a[c].name.toLowerCase()===b)return!0;return!1}function l(a,b,c,d){return c=c?S(c.toLowerCase()):"","script"===a&&"language"===b&&"javascript"===c||"form"===a&&"method"===b&&"get"===c||"input"===a&&"type"===b&&"text"===c||"script"===a&&"charset"===b&&!k(d,"src")||"a"===a&&"name"===b&&k(d,"id")||"area"===a&&"shape"===b&&"rect"===c}function m(a,b,c){return"script"===a&&"type"===b&&"text/javascript"===S(c.toLowerCase())}function n(a,b){if("script"!==a)return!1;for(var c=0,d=b.length;d>c;c++){var e=b[c].name.toLowerCase();if("type"===e){var f=S(b[c].value).split(/;/,2)[0].toLowerCase();return""===f||V(f)}}return!0}function o(a,b,c){return("style"===a||"link"===a)&&"type"===b&&"text/css"===S(c.toLowerCase())}function p(a,b){var c=/^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|truespeed|typemustmatch|visible)$/i.test(a);if(c)return!0;var d=W[a.toLowerCase()];return d?-1===d.indexOf(b.toLowerCase()):!1}function q(a,b){return/^(?:a|area|link|base)$/.test(b)&&"href"===a||"img"===b&&/^(?:src|longdesc|usemap)$/.test(a)||"object"===b&&/^(?:classid|codebase|data|usemap)$/.test(a)||"q"===b&&"cite"===a||"blockquote"===b&&"cite"===a||("ins"===b||"del"===b)&&"cite"===a||"form"===b&&"action"===a||"input"===b&&("src"===a||"usemap"===a)||"head"===b&&"profile"===a||"script"===b&&("src"===a||"for"===a)}function r(a,b){return/^(?:a|area|object|button)$/.test(b)&&"tabindex"===a||"input"===b&&("maxlength"===a||"tabindex"===a)||"select"===b&&("size"===a||"tabindex"===a)||"textarea"===b&&/^(?:rows|cols|tabindex)$/.test(a)||"colgroup"===b&&"span"===a||"col"===b&&"span"===a||("th"===b||"td"===b)&&("rowspan"===a||"colspan"===a)}function s(a,b){if("link"!==a)return!1;for(var c=0,d=b.length;d>c;c++)if("rel"===b[c].name&&"canonical"===b[c].value)return!0}function t(a,c,d,e,f){if(d&&i(c,e)){if(d=S(d).replace(/^javascript:\s*/i,"").replace(/\s*;$/,""),e.minifyJS){var g=L(X+d+Y,e.minifyJS);return g.slice(X.length,-Y.length)}return d}return"class"===c?b(S(d)):q(c,a)?(d=S(d),e.minifyURLs&&!s(a,f)?K(d,e.minifyURLs):d):r(c,a)?S(d):"style"===c?(d=S(d),d&&(d=d.replace(/\s*;\s*$/,"")),e.minifyCSS?M(d,e.minifyCSS,!0):d):(u(a,f)&&"content"===c?d=d.replace(/\s+/g,"").replace(/[0-9]+\.[0-9]+/g,function(a){return(+a).toString()}):d&&e.customAttrCollapse&&e.customAttrCollapse.test(c)&&(d=d.replace(/\n+|\r+|\s{2,}/g,"")),d)}function u(a,b){if("meta"!==a)return!1;for(var c=0,d=b.length;d>c;c++)if("name"===b[c].name&&"viewport"===b[c].value)return!0}function v(a){return"*{"+a+"}"}function w(a){var b=a.match(/^\*\{([\s\S]*)\}$/m);return b&&b[1]?b[1]:a}function x(a){return a.replace(/^(\[[^\]]+\]>)\s*/,"$1").replace(/\s*(<!\[endif\])$/,"$1")}function y(a){return a.replace(/^(?:\s*\/\*\s*<!\[CDATA\[\s*\*\/|\s*\/\/\s*<!\[CDATA\[.*)/,"").replace(/(?:\/\*\s*\]\]>\s*\*\/|\/\/\s*\]\]>)\s*$/,"")}function z(a,b,c){for(var d=0,e=c.length;e>d;d++)if("type"===c[d].name.toLowerCase()&&b.processScripts.indexOf(c[d].value)>-1)return O(a,b);return a}function A(a,b){return a.replace(Z[b],"").replace($[b],"")}function B(a,b){switch(a){case"html":case"head":return!0;case"body":return!ba(b);case"colgroup":return"col"===b;case"tbody":return"tr"===b}return!1}function C(a,b){switch(b){case"colgroup":return"colgroup"===a;case"tbody":return ja(a)}return!1}function D(a,b){switch(a){case"html":case"head":case"body":case"colgroup":case"caption":return!0;case"li":case"optgroup":case"tr":return b===a;case"dt":case"dd":return ca(b);case"p":return da(b);case"rb":case"rt":case"rp":return fa(b);case"rtc":return ga(b);case"option":return ha(b);case"thead":case"tbody":return ia(b);case"tfoot":return"tbody"===b;case"td":case"th":return ka(b)}return!1}function E(a,b,c){var d=!c||/^\s*$/.test(c);return d?"input"===a&&"value"===b||oa.test(b):!1}function F(a,b){if("textarea"===a)return!1;if("script"===a)for(var c=b.length-1;c>=0;c--)if("src"===b[c].name)return!1;return!0}function G(a){return!/^(?:script|style|pre|textarea)$/.test(a)}function H(a){return!/^(?:pre|textarea)$/.test(a)}function I(a,b,c,d,e,f,g){var h,i,k=f.caseSensitive?a.name:a.name.toLowerCase(),n=a.value,q=a.quote;if(f.removeRedundantAttributes&&l(c,k,n,b)||f.removeScriptTypeAttributes&&m(c,k,n)||f.removeStyleLinkTypeAttributes&&o(c,k,n))return"";if(n=t(c,k,n,f,b),f.removeEmptyAttributes&&E(c,k,n))return"";if(void 0!==n&&!f.removeAttributeQuotes||!j(n)){if(!f.preventAttributesEscaping){if(void 0!==f.quoteCharacter)q="'"===f.quoteCharacter?"'":'"';else{var r=(n.match(/'/g)||[]).length,s=(n.match(/"/g)||[]).length;q=s>r?"'":'"'}n='"'===q?n.replace(/"/g,"""):n.replace(/'/g,"'")}i=q+n+q,g||f.removeTagWhitespace||(i+=" ")}else i=!g||d||/\/$/.test(n)?n+" ":n;return void 0===n||f.collapseBooleanAttributes&&p(k,n)?(h=k,g||(h+=" ")):h=k+a.customAssign+i,a.customOpen+h+a.customClose}function J(a){for(var b=["canCollapseWhitespace","canTrimWhitespace"],c=0,d=b.length;d>c;c++)a[b[c]]||(a[b[c]]=function(){return!1})}function K(b,c){"object"!=typeof c&&(c={});try{var d=a.RelateUrl;return"undefined"==typeof d&&"function"==typeof require&&(d=require("relateurl")),d&&d.relate?d.relate(b,c):b}catch(e){Q(e)}return b}function L(b,c){"object"!=typeof c&&(c={}),c.fromString=!0;var d=c.output||{};d.inline_script=!0,c.output=d;try{var e=a.UglifyJS;if("undefined"==typeof e&&"function"==typeof require&&(e=require("uglify-js")),!e)return b;if(e.minify)return e.minify(b,c).code;if(e.parse){var f=e.parse(b);f.figure_out_scope();var g=e.Compressor(),h=f.transform(g);h.figure_out_scope(),h.compute_char_frequency(),c.mangle!==!1&&h.mangle_names();var i=e.OutputStream(c.output);return h.print(i),i.toString()}return b}catch(j){Q(j)}return b}function M(a,b,c){"object"!=typeof b&&(b={}),"undefined"==typeof b.advanced&&(b.advanced=!1);try{var d;if("undefined"!=typeof CleanCSS)d=new CleanCSS(b);else if("function"==typeof require){var e=require("clean-css");d=new e(b)}return c?w(d.minify(v(a)).styles):d.minify(a).styles}catch(f){Q(f)}return a}function N(a){var b;do b=Math.random().toString(36).slice(2);while(~a.indexOf(b));return b}function O(a,c){function d(a,b){return G(a)||c.canCollapseWhitespace(a,b)}function i(a,b){return H(a)||c.canTrimWhitespace(a,b)}function j(){for(var a=r.length-1;a>0&&!/^<[^\/!]/.test(r[a]);)a--;r.length=Math.max(0,a)}function k(){for(var a=r.length-1;a>0&&!/^<\//.test(r[a]);)a--;r.length=a}c=c||{};var l=[];a=S(a),J(c);var m,o,p,q=[],r=[],s="",t="",u=[],v=[],w=[],E="",K="",O=c.lint,T=Date.now(),U=[],V=[];~a.indexOf("<!-- htmlmin:ignore -->")&&(o="<!--!"+N(a)+"-->",a=a.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g,function(a,b){return U.push(b),o}));var W=(c.ignoreCustomFragments||[/<%[\s\S]*?%>/,/<\?[\s\S]*?\?>/]).map(function(a){return a.source});if(W.length){var X=new RegExp("\\s*(?:"+W.join("|")+")\\s*","g");a=a.replace(X,function(b){return p||(p=N(a)),V.push(b)," "+p+" "})}new R(a,{html5:"undefined"!=typeof c.html5?c.html5:!0,start:function(a,b,e,f){var g=a.toLowerCase();if("svg"===g){l.push(c);var h={};for(var n in c)h[n]=c[n];h.keepClosingSlash=!0,h.caseSensitive=!0,c=h}a=c.caseSensitive?a:g,t=a,m=a,s="",u=b;var o=c.removeOptionalTags;o&&(B(E,a)&&j(),E="",D(K,a)&&(k(),o=!C(K,a)),K=""),c.collapseWhitespace&&(i(a,b)||v.push(a),d(a,b)||w.push(a));var p="<"+a,q=f&&c.keepClosingSlash;r.push(p),O&&O.testElement(a);for(var x,y=[],z=!0,A=b.length;--A>=0;)O&&O.testAttribute(a,b[A].name.toLowerCase(),b[A].value),x=I(b[A],b,a,q,A,c,z),x&&(z=!1,y.unshift(x));y.length>0?(r.push(" "),r.push.apply(r,y)):o&&_(a)&&(E=a),r.push(r.pop()+(q?"/":"")+">")},end:function(a,b){var d=a.toLowerCase();if("svg"===d&&(c=l.pop()),a=c.caseSensitive?a:d,c.removeOptionalTags&&(!K||"dt"===K||"thead"===K||"p"===K&&ea(a)||k(),K=aa(a)?a:""),c.collapseWhitespace){if(v.length)a===v[v.length-1]&&v.pop();else{var e;r.length>1&&""===r[r.length-1]&&/\s+$/.test(r[r.length-2])?e=r.length-2:r.length>0&&/\s+$/.test(r[r.length-1])&&(e=r.length-1),e>0&&(r[e]=r[e].replace(/\s+$/,function(b){return f(b,"comment","/"+a,c)}))}w.length&&a===w[w.length-1]&&w.pop()}var g=!1;a===t&&(t="",g=""===s),c.removeEmptyElements&&g&&F(a,b)?(j(),E="",K=""):(q.push.apply(q,r),r=["</"+a+">"],m="/"+a,s="")},chars:function(a,d,e){if(d=""===d?"comment":d,e=""===e?"comment":e,c.collapseWhitespace){if(!v.length){if("comment"===d){var g=""===r[r.length-1];if(g&&(d=m),r.length>1&&(g||" "===s.charAt(s.length-1))){var h=r.length-2;r[h]=r[h].replace(/\s+$/,function(b){return a=b+a,""})}}a=d||e?f(a,d,e,c):S(a)}w.length||(a=d&&e||"html"===e?a:b(a))}("script"===t||"style"===t)&&(c.removeCommentsFromCDATA&&(a=A(a,t)),c.removeCDATASectionsFromCDATA&&(a=y(a)),c.processScripts&&(a=z(a,c,u))),c.minifyJS&&n(t,u)&&(a=L(a,c.minifyJS),";"===a.charAt(a.length-1)&&(a=a.slice(0,-1))),"style"===t&&c.minifyCSS&&(a=M(a,c.minifyCSS)),c.removeOptionalTags&&a&&(("html"===E||"body"===E&&!/^\s/.test(a))&&j(),E="",(ma(K)||na(K)&&!/^\s/.test(a))&&k(),K=""),m=/^\s*$/.test(a)?d:"comment",s+=a,O&&O.testChars(a),r.push(a)},comment:function(a,b){var d=b?"<!":"<!--",e=b?">":"-->";a=c.removeComments?g(a)?d+x(a)+e:h(a,c)?"<!--"+a+"-->":"":d+a+e,c.removeOptionalTags&&a&&(E="",K=""),r.push(a)},doctype:function(a){r.push(c.useShortDoctype?"<!DOCTYPE html>":b(a))},customAttrAssign:c.customAttrAssign,customAttrSurround:c.customAttrSurround}),c.removeOptionalTags&&(la(E)&&j(),K&&"dt"!==K&&"thead"!==K&&k()),q.push.apply(q,r);var Y=P(q,c);return p&&(Y=Y.replace(new RegExp("(\\s*)"+p+"(\\s*)","g"),function(a,b,d){var f=V.shift();return c.collapseWhitespace?e(b+f+d,{preserveLineBreaks:c.preserveLineBreaks,conservativeCollapse:!0},!0,!0):f})),o&&(Y=Y.replace(new RegExp(o,"g"),function(){return U.shift()})),Q("minified in: "+(Date.now()-T)+"ms"),Y}function P(a,b){var c,d=b.maxLineLength;if(d){for(var e,f=[],g="",h=0,i=a.length;i>h;h++)e=a[h],g.length+e.length<d?g+=e:(f.push(g.replace(/^\n/,"")),g=e);f.push(g),c=f.join("\n")}else c=a.join("");return S(c)}var Q,R;Q=a.console&&a.console.log?function(b){a.console.log(b)}:function(){},a.HTMLParser?R=a.HTMLParser:"function"==typeof require&&(R=require("./htmlparser").HTMLParser);var S=function(a){return"string"!=typeof a?a:a.replace(/^\s+/,"").replace(/\s+$/,"")};String.prototype.trim&&(S=function(a){return"string"!=typeof a?a:a.trim()});var T=d("a,abbr,acronym,b,bdi,bdo,big,button,cite,code,del,dfn,em,font,i,ins,kbd,mark,math,q,rt,rp,s,samp,small,span,strike,strong,sub,sup,svg,time,tt,u,var"),U=d("comment,img,input"),V=c(["text/javascript","text/ecmascript","text/jscript","application/javascript","application/x-javascript","application/ecmascript"]),W={draggable:["true","false"]},X="!function(){",Y="}();",Z={script:/^\s*(?:\/\/)?\s*<!--.*\n?/,style:/^\s*<!--\s*/},$={script:/\s*(?:\/\/)?\s*-->\s*$/,style:/\s*-->\s*$/},_=d("html,head,body,colgroup,tbody"),aa=d("html,head,body,li,dt,dd,p,rb,rt,rtc,rp,optgroup,option,colgroup,caption,thead,tbody,tfoot,tr,td,th"),ba=d("meta,link,script,style,template,noscript"),ca=d("dt,dd"),da=d("address,article,aside,blockquote,details,div,dl,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,menu,nav,ol,p,pre,section,table,ul"),ea=d("a,audio,del,ins,map,noscript,video"),fa=d("rb,rt,rtc,rp"),ga=d("rb,rtc,rp"),ha=d("option,optgroup"),ia=d("tbody,tfoot"),ja=d("thead,tbody,tfoot"),ka=d("td,th"),la=d("html,head,body"),ma=d("html,body"),na=d("head,colgroup,caption"),oa=new RegExp("^(?:class|id|style|title|lang|dir|on(?:focus|blur|change|click|dblclick|mouse(?:down|up|over|move|out)|key(?:press|down|up)))$");"undefined"!=typeof exports?exports.minify=O:a.minify=O}("undefined"==typeof exports?this:exports),function(a){"use strict";function b(a){return/^(?:big|small|hr|blink|marquee)$/.test(a)}function c(a){return/^(?:applet|basefont|center|dir|font|isindex|strike)$/.test(a)}function d(a){return/^on[a-z]+/.test(a)}function e(a){return"style"===a.toLowerCase()}function f(a,b){return"align"===b&&/^(?:caption|applet|iframe|img|imput|object|legend|table|hr|div|h[1-6]|p)$/.test(a)||"alink"===b&&"body"===a||"alt"===b&&"applet"===a||"archive"===b&&"applet"===a||"background"===b&&"body"===a||"bgcolor"===b&&/^(?:table|t[rdh]|body)$/.test(a)||"border"===b&&/^(?:img|object)$/.test(a)||"clear"===b&&"br"===a||"code"===b&&"applet"===a||"codebase"===b&&"applet"===a||"color"===b&&/^(?:base(?:font)?)$/.test(a)||"compact"===b&&/^(?:dir|[dou]l|menu)$/.test(a)||"face"===b&&/^base(?:font)?$/.test(a)||"height"===b&&/^(?:t[dh]|applet)$/.test(a)||"hspace"===b&&/^(?:applet|img|object)$/.test(a)||"language"===b&&"script"===a||"link"===b&&"body"===a||"name"===b&&"applet"===a||"noshade"===b&&"hr"===a||"nowrap"===b&&/^t[dh]$/.test(a)||"object"===b&&"applet"===a||"prompt"===b&&"isindex"===a||"size"===b&&/^(?:hr|font|basefont)$/.test(a)||"start"===b&&"ol"===a||"text"===b&&"body"===a||"type"===b&&/^(?:li|ol|ul)$/.test(a)||"value"===b&&"li"===a||"version"===b&&"html"===a||"vlink"===b&&"body"===a||"vspace"===b&&/^(?:applet|img|object)$/.test(a)||"width"===b&&/^(?:hr|td|th|applet|pre)$/.test(a)}function g(a,b){return"href"===a&&/^\s*javascript\s*:\s*void\s*(\s+0|\(\s*0\s*\))\s*$/i.test(b)}function h(){this.log=[],this._lastElement=null,this._isElementRepeated=!1}h.prototype.testElement=function(a){c(a)?this.log.push('Found <span class="deprecated-element">deprecated</span> <strong><code><'+a+"></code></strong> element"):b(a)?this.log.push('Found <span class="presentational-element">presentational</span> <strong><code><'+a+"></code></strong> element"):this.checkRepeatingElement(a)},h.prototype.checkRepeatingElement=function(a){"br"===a&&"br"===this._lastElement?this._isElementRepeated=!0:this._isElementRepeated&&(this._reportRepeatingElement(),this._isElementRepeated=!1),this._lastElement=a},h.prototype._reportRepeatingElement=function(){this.log.push("Found <code><br></code> sequence. Try replacing it with styling.")},h.prototype.testAttribute=function(a,b,c){d(b)?this.log.push('Found <span class="event-attribute">event attribute</span> (<strong>'+b+"</strong>) on <strong><code><"+a+"></code></strong> element."):f(a,b)?this.log.push('Found <span class="deprecated-attribute">deprecated</span> <strong>'+b+"</strong> attribute on <strong><code><"+a+"></code></strong> element."):e(b)?this.log.push('Found <span class="style-attribute">style attribute</span> on <strong><code><'+a+"></code></strong> element."):g(b,c)&&this.log.push('Found <span class="inaccessible-attribute">inaccessible attribute</span> (on <strong><code><'+a+"></code></strong> element).")},h.prototype.testChars=function(a){this._lastElement="",/( \s*){2,}/.test(a)&&this.log.push("Found repeating <strong><code>&nbsp;</code></strong> sequence. Try replacing it with styling.")},h.prototype.test=function(a,b,c){this.testElement(a),this.testAttribute(a,b,c)},h.prototype.populate=function(a){if(this._isElementRepeated&&this._reportRepeatingElement(),this.log.length)if(a)a.innerHTML="<ol><li>"+this.log.join("<li>")+"</ol>";else{var b=" - "+this.log.join("\n - ").replace(/(<([^>]+)>)/gi,"").replace(/</g,"<").replace(/>/g,">");console.log(b)}},a.HTMLLint=h}("undefined"==typeof exports?this:exports); | ||
!function(a){"use strict";function b(a){var b=e.source+"(?:\\s*("+c(a)+")\\s*(?:"+h.join("|")+"))?";if(a.customAttrSurround){for(var d=[],f=a.customAttrSurround.length-1;f>=0;f--)d[f]="(?:("+a.customAttrSurround[f][0].source+")\\s*"+b+"\\s*("+a.customAttrSurround[f][1].source+"))";d.push("(?:"+b+")"),b="(?:"+d.join("|")+")"}return new RegExp("^\\s*"+b)}function c(a){return g.concat(a.customAttrAssign||[]).map(function(a){return"(?:"+a.source+")"}).join("|")}function d(a){for(var b={},c=a.split(","),d=0;d<c.length;d++)b[c[d]]=!0,b[c[d].toUpperCase()]=!0;return b}var e=/([^\s"'<>\/=]+)/,f=/=/,g=[f],h=[/"([^"]*)"+/.source,/'([^']*)'+/.source,/([^\s"'=<>`]+)/.source],i=function(){var b;return b="function"==typeof require?require("ncname"):a.NCName,b=b?b.source.slice(1,-1):"[:A-Za-z_][:\\w\\-\\.]*","((?:"+b+"\\:)?"+b+")"}(),j=new RegExp("^<"+i),k=/^\s*(\/?)>/,l=new RegExp("^<\\/"+i+"[^>]*>"),m=/^<!DOCTYPE [^>]+>/i,n=!1;"x".replace(/x(.)?/g,function(a,b){n=""===b});var o=d("area,base,basefont,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),p=d("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,noscript,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,svg,textarea,tt,u,var"),q=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),r=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"),s=d("script,style"),t=d("abbr,b,bdi,bdo,button,cite,code,data,dfn,em,h1,h2,h3,h4,h5,h6,i,kbd,label,legend,mark,meter,output,p,pre,progress,q,rp,rt,s,samp,small,span,strong,sub,sup,time,u,var"),u=d("a,abbr,area,audio,b,bdi,bdo,br,button,canvas,cite,code,data,datalist,del,dfn,em,embed,i,iframe,img,input,ins,kbd,keygen,label,link,main,map,mark,math,menu,meter,nav,noscript,object,ol,output,p,picture,pre,progress,q,ruby,s,samp,script,section,select,small,span,strong,sub,sup,svg,table,template,textarea,time,u,ul,var,video,wbr"),v={},w=a.HTMLParser=function(a,c){function d(a){var b=a.match(j);if(b){var c={tagName:b[1],attrs:[]};a=a.slice(b[0].length);for(var d,e;!(d=a.match(k))&&(e=a.match(y));)a=a.slice(e[0].length),c.attrs.push(e);if(d)return c.unarySlash=d[1],c.rest=a.slice(d[0].length),c}}function e(a){var b=a.tagName,d=a.unarySlash;if(c.html5&&g&&t[g]&&!u[b]&&f("",g),!c.html5)for(;g&&p[g];)f("",g);q[b]&&g===b&&f("",b);var e=o[b]||"html"===b&&"head"===g||!!d,h=a.attrs.map(function(a){var b,d,e,f,g,h,i,j=7;n&&-1===a[0].indexOf('""')&&(""===a[3]&&(a[3]=void 0),""===a[4]&&(a[4]=void 0),""===a[5]&&(a[5]=void 0));var k=1;if(c.customAttrSurround)for(var l=0,m=c.customAttrSurround.length;m>l;l++,k+=j)if(b=a[k+1],h=a[k+2],b){e=a[k+3],d=e||a[k+4]||a[k+5],i=e?'"':d?"'":"",f=a[k],g=a[k+6];break}return!b&&(b=a[k])&&(h=a[k+1],e=a[k+2],d=e||a[k+3]||a[k+4],i=e?'"':d?"'":""),void 0===d&&(d=r[b]?b:e),{name:b,value:d,customAssign:h||"=",customOpen:f||"",customClose:g||"",quote:i||""}});e||(x.push({tag:b,attrs:h}),g=b,d=""),c.start&&c.start(b,h,e,d)}function f(a,b){var d;if(b){var e=b.toLowerCase();for(d=x.length-1;d>=0&&x[d].tag.toLowerCase()!==e;d--);}else d=0;if(d>=0){for(var f=x.length-1;f>=d;f--)c.end&&c.end(x[f].tag,x[f].attrs,f>d||!a);x.length=d,g=d&&x[d-1].tag}}for(var g,h,i,w,x=[],y=b(c);a;){if(h=a,g&&s[g]){var z=g.toLowerCase(),A=v[z]||(v[z]=new RegExp("([\\s\\S]*?)</"+z+"[^>]*>","i"));a=a.replace(A,function(a,b){return"script"!==z&&"style"!==z&&"noscript"!==z&&(b=b.replace(/<!--([\s\S]*?)-->/g,"$1").replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g,"$1")),c.chars&&c.chars(b),""}),f("</"+z+">",z)}else{var B=a.indexOf("<");if(0===B){if(/^<!--/.test(a)){var C=a.indexOf("-->");if(C>=0){c.comment&&c.comment(a.substring(4,C)),a=a.substring(C+3),i="";continue}}if(/^<!\[/.test(a)){var D=a.indexOf("]>");if(D>=0){c.comment&&c.comment(a.substring(2,D+1),!0),a=a.substring(D+2),i="";continue}}var E=a.match(m);if(E){c.doctype&&c.doctype(E[0]),a=a.substring(E[0].length),i="";continue}var F=a.match(l);if(F){a=a.substring(F[0].length),F[0].replace(l,f),i="/"+F[1].toLowerCase();continue}var G=d(a);if(G){a=G.rest,e(G),i=G.tagName.toLowerCase();continue}}var H;B>=0?(H=a.substring(0,B),a=a.substring(B)):(H=a,a="");var I=d(a);I?w=I.tagName:(I=a.match(l),w=I?"/"+I[1]:""),c.chars&&c.chars(H,i,w),i=""}if(a===h)throw"Parse Error: "+a}c.partialMarkup||f()};a.HTMLtoXML=function(a){var b="";return new w(a,{start:function(a,c,d){b+="<"+a;for(var e=0;e<c.length;e++)b+=" "+c[e].name+'="'+(c[e].value||"").replace(/"/g,""")+'"';b+=(d?"/":"")+">"},end:function(a){b+="</"+a+">"},chars:function(a){b+=a},comment:function(a){b+="<!--"+a+"-->"},ignore:function(a){b+=a}}),b},a.HTMLtoDOM=function(a,b){var c=d("html,head,body,title"),e={link:"head",base:"head"};b?b=b.ownerDocument||b.getOwnerDocument&&b.getOwnerDocument()||b:"undefined"!=typeof DOMDocument?b=new DOMDocument:"undefined"!=typeof document&&document.implementation&&document.implementation.createDocument?b=document.implementation.createDocument("","",null):"undefined"!=typeof ActiveX&&(b=new ActiveXObject("Msxml.DOMDocument"));var f=[],g=b.documentElement||b.getDocumentElement&&b.getDocumentElement();if(!g&&b.createElement&&!function(){var a=b.createElement("html"),c=b.createElement("head");c.appendChild(b.createElement("title")),a.appendChild(c),a.appendChild(b.createElement("body")),b.appendChild(a)}(),b.getElementsByTagName)for(var h in c)c[h]=b.getElementsByTagName(h)[0];var i=c.body;return new w(a,{start:function(a,d,g){if(c[a])return void(i=c[a]);var h=b.createElement(a);for(var j in d)h.setAttribute(d[j].name,d[j].value);e[a]&&"boolean"!=typeof c[e[a]]?c[e[a]].appendChild(h):i&&i.appendChild&&i.appendChild(h),g||(f.push(h),i=h)},end:function(){f.length-=1,i=f[f.length-1]},chars:function(a){i.appendChild(b.createTextNode(a))},comment:function(){},ignore:function(){}}),b}}("undefined"==typeof exports?this:exports),function(a){"use strict";function b(a){return" "===a?a:" "}function c(a){return a?a.replace(/[\t\n\r ]+/g,b):a}function d(a){var b={};return a.forEach(function(a){b[a]=1}),function(a){return 1===b[a]}}function e(a){return d(a.split(/,/))}function f(a,d,e,f,g){var h="",i="";return d.preserveLineBreaks&&(a=a.replace(/^[\t ]*[\n\r][\t\n\r ]*/,function(){return h="\n",""}).replace(/[\t\n\r ]*[\n\r][\t ]*$/,function(){return i="\n",""})),e&&(a=a.replace(/^\s+/,!h&&d.conservativeCollapse?b:"")),f&&(a=a.replace(/\s+$/,!i&&d.conservativeCollapse?b:"")),g&&(a=c(a)),h+a+i}function g(a,b,c,d){var e=b&&!W(b);e&&!d.collapseInlineTagWhitespace&&(e="/"===b.charAt(0)?!U(b.slice(1)):!V(b));var g=c&&!W(c);return g&&!d.collapseInlineTagWhitespace&&(g="/"===c.charAt(0)?!V(c.slice(1)):!U(c)),f(a,d,e,g,b&&c)}function h(a){return/^\[if\s[^\]]+\]|\[endif\]$/.test(a)}function i(a,b){if(/^!/.test(a))return!0;if(b.ignoreCustomComments)for(var c=0,d=b.ignoreCustomComments.length;d>c;c++)if(b.ignoreCustomComments[c].test(a))return!0;return!1}function j(a,b){var c=b.customEventAttributes;if(c){for(var d=c.length;d--;)if(c[d].test(a))return!0;return!1}return/^on[a-z]{3,}$/.test(a)}function k(a){return/^[^\x20\t\n\f\r"'`=<>]+$/.test(a)}function l(a,b){for(var c=a.length;c--;)if(a[c].name.toLowerCase()===b)return!0;return!1}function m(a,b,c,d){return c=c?T(c.toLowerCase()):"","script"===a&&"language"===b&&"javascript"===c||"form"===a&&"method"===b&&"get"===c||"input"===a&&"type"===b&&"text"===c||"script"===a&&"charset"===b&&!l(d,"src")||"a"===a&&"name"===b&&l(d,"id")||"area"===a&&"shape"===b&&"rect"===c}function n(a,b,c){return"script"===a&&"type"===b&&X(T(c.toLowerCase()))}function o(a,b){if("script"!==a)return!1;for(var c=0,d=b.length;d>c;c++){var e=b[c].name.toLowerCase();if("type"===e){var f=T(b[c].value.split(/;/,2)[0]).toLowerCase();return""===f||X(f)}}return!0}function p(a,b,c){return("style"===a||"link"===a)&&"type"===b&&"text/css"===T(c.toLowerCase())}function q(a,b){return Y(a)||"draggable"===a&&!Z(b)}function r(a,b){return/^(?:a|area|link|base)$/.test(b)&&"href"===a||"img"===b&&/^(?:src|longdesc|usemap)$/.test(a)||"object"===b&&/^(?:classid|codebase|data|usemap)$/.test(a)||"q"===b&&"cite"===a||"blockquote"===b&&"cite"===a||("ins"===b||"del"===b)&&"cite"===a||"form"===b&&"action"===a||"input"===b&&("src"===a||"usemap"===a)||"head"===b&&"profile"===a||"script"===b&&("src"===a||"for"===a)}function s(a,b){return/^(?:a|area|object|button)$/.test(b)&&"tabindex"===a||"input"===b&&("maxlength"===a||"tabindex"===a)||"select"===b&&("size"===a||"tabindex"===a)||"textarea"===b&&/^(?:rows|cols|tabindex)$/.test(a)||"colgroup"===b&&"span"===a||"col"===b&&"span"===a||("th"===b||"td"===b)&&("rowspan"===a||"colspan"===a)}function t(a,b){if("link"!==a)return!1;for(var c=0,d=b.length;d>c;c++)if("rel"===b[c].name&&"canonical"===b[c].value)return!0}function u(a,b,d,e,f){if(d&&j(b,e)){if(d=T(d).replace(/^javascript:\s*/i,"").replace(/\s*;$/,""),e.minifyJS){var g=va($+d+_,e.minifyJS);return g.slice($.length,-_.length)}return d}return"class"===b?c(T(d)):r(b,a)?(d=T(d),e.minifyURLs&&!t(a,f)?ua(d,e.minifyURLs):d):s(b,a)?T(d):"style"===b?(d=T(d),d&&(d=d.replace(/\s*;\s*$/,"")),e.minifyCSS?wa(d,e.minifyCSS,!0):d):(v(a,f)&&"content"===b?d=d.replace(/\s+/g,"").replace(/[0-9]+\.[0-9]+/g,function(a){return(+a).toString()}):d&&e.customAttrCollapse&&e.customAttrCollapse.test(b)?d=d.replace(/\n+|\r+|\s{2,}/g,""):"script"===a&&"type"===b&&(d=T(d.replace(/\s*;\s*/g,";"))),d)}function v(a,b){if("meta"!==a)return!1;for(var c=0,d=b.length;d>c;c++)if("name"===b[c].name&&"viewport"===b[c].value)return!0}function w(a){return"*{"+a+"}"}function x(a){var b=a.match(/^\*\{([\s\S]*)\}$/m);return b&&b[1]?b[1]:a}function y(a,b){return a.replace(/^(\[if\s[^\]]+\]>)([\s\S]*?)(<!\[endif\])$/,function(a,c,d,e){return c+P(d,b,!0)+e})}function z(a){return a.replace(/^(?:\s*\/\*\s*<!\[CDATA\[\s*\*\/|\s*\/\/\s*<!\[CDATA\[.*)/,"").replace(/(?:\/\*\s*\]\]>\s*\*\/|\/\/\s*\]\]>)\s*$/,"")}function A(a,b,c){for(var d=0,e=c.length;e>d;d++)if("type"===c[d].name.toLowerCase()&&b.processScripts.indexOf(c[d].value)>-1)return P(a,b);return a}function B(a,b){return a.replace(aa[b],"").replace(ba[b],"")}function C(a,b){switch(a){case"html":case"head":return!0;case"body":return!ea(b);case"colgroup":return"col"===b;case"tbody":return"tr"===b}return!1}function D(a,b){switch(b){case"colgroup":return"colgroup"===a;case"tbody":return ma(a)}return!1}function E(a,b){switch(a){case"html":case"head":case"body":case"colgroup":case"caption":return!0;case"li":case"optgroup":case"tr":return b===a;case"dt":case"dd":return fa(b);case"p":return ga(b);case"rb":case"rt":case"rp":return ia(b);case"rtc":return ja(b);case"option":return ka(b);case"thead":case"tbody":return la(b);case"tfoot":return"tbody"===b;case"td":case"th":return na(b)}return!1}function F(a,b,c){var d=!c||/^\s*$/.test(c);return d?"input"===a&&"value"===b||ta.test(b):!1}function G(a,b){for(var c=b.length-1;c>=0;c--)if(b[c].name===a)return!0;return!1}function H(a,b){switch(a){case"textarea":return!1;case"audio":case"script":case"video":if(G("src",b))return!1;break;case"iframe":if(G("src",b)||G("srcdoc",b))return!1;break;case"object":if(G("data",b))return!1;break;case"applet":if(G("code",b))return!1}return!0}function I(a){return!/^(?:script|style|pre|textarea)$/.test(a)}function J(a){return!/^(?:pre|textarea)$/.test(a)}function K(a,b,c,d,e,f,g){var h,i,j=f.caseSensitive?a.name:a.name.toLowerCase(),l=a.value,o=a.quote;if(f.removeRedundantAttributes&&m(c,j,l,b)||f.removeScriptTypeAttributes&&n(c,j,l)||f.removeStyleLinkTypeAttributes&&p(c,j,l))return"";if(l=u(c,j,l,f,b),f.removeEmptyAttributes&&F(c,j,l))return"";if(void 0!==l&&!f.removeAttributeQuotes||!k(l)){if(!f.preventAttributesEscaping){if(void 0!==f.quoteCharacter)o="'"===f.quoteCharacter?"'":'"';else{var r=(l.match(/'/g)||[]).length,s=(l.match(/"/g)||[]).length;o=s>r?"'":'"'}l='"'===o?l.replace(/"/g,"""):l.replace(/'/g,"'")}i=o+l+o,g||f.removeTagWhitespace||(i+=" ")}else i=!g||d||/\/$/.test(l)?l+" ":l;return void 0===l||f.collapseBooleanAttributes&&q(j.toLowerCase(),l.toLowerCase())?(h=j,g||(h+=" ")):h=j+a.customAssign+i,a.customOpen+h+a.customClose}function L(a){"includeAutoGeneratedTags"in a||(a.includeAutoGeneratedTags=!0);for(var b=["canCollapseWhitespace","canTrimWhitespace"],c=0,d=b.length;d>c;c++)a[b[c]]||(a[b[c]]=function(){return!1});a.minifyURLs&&"object"!=typeof a.minifyURLs&&(a.minifyURLs={}),a.minifyJS&&("object"!=typeof a.minifyJS&&(a.minifyJS={}),a.minifyJS.fromString=!0,(a.minifyJS.output||(a.minifyJS.output={})).inline_script=!0),a.minifyCSS&&("object"!=typeof a.minifyCSS&&(a.minifyCSS={}),"undefined"==typeof a.minifyCSS.advanced&&(a.minifyCSS.advanced=!1))}function M(a){return a}function N(b,c){var d=a[b];return"undefined"==typeof d&&"function"==typeof require&&(d=require(c)),d}function O(a){var b;do b=Math.random().toString(36).slice(2);while(~a.indexOf(b));return b}function P(a,b,d){function e(a,c){return I(a)||b.canCollapseWhitespace(a,c)}function j(a,c){return J(a)||b.canTrimWhitespace(a,c)}function k(){for(var a=t.length-1;a>0&&!/^<[^\/!]/.test(t[a]);)a--;t.length=Math.max(0,a)}function l(){for(var a=t.length-1;a>0&&!/^<\//.test(t[a]);)a--;t.length=Math.max(0,a)}function m(a){var c;t.length>1&&/^(?:<!|$)/.test(t[t.length-1])&&/\s$/.test(t[t.length-2])?c=t.length-2:t.length>0&&/\s$/.test(t[t.length-1])&&(c=t.length-1),c>0&&(t[c]=t[c].replace(/\s+$/,function(c){return g(c,"comment",a,b)}))}b=b||{};var n=[];L(b),a=b.collapseWhitespace?T(a):a;var p,q,r,s,t=[],u="",v="",w=[],x=[],F=[],G="",M="",N=b.lint,P=Date.now(),U=[],W=[];a=a.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g,function(b,c){r||(r="<!--!"+O(a)+"-->");var d=r+U.length;return U.push(c),d});var X=(b.ignoreCustomFragments||[/<%[\s\S]*?%>/,/<\?[\s\S]*?\?>/]).map(function(a){return a.source});if(X.length){var Y=new RegExp("\\s*(?:"+X.join("|")+")+\\s*","g");a=a.replace(Y,function(b){s||(s=O(a));var c=s+W.length;return W.push(b)," "+c+" "})}new S(a,{partialMarkup:d,html5:"undefined"!=typeof b.html5?b.html5:!0,start:function(a,c,d,f){var g=a.toLowerCase();if("svg"===g){n.push(b);var h={};for(var i in b)h[i]=b[i];h.keepClosingSlash=!0,h.caseSensitive=!0,b=h}a=b.caseSensitive?a:g,v=a,p=a,V(a)||(u=""),q=!1,w=c;var o=b.removeOptionalTags;if(o){var r=sa(a);r&&C(G,a)&&k(),G="",r&&E(M,a)&&(l(),o=!D(M,a)),M=""}b.collapseWhitespace&&(x.length||m(a),j(a,c)||x.push(a),e(a,c)||F.push(a));var s="<"+a,y=f&&b.keepClosingSlash;t.push(s),N&&N.testElement(a);for(var z,A=[],B=!0,H=c.length;--H>=0;)N&&N.testAttribute(a,c[H].name.toLowerCase(),c[H].value),z=K(c[H],c,a,y,H,b,B),z&&(B=!1,A.unshift(z));A.length>0?(t.push(" "),t.push.apply(t,A)):o&&ca(a)&&(G=a),t.push(t.pop()+(y?"/":"")+">")},end:function(a,c,d){var e=a.toLowerCase();"svg"===e&&(b=n.pop()),a=b.caseSensitive?a:e,b.collapseWhitespace&&(x.length?a===x[x.length-1]&&x.pop():m("/"+a),F.length&&a===F[F.length-1]&&F.pop());var f=!1;a===v&&(v="",f=!q),b.removeOptionalTags&&(f&&oa(G)&&k(),G="",!sa(a)||!M||ra(M)||"p"===M&&ha(a)||l(),M=da(a)?a:""),b.removeEmptyElements&&f&&H(a,c)?(k(),G="",M=""):(!b.includeAutoGeneratedTags&&d||t.push("</"+a+">"),p="/"+a,V(a)||(u=""))},chars:function(a,d,e){if(d=""===d?"comment":d,e=""===e?"comment":e,b.collapseWhitespace){if(!x.length){if("comment"===d){var h=""===t[t.length-1];if(h&&(d=p),t.length>1&&(h||/ $/.test(u))){var i=t.length-2;t[i]=t[i].replace(/\s+$/,function(b){return a=b+a,""})}}if(d&&V("/"===d.charAt(0)?d.slice(1):d)&&(a=f(a,b,/(?:^|\s)$/.test(u))),a=d||e?g(a,d,e,b):T(a),!a&&/\s$/.test(u)&&d&&"/"===d.charAt(0))for(var m=t.length-2,n=d.slice(1);m>=0&&j(n);m--){var r=t[m],s=r.match(/^<\/([\w:-]+)>$/);if(s)n=s[1];else if(/>$/.test(r)||(t[m]=g(r,null,e,b)))break}}F.length||(a=d&&e||"html"===e?a:c(a))}"script"!==v&&"style"!==v||(b.removeCommentsFromCDATA&&(a=B(a,v)),b.removeCDATASectionsFromCDATA&&(a=z(a)),b.processScripts&&(a=A(a,b,w))),b.minifyJS&&o(v,w)&&(a=va(a,b.minifyJS),";"===a.charAt(a.length-1)&&(a=a.slice(0,-1))),"style"===v&&b.minifyCSS&&(a=wa(a,b.minifyCSS)),b.removeOptionalTags&&a&&(("html"===G||"body"===G&&!/^\s/.test(a))&&k(),G="",(pa(M)||qa(M)&&!/^\s/.test(a))&&l(),M=""),p=/^\s*$/.test(a)?d:"comment",u+=a,a&&(q=!0),N&&N.testChars(a),t.push(a)},comment:function(a,c){var d=c?"<!":"<!--",e=c?">":"-->";a=h(a)?d+y(a,b)+e:b.removeComments?i(a,b)?"<!--"+a+"-->":"":d+a+e,b.removeOptionalTags&&a&&(G="",M=""),t.push(a)},doctype:function(a){t.push(b.useShortDoctype?"<!DOCTYPE html>":c(a))},customAttrAssign:b.customAttrAssign,customAttrSurround:b.customAttrSurround}),b.removeOptionalTags&&(oa(G)&&k(),M&&!ra(M)&&l());var Z=Q(t,b);return s&&(Z=Z.replace(new RegExp("(\\s*)"+s+"([0-9]+)(\\s*)","g"),function(a,c,d,e){var g=W[+d];return b.collapseWhitespace?(" "!==c&&(g=c+g)," "!==e&&(g+=e),f(g,{preserveLineBreaks:b.preserveLineBreaks,conservativeCollapse:!0},/^\s/.test(g),/\s$/.test(g))):g})),r&&(Z=Z.replace(new RegExp(r+"([0-9]+)","g"),function(a,b){return U[+b]})),R("minified in: "+(Date.now()-P)+"ms"),Z}function Q(a,b){var c,d=b.maxLineLength;if(d){for(var e,f=[],g="",h=0,i=a.length;i>h;h++)e=a[h],g.length+e.length<d?g+=e:(f.push(g.replace(/^\n/,"")),g=e);f.push(g),c=f.join("\n")}else c=a.join("");return b.collapseWhitespace?T(c):c}var R,S;R=a.console&&a.console.log?function(b){a.console.log(b)}:function(){},a.HTMLParser?S=a.HTMLParser:"function"==typeof require&&(S=require("./htmlparser").HTMLParser);var T=function(a){return"string"!=typeof a?a:a.replace(/^\s+/,"").replace(/\s+$/,"")};String.prototype.trim&&(T=function(a){return"string"!=typeof a?a:a.trim()});var U=e("a,abbr,acronym,b,bdi,bdo,big,button,cite,code,del,dfn,em,font,i,ins,kbd,mark,math,q,rt,rp,s,samp,small,span,strike,strong,sub,sup,svg,time,tt,u,var"),V=e("a,abbr,acronym,b,big,del,em,font,i,ins,kbd,mark,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var"),W=e("comment,img,input"),X=d(["text/javascript","text/ecmascript","text/jscript","application/javascript","application/x-javascript","application/ecmascript"]),Y=e("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),Z=e("true,false"),$="!function(){",_="}();",aa={script:/^\s*(?:\/\/)?\s*<!--.*\n?/,style:/^\s*<!--\s*/},ba={script:/\s*(?:\/\/)?\s*-->\s*$/,style:/\s*-->\s*$/},ca=e("html,head,body,colgroup,tbody"),da=e("html,head,body,li,dt,dd,p,rb,rt,rtc,rp,optgroup,option,colgroup,caption,thead,tbody,tfoot,tr,td,th"),ea=e("meta,link,script,style,template,noscript"),fa=e("dt,dd"),ga=e("address,article,aside,blockquote,details,div,dl,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,menu,nav,ol,p,pre,section,table,ul"),ha=e("a,audio,del,ins,map,noscript,video"),ia=e("rb,rt,rtc,rp"),ja=e("rb,rtc,rp"),ka=e("option,optgroup"),la=e("tbody,tfoot"),ma=e("thead,tbody,tfoot"),na=e("td,th"),oa=e("html,head,body"),pa=e("html,body"),qa=e("head,colgroup,caption"),ra=e("dt,thead"),sa=e("a,abbr,acronym,address,applet,area,article,aside,audio,b,base,basefont,bdi,bdo,bgsound,big,blink,blockquote,body,br,button,canvas,caption,center,cite,code,col,colgroup,command,content,data,datalist,dd,del,details,dfn,dialog,dir,div,dl,dt,element,em,embed,fieldset,figcaption,figure,font,footer,form,frame,frameset,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,image,img,input,ins,isindex,kbd,keygen,label,legend,li,link,listing,main,map,mark,marquee,menu,menuitem,meta,meter,multicol,nav,nobr,noembed,noframes,noscript,object,ol,optgroup,option,output,p,param,picture,plaintext,pre,progress,q,rp,rt,rtc,ruby,s,samp,script,section,select,shadow,small,source,spacer,span,strike,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,tt,u,ul,var,video,wbr,xmp"),ta=new RegExp("^(?:class|id|style|title|lang|dir|on(?:focus|blur|change|click|dblclick|mouse(?:down|up|over|move|out)|key(?:press|down|up)))$"),ua=function(){var a=N("RelateUrl","relateurl");return a&&a.relate?function(b,c){try{return a.relate(b,c)}catch(d){return R(d),b}}:M}(),va=function(){var a=N("UglifyJS","uglify-js");return a&&a.minify?function(b,c){try{return a.minify(b,c).code}catch(d){return R(d),b}}:M}(),wa=function(){var a=N("CleanCSS","clean-css");return a?function(b,c,d){try{var e=new a(c);return d?x(e.minify(w(b)).styles):e.minify(b).styles}catch(f){return R(f),b}}:M}();a.minify=function(a,b){return P(a,b)}}("undefined"==typeof exports?this:exports),function(a){"use strict";function b(a){return/^(?:big|small|hr|blink|marquee)$/.test(a)}function c(a){return/^(?:applet|basefont|center|dir|font|isindex|strike)$/.test(a)}function d(a){return/^on[a-z]+/.test(a)}function e(a){return"style"===a.toLowerCase()}function f(a,b){return"align"===b&&/^(?:caption|applet|iframe|img|imput|object|legend|table|hr|div|h[1-6]|p)$/.test(a)||"alink"===b&&"body"===a||"alt"===b&&"applet"===a||"archive"===b&&"applet"===a||"background"===b&&"body"===a||"bgcolor"===b&&/^(?:table|t[rdh]|body)$/.test(a)||"border"===b&&/^(?:img|object)$/.test(a)||"clear"===b&&"br"===a||"code"===b&&"applet"===a||"codebase"===b&&"applet"===a||"color"===b&&/^(?:base(?:font)?)$/.test(a)||"compact"===b&&/^(?:dir|[dou]l|menu)$/.test(a)||"face"===b&&/^base(?:font)?$/.test(a)||"height"===b&&/^(?:t[dh]|applet)$/.test(a)||"hspace"===b&&/^(?:applet|img|object)$/.test(a)||"language"===b&&"script"===a||"link"===b&&"body"===a||"name"===b&&"applet"===a||"noshade"===b&&"hr"===a||"nowrap"===b&&/^t[dh]$/.test(a)||"object"===b&&"applet"===a||"prompt"===b&&"isindex"===a||"size"===b&&/^(?:hr|font|basefont)$/.test(a)||"start"===b&&"ol"===a||"text"===b&&"body"===a||"type"===b&&/^(?:li|ol|ul)$/.test(a)||"value"===b&&"li"===a||"version"===b&&"html"===a||"vlink"===b&&"body"===a||"vspace"===b&&/^(?:applet|img|object)$/.test(a)||"width"===b&&/^(?:hr|td|th|applet|pre)$/.test(a)}function g(a,b){return"href"===a&&/^\s*javascript\s*:\s*void\s*(\s+0|\(\s*0\s*\))\s*$/i.test(b)}function h(){this.log=[],this._lastElement=null,this._isElementRepeated=!1}h.prototype.testElement=function(a){c(a)?this.log.push('Found <span class="deprecated-element">deprecated</span> <strong><code><'+a+"></code></strong> element"):b(a)?this.log.push('Found <span class="presentational-element">presentational</span> <strong><code><'+a+"></code></strong> element"):this.checkRepeatingElement(a)},h.prototype.checkRepeatingElement=function(a){"br"===a&&"br"===this._lastElement?this._isElementRepeated=!0:this._isElementRepeated&&(this._reportRepeatingElement(),this._isElementRepeated=!1),this._lastElement=a},h.prototype._reportRepeatingElement=function(){this.log.push("Found <code><br></code> sequence. Try replacing it with styling.")},h.prototype.testAttribute=function(a,b,c){d(b)?this.log.push('Found <span class="event-attribute">event attribute</span> (<strong>'+b+"</strong>) on <strong><code><"+a+"></code></strong> element."):f(a,b)?this.log.push('Found <span class="deprecated-attribute">deprecated</span> <strong>'+b+"</strong> attribute on <strong><code><"+a+"></code></strong> element."):e(b)?this.log.push('Found <span class="style-attribute">style attribute</span> on <strong><code><'+a+"></code></strong> element."):g(b,c)&&this.log.push('Found <span class="inaccessible-attribute">inaccessible attribute</span> (on <strong><code><'+a+"></code></strong> element).")},h.prototype.testChars=function(a){this._lastElement="",/( \s*){2,}/.test(a)&&this.log.push("Found repeating <strong><code>&nbsp;</code></strong> sequence. Try replacing it with styling.")},h.prototype.test=function(a,b,c){this.testElement(a),this.testAttribute(a,b,c)},h.prototype.populate=function(a){if(this._isElementRepeated&&this._reportRepeatingElement(),this.log.length)if(a)a.innerHTML="<ol><li>"+this.log.join("<li>")+"</ol>";else{var b=" - "+this.log.join("\n - ").replace(/(<([^>]+)>)/gi,"").replace(/</g,"<").replace(/>/g,">");console.log(b)}},a.HTMLLint=h}("undefined"==typeof exports?this:exports); |
{ | ||
"name": "html-minifier", | ||
"description": "HTML minifier with lint-like capabilities.", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"keywords": [ | ||
@@ -36,2 +36,7 @@ "html", | ||
"scripts": { | ||
"assets": "grunt assets", | ||
"assets/clean-css": "browserify node_modules/clean-css/ --standalone CleanCSS | uglifyjs --compress --mangle -o assets/cleancss-browser.js", | ||
"assets/ncname": "browserify node_modules/ncname/ --standalone NCName | uglifyjs --compress --mangle -o assets/ncname-browser.js", | ||
"assets/relateurl": "browserify node_modules/relateurl/ --standalone RelateUrl | uglifyjs --compress --mangle -o assets/relateurl-browser.js", | ||
"assets/uglify-js": "browserify node_modules/uglify-js/ --standalone UglifyJS | uglifyjs --compress --mangle -o assets/uglify-browser.js", | ||
"test": "grunt test" | ||
@@ -44,2 +49,3 @@ }, | ||
"concat-stream": "1.5.x", | ||
"ncname": "1.0.x", | ||
"relateurl": "0.2.x", | ||
@@ -49,2 +55,3 @@ "uglify-js": "2.6.x" | ||
"devDependencies": { | ||
"browserify": "13.0.x", | ||
"brotli": "1.1.x", | ||
@@ -54,10 +61,10 @@ "chalk": "1.1.x", | ||
"grunt": "0.4.x", | ||
"grunt-contrib-concat": "0.5.x", | ||
"grunt-contrib-jshint": "1.0.x", | ||
"grunt-contrib-qunit": "1.0.x", | ||
"grunt-contrib-uglify": "0.11.x", | ||
"grunt-cli": "0.1.x", | ||
"grunt-contrib-concat": "1.0.x", | ||
"grunt-contrib-uglify": "1.0.x", | ||
"grunt-eslint": "18.0.x", | ||
"grunt-exec": "0.4.x", | ||
"grunt-jscs": "2.7.x", | ||
"grunt-lib-phantomjs": "1.0.x", | ||
"load-grunt-tasks": "3.4.x", | ||
"lzma": "2.2.x", | ||
"lzma": "2.3.x", | ||
"minimize": "1.8.x", | ||
@@ -64,0 +71,0 @@ "progress": "1.1.x", |
@@ -5,4 +5,5 @@ [![NPM version](https://img.shields.io/npm/v/html-minifier.svg)](https://www.npmjs.com/package/html-minifier) | ||
[![devDependency Status](https://img.shields.io/david/dev/kangax/html-minifier.svg)](https://david-dm.org/kangax/html-minifier#info=devDependencies) | ||
[![Gitter](https://img.shields.io/gitter/room/kangax/html-minifier.svg)](https://gitter.im/kangax/html-minifier) | ||
[HTMLMinifier](http://kangax.github.io/html-minifier/) is a highly **configurable**, **well-tested**, Javascript-based HTML minifier, with lint-like capabilities. | ||
[HTMLMinifier](http://kangax.github.io/html-minifier/) is a highly **configurable**, **well-tested**, JavaScript-based HTML minifier, with lint-like capabilities. | ||
@@ -30,4 +31,4 @@ See [corresponding blog post](http://perfectionkills.com/experimenting-with-html-minifier/) for all the gory details of [how it works](http://perfectionkills.com/experimenting-with-html-minifier/#how_it_works), [description of each option](http://perfectionkills.com/experimenting-with-html-minifier/#options), [testing results](http://perfectionkills.com/experimenting-with-html-minifier/#field_testing) and [conclusions](http://perfectionkills.com/experimenting-with-html-minifier/#cost_and_benefits). | ||
| [Wikipedia](http://en.wikipedia.org/wiki/President_of_the_United_States) | 401 | <b>367</b> | 388 | 400 | n/a | | ||
| [Eloquent Javascript](http://eloquentjavascript.net/print.html) | 870 | <b>826</b> | 840 | 864 | n/a | | ||
| [ES6 draft](https://people.mozilla.org/~jorendorff/es6-draft.html) | 3678 | <b>2991</b> | 3079 | 3204 | n/a | | ||
| [Eloquent Javascript](http://eloquentjavascript.net/print.html) | 870 | <b>827</b> | 840 | 864 | n/a | | ||
| [ES6 draft](https://people.mozilla.org/~jorendorff/es6-draft.html) | 3678 | <b>2990</b> | 3079 | 3204 | n/a | | ||
@@ -62,4 +63,5 @@ | ||
| `minifyJS` | Minify Javascript in script elements and event attributes (uses [UglifyJS](https://github.com/mishoo/UglifyJS2)) | `false` (could be `true`, `false`, `Object` (options)) | | ||
| `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/GoalSmashers/clean-css)) | `false` (could be `true`, `false`, `Object` (options)) | | ||
| `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/jakubpawlowicz/clean-css)) | `false` (could be `true`, `false`, `Object` (options)) | | ||
| `minifyURLs` | Minify URLs in various attributes (uses [relateurl](https://github.com/stevenvachon/relateurl)) | `false` (could be `Object` (options)) | | ||
| `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` | | ||
| `ignoreCustomComments` | Array of regex'es that allow to ignore certain comments, when matched | `[ ]` | | ||
@@ -75,5 +77,6 @@ | `ignoreCustomFragments` | Array of regex'es that allow to ignore certain fragments, when matched (e.g. `<?php ... ?>`, `{{ ... }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` | | ||
## Special cases | ||
### Ignoring chunks of markup. | ||
### Ignoring chunks of markup | ||
@@ -106,2 +109,3 @@ If you have chunks of markup you would like preserved, you can wrap them `<!-- htmlmin:ignore -->`. | ||
## Installation Instructions | ||
@@ -126,2 +130,3 @@ | ||
## Usage | ||
@@ -141,2 +146,3 @@ | ||
## Running benchmarks | ||
@@ -143,0 +149,0 @@ |
@@ -1,3 +0,1 @@ | ||
/* global CleanCSS */ | ||
(function(global) { | ||
@@ -39,4 +37,8 @@ 'use strict'; | ||
function compressWhitespace(spaces) { | ||
return spaces === '\t' ? spaces : ' '; | ||
} | ||
function collapseWhitespaceAll(str) { | ||
return str ? str.replace(/[\t\n\r ]+/g, ' ') : str; | ||
return str ? str.replace(/[\t\n\r ]+/g, compressWhitespace) : str; | ||
} | ||
@@ -62,6 +64,6 @@ | ||
if (options.preserveLineBreaks) { | ||
str = str.replace(/^[\t ]*[\n\r]+[\t\n\r ]*/, function() { | ||
str = str.replace(/^[\t ]*[\n\r][\t\n\r ]*/, function() { | ||
lineBreakBefore = '\n'; | ||
return ''; | ||
}).replace(/[\t\n\r ]*[\n\r]+[\t ]*$/, function() { | ||
}).replace(/[\t\n\r ]*[\n\r][\t ]*$/, function() { | ||
lineBreakAfter = '\n'; | ||
@@ -73,7 +75,7 @@ return ''; | ||
if (trimLeft) { | ||
str = str.replace(/^\s+/, !lineBreakBefore && options.conservativeCollapse ? ' ' : ''); | ||
str = str.replace(/^\s+/, !lineBreakBefore && options.conservativeCollapse ? compressWhitespace : ''); | ||
} | ||
if (trimRight) { | ||
str = str.replace(/\s+$/, !lineBreakAfter && options.conservativeCollapse ? ' ' : ''); | ||
str = str.replace(/\s+$/, !lineBreakAfter && options.conservativeCollapse ? compressWhitespace : ''); | ||
} | ||
@@ -89,11 +91,18 @@ | ||
// array of non-empty element tags that will maintain a single space outside of them | ||
// non-empty tags that will maintain whitespace around them | ||
var inlineTags = createMapFromString('a,abbr,acronym,b,bdi,bdo,big,button,cite,code,del,dfn,em,font,i,ins,kbd,mark,math,q,rt,rp,s,samp,small,span,strike,strong,sub,sup,svg,time,tt,u,var'); | ||
// non-empty tags that will maintain whitespace within them | ||
var inlineTextTags = createMapFromString('a,abbr,acronym,b,big,del,em,font,i,ins,kbd,mark,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var'); | ||
// self-closing tags that will maintain whitespace around them | ||
var selfClosingInlineTags = createMapFromString('comment,img,input'); | ||
function collapseWhitespaceSmart(str, prevTag, nextTag, options) { | ||
var trimLeft = prevTag && !selfClosingInlineTags(prevTag) && | ||
(options.collapseInlineTagWhitespace || prevTag.charAt(0) !== '/' || !inlineTags(prevTag.substr(1))); | ||
var trimRight = nextTag && !selfClosingInlineTags(nextTag) && | ||
(options.collapseInlineTagWhitespace || nextTag.charAt(0) === '/' || !inlineTags(nextTag)); | ||
var trimLeft = prevTag && !selfClosingInlineTags(prevTag); | ||
if (trimLeft && !options.collapseInlineTagWhitespace) { | ||
trimLeft = prevTag.charAt(0) === '/' ? !inlineTags(prevTag.slice(1)) : !inlineTextTags(prevTag); | ||
} | ||
var trimRight = nextTag && !selfClosingInlineTags(nextTag); | ||
if (trimRight && !options.collapseInlineTagWhitespace) { | ||
trimRight = nextTag.charAt(0) === '/' ? !inlineTextTags(nextTag.slice(1)) : !inlineTags(nextTag); | ||
} | ||
return collapseWhitespace(str, options, trimLeft, trimRight, prevTag && nextTag); | ||
@@ -103,3 +112,3 @@ } | ||
function isConditionalComment(text) { | ||
return ((/\[if[^\]]+\]/).test(text) || (/\s*((?:<!)?\[endif\])$/).test(text)); | ||
return /^\[if\s[^\]]+\]|\[endif\]$/.test(text); | ||
} | ||
@@ -182,10 +191,2 @@ | ||
function isScriptTypeAttribute(tag, attrName, attrValue) { | ||
return ( | ||
tag === 'script' && | ||
attrName === 'type' && | ||
trimWhitespace(attrValue.toLowerCase()) === 'text/javascript' | ||
); | ||
} | ||
// https://mathiasbynens.be/demo/javascript-mime-type | ||
@@ -202,2 +203,10 @@ // https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type | ||
function isScriptTypeAttribute(tag, attrName, attrValue) { | ||
return ( | ||
tag === 'script' && | ||
attrName === 'type' && | ||
executableScriptsMimetypes(trimWhitespace(attrValue.toLowerCase())) | ||
); | ||
} | ||
function isExecutableScript(tag, attrs) { | ||
@@ -210,3 +219,3 @@ if (tag !== 'script') { | ||
if (attrName === 'type') { | ||
var attrValue = trimWhitespace(attrs[i].value).split(/;/, 2)[0].toLowerCase(); | ||
var attrValue = trimWhitespace(attrs[i].value.split(/;/, 2)[0]).toLowerCase(); | ||
return attrValue === '' || executableScriptsMimetypes(attrValue); | ||
@@ -226,19 +235,7 @@ } | ||
var enumeratedAttributeValues = { | ||
draggable: ['true', 'false'] // defaults to 'auto' | ||
}; | ||
var isSimpleBoolean = createMapFromString('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible'); | ||
var isBooleanValue = createMapFromString('true,false'); | ||
function isBooleanAttribute(attrName, attrValue) { | ||
var isSimpleBoolean = (/^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|truespeed|typemustmatch|visible)$/i).test(attrName); | ||
if (isSimpleBoolean) { | ||
return true; | ||
} | ||
var attrValueEnumeration = enumeratedAttributeValues[attrName.toLowerCase()]; | ||
if (!attrValueEnumeration) { | ||
return false; | ||
} | ||
else { | ||
return (-1 === attrValueEnumeration.indexOf(attrValue.toLowerCase())); | ||
} | ||
return isSimpleBoolean(attrName) || attrName === 'draggable' && !isBooleanValue(attrValue); | ||
} | ||
@@ -330,2 +327,5 @@ | ||
} | ||
else if (tag === 'script' && attrName === 'type') { | ||
attrValue = trimWhitespace(attrValue.replace(/\s*;\s*/g, ';')); | ||
} | ||
return attrValue; | ||
@@ -361,6 +361,6 @@ } | ||
function cleanConditionalComment(comment) { | ||
return comment | ||
.replace(/^(\[[^\]]+\]>)\s*/, '$1') | ||
.replace(/\s*(<!\[endif\])$/, '$1'); | ||
function cleanConditionalComment(comment, options) { | ||
return comment.replace(/^(\[if\s[^\]]+\]>)([\s\S]*?)(<!\[endif\])$/, function(match, prefix, text, suffix) { | ||
return prefix + minify(text, options, true) + suffix; | ||
}); | ||
} | ||
@@ -403,2 +403,3 @@ | ||
// - </rb>, </rt>, </rtc>, </rp> & </tfoot> follow http://www.w3.org/TR/html5/syntax.html#optional-tags | ||
// - retain all tags which are adjacent to non-standard HTML tags | ||
var optionalStartTags = createMapFromString('html,head,body,colgroup,tbody'); | ||
@@ -419,2 +420,4 @@ var optionalEndTags = createMapFromString('html,head,body,li,dt,dd,p,rb,rt,rtc,rp,optgroup,option,colgroup,caption,thead,tbody,tfoot,tr,td,th'); | ||
var looseTags = createMapFromString('head,colgroup,caption'); | ||
var trailingTags = createMapFromString('dt,thead'); | ||
var htmlTags = createMapFromString('a,abbr,acronym,address,applet,area,article,aside,audio,b,base,basefont,bdi,bdo,bgsound,big,blink,blockquote,body,br,button,canvas,caption,center,cite,code,col,colgroup,command,content,data,datalist,dd,del,details,dfn,dialog,dir,div,dl,dt,element,em,embed,fieldset,figcaption,figure,font,footer,form,frame,frameset,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,image,img,input,ins,isindex,kbd,keygen,label,legend,li,link,listing,main,map,mark,marquee,menu,menuitem,meta,meter,multicol,nav,nobr,noembed,noframes,noscript,object,ol,optgroup,option,output,p,param,picture,plaintext,pre,progress,q,rp,rt,rtc,ruby,s,samp,script,section,select,shadow,small,source,spacer,span,strike,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,tt,u,ul,var,video,wbr,xmp'); | ||
@@ -497,15 +500,38 @@ function canRemoveParentTag(optionalStartTag, tag) { | ||
function canRemoveElement(tag, attrs) { | ||
if (tag === 'textarea') { | ||
return false; | ||
function hasAttrName(name, attrs) { | ||
for (var i = attrs.length - 1; i >= 0; i--) { | ||
if (attrs[i].name === name) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
if (tag === 'script') { | ||
for (var i = attrs.length - 1; i >= 0; i--) { | ||
if (attrs[i].name === 'src') { | ||
function canRemoveElement(tag, attrs) { | ||
switch (tag) { | ||
case 'textarea': | ||
return false; | ||
case 'audio': | ||
case 'script': | ||
case 'video': | ||
if (hasAttrName('src', attrs)) { | ||
return false; | ||
} | ||
} | ||
break; | ||
case 'iframe': | ||
if (hasAttrName('src', attrs) || hasAttrName('srcdoc', attrs)) { | ||
return false; | ||
} | ||
break; | ||
case 'object': | ||
if (hasAttrName('data', attrs)) { | ||
return false; | ||
} | ||
break; | ||
case 'applet': | ||
if (hasAttrName('code', attrs)) { | ||
return false; | ||
} | ||
break; | ||
} | ||
return true; | ||
@@ -580,3 +606,3 @@ } | ||
if (attrValue === undefined || (options.collapseBooleanAttributes && | ||
isBooleanAttribute(attrName, attrValue))) { | ||
isBooleanAttribute(attrName.toLowerCase(), attrValue.toLowerCase()))) { | ||
attrFragment = attrName; | ||
@@ -594,6 +620,8 @@ if (!isLast) { | ||
function setDefaultTesters(options) { | ||
function processOptions(options) { | ||
if (!('includeAutoGeneratedTags' in options)) { | ||
options.includeAutoGeneratedTags = true; | ||
} | ||
var defaultTesters = ['canCollapseWhitespace', 'canTrimWhitespace']; | ||
for (var i = 0, len = defaultTesters.length; i < len; i++) { | ||
@@ -606,119 +634,97 @@ if (!options[defaultTesters[i]]) { | ||
} | ||
} | ||
function minifyURLs(text, options) { | ||
if (typeof options !== 'object') { | ||
options = { }; | ||
if (options.minifyURLs && typeof options.minifyURLs !== 'object') { | ||
options.minifyURLs = { }; | ||
} | ||
try { | ||
// try to get global reference first | ||
var __RelateUrl = global.RelateUrl; | ||
if (typeof __RelateUrl === 'undefined' && typeof require === 'function') { | ||
__RelateUrl = require('relateurl'); | ||
if (options.minifyJS) { | ||
if (typeof options.minifyJS !== 'object') { | ||
options.minifyJS = { }; | ||
} | ||
options.minifyJS.fromString = true; | ||
(options.minifyJS.output || (options.minifyJS.output = { })).inline_script = true; | ||
} | ||
// noop | ||
if (!__RelateUrl) { | ||
return text; | ||
if (options.minifyCSS) { | ||
if (typeof options.minifyCSS !== 'object') { | ||
options.minifyCSS = { }; | ||
} | ||
if (__RelateUrl.relate) { | ||
return __RelateUrl.relate(text, options); | ||
if (typeof options.minifyCSS.advanced === 'undefined') { | ||
options.minifyCSS.advanced = false; | ||
} | ||
else { | ||
return text; | ||
} | ||
} | ||
catch (err) { | ||
log(err); | ||
} | ||
} | ||
function noop(text) { | ||
return text; | ||
} | ||
function minifyJS(text, options) { | ||
if (typeof options !== 'object') { | ||
options = { }; | ||
function getModule(name, path) { | ||
// try to get global reference first | ||
var result = global[name]; | ||
if (typeof result === 'undefined' && typeof require === 'function') { | ||
result = require(path); | ||
} | ||
options.fromString = true; | ||
var outputOptions = options.output || {}; | ||
outputOptions.inline_script = true; | ||
options.output = outputOptions; | ||
return result; | ||
} | ||
try { | ||
// try to get global reference first | ||
var __UglifyJS = global.UglifyJS; | ||
if (typeof __UglifyJS === 'undefined' && typeof require === 'function') { | ||
__UglifyJS = require('uglify-js'); | ||
} | ||
// noop | ||
if (!__UglifyJS) { | ||
return text; | ||
} | ||
if (__UglifyJS.minify) { | ||
return __UglifyJS.minify(text, options).code; | ||
} | ||
else if (__UglifyJS.parse) { | ||
var ast = __UglifyJS.parse(text); | ||
ast.figure_out_scope(); | ||
var compressor = __UglifyJS.Compressor(); | ||
var compressedAst = ast.transform(compressor); | ||
compressedAst.figure_out_scope(); | ||
compressedAst.compute_char_frequency(); | ||
if (options.mangle !== false) { | ||
compressedAst.mangle_names(); | ||
var minifyURLs = (function() { | ||
var RelateUrl = getModule('RelateUrl', 'relateurl'); | ||
if (RelateUrl && RelateUrl.relate) { | ||
return function(text, options) { | ||
try { | ||
return RelateUrl.relate(text, options); | ||
} | ||
var stream = __UglifyJS.OutputStream(options.output); | ||
compressedAst.print(stream); | ||
return stream.toString(); | ||
} | ||
else { | ||
return text; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
catch (err) { | ||
log(err); | ||
else { | ||
return noop; | ||
} | ||
return text; | ||
} | ||
})(); | ||
function minifyCSS(text, options, inline) { | ||
if (typeof options !== 'object') { | ||
options = { }; | ||
var minifyJS = (function() { | ||
var UglifyJS = getModule('UglifyJS', 'uglify-js'); | ||
if (UglifyJS && UglifyJS.minify) { | ||
return function(text, options) { | ||
try { | ||
return UglifyJS.minify(text, options).code; | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
if (typeof options.advanced === 'undefined') { | ||
options.advanced = false; | ||
else { | ||
return noop; | ||
} | ||
try { | ||
var cleanCSS; | ||
})(); | ||
if (typeof CleanCSS !== 'undefined') { | ||
cleanCSS = new CleanCSS(options); | ||
} | ||
else if (typeof require === 'function') { | ||
var CleanCSSModule = require('clean-css'); | ||
cleanCSS = new CleanCSSModule(options); | ||
} | ||
if (inline) { | ||
return unwrapCSS(cleanCSS.minify(wrapCSS(text)).styles); | ||
} | ||
else { | ||
return cleanCSS.minify(text).styles; | ||
} | ||
var minifyCSS = (function() { | ||
var CleanCSS = getModule('CleanCSS', 'clean-css'); | ||
if (CleanCSS) { | ||
return function(text, options, inline) { | ||
try { | ||
var cleanCSS = new CleanCSS(options); | ||
if (inline) { | ||
return unwrapCSS(cleanCSS.minify(wrapCSS(text)).styles); | ||
} | ||
else { | ||
return cleanCSS.minify(text).styles; | ||
} | ||
} | ||
catch (err) { | ||
log(err); | ||
return text; | ||
} | ||
}; | ||
} | ||
catch (err) { | ||
log(err); | ||
else { | ||
return noop; | ||
} | ||
return text; | ||
} | ||
})(); | ||
@@ -733,14 +739,12 @@ function uniqueId(value) { | ||
function minify(value, options) { | ||
function minify(value, options, partialMarkup) { | ||
options = options || {}; | ||
var optionsStack = []; | ||
processOptions(options); | ||
value = options.collapseWhitespace ? trimWhitespace(value) : value; | ||
value = trimWhitespace(value); | ||
setDefaultTesters(options); | ||
var results = [ ], | ||
buffer = [ ], | ||
var buffer = [ ], | ||
charsPrevTag, | ||
currentChars = '', | ||
hasChars, | ||
currentTag = '', | ||
@@ -759,13 +763,14 @@ currentAttrs = [], | ||
if (~value.indexOf('<!-- htmlmin:ignore -->')) { | ||
uidIgnore = '<!--!' + uniqueId(value) + '-->'; | ||
// temporarily replace ignored chunks with comments, | ||
// so that we don't have to worry what's there. | ||
// for all we care there might be | ||
// completely-horribly-broken-alien-non-html-emoj-cthulhu-filled content | ||
value = value.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g, function(match, group1) { | ||
ignoredMarkupChunks.push(group1); | ||
return uidIgnore; | ||
}); | ||
} | ||
// temporarily replace ignored chunks with comments, | ||
// so that we don't have to worry what's there. | ||
// for all we care there might be | ||
// completely-horribly-broken-alien-non-html-emoj-cthulhu-filled content | ||
value = value.replace(/<!-- htmlmin:ignore -->([\s\S]*?)<!-- htmlmin:ignore -->/g, function(match, group1) { | ||
if (!uidIgnore) { | ||
uidIgnore = '<!--!' + uniqueId(value) + '-->'; | ||
} | ||
var token = uidIgnore + ignoredMarkupChunks.length; | ||
ignoredMarkupChunks.push(group1); | ||
return token; | ||
}); | ||
@@ -779,3 +784,3 @@ var customFragments = (options.ignoreCustomFragments || [ | ||
if (customFragments.length) { | ||
var reCustomIgnore = new RegExp('\\s*(?:' + customFragments.join('|') + ')\\s*', 'g'); | ||
var reCustomIgnore = new RegExp('\\s*(?:' + customFragments.join('|') + ')+\\s*', 'g'); | ||
// temporarily replace custom ignored fragments with unique attributes | ||
@@ -786,4 +791,5 @@ value = value.replace(reCustomIgnore, function(match) { | ||
} | ||
var token = uidAttr + ignoredCustomMarkupChunks.length; | ||
ignoredCustomMarkupChunks.push(match); | ||
return ' ' + uidAttr + ' '; | ||
return '\t' + token + '\t'; | ||
}); | ||
@@ -813,6 +819,26 @@ } | ||
} | ||
buffer.length = index; | ||
buffer.length = Math.max(0, index); | ||
} | ||
// look for trailing whitespaces from previously processed text | ||
// which may not be trimmed due to a following comment or an empty | ||
// element which has now been removed | ||
function squashTrailingWhitespace(nextTag) { | ||
var charsIndex; | ||
if (buffer.length > 1 && /^(?:<!|$)/.test(buffer[buffer.length - 1]) && | ||
/\s$/.test(buffer[buffer.length - 2])) { | ||
charsIndex = buffer.length - 2; | ||
} | ||
else if (buffer.length > 0 && /\s$/.test(buffer[buffer.length - 1])) { | ||
charsIndex = buffer.length - 1; | ||
} | ||
if (charsIndex > 0) { | ||
buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(text) { | ||
return collapseWhitespaceSmart(text, 'comment', nextTag, options); | ||
}); | ||
} | ||
} | ||
new HTMLParser(value, { | ||
partialMarkup: partialMarkup, | ||
html5: typeof options.html5 !== 'undefined' ? options.html5 : true, | ||
@@ -838,3 +864,6 @@ | ||
charsPrevTag = tag; | ||
currentChars = ''; | ||
if (!inlineTextTags(tag)) { | ||
currentChars = ''; | ||
} | ||
hasChars = false; | ||
currentAttrs = attrs; | ||
@@ -844,2 +873,3 @@ | ||
if (optional) { | ||
var htmlTag = htmlTags(tag); | ||
// <html> may be omitted if first thing inside is not comment | ||
@@ -850,3 +880,3 @@ // <head> may be omitted if first thing inside is an element | ||
// <tbody> may be omitted if first thing inside is <tr> | ||
if (canRemoveParentTag(optionalStartTag, tag)) { | ||
if (htmlTag && canRemoveParentTag(optionalStartTag, tag)) { | ||
removeStartTag(); | ||
@@ -856,3 +886,3 @@ } | ||
// end-tag-followed-by-start-tag omission rules | ||
if (canRemovePrecedingTag(optionalEndTag, tag)) { | ||
if (htmlTag && canRemovePrecedingTag(optionalEndTag, tag)) { | ||
removeEndTag(); | ||
@@ -868,2 +898,5 @@ // <colgroup> cannot be omitted if preceding </colgroup> is omitted | ||
if (options.collapseWhitespace) { | ||
if (!stackNoTrimWhitespace.length) { | ||
squashTrailingWhitespace(tag); | ||
} | ||
if (!_canTrimWhitespace(tag, attrs)) { | ||
@@ -909,3 +942,3 @@ stackNoTrimWhitespace.push(tag); | ||
}, | ||
end: function(tag, attrs) { | ||
end: function(tag, attrs, autoGenerated) { | ||
var lowerTag = tag.toLowerCase(); | ||
@@ -915,16 +948,4 @@ if (lowerTag === 'svg') { | ||
} | ||
tag = options.caseSensitive ? tag : lowerTag; | ||
if (options.removeOptionalTags) { | ||
// </html> or </body> may be omitted if not followed by comment | ||
// </head> may be omitted if not followed by space or comment | ||
// </p> may be omitted if no more content in non-</a> parent | ||
// except for </dt> or </thead>, end tags may be omitted if no more content in parent element | ||
if (optionalEndTag && optionalEndTag !== 'dt' && optionalEndTag !== 'thead' && (optionalEndTag !== 'p' || !pInlineTags(tag))) { | ||
removeEndTag(); | ||
} | ||
optionalEndTag = optionalEndTags(tag) ? tag : ''; | ||
} | ||
// check if current tag is in a whitespace stack | ||
@@ -938,14 +959,3 @@ if (options.collapseWhitespace) { | ||
else { | ||
var charsIndex; | ||
if (buffer.length > 1 && buffer[buffer.length - 1] === '' && /\s+$/.test(buffer[buffer.length - 2])) { | ||
charsIndex = buffer.length - 2; | ||
} | ||
else if (buffer.length > 0 && /\s+$/.test(buffer[buffer.length - 1])) { | ||
charsIndex = buffer.length - 1; | ||
} | ||
if (charsIndex > 0) { | ||
buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(text) { | ||
return collapseWhitespaceSmart(text, 'comment', '/' + tag, options); | ||
}); | ||
} | ||
squashTrailingWhitespace('/' + tag); | ||
} | ||
@@ -961,4 +971,21 @@ if (stackNoCollapseWhitespace.length && | ||
currentTag = ''; | ||
isElementEmpty = currentChars === ''; | ||
isElementEmpty = !hasChars; | ||
} | ||
if (options.removeOptionalTags) { | ||
// <html>, <head> or <body> may be omitted if the element is empty | ||
if (isElementEmpty && topLevelTags(optionalStartTag)) { | ||
removeStartTag(); | ||
} | ||
optionalStartTag = ''; | ||
// </html> or </body> may be omitted if not followed by comment | ||
// </head> may be omitted if not followed by space or comment | ||
// </p> may be omitted if no more content in non-</a> parent | ||
// except for </dt> or </thead>, end tags may be omitted if no more content in parent element | ||
if (htmlTags(tag) && optionalEndTag && !trailingTags(optionalEndTag) && (optionalEndTag !== 'p' || !pInlineTags(tag))) { | ||
removeEndTag(); | ||
} | ||
optionalEndTag = optionalEndTags(tag) ? tag : ''; | ||
} | ||
if (options.removeEmptyElements && isElementEmpty && canRemoveElement(tag, attrs)) { | ||
@@ -972,6 +999,9 @@ // remove last "element" from buffer | ||
// push out everything but the end tag | ||
results.push.apply(results, buffer); | ||
buffer = ['</' + tag + '>']; | ||
if (options.includeAutoGeneratedTags || !autoGenerated) { | ||
buffer.push('</' + tag + '>'); | ||
} | ||
charsPrevTag = '/' + tag; | ||
currentChars = ''; | ||
if (!inlineTextTags(tag)) { | ||
currentChars = ''; | ||
} | ||
} | ||
@@ -989,3 +1019,3 @@ }, | ||
} | ||
if (buffer.length > 1 && (removed || currentChars.charAt(currentChars.length - 1) === ' ')) { | ||
if (buffer.length > 1 && (removed || / $/.test(currentChars))) { | ||
var charsIndex = buffer.length - 2; | ||
@@ -998,3 +1028,18 @@ buffer[charsIndex] = buffer[charsIndex].replace(/\s+$/, function(trailingSpaces) { | ||
} | ||
if (prevTag && inlineTextTags(prevTag.charAt(0) === '/' ? prevTag.slice(1) : prevTag)) { | ||
text = collapseWhitespace(text, options, /(?:^|\s)$/.test(currentChars)); | ||
} | ||
text = prevTag || nextTag ? collapseWhitespaceSmart(text, prevTag, nextTag, options) : trimWhitespace(text); | ||
if (!text && /\s$/.test(currentChars) && prevTag && prevTag.charAt(0) === '/') { | ||
for (var index = buffer.length - 2, endTag = prevTag.slice(1); index >= 0 && _canTrimWhitespace(endTag); index--) { | ||
var str = buffer[index]; | ||
var match = str.match(/^<\/([\w:-]+)>$/); | ||
if (match) { | ||
endTag = match[1]; | ||
} | ||
else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, null, nextTag, options))) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
@@ -1041,2 +1086,5 @@ if (!stackNoCollapseWhitespace.length) { | ||
currentChars += text; | ||
if (text) { | ||
hasChars = true; | ||
} | ||
if (lint) { | ||
@@ -1050,7 +1098,7 @@ lint.testChars(text); | ||
var suffix = nonStandard ? '>' : '-->'; | ||
if (options.removeComments) { | ||
if (isConditionalComment(text)) { | ||
text = prefix + cleanConditionalComment(text) + suffix; | ||
} | ||
else if (isIgnoredComment(text, options)) { | ||
if (isConditionalComment(text)) { | ||
text = prefix + cleanConditionalComment(text, options) + suffix; | ||
} | ||
else if (options.removeComments) { | ||
if (isIgnoredComment(text, options)) { | ||
text = '<!--' + text + '-->'; | ||
@@ -1086,3 +1134,3 @@ } | ||
// except for </dt> or </thead>, end tags may be omitted if no more content in parent element | ||
if (optionalEndTag && optionalEndTag !== 'dt' && optionalEndTag !== 'thead') { | ||
if (optionalEndTag && !trailingTags(optionalEndTag)) { | ||
removeEndTag(); | ||
@@ -1092,17 +1140,27 @@ } | ||
results.push.apply(results, buffer); | ||
var str = joinResultSegments(results, options); | ||
var str = joinResultSegments(buffer, options); | ||
if (uidAttr) { | ||
str = str.replace(new RegExp('(\\s*)' + uidAttr + '(\\s*)', 'g'), function(match, prefix, suffix) { | ||
var chunk = ignoredCustomMarkupChunks.shift(); | ||
return options.collapseWhitespace ? collapseWhitespace(prefix + chunk + suffix, { | ||
preserveLineBreaks: options.preserveLineBreaks, | ||
conservativeCollapse: true | ||
}, true, true) : chunk; | ||
str = str.replace(new RegExp('(\\s*)' + uidAttr + '([0-9]+)(\\s*)', 'g'), function(match, prefix, index, suffix) { | ||
var chunk = ignoredCustomMarkupChunks[+index]; | ||
if (options.collapseWhitespace) { | ||
if (prefix !== '\t') { | ||
chunk = prefix + chunk; | ||
} | ||
if (suffix !== '\t') { | ||
chunk += suffix; | ||
} | ||
return collapseWhitespace(chunk, { | ||
preserveLineBreaks: options.preserveLineBreaks, | ||
conservativeCollapse: true | ||
}, /^\s/.test(chunk), /\s$/.test(chunk)); | ||
} | ||
else { | ||
return chunk; | ||
} | ||
}); | ||
} | ||
if (uidIgnore) { | ||
str = str.replace(new RegExp(uidIgnore, 'g'), function() { | ||
return ignoredMarkupChunks.shift(); | ||
str = str.replace(new RegExp(uidIgnore + '([0-9]+)', 'g'), function(match, index) { | ||
return ignoredMarkupChunks[+index]; | ||
}); | ||
@@ -1140,13 +1198,9 @@ } | ||
return trimWhitespace(str); | ||
return options.collapseWhitespace ? trimWhitespace(str) : str; | ||
} | ||
// for CommonJS enviroments, export everything | ||
if (typeof exports !== 'undefined') { | ||
exports.minify = minify; | ||
} | ||
else { | ||
global.minify = minify; | ||
} | ||
global.minify = function(value, options) { | ||
return minify(value, options); | ||
}; | ||
}(typeof exports === 'undefined' ? this : exports)); |
@@ -35,14 +35,33 @@ /*! | ||
// Regular Expressions for parsing tags and attributes | ||
var singleAttrIdentifier = /([\w:\.-]+)/, | ||
var singleAttrIdentifier = /([^\s"'<>\/=]+)/, | ||
singleAttrAssign = /=/, | ||
singleAttrAssigns = [ singleAttrAssign ], | ||
singleAttrValues = [ | ||
/"((?:\\.|[^"])*)"/.source, // attr value double quotes | ||
/'((?:\\.|[^'])*)'/.source, // attr value, single quotes | ||
/([^>\s]+)/.source // attr value, no quotes | ||
// attr value double quotes | ||
/"([^"]*)"+/.source, | ||
// attr value, single quotes | ||
/'([^']*)'+/.source, | ||
// attr value, no quotes | ||
/([^\s"'=<>`]+)/.source | ||
], | ||
startTagOpen = /^<([\w:-]+)/, | ||
startTagClose = /\s*(\/?)>/, | ||
endTag = /^<\/([\w:-]+)[^>]*>/, | ||
endingSlash = /\/>$/, | ||
// https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName | ||
qnameCapture = (function() { | ||
var ncname; | ||
if (typeof require === 'function') { | ||
ncname = require('ncname'); | ||
} | ||
else { | ||
ncname = global.NCName; | ||
} | ||
if (ncname) { | ||
ncname = ncname.source.slice(1, -1); | ||
} | ||
else { | ||
ncname = '[:A-Za-z_][:\\w\\-\\.]*'; | ||
} | ||
return '((?:' + ncname + '\\:)?' + ncname + ')'; | ||
})(), | ||
startTagOpen = new RegExp('^<' + qnameCapture), | ||
startTagClose = /^\s*(\/?)>/, | ||
endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>'), | ||
doctype = /^<!DOCTYPE [^>]+>/i; | ||
@@ -71,71 +90,25 @@ | ||
var reCache = {}, stackedTag, reStackedTag, tagMatch; | ||
// Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content | ||
var phrasingOnly = makeMap('abbr,b,bdi,bdo,button,cite,code,data,dfn,em,h1,h2,h3,h4,h5,h6,i,kbd,label,legend,mark,meter,output,p,pre,progress,q,rp,rt,s,samp,small,span,strong,sub,sup,time,u,var'); | ||
var phrasing = makeMap('a,abbr,area,audio,b,bdi,bdo,br,button,canvas,cite,code,data,datalist,del,dfn,em,embed,i,iframe,img,input,ins,kbd,keygen,label,link,main,map,mark,math,menu,meter,nav,noscript,object,ol,output,p,picture,pre,progress,q,ruby,s,samp,script,section,select,small,span,strong,sub,sup,svg,table,template,textarea,time,u,ul,var,video,wbr'); | ||
function startTagForHandler( handler ) { | ||
var customStartTagAttrs; | ||
var reCache = {}; | ||
var startTagAttrs = new RegExp( | ||
'(?:\\s*[\\w:\\.-]+' | ||
+ '(?:\\s*' | ||
+ '(?:' + joinSingleAttrAssigns(handler) + ')' | ||
+ '\\s*(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>\\s]+)' | ||
+ ')?' | ||
+ ')*' | ||
); | ||
if ( handler.customAttrSurround ) { | ||
function attrForHandler(handler) { | ||
var pattern = singleAttrIdentifier.source | ||
+ '(?:\\s*(' + joinSingleAttrAssigns(handler) + ')' | ||
+ '\\s*(?:' + singleAttrValues.join('|') + '))?'; | ||
if (handler.customAttrSurround) { | ||
var attrClauses = []; | ||
for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { | ||
// Capture the custom attribute opening and closing markup surrounding the standard attribute rules | ||
attrClauses[i] = '(?:\\s*' | ||
+ handler.customAttrSurround[i][0].source | ||
+ '\\s*' | ||
+ startTagAttrs.source | ||
+ '\\s*' | ||
+ handler.customAttrSurround[i][1].source | ||
+ ')'; | ||
} | ||
attrClauses.unshift(startTagAttrs.source); | ||
customStartTagAttrs = new RegExp( | ||
'((?:' + attrClauses.join('|') + ')*)' | ||
); | ||
} | ||
else { | ||
// No custom attribute wrappers specified, so just capture the standard attribute rules | ||
customStartTagAttrs = new RegExp('(' + startTagAttrs.source + ')'); | ||
} | ||
return new RegExp(startTagOpen.source + customStartTagAttrs.source + startTagClose.source); | ||
} | ||
function attrForHandler( handler ) { | ||
var singleAttr = new RegExp( | ||
singleAttrIdentifier.source | ||
+ '(?:\\s*' | ||
+ '(' + joinSingleAttrAssigns( handler ) + ')' | ||
+ '\\s*' | ||
+ '(?:' | ||
+ singleAttrValues.join('|') | ||
+ ')' | ||
+ ')?' | ||
); | ||
if ( handler.customAttrSurround ) { | ||
var attrClauses = []; | ||
for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { | ||
attrClauses[i] = '(?:' | ||
+ '(' + handler.customAttrSurround[i][0].source + ')\\s*' | ||
+ singleAttr.source | ||
+ pattern | ||
+ '\\s*(' + handler.customAttrSurround[i][1].source + ')' | ||
+ ')'; | ||
} | ||
attrClauses.unshift('(?:' + singleAttr.source + ')'); | ||
return new RegExp(attrClauses.join('|'), 'g'); | ||
attrClauses.push('(?:' + pattern + ')'); | ||
pattern = '(?:' + attrClauses.join('|') + ')'; | ||
} | ||
else { | ||
return new RegExp(singleAttr.source, 'g'); | ||
} | ||
return new RegExp('^\\s*' + pattern); | ||
} | ||
@@ -152,89 +125,88 @@ | ||
var HTMLParser = global.HTMLParser = function( html, handler ) { | ||
var index, match, stack = [], last = html, prevTag, nextTag; | ||
stack.last = function() { | ||
var last = this[ this.length - 1 ]; | ||
return last && last.tag; | ||
}; | ||
var startTag = startTagForHandler(handler); | ||
var attr = attrForHandler(handler); | ||
var stack = [], lastTag; | ||
var attribute = attrForHandler(handler); | ||
var last, prevTag, nextTag; | ||
while ( html ) { | ||
last = html; | ||
// Make sure we're not in a script or style element | ||
if ( !stack.last() || !special[ stack.last() ] ) { | ||
if ( !lastTag || !special[ lastTag ] ) { | ||
var textEnd = html.indexOf('<'); | ||
if (textEnd === 0) { | ||
// Comment: | ||
if ( /^<!--/.test( html ) ) { | ||
var commentEnd = html.indexOf('-->'); | ||
// Comment: | ||
if ( /^<!--/.test( html ) ) { | ||
index = html.indexOf('-->'); | ||
if ( index >= 0 ) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring( 4, index ) ); | ||
if ( commentEnd >= 0 ) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring( 4, commentEnd ) ); | ||
} | ||
html = html.substring( commentEnd + 3 ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
html = html.substring( index + 3 ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
} | ||
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment | ||
if ( /^<!\[/.test( html ) ) { | ||
index = html.indexOf(']>'); | ||
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment | ||
if ( /^<!\[/.test( html ) ) { | ||
var conditionalEnd = html.indexOf(']>'); | ||
if (index >= 0) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring(2, index + 1 ), true /* non-standard */ ); | ||
if (conditionalEnd >= 0) { | ||
if ( handler.comment ) { | ||
handler.comment( html.substring(2, conditionalEnd + 1 ), true /* non-standard */ ); | ||
} | ||
html = html.substring( conditionalEnd + 2 ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
html = html.substring( index + 2 ); | ||
} | ||
// Doctype: | ||
var doctypeMatch = html.match( doctype ); | ||
if ( doctypeMatch ) { | ||
if ( handler.doctype ) { | ||
handler.doctype( doctypeMatch[0] ); | ||
} | ||
html = html.substring( doctypeMatch[0].length ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
} | ||
// Doctype: | ||
if ( (match = doctype.exec( html )) ) { | ||
if ( handler.doctype ) { | ||
handler.doctype( match[0] ); | ||
// End tag: | ||
var endTagMatch = html.match( endTag ); | ||
if ( endTagMatch ) { | ||
html = html.substring( endTagMatch[0].length ); | ||
endTagMatch[0].replace( endTag, parseEndTag ); | ||
prevTag = '/' + endTagMatch[1].toLowerCase(); | ||
continue; | ||
} | ||
html = html.substring( match[0].length ); | ||
prevTag = ''; | ||
continue; | ||
} | ||
// End tag: | ||
if ( /^<\//.test( html ) ) { | ||
match = html.match( endTag ); | ||
if ( match ) { | ||
html = html.substring( match[0].length ); | ||
match[0].replace( endTag, parseEndTag ); | ||
prevTag = '/' + match[1].toLowerCase(); | ||
// Start tag: | ||
var startTagMatch = parseStartTag(html); | ||
if ( startTagMatch ) { | ||
html = startTagMatch.rest; | ||
handleStartTag(startTagMatch); | ||
prevTag = startTagMatch.tagName.toLowerCase(); | ||
continue; | ||
} | ||
} | ||
var text; | ||
if (textEnd >= 0) { | ||
text = html.substring( 0, textEnd ); | ||
html = html.substring( textEnd ); | ||
} | ||
// Start tag: | ||
if ( /^</.test( html ) ) { | ||
match = html.match( startTag ); | ||
if ( match ) { | ||
html = html.substring( match[0].length ); | ||
match[0].replace( startTag, parseStartTag ); | ||
prevTag = match[1].toLowerCase(); | ||
continue; | ||
} | ||
else { | ||
text = html; | ||
html = ''; | ||
} | ||
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]; | ||
var nextTagMatch = parseStartTag(html); | ||
if (nextTagMatch) { | ||
nextTag = nextTagMatch.tagName; | ||
} | ||
else { | ||
tagMatch = html.match( endTag ); | ||
if (tagMatch) { | ||
nextTag = '/' + tagMatch[1]; | ||
nextTagMatch = html.match( endTag ); | ||
if (nextTagMatch) { | ||
nextTag = '/' + nextTagMatch[1]; | ||
} | ||
@@ -253,6 +225,5 @@ else { | ||
else { | ||
var stackedTag = lastTag.toLowerCase(); | ||
var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)<\/' + stackedTag + '[^>]*>', 'i')); | ||
stackedTag = stack.last().toLowerCase(); | ||
reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)<\/' + stackedTag + '[^>]*>', 'i')); | ||
html = html.replace(reStackedTag, function(all, text) { | ||
@@ -272,3 +243,3 @@ if (stackedTag !== 'script' && stackedTag !== 'style' && stackedTag !== 'noscript') { | ||
parseEndTag( '', stackedTag ); | ||
parseEndTag( '</' + stackedTag + '>', stackedTag ); | ||
} | ||
@@ -279,24 +250,51 @@ | ||
} | ||
last = html; | ||
} | ||
// Clean up any remaining tags | ||
parseEndTag(); | ||
if (!handler.partialMarkup) { | ||
// Clean up any remaining tags | ||
parseEndTag(); | ||
} | ||
function parseStartTag( tag, tagName, rest, unary ) { | ||
var unarySlash = false; | ||
function parseStartTag(input) { | ||
var start = input.match(startTagOpen); | ||
if (start) { | ||
var match = { | ||
tagName: start[1], | ||
attrs: [] | ||
}; | ||
input = input.slice(start[0].length); | ||
var end, attr; | ||
while (!(end = input.match(startTagClose)) && (attr = input.match(attribute))) { | ||
input = input.slice(attr[0].length); | ||
match.attrs.push(attr); | ||
} | ||
if (end) { | ||
match.unarySlash = end[1]; | ||
match.rest = input.slice(end[0].length); | ||
return match; | ||
} | ||
} | ||
} | ||
while ( !handler.html5 && stack.last() && inline[ stack.last() ]) { | ||
parseEndTag( '', stack.last() ); | ||
function handleStartTag(match) { | ||
var tagName = match.tagName; | ||
var unarySlash = match.unarySlash; | ||
if (handler.html5 && lastTag && phrasingOnly[lastTag] && !phrasing[tagName]) { | ||
parseEndTag( '', lastTag ); | ||
} | ||
if ( closeSelf[ tagName ] && stack.last() === tagName ) { | ||
if (!handler.html5) { | ||
while (lastTag && inline[ lastTag ]) { | ||
parseEndTag( '', lastTag ); | ||
} | ||
} | ||
if ( closeSelf[ tagName ] && lastTag === tagName ) { | ||
parseEndTag( '', tagName ); | ||
} | ||
unary = empty[ tagName ] || !!unary; | ||
var unary = empty[ tagName ] || tagName === 'html' && lastTag === 'head' || !!unarySlash; | ||
var attrs = []; | ||
rest.replace(attr, function () { | ||
var attrs = match.attrs.map(function(args) { | ||
var name, value, fallbackValue, customOpen, customClose, customAssign, quote; | ||
@@ -306,31 +304,19 @@ var ncp = 7; // number of captured parts, scalar | ||
// hackish work around FF bug https://bugzilla.mozilla.org/show_bug.cgi?id=369778 | ||
if (IS_REGEX_CAPTURING_BROKEN && arguments[0].indexOf('""') === -1) { | ||
if (arguments[3] === '') { arguments[3] = undefined; } | ||
if (arguments[4] === '') { arguments[4] = undefined; } | ||
if (arguments[5] === '') { arguments[5] = undefined; } | ||
if (IS_REGEX_CAPTURING_BROKEN && args[0].indexOf('""') === -1) { | ||
if (args[3] === '') { args[3] = undefined; } | ||
if (args[4] === '') { args[4] = undefined; } | ||
if (args[5] === '') { args[5] = undefined; } | ||
} | ||
name = arguments[1]; | ||
if ( name ) { | ||
customAssign = arguments[2]; | ||
fallbackValue = arguments[3]; | ||
value = fallbackValue || arguments[4] || arguments[5]; | ||
if (customAssign) { | ||
quote = arguments[0].charAt(name.length + customAssign.length); | ||
quote = (quote === '\'' || quote === '"') ? quote : ''; | ||
} | ||
} | ||
else if ( handler.customAttrSurround ) { | ||
for ( var i = handler.customAttrSurround.length - 1; i >= 0; i-- ) { | ||
name = arguments[i * ncp + 7]; | ||
customAssign = arguments[i * ncp + 8]; | ||
if ( name ) { | ||
fallbackValue = arguments[i * ncp + 9]; | ||
value = fallbackValue | ||
|| arguments[i * ncp + 10] | ||
|| arguments[i * ncp + 11]; | ||
customOpen = arguments[i * ncp + 6]; | ||
customClose = arguments[i * ncp + 12]; | ||
var j = 1; | ||
if (handler.customAttrSurround) { | ||
for (var i = 0, l = handler.customAttrSurround.length; i < l; i++, j += ncp) { | ||
name = args[j + 1]; | ||
customAssign = args[j + 2]; | ||
if (name) { | ||
fallbackValue = args[j + 3]; | ||
value = fallbackValue || args[j + 4] || args[j + 5]; | ||
quote = fallbackValue ? '"' : value ? '\'' : ''; | ||
customOpen = args[j]; | ||
customClose = args[j + 6]; | ||
break; | ||
@@ -341,7 +327,14 @@ } | ||
if ( value === undefined ) { | ||
if (!name && (name = args[j])) { | ||
customAssign = args[j + 1]; | ||
fallbackValue = args[j + 2]; | ||
value = fallbackValue || args[j + 3] || args[j + 4]; | ||
quote = fallbackValue ? '"' : value ? '\'' : ''; | ||
} | ||
if (value === undefined) { | ||
value = fillAttrs[name] ? name : fallbackValue; | ||
} | ||
attrs.push({ | ||
return { | ||
name: name, | ||
@@ -353,3 +346,3 @@ value: value, | ||
quote: quote || '' | ||
}); | ||
}; | ||
}); | ||
@@ -359,8 +352,6 @@ | ||
stack.push( { tag: tagName, attrs: attrs } ); | ||
lastTag = tagName; | ||
unarySlash = ''; | ||
} | ||
else { | ||
unarySlash = tag.match( endingSlash ); | ||
} | ||
if ( handler.start ) { | ||
@@ -392,3 +383,3 @@ handler.start( tagName, attrs, unary, unarySlash ); | ||
if ( handler.end ) { | ||
handler.end( stack[ i ].tag, stack[ i ].attrs ); | ||
handler.end( stack[ i ].tag, stack[ i ].attrs, i > pos || !tag ); | ||
} | ||
@@ -399,2 +390,3 @@ } | ||
stack.length = pos; | ||
lastTag = pos && stack[ pos - 1 ].tag; | ||
} | ||
@@ -401,0 +393,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
180051
12
3671
147
7
17
5
+ Addedncname@1.0.x
+ Addedncname@1.0.0(transitive)
+ Addedxml-char-classes@1.0.0(transitive)