Parsing CSS rules

Any questions involving matching text strings to patterns - the pattern is called a "regular expression."

Moderator: General Moderators

Post Reply
pile0nades
Forum Newbie
Posts: 5
Joined: Mon Jan 29, 2007 10:35 pm

Parsing CSS rules

Post 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?
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post 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.
pile0nades
Forum Newbie
Posts: 5
Joined: Mon Jan 29, 2007 10:35 pm

Post 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.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post 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\\);)',
  ),
)
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

8O feyd, you rock! Just looking at that makes me dizzy!
pile0nades
Forum Newbie
Posts: 5
Joined: Mon Jan 29, 2007 10:35 pm

Post 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!
Last edited by pile0nades on Mon Feb 12, 2007 11:32 pm, edited 1 time in total.
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post 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 :-)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

that's an awesome tool... especially since I've finally bought a book on regex and have just begun learning it.
pile0nades
Forum Newbie
Posts: 5
Joined: Mon Jan 29, 2007 10:35 pm

Post 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?
pile0nades
Forum Newbie
Posts: 5
Joined: Mon Jan 29, 2007 10:35 pm

Post 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!
Post Reply