JAVASCRIPT   16

cssbeautify.js

Guest on 5th June 2021 05:28:14 PM

  1. /*
  2.  Copyright (C) Sencha Inc.
  3.  Copyright (C)  Sencha Inc.
  4.  
  5.  Author: Ariya Hidayat.
  6.  
  7.  Permission is hereby granted, free of charge, to any person obtaining a copy
  8.  of this software and associated documentation files (the "Software"), to deal
  9.  in the Software without restriction, including without limitation the rights
  10.  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11.  copies of the Software, and to permit persons to whom the Software is
  12.  furnished to do so, subject to the following conditions:
  13.  
  14.  The above copyright notice and this permission notice shall be included in
  15.  all copies or substantial portions of the Software.
  16.  
  17.  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18.  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19.  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20.  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21.  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22.  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23.  THE SOFTWARE.
  24. */
  25.  
  26. /*jslint continue: true, indent: 4 */
  27. /*global exports:true, module:true, window:true */
  28.  
  29. (function () {
  30.  
  31.     'use strict';
  32.  
  33.     function cssbeautify(style, opt) {
  34.  
  35.         var options, index = 0, length = style.length, blocks, formatted = '',
  36.             ch, ch2, str, state, State, depth, quote, comment,
  37.             openbracesuffix = true,
  38.             autosemicolon = false,
  39.             trimRight;
  40.  
  41.         options = arguments.length > 1 ? opt : {};
  42.         if (typeof options.indent === 'undefined') {
  43.             options.indent = '    ';
  44.         }
  45.         if (typeof options.openbrace === 'string') {
  46.             openbracesuffix = (options.openbrace === 'end-of-line');
  47.         }
  48.         if (typeof options.autosemicolon === 'boolean') {
  49.             autosemicolon = options.autosemicolon;
  50.         }
  51.  
  52.         function isWhitespace(c) {
  53.             return (c === ' ') || (c === '\n') || (c === '\t') || (c === '\r') || (c === '\f');
  54.         }
  55.  
  56.         function isQuote(c) {
  57.             return (c === '\'') || (c === '"');
  58.         }
  59.  
  60.         // FIXME: handle Unicode characters
  61.         function isName(c) {
  62.             return (ch >= 'a' && ch <= 'z') ||
  63.                 (ch >= 'A' && ch <= 'Z') ||
  64.                 (ch >= '0' && ch <= '9') ||
  65.                 '-_*.:#'.indexOf(c) >= 0;
  66.         }
  67.  
  68.         function appendIndent() {
  69.             var i;
  70.             for (i = depth; i > 0; i -= 1) {
  71.                 formatted += options.indent;
  72.             }
  73.         }
  74.  
  75.         function openBlock() {
  76.             formatted = trimRight(formatted);
  77.             if (openbracesuffix) {
  78.                 formatted += ' {';
  79.             } else {
  80.                 formatted += '\n';
  81.                 appendIndent();
  82.                 formatted += '{';
  83.             }
  84.             if (ch2 !== '\n') {
  85.                 formatted += '\n';
  86.             }
  87.             depth += 1;
  88.         }
  89.  
  90.         function closeBlock() {
  91.             depth -= 1;
  92.             formatted = trimRight(formatted);
  93.  
  94.             if (autosemicolon) {
  95.                 if (formatted.charAt(formatted.length - 1) !== ';') {
  96.                     formatted += ';';
  97.                 }
  98.             }
  99.  
  100.             formatted += '\n';
  101.             appendIndent();
  102.             formatted += '}';
  103.             blocks.push(formatted);
  104.             formatted = '';
  105.         }
  106.  
  107.         if (String.prototype.trimRight) {
  108.             trimRight = function (s) {
  109.                 return s.trimRight();
  110.             };
  111.         } else {
  112.             // old Internet Explorer
  113.             trimRight = function (s) {
  114.                 return s.replace(/\s+$/, '');
  115.             };
  116.         }
  117.  
  118.         State = {
  119.             Start: 0,
  120.             AtRule: 1,
  121.             Block: 2,
  122.             Selector: 3,
  123.             Ruleset: 4,
  124.             Property: 5,
  125.             Separator: 6,
  126.             Expression: 7,
  127.             URL: 8
  128.         };
  129.  
  130.         depth = 0;
  131.         state = State.Start;
  132.         comment = false;
  133.         blocks = [];
  134.  
  135.         // We want to deal with LF (\n) only
  136.         style = style.replace(/\r\n/g, '\n');
  137.  
  138.         while (index < length) {
  139.             ch = style.charAt(index);
  140.             ch2 = style.charAt(index + 1);
  141.             index += 1;
  142.  
  143.             // Inside a string literal?
  144.             if (isQuote(quote)) {
  145.                 formatted += ch;
  146.                 if (ch === quote) {
  147.                     quote = null;
  148.                 }
  149.                 if (ch === '\\' && ch2 === quote) {
  150.                     // Don't treat escaped character as the closing quote
  151.                     formatted += ch2;
  152.                     index += 1;
  153.                 }
  154.                 continue;
  155.             }
  156.  
  157.             // Starting a string literal?
  158.             if (isQuote(ch)) {
  159.                 formatted += ch;
  160.                 quote = ch;
  161.                 continue;
  162.             }
  163.  
  164.             // Comment
  165.             if (comment) {
  166.                 formatted += ch;
  167.                 if (ch === '*' && ch2 === '/') {
  168.                     comment = false;
  169.                     formatted += ch2;
  170.                     index += 1;
  171.                 }
  172.                 continue;
  173.             } else {
  174.                 if (ch === '/' && ch2 === '*') {
  175.                     comment = true;
  176.                     formatted += ch;
  177.                     formatted += ch2;
  178.                     index += 1;
  179.                     continue;
  180.                 }
  181.             }
  182.  
  183.             if (state === State.Start) {
  184.  
  185.                 if (blocks.length === 0) {
  186.                     if (isWhitespace(ch) && formatted.length === 0) {
  187.                         continue;
  188.                     }
  189.                 }
  190.  
  191.                 // Copy white spaces and control characters
  192.                 if (ch <= ' ' || ch.charCodeAt(0) >= 128) {
  193.                     state = State.Start;
  194.                     formatted += ch;
  195.                     continue;
  196.                 }
  197.  
  198.                 // Selector or at-rule
  199.                 if (isName(ch) || (ch === '@')) {
  200.  
  201.                     // Clear trailing whitespaces and linefeeds.
  202.                     str = trimRight(formatted);
  203.  
  204.                     if (str.length === 0) {
  205.                         // If we have empty string after removing all the trailing
  206.                         // spaces, that means we are right after a block.
  207.                         // Ensure a blank line as the separator.
  208.                         if (blocks.length > 0) {
  209.                             formatted = '\n\n';
  210.                         }
  211.                     } else {
  212.                         // After finishing a ruleset or directive statement,
  213.                         // there should be one blank line.
  214.                         if (str.charAt(str.length - 1) === '}' ||
  215.                                 str.charAt(str.length - 1) === ';') {
  216.  
  217.                             formatted = str + '\n\n';
  218.                         } else {
  219.                             // After block comment, keep all the linefeeds but
  220.                             // start from the first column (remove whitespaces prefix).
  221.                             while (true) {
  222.                                 ch2 = formatted.charAt(formatted.length - 1);
  223.                                 if (ch2 !== ' ' && ch2.charCodeAt(0) !== 9) {
  224.                                     break;
  225.                                 }
  226.                                 formatted = formatted.substr(0, formatted.length - 1);
  227.                             }
  228.                         }
  229.                     }
  230.                     formatted += ch;
  231.                     state = (ch === '@') ? State.AtRule : State.Selector;
  232.                     continue;
  233.                 }
  234.             }
  235.  
  236.             if (state === State.AtRule) {
  237.  
  238.                 // ';' terminates a statement.
  239.                 if (ch === ';') {
  240.                     formatted += ch;
  241.                     state = State.Start;
  242.                     continue;
  243.                 }
  244.  
  245.                 // '{' starts a block
  246.                 if (ch === '{') {
  247.                     openBlock();
  248.                     state = State.Block;
  249.                     continue;
  250.                 }
  251.  
  252.                 formatted += ch;
  253.                 continue;
  254.             }
  255.  
  256.             if (state === State.Block) {
  257.  
  258.                 // Selector
  259.                 if (isName(ch)) {
  260.  
  261.                     // Clear trailing whitespaces and linefeeds.
  262.                     str = trimRight(formatted);
  263.  
  264.                     if (str.length === 0) {
  265.                         // If we have empty string after removing all the trailing
  266.                         // spaces, that means we are right after a block.
  267.                         // Ensure a blank line as the separator.
  268.                         if (blocks.length > 0) {
  269.                             formatted = '\n\n';
  270.                         }
  271.                     } else {
  272.                         // Insert blank line if necessary.
  273.                         if (str.charAt(str.length - 1) === '}') {
  274.                             formatted = str + '\n\n';
  275.                         } else {
  276.                             // After block comment, keep all the linefeeds but
  277.                             // start from the first column (remove whitespaces prefix).
  278.                             while (true) {
  279.                                 ch2 = formatted.charAt(formatted.length - 1);
  280.                                 if (ch2 !== ' ' && ch2.charCodeAt(0) !== 9) {
  281.                                     break;
  282.                                 }
  283.                                 formatted = formatted.substr(0, formatted.length - 1);
  284.                             }
  285.                         }
  286.                     }
  287.  
  288.                     appendIndent();
  289.                     formatted += ch;
  290.                     state = State.Selector;
  291.                     continue;
  292.                 }
  293.  
  294.                 // '}' resets the state.
  295.                 if (ch === '}') {
  296.                     closeBlock();
  297.                     state = State.Start;
  298.                     continue;
  299.                 }
  300.  
  301.                 formatted += ch;
  302.                 continue;
  303.             }
  304.  
  305.             if (state === State.Selector) {
  306.  
  307.                 // '{' starts the ruleset.
  308.                 if (ch === '{') {
  309.                     openBlock();
  310.                     state = State.Ruleset;
  311.                     continue;
  312.                 }
  313.  
  314.                 // '}' resets the state.
  315.                 if (ch === '}') {
  316.                     closeBlock();
  317.                     state = State.Start;
  318.                     continue;
  319.                 }
  320.  
  321.                 formatted += ch;
  322.                 continue;
  323.             }
  324.  
  325.             if (state === State.Ruleset) {
  326.  
  327.                 // '}' finishes the ruleset.
  328.                 if (ch === '}') {
  329.                     closeBlock();
  330.                     state = State.Start;
  331.                     if (depth > 0) {
  332.                         state = State.Block;
  333.                     }
  334.                     continue;
  335.                 }
  336.  
  337.                 // Make sure there is no blank line or trailing spaces inbetween
  338.                 if (ch === '\n') {
  339.                     formatted = trimRight(formatted);
  340.                     formatted += '\n';
  341.                     continue;
  342.                 }
  343.  
  344.                 // property name
  345.                 if (!isWhitespace(ch)) {
  346.                     formatted = trimRight(formatted);
  347.                     formatted += '\n';
  348.                     appendIndent();
  349.                     formatted += ch;
  350.                     state = State.Property;
  351.                     continue;
  352.                 }
  353.                 formatted += ch;
  354.                 continue;
  355.             }
  356.  
  357.             if (state === State.Property) {
  358.  
  359.                 // ':' concludes the property.
  360.                 if (ch === ':') {
  361.                     formatted = trimRight(formatted);
  362.                     formatted += ': ';
  363.                     state = State.Expression;
  364.                     if (isWhitespace(ch2)) {
  365.                         state = State.Separator;
  366.                     }
  367.                     continue;
  368.                 }
  369.  
  370.                 // '}' finishes the ruleset.
  371.                 if (ch === '}') {
  372.                     closeBlock();
  373.                     state = State.Start;
  374.                     if (depth > 0) {
  375.                         state = State.Block;
  376.                     }
  377.                     continue;
  378.                 }
  379.  
  380.                 formatted += ch;
  381.                 continue;
  382.             }
  383.  
  384.             if (state === State.Separator) {
  385.  
  386.                 // Non-whitespace starts the expression.
  387.                 if (!isWhitespace(ch)) {
  388.                     formatted += ch;
  389.                     state = State.Expression;
  390.                     continue;
  391.                 }
  392.  
  393.                 // Anticipate string literal.
  394.                 if (isQuote(ch2)) {
  395.                     state = State.Expression;
  396.                 }
  397.  
  398.                 continue;
  399.             }
  400.  
  401.             if (state === State.Expression) {
  402.  
  403.                 // '}' finishes the ruleset.
  404.                 if (ch === '}') {
  405.                     closeBlock();
  406.                     state = State.Start;
  407.                     if (depth > 0) {
  408.                         state = State.Block;
  409.                     }
  410.                     continue;
  411.                 }
  412.  
  413.                 // ';' completes the declaration.
  414.                 if (ch === ';') {
  415.                     formatted = trimRight(formatted);
  416.                     formatted += ';\n';
  417.                     state = State.Ruleset;
  418.                     continue;
  419.                 }
  420.  
  421.                 formatted += ch;
  422.  
  423.                 if (ch === '(') {
  424.                     if (formatted.charAt(formatted.length - 2) === 'l' &&
  425.                             formatted.charAt(formatted.length - 3) === 'r' &&
  426.                             formatted.charAt(formatted.length - 4) === 'u') {
  427.  
  428.                         // URL starts with '(' and closes with ')'.
  429.                         state = State.URL;
  430.                         continue;
  431.                     }
  432.                 }
  433.  
  434.                 continue;
  435.             }
  436.  
  437.             if (state === State.URL) {
  438.  
  439.  
  440.                 // ')' finishes the URL (only if it is not escaped).
  441.                 if (ch === ')' && formatted.charAt(formatted.length - 1 !== '\\')) {
  442.                     formatted += ch;
  443.                     state = State.Expression;
  444.                     continue;
  445.                 }
  446.             }
  447.  
  448.             // The default action is to copy the character (to prevent
  449.             // infinite loop).
  450.             formatted += ch;
  451.         }
  452.  
  453.         formatted = blocks.join('') + formatted;
  454.  
  455.         return formatted;
  456.     }
  457.  
  458.     if (typeof exports !== 'undefined') {
  459.         // Node.js module.
  460.         module.exports = exports = cssbeautify;
  461.     } else if (typeof window === 'object') {
  462.         // Browser loading.
  463.         window.cssbeautify = cssbeautify;
  464.     }
  465.  
  466. }());

Raw Paste


Login or Register to edit or fork this paste. It's free.