From: kangax Date: Tue, 18 Mar 2014 01:37:22 +0000 (-0400) Subject: Add support for minifying CSS (minifyCSS = true) via CSSO X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=2a5ce0537f2bc4012d2cee06c77763ee3cbd913b;p=html-minifier.git Add support for minifying CSS (minifyCSS = true) via CSSO --- diff --git a/.jshintrc b/.jshintrc index 7d113c1..250c08c 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,4 +1,10 @@ { + "globals": { + "CSSOCompressor": true, + "CSSOTranslator": true, + "cleanInfo": true, + "srcToCSSP": true + }, "browser": true, "eqeqeq": true, "es3": true, diff --git a/README.md b/README.md index d34c00c..4c81dc3 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,7 @@ How does HTMLMinifier compare to [another solution](http://www.willpeavy.com/min Installing with [npm](https://github.com/isaacs/npm): ``` -npm install html-minifier -``` +npm install html-minifier``` ## Options Quick Reference @@ -48,7 +47,8 @@ npm install html-minifier | `lint` | [Toggle linting](http://perfectionkills.com/experimenting-with-html-minifier/#validate_input_through_html_lint) | `false` | | `keepClosingSlash` | Keep the trailing slash on singleton elements | `false` | | `caseSensitive` | Treat attributes in case sensitive manner (useful for SVG; e.g. viewBox) | `false` | -| `minifyJS` | Minify Javascript in script elements | `false` | +| `minifyJS` | Minify Javascript in script elements (uses [UglifyJS](https://github.com/mishoo/UglifyJS2)) | `false` | +| `minifyCSS` | Minify CSS in style elements (uses [CSSO](https://github.com/css/csso)) | `false` | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/kangax/html-minifier/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/csso.js b/csso.js new file mode 100644 index 0000000..a82b09e --- /dev/null +++ b/csso.js @@ -0,0 +1,3889 @@ +var $util = {}; + +$util.cleanInfo = function(tree) { + var r = []; + tree = tree.slice(1); + + tree.forEach(function(e) { + r.push(Array.isArray(e) ? $util.cleanInfo(e) : e); + }); + + return r; +}; + +$util.treeToString = function(tree, level) { + var spaces = $util.dummySpaces(level), + level = level ? level : 0, + s = (level ? '\n' + spaces : '') + '['; + + tree.forEach(function(e) { + s += (Array.isArray(e) ? $util.treeToString(e, level + 1) : e.f !== undefined ? $util.ircToString(e) : ('\'' + e.toString() + '\'')) + ', '; + }); + + return s.substr(0, s.length - 2) + ']'; +}; + +$util.ircToString = function(o) { + return '{' + o.f + ',' + o.l + '}'; +}; + +$util.dummySpaces = function(num) { + return ' '.substr(0, num * 2); +}; +function srcToCSSP(s, rule, _needInfo) { +var TokenType = { + StringSQ: 'StringSQ', + StringDQ: 'StringDQ', + CommentML: 'CommentML', + CommentSL: 'CommentSL', + + Newline: 'Newline', + Space: 'Space', + Tab: 'Tab', + + ExclamationMark: 'ExclamationMark', // ! + QuotationMark: 'QuotationMark', // " + NumberSign: 'NumberSign', // # + DollarSign: 'DollarSign', // $ + PercentSign: 'PercentSign', // % + Ampersand: 'Ampersand', // & + Apostrophe: 'Apostrophe', // ' + LeftParenthesis: 'LeftParenthesis', // ( + RightParenthesis: 'RightParenthesis', // ) + Asterisk: 'Asterisk', // * + PlusSign: 'PlusSign', // + + Comma: 'Comma', // , + HyphenMinus: 'HyphenMinus', // - + FullStop: 'FullStop', // . + Solidus: 'Solidus', // / + Colon: 'Colon', // : + Semicolon: 'Semicolon', // ; + LessThanSign: 'LessThanSign', // < + EqualsSign: 'EqualsSign', // = + GreaterThanSign: 'GreaterThanSign', // > + QuestionMark: 'QuestionMark', // ? + CommercialAt: 'CommercialAt', // @ + LeftSquareBracket: 'LeftSquareBracket', // [ + ReverseSolidus: 'ReverseSolidus', // \ + RightSquareBracket: 'RightSquareBracket', // ] + CircumflexAccent: 'CircumflexAccent', // ^ + LowLine: 'LowLine', // _ + LeftCurlyBracket: 'LeftCurlyBracket', // { + VerticalLine: 'VerticalLine', // | + RightCurlyBracket: 'RightCurlyBracket', // } + Tilde: 'Tilde', // ~ + + Identifier: 'Identifier', + DecimalNumber: 'DecimalNumber' +}; + +function getTokens(s) { + var Punctuation, + urlMode = false, + blockMode = 0; + + Punctuation = { + ' ': TokenType.Space, + '\n': TokenType.Newline, + '\r': TokenType.Newline, + '\t': TokenType.Tab, + '!': TokenType.ExclamationMark, + '"': TokenType.QuotationMark, + '#': TokenType.NumberSign, + '$': TokenType.DollarSign, + '%': TokenType.PercentSign, + '&': TokenType.Ampersand, + '\'': TokenType.Apostrophe, + '(': TokenType.LeftParenthesis, + ')': TokenType.RightParenthesis, + '*': TokenType.Asterisk, + '+': TokenType.PlusSign, + ',': TokenType.Comma, + '-': TokenType.HyphenMinus, + '.': TokenType.FullStop, + '/': TokenType.Solidus, + ':': TokenType.Colon, + ';': TokenType.Semicolon, + '<': TokenType.LessThanSign, + '=': TokenType.EqualsSign, + '>': TokenType.GreaterThanSign, + '?': TokenType.QuestionMark, + '@': TokenType.CommercialAt, + '[': TokenType.LeftSquareBracket, + // '\\': TokenType.ReverseSolidus, + ']': TokenType.RightSquareBracket, + '^': TokenType.CircumflexAccent, + '_': TokenType.LowLine, + '{': TokenType.LeftCurlyBracket, + '|': TokenType.VerticalLine, + '}': TokenType.RightCurlyBracket, + '~': TokenType.Tilde + }; + + function isDecimalDigit(c) { + return '0123456789'.indexOf(c) >= 0; + } + + function throwError(message) { + throw message; + } + + var buffer = '', + tokens = [], + pos, + tn = 0, + ln = 1; + + function _getTokens(s) { + if (!s) return []; + + tokens = []; + + var c, cn; + + for (pos = 0; pos < s.length; pos++) { + c = s.charAt(pos); + cn = s.charAt(pos + 1); + + if (c === '/' && cn === '*') { + parseMLComment(s); + } else if (!urlMode && c === '/' && cn === '/') { + if (blockMode > 0) parseIdentifier(s); + else parseSLComment(s); + } else if (c === '"' || c === "'") { + parseString(s, c); + } else if (c === ' ') { + parseSpaces(s) + } else if (c in Punctuation) { + pushToken(Punctuation[c], c); + if (c === '\n' || c === '\r') ln++; + if (c === ')') urlMode = false; + if (c === '{') blockMode++; + if (c === '}') blockMode--; + } else if (isDecimalDigit(c)) { + parseDecimalNumber(s); + } else { + parseIdentifier(s); + } + } + + mark(); + + return tokens; + } + + function pushToken(type, value) { + tokens.push({ tn: tn++, ln: ln, type: type, value: value }); + } + + function parseSpaces(s) { + var start = pos; + + for (; pos < s.length; pos++) { + if (s.charAt(pos) !== ' ') break; + } + + pushToken(TokenType.Space, s.substring(start, pos)); + pos--; + } + + function parseMLComment(s) { + var start = pos; + + for (pos = pos + 2; pos < s.length; pos++) { + if (s.charAt(pos) === '*') { + if (s.charAt(pos + 1) === '/') { + pos++; + break; + } + } + } + + pushToken(TokenType.CommentML, s.substring(start, pos + 1)); + } + + function parseSLComment(s) { + var start = pos; + + for (pos = pos + 2; pos < s.length; pos++) { + if (s.charAt(pos) === '\n' || s.charAt(pos) === '\r') { + pos++; + break; + } + } + + pushToken(TokenType.CommentSL, s.substring(start, pos)); + pos--; + } + + function parseString(s, q) { + var start = pos; + + for (pos = pos + 1; pos < s.length; pos++) { + if (s.charAt(pos) === '\\') pos++; + else if (s.charAt(pos) === q) break; + } + + pushToken(q === '"' ? TokenType.StringDQ : TokenType.StringSQ, s.substring(start, pos + 1)); + } + + function parseDecimalNumber(s) { + var start = pos; + + for (; pos < s.length; pos++) { + if (!isDecimalDigit(s.charAt(pos))) break; + } + + pushToken(TokenType.DecimalNumber, s.substring(start, pos)); + pos--; + } + + function parseIdentifier(s) { + var start = pos; + + while (s.charAt(pos) === '/') pos++; + + for (; pos < s.length; pos++) { + if (s.charAt(pos) === '\\') pos++; + else if (s.charAt(pos) in Punctuation) break; + } + + var ident = s.substring(start, pos); + + urlMode = urlMode || ident === 'url'; + + pushToken(TokenType.Identifier, ident); + pos--; + } + + // ==================================== + // second run + // ==================================== + + function mark() { + var ps = [], // Parenthesis + sbs = [], // SquareBracket + cbs = [], // CurlyBracket + t; + + for (var i = 0; i < tokens.length; i++) { + t = tokens[i]; + switch(t.type) { + case TokenType.LeftParenthesis: + ps.push(i); + break; + case TokenType.RightParenthesis: + if (ps.length) { + t.left = ps.pop(); + tokens[t.left].right = i; + } + break; + case TokenType.LeftSquareBracket: + sbs.push(i); + break; + case TokenType.RightSquareBracket: + if (sbs.length) { + t.left = sbs.pop(); + tokens[t.left].right = i; + } + break; + case TokenType.LeftCurlyBracket: + cbs.push(i); + break; + case TokenType.RightCurlyBracket: + if (cbs.length) { + t.left = cbs.pop(); + tokens[t.left].right = i; + } + break; + } + } + } + + return _getTokens(s); +} +// version: 1.0.0 + +function getCSSPAST(_tokens, rule, _needInfo) { + + var tokens, + pos, + failLN = 0, + currentBlockLN = 0, + needInfo = false; + + var CSSPNodeType, + CSSLevel, + CSSPRules; + + CSSPNodeType = { + IdentType: 'ident', + AtkeywordType: 'atkeyword', + StringType: 'string', + ShashType: 'shash', + VhashType: 'vhash', + NumberType: 'number', + PercentageType: 'percentage', + DimensionType: 'dimension', + CdoType: 'cdo', + CdcType: 'cdc', + DecldelimType: 'decldelim', + SType: 's', + AttrselectorType: 'attrselector', + AttribType: 'attrib', + NthType: 'nth', + NthselectorType: 'nthselector', + NamespaceType: 'namespace', + ClazzType: 'clazz', + PseudoeType: 'pseudoe', + PseudocType: 'pseudoc', + DelimType: 'delim', + StylesheetType: 'stylesheet', + AtrulebType: 'atruleb', + AtrulesType: 'atrules', + AtrulerqType: 'atrulerq', + AtrulersType: 'atrulers', + AtrulerType: 'atruler', + BlockType: 'block', + RulesetType: 'ruleset', + CombinatorType: 'combinator', + SimpleselectorType: 'simpleselector', + SelectorType: 'selector', + DeclarationType: 'declaration', + PropertyType: 'property', + ImportantType: 'important', + UnaryType: 'unary', + OperatorType: 'operator', + BracesType: 'braces', + ValueType: 'value', + ProgidType: 'progid', + FiltervType: 'filterv', + FilterType: 'filter', + CommentType: 'comment', + UriType: 'uri', + RawType: 'raw', + FunctionBodyType: 'functionBody', + FunktionType: 'funktion', + FunctionExpressionType: 'functionExpression', + UnknownType: 'unknown' + }; + + CSSPRules = { + 'ident': function() { if (checkIdent(pos)) return getIdent() }, + 'atkeyword': function() { if (checkAtkeyword(pos)) return getAtkeyword() }, + 'string': function() { if (checkString(pos)) return getString() }, + 'shash': function() { if (checkShash(pos)) return getShash() }, + 'vhash': function() { if (checkVhash(pos)) return getVhash() }, + 'number': function() { if (checkNumber(pos)) return getNumber() }, + 'percentage': function() { if (checkPercentage(pos)) return getPercentage() }, + 'dimension': function() { if (checkDimension(pos)) return getDimension() }, +// 'cdo': function() { if (checkCDO()) return getCDO() }, +// 'cdc': function() { if (checkCDC()) return getCDC() }, + 'decldelim': function() { if (checkDecldelim(pos)) return getDecldelim() }, + 's': function() { if (checkS(pos)) return getS() }, + 'attrselector': function() { if (checkAttrselector(pos)) return getAttrselector() }, + 'attrib': function() { if (checkAttrib(pos)) return getAttrib() }, + 'nth': function() { if (checkNth(pos)) return getNth() }, + 'nthselector': function() { if (checkNthselector(pos)) return getNthselector() }, + 'namespace': function() { if (checkNamespace(pos)) return getNamespace() }, + 'clazz': function() { if (checkClazz(pos)) return getClazz() }, + 'pseudoe': function() { if (checkPseudoe(pos)) return getPseudoe() }, + 'pseudoc': function() { if (checkPseudoc(pos)) return getPseudoc() }, + 'delim': function() { if (checkDelim(pos)) return getDelim() }, + 'stylesheet': function() { if (checkStylesheet(pos)) return getStylesheet() }, + 'atruleb': function() { if (checkAtruleb(pos)) return getAtruleb() }, + 'atrules': function() { if (checkAtrules(pos)) return getAtrules() }, + 'atrulerq': function() { if (checkAtrulerq(pos)) return getAtrulerq() }, + 'atrulers': function() { if (checkAtrulers(pos)) return getAtrulers() }, + 'atruler': function() { if (checkAtruler(pos)) return getAtruler() }, + 'block': function() { if (checkBlock(pos)) return getBlock() }, + 'ruleset': function() { if (checkRuleset(pos)) return getRuleset() }, + 'combinator': function() { if (checkCombinator(pos)) return getCombinator() }, + 'simpleselector': function() { if (checkSimpleselector(pos)) return getSimpleSelector() }, + 'selector': function() { if (checkSelector(pos)) return getSelector() }, + 'declaration': function() { if (checkDeclaration(pos)) return getDeclaration() }, + 'property': function() { if (checkProperty(pos)) return getProperty() }, + 'important': function() { if (checkImportant(pos)) return getImportant() }, + 'unary': function() { if (checkUnary(pos)) return getUnary() }, + 'operator': function() { if (checkOperator(pos)) return getOperator() }, + 'braces': function() { if (checkBraces(pos)) return getBraces() }, + 'value': function() { if (checkValue(pos)) return getValue() }, + 'progid': function() { if (checkProgid(pos)) return getProgid() }, + 'filterv': function() { if (checkFilterv(pos)) return getFilterv() }, + 'filter': function() { if (checkFilter(pos)) return getFilter() }, + 'comment': function() { if (checkComment(pos)) return getComment() }, + 'uri': function() { if (checkUri(pos)) return getUri() }, + 'raw': function() { if (checkRaw(pos)) return getRaw() }, + 'funktion': function() { if (checkFunktion(pos)) return getFunktion() }, + 'functionExpression': function() { if (checkFunctionExpression(pos)) return getFunctionExpression() }, + 'unknown': function() { if (checkUnknown(pos)) return getUnknown() } + }; + + function fail(token) { + if (token && token.ln > failLN) failLN = token.ln; + } + + function throwError() { + console.error('Please check the validity of the CSS block starting from the line #' + currentBlockLN); + if (process) process.exit(1); + throw new Error(); + } + + function _getAST(_tokens, rule, _needInfo) { + tokens = _tokens; + needInfo = _needInfo; + pos = 0; + + markSC(); + + return rule ? CSSPRules[rule]() : CSSPRules['stylesheet'](); + } + +//any = braces | string | percentage | dimension | number | uri | functionExpression | funktion | ident | unary + function checkAny(_i) { + return checkBraces(_i) || + checkString(_i) || + checkPercentage(_i) || + checkDimension(_i) || + checkNumber(_i) || + checkUri(_i) || + checkFunctionExpression(_i) || + checkFunktion(_i) || + checkIdent(_i) || + checkUnary(_i); + } + + function getAny() { + if (checkBraces(pos)) return getBraces(); + else if (checkString(pos)) return getString(); + else if (checkPercentage(pos)) return getPercentage(); + else if (checkDimension(pos)) return getDimension(); + else if (checkNumber(pos)) return getNumber(); + else if (checkUri(pos)) return getUri(); + else if (checkFunctionExpression(pos)) return getFunctionExpression(); + else if (checkFunktion(pos)) return getFunktion(); + else if (checkIdent(pos)) return getIdent(); + else if (checkUnary(pos)) return getUnary(); + } + +//atkeyword = '@' ident:x -> [#atkeyword, x] + function checkAtkeyword(_i) { + var l; + + if (tokens[_i++].type !== TokenType.CommercialAt) return fail(tokens[_i - 1]); + + if (l = checkIdent(_i)) return l + 1; + + return fail(tokens[_i]); + } + + function getAtkeyword() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.AtkeywordType, getIdent()]: + [CSSPNodeType.AtkeywordType, getIdent()]; + } + +//attrib = '[' sc*:s0 ident:x sc*:s1 attrselector:a sc*:s2 (ident | string):y sc*:s3 ']' -> this.concat([#attrib], s0, [x], s1, [a], s2, [y], s3) +// | '[' sc*:s0 ident:x sc*:s1 ']' -> this.concat([#attrib], s0, [x], s1), + function checkAttrib(_i) { + if (tokens[_i].type !== TokenType.LeftSquareBracket) return fail(tokens[_i]); + + if (!tokens[_i].right) return fail(tokens[_i]); + + return tokens[_i].right - _i + 1; + } + + function checkAttrib1(_i) { + var start = _i; + + _i++; + + var l = checkSC(_i); // s0 + + if (l) _i += l; + + if (l = checkIdent(_i)) _i += l; // x + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; // s1 + + if (l = checkAttrselector(_i)) _i += l; // a + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; // s2 + + if ((l = checkIdent(_i)) || (l = checkString(_i))) _i += l; // y + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; // s3 + + if (tokens[_i].type === TokenType.RightSquareBracket) return _i - start; + + return fail(tokens[_i]); + } + + function getAttrib1() { + var startPos = pos; + + pos++; + + var a = (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.AttribType] : [CSSPNodeType.AttribType]) + .concat(getSC()) + .concat([getIdent()]) + .concat(getSC()) + .concat([getAttrselector()]) + .concat(getSC()) + .concat([checkString(pos)? getString() : getIdent()]) + .concat(getSC()); + + pos++; + + return a; + } + + function checkAttrib2(_i) { + var start = _i; + + _i++; + + var l = checkSC(_i); + + if (l) _i += l; + + if (l = checkIdent(_i)) _i += l; + + if (l = checkSC(_i)) _i += l; + + if (tokens[_i].type === TokenType.RightSquareBracket) return _i - start; + + return fail(tokens[_i]); + } + + function getAttrib2() { + var startPos = pos; + + pos++; + + var a = (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.AttribType] : [CSSPNodeType.AttribType]) + .concat(getSC()) + .concat([getIdent()]) + .concat(getSC()); + + pos++; + + return a; + } + + function getAttrib() { + if (checkAttrib1(pos)) return getAttrib1(); + if (checkAttrib2(pos)) return getAttrib2(); + } + +//attrselector = (seq('=') | seq('~=') | seq('^=') | seq('$=') | seq('*=') | seq('|=')):x -> [#attrselector, x] + function checkAttrselector(_i) { + if (tokens[_i].type === TokenType.EqualsSign) return 1; + if (tokens[_i].type === TokenType.VerticalLine && (!tokens[_i + 1] || tokens[_i + 1].type !== TokenType.EqualsSign)) return 1; + + if (!tokens[_i + 1] || tokens[_i + 1].type !== TokenType.EqualsSign) return fail(tokens[_i]); + + switch(tokens[_i].type) { + case TokenType.Tilde: + case TokenType.CircumflexAccent: + case TokenType.DollarSign: + case TokenType.Asterisk: + case TokenType.VerticalLine: + return 2; + } + + return fail(tokens[_i]); + } + + function getAttrselector() { + var startPos = pos, + s = tokens[pos++].value; + + if (tokens[pos] && tokens[pos].type === TokenType.EqualsSign) s += tokens[pos++].value; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.AttrselectorType, s] : + [CSSPNodeType.AttrselectorType, s]; + } + +//atrule = atruler | atruleb | atrules + function checkAtrule(_i) { + var start = _i, + l; + + if (tokens[start].atrule_l !== undefined) return tokens[start].atrule_l; + + if (l = checkAtruler(_i)) tokens[_i].atrule_type = 1; + else if (l = checkAtruleb(_i)) tokens[_i].atrule_type = 2; + else if (l = checkAtrules(_i)) tokens[_i].atrule_type = 3; + else return fail(tokens[start]); + + tokens[start].atrule_l = l; + + return l; + } + + function getAtrule() { + switch (tokens[pos].atrule_type) { + case 1: return getAtruler(); + case 2: return getAtruleb(); + case 3: return getAtrules(); + } + } + +//atruleb = atkeyword:ak tset*:ap block:b -> this.concat([#atruleb, ak], ap, [b]) + function checkAtruleb(_i) { + var start = _i, + l; + + if (l = checkAtkeyword(_i)) _i += l; + else return fail(tokens[_i]); + + if (l = checkTsets(_i)) _i += l; + + if (l = checkBlock(_i)) _i += l; + else return fail(tokens[_i]); + + return _i - start; + } + + function getAtruleb() { + return (needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.AtrulebType, getAtkeyword()] : + [CSSPNodeType.AtrulebType, getAtkeyword()]) + .concat(getTsets()) + .concat([getBlock()]); + } + +//atruler = atkeyword:ak atrulerq:x '{' atrulers:y '}' -> [#atruler, ak, x, y] + function checkAtruler(_i) { + var start = _i, + l; + + if (l = checkAtkeyword(_i)) _i += l; + else return fail(tokens[_i]); + + if (l = checkAtrulerq(_i)) _i += l; + + if (_i < tokens.length && tokens[_i].type === TokenType.LeftCurlyBracket) _i++; + else return fail(tokens[_i]); + + if (l = checkAtrulers(_i)) _i += l; + + if (_i < tokens.length && tokens[_i].type === TokenType.RightCurlyBracket) _i++; + else return fail(tokens[_i]); + + return _i - start; + } + + function getAtruler() { + var atruler = needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.AtrulerType, getAtkeyword(), getAtrulerq()] : + [CSSPNodeType.AtrulerType, getAtkeyword(), getAtrulerq()]; + + pos++; + + atruler.push(getAtrulers()); + + pos++; + + return atruler; + } + +//atrulerq = tset*:ap -> [#atrulerq].concat(ap) + function checkAtrulerq(_i) { + return checkTsets(_i); + } + + function getAtrulerq() { + return (needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.AtrulerqType] : [CSSPNodeType.AtrulerqType]).concat(getTsets()); + } + +//atrulers = sc*:s0 ruleset*:r sc*:s1 -> this.concat([#atrulers], s0, r, s1) + function checkAtrulers(_i) { + var start = _i, + l; + + if (l = checkSC(_i)) _i += l; + + while ((l = checkRuleset(_i)) || (l = checkAtrule(_i)) || (l = checkSC(_i))) { + _i += l; + } + + tokens[_i].atrulers_end = 1; + + if (l = checkSC(_i)) _i += l; + + return _i - start; + } + + function getAtrulers() { + var atrulers = (needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.AtrulersType] : [CSSPNodeType.AtrulersType]).concat(getSC()), + x; + + while (!tokens[pos].atrulers_end) { + if (checkSC(pos)) { + atrulers = atrulers.concat(getSC()); + } else if (checkRuleset(pos)) { + atrulers.push(getRuleset()); + } else { + atrulers.push(getAtrule()); + } + } + + return atrulers.concat(getSC()); + } + +//atrules = atkeyword:ak tset*:ap ';' -> this.concat([#atrules, ak], ap) + function checkAtrules(_i) { + var start = _i, + l; + + if (l = checkAtkeyword(_i)) _i += l; + else return fail(tokens[_i]); + + if (l = checkTsets(_i)) _i += l; + + if (_i >= tokens.length) return _i - start; + + if (tokens[_i].type === TokenType.Semicolon) _i++; + else return fail(tokens[_i]); + + return _i - start; + } + + function getAtrules() { + var atrules = (needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.AtrulesType, getAtkeyword()] : [CSSPNodeType.AtrulesType, getAtkeyword()]).concat(getTsets()); + + pos++; + + return atrules; + } + +//block = '{' blockdecl*:x '}' -> this.concatContent([#block], x) + function checkBlock(_i) { + if (_i < tokens.length && tokens[_i].type === TokenType.LeftCurlyBracket) return tokens[_i].right - _i + 1; + + return fail(tokens[_i]); + } + + function getBlock() { + var block = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.BlockType] : [CSSPNodeType.BlockType], + end = tokens[pos].right; + + pos++; + + while (pos < end) { + if (checkBlockdecl(pos)) block = block.concat(getBlockdecl()); + else throwError(); + } + + pos = end + 1; + + return block; + } + +//blockdecl = sc*:s0 (filter | declaration):x decldelim:y sc*:s1 -> this.concat(s0, [x], [y], s1) +// | sc*:s0 (filter | declaration):x sc*:s1 -> this.concat(s0, [x], s1) +// | sc*:s0 decldelim:x sc*:s1 -> this.concat(s0, [x], s1) +// | sc+:s0 -> s0 + + function checkBlockdecl(_i) { + var l; + + if (l = _checkBlockdecl0(_i)) tokens[_i].bd_type = 1; + else if (l = _checkBlockdecl1(_i)) tokens[_i].bd_type = 2; + else if (l = _checkBlockdecl2(_i)) tokens[_i].bd_type = 3; + else if (l = _checkBlockdecl3(_i)) tokens[_i].bd_type = 4; + else return fail(tokens[_i]); + + return l; + } + + function getBlockdecl() { + switch (tokens[pos].bd_type) { + case 1: return _getBlockdecl0(); + case 2: return _getBlockdecl1(); + case 3: return _getBlockdecl2(); + case 4: return _getBlockdecl3(); + } + } + + //sc*:s0 (filter | declaration):x decldelim:y sc*:s1 -> this.concat(s0, [x], [y], s1) + function _checkBlockdecl0(_i) { + var start = _i, + l; + + if (l = checkSC(_i)) _i += l; + + if (l = checkFilter(_i)) { + tokens[_i].bd_filter = 1; + _i += l; + } else if (l = checkDeclaration(_i)) { + tokens[_i].bd_decl = 1; + _i += l; + } else return fail(tokens[_i]); + + if (_i < tokens.length && (l = checkDecldelim(_i))) _i += l; + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; + + return _i - start; + } + + function _getBlockdecl0() { + return getSC() + .concat([tokens[pos].bd_filter? getFilter() : getDeclaration()]) + .concat([getDecldelim()]) + .concat(getSC()); + } + + //sc*:s0 (filter | declaration):x sc*:s1 -> this.concat(s0, [x], s1) + function _checkBlockdecl1(_i) { + var start = _i, + l; + + if (l = checkSC(_i)) _i += l; + + if (l = checkFilter(_i)) { + tokens[_i].bd_filter = 1; + _i += l; + } else if (l = checkDeclaration(_i)) { + tokens[_i].bd_decl = 1; + _i += l; + } else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; + + return _i - start; + } + + function _getBlockdecl1() { + return getSC() + .concat([tokens[pos].bd_filter? getFilter() : getDeclaration()]) + .concat(getSC()); + } + + //sc*:s0 decldelim:x sc*:s1 -> this.concat(s0, [x], s1) + function _checkBlockdecl2(_i) { + var start = _i, + l; + + if (l = checkSC(_i)) _i += l; + + if (l = checkDecldelim(_i)) _i += l; + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; + + return _i - start; + } + + function _getBlockdecl2() { + return getSC() + .concat([getDecldelim()]) + .concat(getSC()); + } + + //sc+:s0 -> s0 + function _checkBlockdecl3(_i) { + return checkSC(_i); + } + + function _getBlockdecl3() { + return getSC(); + } + +//braces = '(' tset*:x ')' -> this.concat([#braces, '(', ')'], x) +// | '[' tset*:x ']' -> this.concat([#braces, '[', ']'], x) + function checkBraces(_i) { + if (_i >= tokens.length || + (tokens[_i].type !== TokenType.LeftParenthesis && + tokens[_i].type !== TokenType.LeftSquareBracket) + ) return fail(tokens[_i]); + + return tokens[_i].right - _i + 1; + } + + function getBraces() { + var startPos = pos, + left = pos, + right = tokens[pos].right; + + pos++; + + var tsets = getTsets(); + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.BracesType, tokens[left].value, tokens[right].value].concat(tsets) : + [CSSPNodeType.BracesType, tokens[left].value, tokens[right].value].concat(tsets); + } + + function checkCDC(_i) {} + + function checkCDO(_i) {} + + // node: Clazz + function checkClazz(_i) { + var l; + + if (tokens[_i].clazz_l) return tokens[_i].clazz_l; + + if (tokens[_i].type === TokenType.FullStop) { + if (l = checkIdent(_i + 1)) { + tokens[_i].clazz_l = l + 1; + return l + 1; + } + } + + return fail(tokens[_i]); + } + + function getClazz() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.ClazzType, getIdent()] : + [CSSPNodeType.ClazzType, getIdent()]; + } + + // node: Combinator + function checkCombinator(_i) { + if (tokens[_i].type === TokenType.PlusSign || + tokens[_i].type === TokenType.GreaterThanSign || + tokens[_i].type === TokenType.Tilde) return 1; + + return fail(tokens[_i]); + } + + function getCombinator() { + return needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.CombinatorType, tokens[pos++].value] : + [CSSPNodeType.CombinatorType, tokens[pos++].value]; + } + + // node: Comment + function checkComment(_i) { + if (tokens[_i].type === TokenType.CommentML) return 1; + + return fail(tokens[_i]); + } + + function getComment() { + var startPos = pos, + s = tokens[pos].value.substring(2), + l = s.length; + + if (s.charAt(l - 2) === '*' && s.charAt(l - 1) === '/') s = s.substring(0, l - 2); + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.CommentType, s] : + [CSSPNodeType.CommentType, s]; + } + + // declaration = property:x ':' value:y -> [#declaration, x, y] + function checkDeclaration(_i) { + var start = _i, + l; + + if (l = checkProperty(_i)) _i += l; + else return fail(tokens[_i]); + + if (_i < tokens.length && tokens[_i].type === TokenType.Colon) _i++; + else return fail(tokens[_i]); + + if (l = checkValue(_i)) _i += l; + else return fail(tokens[_i]); + + return _i - start; + } + + function getDeclaration() { + var declaration = needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.DeclarationType, getProperty()] : + [CSSPNodeType.DeclarationType, getProperty()]; + + pos++; + + declaration.push(getValue()); + + return declaration; + } + + // node: Decldelim + function checkDecldelim(_i) { + if (_i < tokens.length && tokens[_i].type === TokenType.Semicolon) return 1; + + return fail(tokens[_i]); + } + + function getDecldelim() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.DecldelimType] : + [CSSPNodeType.DecldelimType]; + } + + // node: Delim + function checkDelim(_i) { + if (_i < tokens.length && tokens[_i].type === TokenType.Comma) return 1; + + if (_i >= tokens.length) return fail(tokens[tokens.length - 1]); + + return fail(tokens[_i]); + } + + function getDelim() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.DelimType] : + [CSSPNodeType.DelimType]; + } + + // node: Dimension + function checkDimension(_i) { + var ln = checkNumber(_i), + li; + + if (!ln || (ln && _i + ln >= tokens.length)) return fail(tokens[_i]); + + if (li = checkNmName2(_i + ln)) return ln + li; + + return fail(tokens[_i]); + } + + function getDimension() { + var startPos = pos, + n = getNumber(), + dimension = needInfo ? + [{ ln: tokens[pos].ln }, CSSPNodeType.IdentType, getNmName2()] : + [CSSPNodeType.IdentType, getNmName2()]; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.DimensionType, n, dimension] : + [CSSPNodeType.DimensionType, n, dimension]; + } + +//filter = filterp:x ':' filterv:y -> [#filter, x, y] + function checkFilter(_i) { + var start = _i, + l; + + if (l = checkFilterp(_i)) _i += l; + else return fail(tokens[_i]); + + if (tokens[_i].type === TokenType.Colon) _i++; + else return fail(tokens[_i]); + + if (l = checkFilterv(_i)) _i += l; + else return fail(tokens[_i]); + + return _i - start; + } + + function getFilter() { + var filter = needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.FilterType, getFilterp()] : + [CSSPNodeType.FilterType, getFilterp()]; + + pos++; + + filter.push(getFilterv()); + + return filter; + } + +//filterp = (seq('-filter') | seq('_filter') | seq('*filter') | seq('-ms-filter') | seq('filter')):t sc*:s0 -> this.concat([#property, [#ident, t]], s0) + function checkFilterp(_i) { + var start = _i, + l, + x; + + if (_i < tokens.length) { + if (tokens[_i].value === 'filter') l = 1; + else { + x = joinValues2(_i, 2); + + if (x === '-filter' || x === '_filter' || x === '*filter') l = 2; + else { + x = joinValues2(_i, 4); + + if (x === '-ms-filter') l = 4; + else return fail(tokens[_i]); + } + } + + tokens[start].filterp_l = l; + + _i += l; + + if (checkSC(_i)) _i += l; + + return _i - start; + } + + return fail(tokens[_i]); + } + + function getFilterp() { + var startPos = pos, + x = joinValues2(pos, tokens[pos].filterp_l), + ident = needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.IdentType, x] : [CSSPNodeType.IdentType, x]; + + pos += tokens[pos].filterp_l; + + return (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.PropertyType, ident] : [CSSPNodeType.PropertyType, ident]) + .concat(getSC()); + + } + +//filterv = progid+:x -> [#filterv].concat(x) + function checkFilterv(_i) { + var start = _i, + l; + + if (l = checkProgid(_i)) _i += l; + else return fail(tokens[_i]); + + while (l = checkProgid(_i)) { + _i += l; + } + + tokens[start].last_progid = _i; + + if (_i < tokens.length && (l = checkSC(_i))) _i += l; + + if (_i < tokens.length && (l = checkImportant(_i))) _i += l; + + return _i - start; + } + + function getFilterv() { + var filterv = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.FiltervType] : [CSSPNodeType.FiltervType], + last_progid = tokens[pos].last_progid; + + while (pos < last_progid) { + filterv.push(getProgid()); + } + + filterv = filterv.concat(checkSC(pos) ? getSC() : []); + + if (pos < tokens.length && checkImportant(pos)) filterv.push(getImportant()); + + return filterv; + } + +//functionExpression = ``expression('' functionExpressionBody*:x ')' -> [#functionExpression, x.join('')], + function checkFunctionExpression(_i) { + var start = _i; + + if (!tokens[_i] || tokens[_i++].value !== 'expression') return fail(tokens[_i - 1]); + + if (!tokens[_i] || tokens[_i].type !== TokenType.LeftParenthesis) return fail(tokens[_i]); + + return tokens[_i].right - start + 1; + } + + function getFunctionExpression() { + var startPos = pos; + + pos++; + + var e = joinValues(pos + 1, tokens[pos].right - 1); + + pos = tokens[pos].right + 1; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.FunctionExpressionType, e] : + [CSSPNodeType.FunctionExpressionType, e]; + } + +//funktion = ident:x '(' functionBody:y ')' -> [#funktion, x, y] + function checkFunktion(_i) { + var start = _i, + l = checkIdent(_i); + + if (!l) return fail(tokens[_i]); + + _i += l; + + if (_i >= tokens.length || tokens[_i].type !== TokenType.LeftParenthesis) return fail(tokens[_i - 1]); + + return tokens[_i].right - start + 1; + } + + function getFunktion() { + var startPos = pos, + ident = getIdent(); + + pos++; + + var body = ident[needInfo? 2 : 1] !== 'not'? + getFunctionBody() : + getNotFunctionBody(); // ok, here we have CSS3 initial draft: http://dev.w3.org/csswg/selectors3/#negation + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.FunktionType, ident, body] : + [CSSPNodeType.FunktionType, ident, body]; + } + + function getFunctionBody() { + var startPos = pos, + body = [], + x; + + while (tokens[pos].type !== TokenType.RightParenthesis) { + if (checkTset(pos)) { + x = getTset(); + if ((needInfo && typeof x[1] === 'string') || typeof x[0] === 'string') body.push(x); + else body = body.concat(x); + } else if (checkClazz(pos)) { + body.push(getClazz()); + } else { + throwError(); + } + } + + pos++; + + return (needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.FunctionBodyType] : + [CSSPNodeType.FunctionBodyType] + ).concat(body); + } + + function getNotFunctionBody() { + var startPos = pos, + body = [], + x; + + while (tokens[pos].type !== TokenType.RightParenthesis) { + if (checkSimpleselector(pos)) { + body.push(getSimpleSelector()); + } else { + throwError(); + } + } + + pos++; + + return (needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.FunctionBodyType] : + [CSSPNodeType.FunctionBodyType] + ).concat(body); + } + + // node: Ident + function checkIdent(_i) { + var start = _i, + wasIdent = false; + + // start char / word + if (_i < tokens.length && + (tokens[_i].type === TokenType.HyphenMinus || + tokens[_i].type === TokenType.LowLine || + tokens[_i].type === TokenType.Identifier || + tokens[_i].type === TokenType.DollarSign || + tokens[_i].type === TokenType.Asterisk)) _i++; + else return fail(tokens[_i]); + + wasIdent = tokens[_i - 1].type === TokenType.Identifier; + + for (; _i < tokens.length; _i++) { + if (tokens[_i].type !== TokenType.HyphenMinus && + tokens[_i].type !== TokenType.LowLine) { + if (tokens[_i].type !== TokenType.Identifier && + (tokens[_i].type !== TokenType.DecimalNumber || !wasIdent) + ) break; + else wasIdent = true; + } + } + + if (!wasIdent && tokens[start].type !== TokenType.Asterisk) return fail(tokens[_i]); + + tokens[start].ident_last = _i - 1; + + return _i - start; + } + + function getIdent() { + var startPos = pos, + s = joinValues(pos, tokens[pos].ident_last); + + pos = tokens[pos].ident_last + 1; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.IdentType, s] : + [CSSPNodeType.IdentType, s]; + } + +//important = '!' sc*:s0 seq('important') -> [#important].concat(s0) + function checkImportant(_i) { + var start = _i, + l; + + if (tokens[_i++].type !== TokenType.ExclamationMark) return fail(tokens[_i - 1]); + + if (l = checkSC(_i)) _i += l; + + if (tokens[_i].value !== 'important') return fail(tokens[_i]); + + return _i - start + 1; + } + + function getImportant() { + var startPos = pos; + + pos++; + + var sc = getSC(); + + pos++; + + return (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.ImportantType] : [CSSPNodeType.ImportantType]).concat(sc); + } + + // node: Namespace + function checkNamespace(_i) { + if (tokens[_i].type === TokenType.VerticalLine) return 1; + + return fail(tokens[_i]); + } + + function getNamespace() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.NamespaceType] : + [CSSPNodeType.NamespaceType]; + } + +//nth = (digit | 'n')+:x -> [#nth, x.join('')] +// | (seq('even') | seq('odd')):x -> [#nth, x] + function checkNth(_i) { + return checkNth1(_i) || checkNth2(_i); + } + + function checkNth1(_i) { + var start = _i; + + for (; _i < tokens.length; _i++) { + if (tokens[_i].type !== TokenType.DecimalNumber && tokens[_i].value !== 'n') break; + } + + if (_i !== start) { + tokens[start].nth_last = _i - 1; + return _i - start; + } + + return fail(tokens[_i]); + } + + function getNth() { + var startPos = pos; + + if (tokens[pos].nth_last) { + var n = needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.NthType, joinValues(pos, tokens[pos].nth_last)] : + [CSSPNodeType.NthType, joinValues(pos, tokens[pos].nth_last)]; + + pos = tokens[pos].nth_last + 1; + + return n; + } + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.NthType, tokens[pos++].value] : + [CSSPNodeType.NthType, tokens[pos++].value]; + } + + function checkNth2(_i) { + if (tokens[_i].value === 'even' || tokens[_i].value === 'odd') return 1; + + return fail(tokens[_i]); + } + +//nthf = ':' seq('nth-'):x (seq('child') | seq('last-child') | seq('of-type') | seq('last-of-type')):y -> (x + y) + function checkNthf(_i) { + var start = _i, + l = 0; + + if (tokens[_i++].type !== TokenType.Colon) return fail(tokens[_i - 1]); l++; + + if (tokens[_i++].value !== 'nth' || tokens[_i++].value !== '-') return fail(tokens[_i - 1]); l += 2; + + if ('child' === tokens[_i].value) { + l += 1; + } else if ('last-child' === tokens[_i].value + + tokens[_i + 1].value + + tokens[_i + 2].value) { + l += 3; + } else if ('of-type' === tokens[_i].value + + tokens[_i + 1].value + + tokens[_i + 2].value) { + l += 3; + } else if ('last-of-type' === tokens[_i].value + + tokens[_i + 1].value + + tokens[_i + 2].value + + tokens[_i + 3].value + + tokens[_i + 4].value) { + l += 5; + } else return fail(tokens[_i]); + + tokens[start + 1].nthf_last = start + l - 1; + + return l; + } + + function getNthf() { + pos++; + + var s = joinValues(pos, tokens[pos].nthf_last); + + pos = tokens[pos].nthf_last + 1; + + return s; + } + +//nthselector = nthf:x '(' (sc | unary | nth)*:y ')' -> [#nthselector, [#ident, x]].concat(y) + function checkNthselector(_i) { + var start = _i, + l; + + if (l = checkNthf(_i)) _i += l; + else return fail(tokens[_i]); + + if (tokens[_i].type !== TokenType.LeftParenthesis || !tokens[_i].right) return fail(tokens[_i]); + + l++; + + var rp = tokens[_i++].right; + + while (_i < rp) { + if (l = checkSC(_i)) _i += l; + else if (l = checkUnary(_i)) _i += l; + else if (l = checkNth(_i)) _i += l; + else return fail(tokens[_i]); + } + + return rp - start + 1; + } + + function getNthselector() { + var startPos = pos, + nthf = needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.IdentType, getNthf()] : + [CSSPNodeType.IdentType, getNthf()], + ns = needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.NthselectorType, nthf] : + [CSSPNodeType.NthselectorType, nthf]; + + pos++; + + while (tokens[pos].type !== TokenType.RightParenthesis) { + if (checkSC(pos)) ns = ns.concat(getSC()); + else if (checkUnary(pos)) ns.push(getUnary()); + else if (checkNth(pos)) ns.push(getNth()); + } + + pos++; + + return ns; + } + + // node: Number + function checkNumber(_i) { + if (_i < tokens.length && tokens[_i].number_l) return tokens[_i].number_l; + + if (_i < tokens.length && tokens[_i].type === TokenType.DecimalNumber && + (!tokens[_i + 1] || + (tokens[_i + 1] && tokens[_i + 1].type !== TokenType.FullStop)) + ) return (tokens[_i].number_l = 1, tokens[_i].number_l); // 10 + + if (_i < tokens.length && + tokens[_i].type === TokenType.DecimalNumber && + tokens[_i + 1] && tokens[_i + 1].type === TokenType.FullStop && + (!tokens[_i + 2] || (tokens[_i + 2].type !== TokenType.DecimalNumber)) + ) return (tokens[_i].number_l = 2, tokens[_i].number_l); // 10. + + if (_i < tokens.length && + tokens[_i].type === TokenType.FullStop && + tokens[_i + 1].type === TokenType.DecimalNumber + ) return (tokens[_i].number_l = 2, tokens[_i].number_l); // .10 + + if (_i < tokens.length && + tokens[_i].type === TokenType.DecimalNumber && + tokens[_i + 1] && tokens[_i + 1].type === TokenType.FullStop && + tokens[_i + 2] && tokens[_i + 2].type === TokenType.DecimalNumber + ) return (tokens[_i].number_l = 3, tokens[_i].number_l); // 10.10 + + return fail(tokens[_i]); + } + + function getNumber() { + var s = '', + startPos = pos, + l = tokens[pos].number_l; + + for (var i = 0; i < l; i++) { + s += tokens[pos + i].value; + } + + pos += l; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.NumberType, s] : + [CSSPNodeType.NumberType, s]; + } + + // node: Operator + function checkOperator(_i) { + if (_i < tokens.length && + (tokens[_i].type === TokenType.Solidus || + tokens[_i].type === TokenType.Comma || + tokens[_i].type === TokenType.Colon || + tokens[_i].type === TokenType.EqualsSign)) return 1; + + return fail(tokens[_i]); + } + + function getOperator() { + return needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.OperatorType, tokens[pos++].value] : + [CSSPNodeType.OperatorType, tokens[pos++].value]; + } + + // node: Percentage + function checkPercentage(_i) { + var x = checkNumber(_i); + + if (!x || (x && _i + x >= tokens.length)) return fail(tokens[_i]); + + if (tokens[_i + x].type === TokenType.PercentSign) return x + 1; + + return fail(tokens[_i]); + } + + function getPercentage() { + var startPos = pos, + n = getNumber(); + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.PercentageType, n] : + [CSSPNodeType.PercentageType, n]; + } + +//progid = sc*:s0 seq('progid:DXImageTransform.Microsoft.'):x letter+:y '(' (m_string | m_comment | ~')' char)+:z ')' sc*:s1 +// -> this.concat([#progid], s0, [[#raw, x + y.join('') + '(' + z.join('') + ')']], s1), + function checkProgid(_i) { + var start = _i, + l, + x; + + if (l = checkSC(_i)) _i += l; + + if ((x = joinValues2(_i, 6)) === 'progid:DXImageTransform.Microsoft.') { + _start = _i; + _i += 6; + } else return fail(tokens[_i - 1]); + + if (l = checkIdent(_i)) _i += l; + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; + + if (tokens[_i].type === TokenType.LeftParenthesis) { + tokens[start].progid_end = tokens[_i].right; + _i = tokens[_i].right + 1; + } else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; + + return _i - start; + } + + function getProgid() { + var startPos = pos, + progid_end = tokens[pos].progid_end; + + return (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.ProgidType] : [CSSPNodeType.ProgidType]) + .concat(getSC()) + .concat([_getProgid(progid_end)]) + .concat(getSC()); + } + + function _getProgid(progid_end) { + var startPos = pos, + x = joinValues(pos, progid_end); + + pos = progid_end + 1; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.RawType, x] : + [CSSPNodeType.RawType, x]; + } + +//property = ident:x sc*:s0 -> this.concat([#property, x], s0) + function checkProperty(_i) { + var start = _i, + l; + + if (l = checkIdent(_i)) _i += l; + else return fail(tokens[_i]); + + if (l = checkSC(_i)) _i += l; + return _i - start; + } + + function getProperty() { + var startPos = pos; + + return (needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.PropertyType, getIdent()] : + [CSSPNodeType.PropertyType, getIdent()]) + .concat(getSC()); + } + + function checkPseudo(_i) { + return checkPseudoe(_i) || + checkPseudoc(_i); + } + + function getPseudo() { + if (checkPseudoe(pos)) return getPseudoe(); + if (checkPseudoc(pos)) return getPseudoc(); + } + + function checkPseudoe(_i) { + var l; + + if (tokens[_i++].type !== TokenType.Colon) return fail(tokens[_i - 1]); + + if (tokens[_i++].type !== TokenType.Colon) return fail(tokens[_i - 1]); + + if (l = checkIdent(_i)) return l + 2; + + return fail(tokens[_i]); + } + + function getPseudoe() { + var startPos = pos; + + pos += 2; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.PseudoeType, getIdent()] : + [CSSPNodeType.PseudoeType, getIdent()]; + } + +//pseudoc = ':' (funktion | ident):x -> [#pseudoc, x] + function checkPseudoc(_i) { + var l; + + if (tokens[_i++].type !== TokenType.Colon) return fail(tokens[_i - 1]); + + if ((l = checkFunktion(_i)) || (l = checkIdent(_i))) return l + 1; + + return fail(tokens[_i]); + } + + function getPseudoc() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.PseudocType, checkFunktion(pos)? getFunktion() : getIdent()] : + [CSSPNodeType.PseudocType, checkFunktion(pos)? getFunktion() : getIdent()]; + } + + //ruleset = selector*:x block:y -> this.concat([#ruleset], x, [y]) + function checkRuleset(_i) { + var start = _i, + l; + + if (tokens[start].ruleset_l !== undefined) return tokens[start].ruleset_l; + + while (l = checkSelector(_i)) { + _i += l; + } + + if (l = checkBlock(_i)) _i += l; + else return fail(tokens[_i]); + + tokens[start].ruleset_l = _i - start; + + return _i - start; + } + + function getRuleset() { + var ruleset = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.RulesetType] : [CSSPNodeType.RulesetType]; + + while (!checkBlock(pos)) { + ruleset.push(getSelector()); + } + + ruleset.push(getBlock()); + + return ruleset; + } + + // node: S + function checkS(_i) { + if (tokens[_i].ws) return tokens[_i].ws_last - _i + 1; + + return fail(tokens[_i]); + } + + function getS() { + var startPos = pos, + s = joinValues(pos, tokens[pos].ws_last); + + pos = tokens[pos].ws_last + 1; + + return needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.SType, s] : [CSSPNodeType.SType, s]; + } + + function checkSC(_i) { + var l, + lsc = 0; + + while (_i < tokens.length) { + if (!(l = checkS(_i)) && !(l = checkComment(_i))) break; + _i += l; + lsc += l; + } + + if (lsc) return lsc; + + if (_i >= tokens.length) return fail(tokens[tokens.length - 1]); + + return fail(tokens[_i]); + } + + function getSC() { + var sc = []; + + while (pos < tokens.length) { + if (checkS(pos)) sc.push(getS()); + else if (checkComment(pos)) sc.push(getComment()); + else break; + } + + return sc; + } + + //selector = (simpleselector | delim)+:x -> this.concat([#selector], x) + function checkSelector(_i) { + var start = _i, + l; + + if (_i < tokens.length) { + while (l = checkSimpleselector(_i) || checkDelim(_i)) { + _i += l; + } + + tokens[start].selector_end = _i - 1; + + return _i - start; + } + } + + function getSelector() { + var selector = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.SelectorType] : [CSSPNodeType.SelectorType], + selector_end = tokens[pos].selector_end; + + while (pos <= selector_end) { + selector.push(checkDelim(pos) ? getDelim() : getSimpleSelector()); + } + + return selector; + } + + // node: Shash + function checkShash(_i) { + if (tokens[_i].type !== TokenType.NumberSign) return fail(tokens[_i]); + + var l = checkNmName(_i + 1); + + if (l) return l + 1; + + return fail(tokens[_i]); + } + + function getShash() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.ShashType, getNmName()] : + [CSSPNodeType.ShashType, getNmName()]; + } + +//simpleselector = (nthselector | combinator | attrib | pseudo | clazz | shash | any | sc | namespace)+:x -> this.concatContent([#simpleselector], [x]) + function checkSimpleselector(_i) { + var start = _i, + l; + + while (_i < tokens.length) { + if (l = _checkSimpleSelector(_i)) _i += l; + else break; + } + + if (_i - start) return _i - start; + + if (_i >= tokens.length) return fail(tokens[tokens.length - 1]); + + return fail(tokens[_i]); + } + + function _checkSimpleSelector(_i) { + return checkNthselector(_i) || + checkCombinator(_i) || + checkAttrib(_i) || + checkPseudo(_i) || + checkClazz(_i) || + checkShash(_i) || + checkAny(_i) || + checkSC(_i) || + checkNamespace(_i); + } + + function getSimpleSelector() { + var ss = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.SimpleselectorType] : [CSSPNodeType.SimpleselectorType], + t; + + while (pos < tokens.length && _checkSimpleSelector(pos)) { + t = _getSimpleSelector(); + + if ((needInfo && typeof t[1] === 'string') || typeof t[0] === 'string') ss.push(t); + else ss = ss.concat(t); + } + + return ss; + } + + function _getSimpleSelector() { + if (checkNthselector(pos)) return getNthselector(); + else if (checkCombinator(pos)) return getCombinator(); + else if (checkAttrib(pos)) return getAttrib(); + else if (checkPseudo(pos)) return getPseudo(); + else if (checkClazz(pos)) return getClazz(); + else if (checkShash(pos)) return getShash(); + else if (checkAny(pos)) return getAny(); + else if (checkSC(pos)) return getSC(); + else if (checkNamespace(pos)) return getNamespace(); + } + + // node: String + function checkString(_i) { + if (_i < tokens.length && + (tokens[_i].type === TokenType.StringSQ || tokens[_i].type === TokenType.StringDQ) + ) return 1; + + return fail(tokens[_i]); + } + + function getString() { + var startPos = pos; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.StringType, tokens[pos++].value] : + [CSSPNodeType.StringType, tokens[pos++].value]; + } + + //stylesheet = (cdo | cdc | sc | statement)*:x -> this.concat([#stylesheet], x) + function checkStylesheet(_i) { + var start = _i, + l; + + while (_i < tokens.length) { + if (l = checkSC(_i)) _i += l; + else { + currentBlockLN = tokens[_i].ln; + if (l = checkAtrule(_i)) _i += l; + else if (l = checkRuleset(_i)) _i += l; + else if (l = checkUnknown(_i)) _i += l; + else throwError(); + } + } + + return _i - start; + } + + function getStylesheet(_i) { + var t, + stylesheet = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.StylesheetType] : [CSSPNodeType.StylesheetType]; + + while (pos < tokens.length) { + if (checkSC(pos)) stylesheet = stylesheet.concat(getSC()); + else { + currentBlockLN = tokens[pos].ln; + if (checkRuleset(pos)) stylesheet.push(getRuleset()); + else if (checkAtrule(pos)) stylesheet.push(getAtrule()); + else if (checkUnknown(pos)) stylesheet.push(getUnknown()); + else throwError(); + } + } + + return stylesheet; + } + +//tset = vhash | any | sc | operator + function checkTset(_i) { + return checkVhash(_i) || + checkAny(_i) || + checkSC(_i) || + checkOperator(_i); + } + + function getTset() { + if (checkVhash(pos)) return getVhash(); + else if (checkAny(pos)) return getAny(); + else if (checkSC(pos)) return getSC(); + else if (checkOperator(pos)) return getOperator(); + } + + function checkTsets(_i) { + var start = _i, + l; + + while (l = checkTset(_i)) { + _i += l; + } + + return _i - start; + } + + function getTsets() { + var tsets = [], + x; + + while (x = getTset()) { + if ((needInfo && typeof x[1] === 'string') || typeof x[0] === 'string') tsets.push(x); + else tsets = tsets.concat(x); + } + + return tsets; + } + + // node: Unary + function checkUnary(_i) { + if (_i < tokens.length && + (tokens[_i].type === TokenType.HyphenMinus || + tokens[_i].type === TokenType.PlusSign) + ) return 1; + + return fail(tokens[_i]); + } + + function getUnary() { + var startPos = pos; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.UnaryType, tokens[pos++].value] : + [CSSPNodeType.UnaryType, tokens[pos++].value]; + } + + // node: Unknown + function checkUnknown(_i) { + if (_i < tokens.length && tokens[_i].type === TokenType.CommentSL) return 1; + + return fail(tokens[_i]); + } + + function getUnknown() { + var startPos = pos; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.UnknownType, tokens[pos++].value] : + [CSSPNodeType.UnknownType, tokens[pos++].value]; + } + +// uri = seq('url(') sc*:s0 string:x sc*:s1 ')' -> this.concat([#uri], s0, [x], s1) +// | seq('url(') sc*:s0 (~')' ~m_w char)*:x sc*:s1 ')' -> this.concat([#uri], s0, [[#raw, x.join('')]], s1), + function checkUri(_i) { + var start = _i, + l; + + if (_i < tokens.length && tokens[_i++].value !== 'url') return fail(tokens[_i - 1]); + + if (!tokens[_i] || tokens[_i].type !== TokenType.LeftParenthesis) return fail(tokens[_i]); + + return tokens[_i].right - start + 1; + } + + function getUri() { + var startPos = pos, + uriExcluding = {}; + + pos += 2; + + uriExcluding[TokenType.Space] = 1; + uriExcluding[TokenType.Tab] = 1; + uriExcluding[TokenType.Newline] = 1; + uriExcluding[TokenType.LeftParenthesis] = 1; + uriExcluding[TokenType.RightParenthesis] = 1; + + if (checkUri1(pos)) { + var uri = (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.UriType] : [CSSPNodeType.UriType]) + .concat(getSC()) + .concat([getString()]) + .concat(getSC()); + + pos++; + + return uri; + } else { + var uri = (needInfo? [{ ln: tokens[startPos].ln }, CSSPNodeType.UriType] : [CSSPNodeType.UriType]) + .concat(getSC()), + l = checkExcluding(uriExcluding, pos), + raw = needInfo? + [{ ln: tokens[pos].ln }, CSSPNodeType.RawType, joinValues(pos, pos + l)] : + [CSSPNodeType.RawType, joinValues(pos, pos + l)]; + + uri.push(raw); + + pos += l + 1; + + uri = uri.concat(getSC()); + + pos++; + + return uri; + } + } + + function checkUri1(_i) { + var start = _i, + l = checkSC(_i); + + if (l) _i += l; + + if (tokens[_i].type !== TokenType.StringDQ && tokens[_i].type !== TokenType.StringSQ) return fail(tokens[_i]); + + _i++; + + if (l = checkSC(_i)) _i += l; + + return _i - start; + } + + // value = (sc | vhash | any | block | atkeyword | operator | important)+:x -> this.concat([#value], x) + function checkValue(_i) { + var start = _i, + l; + + while (_i < tokens.length) { + if (l = _checkValue(_i)) _i += l; + else break; + } + + if (_i - start) return _i - start; + + return fail(tokens[_i]); + } + + function _checkValue(_i) { + return checkSC(_i) || + checkVhash(_i) || + checkAny(_i) || + checkBlock(_i) || + checkAtkeyword(_i) || + checkOperator(_i) || + checkImportant(_i); + } + + function getValue() { + var ss = needInfo? [{ ln: tokens[pos].ln }, CSSPNodeType.ValueType] : [CSSPNodeType.ValueType], + t; + + while (pos < tokens.length && _checkValue(pos)) { + t = _getValue(); + + if ((needInfo && typeof t[1] === 'string') || typeof t[0] === 'string') ss.push(t); + else ss = ss.concat(t); + } + + return ss; + } + + function _getValue() { + if (checkSC(pos)) return getSC(); + else if (checkVhash(pos)) return getVhash(); + else if (checkAny(pos)) return getAny(); + else if (checkBlock(pos)) return getBlock(); + else if (checkAtkeyword(pos)) return getAtkeyword(); + else if (checkOperator(pos)) return getOperator(); + else if (checkImportant(pos)) return getImportant(); + } + + // node: Vhash + function checkVhash(_i) { + if (_i >= tokens.length || tokens[_i].type !== TokenType.NumberSign) return fail(tokens[_i]); + + var l = checkNmName2(_i + 1); + + if (l) return l + 1; + + return fail(tokens[_i]); + } + + function getVhash() { + var startPos = pos; + + pos++; + + return needInfo? + [{ ln: tokens[startPos].ln }, CSSPNodeType.VhashType, getNmName2()] : + [CSSPNodeType.VhashType, getNmName2()]; + } + + function checkNmName(_i) { + var start = _i; + + // start char / word + if (tokens[_i].type === TokenType.HyphenMinus || + tokens[_i].type === TokenType.LowLine || + tokens[_i].type === TokenType.Identifier || + tokens[_i].type === TokenType.DecimalNumber) _i++; + else return fail(tokens[_i]); + + for (; _i < tokens.length; _i++) { + if (tokens[_i].type !== TokenType.HyphenMinus && + tokens[_i].type !== TokenType.LowLine && + tokens[_i].type !== TokenType.Identifier && + tokens[_i].type !== TokenType.DecimalNumber) break; + } + + tokens[start].nm_name_last = _i - 1; + + return _i - start; + } + + function getNmName() { + var s = joinValues(pos, tokens[pos].nm_name_last); + + pos = tokens[pos].nm_name_last + 1; + + return s; + } + + function checkNmName2(_i) { + var start = _i; + + if (tokens[_i].type === TokenType.Identifier) return 1; + else if (tokens[_i].type !== TokenType.DecimalNumber) return fail(tokens[_i]); + + _i++; + + if (!tokens[_i] || tokens[_i].type !== TokenType.Identifier) return 1; + + return 2; + } + + function getNmName2() { + var s = tokens[pos].value; + + if (tokens[pos++].type === TokenType.DecimalNumber && + pos < tokens.length && + tokens[pos].type === TokenType.Identifier + ) s += tokens[pos++].value; + + return s; + } + + function checkExcluding(exclude, _i) { + var start = _i; + + while(_i < tokens.length) { + if (exclude[tokens[_i++].type]) break; + } + + return _i - start - 2; + } + + function joinValues(start, finish) { + var s = ''; + + for (var i = start; i < finish + 1; i++) { + s += tokens[i].value; + } + + return s; + } + + function joinValues2(start, num) { + if (start + num - 1 >= tokens.length) return; + + var s = ''; + + for (var i = 0; i < num; i++) { + s += tokens[start + i].value; + } + + return s; + } + + function markSC() { + var ws = -1, // whitespaces + sc = -1, // ws and comments + t; + + for (var i = 0; i < tokens.length; i++) { + t = tokens[i]; + switch (t.type) { + case TokenType.Space: + case TokenType.Tab: + case TokenType.Newline: + t.ws = true; + t.sc = true; + + if (ws === -1) ws = i; + if (sc === -1) sc = i; + + break; + case TokenType.CommentML: + if (ws !== -1) { + tokens[ws].ws_last = i - 1; + ws = -1; + } + + t.sc = true; + + break; + default: + if (ws !== -1) { + tokens[ws].ws_last = i - 1; + ws = -1; + } + + if (sc !== -1) { + tokens[sc].sc_last = i - 1; + sc = -1; + } + } + } + + if (ws !== -1) tokens[ws].ws_last = i - 1; + if (sc !== -1) tokens[sc].sc_last = i - 1; + } + + return _getAST(_tokens, rule, _needInfo); +} + + return getCSSPAST(getTokens(s), rule, _needInfo); +} +var translator = new CSSOTranslator(), + cleanInfo = $util.cleanInfo; +function TRBL(name, imp) { + this.name = TRBL.extractMain(name); + this.sides = { + 'top': null, + 'right': null, + 'bottom': null, + 'left': null + }; + this.imp = imp ? 4 : 0; +} + +TRBL.props = { + 'margin': 1, + 'margin-top': 1, + 'margin-right': 1, + 'margin-bottom': 1, + 'margin-left': 1, + 'padding': 1, + 'padding-top': 1, + 'padding-right': 1, + 'padding-bottom': 1, + 'padding-left': 1 +}; + +TRBL.extractMain = function(name) { + var i = name.indexOf('-'); + return i === -1 ? name : name.substr(0, i); +}; + +TRBL.prototype.impSum = function() { + var imp = 0, n = 0; + for (var k in this.sides) { + if (this.sides[k]) { + n++; + if (this.sides[k].imp) imp++; + } + } + return imp === n ? imp : 0; +}; + +TRBL.prototype.add = function(name, sValue, tValue, imp) { + var s = this.sides, + currentSide, + i, x, side, a = [], last, + imp = imp ? 1 : 0, + wasUnary = false; + if ((i = name.lastIndexOf('-')) !== -1) { + side = name.substr(i + 1); + if (side in s) { + if (!(currentSide = s[side]) || (imp && !currentSide.imp)) { + s[side] = { s: imp ? sValue.substring(0, sValue.length - 10) : sValue, t: [tValue[0]], imp: imp }; + if (tValue[0][1] === 'unary') s[side].t.push(tValue[1]); + } + return true; + } + } else if (name === this.name) { + for (i = 0; i < tValue.length; i++) { + x = tValue[i]; + last = a[a.length - 1]; + switch(x[1]) { + case 'unary': + a.push({ s: x[2], t: [x], imp: imp }); + wasUnary = true; + break; + case 'number': + case 'ident': + if (wasUnary) { + last.t.push(x); + last.s += x[2]; + } else { + a.push({ s: x[2], t: [x], imp: imp }); + } + wasUnary = false; + break; + case 'percentage': + if (wasUnary) { + last.t.push(x); + last.s += x[2][2] + '%'; + } else { + a.push({ s: x[2][2] + '%', t: [x], imp: imp }); + } + wasUnary = false; + break; + case 'dimension': + if (wasUnary) { + last.t.push(x); + last.s += x[2][2] + x[3][2]; + } else { + a.push({ s: x[2][2] + x[3][2], t: [x], imp: imp }); + } + wasUnary = false; + break; + case 's': + case 'comment': + case 'important': + break; + default: + return false; + } + } + + if (a.length > 4) return false; + + if (!a[1]) a[1] = a[0]; + if (!a[2]) a[2] = a[0]; + if (!a[3]) a[3] = a[1]; + + if (!s.top) s.top = a[0]; + if (!s.right) s.right = a[1]; + if (!s.bottom) s.bottom = a[2]; + if (!s.left) s.left = a[3]; + + return true; + } +}; + +TRBL.prototype.isOkToMinimize = function() { + var s = this.sides, + imp; + + if (!!(s.top && s.right && s.bottom && s.left)) { + imp = s.top.imp + s.right.imp + s.bottom.imp + s.left.imp; + return (imp === 0 || imp === 4 || imp === this.imp); + } + return false; +}; + +TRBL.prototype.getValue = function() { + var s = this.sides, + a = [s.top, s.right, s.bottom, s.left], + r = [{}, 'value']; + + if (s.left.s === s.right.s) { + a.length--; + if (s.bottom.s === s.top.s) { + a.length--; + if (s.right.s === s.top.s) { + a.length--; + } + } + } + + for (var i = 0; i < a.length - 1; i++) { + r = r.concat(a[i].t); + r.push([{ s: ' ' }, 's', ' ']); + } + r = r.concat(a[i].t); + + if (this.impSum()) r.push([{ s: '!important'}, 'important']); + + return r; +}; + +TRBL.prototype.getProperty = function() { + return [{ s: this.name }, 'property', [{ s: this.name }, 'ident', this.name]]; +}; + +TRBL.prototype.getString = function() { + var p = this.getProperty(), + v = this.getValue().slice(2), + r = p[0].s + ':'; + + for (var i = 0; i < v.length; i++) r += v[i][0].s; + + return r; +}; + +function CSSOCompressor() {} + +CSSOCompressor.prototype.init = function() { + this.props = {}; + this.shorts = {}; + this.shorts2 = {}; + + this.ccrules = {}; // clean comment rules — special case to resolve ambiguity + this.crules = {}; // compress rules + this.prules = {}; // prepare rules + this.frrules = {}; // freeze ruleset rules + this.msrules = {}; // mark shorthands rules + this.csrules = {}; // clean shorthands rules + this.rbrules = {}; // restructure block rules + this.rjrules = {}; // rejoin ruleset rules + this.rrrules = {}; // restructure ruleset rules + this.frules = {}; // finalize rules + + this.initRules(this.crules, this.defCCfg); + this.initRules(this.ccrules, this.cleanCfg); + this.initRules(this.frrules, this.frCfg); + this.initRules(this.prules, this.preCfg); + this.initRules(this.msrules, this.msCfg); + this.initRules(this.csrules, this.csCfg); + this.initRules(this.rbrules, this.defRBCfg); + this.initRules(this.rjrules, this.defRJCfg); + this.initRules(this.rrrules, this.defRRCfg); + this.initRules(this.frules, this.defFCfg); + + this.shortGroupID = 0; + this.lastShortGroupID = 0; + this.lastShortSelector = 0; +}; + +CSSOCompressor.prototype.initRules = function(r, cfg) { + var o = this.order, + p = this.profile, + x, i, k, + t = []; + + for (i = 0; i < o.length; i++) if (o[i] in cfg) t.push(o[i]); + + if (!t.length) t = o; + for (i = 0; i < t.length; i++) { + x = p[t[i]]; + for (k in x) r[k] ? r[k].push(t[i]) : r[k] = [t[i]]; + } +}; + +CSSOCompressor.prototype.cleanCfg = { + 'cleanComment': 1 +}; + +CSSOCompressor.prototype.defCCfg = { + 'cleanCharset': 1, + 'cleanImport': 1, + 'cleanWhitespace': 1, + 'cleanDecldelim': 1, + 'compressNumber': 1, + 'cleanUnary': 1, + 'compressColor': 1, + 'compressDimension': 1, + 'compressString': 1, + 'compressFontWeight': 1, + 'compressFont': 1, + 'compressBackground': 1, + 'cleanEmpty': 1 +}; + +CSSOCompressor.prototype.defRBCfg = { + 'restructureBlock': 1 +}; + +CSSOCompressor.prototype.defRJCfg = { + 'rejoinRuleset': 1, + 'cleanEmpty': 1 +}; + +CSSOCompressor.prototype.defRRCfg = { + 'restructureRuleset': 1, + 'cleanEmpty': 1 +}; + +CSSOCompressor.prototype.defFCfg = { + 'cleanEmpty': 1, + 'delimSelectors': 1, + 'delimBlocks': 1 +}; + +CSSOCompressor.prototype.preCfg = { + 'destroyDelims': 1, + 'preTranslate': 1 +}; + +CSSOCompressor.prototype.msCfg = { + 'markShorthands': 1 +}; + +CSSOCompressor.prototype.frCfg = { + 'freezeRulesets': 1 +}; + +CSSOCompressor.prototype.csCfg = { + 'cleanShorthands': 1, + 'cleanEmpty': 1 +}; + +CSSOCompressor.prototype.order = [ + 'cleanCharset', + 'cleanImport', + 'cleanComment', + 'cleanWhitespace', + 'compressNumber', + 'cleanUnary', + 'compressColor', + 'compressDimension', + 'compressString', + 'compressFontWeight', + 'compressFont', + 'compressBackground', + 'freezeRulesets', + 'destroyDelims', + 'preTranslate', + 'markShorthands', + 'cleanShorthands', + 'restructureBlock', + 'rejoinRuleset', + 'restructureRuleset', + 'cleanEmpty', + 'delimSelectors', + 'delimBlocks' +]; + +CSSOCompressor.prototype.profile = { + 'cleanCharset': { + 'atrules': 1 + }, + 'cleanImport': { + 'atrules': 1 + }, + 'cleanWhitespace': { + 's': 1 + }, + 'compressNumber': { + 'number': 1 + }, + 'cleanUnary': { + 'unary': 1 + }, + 'compressColor': { + 'vhash': 1, + 'funktion': 1, + 'ident': 1 + }, + 'compressDimension': { + 'dimension': 1 + }, + 'compressString': { + 'string': 1 + }, + 'compressFontWeight': { + 'declaration': 1 + }, + 'compressFont': { + 'declaration': 1 + }, + 'compressBackground': { + 'declaration': 1 + }, + 'cleanComment': { + 'comment': 1 + }, + 'cleanDecldelim': { + 'block': 1 + }, + 'cleanEmpty': { + 'ruleset': 1, + 'atruleb': 1, + 'atruler': 1 + }, + 'destroyDelims': { + 'decldelim': 1, + 'delim': 1 + }, + 'preTranslate': { + 'declaration': 1, + 'property': 1, + 'simpleselector': 1, + 'filter': 1, + 'value': 1, + 'number': 1, + 'percentage': 1, + 'dimension': 1, + 'ident': 1 + }, + 'restructureBlock': { + 'block': 1 + }, + 'rejoinRuleset': { + 'ruleset': 1 + }, + 'restructureRuleset': { + 'ruleset': 1 + }, + 'delimSelectors': { + 'selector': 1 + }, + 'delimBlocks': { + 'block': 1 + }, + 'markShorthands': { + 'block': 1 + }, + 'cleanShorthands': { + 'declaration': 1 + }, + 'freezeRulesets': { + 'ruleset': 1 + } +}; + +CSSOCompressor.prototype.isContainer = function(o) { + if (Array.isArray(o)) { + for (var i = 0; i < o.length; i++) if (Array.isArray(o[i])) return true; + } +}; + +CSSOCompressor.prototype.process = function(rules, token, container, i, path) { + var rule = token[1]; + if (rule && rules[rule]) { + var r = rules[rule], + x1 = token, x2, + o = this.order, k; + for (var k = 0; k < r.length; k++) { + x2 = this[r[k]](x1, rule, container, i, path); + if (x2 === null) return null; + else if (x2 !== undefined) x1 = x2; + } + } + return x1; +}; + +CSSOCompressor.prototype.compress = function(tree, ro) { + tree = tree || ['stylesheet']; + this.init(); + this.info = true; + + var x = (typeof tree[0] !== 'string') ? tree : this.injectInfo([tree])[0], + l0, l1 = 100000000000, ls, + x0, x1, xs, + protectedComment = this.findProtectedComment(tree); + + // compression without restructure + x = this.walk(this.ccrules, x, '/0'); + x = this.walk(this.crules, x, '/0'); + x = this.walk(this.prules, x, '/0'); + x = this.walk(this.frrules, x, '/0'); + + ls = translator.translate(cleanInfo(x)).length; + + if (!ro) { // restructure ON + xs = this.copyArray(x); + x = this.walk(this.rjrules, x, '/0'); + this.disjoin(x); + x = this.walk(this.msrules, x, '/0'); + x = this.walk(this.csrules, x, '/0'); + x = this.walk(this.rbrules, x, '/0'); + do { + l0 = l1; + x0 = this.copyArray(x); + x = this.walk(this.rjrules, x, '/0'); + x = this.walk(this.rrrules, x, '/0'); + l1 = translator.translate(cleanInfo(x)).length; + x1 = this.copyArray(x); + } while (l0 > l1); + if (ls < l0 && ls < l1) x = xs; + else if (l0 < l1) x = x0; + } + + x = this.walk(this.frules, x, '/0'); + + if (protectedComment) x.splice(2, 0, protectedComment); + + return x; +}; + +CSSOCompressor.prototype.findProtectedComment = function(tree) { + var token; + for (var i = 2; i < tree.length; i++) { + token = tree[i]; + if (token[1] === 'comment' && token[2].length > 0 && token[2].charAt(0) === '!') return token; + if (token[1] !== 's') return; + } +}; + +CSSOCompressor.prototype.injectInfo = function(token) { + var t; + for (var i = token.length - 1; i > -1; i--) { + t = token[i]; + if (t && Array.isArray(t)) { + if (this.isContainer(t)) t = this.injectInfo(t); + t.splice(0, 0, {}); + } + } + return token; +}; + +CSSOCompressor.prototype.disjoin = function(container) { + var t, s, r, sr; + + for (var i = container.length - 1; i > -1; i--) { + t = container[i]; + if (t && Array.isArray(t)) { + if (t[1] === 'ruleset') { + t[0].shortGroupID = this.shortGroupID++; + s = t[2]; + if (s.length > 3) { + sr = s.slice(0, 2); + for (var k = s.length - 1; k > 1; k--) { + r = this.copyArray(t); + r[2] = sr.concat([s[k]]); + r[2][0].s = s[k][0].s; + container.splice(i + 1, 0, r); + } + container.splice(i, 1); + } + } + } + if (this.isContainer(t)) this.disjoin(t); + } +}; + +CSSOCompressor.prototype.walk = function(rules, container, path) { + var t, x; + for (var i = container.length - 1; i > -1; i--) { + t = container[i]; + if (t && Array.isArray(t)) { + t[0].parent = container; + if (this.isContainer(t)) t = this.walk(rules, t, path + '/' + i); // go inside + if (t === null) container.splice(i, 1); + else { + if (x = this.process(rules, t, container, i, path)) container[i] = x; // compressed not null + else if (x === null) container.splice(i, 1); // null is the mark to delete token + } + } + } + return container.length ? container : null; +}; + +CSSOCompressor.prototype.freezeRulesets = function(token, rule, container, i) { + var info = token[0], + selector = token[2]; + + info.freeze = this.freezeNeeded(selector); + info.freezeID = this.selectorSignature(selector); + info.pseudoID = this.composePseudoID(selector); + info.pseudoSignature = this.pseudoSelectorSignature(selector, this.allowedPClasses, true); + this.markSimplePseudo(selector); + + return token; +}; + +CSSOCompressor.prototype.markSimplePseudo = function(selector) { + var ss, sg = {}; + + for (var i = 2; i < selector.length; i++) { + ss = selector[i]; + ss[0].pseudo = this.containsPseudo(ss); + ss[0].sg = sg; + sg[ss[0].s] = 1; + } +}; + +CSSOCompressor.prototype.composePseudoID = function(selector) { + var a = [], ss; + + for (var i = 2; i < selector.length; i++) { + ss = selector[i]; + if (this.containsPseudo(ss)) { + a.push(ss[0].s); + } + } + + a.sort(); + + return a.join(','); +}; + +CSSOCompressor.prototype.containsPseudo = function(sselector) { + for (var j = 2; j < sselector.length; j++) { + switch (sselector[j][1]) { + case 'pseudoc': + case 'pseudoe': + case 'nthselector': + if (!(sselector[j][2][2] in this.notFPClasses)) return true; + } + } +}; + +CSSOCompressor.prototype.selectorSignature = function(selector) { + var a = []; + + for (var i = 2; i < selector.length; i++) { + a.push(translator.translate(cleanInfo(selector[i]))); + } + + a.sort(); + + return a.join(','); +}; + +CSSOCompressor.prototype.pseudoSelectorSignature = function(selector, exclude, dontAppendExcludeMark) { + var a = [], b = {}, ss, wasExclude = false; + exclude = exclude || {}; + + for (var i = 2; i < selector.length; i++) { + ss = selector[i]; + for (var j = 2; j < ss.length; j++) { + switch (ss[j][1]) { + case 'pseudoc': + case 'pseudoe': + case 'nthselector': + if (!(ss[j][2][2] in exclude)) b[ss[j][2][2]] = 1; + else wasExclude = true; + break; + } + } + } + + for (var k in b) a.push(k); + + a.sort(); + + return a.join(',') + (dontAppendExcludeMark? '' : wasExclude); +}; + +CSSOCompressor.prototype.notFPClasses = { + 'link': 1, + 'visited': 1, + 'hover': 1, + 'active': 1, + 'first-letter': 1, + 'first-line': 1 +}; + +CSSOCompressor.prototype.notFPElements = { + 'first-letter': 1, + 'first-line': 1 +}; + +CSSOCompressor.prototype.freezeNeeded = function(selector) { + var ss; + for (var i = 2; i < selector.length; i++) { + ss = selector[i]; + for (var j = 2; j < ss.length; j++) { + switch (ss[j][1]) { + case 'pseudoc': + if (!(ss[j][2][2] in this.notFPClasses)) return true; + break; + case 'pseudoe': + if (!(ss[j][2][2] in this.notFPElements)) return true; + break; + case 'nthselector': + return true; + break; + } + } + } + return false; +}; + +CSSOCompressor.prototype.cleanCharset = function(token, rule, container, i) { + if (token[2][2][2] === 'charset') { + for (i = i - 1; i > 1; i--) { + if (container[i][1] !== 's' && container[i][1] !== 'comment') return null; + } + } +}; + +CSSOCompressor.prototype.cleanImport = function(token, rule, container, i) { + var x; + for (i = i - 1; i > 1; i--) { + x = container[i][1]; + if (x !== 's' && x !== 'comment') { + if (x === 'atrules') { + x = container[i][2][2][2]; + if (x !== 'import' && x !== 'charset') return null; + } else return null; + } + } +}; + +CSSOCompressor.prototype.cleanComment = function(token, rule, container, i) { + var pr = ((container[1] === 'braces' && i === 4) || + (container[1] !== 'braces' && i === 2)) ? null : container[i - 1][1], + nr = i === container.length - 1 ? null : container[i + 1][1]; + + if (nr !== null && pr !== null) { + if (this._cleanComment(nr) || this._cleanComment(pr)) return null; + } else return null; +}; + +CSSOCompressor.prototype._cleanComment = function(r) { + switch(r) { + case 's': + case 'operator': + case 'attrselector': + case 'block': + case 'decldelim': + case 'ruleset': + case 'declaration': + case 'atruleb': + case 'atrules': + case 'atruler': + case 'important': + case 'nth': + case 'combinator': + return true; + } +}; + +CSSOCompressor.prototype.nextToken = function(container, type, i, exactly) { + var t, r; + for (; i < container.length; i++) { + t = container[i]; + if (Array.isArray(t)) { + r = t[1]; + if (r === type) return t; + else if (exactly && r !== 's') return; + } + } +}; + +CSSOCompressor.prototype.cleanWhitespace = function(token, rule, container, i) { + var pr = ((container[1] === 'braces' && i === 4) || + (container[1] !== 'braces' && i === 2)) ? null : container[i - 1][1], + nr = i === container.length - 1 ? null : container[i + 1][1]; + + if (nr === 'unknown') token[2] = '\n'; + else { + if (!(container[1] === 'atrulerq' && !pr) && !this.issue16(container, i)) { + if (nr !== null && pr !== null) { + if (this._cleanWhitespace(nr, false) || this._cleanWhitespace(pr, true)) return null; + } else return null; + } + + token[2] = ' '; + } + + return token; +}; + +// See https://github.com/afelix/csso/issues/16 +CSSOCompressor.prototype.issue16 = function(container, i) { + return (i !== 2 && i !== container.length - 1 && container[i - 1][1] === 'uri'); +}; + +CSSOCompressor.prototype._cleanWhitespace = function(r, left) { + switch(r) { + case 's': + case 'operator': + case 'attrselector': + case 'block': + case 'decldelim': + case 'ruleset': + case 'declaration': + case 'atruleb': + case 'atrules': + case 'atruler': + case 'important': + case 'nth': + case 'combinator': + return true; + } + if (left) { + switch(r) { + case 'funktion': + case 'braces': + case 'uri': + return true; + } + } +}; + +CSSOCompressor.prototype.cleanDecldelim = function(token) { + for (var i = token.length - 1; i > 1; i--) { + if (token[i][1] === 'decldelim' && + token[i + 1][1] !== 'declaration') token.splice(i, 1); + } + if (token[2][1] === 'decldelim') token.splice(2, 1); + return token; +}; + +CSSOCompressor.prototype.compressNumber = function(token, rule, container, i) { + var x = token[2]; + + if (/^0*/.test(x)) x = x.replace(/^0+/, ''); + if (/\.0*$/.test(x)) x = x.replace(/\.0*$/, ''); + if (/\..*[1-9]+0+$/.test(x)) x = x.replace(/0+$/, ''); + if (x === '.' || x === '') x = '0'; + + token[2] = x; + token[0].s = x; + return token; +}; + +CSSOCompressor.prototype.findDeclaration = function(token) { + var parent = token; + while ((parent = parent[0].parent) && parent[1] !== 'declaration'); + return parent; +}; + +CSSOCompressor.prototype.cleanUnary = function(token, rule, container, i) { + var next = container[i + 1]; + if (next && next[1] === 'number' && next[2] === '0') return null; + return token; +}; + +CSSOCompressor.prototype.compressColor = function(token, rule, container, i) { + switch(rule) { + case 'vhash': + return this.compressHashColor(token); + case 'funktion': + return this.compressFunctionColor(token); + case 'ident': + return this.compressIdentColor(token, rule, container, i); + } +}; + +CSSOCompressor.prototype.compressIdentColor = function(token, rule, container) { + var map = { 'yellow': 'ff0', + 'fuchsia': 'f0f', + 'white': 'fff', + 'black': '000', + 'blue': '00f', + 'aqua': '0ff' }, + allow = { 'value': 1, 'functionBody': 1 }, + _x = token[2].toLowerCase(); + + if (container[1] in allow && _x in map) return [{}, 'vhash', map[_x]]; +}; + +CSSOCompressor.prototype.compressHashColor = function(token) { + return this._compressHashColor(token[2], token[0]); +}; + +CSSOCompressor.prototype._compressHashColor = function(x, info) { + var map = { 'f00': 'red', + 'c0c0c0': 'silver', + '808080': 'gray', + '800000': 'maroon', + '800080': 'purple', + '008000': 'green', + '808000': 'olive', + '000080': 'navy', + '008080': 'teal'}, + _x = x; + x = x.toLowerCase(); + + if (x.length === 6 && + x.charAt(0) === x.charAt(1) && + x.charAt(2) === x.charAt(3) && + x.charAt(4) === x.charAt(5)) x = x.charAt(0) + x.charAt(2) + x.charAt(4); + + return map[x] ? [info, 'string', map[x]] : [info, 'vhash', (x.length < _x.length ? x : _x)]; +}; + +CSSOCompressor.prototype.compressFunctionColor = function(token) { + var i, v = [], t, h = '', body; + + if (token[2][2] === 'rgb') { + body = token[3]; + for (i = 2; i < body.length; i++) { + t = body[i][1]; + if (t === 'number') v.push(body[i]); + else if (t !== 'operator') { v = []; break } + } + if (v.length === 3) { + h += (t = Number(v[0][2]).toString(16)).length === 1 ? '0' + t : t; + h += (t = Number(v[1][2]).toString(16)).length === 1 ? '0' + t : t; + h += (t = Number(v[2][2]).toString(16)).length === 1 ? '0' + t : t; + if (h.length === 6) return this._compressHashColor(h, {}); + } + } +}; + +CSSOCompressor.prototype.compressDimension = function(token) { + var declaration; + if (token[2][2] === '0') { + if (token[3][2] === 's' && (declaration = this.findDeclaration(token))) { + var declName = declaration[2][2][2]; + if (declName === '-moz-transition') return; // https://github.com/css/csso/issues/82 + if (declName === '-moz-animation' || declName === 'animation') return; // https://github.com/css/csso/issues/100 + } + return token[2]; + } +}; + +CSSOCompressor.prototype.compressString = function(token, rule, container) { + var s = token[2], r = '', c; + for (var i = 0; i < s.length; i++) { + c = s.charAt(i); + if (c === '\\' && s.charAt(i + 1) === '\n') i++; + else r += c; + } +// if (container[1] === 'attrib' && /^('|")[a-zA-Z0-9]*('|")$/.test(r)) { +// r = r.substring(1, r.length - 1); +// } + if (s.length !== r.length) return [{}, 'string', r]; +}; + +CSSOCompressor.prototype.compressFontWeight = function(token) { + var p = token[2], + v = token[3]; + if (p[2][2].indexOf('font-weight') !== -1 && v[2][1] === 'ident') { + if (v[2][2] === 'normal') v[2] = [{}, 'number', '400']; + else if (v[2][2] === 'bold') v[2] = [{}, 'number', '700']; + return token; + } +}; + +CSSOCompressor.prototype.compressFont = function(token) { + var p = token[2], + v = token[3], + i, x, t; + if (/font$/.test(p[2][2]) && v.length) { + v.splice(2, 0, [{}, 's', '']); + for (i = v.length - 1; i > 2; i--) { + x = v[i]; + if (x[1] === 'ident') { + x = x[2]; + if (x === 'bold') v[i] = [{}, 'number', '700']; + else if (x === 'normal') { + t = v[i - 1]; + if (t[1] === 'operator' && t[2] === '/') v.splice(--i, 2); + else v.splice(i, 1); + if (v[i - 1][1] === 's') v.splice(--i, 1); + } + else if (x === 'medium' && v[i + 1] && v[i + 1][2] !== '/') { + v.splice(i, 1); + if (v[i - 1][1] === 's') v.splice(--i, 1); + } + } + } + if (v.length > 2 && v[2][1] === 's') v.splice(2, 1); + if (v.length === 2) v.push([{}, 'ident', 'normal']); + return token; + } +}; + +CSSOCompressor.prototype.compressBackground = function(token) { + var p = token[2], + v = token[3], + i, x, t, + n = v[v.length - 1][1] === 'important' ? 3 : 2; + if (/background$/.test(p[2][2]) && v.length) { + v.splice(2, 0, [{}, 's', '']); + for (i = v.length - 1; i > n; i--) { + x = v[i]; + if (x[1] === 'ident') { + x = x[2]; + if (x === 'transparent' || x === 'none' || x === 'repeat' || x === 'scroll') { + v.splice(i, 1); + if (v[i - 1][1] === 's') v.splice(--i, 1); + } + } + } + if (v.length > 2 && v[2][1] === 's') v.splice(2, 1); + if (v.length === 2) v.splice(2, 0, [{}, 'number', '0'], [{}, 's', ' '], [{}, 'number', '0']); + return token; + } +}; + +CSSOCompressor.prototype.cleanEmpty = function(token, rule) { + switch(rule) { + case 'ruleset': + if (token[3].length === 2) return null; + break; + case 'atruleb': + if (token[token.length - 1].length < 3) return null; + break; + case 'atruler': + if (token[4].length < 3) return null; + break; + } +}; + +CSSOCompressor.prototype.destroyDelims = function() { + return null; +}; + +CSSOCompressor.prototype.preTranslate = function(token) { + token[0].s = translator.translate(cleanInfo(token)); + return token; +}; + +CSSOCompressor.prototype.markShorthands = function(token, rule, container, j, path) { + if (container[1] === 'ruleset') { + var selector = container[2][2][0].s, + freeze = container[0].freeze, + freezeID = container[0].freezeID; + } else { + var selector = '', + freeze = false, + freezeID = 'fake'; + } + var x, p, v, imp, s, key, sh, + pre = this.pathUp(path) + '/' + (freeze ? '&' + freezeID + '&' : '') + selector + '/', + createNew, shortsI, shortGroupID = container[0].shortGroupID; + + for (var i = token.length - 1; i > -1; i--) { + createNew = true; + x = token[i]; + if (x[1] === 'declaration') { + v = x[3]; + imp = v[v.length - 1][1] === 'important'; + p = x[2][0].s; + x[0].id = path + '/' + i; + if (p in TRBL.props) { + key = pre + TRBL.extractMain(p); + var shorts = this.shorts2[key] || []; + shortsI = shorts.length === 0 ? 0 : shorts.length - 1; + + if (!this.lastShortSelector || selector === this.lastShortSelector || shortGroupID === this.lastShortGroupID) { + if (shorts.length) { + sh = shorts[shortsI]; + //if (imp && !sh.imp) sh.invalid = true; + createNew = false; + } + } + + if (createNew) { + x[0].replaceByShort = true; + x[0].shorthandKey = { key: key, i: shortsI }; + sh = new TRBL(p, imp); + shorts.push(sh); + } + + if (!sh.invalid) { + x[0].removeByShort = true; + x[0].shorthandKey = { key: key, i: shortsI }; + sh.add(p, v[0].s, v.slice(2), imp); + } + + this.shorts2[key] = shorts; + + this.lastShortSelector = selector; + this.lastShortGroupID = shortGroupID; + } + } + } + + + return token; +}; + +CSSOCompressor.prototype.cleanShorthands = function(token) { + if (token[0].removeByShort || token[0].replaceByShort) { + var s, t, sKey = token[0].shorthandKey; + + s = this.shorts2[sKey.key][sKey.i]; + + if (!s.invalid && s.isOkToMinimize()) { + if (token[0].replaceByShort) { + t = [{}, 'declaration', s.getProperty(), s.getValue()]; + t[0].s = translator.translate(cleanInfo(t)); + return t; + } else return null; + } + } +}; + +CSSOCompressor.prototype.dontRestructure = { + 'src': 1, // https://github.com/afelix/csso/issues/50 + 'clip': 1, // https://github.com/afelix/csso/issues/57 + 'display': 1 // https://github.com/afelix/csso/issues/71 +}; + +CSSOCompressor.prototype.restructureBlock = function(token, rule, container, j, path) { + if (container[1] === 'ruleset') { + var props = this.props, + isPseudo = container[2][2][0].pseudo, + selector = container[2][2][0].s, + freeze = container[0].freeze, + freezeID = container[0].freezeID, + pseudoID = container[0].pseudoID, + sg = container[2][2][0].sg; + } else { + var props = {}, + isPseudo = false, + selector = '', + freeze = false, + freezeID = 'fake', + pseudoID = 'fake', + sg = {}; + } + + var x, p, v, imp, t, + pre = this.pathUp(path) + '/' + selector + '/', + ppre; + for (var i = token.length - 1; i > -1; i--) { + x = token[i]; + if (x[1] === 'declaration') { + v = x[3]; + imp = v[v.length - 1][1] === 'important'; + p = x[2][0].s; + ppre = this.buildPPre(pre, p, v, x, freeze); + x[0].id = path + '/' + i; + if (!this.dontRestructure[p] && (t = props[ppre])) { + if ((isPseudo && freezeID === t.freezeID) || // pseudo from equal selectors group + (!isPseudo && pseudoID === t.pseudoID) || // not pseudo from equal pseudo signature group + (isPseudo && pseudoID === t.pseudoID && this.hashInHash(sg, t.sg))) { // pseudo from covered selectors group + if (imp && !t.imp) { + props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg, + freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID }; + this.deleteProperty(t.block, t.id); + } else { + token.splice(i, 1); + } + } + } else if (this.needless(p, props, pre, imp, v, x, freeze)) { + token.splice(i, 1); + } else { + props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg, + freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID }; + } + } + } + return token; +}; + +CSSOCompressor.prototype.buildPPre = function(pre, p, v, d, freeze) { + var fp = freeze ? 'ft:' : 'ff:'; + if (p.indexOf('background') !== -1) return fp + pre + d[0].s; + + var _v = v.slice(2), + colorMark = [ + 0, // ident, vhash, rgb + 0, // hsl + 0, // hsla + 0 // rgba + ], + vID = ''; + + for (var i = 0; i < _v.length; i++) { + if (!vID) vID = this.getVendorIDFromToken(_v[i]); + switch(_v[i][1]) { + case 'vhash': + case 'ident': + colorMark[0] = 1; break; + case 'funktion': + switch(_v[i][2][2]) { + case 'rgb': + colorMark[0] = 1; break; + case 'hsl': + colorMark[1] = 1; break; + case 'hsla': + colorMark[2] = 1; break; + case 'rgba': + colorMark[3] = 1; break; + } + break; + } + } + + return fp + pre + p + colorMark.join('') + (vID ? vID : ''); +}; + +CSSOCompressor.prototype.vendorID = { + '-o-': 'o', + '-moz-': 'm', + '-webkit-': 'w', + '-ms-': 'i', + '-epub-': 'e', + '-apple-': 'a', + '-xv-': 'x', + '-wap-': 'p' +}; + +CSSOCompressor.prototype.getVendorIDFromToken = function(token) { + var vID; + switch(token[1]) { + case 'ident': + if (vID = this.getVendorFromString(token[2])) return this.vendorID[vID]; + break; + case 'funktion': + if (vID = this.getVendorFromString(token[2][2])) return this.vendorID[vID]; + break; + } +}; + +CSSOCompressor.prototype.getVendorFromString = function(string) { + var vendor = string.charAt(0), i; + if (vendor === '-') { + if ((i = string.indexOf('-', 2)) !== -1) return string.substr(0, i + 1); + } + return ''; +}; + +CSSOCompressor.prototype.deleteProperty = function(block, id) { + var d; + for (var i = block.length - 1; i > 1; i--) { + d = block[i]; + if (Array.isArray(d) && d[1] === 'declaration' && d[0].id === id) { + block.splice(i, 1); + return; + } + } +}; + +CSSOCompressor.prototype.nlTable = { + 'border-width': ['border'], + 'border-style': ['border'], + 'border-color': ['border'], + 'border-top': ['border'], + 'border-right': ['border'], + 'border-bottom': ['border'], + 'border-left': ['border'], + 'border-top-width': ['border-top', 'border-width', 'border'], + 'border-right-width': ['border-right', 'border-width', 'border'], + 'border-bottom-width': ['border-bottom', 'border-width', 'border'], + 'border-left-width': ['border-left', 'border-width', 'border'], + 'border-top-style': ['border-top', 'border-style', 'border'], + 'border-right-style': ['border-right', 'border-style', 'border'], + 'border-bottom-style': ['border-bottom', 'border-style', 'border'], + 'border-left-style': ['border-left', 'border-style', 'border'], + 'border-top-color': ['border-top', 'border-color', 'border'], + 'border-right-color': ['border-right', 'border-color', 'border'], + 'border-bottom-color': ['border-bottom', 'border-color', 'border'], + 'border-left-color': ['border-left', 'border-color', 'border'], + 'margin-top': ['margin'], + 'margin-right': ['margin'], + 'margin-bottom': ['margin'], + 'margin-left': ['margin'], + 'padding-top': ['padding'], + 'padding-right': ['padding'], + 'padding-bottom': ['padding'], + 'padding-left': ['padding'], + 'font-style': ['font'], + 'font-variant': ['font'], + 'font-weight': ['font'], + 'font-size': ['font'], + 'font-family': ['font'], + 'list-style-type': ['list-style'], + 'list-style-position': ['list-style'], + 'list-style-image': ['list-style'] +}; + +CSSOCompressor.prototype.needless = function(name, props, pre, imp, v, d, freeze) { + var hack = name.charAt(0); + if (hack === '*' || hack === '_' || hack === '$') name = name.substr(1); + else if (hack === '/' && name.charAt(1) === '/') { + hack = '//'; + name = name.substr(2); + } else hack = ''; + + var vendor = this.getVendorFromString(name), + prop = name.substr(vendor.length), + x, t, ppre; + + if (prop in this.nlTable) { + x = this.nlTable[prop]; + for (var i = 0; i < x.length; i++) { + ppre = this.buildPPre(pre, hack + vendor + x[i], v, d, freeze); + if (t = props[ppre]) return (!imp || t.imp); + } + } +}; + +CSSOCompressor.prototype.rejoinRuleset = function(token, rule, container, i) { + var p = (i === 2 || container[i - 1][1] === 'unknown') ? null : container[i - 1], + ps = p ? p[2].slice(2) : [], + pb = p ? p[3].slice(2) : [], + ts = token[2].slice(2), + tb = token[3].slice(2), + ph, th, r; + + if (!tb.length) return null; + + if (ps.length && pb.length && token[0].pseudoSignature == p[0].pseudoSignature) { + if (token[1] !== p[1]) return; + // try to join by selectors + ph = this.getHash(ps); + th = this.getHash(ts); + + if (this.equalHash(th, ph)) { + p[3] = p[3].concat(token[3].splice(2)); + return null; + } + if (this.okToJoinByProperties(token, p)) { + // try to join by properties + r = this.analyze(token, p); + if (!r.ne1.length && !r.ne2.length) { + p[2] = this.cleanSelector(p[2].concat(token[2].splice(2))); + p[2][0].s = translator.translate(cleanInfo(p[2])); + return null; + } + } + } +}; + +CSSOCompressor.prototype.okToJoinByProperties = function(r0, r1) { + var i0 = r0[0], i1 = r1[0]; + + // same frozen ruleset + if (i0.freezeID === i1.freezeID) return true; + + // same pseudo-classes in selectors + if (i0.pseudoID === i1.pseudoID) return true; + + // different frozen rulesets + if (i0.freeze && i1.freeze) { + return this.pseudoSelectorSignature(r0[2], this.allowedPClasses) === this.pseudoSelectorSignature(r1[2], this.allowedPClasses); + } + + // is it frozen at all? + return !(i0.freeze || i1.freeze); +}; + +CSSOCompressor.prototype.allowedPClasses = { + 'after': 1, + 'before': 1 +}; + +CSSOCompressor.prototype.containsOnlyAllowedPClasses = function(selector) { + var ss; + for (var i = 2; i < selector.length; i++) { + ss = selector[i]; + for (var j = 2; j < ss.length; j++) { + if (ss[j][1] == 'pseudoc' || ss[j][1] == 'pseudoe') { + if (!(ss[j][2][2] in this.allowedPClasses)) return false; + } + } + } + return true; +}; + +CSSOCompressor.prototype.restructureRuleset = function(token, rule, container, i) { + var p = (i === 2 || container[i - 1][1] === 'unknown') ? null : container[i - 1], + ps = p ? p[2].slice(2) : [], + pb = p ? p[3].slice(2) : [], + tb = token[3].slice(2), + r, nr; + + if (!tb.length) return null; + + if (ps.length && pb.length && token[0].pseudoSignature == p[0].pseudoSignature) { + if (token[1] !== p[1]) return; + // try to join by properties + r = this.analyze(token, p); + + if (r.eq.length && (r.ne1.length || r.ne2.length)) { + if (r.ne1.length && !r.ne2.length) { // p in token + var ns = token[2].slice(2), // TODO: copypaste + nss = translator.translate(cleanInfo(token[2])), + sl = nss.length + // selector length + ns.length - 1, // delims length + bl = this.calcLength(r.eq) + // declarations length + r.eq.length - 1; // decldelims length + if (sl < bl) { + p[2] = this.cleanSelector(p[2].concat(token[2].slice(2))); + token[3].splice(2); + token[3] = token[3].concat(r.ne1); + return token; + } + } else if (r.ne2.length && !r.ne1.length) { // token in p + var ns = p[2].slice(2), + nss = translator.translate(cleanInfo(p[2])), + sl = nss.length + // selector length + ns.length - 1, // delims length + bl = this.calcLength(r.eq) + // declarations length + r.eq.length - 1; // decldelims length + if (sl < bl) { + token[2] = this.cleanSelector(p[2].concat(token[2].slice(2))); + p[3].splice(2); + p[3] = p[3].concat(r.ne2); + return token; + } + } else { // extract equal block? + var ns = this.cleanSelector(p[2].concat(token[2].slice(2))), + nss = translator.translate(cleanInfo(ns)), + rl = nss.length + // selector length + ns.length - 1 + // delims length + 2, // braces length + bl = this.calcLength(r.eq) + // declarations length + r.eq.length - 1; // decldelims length + + if (bl >= rl) { // ok, it's good enough to extract + ns[0].s = nss; + nr = [{f:0, l:0}, 'ruleset', ns, [{f:0,l:0}, 'block'].concat(r.eq)]; + token[3].splice(2); + token[3] = token[3].concat(r.ne1); + p[3].splice(2); + p[3] = p[3].concat(r.ne2); + container.splice(i, 0, nr); + return nr; + } + } + } + } +}; + +CSSOCompressor.prototype.calcLength = function(tokens) { + var r = 0; + for (var i = 0; i < tokens.length; i++) r += tokens[i][0].s.length; + return r; +}; + +CSSOCompressor.prototype.cleanSelector = function(token) { + if (token.length === 2) return null; + var h = {}, s; + for (var i = 2; i < token.length; i++) { + s = token[i][0].s; + if (s in h) token.splice(i, 1), i--; + else h[s] = 1; + } + + return token; +}; + +CSSOCompressor.prototype.analyze = function(r1, r2) { + var r = { eq: [], ne1: [], ne2: [] }; + + if (r1[1] !== r2[1]) return r; + + var b1 = r1[3], b2 = r2[3], + d1 = b1.slice(2), d2 = b2.slice(2), + h1, h2, i, x; + + h1 = this.getHash(d1); + h2 = this.getHash(d2); + + for (i = 0; i < d1.length; i++) { + x = d1[i]; + if (x[0].s in h2) r.eq.push(x); + else r.ne1.push(x); + } + + for (i = 0; i < d2.length; i++) { + x = d2[i]; + if (!(x[0].s in h1)) r.ne2.push(x); + } + + return r; +}; + +CSSOCompressor.prototype.equalHash = function(h0, h1) { + var k; + for (k in h0) if (!(k in h1)) return false; + for (k in h1) if (!(k in h0)) return false; + return true; +}; + +CSSOCompressor.prototype.getHash = function(tokens) { + var r = {}; + for (var i = 0; i < tokens.length; i++) r[tokens[i][0].s] = 1; + return r; +}; + +CSSOCompressor.prototype.hashInHash = function(h0, h1) { + for (var k in h0) if (!(k in h1)) return false; + return true; +}; + +CSSOCompressor.prototype.delimSelectors = function(token) { + for (var i = token.length - 1; i > 2; i--) { + token.splice(i, 0, [{}, 'delim']); + } +}; + +CSSOCompressor.prototype.delimBlocks = function(token) { + for (var i = token.length - 1; i > 2; i--) { + token.splice(i, 0, [{}, 'decldelim']); + } +}; + +CSSOCompressor.prototype.copyArray = function(a) { + var r = [], t; + + for (var i = 0; i < a.length; i++) { + t = a[i]; + if (Array.isArray(t)) r.push(this.copyArray(t)); + else if (typeof t === 'object') r.push(this.copyObject(t)); + else r.push(t); + } + + return r; +}; + +CSSOCompressor.prototype.copyObject = function(o) { + var r = {}; + for (var k in o) r[k] = o[k]; + return r; +}; + +CSSOCompressor.prototype.pathUp = function(path) { + return path.substr(0, path.lastIndexOf('/')); +}; +function CSSOTranslator() {} + +CSSOTranslator.prototype.translate = function(tree) { +// console.trace('--------'); +// console.log(tree); + return this._t(tree); +}; + +CSSOTranslator.prototype._m_simple = { + 'unary': 1, 'nth': 1, 'combinator': 1, 'ident': 1, 'number': 1, 's': 1, + 'string': 1, 'attrselector': 1, 'operator': 1, 'raw': 1, 'unknown': 1 +}; + +CSSOTranslator.prototype._m_composite = { + 'simpleselector': 1, 'dimension': 1, 'selector': 1, 'property': 1, 'value': 1, + 'filterv': 1, 'progid': 1, 'ruleset': 1, 'atruleb': 1, 'atrulerq': 1, 'atrulers': 1, + 'stylesheet': 1 +}; + +CSSOTranslator.prototype._m_primitive = { + 'cdo': 'cdo', 'cdc': 'cdc', 'decldelim': ';', 'namespace': '|', 'delim': ',' +}; + +CSSOTranslator.prototype._t = function(tree) { + var t = tree[0]; + if (t in this._m_primitive) return this._m_primitive[t]; + else if (t in this._m_simple) return this._simple(tree); + else if (t in this._m_composite) return this._composite(tree); + return this[t](tree); +}; + +CSSOTranslator.prototype._composite = function(t, i) { + var s = ''; + i = i === undefined ? 1 : i; + for (; i < t.length; i++) s += this._t(t[i]); + return s; +}; + +CSSOTranslator.prototype._simple = function(t) { + return t[1]; +}; + +CSSOTranslator.prototype.percentage = function(t) { + return this._t(t[1]) + '%'; +}; + +CSSOTranslator.prototype.comment = function(t) { + return '/*' + t[1] + '*/'; +}; + +CSSOTranslator.prototype.clazz = function(t) { + return '.' + this._t(t[1]); +}; + +CSSOTranslator.prototype.atkeyword = function(t) { + return '@' + this._t(t[1]); +}; + +CSSOTranslator.prototype.shash = function(t) { + return '#' + t[1]; +}; + +CSSOTranslator.prototype.vhash = function(t) { + return '#' + t[1]; +}; + +CSSOTranslator.prototype.attrib = function(t) { + return '[' + this._composite(t) + ']'; +}; + +CSSOTranslator.prototype.important = function(t) { + return '!' + this._composite(t) + 'important'; +}; + +CSSOTranslator.prototype.nthselector = function(t) { + return ':' + this._simple(t[1]) + '(' + this._composite(t, 2) + ')'; +}; + +CSSOTranslator.prototype.funktion = function(t) { + return this._simple(t[1]) + '(' + this._composite(t[2]) + ')'; +}; + +CSSOTranslator.prototype.declaration = function(t) { + return this._t(t[1]) + ':' + this._t(t[2]); +}; + +CSSOTranslator.prototype.filter = function(t) { + return this._t(t[1]) + ':' + this._t(t[2]); +}; + +CSSOTranslator.prototype.block = function(t) { + return '{' + this._composite(t) + '}'; +}; + +CSSOTranslator.prototype.braces = function(t) { + return t[1] + this._composite(t, 3) + t[2]; +}; + +CSSOTranslator.prototype.atrules = function(t) { + return this._composite(t) + ';'; +}; + +CSSOTranslator.prototype.atruler = function(t) { + return this._t(t[1]) + this._t(t[2]) + '{' + this._t(t[3]) + '}'; +}; + +CSSOTranslator.prototype.pseudoe = function(t) { + return '::' + this._t(t[1]); +}; + +CSSOTranslator.prototype.pseudoc = function(t) { + return ':' + this._t(t[1]); +}; + +CSSOTranslator.prototype.uri = function(t) { + return 'url(' + this._composite(t) + ')'; +}; + +CSSOTranslator.prototype.functionExpression = function(t) { + return 'expression(' + t[1] + ')'; +}; diff --git a/dist/htmlminifier.js b/dist/htmlminifier.js index 8a77776..6196dfa 100644 --- a/dist/htmlminifier.js +++ b/dist/htmlminifier.js @@ -737,6 +737,27 @@ } } + function minifyCSS(text) { + try { + var csso; + + if (typeof require === 'function' && (csso = require('csso'))) { + return csso.justDoIt(text); + } + else if (typeof CSSOCompressor !== 'undefined' && + typeof CSSOTranslator !== 'undefined') { + + var compressor = new CSSOCompressor(), + translator = new CSSOTranslator(); + + return translator.translate(cleanInfo(compressor.compress(srcToCSSP(text, 'stylesheet', true)))); + } + } + catch (err) { } + + return text; + } + function minify(value, options) { options = options || {}; @@ -830,9 +851,12 @@ if (options.removeCDATASectionsFromCDATA) { text = removeCDATASections(text); } - if (options.minifyJS) { - text = minifyJS(text); - } + } + if (currentTag === 'script' && options.minifyJS) { + text = minifyJS(text); + } + if (currentTag === 'style' && options.minifyCSS) { + text = minifyCSS(text); } if (options.collapseWhitespace) { if (!stackNoTrimWhitespace.length) { diff --git a/dist/htmlminifier.min.js b/dist/htmlminifier.min.js index 3860470..67d2d95 100644 --- a/dist/htmlminifier.min.js +++ b/dist/htmlminifier.min.js @@ -3,4 +3,4 @@ * Copyright 2010-2014 Juriy "kangax" Zaytsev * Licensed under MIT (https://github.com/kangax/html-minifier/blob/gh-pages/LICENSE) */ -!function(a){"use strict";function b(a){for(var b={},c=a.split(","),d=0;d\s]+))?)*)\s*(\/?)>/,g=/^<\/([\w:-]+)[^>]*>/,h=/\/>$/,i=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g,j=/^]+>/i,k=/<(%|\?)/,l=/(%|\?)>/,m=b("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"),n=b("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"),o=b("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),p=b("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"),q=b("script,style"),r={},s=a.HTMLParser=function(a,b){function s(a,c,d,e){for(var f=!1;!b.html5&&z.last()&&n[z.last()];)t("",z.last());if(o[c]&&z.last()===c&&t("",c),e=m[c]||!!e,e?f=a.match(h):z.push(c),b.start){var g=[];d.replace(i,function(a,b){var c=arguments[2]?arguments[2]:arguments[3]?arguments[3]:arguments[4]?arguments[4]:p[b]?b:"";g.push({name:b,value:c,escaped:c.replace(/(^|[^\\])"/g,"$1"")})}),b.start&&b.start(c,g,e,f)}}function t(a,c){var d;if(c)for(d=z.length-1;d>=0&&z[d]!==c;d--);else d=0;if(d>=0){for(var e=z.length-1;e>=d;e--)b.end&&b.end(z[e]);z.length=d}}var u,v,w,x,y,z=[],A=a;for(z.last=function(){return this[this.length-1]};a;){if(v=!0,z.last()&&q[z.last()])c=z.last().toLowerCase(),d=r[c]||(r[c]=new RegExp("([\\s\\S]*?)]*>","i")),a=a.replace(d,function(a,d){return"script"!==c&&"style"!==c&&(d=d.replace(//g,"$1").replace(//g,"$1")),b.chars&&b.chars(d),""}),t("",c);else if(0===a.indexOf(""),u>=0&&(b.comment&&b.comment(a.substring(4,u)),a=a.substring(u+3),v=!1)):0===a.search(k)?(u=a.search(l),u>=0&&(b.ignore&&b.ignore(a.substring(0,u+2)),a=a.substring(u+2),v=!1)):(w=j.exec(a))?(b.doctype&&b.doctype(w[0]),a=a.substring(w[0].length),v=!1):0===a.indexOf("u?a:a.substring(0,u);a=0>u?"":a.substring(u),e=a.match(f),e?y=e[1]:(e=a.match(g),y=e?"/"+e[1]:""),b.chars&&b.chars(B,x,y)}if(a===A)throw"Parse Error: "+a;A=a}t()};a.HTMLtoXML=function(a){var b="";return new s(a,{start:function(a,c,d){b+="<"+a;for(var e=0;e"},end:function(a){b+=""},chars:function(a){b+=a},comment:function(a){b+=""},ignore:function(a){b+=a}}),b},a.HTMLtoDOM=function(a,c){var d=b("html,head,body,title"),e={link:"head",base:"head"};c?c=c.ownerDocument||c.getOwnerDocument&&c.getOwnerDocument()||c:"undefined"!=typeof DOMDocument?c=new DOMDocument:"undefined"!=typeof document&&document.implementation&&document.implementation.createDocument?c=document.implementation.createDocument("","",null):"undefined"!=typeof ActiveX&&(c=new ActiveXObject("Msxml.DOMDocument"));var f=[],g=c.documentElement||c.getDocumentElement&&c.getDocumentElement();if(!g&&c.createElement&&!function(){var a=c.createElement("html"),b=c.createElement("head");b.appendChild(c.createElement("title")),a.appendChild(b),a.appendChild(c.createElement("body")),c.appendChild(a)}(),c.getElementsByTagName)for(var h in d)d[h]=c.getElementsByTagName(h)[0];var i=d.body;return new s(a,{start:function(a,b,g){if(d[a])return void(i=d[a]);var h=c.createElement(a);for(var j in b)h.setAttribute(b[j].name,b[j].value);e[a]&&"boolean"!=typeof d[e[a]]?d[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(c.createTextNode(a))},comment:function(){},ignore:function(){}}),c}}("undefined"==typeof exports?this:exports),function(a){"use strict";function b(a){return a.replace(/\s+/g," ")}function c(a,b,c){var d=["a","abbr","acronym","b","bdi","bdo","big","button","cite","code","del","dfn","em","font","i","ins","kbd","mark","q","rt","rp","s","samp","small","span","strike","strong","sub","sup","time","tt","u","var"];return b&&"img"!==b&&("/"!==b.substr(0,1)||"/"===b.substr(0,1)&&-1===d.indexOf(b.substr(1)))&&(a=a.replace(/^\s+/,"")),c&&"img"!==c&&("/"===c.substr(0,1)||"/"!==c.substr(0,1)&&-1===d.indexOf(c))&&(a=a.replace(/\s+$/,"")),b&&c?a.replace(/[\t\n\r]+/g," ").replace(/[ ]+/g," "):a}function d(a){return/\[if[^\]]+\]/.test(a)||/\s*(]+$/.test(a)&&!/\/$/.test(a)&&!/\/$/.test(a)}function h(a,b){for(var c=a.length;c--;)if(a[c].name.toLowerCase()===b)return!0;return!1}function i(a,b,c,d){return c=D(c.toLowerCase()),"script"===a&&"language"===b&&"javascript"===c||"form"===a&&"method"===b&&"get"===c||"input"===a&&"type"===b&&"text"===c||"script"===a&&"charset"===b&&!h(d,"src")||"a"===a&&"name"===b&&h(d,"id")||"area"===a&&"shape"===b&&"rect"===c}function j(a,b,c){return"script"===a&&"type"===b&&"text/javascript"===D(c.toLowerCase())}function k(a,b,c){return("style"===a||"link"===a)&&"type"===b&&"text/css"===D(c.toLowerCase())}function l(a){return/^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|draggable|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|spellcheck|translate|truespeed|typemustmatch|visible)$/.test(a)}function m(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 n(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 o(a,c,d){return f(c)?D(d).replace(/^javascript:\s*/i,"").replace(/\s*;$/,""):"class"===c?b(D(d)):m(c,a)||n(c,a)?D(d):"style"===c?D(d).replace(/\s*;\s*$/,""):d}function p(a){return a.replace(/^(\[[^\]]+\]>)\s*/,"$1").replace(/\s*(\s*\*\/|\/\/\s*\]\]>)\s*$/,"")}function r(a,b){return a.replace(E[b],"").replace(F[b],"")}function s(a){return/^(?:html|t?body|t?head|tfoot|tr|td|th|dt|dd|option|colgroup|source)$/.test(a)}function t(a,b,c){var d=/^(["'])?\s*\1$/.test(c);return d?"input"===a&&"value"===b||G.test(b):!1}function u(a){return"textarea"!==a}function v(a){return!/^(?:script|style|pre|textarea)$/.test(a)}function w(a){return!/^(?:pre|textarea)$/.test(a)}function x(a,b,c,d){var e,f=d.caseSensitive?a.name:a.name.toLowerCase(),h=a.escaped;return d.removeRedundantAttributes&&i(c,f,h,b)||d.removeScriptTypeAttributes&&j(c,f,h)||d.removeStyleLinkTypeAttributes&&k(c,f,h)?"":(h=o(c,f,h),d.removeAttributeQuotes&&g(h)||(h='"'+h+'"'),d.removeEmptyAttributes&&t(c,f,h)?"":(e=d.collapseBooleanAttributes&&l(f)?f:f+"="+h," "+e))}function y(a){for(var b=["canCollapseWhitespace","canTrimWhitespace"],c=0,d=b.length;d>c;c++)a[b[c]]||(a[b[c]]=function(){return!1})}function z(b){try{var c=a.UglifyJS;if("undefined"==typeof c&&"function"==typeof require&&(c=require("uglify-js")),!c)return b;if(c.minify)return c.minify(b,{fromString:!0}).code;if(c.parse){var d=c.parse(b);d.figure_out_scope();var e=c.Compressor(),f=d.transform(e);f.figure_out_scope(),f.compute_char_frequency(),f.mangle_names();var g=c.OutputStream();return f.print(g),g.toString()}return b}catch(h){return console.log(h),b}}function A(a,f){function g(a,b){return v(a)||f.canCollapseWhitespace(a,b)}function h(a,b){return w(a)||f.canTrimWhitespace(a,b)}f=f||{},a=D(a),y(f);var i=[],j=[],k="",l="",m=[],n=[],o=[],t=f.lint,A=new Date;new C(a,{html5:"undefined"!=typeof f.html5?f.html5:!0,start:function(a,b,c,d){a=a.toLowerCase(),l=a,k="",m=b,f.collapseWhitespace&&(h(a,b)||n.push(a),g(a,b)||o.push(a)),j.push("<",a),t&&t.testElement(a);for(var e=0,i=b.length;i>e;e++)t&&t.testAttribute(a,b[e].name.toLowerCase(),b[e].escaped),j.push(x(b[e],b,a,f));j.push((d&&f.keepClosingSlash?"/":"")+">")},end:function(a){f.collapseWhitespace&&(n.length&&a===n[n.length-1]&&n.pop(),o.length&&a===o[o.length-1]&&o.pop());var b=""===k&&a===l;return f.removeEmptyElements&&b&&u(a)?void j.splice(j.lastIndexOf("<")):void(f.removeOptionalTags&&s(a)||(j.push(""),i.push.apply(i,j),j.length=0,k=""))},chars:function(a,d,e){("script"===l||"style"===l)&&(f.removeCommentsFromCDATA&&(a=r(a,l)),f.removeCDATASectionsFromCDATA&&(a=q(a)),f.minifyJS&&(a=z(a))),f.collapseWhitespace&&(n.length||(a=d||e?c(a,d,e):D(a)),o.length||(a=b(a))),k=a,t&&t.testChars(a),j.push(a)},comment:function(a){a=f.removeComments?d(a)?"":e(a)?"":"":"",j.push(a)},ignore:function(a){j.push(f.removeIgnored?"":a)},doctype:function(a){j.push(f.useShortDoctype?"":b(a))}}),i.push.apply(i,j);var E=i.join("");return B("minified in: "+(new Date-A)+"ms"),E}var B,C;B=a.console&&a.console.log?function(b){a.console.log(b)}:function(){},a.HTMLParser?C=a.HTMLParser:"function"==typeof require&&(C=require("./htmlparser").HTMLParser);var D=function(a){return a.replace(/^\s+/,"").replace(/\s+$/,"")};String.prototype.trim&&(D=function(a){return a.trim()});var E={script:/^\s*(?:\/\/)?\s*\s*$/,style:/\s*-->\s*$/},G=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=A:a.minify=A}(this),function(a){"use strict";function b(a){return/^(?:b|i|big|small|hr|blink|marquee)$/.test(a)}function c(a){return/^(?:applet|basefont|center|dir|font|isindex|s|strike|u)$/.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 deprecated <'+a+"> element"):b(a)?this.log.push('Found presentational <'+a+"> 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 <br> sequence. Try replacing it with styling.")},h.prototype.testAttribute=function(a,b,c){d(b)?this.log.push('Found event attribute ('+b+") on <"+a+"> element."):f(a,b)?this.log.push('Found deprecated '+b+" attribute on <"+a+"> element."):e(b)?this.log.push('Found style attribute on <'+a+"> element."):g(b,c)&&this.log.push('Found inaccessible attribute (on <'+a+"> element).")},h.prototype.testChars=function(a){this._lastElement="",/( \s*){2,}/.test(a)&&this.log.push("Found repeating &nbsp; 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="
  1. "+this.log.join("
  2. ")+"
";else{var b=" - "+this.log.join("\n - ").replace(/(<([^>]+)>)/gi,"").replace(/</g,"<").replace(/>/g,">");console.log(b)}},a.HTMLLint=h}("undefined"==typeof exports?this:exports); \ No newline at end of file +!function(a){"use strict";function b(a){for(var b={},c=a.split(","),d=0;d\s]+))?)*)\s*(\/?)>/,g=/^<\/([\w:-]+)[^>]*>/,h=/\/>$/,i=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g,j=/^]+>/i,k=/<(%|\?)/,l=/(%|\?)>/,m=b("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"),n=b("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"),o=b("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),p=b("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"),q=b("script,style"),r={},s=a.HTMLParser=function(a,b){function s(a,c,d,e){for(var f=!1;!b.html5&&z.last()&&n[z.last()];)t("",z.last());if(o[c]&&z.last()===c&&t("",c),e=m[c]||!!e,e?f=a.match(h):z.push(c),b.start){var g=[];d.replace(i,function(a,b){var c=arguments[2]?arguments[2]:arguments[3]?arguments[3]:arguments[4]?arguments[4]:p[b]?b:"";g.push({name:b,value:c,escaped:c.replace(/(^|[^\\])"/g,"$1"")})}),b.start&&b.start(c,g,e,f)}}function t(a,c){var d;if(c)for(d=z.length-1;d>=0&&z[d]!==c;d--);else d=0;if(d>=0){for(var e=z.length-1;e>=d;e--)b.end&&b.end(z[e]);z.length=d}}var u,v,w,x,y,z=[],A=a;for(z.last=function(){return this[this.length-1]};a;){if(v=!0,z.last()&&q[z.last()])c=z.last().toLowerCase(),d=r[c]||(r[c]=new RegExp("([\\s\\S]*?)]*>","i")),a=a.replace(d,function(a,d){return"script"!==c&&"style"!==c&&(d=d.replace(//g,"$1").replace(//g,"$1")),b.chars&&b.chars(d),""}),t("",c);else if(0===a.indexOf(""),u>=0&&(b.comment&&b.comment(a.substring(4,u)),a=a.substring(u+3),v=!1)):0===a.search(k)?(u=a.search(l),u>=0&&(b.ignore&&b.ignore(a.substring(0,u+2)),a=a.substring(u+2),v=!1)):(w=j.exec(a))?(b.doctype&&b.doctype(w[0]),a=a.substring(w[0].length),v=!1):0===a.indexOf("u?a:a.substring(0,u);a=0>u?"":a.substring(u),e=a.match(f),e?y=e[1]:(e=a.match(g),y=e?"/"+e[1]:""),b.chars&&b.chars(B,x,y)}if(a===A)throw"Parse Error: "+a;A=a}t()};a.HTMLtoXML=function(a){var b="";return new s(a,{start:function(a,c,d){b+="<"+a;for(var e=0;e"},end:function(a){b+=""},chars:function(a){b+=a},comment:function(a){b+=""},ignore:function(a){b+=a}}),b},a.HTMLtoDOM=function(a,c){var d=b("html,head,body,title"),e={link:"head",base:"head"};c?c=c.ownerDocument||c.getOwnerDocument&&c.getOwnerDocument()||c:"undefined"!=typeof DOMDocument?c=new DOMDocument:"undefined"!=typeof document&&document.implementation&&document.implementation.createDocument?c=document.implementation.createDocument("","",null):"undefined"!=typeof ActiveX&&(c=new ActiveXObject("Msxml.DOMDocument"));var f=[],g=c.documentElement||c.getDocumentElement&&c.getDocumentElement();if(!g&&c.createElement&&!function(){var a=c.createElement("html"),b=c.createElement("head");b.appendChild(c.createElement("title")),a.appendChild(b),a.appendChild(c.createElement("body")),c.appendChild(a)}(),c.getElementsByTagName)for(var h in d)d[h]=c.getElementsByTagName(h)[0];var i=d.body;return new s(a,{start:function(a,b,g){if(d[a])return void(i=d[a]);var h=c.createElement(a);for(var j in b)h.setAttribute(b[j].name,b[j].value);e[a]&&"boolean"!=typeof d[e[a]]?d[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(c.createTextNode(a))},comment:function(){},ignore:function(){}}),c}}("undefined"==typeof exports?this:exports),function(a){"use strict";function b(a){return a.replace(/\s+/g," ")}function c(a,b,c){var d=["a","abbr","acronym","b","bdi","bdo","big","button","cite","code","del","dfn","em","font","i","ins","kbd","mark","q","rt","rp","s","samp","small","span","strike","strong","sub","sup","time","tt","u","var"];return b&&"img"!==b&&("/"!==b.substr(0,1)||"/"===b.substr(0,1)&&-1===d.indexOf(b.substr(1)))&&(a=a.replace(/^\s+/,"")),c&&"img"!==c&&("/"===c.substr(0,1)||"/"!==c.substr(0,1)&&-1===d.indexOf(c))&&(a=a.replace(/\s+$/,"")),b&&c?a.replace(/[\t\n\r]+/g," ").replace(/[ ]+/g," "):a}function d(a){return/\[if[^\]]+\]/.test(a)||/\s*(]+$/.test(a)&&!/\/$/.test(a)&&!/\/$/.test(a)}function h(a,b){for(var c=a.length;c--;)if(a[c].name.toLowerCase()===b)return!0;return!1}function i(a,b,c,d){return c=E(c.toLowerCase()),"script"===a&&"language"===b&&"javascript"===c||"form"===a&&"method"===b&&"get"===c||"input"===a&&"type"===b&&"text"===c||"script"===a&&"charset"===b&&!h(d,"src")||"a"===a&&"name"===b&&h(d,"id")||"area"===a&&"shape"===b&&"rect"===c}function j(a,b,c){return"script"===a&&"type"===b&&"text/javascript"===E(c.toLowerCase())}function k(a,b,c){return("style"===a||"link"===a)&&"type"===b&&"text/css"===E(c.toLowerCase())}function l(a){return/^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|draggable|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|spellcheck|translate|truespeed|typemustmatch|visible)$/.test(a)}function m(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 n(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 o(a,c,d){return f(c)?E(d).replace(/^javascript:\s*/i,"").replace(/\s*;$/,""):"class"===c?b(E(d)):m(c,a)||n(c,a)?E(d):"style"===c?E(d).replace(/\s*;\s*$/,""):d}function p(a){return a.replace(/^(\[[^\]]+\]>)\s*/,"$1").replace(/\s*(\s*\*\/|\/\/\s*\]\]>)\s*$/,"")}function r(a,b){return a.replace(F[b],"").replace(G[b],"")}function s(a){return/^(?:html|t?body|t?head|tfoot|tr|td|th|dt|dd|option|colgroup|source)$/.test(a)}function t(a,b,c){var d=/^(["'])?\s*\1$/.test(c);return d?"input"===a&&"value"===b||H.test(b):!1}function u(a){return"textarea"!==a}function v(a){return!/^(?:script|style|pre|textarea)$/.test(a)}function w(a){return!/^(?:pre|textarea)$/.test(a)}function x(a,b,c,d){var e,f=d.caseSensitive?a.name:a.name.toLowerCase(),h=a.escaped;return d.removeRedundantAttributes&&i(c,f,h,b)||d.removeScriptTypeAttributes&&j(c,f,h)||d.removeStyleLinkTypeAttributes&&k(c,f,h)?"":(h=o(c,f,h),d.removeAttributeQuotes&&g(h)||(h='"'+h+'"'),d.removeEmptyAttributes&&t(c,f,h)?"":(e=d.collapseBooleanAttributes&&l(f)?f:f+"="+h," "+e))}function y(a){for(var b=["canCollapseWhitespace","canTrimWhitespace"],c=0,d=b.length;d>c;c++)a[b[c]]||(a[b[c]]=function(){return!1})}function z(b){try{var c=a.UglifyJS;if("undefined"==typeof c&&"function"==typeof require&&(c=require("uglify-js")),!c)return b;if(c.minify)return c.minify(b,{fromString:!0}).code;if(c.parse){var d=c.parse(b);d.figure_out_scope();var e=c.Compressor(),f=d.transform(e);f.figure_out_scope(),f.compute_char_frequency(),f.mangle_names();var g=c.OutputStream();return f.print(g),g.toString()}return b}catch(h){return console.log(h),b}}function A(a){try{var b;if("function"==typeof require&&(b=require("csso")))return b.justDoIt(a);if("undefined"!=typeof CSSOCompressor&&"undefined"!=typeof CSSOTranslator){var c=new CSSOCompressor,d=new CSSOTranslator;return d.translate(cleanInfo(c.compress(srcToCSSP(a,"stylesheet",!0))))}}catch(e){}return a}function B(a,f){function g(a,b){return v(a)||f.canCollapseWhitespace(a,b)}function h(a,b){return w(a)||f.canTrimWhitespace(a,b)}f=f||{},a=E(a),y(f);var i=[],j=[],k="",l="",m=[],n=[],o=[],t=f.lint,B=new Date;new D(a,{html5:"undefined"!=typeof f.html5?f.html5:!0,start:function(a,b,c,d){a=a.toLowerCase(),l=a,k="",m=b,f.collapseWhitespace&&(h(a,b)||n.push(a),g(a,b)||o.push(a)),j.push("<",a),t&&t.testElement(a);for(var e=0,i=b.length;i>e;e++)t&&t.testAttribute(a,b[e].name.toLowerCase(),b[e].escaped),j.push(x(b[e],b,a,f));j.push((d&&f.keepClosingSlash?"/":"")+">")},end:function(a){f.collapseWhitespace&&(n.length&&a===n[n.length-1]&&n.pop(),o.length&&a===o[o.length-1]&&o.pop());var b=""===k&&a===l;return f.removeEmptyElements&&b&&u(a)?void j.splice(j.lastIndexOf("<")):void(f.removeOptionalTags&&s(a)||(j.push(""),i.push.apply(i,j),j.length=0,k=""))},chars:function(a,d,e){("script"===l||"style"===l)&&(f.removeCommentsFromCDATA&&(a=r(a,l)),f.removeCDATASectionsFromCDATA&&(a=q(a))),"script"===l&&f.minifyJS&&(a=z(a)),"style"===l&&f.minifyCSS&&(a=A(a)),f.collapseWhitespace&&(n.length||(a=d||e?c(a,d,e):E(a)),o.length||(a=b(a))),k=a,t&&t.testChars(a),j.push(a)},comment:function(a){a=f.removeComments?d(a)?"":e(a)?"":"":"",j.push(a)},ignore:function(a){j.push(f.removeIgnored?"":a)},doctype:function(a){j.push(f.useShortDoctype?"":b(a))}}),i.push.apply(i,j);var F=i.join("");return C("minified in: "+(new Date-B)+"ms"),F}var C,D;C=a.console&&a.console.log?function(b){a.console.log(b)}:function(){},a.HTMLParser?D=a.HTMLParser:"function"==typeof require&&(D=require("./htmlparser").HTMLParser);var E=function(a){return a.replace(/^\s+/,"").replace(/\s+$/,"")};String.prototype.trim&&(E=function(a){return a.trim()});var F={script:/^\s*(?:\/\/)?\s*\s*$/,style:/\s*-->\s*$/},H=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=B:a.minify=B}(this),function(a){"use strict";function b(a){return/^(?:b|i|big|small|hr|blink|marquee)$/.test(a)}function c(a){return/^(?:applet|basefont|center|dir|font|isindex|s|strike|u)$/.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 deprecated <'+a+"> element"):b(a)?this.log.push('Found presentational <'+a+"> 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 <br> sequence. Try replacing it with styling.")},h.prototype.testAttribute=function(a,b,c){d(b)?this.log.push('Found event attribute ('+b+") on <"+a+"> element."):f(a,b)?this.log.push('Found deprecated '+b+" attribute on <"+a+"> element."):e(b)?this.log.push('Found style attribute on <'+a+"> element."):g(b,c)&&this.log.push('Found inaccessible attribute (on <'+a+"> element).")},h.prototype.testChars=function(a){this._lastElement="",/( \s*){2,}/.test(a)&&this.log.push("Found repeating &nbsp; 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="
  1. "+this.log.join("
  2. ")+"
";else{var b=" - "+this.log.join("\n - ").replace(/(<([^>]+)>)/gi,"").replace(/</g,"<").replace(/>/g,">");console.log(b)}},a.HTMLLint=h}("undefined"==typeof exports?this:exports); \ No newline at end of file diff --git a/index.html b/index.html index 5124e3a..52a97b7 100644 --- a/index.html +++ b/index.html @@ -174,6 +174,12 @@ Minify JS +
  • + + +