diff --git a/beautify/beautify-css.js b/beautify/beautify-css.js new file mode 100644 index 00000000..5e45c2b6 --- /dev/null +++ b/beautify/beautify-css.js @@ -0,0 +1,457 @@ +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* + + The MIT License (MIT) + + Copyright (c) 2007-2013 Einar Lielmanis and contributors. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + CSS Beautifier +--------------- + + Written by Harutyun Amirjanyan, (amirjanyan@gmail.com) + + Based on code initially developed by: Einar Lielmanis, + http://jsbeautifier.org/ + + Usage: + css_beautify(source_text); + css_beautify(source_text, options); + + The options are (default in brackets): + indent_size (4) — indentation size, + indent_char (space) — character to indent with, + selector_separator_newline (true) - separate selectors with newline or + not (e.g. "a,\nbr" or "a, br") + end_with_newline (false) - end with a newline + newline_between_rules (true) - add a new line after every css rule + + e.g + + css_beautify(css_source_text, { + 'indent_size': 1, + 'indent_char': '\t', + 'selector_separator': ' ', + 'end_with_newline': false, + 'newline_between_rules': true + }); +*/ + +// http://www.w3.org/TR/CSS21/syndata.html#tokenization +// http://www.w3.org/TR/css3-syntax/ + +(function() { + function css_beautify(source_text, options) { + options = options || {}; + var indentSize = options.indent_size || 4; + var indentCharacter = options.indent_char || ' '; + var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; + var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; + var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules; + + // compatibility + if (typeof indentSize === "string") { + indentSize = parseInt(indentSize, 10); + } + + + // tokenizer + var whiteRe = /^\s+$/; + var wordRe = /[\w$\-_]/; + + var pos = -1, + ch; + var parenLevel = 0; + + function next() { + ch = source_text.charAt(++pos); + return ch || ''; + } + + function peek(skipWhitespace) { + var prev_pos = pos; + if (skipWhitespace) { + eatWhitespace(); + } + result = source_text.charAt(pos + 1) || ''; + pos = prev_pos - 1; + next(); + return result; + } + + function eatString(endChars) { + var start = pos; + while (next()) { + if (ch === "\\") { + next(); + } else if (endChars.indexOf(ch) !== -1) { + break; + } else if (ch === "\n") { + break; + } + } + return source_text.substring(start, pos + 1); + } + + function peekString(endChar) { + var prev_pos = pos; + var str = eatString(endChar); + pos = prev_pos - 1; + next(); + return str; + } + + function eatWhitespace() { + var result = ''; + while (whiteRe.test(peek())) { + next(); + result += ch; + } + return result; + } + + function skipWhitespace() { + var result = ''; + if (ch && whiteRe.test(ch)) { + result = ch; + } + while (whiteRe.test(next())) { + result += ch; + } + return result; + } + + function eatComment(singleLine) { + var start = pos; + singleLine = peek() === "/"; + next(); + while (next()) { + if (!singleLine && ch === "*" && peek() === "/") { + next(); + break; + } else if (singleLine && ch === "\n") { + return source_text.substring(start, pos); + } + } + + return source_text.substring(start, pos) + ch; + } + + + function lookBack(str) { + return source_text.substring(pos - str.length, pos).toLowerCase() === + str; + } + + // Nested pseudo-class if we are insideRule + // and the next special character found opens + // a new block + function foundNestedPseudoClass() { + for (var i = pos + 1; i < source_text.length; i++) { + var ch = source_text.charAt(i); + if (ch === "{") { + return true; + } else if (ch === ";" || ch === "}" || ch === ")") { + return false; + } + } + return false; + } + + // printer + var basebaseIndentString = source_text.match(/^[\t ]*/)[0]; + var singleIndent = new Array(indentSize + 1).join(indentCharacter); + var indentLevel = 0; + var nestedLevel = 0; + + function indent() { + indentLevel++; + basebaseIndentString += singleIndent; + } + + function outdent() { + indentLevel--; + basebaseIndentString = basebaseIndentString.slice(0, -indentSize); + } + + var print = {}; + print["{"] = function(ch) { + print.singleSpace(); + output.push(ch); + print.newLine(); + }; + print["}"] = function(ch) { + print.newLine(); + output.push(ch); + print.newLine(); + }; + + print._lastCharWhitespace = function() { + return whiteRe.test(output[output.length - 1]); + }; + + print.newLine = function(keepWhitespace) { + if (!keepWhitespace) { + print.trim(); + } + + if (output.length) { + output.push('\n'); + } + if (basebaseIndentString) { + output.push(basebaseIndentString); + } + }; + print.singleSpace = function() { + if (output.length && !print._lastCharWhitespace()) { + output.push(' '); + } + }; + + print.trim = function() { + while (print._lastCharWhitespace()) { + output.pop(); + } + }; + + + var output = []; + if (basebaseIndentString) { + output.push(basebaseIndentString); + } + /*_____________________--------------------_____________________*/ + + var insideRule = false; + var enteringConditionalGroup = false; + var top_ch = ''; + var last_top_ch = ''; + + while (true) { + var whitespace = skipWhitespace(); + var isAfterSpace = whitespace !== ''; + var isAfterNewline = whitespace.indexOf('\n') !== -1; + last_top_ch = top_ch; + top_ch = ch; + + if (!ch) { + break; + } else if (ch === '/' && peek() === '*') { /* css comment */ + var header = lookBack(""); + print.newLine(); + output.push(eatComment()); + print.newLine(); + if (header) { + print.newLine(true); + } + } else if (ch === '/' && peek() === '/') { // single line comment + if (!isAfterNewline && last_top_ch !== '{') { + print.trim(); + } + print.singleSpace(); + output.push(eatComment()); + print.newLine(); + } else if (ch === '@') { + // pass along the space we found as a separate item + if (isAfterSpace) { + print.singleSpace(); + } + output.push(ch); + + // strip trailing space, if present, for hash property checks + var variableOrRule = peekString(": ,;{}()[]/='\""); + + if (variableOrRule.match(/[ :]$/)) { + // we have a variable or pseudo-class, add it and insert one space before continuing + next(); + variableOrRule = eatString(": ").replace(/\s$/, ''); + output.push(variableOrRule); + print.singleSpace(); + } + + variableOrRule = variableOrRule.replace(/\s$/, '') + + // might be a nesting at-rule + if (variableOrRule in css_beautify.NESTED_AT_RULE) { + nestedLevel += 1; + if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) { + enteringConditionalGroup = true; + } + } + } else if (ch === '{') { + if (peek(true) === '}') { + eatWhitespace(); + next(); + print.singleSpace(); + output.push("{}"); + print.newLine(); + if (newline_between_rules && indentLevel === 0) { + print.newLine(true); + } + } else { + indent(); + print["{"](ch); + // when entering conditional groups, only rulesets are allowed + if (enteringConditionalGroup) { + enteringConditionalGroup = false; + insideRule = (indentLevel > nestedLevel); + } else { + // otherwise, declarations are also allowed + insideRule = (indentLevel >= nestedLevel); + } + } + } else if (ch === '}') { + outdent(); + print["}"](ch); + insideRule = false; + if (nestedLevel) { + nestedLevel--; + } + if (newline_between_rules && indentLevel === 0) { + print.newLine(true); + } + } else if (ch === ":") { + eatWhitespace(); + if ((insideRule || enteringConditionalGroup) && + !(lookBack("&") || foundNestedPseudoClass())) { + // 'property: value' delimiter + // which could be in a conditional group query + output.push(':'); + print.singleSpace(); + } else { + // sass/less parent reference don't use a space + // sass nested pseudo-class don't use a space + if (peek() === ":") { + // pseudo-element + next(); + output.push("::"); + } else { + // pseudo-class + output.push(':'); + } + } + } else if (ch === '"' || ch === '\'') { + if (isAfterSpace) { + print.singleSpace(); + } + output.push(eatString(ch)); + } else if (ch === ';') { + output.push(ch); + print.newLine(); + } else if (ch === '(') { // may be a url + if (lookBack("url")) { + output.push(ch); + eatWhitespace(); + if (next()) { + if (ch !== ')' && ch !== '"' && ch !== '\'') { + output.push(eatString(')')); + } else { + pos--; + } + } + } else { + parenLevel++; + if (isAfterSpace) { + print.singleSpace(); + } + output.push(ch); + eatWhitespace(); + } + } else if (ch === ')') { + output.push(ch); + parenLevel--; + } else if (ch === ',') { + output.push(ch); + eatWhitespace(); + if (!insideRule && selectorSeparatorNewline && parenLevel < 1) { + print.newLine(); + } else { + print.singleSpace(); + } + } else if (ch === ']') { + output.push(ch); + } else if (ch === '[') { + if (isAfterSpace) { + print.singleSpace(); + } + output.push(ch); + } else if (ch === '=') { // no whitespace before or after + eatWhitespace() + ch = '='; + output.push(ch); + } else { + if (isAfterSpace) { + print.singleSpace(); + } + + output.push(ch); + } + } + + + var sweetCode = output.join('').replace(/[\r\n\t ]+$/, ''); + + // establish end_with_newline + if (end_with_newline) { + sweetCode += "\n"; + } + + return sweetCode; + } + + // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule + css_beautify.NESTED_AT_RULE = { + "@page": true, + "@font-face": true, + "@keyframes": true, + // also in CONDITIONAL_GROUP_RULE below + "@media": true, + "@supports": true, + "@document": true + }; + css_beautify.CONDITIONAL_GROUP_RULE = { + "@media": true, + "@supports": true, + "@document": true + }; + + /*global define */ + if (typeof define === "function" && define.amd) { + // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) + define([], function() { + return { + css_beautify: css_beautify + }; + }); + } else if (typeof exports !== "undefined") { + // Add support for CommonJS. Just put this file somewhere on your require.paths + // and you will be able to `var html_beautify = require("beautify").html_beautify`. + exports.css_beautify = css_beautify; + } else if (typeof window !== "undefined") { + // If we're running a web page and don't have either of the above, add our one global + window.css_beautify = css_beautify; + } else if (typeof global !== "undefined") { + // If we don't even have window, try global. + global.css_beautify = css_beautify; + } + +}());