/* Multiple background CSS Parser for IE6/7/8, Firefox <=3.5 Author: Ben Green (bgreen@chicowebdesign.com) Date: 101117 License: Free Usage: background: url(left.gif) no-repeat 0 0, url(right.gif) no-repeat 100% 0, url(middle.gif) repeat-x 0 0; Also includes support for :active, :hover, and :focus psuedo classes */ jQuery(function($){ //conditional for which browsers you want to support (ie6/7/8,firefox<=3.5) if(($.browser.msie && parseFloat($.browser.version)>=6 && parseFloat($.browser.version)< 9)|| ($.browser.mozilla && parseInt($.browser.version.substr(0,1))<=1 && parseInt($.browser.version.substr(2,1))<=9 && parseInt($.browser.version.substr(4,1))<=1)){ //store the loaded css rules var myCss={}; var putLayerOutside=new Array("input"); /* Supplementary JQuery Extension: * * @author Terry Wooton+Ben Green * @desc Adds a background layer to an element * @version 2 * @example * $("#elaement").addLayer("url('/test.gif') bottom left no-repeat"); * @license free * @param background css * @param outer boolean * @modified Ben Green to support padding correctly and took off weird extra divs... and the ability to put extra wrappers on the outside of elements */ $.fn.addLayer = function(bg,outer,params) { $(this).each(function() { var s = $(this).extend({},params || {}); if(outer){ //put layers on outside var $this=$(this); $this.wrap('
'); $last=$this.parent(); $last.css({"margin":$this.css("margin"), "display":$this.css("display"), "position":$this.css("position"), "top":$this.css("top"), "left":$this.css("left"), "right":$this.css("right"), "bottom":$this.css("bottom"), "float":$this.css("float"), "clear":$this.css("clear"), "z-index":$this.css("z-index"), "height":parseFloat($this.css("height").replace("px",""))+parseFloat($this.css("padding-top").replace("px",""))+parseFloat($this.css("padding-bottom").replace("px","")), "width":parseFloat($this.css("width").replace("px",""))+parseFloat($this.css("padding-left").replace("px",""))+parseFloat($this.css("padding-right").replace("px","")), "-moz-border-radius-bottomright":$this.css("-moz-border-radius-bottomright"), "-moz-border-radius-bottomleft":$this.css("-moz-border-radius-bottomleft"), "-moz-border-radius-topleft":$this.css("-moz-border-radius-topleft"), "-moz-border-radius-topright":$this.css("-moz-border-radius-topright"), "background":bg}); $this.css({"margin":'', "display":'', "position":'', "top":'', "left":'', "right":'', "bottom":'', "float":'', "z-index":'', "clear":'', "height":'', "width":'', "-moz-border-radius-bottomright":'', "-moz-border-radius-bottomleft":'', "-moz-border-radius-topleft":'', "-moz-border-radius-topright":''}); }else{ //put layers on inside $last = ($(this).find('.add_background:last').length > 0 ? $(this).find('.add_background:last') : $(this)); var innerHtml=$last.html(); if($.browser.msie){ $last.html('
'+innerHtml+'
'); }else{ //firefox will add wrapper divs when setting the innerhtml for some reason $last.empty().append('
'+innerHtml+'
'); }; $last = $(this).find('.add_background:last'); if(($.browser.msie && parseFloat($.browser.version)<8)&&($(this).css('position')=="static")){ //width/height screws up negative margin in ie6/7 on elements without position:relative? $last.css({'background':bg}); }else{ $last.css({'background':bg,'width':'100%','height':'100%'}); }; //take care of any padding that would mess up the layout by using negative margins var $parent=$last.parent(); var p=new Array($parent.css("padding-top"),$parent.css("padding-right"),$parent.css("padding-bottom"),$parent.css("padding-left"),$parent.css("height")); if($.browser.msie && parseFloat($.browser.version)<6){ $parent.css("padding","0"); $last.css({'padding-top':p[0],'padding-right':p[1],'padding-bottom':p[2],'padding-left':p[3]}); }else{ $last.css({'padding-top':p[0],'padding-right':p[1],'padding-bottom':p[2],'padding-left':p[3], 'margin-top':'-'+p[0],'margin-right':'-'+p[1],'margin-bottom':'-'+p[2],'margin-left':'-'+p[3]}); if($.browser.msie && parseInt($.browser.version)<=7){ //ie 7 won't push everything all the way out all the time so give it a height $last.css({'height':p[4]}); }; }; //commented out just like extra wrapper div in the constructor above //$last = $(this).find('.add_background div:last'); if(s.insideCss){ $last.css(s.insideCss); }; if(s.insideClass) $last.addClass(s.insideClass); }; }); }; //End Supplementary Extension //remove a layer //@param element: selector for the actual layer element, not the element with the layers function removeLayer(element){ var $layer=$(element); var $parent=$layer.parent(); var p=new Array($layer.css("padding-top"),$layer.css("padding-right"),$layer.css("padding-bottom"),$layer.css("padding-left")); var innerHtml=$layer.html(); $parent.html(innerHtml); if($.browser.msie && parseFloat($.browser.version)<6){ $parent.css({'padding-top':p[0],'padding-right':p[1],'padding-bottom':p[2],'padding-left':p[3]}); }; }; function removeOuterLayer(element){ var $layer=$(element); var $child=$($layer.children()[0]); $child.css({"margin":$layer.css("margin"), "position":$layer.css("position"), "top":$layer.css("top"), "left":$layer.css("left"), "right":$layer.css("right"), "bottom":$layer.css("bottom"), "float":$layer.css("float"), "clear":$layer.css("clear"), "z-index":$layer.css("z-index")}); $child.unwrap(); }; //find lower/uppercase combinations and replace with lowercase for compatability function replaceWithLower(inStr,replaceWith){ var linStr=inStr.toLowerCase(); var pos=linStr.indexOf(replaceWith); while(pos>-1){ inStr=inStr.substr(0,pos)+replaceWith+inStr.substr(pos+replaceWith.length); pos=linStr.indexOf(replaceWith,pos+1); }; return inStr; }; //read in a selector and output and integer precedence score function selectorScore(selector,important){ //decendents and siblings aren't any different to us selector=selector.replace(">"," ").replace("+"," "); var sSpl=selector.split(" "); var score=0; for(var i=0;i-1){ function analyzePosition(p){ var lowest=10000; /*theorical max selector length*/ var which=-1; for(var i=0;i-1)){ lowest=p[i]; which=i; }; }; if(lowest==10000) lowest=-1; return {'lowest':lowest,'which':which}; }; var p=[cPart.indexOf("#",pos),cPart.indexOf(".",pos),cPart.indexOf(":",pos)]; var a=analyzePosition(p); if(pos==0 && a.lowest>0){ //this item began with an element score+=1; pos++; }else if(pos==0 && a.lowest==-1){ //this item is just an element! score+=1; break; }else if(a.lowest>-1){ if(a.which==0){score+=100; /* id found */ }; if(a.which==1){score+=10; /* class found */ }; if(a.which==2){score+=10; /* psuedo-class found */ }; pos=a.lowest+1; }else if(pos==0){ score+=1; /* element found */ }else{ break; }; }; }; return score+(important ? 1000 : 0); }; //in order to not get messed up on things like: //rgba(100,100,100,0.5) //change to: //rgba(100##comma##100##comma##100##comma##0.5) //will only do the change on commas inside of parentheses function replaceInnerComma(str){ var pos=str.indexOf("("); while(pos>-1){ var endPos=str.indexOf(")",pos+1); var newMiddle=str.substr(pos,endPos-pos).replace(/,/g,"##comma##"); str=str.substr(0,pos)+newMiddle+str.substr(endPos); pos=str.indexOf("(",endPos+1); }; return str; }; //readCSS - main function to read css //@param: conts - chunk of css to read in //@param: prop - which function to look for //@param: path - path which to make the urls change to (false to disable) function readCss(conts,prop,path){ var output={}; //get rid of comments conts=conts.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g,''); var pos=conts.indexOf(prop); //loop through all backgrounds in the stylesheet while(pos>-1){ var bgVal=replaceWithLower($.trim(conts.substr(conts.indexOf(":",pos+1)+1,conts.indexOf(";",pos+1)-conts.indexOf(":",pos+1)-1)),"url("); //only do anything if there are actually multiple bgs in the property bgVal=replaceInnerComma(bgVal); if(bgVal.indexOf(",")>-1){ //make paths relative to the stylesheet work out if(path){ var urlPos=bgVal.indexOf("url("); while(urlPos>-1){ //don't do any working on absolute urls if(bgVal.substr(urlPos+4,7).toLowerCase()!="http://"){ var extraSlash=(bgVal.substr(urlPos+4,1) == '/'); var domain=document.URL.substr(0,document.URL.indexOf('/',9)); bgVal=bgVal.substr(0,urlPos+4)+(extraSlash ? domain :path)+bgVal.substr(urlPos+4); }; //seek to next urlPos=bgVal.indexOf("url(",urlPos+1); }; }; //determine if there was an important in this bg value var important=bgVal.length; bgVal=bgVal.replace("!important",""); important=(important==bgVal.length ? false : true); var prevBrace=conts.lastIndexOf("}",pos)+1; //Allow for charset if(conts.lastIndexOf(";",pos)+1>prevBrace) prevBrace=conts.lastIndexOf(";",conts.lastIndexOf("{",pos))+1; if(prevBrace==-1) prevBrace=0; var selector = $.trim(conts.substr(prevBrace,conts.lastIndexOf("{",pos)-prevBrace)); //account for multiple selectors var selSplit=selector.split(","); for(var i=0;i=0;i--){ vals[i]=$.trim(vals[i].replace(/##comma##/g,",")); }; if(inArray(putLayerOutside,this.tagName.toLowerCase())){ //these layers are going on the outside so it may be an input on ie! var curLayer=$element.parent(); while(curLayer.parent().hasClass("add_background_outer")){ curLayer=curLayer.parent(); }; for(var i=vals.length-1;i>=0;i--){ if(!curLayer.hasClass("add_background_outer")){ //not enough backgrounds! add more! $element.addLayer(vals[i],true); }else{ //we still have backgrounds, let's use 'em curLayer.css('background',vals[i]); //jump into the next layer curLayer=$(curLayer.children()[0]); }; }; //are there any extra backgrounds? //if so, axe them! while(curLayer.hasClass("add_background_outer")){ var saveLayer=curLayer; //jump into the next layer curLayer=$(curLayer.children()[0]); removeOuterLayer(saveLayer); }; }else{ //replacing current backgrounds var count=$element.find(".add_background").length; var curLayer=$($element.children(".add_background")[0]); for(var i=vals.length-1;i>=0;i--){ if(curLayer.length<1){ //not enough backgrounds! add more! $element.addLayer(vals[i],false); }else{ //we still have backgrounds, let's use 'em curLayer.css('background',vals[i]); //jump into the next layer curLayer=$(curLayer.children(".add_background")[0]); }; }; //are there any extra backgrounds? //if so, axe them! while(curLayer.length){ var saveLayer=curLayer; //jump into the next layer curLayer=$(curLayer.children(".add_background")[0]); removeLayer(saveLayer); }; }; }); }; //mouseover event for elements with hover multiple bgs function elementMouseover(e){ if($(this).attr("jQueryMultipleBgCurSelector")!=$(this).attr("jQueryMultipleBgActiveSelector")){ applyBg(this,"jQueryMultipleBgHoverSelector"); }; }; //mouseout event for elements with hover multiple bgs function elementMouseout(e){ applyBg(this,"jQueryMultipleBgStaticSelector"); }; //mousedown event for elements with active multiple bgs function elementMousedown(e){ applyBg(this,"jQueryMultipleBgActiveSelector"); }; //mouseup event for elements with active multiple bgs function elementMouseup(e){ if($(this).attr("jQueryMultipleBgHoverSelector")){ applyBg(this,"jQueryMultipleBgHoverSelector"); }else{ applyBg(this,"jQueryMultipleBgStaticSelector"); }; }; //focus event for elements with focus multiple bgs function elementFocus(e){ applyBg(this,"jQueryMultipleBgFocusSelector"); }; //blur event for elements with focus multiple bgs function elementBlur(e){ applyBg(this,"jQueryMultipleBgStaticSelector"); }; // Returns true if the passed value is found in the // array. Returns false if it is not. function inArray(myArray,value,caseSensitive) { var i; for (i=0; i < myArray.length; i++) { // use === to check for Matches. ie., identical (===), if(caseSensitive){ //performs match even the string is case sensitive if (myArray[i].toLowerCase() == value.toLowerCase()) { return true; }; }else{ if (myArray[i] == value) { return true; }; }; }; return false; }; //apply an array of css selectors with multiple backgrounds function applyCss(root){ if(root==undefined) root=document; for(var sel in myCss){ var lsel=sel.toLowerCase(); if((lsel.indexOf(":hover")>-1)||(lsel.indexOf(":active")>-1)||(lsel.indexOf(":focus")>-1)){ var leftSide, rightSide; if(lsel.indexOf(":hover")>-1){ leftSide=sel.substr(0,lsel.indexOf(":hover")); rightSide=sel.substr(lsel.indexOf(":hover")+6); //set the reference selector on each element $(leftSide+rightSide,root).each(function(){ var curSelector=$(this).attr("jQueryMultipleBgHoverSelector"); //make sure the current selector still works if((curSelector)&&(!inArray($(replaceWithLower(curSelector,":hover").replace(/:hover/g,'')),this))) curSelector=undefined; if((!curSelector)||(myCss[curSelector].selScore-1){ leftSide=sel.substr(0,lsel.indexOf(":active")); rightSide=sel.substr(lsel.indexOf(":active")+7); //set the reference selector on each element $(leftSide+rightSide,root).each(function(){ var curSelector=$(this).attr("jQueryMultipleBgActiveSelector"); //make sure the current selector still works if((curSelector)&&(!inArray($(replaceWithLower(curSelector,":active").replace(/:active/g,'')),this))) curSelector=undefined; if((!curSelector)||(myCss[curSelector].selScore-1){ leftSide=sel.substr(0,lsel.indexOf(":focus")); rightSide=sel.substr(lsel.indexOf(":focus")+6); //set the reference selector on each element $(leftSide+rightSide,root).each(function(){ var curSelector=$(this).attr("jQueryMultipleBgFocusSelector"); //make sure the current selector still works if((curSelector)&&(!inArray($(replaceWithLower(curSelector,":focus").replace(/:focus/g,'')),this))) curSelector=undefined; if((!curSelector)||(myCss[curSelector].selScore