UWA Widgets coden

Web 2.0, Just Do It…

Je besser man etwas kennt, desto besser kann man darüber lästern… Ich habe beschlossen, mich ganz praktisch durch das Tal der Tränen zu kämpfen und mich auf dieser Wanderung mit den technischen Teilen von Web 2.0 herumzuschlagen. Wie fühlt sich Ajax an? Wieviel Schatten spendet der DOM-Tree? XML oder JSON, JavaScript und APIs sollten die Dämonen auf meinem Weg sein.

Das Resultat

Das Resultat kann im Ecosystem angesehen und installiert werden. Es ist ein Widget, das Adressen über tel.search.ch sucht.
Tel.search.ch bietet ein geniales, gut dokumentiertes REST API an.

Ein paar Screenies?
So siehts in Netvibes aus:
Das tel.search.ch Widget in Netvibes

So in iGoogle:
Das tel.search.ch Widget in iGoogle

Und so in der Sidebar von Windows Vista:
tel.search.ch UWA Widget in Windows Vista Sidebar

UWA Widget Api

Als würdiges Versuchsobjekt habe ich UWA-Widgets ausgesucht , auf die bin ich früher schonmal eingegangen. Versuchsweise habe ich mich schon mit dem Vorgänger herumgeschlagen (MiniAPI), und ich wollte ein Widget, das es erlaubt, Telefonnummern der Schweiz mittels tel.search.ch zu suchen neu implementieren. Es gibt schon einige Lösungen, aber die beschränken sich darauf, ein Fassaden-Formular an das Telefonbuch zu submitten für die Resultate; Meine Lösung sollte brutal dynamisch, inline, etc sein.
Der Vorteil von UWA-Widgets ist, dass sie Standalone, natürlich in Netvibes, in iGoogle, Windows Live, Windows Vista, Opera und bald auch auf vielen Anderen Plattformen genutzt werden können.
Zum Einarbeiten in die UWA-Technologie stehen Tutorials, das Developers Blog und einige Beispiele (inkl Code) zur Verfügung.

JavaScript

Implementiert wird das Ganze in Javascript. Diese Sprache muss genial sein; Hyperdynamisch, an allen Ecken und Enden erweiterbar, etcetc. Mir zeigt sich diese Flexibilität vorallem in der Komplexität und einem ständigen schlechten Gewissen beim Programmieren, denn jeder geniale Winkelzug wird auf irgendeinem Browser nicht laufen. Meine ersten Versuche mit Javascript vor 400 Jahren habe ich mehr als entnervt in viel Bier ertränken müssen, denn da waren nicht nur die Browser inkompatibel, sondern auch die Implementierung in den Browsern selbst war verbugged. Zumindest das hat sich enorm gebessert.
Für altgediente Webentwickler bedingt Ajax und die clientseitige Aktivität eine ganz neue Denkweise. Man muss sich ständig vom Transaktionsorientierten lösen und nicht immer alles serialisieren wollen. Der Effekt der sich dynamisch verändernden Seiten ist aber für einige Entzückungsschreie gut.

Highlights der Implementierung

Neben dem Feature, dass das Widget überhaupt läuft 🙂 (echt ein Highlight, ich hab am Schluss nicht mehr daran geglaubt) hat es noch ein paar wenige Dinge drin, die es vom Null-8-Fünfzehn Widget unterscheiden.

Übersetzung

Das Benutzerinterface kann vollständig übersetzt werden. Die einzelnen Strings sind dabei in einem Objekt abgelegt:

var tsch = {
    lang: {
        de: {
                widgetdesc: 'Suchen Sie im Schweizer Telefonbuch',
                widgettitle: 'tel.search.ch Suche',
                what: 'Wer oder was',
                where: 'Wo',
                companies: 'Firmen',   
[...]

Zuerst wird in onLoad versucht, die Sprache automatisch herauszufinden:

// Language
var curlang=widget.getValue('language');
       
if(curlang=='auto') {
  curlang=widget.lang.substring(0,2);
}
if(!tsch.lang[curlang]) curlang=widget.getValue('defaultlanguage');    
widget.setValue('currentlanguage', curlang);

Danach können einzelne Strings übersetzt werden:

/**
 * Get the localized translation of a word
 */

tsch.translate = function(p) {
    var defaultlanguage=widget.getValue('defaultlanguage');
    var curlang=widget.getValue('currentlanguage');
    var ret=tsch.lang[curlang][p];
    // Try Default Language    
    if(!ret) {
        ret=tsch.lang[defaultlanguage][p];
    }
   
    // Give up
    if(!ret) {
        ret='[['+p+']]';
    }
    return ret;
}

Etwas mehr Mühe mach das Übersetzen des Admininterfaces. Ein Ansatz gibt es im Forum, aber das war dann echt zu mühsam und ist offiziell nicht unterstützt.

Das Web 2.0 Waiter Icon

Das Web 2.0 Warteicon ist einer der grösseren Betrügereien des Web 2.0 🙂 . Das ist ein ganz normales, animiertes Icon das halt ein- und ausgeblendet wird.

rotator

// Show busy-indicator
widget.elements['waiter'].show();

// Hide busy-indicator
widget.elements['waiter'].hide();

Einen Haufen toller Indikatoren gibt es bei Ajaxload.

VCARD

tel.search.ch hat ein geniales REST-API. Man kann direkt auf die VCARD eines Eintrags linken. Mit einem Klick kann dann die Adresse direkt ins lokale Adressbuch übertragen werden.

Technische Vorkehrungen

Die folgenden Vorkehrungen erleichtern das Programmieren.

Logging

Wird im UWA-Modul der Meta „debugMode“ auf true gesetzt, dann können verschiedene Dinge geloggt werden.

<meta name="debugMode" content="false" />

Der Befehl um Daten auszugeben lautet:

widget.log("This will be printed in the Console.");

iGoogle Caching

Soll es auch in iGoogle laufen, so kann einem das Caching ganz schöne Streiche spielen. Umgehen kann man das mit einem Developer Widget wie in den FAQs beschrieben.

Local Proxy

Auf Grund von Sicherheitsbeschränkungen können die Browser keine direkte „Ajaxverbindung“ zu Hosts aufnehmen die sich in der URL von der Quelle des Dokuments unterscheiden. Darum braucht es zum lokalen Testen einen Proxy oder man erhält Fehlermeldungen wie Permission denied to call method XMLHttpRequest.open. Mein Proxy ruft gerade sich selbst nochmals auf, um das Encoding in UTF-8 umzuwandeln( siehe unten).
Folgender Code aktiviert ihn im Widget:

if (document.location && (document.location.hostname == 'localhost' || document.location.hostname == 'apps.oncode.info')) {
            // Use a Proxy to fetch data from tel.search.ch
            var mypath=document.location.pathname;
            var lastslash=mypath.lastIndexOf('/');             
            mypath = mypath.substring(0,lastslash+1);          
            mypath= window.location.protocol+"//"+document.location.hostname+mypath;
            widget.setValue('encodingproxy',mypath+'ajaxProxy.php'); // This will translate ISO to UTF8
            UWA.proxies.ajax = mypath+'ajaxProxy.php'; // The same file acts as proxy...
} else {
            //Just use it as encoding-Translator
            widget.setValue('encodingproxy','http://apps.oncode.info/uwa/tel.search.ch/widget/ajaxProxy.php');
}

Wird das Widget über die UWA-Seite abgerufen, so wird deren Proxy verwendet. Der Quelltext kann natürlich auch heruntergeladen werden.

Werkzeuge und Hilfen

Ohne viele technische Hilfen wäre das Ganze wohl ziemlich katastrophal mühsam geworden.

Firebug

Dieses Firefox-Addon kommt direkt vom Programmierehimmel! Damit kann man den Javascriptcode Schritt für Schritt ausführen lassen, Variablen inspizieren, Breakpoints setzen (mit Bedingungen), etc etc. Ohne das wär die Programmierung ganz einfach die Hölle gewesen. Ich sag nur eins: Downloaden und Installieren.

Entwicklung für den MSIE

Läuft das Widget unter Firefox, so muss es ganz und gar nicht unter MSIE auch laufen. Da fühlt man sich doch gerade an die gute, alte Zeit erinnert! Weiter unten sondere ich noch etwas mehr ab zu diesem Thema. Geholfen hat mir der Microsoft Script Debugger. Der ist ein gar widerspenstig Tier zum Installieren, aber plötzlich funzt er, nach einer wilden Reboot-Seschn. Ein eingestreutes debugger; Statement sollte ihn starten und ein schon fast menschliches Debuggen ermöglichen.

Für dynamisch generiertes HTML empfiehlt sich die Internet Explorer Developer bar.

Javascript in Eclipse

Als Entwicklungsumgebung habe ich Eclipse verwendet. Der Javascripteditor für das WST hat im Moment jedoch keine Outline und auch sonst ist er eher spartanisch. Viel komfortabler ist JSEclipse von Adobe. Nicht richtig frei, aber kosten tut sie nix.

Schwierigkeiten

Schwierigkeiten gabs ne Menge bei der Implementierung! Hier ein kleines Best Of. Vielleicht hilft das jemandem, ein paar Stunden zu sparen.

Kampf der Bibliotheken

Netvibes Javascriptbibliothek ist gross. Dazu scheinen sie noch Prototype und etwas mit Effekten einzubinden. Nun, Javascript ist massig erweiterbar, dynamisch, zur Laufzeit, an allen Ecken und Enden. Von irgendeiner Bibliothek (netvibes) wurde das Grund-Array-Objekt so erweitert, dass jedes Objekt ein Member mit Namen copy mehr hat. Das hat natürlich das maps.search.ch Javascript aus dem Tritt gebracht, da es mittels

    for(item in arraysomething) {

über die Members iterierte und nicht mit dem neuen Mitglied copy gerechnet hatte. Nun wurde es haarig. Es gibt ganz komische Lösungen bei welchen der Code von irgendwo in ein IFrame kopiert wird etc. Ich habe mich – ganz meiner Natur entsprechend – dafür entschieden es simpel zu halten. Um die Karte darzustellen wird ein iframe gemacht und darin mittels PHP an Hand von Parametern dynamisch das Javascript für die Karte generiert wird.

Unterschiede Firefox und Microsoft Internetexplorer

Als die Geschichte dann mal gelaufen ist unter Firefox dachte ich wir seien im Jahre 2007 angekommen und es müsste so auch im Microsoft Internetexplorer laufen. Denkste! Nun ging die grosse Suche los, und das alles ohne meinen geliebten Feuerkäfer. Ich habe zwischendurch echt daran gedacht, das Script Firefox only zu deklarieren.

Die nicht erscheinende Tabelle

Alles funzt, aber die Tabelle die mittels Javascript dynamisch generiert wurde, wird im MSIE einfach nicht dargestellt. Die Developer Toolbar hat mir eindeutig gezeigt das die <table> Knoten eingefügt wurden. Einiges an Fluchen und Recherche hat zu Tage gebracht, dass der MSIE das nur mit <tbody> Tags dynamisch darstellt. Danke. Dummerweise durfte ich nicht mal zu laut darüber lamentieren, denn das entspricht sogar irgendeinem Standard.

getElementByTagNameNS

Das XML-Ergebnis von tel.search.ch benutzt verschiedene Namespaces. MSIE hat keine Tüte was XML-Namespaces sind und man spricht einen <tel:category> Eintrag einfach mit getElementsByTagName('tel:category') an. FireFox ist Perfektionist und will es sehr genau wissen. Er will wissen was von wo kommt und verlangt nach einem getElementsByTagNameNS('http://tel.search.ch/api/spec/result/1.0/','category');. MSIE kennt das mit dem NS am Schluss nicht einmal. Oky, dann schreiben wir halt eine Weiche.

tsch.getPrefixFields = function(prefix, ns, field, xml) {
        var elem;
        // Mozilla
        if (typeof document.getElementsByTagNameNS != 'undefined') {
            elem = xml.getElementsByTagNameNS(ns, field);
        }  else { // MSIE... No clue about namespaces
            elem = xml.getElementsByTagName(prefix+":"+field);
        }
   
        return elem;
}

Verschiedenes

Irgendwie will der MSIE auch, das man Arrays mittels new Array() erzeugt während FireFox das nicht für notwendig befindet. Ok, ich habs dann per Trial and Error herausgefunden.
Eher darstellungstechnisch brauchte es einen Clearer um die Hintergrundfarbe zu korrigieren. Der Clearer ist mittels CSS definiert:

.telform div.clearer {
      clear:both;
      font-size:1px;
      line-height:1px;
      display:block;
      height:1px;
}

Er wird im HTML zum Schluss des Forms eingesetzt:

          <div class="clearer">&nbsp;</div>

Encoding

Ich hasse das Schei?en?odin?! Die dümmste Sache seit es Codes gibt, Herrgottsack. tel.search.ch liefert die Resultate in ISO-8859-1, UWA will UTF-8. Um was alles soll ich mich denn noch kümmern? Nun, ich hab dann meinen Proxy erweitert. Er ist nun nicht mehr nur Proxy um Daten im Lokalbetrieb durchzuleiten, sondern kann auch sich selbst aufrufen oder von aussen aufgerufen werden und den Zeichensatz umwandeln. Bekommt er einen Parameter url so holt er (über sich selbst) die Daten. Ansonsten leitet er den QUERY_STRING an tel.search.ch weiter und wandelt das Resultat in UTF-8.

    if($doencoding) {
        $xml=str_replace("ISO-8859-1","UTF-8",$xml);
        $xml=iconv("ISO-8859-1","UTF-8",$xml);
    }

Benutzerstatistik (Update August 2008)

Der Google-Trackercode funzt definitiv nicht mehr! Um Statistiken über die Verwendung zu erhalten, empfehle ich GetClicky, die ein bilderbasiertes Tracking anbieten:
<img alt="Clicky" src="http://in.getclicky.com/xxxxx-dbx.gif" />

iGoogle Verzeichnis

Ich möchte das Widget natürlich auch ins iGoogle Verzeichnis einbauen. Doch die wollen den Meta-Tag author_email. Dieser wird von Netvibes leider noch nicht durchgeroutet. Es exisitiert eine Issue und eine Anfrage.

Favicon

In den Beispielen wird immer angegeben, dass es ein Favicon vom Typ X-Icon angegeben werden kann. Das wären dann .ico Dateien und würde sinn machen, denn es können verschiedene Grössen in eine Datei gespeichert werden. Angeben kann man es, aber leider sieht man es dann nicht, und die Darstellung wäre doch eine der Kernfunktion eines Icons. Auch mit einer einzelnen Grösse bleibt es unsichtbar. Erst eine profane png Datei bringt das Glück. Ok, wieder was gelernt.

OnClick

Fazit zuerst. Die Dokumentation hat recht: onclick im HTML funktioniert nicht. Also nicht ganz und gar überhaupt nicht, sondern nur ziemlich nicht und meistens nicht zum Beispiel in der Vista Sidebar. Nun hat das Widget eine Listenansicht und ein Klick sollte Details anzeigen. Wie zum Teufel soll das gehen? Man kann einen Link dynamisch erzeugen und eine Funktion angeben:

    var a = widget.createElement('a');
    a.href = 'javascript:void(0)';
    a.onclick = tsch.showDetails(this)
    a.innerHTML = data;

Weitere komische Geschichten

Wenn man sich wundert wieso bei jedem Link target="_blank" angehängt wird, darf man das. Man kann aber auch in wildem Aktionismus dieses Verhalten beenden:

widget.environment.handleLinks = function(){};

Neues Fenster öffnen

Will man plattformunabhängig ein neues Fenster öffnen, so wird man in der Dokumentation darauf hingewiesen, dass dies mit

openURL('http://...')

zu machen habe. Hier widerspricht sich die Doku aber, denn onclick gibts nicht und onClick darf man nicht benutzen. Also bleibt nur das a element dynamische zu erstellen via Javascript. Nun, aber dieses kann nicht in ein Tab eingefüllt werden, weil diese Tabs nur mit Text (HTML) gefüllt werden können und es keinen DOM Ansatzpunkt gibt. Eine entsprechende Anfrage ist unbeantwortet geblieben (liegt vielleicht an der höllischen ID 😉 ?).

Fazit

Ein UWA-Widget zu coden macht Spass. Es aber überall lauffähig zu kriegen nicht mehr so.. Naja, ich habs getan und lebe noch.

5 Gedanken zu “UWA Widgets coden

  1. Benutze bitte den more tag damit nicht alles auf der Startseite steht, danköö

  2. Pingback: Vorbildliches OPAC-Widget von der Jacobs University « Jakoblog — Das Weblog von Jakob Voß

  3. Viel Spass und gute Nerven im Land, in welchem Wahnsinn und Genie nur durch 0en und 1en getrennt sind :)…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.