Skip to content

Instantly share code, notes, and snippets.

@gka
Created January 8, 2012 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gka/1579670 to your computer and use it in GitHub Desktop.
Save gka/1579670 to your computer and use it in GitHub Desktop.
CSS parsing in JavaScript
/**
* This is the light version of Danial Wachsstock's jQuery based CSS parser
* http://bililite.com/blog/2009/01/16/jquery-css-parser/#parserdetails
*
* Everything is removed but the core css2object parsing
*
* Usage is straight forward:
*
* $.parseCSS('body { background: red; font-weight: 300 }');
* // returns { 'body': { 'background': 'red', 'font-weight': 300 } }
*
*/
// jQuery based CSS parser
// documentation: http://youngisrael-stl.org/wordpress/2009/01/16/jquery-css-parser/
// Version: 1.3
// Copyright (c) 2011 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
(function($){
$.parsecss = function(str, callback){
var ret = {};
str = munge(str).replace(/@(([^;`]|`[^b]|`b[^%])*(`b%)?);?/g, function(s,rule){
// @rules end with ; or a block, with the semicolon not being part of the rule but the closing brace (represented by `b%) is
processAtRule($.trim(rule), callback);
return '';
});
$.each (str.split('`b%'), function(i,css){ // split on the end of a block
css = css.split('%b`'); // css[0] is the selector; css[1] is the index in munged for the cssText
if (css.length < 2) return; // invalid css
css[0] = restore(css[0]);
ret[css[0]] = $.extend(ret[css[0]] || {}, parsedeclarations(css[1]));
});
if ($.isFunction(callback)) callback(ret);
else return ret;
};
// caches
var munged = {}; // strings that were removed by the parser so they don't mess up searching for specific characters
// private functions
function parsedeclarations(index){ // take a string from the munged array and parse it into an object of property: value pairs
var str = munged[index].replace(/^{|}$/g, ''); // find the string and remove the surrounding braces
str = munge(str); // make sure any internal braces or strings are escaped
var parsed = {};
$.each (str.split(';'), function (i, decl){
decl = decl.split(':');
if (decl.length < 2) return;
parsed[restore(decl[0])] = restore(decl.slice(1).join(':'));
});
return parsed;
}
// replace strings and brace-surrounded blocks with %s`number`s% and %b`number`b%. By successively taking out the innermost
// blocks, we ensure that we're matching braces. No way to do this with just regular expressions. Obviously, this assumes no one
// would use %s` in the real world.
// Turns out this is similar to the method that Dean Edwards used for his CSS parser in IE7.js (http://code.google.com/p/ie7-js/)
var REbraces = /{[^{}]*}/;
var REfull = /\[[^\[\]]*\]|{[^{}]*}|\([^()]*\)|function(\s+\w+)?(\s*%b`\d+`b%){2}/; // match pairs of parentheses, brackets, and braces and function definitions.
var REatcomment = /\/\*@((?:[^\*]|\*[^\/])*)\*\//g; // comments of the form /*@ text */ have text parsed
// we have to combine the comments and the strings because comments can contain string delimiters and strings can contain comment delimiters
// var REcomment = /\/\*(?:[^\*]|\*[^\/])*\*\/|<!--|-->/g; // other comments are stripped. (this is a simplification of real SGML comments (see http://htmlhelp.com/reference/wilbur/misc/comment.html) , but it's what real browsers use)
// var REstring = /\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*'/g; // match escaped characters and strings
var REcomment_string =
/(?:\/\*(?:[^\*]|\*[^\/])*\*\/)|(\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*')/g;
var REmunged = /%\w`(\d+)`\w%/;
var uid = 0; // unique id number
function munge(str, full){
str = str
.replace(REatcomment,'$1') // strip /*@ comments but leave the text (to let invalid CSS through)
.replace(REcomment_string, function (s, string){ // strip strings and escaped characters, leaving munged markers, and strip comments
if (!string) return '';
var replacement = '%s`'+(++uid)+'`s%';
munged[uid] = string.replace(/^\\/,''); // strip the backslash now
return replacement;
})
;
// need a loop here rather than .replace since we need to replace nested braces
var RE = full ? REfull : REbraces;
while (match = RE.exec(str)){
replacement = '%b`'+(++uid)+'`b%';
munged[uid] = match[0];
str = str.replace(RE, replacement);
}
return str;
}
function restore(str){
if (str === undefined) return str;
while (match = REmunged.exec(str)){
str = str.replace(REmunged, munged[match[1]]);
}
return $.trim(str);
}
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment