| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- /*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, <einar@jsbeautifier.org>
- 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 || {};
- source_text = source_text || '';
- // HACK: newline parsing inconsistent. This brute force normalizes the input.
- source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
- 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;
- var eol = options.eol ? options.eol : '\n';
- // compatibility
- if (typeof indentSize === "string") {
- indentSize = parseInt(indentSize, 10);
- }
- if(options.indent_with_tabs){
- indentCharacter = '\t';
- indentSize = 1;
- }
- eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
- // 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 result = '';
- 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() {
- var openParen = 0;
- for (var i = pos + 1; i < source_text.length; i++) {
- var ch = source_text.charAt(i);
- if (ch === "{") {
- return true;
- } else if (ch === '(') {
- // pseudoclasses can contain ()
- openParen += 1;
- } else if (ch === ')') {
- if (openParen == 0) {
- return false;
- }
- openParen -= 1;
- } else if (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 (output.length) {
- if (!keepWhitespace && output[output.length - 1] !== '\n') {
- print.trim();
- }
- output.push('\n');
- if (basebaseIndentString) {
- output.push(basebaseIndentString);
- }
- }
- };
- print.singleSpace = function() {
- if (output.length && !print._lastCharWhitespace()) {
- output.push(' ');
- }
- };
- print.preserveSingleSpace = function() {
- if (isAfterSpace) {
- print.singleSpace();
- }
- };
- print.trim = function() {
- while (print._lastCharWhitespace()) {
- output.pop();
- }
- };
- var output = [];
- /*_____________________--------------------_____________________*/
- var insideRule = false;
- var insidePropertyValue = 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 = indentLevel === 0;
- if (isAfterNewline || header) {
- 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 === '@') {
- print.preserveSingleSpace();
- 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 === '#' && peek() === '{') {
- print.preserveSingleSpace();
- output.push(eatString('}'));
- } 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;
- insidePropertyValue = 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
- insidePropertyValue = true;
- 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 === '\'') {
- print.preserveSingleSpace();
- output.push(eatString(ch));
- } else if (ch === ';') {
- insidePropertyValue = false;
- 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++;
- print.preserveSingleSpace();
- output.push(ch);
- eatWhitespace();
- }
- } else if (ch === ')') {
- output.push(ch);
- parenLevel--;
- } else if (ch === ',') {
- output.push(ch);
- eatWhitespace();
- if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
- print.newLine();
- } else {
- print.singleSpace();
- }
- } else if (ch === ']') {
- output.push(ch);
- } else if (ch === '[') {
- print.preserveSingleSpace();
- output.push(ch);
- } else if (ch === '=') { // no whitespace before or after
- eatWhitespace()
- ch = '=';
- output.push(ch);
- } else {
- print.preserveSingleSpace();
- output.push(ch);
- }
- }
- var sweetCode = '';
- if (basebaseIndentString) {
- sweetCode += basebaseIndentString;
- }
- sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');
- // establish end_with_newline
- if (end_with_newline) {
- sweetCode += '\n';
- }
- if (eol != '\n') {
- sweetCode = sweetCode.replace(/[\n]/g, eol);
- }
- 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;
- }
- }());
|