Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / src / util / CSS.js
1 /*!
2  * Ext JS Library 3.1.1
3  * Copyright(c) 2006-2010 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.util.CSS\r
9  * Utility class for manipulating CSS rules\r
10  * @singleton\r
11  */\r
12 Ext.util.CSS = function(){\r
13         var rules = null;\r
14         var doc = document;\r
15 \r
16     var camelRe = /(-[a-z])/gi;\r
17     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };\r
18 \r
19    return {\r
20    /**\r
21     * Creates a stylesheet from a text blob of rules.\r
22     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.\r
23     * @param {String} cssText The text containing the css rules\r
24     * @param {String} id An id to add to the stylesheet for later removal\r
25     * @return {StyleSheet}\r
26     */\r
27    createStyleSheet : function(cssText, id){\r
28        var ss;\r
29        var head = doc.getElementsByTagName("head")[0];\r
30        var rules = doc.createElement("style");\r
31        rules.setAttribute("type", "text/css");\r
32        if(id){\r
33            rules.setAttribute("id", id);\r
34        }\r
35        if(Ext.isIE){\r
36            head.appendChild(rules);\r
37            ss = rules.styleSheet;\r
38            ss.cssText = cssText;\r
39        }else{\r
40            try{\r
41                 rules.appendChild(doc.createTextNode(cssText));\r
42            }catch(e){\r
43                rules.cssText = cssText;\r
44            }\r
45            head.appendChild(rules);\r
46            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);\r
47        }\r
48        this.cacheStyleSheet(ss);\r
49        return ss;\r
50    },\r
51 \r
52    /**\r
53     * Removes a style or link tag by id\r
54     * @param {String} id The id of the tag\r
55     */\r
56    removeStyleSheet : function(id){\r
57        var existing = doc.getElementById(id);\r
58        if(existing){\r
59            existing.parentNode.removeChild(existing);\r
60        }\r
61    },\r
62 \r
63    /**\r
64     * Dynamically swaps an existing stylesheet reference for a new one\r
65     * @param {String} id The id of an existing link tag to remove\r
66     * @param {String} url The href of the new stylesheet to include\r
67     */\r
68    swapStyleSheet : function(id, url){\r
69        this.removeStyleSheet(id);\r
70        var ss = doc.createElement("link");\r
71        ss.setAttribute("rel", "stylesheet");\r
72        ss.setAttribute("type", "text/css");\r
73        ss.setAttribute("id", id);\r
74        ss.setAttribute("href", url);\r
75        doc.getElementsByTagName("head")[0].appendChild(ss);\r
76    },\r
77    \r
78    /**\r
79     * Refresh the rule cache if you have dynamically added stylesheets\r
80     * @return {Object} An object (hash) of rules indexed by selector\r
81     */\r
82    refreshCache : function(){\r
83        return this.getRules(true);\r
84    },\r
85 \r
86    // private\r
87    cacheStyleSheet : function(ss){\r
88        if(!rules){\r
89            rules = {};\r
90        }\r
91        try{// try catch for cross domain access issue\r
92            var ssRules = ss.cssRules || ss.rules;\r
93            for(var j = ssRules.length-1; j >= 0; --j){\r
94                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];\r
95            }\r
96        }catch(e){}\r
97    },\r
98    \r
99    /**\r
100     * Gets all css rules for the document\r
101     * @param {Boolean} refreshCache true to refresh the internal cache\r
102     * @return {Object} An object (hash) of rules indexed by selector\r
103     */\r
104    getRules : function(refreshCache){\r
105                 if(rules === null || refreshCache){\r
106                         rules = {};\r
107                         var ds = doc.styleSheets;\r
108                         for(var i =0, len = ds.length; i < len; i++){\r
109                             try{\r
110                         this.cacheStyleSheet(ds[i]);\r
111                     }catch(e){} \r
112                 }\r
113                 }\r
114                 return rules;\r
115         },\r
116         \r
117         /**\r
118     * Gets an an individual CSS rule by selector(s)\r
119     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.\r
120     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically\r
121     * @return {CSSRule} The CSS rule or null if one is not found\r
122     */\r
123    getRule : function(selector, refreshCache){\r
124                 var rs = this.getRules(refreshCache);\r
125                 if(!Ext.isArray(selector)){\r
126                     return rs[selector.toLowerCase()];\r
127                 }\r
128                 for(var i = 0; i < selector.length; i++){\r
129                         if(rs[selector[i]]){\r
130                                 return rs[selector[i].toLowerCase()];\r
131                         }\r
132                 }\r
133                 return null;\r
134         },\r
135         \r
136         \r
137         /**\r
138     * Updates a rule property\r
139     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.\r
140     * @param {String} property The css property\r
141     * @param {String} value The new value for the property\r
142     * @return {Boolean} true If a rule was found and updated\r
143     */\r
144    updateRule : function(selector, property, value){\r
145                 if(!Ext.isArray(selector)){\r
146                         var rule = this.getRule(selector);\r
147                         if(rule){\r
148                                 rule.style[property.replace(camelRe, camelFn)] = value;\r
149                                 return true;\r
150                         }\r
151                 }else{\r
152                         for(var i = 0; i < selector.length; i++){\r
153                                 if(this.updateRule(selector[i], property, value)){\r
154                                         return true;\r
155                                 }\r
156                         }\r
157                 }\r
158                 return false;\r
159         }\r
160    };   \r
161 }();