Der Code ist noch im Teststadium und es wird sich sicher noch einiges ändern, aber da ich schon ein paar Tester bräuchte poste ich schonmal die derzeitige Version:
Script:Zeigen
Code: Alles auswählen
// Grundscriptsammlung
// V. 0.0.1
GSS = new (function () {
  // Mit Global greifen wir auf öffentliche Funktionen zu und definieren diese auch innerhalb von GSS.
  Global = this;
  /* Funktionen */
  // getChatData - lädt und speichert alle wichtigen Daten über den Chat und den eingeloggten User
  Global.getChatData = function (interval, startStreamer)
  {
    var chatData = RegExp(/server(\d)\.webkicks\.de\/(\w+)\/(\w+)stream\/(\w+)\/.+\/\w+/).exec(parent.document.getElementsByName("mainframe")[0].src);
    // Server, Chatname, Anmeldungsstatus, Nick (ohne Case Sensitive), erste Chateinstellungen (Raumsystem), Intervall für Streamer
    Global.chat.server = chatData[1];
    Global.chat.name = chatData[2];
    Global.chat.rank = (chatData[3] == "guest_") ? 0 : 1;
    Global.chat.nick = chatData[4];
    Global.chat.settings[0] = (parent.ChannelFrame) ? 1 : 0;
    Global.chat.streamInterval = interval;
    // Auslesefunktionen für das Eingabeframe, die Onlineliste und, falls vorhanden, das Raumauswahlframe aufrufen
    Global.getInputData();
    Global.getOnlineData();
    if (Global.chat.settings[0])
    {
      Global.getChannelData();
    }
    // Auslesen der Teamliste
    Global.xmlRequest({
      path:Global.chat.name+"/api/get_teamlist",
      method:"GET",
      func: function (r)
      {
        Global.getTeamData(r);
      }
    });
    // Soll der Streamer standardmäßig gestartet werden?
    if (startStreamer)
    {
      Global.startStreamer();
      Global.chat.streaming = 1;
    }
  };
  // getInputData - lädt und speichert alle relevanten Daten aus dem Eingabeframe
  Global.getInputData = function ()
  {
    if (Global.inputLoaded())
    {
      var p = parent.info.document;
      // eigenen Nick in Case Sensitive speichern
      Global.chat.nick = Global.stripTags(p.getElementsByTagName("b")[p.getElementsByTagName("b").length-1].innerHTML);
      // Ist der User Teammitglied und nicht Hauptadmin?
      if (p.getElementsByTagName("a")[p.getElementsByTagName("a").length-1].innerHTML != "ALARM" && Global.chat.rank != 4)
      {
        Global.chat.rank = (p.getElementsByTagName("script")[0].innerHTML.match("function CallMenue")) ? 3 : 2;
      }
      // User-Objekt erstellen, falls noch nicht vorhanden
      Global.updateUser(Global.chat.nick, {status: 1, rank: Global.chat.rank, color: Global.extractColors(p.getElementsByTagName("b")[p.getElementsByTagName("b").length-1])});
      // Links und Einstellungen (Smileys, Topliste, Profile) auslesen
      Global.chat.links = [];
      Global.chat.settings = [0,0,0];
      for (var i = 1; i < p.links.length; i++)
      {
        var link = RegExp(/^http:\/\/outlink\.webkicks\.de\/dref\.cgi\?url\=(.+)/i).exec(p.links[i].location);
        if (link)
        {
          Cache.menuLinks.push([link[1], p.links[i].text]);
        }
        else if (p.links[i].location == "javascript:CallRL()")
        {
          Global.chat.settings[1] = 1;
        }
        else if (p.links[i].location == "http://server"+Global.chat.server+".webkicks.de/"+Global.chat.name+"/top/wk")
        {
          Global.chat.settings[2] = 1;
        }
        else if (p.links[i].location == "javascript:CallProfil()")
        {
          Global.chat.settings[3] = 1;
        }
      }
    }
    else
    {
      window.setTimeout(function () { GSS.getInputData() }, 500);
    }
  };
  // getOnlineData - lädt und speichert alle relevanten Daten aus dem Onlineframe
  Global.getOnlineData = function ()
  {
    if (Global.onlineLoaded())
    {
      // Daten zum derzeitigen Raum speichern
      Global.chat.currentRoom = [RegExp(/server\d\.webkicks\.de\/cgi-bin\/ol\.cgi\?cid=\w+&raum=(.+)/).exec(parent.document.getElementsByName("rightFrame")[0].src)[1], []];
      // User im Raum auslesen
      var divs = parent.rightFrame.document.getElementById("bd:").getElementsByTagName("div");
      for (var i = 0; i < divs.length; i++)
      {
        if (divs[i].id && !RegExp(/\W/i).test(divs[i].id))
        {
          Global.chat.currentRoom[1].push(divs[i].id);
          // User-Objekt erstellen, falls noch nicht vorhanden
          Global.updateUser(divs[i].id, {status: 1});
        }
      }
      Global.chat.currentRoom[1].sort();
    }
    else
    {
      window.setTimeout(function () { GSS.getOnlineData() }, 500);
    }
  };
  // getChannelData - lädt und speichert alle relevanten Daten aus dem Raumauswahl-Frame
  Global.getChannelData = function ()
  {
    if (Global.channelLoaded())
    {
      // Zugängliche Räume auslesen
      var options = parent.ChannelFrame.document.getElementsByTagName("option");
      Global.chat.rooms = [];
      for (var i = 0; i < options.length; i++)
      {
        Global.chat.rooms[i] = options[i].value.split(" ");
        // Hauptchat als "main" speichern, Privatraum als "sep.NICK" (interne Raum-IDs, werden z.B. für das Onlineframe benötigt)
        if (!Global.chat.rooms[i][1])
        {
          Global.chat.rooms[i] = "sep."+Global.chat.nick;
        }
        else if (Global.chat.rooms[i][1] == "Hauptchat")
        {
          Global.chat.rooms[i] = "main";
        }
        else
        {
          Global.chat.rooms[i] = Global.chat.rooms[i][1];
        }
      }
      Global.chat.rooms.sort();
    }
    else
    {
      window.setTimeout(function () { GSS.getChannelData() }, 500);
    }
  };
  // getTeamData - lädt und speichert alle relevanten Daten über das Team
  // r - XMLHttpRequest-Objekt, das von xmlRequest weitergegeben wurde
  Global.getTeamData = function (r)
  {
    if (r.readyState == 4)
    {
      Global.chat.team = [[],[],""];
      var content = r.responseXML.documentElement;
      // Mods auslesen
      for (var i = 0; i < content.getElementsByTagName("mod").length; i++)
      {
        Global.chat.team[0].push(content.getElementsByTagName("mod")[i].firstChild.data);
        // User-Objekt erstellen, falls noch nicht vorhanden
        Global.updateUser(Global.chat.team[0][i], {rank: 2});        
      }
      for (var i = 0; i < content.getElementsByTagName("admin").length; i++)
      {
        Global.chat.team[1].push(content.getElementsByTagName("admin")[i].firstChild.data);
        // User-Objekt erstellen, falls noch nicht vorhanden
        Global.updateUser(Global.chat.team[1][i], {rank: 3});        
      }
      Global.chat.team[0].sort();
      Global.chat.team[1].sort();
      Global.chat.team[2] = content.getElementsByTagName("hauptadmin")[0].firstChild.data;
      // User-Objekt erstellen, falls noch nicht vorhanden
      Global.updateUser(Global.chat.team[2], {rank: 4});
      // Bin ich Hauptadmin?
      if (Global.chat.nick == Global.chat.team[2])
      {
        Global.chat.rank = 4;
      }
    }
  };
  // stripTags - entfernt HTML-Tags aus einem String
  // str: zu bearbeitender String
  Global.stripTags = function (str)
  {
    return str.replace(/<\/?[^>]+>/ig, "");
  };
  // stripScripts - entfernt Script- und Style-Tags samt Inhalt.
  // str: zu bearbeitender String
  Global.stripScripts = function (str)
  {
    return str.replace(/<(?:script|style)[^>]*>(?:.*)?<\/(?:script|style)>/ig, "");
  };
  // stripBadChars - Entfernt kritische Zeichen aus einem String
  // str - zu bearbeitender String
  Global.stripBadChars = function (str)
  {
    return str.replace(/</g, "<").replace(/>/g, ">").replace(/\"/g, """).replace(/\'/g, "'");
  };
  // stripRegExp - escaped spez. RegExp-Zeichen in einem String sodass der String als RegExp benutzbar wird
  // str: zu bearbeitender String
  Global.stripRegExp = function (str)
  {
    return str.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1");
  };
  // stripReplacers - filtert alle Webkicks-Replacer in einem Textstring. Gibt ein Array mit dem neuen String und den Replacern (Name und Dateiendung) zurück.
  // str - zu bearbeitender String
  Global.stripReplacers = function (str)
  {
    reps = [];
    while ((/<span onclick\=\"javascript: repClick\(\'[^\']+\'\)" style\=\"cursor: pointer;\"><img src=\"\/\w+\/replacer\/[^\.]+\.\w+\" alt\=\"\:[^\"]+\"><\/span>/i).test(str))
    {
      // Replacer speichern
      reps.push(RegExp(/<span onclick\=\"javascript: repClick\(\'[^\']+\'\)" style\=\"cursor: pointer;\"><img src=\"\/\w+\/replacer\/([^\.]+)\.(\w+)\" alt\=\"\:[^\"]+\"><\/span>/i).exec(str).slice(1));
      // Replacer aus String entfernen
      str = str.replace(/<span onclick\=\"javascript: repClick\(\'[^\']+\'\)" style\=\"cursor: pointer;\"><img src=\"\/\w+\/replacer\/[^\.]+\.\w+\" alt\=\"(\:[^\"]+)\"><\/span>/i, "$1");
    }
    return [str, reps];
  };
  // stripSEVars - filtert die SE-Variablen %me% und %user% und ersetzt sie entsprechend mit ihren Text-Equalienten. Für %user% wird der entsprechende Usernick zurückgegeben.
  // str - zu bearbeitender String, dontUpdate: bestimmt, ob Userobjekte geupdatet werden sollen
  Global.stripSEVars = function (str, dontUpdate)
  {
    var vars = [];
    // String als HTML-Element speichern
    var dom = document.createElement("div");
    dom.innerHTML = str;
    for (var i = 0; i < dom.getElementsByTagName("b").length; i++)
    {
      var nickDom = dom.getElementsByTagName("b")[i];
      var content = Global.stripTags(nickDom.innerHTML);
      // Wir sind hier ziemlich vorsichtig, deshalb kommt nun eine seeehr lange Abfrage die unter anderem Länge, HTML-Inhalt und eine ganze Menge anderes Zeugs prüft :)
      if (!nickDom.getElementsByTagName("b")[0] && !content.match(/\W/) && content.length < 21 && content.length > 1 && nickDom.firstChild.nodeName == "FONT" && (!nickDom.childNodes[1] && (!nickDom.firstChild.childNodes[1] && nickDom.firstChild.firstChild.nodeName == "#text" || nickDom.firstChild.childNodes[1] && nickDom.firstChild.firstChild.nodeName == "FONT" && nickDom.firstChild.lastChild.nodeName == "FONT") || nickDom.childNodes[1] && nickDom.firstChild.nodeName == "FONT" && nickDom.lastChild.nodeName == "FONT"))
      {
        if (dom.getElementsByTagName("b")[i].innerHTML == Cache.user[self.user[0]].color[1])
        {
          // %me%, gegebenenfalls Userobjekt erneuern
          if (!dontUpdate)
          {
            Global.updateUser(Global.chat.nick, {color: Global.extractColors(nickDom)});
          }
          dom.getElementsByTagName("b")[i].innerHTML = "%me%";
        }
        else
        {
          // %user%, gegebenenfalls Userobjekt erneuern
          if (!dontUpdate)
          {
            Global.updateUser(content, {color: Global.extractColors(nickDom)});
          }
          dom.getElementsByTagName("b")[i].innerHTML = "%user%";
          vars[1] = content;
        }
      }
    }
    return vars;
  };
  // onlineLoaded - prüft, ob die Onlineliste geladen wurde
  Global.onlineLoaded = function ()
  {
    return !!(parent.rightFrame.document && parent.rightFrame.document.getElementById("bd:"));
  };
  // inputLoaded - prüft, ob das Eingabeframe korrekt geladen wurde
  Global.inputLoaded = function ()
  {
    return !!(parent.info.document && parent.info.document.eingabe);
  };
  // channelLoaded - prüft, ob das Raumauswahlframe korrekt geladen wurde
  Global.channelLoaded = function ()
  {
    return !!(parent.ChannelFrame.document && parent.ChannelFrame.document.getElementsByTagName("select")[0]);
  };
  // sendText - sendet einen Text in den Chat
  // text: zu sendender Text; user: Absender des Texts
  Global.sendText = function (text, user)
  {
    // Soll der User den Text absenden?
    if (!user || Global.chat.nick.toLowerCase() == Global.stripTags(user).toLowerCase())
    {
      var path = (Global.chat.rank) ? "chat" : "guest_ct";
      path = "/cgi-bin/"+path+".cgi";
      Global.xmlRequest({
        path: path,
        method: "POST",
        str: "user="+Global.chat.nick+"&pass="+parent.info.pass+"&cid="+Global.chat.name+"&message="+escape(text).replace(/\+/g, "%2B").replace(/%u/g, "%26#x")
      });
    }
  };
  // xmlRequest - lädt Daten von einer Datei auf dem WK-Chatserver
  // Erwartet ein Object (data) mit folgenden Angaben:
  // path: Pfad auf dem WK-Server, ohne zusätzlichen Slash am Anfang - MUSS immer angegeben werden!
  // method: Request-Methode (POST, GET, ...) - MUSS immer angegeben werden!
  // str: String mit Daten für den Request
  // func: Funktion, die bei onreadystatechange ausgeführt wird
  Global.xmlRequest = function (data)
  {
    var xml = new XMLHttpRequest();
    xml.open(data.method, "http://server"+Global.chat.server+".webkicks.de/"+data.path, true );
    // Funktion reinpacken oder klar als undefined definieren
    xml.onreadystatechange = (data.func) ? function (e) { data.func(xml) } : undefined;
    xml.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');
    // Daten absenden
    if (data.str)
    {
      xml.send(data.str);
    }
    else
    {
      xml.send(null);
    }
  };
  // getCookie - lädt Inhalt eines Cookie aus.
  // id: ID des Cookies
  Global.getCookie = function (id)
  {
    id += "=";
    var cookies = parent.document.cookie;
    for (var i = 0; i < cookies.length; i++)
    {
      // Cookie gefunden?
      if (cookies.substring(i, i + id.length) == id)
      {
        // Rausfinden wie lang der Cookie ist und den Wert zurückgeben
        var position = cookies.indexOf(";", i + id.length);
        position = (position > -1) ? position : cookies.length;
        return cookies.substring(i + id.length, position);
      }
    }
    return false;
  };
  // setCookie - speichert einen Cookie
  // id: ID des Cookies; content: Inhalt des Cookies; exp: Ablaufdatum des Cookies
  Global.setCookie = function (id, content, exp)
  {
    var cookie = id + "=" + content;
    // Hat der Cookie ein Ablaufdatum?
    cookie += (exp) ? ";expires=" + exp.toGMTString() : '';
    parent.document.cookie = cookie;
  };
  // startStreamer - diese Funktion startet den Streamer sobald der Chat fertig geladen wurde.
  Global.startStreamer = function ()
  {
    // Alles fertig geladen?
    if (Global.inputLoaded() && Global.onlineLoaded() && (!Global.chat.settings[0] || Global.choiceLoaded()) && Global.chat.team)
    {
      Global.streamer();
    }
    else
    {
      setTimeout(function () { GSS.startStreamer(); }, 1000);
    }
  };
  // streamer - die allgemeine Script-Intervallfunktion, über die alle intervallabhängige Scripts ausgeführt werden.
  Global.streamer = function ()
  {
    // Durchlauf staten, solange nicht alle Chatzeilen überprüft sind
    Streamer: while (Global.chat.streamCount < Global.chat.streamContent.length)
    {
//      try
//      {
        // getLineData auf die Zeile anwenden, Daten speichern
        Global.chat.currentLine = Global.getLineData(Global.chat.streamContent[Global.chat.streamCount]);
        // Zeile unkommentieren, um Ergebnisse im Stream zu sehen :)
//        document.write([Global.chat.currentLine.type,Global.chat.currentLine.text,Global.chat.currentLine.user,Global.chat.currentLine.time,Global.chat.currentLine.guest,Global.chat.currentLine.vars]);
//        for (var i = 0; i < Global.scripts.functions.length; i++)
//        {
//          Global.scripts.functions[i]();
//        }
        Global.chat.streamCount++;
//      }
//      catch (error)
//      {
//        document.write(error);
//        setTimeout(function () { GSS.streamer(); }, 10000);
//        break Streamer;
//      }
    }
    // Ist nichts schiefgelaufen? Dann den nächsten Durchlauf vorbereiten.
    if (Global.chat.streamCount == Global.chat.streamContent.length)
    {
      setTimeout(function () { GSS.streamer(); }, GSS.chat.streamInterval);
    }
  }
  // getLineData - klassifiziert und "ordnet" eine Chatzeile
  // line: HTML-Objekt, das an die Funktion weitergegeben wird
  // 0: normal
  // 1: /link - 0: URL, die gepostet wurde
  // 2: /me und SE
  // 3: /away
  // 4: /away back
  // 5: andere kursive Befehle, einschließlich /hp, /mail und /icq
  // 6: flüster an jmd. (user[1] = Empfänger)
  // 7: flüster von jmd. (user[1] = Empfänger)
  // 8: /team
  // 9: /comment und /commentall
  // 10: Login
  // 11: Login aus Raum - 0: Raum
  // 12: Logout
  // 13: Logout aus Raum - 0: Raum; 1: User wurde gemoved
  // 14: /mecol und /col - type: definiert, ob es sich um /mecol (0) oder /col (1) handelt
  // 15: Flooding - 0: Verwarnungs-Anzahl (falls nicht gegeben: Flooding-Kick)
  // 16: Kicks, Banns, Deletes, Restores, Knebel - 0: Typ der Aktion (0: Knebel, 1: Restore, 2: Kick, 3: Bann, 4: /makemod, 5: /makeadmin, 6: /modkick, 7: /adminkick, 8: IP-Bann; 9: Delete; 1: Knebelzeit bei Knebel oder IP bei IP-Bann
  // 17: Chatbot Allg. - 0; PM (1 = ja, 0 = nein)
  // 18: /w - 0: Alle Räume samt Usern
  // 19: /iplist - 0: Array aller IPs mit zugehörigen Usern
  // 20: /rmip - 0: ausgelesene IP
  // 21: Timeout-Warnung
  // 22: Sonstiges
  //
  // type: Zeilentyp; text: Zeilentext; user: Liste der involvierten User; vars: zusätzliche Variablen; time: Array mit Timestamp; guest: 1 wenn die Zeile von einem Gast ist
  Global.getLineData = function (line)
  {
    // Zeilenumbrüche vorsichtshalber entfernen
    line.innerHTML = line.innerHTML.replace(/\n/g, "");
    // Alle wichtigen Daten vordefinieren damit wir am Ende keine unnötigen Fehler bekommen.
    var data = {type:22,text:"",user:[],vars:[],time:"",guest:0};
    // Hat die Zeile Inhalt?
    if (line.firstChild)
    {
      // Hat die Zeile einen Timestamp?
      if (RegExp(/^\s*\(\d+:\d+\)\s+.*/).test(Global.stripTags(line.innerHTML)))
      {
        // Timestamp speichern
        data.time = line.getElementsByTagName("font")[0].innerHTML.replace("(", "").replace(")", "").split(":");
        if (line.childNodes[2] && line.childNodes[2].nodeName == "IMG" && line.childNodes[2].src.indexOf("/pfeilg.gif") > -1 && RegExp(/^\s*\(\d+:\d+\)\s+.*/).test(Global.stripTags(line.innerHTML)))
        {
          // Typ 9 - /comment und /commentall
          data.type = 9;
          data.user[0] = line.getElementsByTagName("font")[1].title;
          data.text = line.getElementsByTagName("span")[0].innerHTML;
        }
        else
        {
          // Basisdaten speichern
          lineArray = RegExp(/^\s*\(\d+:\d+\)\s+(\w+)(\W)\s?(.*)/).exec(Global.stripTags(Global.stripScripts(line.innerHTML)));
          // Standard-Chatzeile?
          if (lineArray)
          {
            lineArray = lineArray.slice(1);
            // Zeilentrennungszeichen als Indikator für den Zeilentyp benutzen
            switch (lineArray[1])
            {
              case ":":
                data.user[0] = lineArray[0];
                if (line.getElementsByTagName("a")[0] && line.getElementsByTagName("a")[0].innerHTML == lineArray[2])
                {
                  // Typ 1 - /link
                  data.type = 1;
                  data.text = line.getElementsByTagName("a")[0].innerHTML;
                  data.vars.push(RegExp(/dref\.cgi\?url\=(.*)/).exec(line.getElementsByTagName("a")[0].href)[1]);
                }
                else
                {
                  // Typ 0 - Normale Chatzeile
                  data.type = 0;
                  data.text = RegExp("title\\=\\\""+data.user[0]+"(?: \\(Gast\\))?\\\">: (.*)$", "i").exec(line.innerHTML)[1].replace(/<\/font>$/, "");
                  data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                }
                break;
              case " ":
                data.user[0] = lineArray[0];
                if (line.firstChild.nodeName == "SPAN")
                {
                  // Typ 12 - Logout
                  data.type = 12;
                  data.text = RegExp(/<span class=\"commandcolor\">(.*)<\/span>(?:\s)?<\/i>/).exec(line.innerHTML)[1];
                  data.guest = (line.getElementsByTagName("i")[0].firstChild.nodeValue == data.user[0]) ? 1 : 0;
                }
                else if (line.childNodes[2])
                {
                  var child = line.childNodes[2];
                  switch(child.nodeName)
                  {
                    case "IMG":
                      if (child.src.indexOf("/pfeil.gif") > -1 && lineArray[2].indexOf("i"))
                      {
                        // Typ 3 - /away
                        data.type = 3;
                        data.text = (lineArray[2].indexOf("(") > -1) ? RegExp(/meldet sich kurz ab \((.*)\)(?:[^)]*)$/).exec(Global.stripScripts(line.innerHTML))[1] : "";
                      }
                      else
                      {
                        // Typ 5 - andere /me-gestylten Zeilen
                        data.type = 5;
                        data.text = lineArray[2];
                      }
                      break;
                    case "FONT":
                      if (child.firstChild && child.firstChild.nodeName == "IMG")
                      {
                        if (child.firstChild.src.indexOf("/gruen.gif") > -1)
                        {
                          data.text = RegExp(/<span class=\"commandcolor\">(.*)<\/span>(?:\s)?<\/i>/).exec(line.innerHTML)[1];
                          if (line.getElementsByTagName("span")[line.getElementsByTagName("span").length-1].firstChild.nodeName == "I")
                          {
                            // Typ 11 - Login aus einem anderen Raum
                            data.type = 11;
                            data.vars[0] = line.getElementsByTagName("b")[line.getElementsByTagName("b").length-1].innerHTML;
                            data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                          }
                          else
                          {
                            // Typ 10 - normaler Login
                            data.type = 10;
                            data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                          }
                        }
                        else if (line.getElementsByTagName("small")[0])
                        {
                          data.text = RegExp(/<span class=\"commandcolor\">(.*)<\/span>(?:\s)?<\/i>/).exec(line.innerHTML)[1];
                          if (line.getElementsByTagName("small")[0].getElementsByTagName("i")[0])
                          {
                            // Typ 13 - Logout in anderen Raum
                            data.type = 13;
                            data.vars[1] = 1;
                            data.vars[0] = line.getElementsByTagName("b")[line.getElementsByTagName("b").length-1].innerHTML;
                            data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                          }
                          else
                          {
                            // Typ 12 - Logout
                            data.type = 12;
                            if (RegExp(/\((.+)\)/).exec(line.getElementsByTagName("small")[0].innerHTML)[1] == "Browser geschlossen")
                            {
                              data.guest = (line.getElementsByTagName("i")[0].firstChild.nodeValue == data.user[0]) ? 1 : 0;
                            }
                          }
                        }
                        else if (line.getElementsByTagName("span")[line.getElementsByTagName("span").length-1].firstChild.nodeName == "I")
                        {
                          // Typ 13 - Logout in anderen Raum
                          data.type = 13;
                          data.text = RegExp(/<span class=\"commandcolor\">(.*)<\/span>(?:\s)?<\/i>/).exec(line.innerHTML)[1];
                          data.vars[0] = line.getElementsByTagName("b")[line.getElementsByTagName("b").length-1].innerHTML;
                          data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                        }
                        else
                        {
                          // Typ 12 - normales Ausloggen
                          data.type = 12;
                          data.text = RegExp(/<span class=\"commandcolor\">(.*)<\/span>(?:\s)?<\/i>/).exec(line.innerHTML)[1];
                          data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                        }
                      }
                      else
                      {
                        // Typ 2 - /me und Scripting-Engine
                        data.text = RegExp(/i> (.*)$/i).exec(line.innerHTML)[1].replace(/<\/i><\/font>$/, "");
                        data.type = 2;
                        data.guest = (line.getElementsByTagName("span")[0].innerHTML == data.user[0]) ? 1 : 0;
                      }
                      break;
                    case "B":
                      if (child.getElementsByTagName("span")[0].firstChild.nodeName == "#text")
                      {
                        if (lineArray[2].indexOf("flüsterst"))
                        {
                          // Typ 7 - Flüstern (von jmd.)
                          data.type = 7;
                          data.user[1] = Global.chat.nick;
                        }
                        else
                        {
                          // Typ 6 - Flüstern (an jmd.)
                          data.type = 6;
                          data.user = [Global.chat.nick, RegExp(/an (\w+)/).exec(lineArray[2])[1]];
                        }
                        data.text = RegExp(/<font color=\"red\">(.*)$/).exec(line.innerHTML)[1].replace(/<\/font><\/b>$/, "");
                      }
                      else if (!lineArray[2].indexOf("meldet sich wieder zurück"))
                      {
                        // Typ 4 - Away-Rückmeldung
                        data.type = 4;
                      }
                      else
                      {
                        // Typ 14 - /mecol und /col
                        data.type = 14;
                        data.vars.type = (line.getElementsByTagName("b")[line.getElementsByTagName("b").length-1].getElementsByTagName("font")[0].innerHTML == lineArray[0]) ? 0 : 1;
                      }
                      break;
                  }
                }
                break;
              case "-":
                switch (lineArray[0])
                {
                  case "Chat":
                    if (lineArray[2].indexOf("Bot-PM"))
                    {
                      lineArray[2] = lineArray[2].replace("Bot: ", "");
                      if (line.getElementsByTagName("small")[0])
                      {
                        // Typ 16: moderative Aktion
                        data.type = 16;
                        data.user[0] = RegExp(/by (\w+)/).exec(line.getElementsByTagName("small")[0].innerHTML)[1];
                        if (RegExp(/- .+/).exec(line.innerHTML))
                        {
                          data.text = RegExp(/- (.*)\)/).exec(line.innerHTML)[1];
                        }
                        if (RegExp(/Neuer (?:Mod|Ad)/).exec(lineArray[2]))
                        {
                          data.vars[0] = (lineArray[2].indexOf("Neuer A")) ? 4 : 5;
                          data.user[1] = RegExp(/: (\w+)/).exec(lineArray[2])[1];
                        }
                        else
                        {
                          if (RegExp(/^(\w+) /).exec(lineArray[2]))
                          {
                            data.user[1] = RegExp(/^(\w+)/).exec(lineArray[2])[1];
                            switch (RegExp(/^[\w\.]+ (\w+)/).exec(lineArray[2])[1])
                            {
                              case "wird":
                                data.vars[0] = 0;
                                data.vars[1] = RegExp(/für (\d+)/).exec(lineArray[2])[1];
                                if (RegExp(/für \d+ .+ Minute\(n\) geknebelt/).exec(lineArray[2]))
                                {
                                  data.text = RegExp(/für \d+ (.+) Minute\(n\) geknebelt/).exec(line.innerHTML)[1];
                                }
                                break;
                              case "kicked":
                                data.vars[0] = 2;
                                break;
                              case "banned":
                                data.vars[0] = 3;
                                break;
                              case "restored":
                                data.vars[0] = 1;
                                break;
                              case "deleted":
                                data.vars[0] = 9;
                                break;
                              case "bekommt":
                                data.vars[0] = (lineArray[2].indexOf("Administrator-") > -1) ? 6 : 7;
                                break;
                            }
                          }
                          else
                          {
                            data.vars[0] = 8;
                            data.vars[1] = RegExp(/^([\d\.]+)/).exec(lineArray[2])[1];
                          }
                        }
                      }
                      else if (lineArray[2].indexOf("Flooding-Verwarnung") > -1 || lineArray[2].indexOf("Flooding gekickt!") > -1)
                      {
                        // Typ 15 - Flooding
                        data.user[0] = line.getElementsByTagName("font")[1].title;
                        data.guest = (line.getElementsByTagName("b")[1]) ? 0 : 1;
                        data.type = 15;
                        if (lineArray[2].indexOf("Flooding-Verwarnung") > -1)
                        {
                          data.vars[0] = (lineArray[2].slice(0,1) == "1") ? 1 : 2;
                        }
                      }
                      else if (!lineArray[2].indexOf("Alarm-Ruf!"))
                      {
                        // Typ 17 - Chatbot Allgemein
                        data.type = 17;
                        data.text = lineArray[2];
                      }
                    }
                    else
                    {
                      lineArray[2] = lineArray[2].replace("Bot-PM: ", "");
                      if (!lineArray[2].indexOf("IP von "))
                      {
                        // Typ 20 - /rmip
                        data.type = 20;
                        data.user[0] = RegExp(/IP von (\w+)/).exec(lineArray[2])[1];
                        data.vars[0] = RegExp(/: (.+)/).exec(lineArray[2])[1];
                      }
                      else
                      {
                        // Typ 17 - Chatbot Allgemein
                        data.type = 17;
                        data.text = lineArray[2];
                        data.vars[0] = 1;
                      }
                    }
                    break;
                  case "Team":
                    if (line.getElementsByTagName("font")[1].title == "Team-Nachricht")
                    {
                      // Typ 8 - /team
                      data.type = 8;
                      data.text = RegExp(/<span class=\"not_reg\"><b> (.*)<\/span>/i).exec(line.innerHTML)[1].replace(/<\/b>$/,"");;
                      data.user[0] = RegExp(/von (\w+)/).exec(line.getElementsByTagName("font")[3].innerHTML)[1];
                    }
                    else
                    {
                      // Typ 22 - sonstiges
                      data.text = lineArray[2].replace("Nachricht: ", "");
                    }
                    break;
                  default:
                    break;
                }
                break;
              case "´":
                // Typ 5 - sonstige kursive Befehle
                data.user[0] = lineArray[0];
                data.type = 5;
                data.text = lineArray[2];
                break;
              default:
                break;
            }
          }
          else
          {
            // Typ 15 - Flooding
            data.type = 15;
            data.vars[0] = (line.getElementsByTagName("span")[0].innerHTML.slice(1,2) == "1") ? 1 : 2;
            data.user[0] = line.getElementsByTagName("font")[1].title;
            data.guest = (line.getElementsByTagName("b")[1]) ? 0 : 1;
          }
        }
      }
      else
      {
        if (line.firstChild && line.firstChild.firstChild.nodeName == "FONT") 
        {
          if (line.firstChild.firstChild.innerHTML == "IP-Liste")
          {
            // Typ 19 - /iplist
            data.type = 19;
            if (line.getElementsByTagName("span")[0])
            {
              data.vars[0] = [];
              for (var i = 0; i < line.getElementsByTagName("span").length; i++)
              {
                data.vars[0][i] = [line.getElementsByTagName("span")[i].getElementsByTagName("b")[0].innerHTML];
                if (line.getElementsByTagName("span")[i].getElementsByTagName("font")[0])
                {
                  data.vars[0][i][1] = line.getElementsByTagName("span")[i].getElementsByTagName("font")[0].innerHTML.replace(/ /g, "").split(",");
                }
                else 
                {
                  data.vars[0][i][1] = [line.getElementsByTagName("span")[i].childNodes[1].nodeValue.replace(": ", "")];
                }
              }
            }
          }
          else 
          {
            // Typ 21 - Timeout-Warnung
            data.type = 21;
          }
        }
        else if (line.lastChild.nodeName == "FONT" && !line.lastChild.innerHTML.indexOf("Info:")) 
        {
          // Typ 18 - /w
          data.type = 18;
          var child = line.childNodes;
          data.vars[0] = [];
          for (var i = 0; i < child.length; i++)
          {
            if (child[i].nodeName == "SPAN")
            {
              var index = data.vars[0].push([child[i].getElementsByTagName("u")[0].innerHTML])-1;
              data.vars[0][index][1] = {};
              for (var j = 0; j < child[i].getElementsByTagName("span").length; j++)
              {
                var user = child[i].getElementsByTagName("span")[j];
                var nick = Global.stripTags(user.innerHTML).replace("(G)", "");
                if (nick.indexOf("*") > -1)
                {
                  nick = data.vars[0][index][2] = nick.replace("(*)", "");
                }
                else 
                {
                  data.vars[0][index][2] = 0;
                }
                data.vars[0][index][1][nick] = 1;
              }
            }
          }
        }
      }
/*
      // Ist der User ein Gast?
      if (guest)
      {
        Cache.user[self.user[0]].rank = 0;
      }
      // Sind in der Zeile Daten für die Nickfarbe?
      else if (self.type < 24 || self.type > 27 && self.type < 33)
      {
        // Loggt der User sich gerade ein?
        if (self.type == 9 || self.type == 10)
        {
          Cache.user[self.user[0]].status = 1;
        }
        // Hat der User den Raum verlassen?
        else if (self.type > 10 && self.type < 20 || self.type == 30)
        {
          Cache.user[self.user[0]].status = 0;
        }
        // Benutzt der User /away?
        else if (self.type == 20)
        {
          updateUser(self.user[0],
          {
            status: 2,
            away: self.text
          });
        }
        // Meldet der User sich aus /away zurück?
        else if (self.type == 21)
        {
          updateUser(self.user[0],
          {
            status: 1,
            away: ""
          });
        }
        // Farbobjekt suchen und prüfen
        for (var i = 0; i < dom.getElementsByTagName("b").length; i++)
        {
          var nickDom = dom.getElementsByTagName("b")[i];
          if (stripTags(nickDom.innerHTML) == self.user[0] && !nickDom.getElementsByTagName("b")[0] && nickDom.firstChild.nodeName == "FONT" && (!nickDom.childNodes[1] && (!nickDom.firstChild.childNodes[1] && nickDom.firstChild.firstChild.nodeName == "#text" || nickDom.firstChild.childNodes[1] && nickDom.firstChild.firstChild.nodeName == "FONT" && nickDom.firstChild.lastChild.nodeName == "FONT") || nickDom.childNodes[1] && nickDom.firstChild.nodeName == "FONT" && nickDom.lastChild.nodeName == "FONT"))
          {
            // Farbe parsen
            if (!Cache.user[self.user[0]].color || Cache.user[self.user[0]].color[1] != nickDom.innerHTML)
            {
              Cache.user[self.user[0]].parseColor(nickDom);
            }
            break;
          }
        }
        // Spezialfall Scripting-Engine: Replacer %me% und %user% wieder einsetzen
        if (self.type == 1)
        {
          dom = document.createElement("div");
          dom.innerHTML = self.text;
          for (var i = 0; i < dom.getElementsByTagName("b").length; i++)
          {
            var nickDom = dom.getElementsByTagName("b")[i];
            var content = stripTags(nickDom.innerHTML);
            if (!nickDom.getElementsByTagName("b")[0] && !content.match(/\W/) && content.length < 21 && content.length > 1 && nickDom.firstChild.nodeName == "FONT" && (!nickDom.childNodes[1] && (!nickDom.firstChild.childNodes[1] && nickDom.firstChild.firstChild.nodeName == "#text" || nickDom.firstChild.childNodes[1] && nickDom.firstChild.firstChild.nodeName == "FONT" && nickDom.firstChild.lastChild.nodeName == "FONT") || nickDom.childNodes[1] && nickDom.firstChild.nodeName == "FONT" && nickDom.lastChild.nodeName == "FONT"))
            {
              self.vars[0] = 1;
              // %me%
              if (dom.getElementsByTagName("b")[i].innerHTML == Cache.user[self.user[0]].color[1])
              {
                dom.getElementsByTagName("b")[i].innerHTML = "%me%";
              }
              // %user%
              else
              {
                if (!self.user[1])
                {
                  self.user[1] = content;
                  updateUser(self.user[1],
                  {
                    color: dom.getElementsByTagName("b")[i]
                  });
                }
                dom.getElementsByTagName("b")[i].innerHTML = "%user%";
              }
            }
          }
          self.text = dom.innerHTML.replace(/<b><b>%me%<\/b><\/b>/ig, "%me%").replace(/<b>%user%<\/b>/ig, "%user%");
        }
        // Spezialfälle /col und /mecol: alten Farbstring speichern, neue Farbe generieren
        else if (self.type == 22 || self.type == 23)
        {
          self.vars[0] = Cache.user[self.user[0]].color[0];
          Cache.user[self.user[0]].parseColor(dom.getElementsByTagName("b")[dom.getElementsByTagName("b").length-1]);
        }
      }
*/
    }
    return data;
  };
  // updateUser - erstellt oder erneuert ein User-Element für den entsprechenden User
  // nick: Nick des Users; data: Objekt mit allen Daten, die geupdatet werden sollen
  Global.updateUser = function (nick, data)
  {
    // Gibt es den User bereits?
    if (!Global.userList[nick])
    {
      Global.userList[nick] = new Global.User(nick, data);
    }
    else
    {
      Global.userList[nick].update(data);
    }
  };
  // expandUser - fügt zusätzliche Standard-Einstellungen zur User-Klasse hinzu
  Global.expandUser = function ()
  {
  };
  // extractColors - liest Farben und Schriftart aus einem Usernick aus. Gibt ein Array mit bis zu drei Werten zurück (Farbe1, Farbe2, Schriftart).
  // dom: HTML-Element mit Nick als Inhalt (ohne B-Tags)
  Global.extractColors = function (dom)
  {
    var col = [];
    // Ist der Nick überhaupt gefärbt?
    if (dom.getElementsByTagName("font")[0])
    {
      // Mecol oder Col?
      if (dom.getElementsByTagName("font").length > 1)
      {
        // Schriftart angegeben?
                 if (!dom.childNodes[1])
        {
                         dom = dom.firstChild;
                      col[2] = dom.face;
                    }
                    col[1] = dom.lastChild.color;
      }
            col[0] = dom.firstChild.color;
    }
    return col;
  };
/*
  Klasse User
  Klasse zum Speichern von Usern und entsprechenden Einstellungen.
  Erwartet: nick: Nick des Users
  Methoden:
    update: setzt weitere Daten in das Element ein.
      Erwartet: data: Objekt mit den neuen Daten
  Variablen:
    nick: Nick des Users
    status: Online-Status des Users (Standard: 0)
      0: nicht im Raum
      1: im Raum
    rank: Systeminterner Rang des Users (Standard: 0)
      0: Gast
      1: User
      2: Mod
      3: Admin
    color: Farbdaten des Users (Standard: User hat Gastfarben)
      0: Farbe 1
      1: Farbe 2
      2: Schriftart
*/
  Global.User = function (nick, data)
  {
    var self = this;
    self.nick = nick;
    self.status = 0;
    self.rank = 0;
    self.color = [];
    self.update = function (data)
    {
      for (var i in data)
      {
        self[i] = data[i];
      }
    }
    self.update(data);
  };
  // In diesem Objekt werden alle User gespeichert
  Global.userList = {};
  /* Variablen */
  // server: Server-ID
  // name: Chatname
  // nick: Nick
  // currentRoom: derzeitiger Raum (interner Raumname, Userliste)
  // rooms: alle betretbaren Räume des Chats (interner Raumname)
  // rank: chatinterner eigener Status (0 = Gast, 1 = User, 2 = Mod, 3 = Admin, 4 = Hauptadmin)
  // *color: Farbschema (Farbe 1, Farbe 2, Schriftart)
  // team: Liste aller Teammitglieder (Mods, Admins, Hauptadmin)
  // links: alle selbsteingetragenen Links im Eingabeframe
  // settings: Raumsystem aktiviert, Smileys aktiviert, Topliste aktiviert, Profile aktiviert
  // streamContent: alle td-Elemente des Chatstreams
  // streamCount: bisher verarbeitete td-Elemente
  // streamInterval: Aufruf-Intervall der Streamer-Scripts
  // streaming: definiert, ob die Streamer-Funktion momentan ausgeführt wird oder nicht
  // currentLine: letzte bearbeitete Chatzeile
  Global.chat = {
    streamContent: document.getElementsByTagName("td"),
    streamer: 0,
    settings: [0,0,0,0]
  };
  Global.chat.streamCount = Global.chat.streamContent.length,
  // scripts: speichert alle relevanten Scriptdaten (Scriptliste, Streamer-Unterfunktionen, ...)
  // functions: alle Funktionen, die über streamer aufgerufen werden sollen
  // list: Script-Liste
  Global.scripts = {
    functions: [],
    list: {}
  };
});
GSS.getChatData(500, 1);- alle Funktionen sind nun unter dem globalen Objekt "GSS" gespeichert. Das heißt, dass ihr z.B. für strip_tags nun GSS.stripTags aufrufen müsst.
 - ebenso sind globale Variablen nun unter GSS.chats zu finden (myStatus ist nun z.B. GSS.chat.rank).
 - es gibt eine globale Intervallfunktion, die alle Scripts verwalten kann, die einen Intervall benötigen.
 - ein allgemeines User-Element, auf welches alle Scripts zugreifen können und das auch erweitert werden kann.
 - eine ganze Menge neuer Funktionen, die mehr oder weniger nützlich für den Scripteralltag sein sollten!  
 Insbesondere GSS.getTeamData und GSS.chat.team wird sicher für viele ziemlich interessant sein. 
- Erweiterungsfunktion für die Userklasse
 - System zum Abfangen von Fehlern im globalen Intervall
 - Script-Supportsystem. Jedes Script kann dort eine ID eintragen und so kann man sich z.B. mit nur einem Befehl alle im Chat eingebauten Scripts ausgeben lassen.
 - Optimierungen, Optimierungen, Optimierungen...
 - Rückwärtskompatibilität zu alten Scripts
 - Testen in anderen Browsern, außer Firefox (WebKit-Browser sollten keine Probleme machen, Opera an sich auch nicht, der IE wird aber wohl in älteren Versionen bei xmlRequest rummucken)
 

