1 /** 2 * @namespace CSS stuff 3 */ 4 Jelo.CSS = function() { 5 /** 6 * @private Resets the opacity style of a DOM element. Required for IE. 7 */ 8 var clearOpacity = function(el) { 9 if (Jelo.Env.isIE) { 10 if (typeof el.style.filter === "string" && (/alpha/i).test(el.style.filter)) { 11 el.style.filter = ""; 12 } 13 } else { 14 el.style.opacity = ""; 15 el.style["-moz-opacity"] = ""; 16 el.style["-khtml-opacity"] = ""; 17 } 18 }; 19 20 /** @private alias */ 21 var toCamel = Jelo.Format.toCamel; 22 23 /** @scope Jelo.CSS */ 24 return { 25 /** 26 * Mainly used by internal functions, clearOpacity fixes IE behavior. 27 * 28 * @function 29 * @param {HTMLElement} element 30 */ 31 clearOpacity : clearOpacity, 32 33 /** 34 * @function 35 * @param {HTMLElement} element The item to which the class should be 36 * assigned. 37 * @param {String} class The class name to assign. Duplicates will be 38 * filtered out automatically. 39 */ 40 addClass : function(e, c) { 41 if (Jelo.Valid.isArray(e)) { 42 var fn = arguments.callee; 43 Jelo.each(e, function() { 44 fn(this, c); 45 }); 46 return; 47 } 48 var curr = e.className; 49 if (!Jelo.CSS.hasClass(e, c)) { 50 e.className = curr + (curr.length ? " " : "") + c; 51 } 52 }, 53 54 /** 55 * @function 56 * @param {HTMLElement} element The item to investigate. 57 * @param {String} class The class name to search for. 58 * @returns {Boolean} True if the item's className property contains the 59 * supplied class name. 60 */ 61 hasClass : function(e, c) { 62 return c && (' ' + e.className + ' ').indexOf(' ' + c + ' ') != -1; 63 }, 64 65 /** 66 * @function 67 * @param {HTMLElement} element The item to affect. 68 * @param {String} class The class name to remove. 69 */ 70 removeClass : function(e, c) { 71 if (Jelo.Valid.isArray(e)) { 72 var fn = arguments.callee; 73 Jelo.each(e, function() { 74 fn(this, c); 75 }); 76 return; 77 } 78 e.className = e.className.replace(new RegExp('\\b' + c + '\\b'), '').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 79 }, 80 81 /** 82 * Gets the current or computed value for an element's CSS property. 83 * 84 * @function 85 * @param {HTMLElement|HTMLElement[]|String} element One or more items 86 * to investigate. If a string is supplied, it is considered a 87 * CSS selector and will match elements accordingly. 88 * @param {String} property The CSS property to retrieve 89 * @return {String} CSS property value 90 */ 91 getStyle : function() { 92 var view = document.defaultView; 93 return (view && view.getComputedStyle) ? function(el, p, toInt) { 94 var /* counter */i, /* value */v, /* return value */ret, /* camelCase property */cp, styles = []; 95 if (!el || !p) { 96 return null; 97 } 98 if (typeof el == "string") { 99 el = Jelo.Dom.select(el); 100 } 101 if (typeof p == "string") { 102 p = p.toLowerCase(); 103 } 104 if (Jelo.Valid.isArray(el)) { 105 for (i = 0; i < el.length; i++) { 106 styles.push(Jelo.CSS.getStyle(el[i], p, toInt)); 107 } 108 return styles; 109 } 110 if (Jelo.Valid.isArray(p)) { 111 for (i = 0; i < p.length; i++) { 112 styles.push(Jelo.CSS.getStyle(el, p[i], toInt)); 113 } 114 return styles; 115 } 116 if (p == "float") { 117 p = "cssFloat"; 118 } 119 cp = toCamel(p); 120 switch (cp) { 121 case "backgroundPositionX" : 122 try { 123 ret = Jelo.CSS.getStyle(el, "background-position").split(" ")[0]; 124 } catch (e1) { 125 return null; 126 } 127 break; 128 case "backgroundPositionY" : 129 try { 130 ret = Jelo.CSS.getStyle(el, "background-position").split(" ")[1]; 131 } catch (e2) { 132 return null; 133 } 134 break; 135 default : 136 if ((v = el.style[p])) { 137 ret = (/color/).test(p) 138 ? Jelo.Format.rgbToHex(v) 139 : v.toString().toLowerCase(); 140 } else if ((v = view.getComputedStyle(el, "")[cp])) { 141 ret = (/color/).test(p) ? Jelo.Format.rgbToHex(v) : v.toString(); 142 } 143 } 144 return toInt ? parseInt(ret, 10) : ret; 145 } 146 : function(el, p, toInt) { 147 var /* counter */i, /* value */v, /* return value */ret, /* camelCase property */cp, styles = []; 148 if (!el || !p) { 149 return null; 150 } 151 if (typeof el == "string") { 152 el = Jelo.Dom.select(el); 153 } 154 if (typeof p == "string") { 155 p = p.toLowerCase(); 156 } 157 if (Jelo.Valid.isArray(el)) { 158 for (i = 0; i < el.length; i++) { 159 styles.push(Jelo.CSS.getStyle(el[i], p, toInt)); 160 } 161 return styles; 162 } 163 if (Jelo.Valid.isArray(p)) { 164 for (i = 0; i < p.length; i++) { 165 styles.push(Jelo.CSS.getStyle(el, p[i], toInt)); 166 } 167 return styles; 168 } 169 if (p == "opacity") { 170 if (typeof el.style.filter == 'string') { 171 var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i); 172 if (m) { 173 var fv = parseFloat(m[1]); 174 if (!isNaN(fv)) { 175 return fv ? fv / 100 : 0; 176 } 177 } 178 } 179 return 1; 180 } 181 if (p == "float") { 182 p = "styleFloat"; 183 } 184 p = toCamel(p); 185 if ((v = el.style[p])) { 186 ret = v.toString(); 187 } else if (el.currentStyle && (v = el.currentStyle[p])) { 188 if (v == "auto") { 189 if ((v = el["offset" + p.replace(/^(.)/, function(m, l) { 190 return l.toUpperCase(); // initial cap 191 })])) { 192 ret = v + "px"; 193 } 194 } 195 ret = v.toString(); 196 } 197 return toInt ? parseInt(ret, 10) : ret; 198 }; 199 }(), 200 201 /** 202 * Gets the current or computed value for an element's CSS property. 203 * 204 * @function 205 * @param {HTMLElement|HTMLElement[]|String} element One or more items 206 * to affect. If a string is supplied, it is considered a CSS 207 * selector and will match elements accordingly. 208 * @param {String|String[]} property The CSS property to assign. 209 * @param {String|String[]} value The value to assign. 210 */ 211 setStyle : function(el, p, v) { 212 var /* counter */i, /* units */u; 213 if (typeof el === "string") { 214 el = Jelo.Dom.select(el); 215 } 216 if (Jelo.Valid.isArray(el)) { 217 for (i = 0; i < el.length; i++) { 218 Jelo.CSS.setStyle(el[i], p, v); 219 } 220 return; 221 } 222 if (Jelo.Valid.isArray(p) || Jelo.Valid.isArray(v)) { 223 if (Jelo.Valid.isArray(p) && Jelo.Valid.isArray(v) && (p.length == v.length)) { 224 for (i = 0; i < p.length; i++) { 225 Jelo.CSS.setStyle(el, p[i], v[i]); 226 } 227 } else { 228 throw new Error('Jelo.CSS.setStyle: Properties and values must both be Arrays with the same length, or both be Strings.'); 229 } 230 return; 231 } 232 p = toCamel(p); 233 if ((/width|height|top|right|bottom|left|size/).test(p)) { 234 u = v.replace(/[^(%|px|em)]/g, ""); 235 if (!u.length) { 236 u = "px"; 237 } 238 v = parseInt(v, 10); 239 if (isNaN(v)) { 240 v = 0; 241 } 242 v += u; 243 } 244 var s = el.style; 245 if (p === "opacity") { 246 if (Jelo.Env.isIE) { 247 s.zoom = 1; 248 s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi, "") + 249 (v == 1 ? "" : " alpha(opacity=" + v * 100 + ")"); 250 } else { 251 s.opacity = parseFloat(v); 252 } 253 } else { 254 s[p] = v; 255 } 256 }, 257 258 /** 259 * Generates a random hexidecimal color, including the hash symbol (e.g. 260 * #5181ff) 261 * 262 * @returns {String} A "CSS-formatted" color. 263 */ 264 randomColor : function() { 265 return '#' + (function(h) { 266 return new Array(7 - h.length).join('0') + h; 267 })((Math.random() * (0xFFFFFF + 1) << 0).toString(16)); 268 }, 269 270 /** 271 * Gets a CSS stylesheet rule. 272 * http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript 273 * 274 * @function 275 * @param {String} selector CSS selector, exactly as entered in the stylesheet. 276 * @param {Boolean=false} deleteFlag True to delete the matching rule. 277 */ 278 getRule : function(s, d) { 279 s = s.toLowerCase && s.toLowerCase(); 280 if (document.styleSheets) { 281 for (var i = 0; i < document.styleSheets.length; i++) { 282 var styleSheet = document.styleSheets[i]; 283 var ii = 0; // TODO: convert to for loop 284 var cssRule = false; 285 do { 286 if (styleSheet.cssRules) { 287 cssRule = styleSheet.cssRules[ii]; 288 } else { 289 cssRule = styleSheet.rules && styleSheet.rules[ii]; 290 } 291 if (cssRule && typeof cssRule.selectorText === 'string') { 292 if (cssRule.selectorText.toLowerCase() == s) { 293 if (d) { 294 if (styleSheet.cssRules) { 295 styleSheet.deleteRule(ii); 296 } else { 297 styleSheet.removeRule(ii); 298 } 299 return true; 300 } else { 301 return cssRule; 302 } 303 } 304 } 305 ii++; 306 } while (cssRule) 307 } 308 } 309 return false; 310 }, 311 /** 312 * Deletes a CSS stylesheet rule. 313 * http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript 314 * 315 * @function 316 * @param {String} selector CSS selector, exactly as entered in the stylesheet. 317 */ 318 deleteRule : function(s) { 319 return Jelo.CSS.getRule(s, true); 320 }, 321 /** 322 * Creates a new CSS stylesheet rule. 323 * http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript 324 * 325 * @function 326 * @param {String} selector CSS selector, exactly as entered in the stylesheet. 327 * @returns {CSSRule} A new rule that can be modified as follows: 328 * var r = Jelo.CSS.createRule('#test'); r.style.color = 'green'; 329 */ 330 createRule : function(s) { 331 if (document.styleSheets) { 332 if (!getRule(s)) { 333 if (document.styleSheets[0].addRule) { 334 document.styleSheets[0].addRule(s, null, 0); 335 } else { 336 document.styleSheets[0].insertRule(s + ' { }', 0); 337 } 338 } 339 } 340 return Jelo.CSS.getRule(s); 341 } 342 }; 343 }(); 344 345 /** 346 * Convenience method. If two arguments are supplied, this is shorthand for 347 * {@link Jelo.CSS.getStyle}. If three arguments are supplied, this is 348 * shorthand for {@link Jelo.CSS.setStyle}. 349 * 350 * @function 351 * @name css 352 * @memberOf Jelo 353 */ 354 Jelo.css = function() { 355 if (arguments.length == 2) { 356 return Jelo.CSS.getStyle(arguments[0], arguments[1]); 357 } 358 if (arguments.length == 3) { 359 return Jelo.CSS.setStyle(arguments[0], arguments[1], arguments[2]); 360 } 361 throw new Error("Jelo.css(element, property) for getStyle, and Jelo.css(element, property, value) for setStyle."); 362 }; 363