Scripting:Lib prettyprint

Aus STNE-Wiki

Wechseln zu: Navigation, Suche

Das Problem:

Die Script-Engine erlaubt einem einige echt komfortable und nette Dinge, aber irgendwann ist man an dem Punkt, wo Portale schön aussehen sollen. An diesem Punkt wird man merken, dass man viele Verrenkungen machen muss, damit man Text fett bekommt oder andere Sachen damit machen kann.

Diesem Problem habe ich nun hier Abhilfe geschaffen und die Library "PrettyPrint" eingeführt. Diesen Haufen an Funktionen schiebt man einfach bei sich in das Portal und steuert es so an, wie ganz unten im Beispiel angegeben. Nun kann man den Text, den man in die Funktion steckt schon fast so formatieren wie in einer IGM. Folgende Tags funktionieren: br, b, i, u, img, ul, li, small, font

Es wird derzeit noch geplant, [Spieler:] und solche Kurztags einzubauen, sowieso <a href="">hyperlinks</a>.


Library "PrettyPrint"

#UseInterface Web;

/**
 * prettyPrint
 * Formatiert einen Text gemäß der in IGMs möglichen Funktionen
 * Gibt ein Objekt zurück, dass an das Response-Objekt gehangen werden kann.
 * Wichtig: hier wird nur valides XML-artiges zeug geparst!
 *          HTML-durcheinander-Verdreckungen werden mit Fehlermeldung beantwortet.
 */
Function prettyPrint(text As String) As CHtmlControl {
  Var stack As New CObjectList(); // Stack für die Verschachtelung
  Var tmpObj As CObject; // Damit diese blöde Engine damit klar kommt.
  Var element As New Stackelement(); // Oberste Ebene definieren, macht die Schleife einfacher
  Var textStart As Integer = 0;
  Var nextTagAuf As Integer = text.IndexOf("<"); // Such den ersten HTML-Tag
  Var nextTagZu As Integer = text.IndexOf(">"); // Auch gleich die Position, wo es zu geht
  nextTagAuf = Math.Min(nextTagAuf, nextTagZu); // Das Minimum der beiden ist -1, wenn etwas nicht gefunden wurde, sonst nextTagAuf
  Var tag As String;
  
  While(nextTagAuf > 0) {
    If(textStart < nextTagAuf) {
      // Erstmal ist da Text, hinzufügen
      element.e.Add(text.Substring(textStart, nextTagAuf - textStart));
    }
    
    // Den Tag isolieren
    tag = text.Substring(nextTagAuf + 1, nextTagZu - nextTagAuf - 1); // <dies hier>
    If(String.Compare(tag, 0, "/", 0, 1) = 0) { // Wenn das erste Zeichen innerhalb des Tags "/" ist --> Endtag
      // Wenn der Tag ein schließender Tag ist
      If(tag = element.tag) { // element.tag sieht schon gleich so aus: "/tag"
        tmpObj = stack.Item(stack.Count - 1);
        element = tmpObj;
        stack.RemoveAt(stack.Count - 1); // Letzten vom Stack poppen
      } Else {
        // Hier ist was kaputt im Quellcode
        element.e.Add("[Fehler: schließender Tag " + element.tag + " erwartet, " + tag + "gefunden. Abbruch.]");
        Exit While; // While verlassen
      }
    } Elseif(is_singleTag(tag)) {
      // Wenns ein BR oder dergl ist, ersetzen
      element.e.Add(replaceSingleTag(tag));
    } Else {
      // Ist scheinbar ein umschließender Tag, also verarbeiten
      tmpObj = element;
      stack.Add(tmpObj); // Alten aktuellsten Tag auf den Stack schieben
      element = replaceTag(tag, element); // Baut ein neues Stackelement mit element und endtag
    }
    
    // Lege die neuen Limits fest
    textStart = nextTagZu + 1;
    nextTagAuf = text.IndexOf("<", textStart);
    nextTagZu = text.IndexOf(">", textStart);
    nextTagAuf = Math.Min(nextTagAuf, nextTagZu); // Das Minimum der beiden ist -1, wenn etwas nicht gefunden wurde, sonst nextTagAuf
    
  }
  
  // Letzten Text hinzufügen
  if(textStart < text.Length) { // Wenn da noch Text ist
    element.e.Add(text.Substring(textStart,text.Length - textStart));
  }
  
  Return element.e; // oberste Ebene zurückgeben
}

          
/**
 * replaceTag
 * Bekommt einen Tag und entscheidet dann, was für Formatierungen usw. es daraus macht.
 * Gibt ein HTML-Objekt zurück, in das der Inhalt reingeschoben werden kann
 */
Function replaceTag(text As String, element As Stackelement) As Stackelement {
  text = text.ToLower();
  Var tagType As String = text;
  Var retHtmlPart As New CHtmlSpan();
  Var newHtmlPart As New CHtmlSpan(); // Neues Element, in dem weiterer Inhalt reinkommt.- Wird manchmal gebraucht
  element.e.Add(retHtmlPart);
  
  If(text.IndexOf(" ") > 0) { // Checken, ob der Tag Parameter hat
    tagType = text.Substring(0, text.IndexOf(" "));
    If(tagType = "font") {
      Var firstquote_index As Integer = text.IndexOf("color='");
      Var secondquote_index As Integer = text.IndexOf("'", firstquote_index + 7);
      If(firstquote_index > 0 AND secondquote_index > 0) {
        retHtmlPart.Style.Add('color', text.Substring(firstquote_index + 7, secondquote_index - firstquote_index -7));
      } Else {
        retHtmlPart.Add("[Fehler: Konnte das Font-Tag nicht korrekt Parsen. Quotes checken?]");
      }
    } Else {
      // Unbekannter Tag, gib Fehlermeldung aber nerv nicht weiter.
      retHtmlPart.Add("[Fehler: unbekannter Tag - " + tagType + " - ignoriert.]");
    }
    
  } Else { // Keine Parameter, erstmal diese Tags implementieren
    If(tagType = "b") {
      retHtmlPart.Style.Add('font-weight', 'bold');
    } elseif(tagType = "u") {
      retHtmlPart.Style.Add('font-decoration', 'underline');
    } elseif(tagType = "i") {
      retHtmlPart.Style.Add('font-style', 'italic');
    } elseif(tagType = "small") {
      retHtmlPart.Style.Add('font-size', 'smaller');
    } elseif(tagType = "ul") { // Einen UL-Tag simulieren, indem das SPAN drumherum manipuliert wird.
      retHtmlPart.Style.Add('display', 'block');
      retHtmlPart.Style.Add('margin', '13px 39px');
      retHtmlPart.Add(newHtmlPart);
      retHtmlPart = newHtmlPart; // Hier überschreiben, damit das neue Inhaltselement auch zurückgegeben wird.+
    } elseif(tagType = "li") { // Einen LI-Tag simulieren, indem das SPAN drumherum manipuliert wird.
      retHtmlPart.Style.Add('display', 'list-item');
      retHtmlPart.Style.Add('list-style-image', 'url(http://game.stne.net/t/1/s/li.gif)');
    } Else {
      // Unbekannter Tag, gib Fehlermeldung aber nerv nicht weiter.
      retHtmlPart.Add("[Fehler: unbekannter Tag - " + tagType + " - ignoriert.]");
    }
  }
  
  // Jetzt sollten auf dem retHtmlPart alle passenden Formatierungen usw. drauf sein. Gib mal zurück.
  Return New Stackelement(retHtmlPart, "/" + tagType);
}

/**
 * replaceSingleTag
 * Ersetzt Tags, die man nicht schließen muss direkt und gibt ein CHtmlControl zurück.
 */
Function replaceSingleTag(text As String) As CHtmlControl {
  text = text.ToLower();
  text = text.Replace("""", "'"); // Damit ich nur nach einer Sorte suchen muss
  Var retHtmlPart As New CHtmlSpan();
  
  If(text.StartsWith("img")) {
    Var firstquote_index As Integer = text.IndexOf("src='");
    Var secondquote_index As Integer = text.IndexOf("'", firstquote_index + 5);
    If(firstquote_index > 0 AND secondquote_index > 0) {
      Return New CHtmlImage(text.Substring(firstquote_index + 5, secondquote_index - firstquote_index-5));
    } Else {
      retHtmlPart.Add("[Fehler: Konnte das IMG-Tag nicht korrekt Parsen. Quotes checken?]");
    }
  } Elseif(text = "br") {
    Return New CHtmlBreak();
  } Else {
    retHtmlPart.Add("[Fehler: Unbekanntes Tag - " + text + " ]");
  }
  
  Return retHtmlPart;
}

/**
 * is_singleTag
 * Checkt, ob ein Tag mit einem als single-Tag bekannten Tag anfängt.
 */
Var singleTags[] As String = {"br", "img"}; // Enthält alle einfachen Tags, die keine Ebenen aufspannen
Function is_singleTag(text As String) As Boolean {
  Var walkstring As String;
  For(Each walkstring In singleTags) {
    If(String.Compare(walkstring, 0, text, 0, walkstring.Length) = 0) {
      Return true;
    }
  }
  Return false;
}

/* Ist eine komplexe Datenstruktur, damit mehr als ein Ding aufm Stack liegen kann */
Class Stackelement {
  Var e As CHtmlControl;
  Var tag As String;
  
  Function New() {
    This.e = New CHtmlControl();
    This.tag = "/none";
  }
  
  Function New(e As CHtmlControl, tag As String) {
    This.e = e;
    This.tag = tag;
  }
}

Var test As String = "hier ist ein <b>fetter<font color='red'> roter </font><u>unterstrichener</u> <i>Testtext</i></b>";
Response.Add(prettyPrint(test));
Persönliche Werkzeuge