Page 1 of 1

Parsing CSS rules

Posted: Mon Jan 29, 2007 11:50 pm
by pile0nades
I need to parse CSS rules and add an !important flag to each rule. It's almost finished, but it adds !important to other things, like data: uris. I want to clean up the !important flags from the data: uri's (4th replace()), or ideally, block it from looking for rules inside CSS url( .. ) blocks (2nd replace()).

Code:

Code: Select all

    // add !important to every style
    function persist(css) {
      return css
      .replace(/\s*!\s*important/gi, "")                 // remove existing !important's from all rules to simplify things
      .replace(/(:.+?);/gi, "$1 !important;")            // add !important to all style rules
      .replace(/(@namespace.*\))\s*!important/gi, "$1")  // remove !important from namespace declarations
      .replace(/(data:[^\s\n]*)!important;/gi, "$1");    // remove !important from data: urls
    }
Sample CSS to parse:

Code: Select all

@namespace url(http://www.w3.org/1999/xhtml);

#ndfnedignrsdlvjgrebv {
  color: red; font-size: 1em;
  border: 1px solid black;
  background-image: url(data:image/x-icon;base64,AAAAA%3D); /* shortened to avoid page widening */
  background-color: #FFFFFF;
}
Is there a way to do this more cleanly?

Posted: Tue Jan 30, 2007 12:29 am
by Kieran Huggins
uh.. why are you doing this? I'd be willing to bet there's a better way of accomplishing what you're after.

Posted: Tue Jan 30, 2007 1:15 am
by pile0nades
Its a script for the userChrome.js firefox extension that adds a button to the Stylish extension. Adding the !important flag to a CSS rule means a webpage can't override it. Some user styles have many rules, so this enables adding them to all rules at once.

Also, sorry if this is in the wrong forum. This is a regexp problem though, not JavaScript.

Posted: Tue Jan 30, 2007 2:07 am
by feyd
As per the CSS2.1 specs, this should work:

Code: Select all

<?php

$text = <<<STOP
@namespace url(http://www.w3.org/1999/xhtml);

#ndfnedignrsdlvjgrebv {
  color: red; font-size: 1em;
  border: 1px solid black;
  background-image: url(data:image/x-icon;base64,AAAAA%3D\);); /* shortened to avoid page widening */
  background-color: #FFFFFF;
}
STOP;

$new = preg_replace('/\b([a-z-]+\s*:\s*(?:.*?(?:url\(\s*(?:\'.*?\'|".*?"|.*?)\s*[^\\\\]*(?:\\\\\\\\)*\))*)*?);/si', '$1 !important;', $text);
preg_match_all('#url\(\s*(?:\'.*?\'|".*?"|.*?)\s*[^\\\\]*(?:\\\\\\\\)*\)#si', $text, $match);

echo $text, PHP_EOL, PHP_EOL, $new, PHP_EOL, PHP_EOL, var_export($match, true);

?>

Code: Select all

@namespace url(http://www.w3.org/1999/xhtml);

#ndfnedignrsdlvjgrebv {
  color: red; font-size: 1em;
  border: 1px solid black;
  background-image: url(data:image/x-icon;base64,AAAAA%3D\);); /* shortened to avoid page widening */
  background-color: #FFFFFF;
}

@namespace url(http://www.w3.org/1999/xhtml) !important;

#ndfnedignrsdlvjgrebv {
  color: red !important; font-size: 1em !important;
  border: 1px solid black !important;
  background-image: url(data:image/x-icon;base64,AAAAA%3D\);) !important; /* shortened to avoid page widening */
  background-color: #FFFFFF !important;
}

array (
  0 =>
  array (
    0 => 'url(http://www.w3.org/1999/xhtml)',
    1 => 'url(data:image/x-icon;base64,AAAAA%3D\\);)',
  ),
)

Posted: Tue Jan 30, 2007 1:43 pm
by Kieran Huggins
8O feyd, you rock! Just looking at that makes me dizzy!

Posted: Mon Feb 12, 2007 9:36 pm
by pile0nades
feyd wrote:

Code: Select all

$new = preg_replace('/\b([a-z-]+\s*:\s*(?:.*?(?:url\(\s*(?:\'.*?\'|".*?"|.*?)\s*[^\\\\]*(?:\\\\\\\\)*\))*)*?);/si', '$1 !important;', $text);
feyd, sorry for not coming back to thank you sooner, that regex works great! And, using this amazing tool and some experimenting, I managed to simplify it to this:

Code: Select all

/(:.*?(?:url\(.*?\))*);/gi
Edit: and improved a bit:

Code: Select all

/(:.*?(?:url\(.*?\)|.)*?)\s*;/gi
Thanks again!

Posted: Mon Feb 12, 2007 9:55 pm
by Kieran Huggins
http://www.cuneytyilmaz.com/prog/jrx/

8O :bow:

I don't know why I've never thought about this kind of a tool before, but I love it to death!

Adding to my sig now :-)

Posted: Mon Feb 12, 2007 10:51 pm
by Luke
that's an awesome tool... especially since I've finally bought a book on regex and have just begun learning it.

Posted: Tue Feb 13, 2007 2:05 am
by pile0nades
One more question. I'm trying to modify the regex to match rules that don't have a semicolon, i.e. ones at the end of a block:

Code: Select all

#ndfnedignrsdlvjgrebv {
  background-color: #FFFFFF
}
The modified regex so far:

Code: Select all

.replace(/(:.*?(?:url\(.*?\)|.)*?)\s*(?:;|(\n*\}))/gi, "$1 !important;$2")
I'm seeing weird behavior of the \n character. It seems just using \n by itself will match multiple newlines. Even weirder is that if I try to use \n* to match 0 or more newlines (to preserve the position of the closing curly brace "}"), it fails to match any newlines at all.

How do you match 0 or more newlines correctly?

Posted: Fri Feb 16, 2007 5:13 am
by pile0nades
Nevermind, I found the solution. It was the greediness of \s* ! Adding a ? fixed it. With some more changes to handle CSS comments, here's the final regex:

Code: Select all

.replace(/(:.*?(?:\w+?\((?:.|\n)*?\)|.)*?)\s*?(?:;|(\s*?(?:\n+?\s*?(?:\/\*.*?\*\/)*?\s*?)*?\}))/gi, "$1 !important;$2")
Thanks again for the help!