Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing CSS background-image

How do I parse CSS background-image, which supports multiple values, which may be none and functions (e.g. url() and linear-gradient()) with multiple comma-separated arguments? I can't seem to do this correctly with regexps. A good test case is as follows:

  linear-gradient(top left, red, rgba(255,0,0,0))
, url(a)
, image(url(b.svg), 'b.png' 150dpi, 'b.gif', rgba(0,0,255,0.5))
, none

Which I'd want to convert to the following array:

[
      "linear-gradient(top left, red, rgba(255,0,0,0))"
    , "url(a)"
    , "image(url(b.svg), 'b.png' 150dpi, 'b.gif', rgba(0,0,255,0.5))"
    , "none"
]
like image 263
Eli Grey Avatar asked Aug 07 '11 01:08

Eli Grey


3 Answers

function split (string) {
    var token = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/g;
    return (function recurse () {
        for (var array = [];;) {
            var result = token.exec(string);
            if (result[2] == '(') {
                array.push(result[1].trim() + '(' + recurse().join(',') + ')');
                result = token.exec(string);
            } else array.push(result[1].trim());
            if (result[2] != ',') return array
        }
    })()
}

split("linear-gradient(top left, red, rgba(255,0,0,0)), url(a), image(url" +
      "(b.svg), 'b.png' 150dpi, 'b.gif', rgba(0,0,255,0.5)), none").toSource()

["linear-gradient(top left,red,rgba(255,0,0,0))", "url(a)",
 "image(url(b.svg),'b.png' 150dpi,'b.gif',rgba(0,0,255,0.5))", "none"]
like image 187
Hafydd Avatar answered Nov 07 '22 08:11

Hafydd


Looking at the current W3C Candidate Recommendation for CSS3 (in particular, see background-image and uri), it is structured as follows:

<background-image> = <bg-image> [ , <bg-image> ]* 
<bg-image> = <image> | none
<image> = <url> | <image-list> | <element-reference> | <image-combination> | <gradient>

... (you can find the rest of syntax for images here)

EDIT:

You will need to parse for matching parenthese or none then, and the former is not possible with regex. This post has a pseudo code for the algorithm: Python parsing bracketed blocks.

like image 34
William Niu Avatar answered Nov 07 '22 10:11

William Niu


I know, the question is pretty old, but if you stumble over it and need another solution, you can use this one:

let mydiv = document.getElementById('mydiv'),
  result = document.getElementById('result'),
  bgImageArray = getBackgroundImageArray(mydiv);

console.log(bgImageArray);

result.innerHTML = bgImageArray.join('<hr>');

function getBackgroundImageArray(el)
{
    // get backgroundImageStyle
    let bgimg_style = getComputedStyle(el).backgroundImage,
        // count for parenthesis
        parenthesis = -1;

    // split background by characters...
    return bgimg_style.split('').reduce((str_to_split, character) => {
        // if opening parenthesis
        if(character === '(') {
            // if first opening parenthesis set parenthesis count to zero
            if(parenthesis === -1) {
                parenthesis = 0;
            }
            // add 1 to parenthesis count
            parenthesis++;
        }
        // for closing parenthesis reduce parenthesis count by 1
        else if(character === ')') {
            parenthesis--;
        }
        else {
            // if current character is a comma and it is not inside a parenthesis, at a "split" character
            if(character === ',') {
                if(parenthesis === 0) {
                    str_to_split += '||';
                    return str_to_split;
                }
            }
        }
    
        // else keep the character
        str_to_split += character;
    
        return str_to_split;
    }, '')
    // split the resulting string by the split characters including whitespaces before and after to generate an array
    .split(/\s*\|\|\s*/);
}
#mydiv {
  height: 75px;
  background-image:
    /* first bg image */
    linear-gradient(90deg, rgba(28,221,218,1) 0%, rgba(45,109,210,1) 35%, rgba(0,212,255,1) 100%),
    
    /* second bg image */
    -webkit-image-set(url(nothing.jpg) 1x, url(everything.png) 2x),

    /* third bg image */
    url('there/is/an/image.svg');
    
}
<div id="mydiv"></div>

<p>See in console for actual result array, below is the array splitted by &lt;hr&gt;</p>

<div id="result"></div>
like image 1
Frank Eisenhardt Avatar answered Nov 07 '22 10:11

Frank Eisenhardt