1 /** Cross Browser Adapter for StyleSheets. 2 * 3 * @author Garrett Smith 4 * This is mainly for IE 5-7. 5 */ 6 APE.namespace("APE.dom"); 7 8 /** @constructor Use the default Factory getById(id) method to get a cached styleSheetAdapter. 9 * @param {String|CSSStyleSheet} either an ID of a link or style element (String) 10 * or a CSSStyleSheet object. 11 */ 12 APE.dom.StyleSheetAdapter = function( idOrStyleSheet ) { 13 if(idOrStyleSheet.cssRules || idOrStyleSheet.rules) { 14 this.sheet = idOrStyleSheet; 15 } 16 if(typeof idOrStyleSheet == "string") { 17 this.idOrStyleSheet = idOrStyleSheet; 18 var linkOrStyleEl = document.getElementById(idOrStyleSheet); 19 this.sheet = linkOrStyleEl.sheet || linkOrStyleEl.styleSheet; 20 linkOrStyleEl = null; 21 } 22 }; 23 24 APE.dom.StyleSheetAdapter.getById = APE.getById; 25 26 APE.dom.StyleSheetAdapter.prototype = { 27 28 /** IE will not return the correct selector text for Element selectors in HTML. 29 * Instead, IE puts the element selector and pseudoclass selectors in upper case. 30 * 31 * This function compares selectors, case sensitive, but 32 * case insensitive for element selector parts. 33 * Does not traverse into media blocks. 34 * @return CSSStyleRule 35 */ 36 getRule : function(selectorText) { 37 var tuc = APE.dom.StyleSheetAdapter._tagNamesToUpperCase; 38 39 // Convert local selectorText to have upper-case element selectors. 40 selectorText = tuc(selectorText); 41 var cssRuleList = this.sheet.cssRules || this.sheet.rules; 42 43 // Loop through the cssRuleList. 44 for(var i = 0, iLen = cssRuleList.length; i < iLen; i++) { 45 46 // Check each rule's selectorText to see if it matches the one provided. 47 // Since a rule can have multiple selectors, separated by ",", 48 // we split on "," (with optional whitespace) and return the rule for any 49 // matching selector text. 50 var selectorTextMatches = cssRuleList[i].selectorText.split(/\s*,\s*/); 51 52 for(var j = 0; j < selectorTextMatches.length; j++) { 53 if(tuc(selectorTextMatches[j]) == selectorText) 54 return cssRuleList[i]; 55 } 56 } 57 return null; 58 }, 59 60 /** @return CSSRuleList */ 61 getRules : function() { 62 return this.sheet.cssRules || this.sheet.rules; 63 }, 64 65 /** 66 * Adds a rule to the styleSheet 67 * @return {CSSRule} cssRule that was added. 68 */ 69 addRule : function(selectorText, cssText) { 70 71 // IE barfs on empty string. ";" makes IE's parser happy. 72 if(!cssText) cssText = ";"; 73 74 var rule, rules = this.getRules(); 75 76 if(this.sheet.insertRule) { 77 this.sheet.insertRule(selectorText + "{" + cssText + "}", rules.length); 78 // Safari 3 doesn't keep a live copy of rules. 79 // we need to get the rules off the sheet again. 80 rules = this.sheet.cssRules; 81 rule = rules[rules.length-1]; 82 } 83 else if (this.sheet.addRule) { 84 this.sheet.addRule(selectorText, cssText); 85 rule = rules[rules.length-1]; 86 87 // Standard: rule.cssText (readonly) 88 // IE: rule.style.cssText (read/write); 89 // We got this far, assume it's safe to write style.cssText. 90 if(rule.style.cssText) 91 rule.style.cssText = cssText; 92 } 93 return rule; 94 } 95 }; 96 97 /** 98 * Converts the tag names of selector text to upper case. 99 * This is used internally for matching/comparison of selector text. 100 * Internet Explorer converts all 101 * HTML Element selectors to upper case. It is a bad design decision by the IE team. 102 * @private 103 */ 104 APE.dom.StyleSheetAdapter._tagNamesToUpperCase = function(s) { 105 // Element Selector = Start of string or ws, followed by one or more letter chars. 106 var elementSelector = /(^|\s)([a-z]+)/; 107 var matches = s.match( elementSelector ), R = RegExp; 108 while( elementSelector.test(s) ) s = s.replace(elementSelector, R.$1+R.$2.toUpperCase()); 109 return s; 110 };