browser/devtools/styleinspector/css-parsing-utils.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 "use strict";
     9 const cssTokenizer  = require("devtools/sourceeditor/css-tokenizer");
    11 /**
    12  * Returns the string enclosed in quotes
    13  */
    14 function quoteString(string) {
    15   let hasDoubleQuotes = string.contains('"');
    16   let hasSingleQuotes = string.contains("'");
    18   if (hasDoubleQuotes && !hasSingleQuotes) {
    19     // In this case, no escaping required, just enclose in single-quotes
    20     return "'" + string + "'";
    21   }
    23   // In all other cases, enclose in double-quotes, and escape any double-quote
    24   // that may be in the string
    25   return '"' + string.replace(/"/g, '\"') + '"';
    26 }
    28 /**
    29  * Returns an array of CSS declarations given an string.
    30  * For example, parseDeclarations("width: 1px; height: 1px") would return
    31  * [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
    32  *
    33  * The input string is assumed to only contain declarations so { and } characters
    34  * will be treated as part of either the property or value, depending where it's
    35  * found.
    36  *
    37  * @param {string} inputString
    38  *        An input string of CSS
    39  * @return {Array} an array of objects with the following signature:
    40  *         [{"name": string, "value": string, "priority": string}, ...]
    41  */
    42 function parseDeclarations(inputString) {
    43   let tokens = cssTokenizer(inputString);
    45   let declarations = [{name: "", value: "", priority: ""}];
    47   let current = "", hasBang = false, lastProp;
    48   for (let token of tokens) {
    49     lastProp = declarations[declarations.length - 1];
    51     if (token.tokenType === ":") {
    52       if (!lastProp.name) {
    53         // Set the current declaration name if there's no name yet
    54         lastProp.name = current.trim();
    55         current = "";
    56         hasBang = false;
    57       } else {
    58         // Otherwise, just append ':' to the current value (declaration value
    59         // with colons)
    60         current += ":";
    61       }
    62     } else if (token.tokenType === ";") {
    63       lastProp.value = current.trim();
    64       current = "";
    65       hasBang = false;
    66       declarations.push({name: "", value: "", priority: ""});
    67     } else {
    68       switch(token.tokenType) {
    69         case "IDENT":
    70           if (token.value === "important" && hasBang) {
    71             lastProp.priority = "important";
    72             hasBang = false;
    73           } else {
    74             if (hasBang) {
    75               current += "!";
    76             }
    77             current += token.value;
    78           }
    79           break;
    80         case "WHITESPACE":
    81           current += " ";
    82           break;
    83         case "DIMENSION":
    84           current += token.repr;
    85           break;
    86         case "HASH":
    87           current += "#" + token.value;
    88           break;
    89         case "URL":
    90           current += "url(" + quoteString(token.value) + ")";
    91           break;
    92         case "FUNCTION":
    93           current += token.value + "(";
    94           break;
    95         case ")":
    96           current += token.tokenType;
    97           break;
    98         case "EOF":
    99           break;
   100         case "DELIM":
   101           if (token.value === "!") {
   102             hasBang = true;
   103           } else {
   104             current += token.value;
   105           }
   106           break;
   107         case "STRING":
   108           current += quoteString(token.value);
   109           break;
   110         case "{":
   111         case "}":
   112           current += token.tokenType;
   113           break;
   114         default:
   115           current += token.value;
   116           break;
   117       }
   118     }
   119   }
   121   // Handle whatever trailing properties or values might still be there
   122   if (current) {
   123     if (!lastProp.name) {
   124       // Trailing property found, e.g. p1:v1;p2:v2;p3
   125       lastProp.name = current.trim();
   126     } else {
   127       // Trailing value found, i.e. value without an ending ;
   128       lastProp.value += current.trim();
   129     }
   130   }
   132   // Remove declarations that have neither a name nor a value
   133   declarations = declarations.filter(prop => prop.name || prop.value);
   135   return declarations;
   136 };
   137 exports.parseDeclarations = parseDeclarations;
   139 /**
   140  * Expects a single CSS value to be passed as the input and parses the value
   141  * and priority.
   142  *
   143  * @param {string} value The value from the text editor.
   144  * @return {object} an object with 'value' and 'priority' properties.
   145  */
   146 function parseSingleValue(value) {
   147   let declaration = parseDeclarations("a: " + value + ";")[0];
   148   return {
   149     value: declaration ? declaration.value : "",
   150     priority: declaration ? declaration.priority : ""
   151   };
   152 };
   153 exports.parseSingleValue = parseSingleValue;

mercurial