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 };