/* 'CSS-Regeln lesen/aendern 050706' (c) by cybaer@binon.net
   ---------------------------------
 Inhalt    : Kann eine komplette CSS-Regel oder eine einzelne Eigenschaft auslesen oder aendern
 Aufruf    : cssRule([target[,attribute[,value]]])
 Parameter : target (mixed (optional): wahlweise String oder Objekt.
                     1) Kein Parameter: Es findet nur eine
                        technische Pruefung des Browsers statt
                     2) String mit dem gewuenschten Selektor (case-insensitive!)
                        oder der "Adresse" der Regel in der Form "Nr. des
                        Stylesheets/Nr. der Regel", jeweils beginnend bei 0,
                        z.B. "0/0". Einem Selektor kann ausserdem ein Begrenzer
                        (Clipper) angehaengt werden (selector@clipper), um die
                        Suche zu beschraenken; gueltige Begrenzer sind Medientypen
                        (z.B. um nur Stylesheets fuer den Screen zu durchsuchen:
                        selector@screen) oder die Nummer eines Stylesheets
                        (fuer Suche im 2. Stylesheet: selector@1).
                        Wird kein Begrenzer uebergeben (oder "selector@*"), so
                        werden alle Stylesheets durchsucht. Haengt man einen
                        "leeren" Begrenzer an ("selector@"), so werden nur
                        Stylesheets durchsucht, bei denen kein Medientyp angegeben
                        wurde.
                     3) Style-Objekt: Es wird direkt dieses Objekt verarbeitet.)
             attribute (String (optional), mit wahlweise folgendem Inhalt:
                        1) gewuenschte Eigenschaft die bearbeitet werden soll,
                           in der Syntax von CSS ("font-size") oder JavaScript
                           ("fontSize");
                        2) "rule" (oder leer), fuer die komplette Regel
                        3) weitere spezifische Anweisungen:
                         a) "Objekt-Schluesselwoerter":
                            - "ruleobject" ermittelt das Objekt der gefundenen Regel selbst
                            - "styleobject" ermittelt das Style-Objekt der Regel
                            - "sheetobject" ermittelt das StyleSheet-Objekt der Regel
                            - "rulecollection" ermittelt die Objekt-Collection dem die Regel angehoert
                            - "ownerobject" ermittelt das Objekt des HTML-Elements der Regel
                         b) "sonstige Schluesselwoerter":
                            - "sheetindex" ermittelt die Index-Nummer des Stylesheets der Regel
                            - "rulepointer" ermittelt einen Pointer auf die Regel
                            - "ownermarkup" ermittelt den (interpretierte) HTML-Quellcode des Sylesheets der Regel
             value (String (optional): neuer Wert, ggf. auch leer, der
                       geschrieben werden soll; wird der Parameter nicht
                       uebergeben, so wird der aktuelle Wert ausgelesen)
 Sprache   : JavaScript 1.1 (ungesichert), JavaScript 1.5 (gesichert)
 Quelle    : http://Coding.binon.net/CSS-Rules (cybaer@binon.net)
             Die kostenlose Nutzung der Quelltexte in eigenen Projekten ist
             bei nicht-kommerziellen Projekten (und deren unentgeltlicher
             Herstellung) bei Nennung der Quelle ausdruecklich gestattet.
 InlineFunc: -
 Konstante : -
 Variable  : -
 SystemVar : -
 ExternVar : -
 Rueckgabe : 1) (bool) false wenn Funktion nicht moeglich ist (Browser zu alt
                oder zu bearbeitende Regel existiert nicht - beim Browser
                Konqueror wird ggf. nur ein false zurueckgegeben, falls
                attribute leer ist, da alte Versionen einen Bug bei der dann
                verwendeten JavaScript-Eigenschaft cssText haben).
             2a) (object) wenn attribute ein "Objekt-Schluesselwort" enthaelt.
              b) Je nach "sonstigem Schluesselwort" ein (mixed) Datentyp:
                  - (integer) bei "sheetindex" (oder NaN bei Misserfolg)
                  - (mixed) bei "rulepointer": (string) bei Erfolg (Leerstring,
                    wenn Zugriff nicht erlaubt ist) oder (bool) false, wenn
                    keine Regel gefunden wurde
                  - (string) bei "ownermarkup"
             3a) Wenn value nicht uebergeben wurde:
                 (string) aktuelle Regel wenn attribute "rule" oder leer ist
                 oder den Stil der Eigenschaft, die in attribute definiert wurde.
              b) Wenn value uebergeben wurde (auch leer):
                 (bool) true, wenn value neu gesetzt werden konnte, sonst false
 Anmerkung : 1. Vorsicht bei der Benutzung von @import! Wird @import benutzt,
                koennen bei Direktaddressierung nur Styles *vor* dem ersten
                @import browseruebergreifend identisch bearbeitet werden. Bei
                Angabe eines Selektors werden zwar die Stylesheets (intern
                wie extern) durchsucht, nicht jedoch die importierten Regeln!
             2. Bei sehr großen und mehreren Stylesheets empfiehlt sich ggf.
                die direkte Adressierung, um das Durchsuchen zu vermeiden. Bei
                Angabe eines Selektors sollte der gesuchte Selektor moeglichst
                weit hinten im Quelltext aufgefuehrt werden, da die Regeln von
                hinten durchsucht werden (bei mehreren identischen Selektoren
                ueberstimmen die Eigenschaften des letzten Selektors ggf.
                identische Eigenschaften eines frueheren Selektors.
             3. Wird eine Regel oder Eigenschaft geloescht, ist natuerlich zu
                beachten, dass nunmehr Regeln oder Eigenschaften eines ggf.
                im Quelltext vorstehenden identischen Selektors aktiv werden!
             4. Gruppen-Selektoren (h1, p { color: blue; }) koennen nicht
                so einfach verarbeitet werden, da u.a. der IE sie intern trennt!
                Die Bestandteile des Gruppen-Selektors koennen aber einzeln
                angesprochen werden: alert(cssRule("h1")); alert(cssRule("p"))
                ACHTUNG: Setzt man in diesem Fall die Regel neu, so wirkt die
                Aenderung beim IE auch *nur* beim gewaehlten (Teil-)Selektor,
                waehrend bei den sonstigen Browsern sich die Aenderung auch
                auf die anderen Teile des Mehrfach-Selektors auswirkt! Um also
                auch im IE bei h1 *und* p die Farbe zu aendern, muss es lauten:
                cssRule("h1","color","red"); cssRule("p","color","red");
                ACHTUNG: Im Browser Safari kann man einen Gruppen-Selektor
                nur unter dem 1. Selektor der Gruppe finden und ansprechen!
 Beispiele : HTML  : <style>
                       .aStil { font-size: 2em; font-weight: bold; }
                     </style>
                     <style media="screen">
                       .bStil { color: red; background-color: blue; }
                       #cStil { }
                     </style>
                     <style media="print">
                       .bStil { color: black; background-color: white; }
                       #cStil { }
                     </style>
                     <span class="aStyle">Text A</span>
                     <span class="bStyle">Text B</span>
                     <span id="cStyle">Text C</span>
             Script: alert(cssStyle("0/0"));
             -> Alert: "font-size: 2em; font-weight: bold"
             Script: alert(cssStyle(".aStil","font-size)); oder auch 
                     alert(cssStyle(".astil","fontSize)); oder auch
                     alert(cssStyle(".aStil","styleobject").fontSize);
             -> Alert: "2em"
             Script: cssStyle("0/0","","");
             -> "Text A" erscheint im "Normalstil" (Stile der 1. Regel werden
                geloescht was <style> .aStyle { } </style> entspricht)
             Script: cssStyle("1/0","color","black");
             -> die rote Schriftfarbe von "Text B" (1. Regel im 2. Sheet)
                wird in schwarz geaendert
             Script: cssStyle("#cStil","display","none");
             -> "Text C" (ID ist "cStil") wird versteckt
             Script: alert(cssStyle(".bStil","color"));
             -> Alert: "black" (erster gefundener ".bStil" ist im Printer-Stylesheet)
             Script: alert(cssStyle(".bStil@screen","color"));
             -> Alert: "red" (erster gefundener ".bStil" fuer das Medium "screen")
             Script: alert(cssStyle(".bStil@","color"));
             -> Alert: false (kein ".bStil" in einem allgemeinen Stylesheet)
             Script: alert(cssStyle(".aStil@","font-size"));
             -> Alert: "2em"
*/

function cssRule(target,attrib,value) {
 // Lokale Variable definieren
 var s=0, r=0, i, result=false, clipper, sheetNo=NaN, startSheetNo, endSheetNo, mediaType, unifiedSelectorText, complete, write;
 var sheetObj=false, ruleColl=false, ruleObj=false, styleObj=false, ownerObj=false, rulePointer="", sheetIndex=NaN;

 // Kein Ziel uebergeben: Test, ob Zugriff ueberhaupt technisch moeglich ist
 if(!target && document.styleSheets) {
  result=true;
 }

 // Bereits ermitteltes Objekt uebergeben
 if(typeof(target)=="object") {
  sheetObj=true;
  styleObj=target;
  target=false;
 }

 // Existiert das benoetigte Objekt und gibt es ueberhaupt ein Stylesheet?
 if(document.styleSheets && document.styleSheets.length && target) {

  // Wurde eine direkte Adressierung der Regel ("x/y") uebergeben?
  if(!isNaN(parseInt(target)) && target.indexOf("/")>0) {
   // Adressierung aufteilen in Stylesheet-Nummer ...
   s=parseInt(target.split("/")[0],10);
   // ... und Regel-Nummer
   r=parseInt(target.split("/")[1],10);
   // Fortfahren, wenn Stylesheet existiert (mit Extra-Check als Opera-Workaround)
   if(typeof(document.styleSheets[s])=="object" && document.styleSheets[s]) {
    sheetObj=document.styleSheets[s];
    rulePointer=""+s;
    // HTML-Element ermitteln (W3C-/IE-DOM)
    ownerObj=(sheetObj.ownerNode)?sheetObj.ownerNode:sheetObj.owningElement;
    // Weitere Daten ermitteln, wenn Zugriff moeglich
    if(sheetAccessible(s)) {

     // Objekt nach IE-DOM (vor cssRules-Abfrage wg. IE/Mac, inkl. media-Abfrage wg. Konqueror) ...
     if(sheetObj.rules && typeof(sheetObj.media)=="string") {
      ruleColl=sheetObj.rules;
      // ... falls Regel existiert
      if(ruleColl[r]) {
       ruleObj=ruleColl[r];
       styleObj=ruleObj.style;
       rulePointer+="/"+r;
      }

     // Objekt nach W3C-DOM ...
     } else if(sheetObj.cssRules) {
      ruleColl=sheetObj.cssRules;
      // ... falls Regel existiert
      if(ruleColl[r]) {
       // Nur Typ 1 (Style), nicht 3 (importedStyle)
       if(ruleColl[r].type==1) {
        ruleObj=ruleColl[r];
        styleObj=ruleObj.style;
        rulePointer+="/"+r;
       }
      }
     }

    }
   }

  // Keine direkte Adressierung sondern Suche nach uebergebenem Selektor
  } else {
   // Uebergebenen Selektor in Kleinschrift umwandeln
   target=target.toLowerCase();
   // Wurde im ersten Parameter auch ein Begrenzer uebergeben?
   i=target.indexOf("@")+1;
   if(i) {
    // Ja: Begrenzer herausloesen
    clipper=target.substring(i);
    // Aus dem Rest den Selektor bilden
    target=target.substring(0,i-1);
    // Den Begrenzer als Zahl interpretieren
    sheetNo=parseInt(clipper);
    // Wenn der Begrenzer keine Zahl war, als Media-Type interpretieren - andernfalls: alle Media-Types ("*") verwenden
    mediaType=(isNaN(sheetNo))?clipper:"*"
    // Wenn Begrenzer eine Zahl: Nummer des Stylesheets - sonst: Media-Type
   } else {
    // Kein Begrenzer: alle Media-Types ("*") verwenden
    mediaType="*";
   }

   // Zu durchsuchende Stylesheets festlegen
   if(!isNaN(sheetNo)) {
    // Stylesheet wurde im Begrenzer angegeben
    startSheetNo=sheetNo;
    endSheetNo=sheetNo;
   } else {
    // Vom letzten Stylesheet ...
    startSheetNo=document.styleSheets.length-1;
    // ... bis zum ersten durchsuchen
    endSheetNo=0;
   }

   // Fortfahren, wenn Start- & End-Sheet-Nummer zulaessig sind
   if(startSheetNo>=endSheetNo && startSheetNo<document.styleSheets.length && endSheetNo>=0) {

    // (Eingegrenzte) Stylesheets (rueckwarts) durchgehen
    for(s=startSheetNo; s>=endSheetNo; s-=1) {
     // Bereits vorhandene Positionsdaten sichern
     sheetObj=document.styleSheets[s];
     rulePointer=""+s;
     // HTML-Element ermitteln (W3C-/IE-DOM)
     ownerObj=(sheetObj.ownerNode)?sheetObj.ownerNode:sheetObj.owningElement;

     // Sofern der Zugriff erlaubt ist, alle Regeln (rueckwarts) nach dem gewuenschten Selektor durchsuchen
     if(sheetAccessible(s)) {

      // Zugriff nach IE-DOM (vor cssRules-Zugriff wg. IE/Mac, inkl. media-Abfrage wg. Konqueror)
      if(sheetObj.rules && typeof(sheetObj.media)=="string") {
       ruleColl=sheetObj.rules;
       for(r=ruleColl.length-1; r>=0; r-=1) {
        // Wenn passender Media-Type & passender Selektor:
        if(((mediaType && sheetObj.media.toLowerCase().indexOf(mediaType)>=0) || (!mediaType && !sheetObj.media) || mediaType=="*") && ruleColl[r].selectorText.toLowerCase().indexOf(target)>-1) {

         // Selektor-Text browseruebergreifend vereinheitlichen
         unifiedSelectorText=unifySelectorText(ruleColl[r].selectorText);

         // Wenn passender Selektor:
         if(unifiedSelectorText==target) {
          // Restliche Positionsdaten sichern und ...
          ruleObj=ruleColl[r];
          styleObj=ruleObj.style;
          rulePointer=s+"/"+r;
          // ... Rest des Stylesheets nicht mehr durchsuchen
          break;
         }
        }
       }

      // Zugriff nach W3C-DOM
      } else if(sheetObj.cssRules) {
       ruleColl=sheetObj.cssRules;
       for(r=ruleColl.length-1; r>=0; r-=1) {
        // Wenn Regulaere Style-Regel, Media-Type passend und gesuchter Selektor im Selektor-Text enthalten:
        if(ruleColl[r].type==1 && ((mediaType && sheetObj.media.mediaText.toLowerCase().indexOf(mediaType)>=0) || (!mediaType && !sheetObj.media.mediaText) || mediaType=="*") && ruleColl[r].selectorText.toLowerCase().indexOf(target)>-1) {

         // Selektor-Text browseruebergreifend vereinheitlichen
         unifiedSelectorText=unifySelectorText(ruleColl[r].selectorText);

         // Wenn direkt passender Selektor:
         if(unifiedSelectorText==target) {
          // Restliche Positionsdaten sichern und ...
          ruleObj=ruleColl[r];
          styleObj=ruleObj.style;
          rulePointer=s+"/"+r;
          // ... Rest des Stylesheets nicht mehr durchsuchen
          break;

         // Wenn Gruppen-Selektor:
         } else if(unifiedSelectorText.indexOf(",")>-1) {
          // Selektor-Text aufteilen
          selectorArray=unifiedSelectorText.split(",");
          // Einzelne Selektoren durchgehen ...
          for(i=0; i<selectorArray.length; i++) {
           // ... trimmen ...
           while(selectorArray[i].charAt(0)==" ") { selectorArray[i]=selectorArray[i].substring(1,selectorArray[i].length); }
           while(selectorArray[i].charAt(selectorArray[i].length-1)==" ") { selectorArray[i]=selectorArray[i].substring(0,selectorArray[i].length-1); }
           // ... und bei Erfolg ...
           if(selectorArray[i]==target) {
            // Restliche Positionsdaten sichern und ...
            ruleObj=ruleColl[r];
            styleObj=ruleObj.style;
            rulePointer=s+"/"+r;
            // ... Rest des Gruppen-Selektors nicht mehr durchsuchen
            break;
           }
          }

          // Wenn Regel gefunden wurde, Rest des Stylesheets nicht mehr durchsuchen
          if(styleObj) { break; }
         }
        }
       }
      }

      // Wenn Regel gefunden wurde, restliche Stylesheets nicht mehr durchsuchen
      if(styleObj) { break; }
     }
    }
   }

   // Wenn kein Stylesheet spezifiziert und keine Regel gefunden wurde ...
   if(isNaN(sheetNo) && !styleObj) {
    // ... Zwischenergebnisse wieder loeschen
    sheetObj=false;
    ruleColl=false;
    ownerObj=false;
    rulePointer="";
   }

  }
 }

 // Stylesheet vorhanden?
 if(!sheetObj) {
  // Nein: Beim "sheetindex" anstelle des ueblichen false ein NaN zurueckgeben
  if(attrib=="sheetindex") { result=NaN; }
 } else {

  // Ja: Ueberpruefung auf Schluesselwort in attrib
  switch(attrib) {
   // War das Sheet-Objekt gewuenscht?
   case "sheetobject":
    result=sheetObj;
    break;
   // War die Rule-Collection gewuenscht?
   case "rulecollection":
    result=ruleColl;
    break;
   // War das Rule-Objekt gewuenscht?
   case "ruleobject":
    result=ruleObj;
    break;
   // War das Style-Objekt gewuenscht?
   case "styleobject":
    result=styleObj;
    break;
   // War das Owning-Objekt gewuenscht?
   case "ownerobject":
    result=ownerObj;
    break;
   // War der HTML-Markup gewuenscht?
   case "ownermarkup":
    if(ownerObj.outerHTML) {
     // Wenn outerHTML vorhanden: nutzen
     result=ownerObj.outerHTML;
     // Ergebnis trimmen
     while(result.charCodeAt(0)==13 || result.charCodeAt(0)==10) { result=result.substring(1); } 
    } else {
     // Wenn outerHTML nicht vorhanden: Markup manuell zusammenstellen
     result="<"+ownerObj.tagName;
     for(i=0; i<ownerObj.attributes.length; i++) { result+=' '+ownerObj.attributes[i].name+'="'+ownerObj.attributes[i].value+'"'; }
     result+=(ownerObj.tagName=="LINK")?">":(">"+ownerObj.innerHTML+"</"+ownerObj.tagName+">");
    }
    break;
   // War der Sheet-Index gewuenscht?
   case "sheetindex":
    result=parseInt(rulePointer);
    break;
   // War der Regel-Pointer gewuenscht?
   case "rulepointer":
    // Wenn
    // - Sheet & Regel gefunden -> "x/y"
    // - Sheet gefunden aber Regel nicht -> "x" -> false
    // - Regel nicht gefunden -> false
    // - Sheet gefunden aber Zugriff verboten -> ""
    result=(rulePointer.indexOf("/")>-1 || rulePointer===false)?rulePointer:(sheetAccessible(rulePointer)?false:"");
    break;

  // Kein Schluesselwort: Gewuenschte Regel/Eigenschaft bearbeiten
  default:

   // Komplette Regel bearbeiten oder nur ein gewuenschtes Attribut daraus?
   complete=(!attrib)?true:false;
   // Gibt es einen neuen Wert (=schreiben) oder nicht (=lesen)?
   write=(typeof(value)!="undefined")?true:false;

   // Nur spezifische Eigenschaft bearbeiten?
   if(!complete) {
    // Ggf. Syntax von CSS- auf Script-Schreibweise aendern:
    attrib=propertyToStyle(attrib);
    // Gewuenschte Eigenschaft bearbeiten
    if(write) {
     // (schreiben)
     styleObj[attrib]=value;
     result=true;
    } else {
     // (auslesen)
     result=styleObj[attrib];
    }

   // Konqueror-Bug abfangen
   } else if(styleObj.cssText!=null) {
    // Kompletten Text bearbeiten
    if(write) {
     // (schreiben)
     styleObj.cssText=value;
     result=true;
    } else {
     // (auslesen)
     result=styleObj.cssText.toLowerCase();
    }
   }

  }
 }
 return result;
}

// Selektor-Text browseruebergreifend vereinheitlichen
function unifySelectorText(selText) {
 // selText='.bStil[CLASS~="bStil"] #cStil[ID="cStil"] .aStil[CLASS~="aStil"]'; // Example
 var i, addSelectors, addSelectorsRegExp;
 // Safari-Workaround: Suche nach Klassen-Selektoren, um ...
 addSelectors=selText.match(/\.([A-Za-z]+[\w-]*\[)/g);
 if(addSelectors) {
  for(i=0; i<addSelectors.length; i++) {
   addSelectorsRegExp=new RegExp("\\"+addSelectors[i].substring(0,addSelectors[i].length-1)+"\\[(CLASS|class)~=["+'"'+"|']?"+addSelectors[i].substring(1,addSelectors[i].length-1)+"["+'"'+"|']?\\]","g");
   // ... ggf. ergaenzte Attribut-Selektoren zu entfernen (.Stil[CLASS~="Stil"] -> .Stil)
   selText=selText.replace(addSelectorsRegExp,addSelectors[i].substring(0,addSelectors[i].length-1));
  }
 }
 // Safari-Workaround: Suche nach ID-Selektoren, um ...
 addSelectors=selText.match(/#([A-Za-z]+[\w-]*\[)/g);
 if(addSelectors) {
  for(i=0; i<addSelectors.length; i++) {
   addSelectorsRegExp=new RegExp(addSelectors[i].substring(0,addSelectors[i].length-1)+"\\[(ID|id)=["+'"'+"|']?"+addSelectors[i].substring(1,addSelectors[i].length-1)+"["+'"'+"|']?\\]","g");
   // ... ggf. ergaenzte Attribut-Selektoren zu entfernen (#Stil[ID="Stil"] -> #Stil)
   selText=selText.replace(addSelectorsRegExp,addSelectors[i].substring(0,addSelectors[i].length-1));
  }
 }
 // Generell Kleinschreibung
 selText=selText.toLowerCase();

 return selText;
}

// Style-Schreibweise von CSS auf JS aendern
function propertyToStyle(property) {
 var syntax;
 // 1. Eigenschaften mit reserviertem Bezeichner: Unterscheidung nach JScript- bzw. JavaScript-Syntax
 if(property=="float") { property=((typeof(window.cssFloat)=="undefined")?"style":"css")+property.charAt(0).toUpperCase()+property.substring(1); }
 // 2. Eigenschaften mit Bindestrich
 else if(property.indexOf("-")>=0) {
  // CSS-Syntax am "-" auftrennen, ...
  syntax=property.split("-");
  // ... ersten Teil uebernehmen und ...
  property=syntax[0];
  // ... folgende Teile mit grossem Anfangsbuchstaben
  for(i=1; i<syntax.length; i++) { property+=syntax[i].charAt(0).toUpperCase()+syntax[i].substring(1); }
 }
 return property;
}

// Pruefen, ob Zugriff auf ein Stylesheet moeglich ist (Same-Origin-Policy)
function sheetAccessible(sheetNo) {
 // Testzugriff
 try {
  // Wenn Zugriff nach W3C- oder IE-DOM moeglich
  if(document.styleSheets[sheetNo].cssRules || document.styleSheets[sheetNo].rules) {
   // Status "Zugriff auf Sheet moeglich"
   throw "sheetAccessible";
  }
 // Statusabfrage
 } catch(e) {
  // Wenn "Zugriff auf Sheet moeglich", Rueckgabe true
  return (e=="sheetAccessible");
 }
}


