    /**
     * @class Ext.util.CSS
     * Utility class for manipulating CSS rules
     * @singleton
     */
    Ext.util.CSS = function(){
    	var rules = null;
       	var doc = document;

        var camelRe = /(-[a-z])/gi;
        var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };

       return {
       /**
        * Creates a stylesheet from a text blob of rules.
        * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
        * @param {String} cssText The text containing the css rules
        * @param {String} id An id to add to the stylesheet for later removal
        * @return {StyleSheet}
        */
       createStyleSheet : function(cssText, id){
           var ss;
           var head = doc.getElementsByTagName("head")[0];
           var rules = doc.createElement("style");
           rules.setAttribute("type", "text/css");
           if(id){
               rules.setAttribute("id", id);
           }
           if(Ext.isIE){
               head.appendChild(rules);
               ss = rules.styleSheet;
               ss.cssText = cssText;
           }else{
               try{
                    rules.appendChild(doc.createTextNode(cssText));
               }catch(e){
                   rules.cssText = cssText;
               }
               head.appendChild(rules);
               ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
           }
           this.cacheStyleSheet(ss);
           return ss;
       },

       /**
        * Removes a style or link tag by id
        * @param {String} id The id of the tag
        */
       removeStyleSheet : function(id){
           var existing = doc.getElementById(id);
           if(existing){
               existing.parentNode.removeChild(existing);
           }
       },

       /**
        * Dynamically swaps an existing stylesheet reference for a new one
        * @param {String} id The id of an existing link tag to remove
        * @param {String} url The href of the new stylesheet to include
        */
       swapStyleSheet : function(id, url){
           this.removeStyleSheet(id);
           var ss = doc.createElement("link");
           ss.setAttribute("rel", "stylesheet");
           ss.setAttribute("type", "text/css");
           ss.setAttribute("id", id);
           ss.setAttribute("href", url);
           doc.getElementsByTagName("head")[0].appendChild(ss);
       },

       /**
        * Refresh the rule cache if you have dynamically added stylesheets
        * @return {Object} An object (hash) of rules indexed by selector
        */
       refreshCache : function(){
           return this.getRules(true);
       },

       // private
       cacheStyleSheet : function(ss){
           if(!rules){
               rules = {};
           }
           try{// try catch for cross domain access issue
               var ssRules = ss.cssRules || ss.rules;
               for(var j = ssRules.length-1; j >= 0; --j){
                   rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
               }
           }catch(e){}
       },

       /**
        * Gets all css rules for the document
        * @param {Boolean} refreshCache true to refresh the internal cache
        * @return {Object} An object (hash) of rules indexed by selector
        */
       getRules : function(refreshCache){
       		if(rules === null || refreshCache){
       			rules = {};
       			var ds = doc.styleSheets;
       			for(var i =0, len = ds.length; i < len; i++){
       			    try{
        		        this.cacheStyleSheet(ds[i]);
        		    }catch(e){}
    	        }
       		}
       		return rules;
       	},

       	/**
        * Gets an an individual CSS rule by selector(s)
        * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
        * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
        * @return {CSSRule} The CSS rule or null if one is not found
        */
       getRule : function(selector, refreshCache){
       		var rs = this.getRules(refreshCache);
       		if(!Ext.isArray(selector)){
       		    return rs[selector.toLowerCase()];
       		}
       		for(var i = 0; i < selector.length; i++){
    			if(rs[selector[i]]){
    				return rs[selector[i].toLowerCase()];
    			}
    		}
    		return null;
       	},

       	/**
        * Updates a rule property
        * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
        * @param {String} property The css property
        * @param {String} value The new value for the property
        * @return {Boolean} true If a rule was found and updated
        */
       updateRule : function(selector, property, value){
       		if(!Ext.isArray(selector)){
       			var rule = this.getRule(selector);
       			if(rule){
       				rule.style[property.replace(camelRe, camelFn)] = value;
       				return true;
       			}
       		}else{
       			for(var i = 0; i < selector.length; i++){
       				if(this.updateRule(selector[i], property, value)){
       					return true;
       				}
       			}
       		}
       		return false;
       	}
       };
    }();