Postbox-Betatest

Wohoo, ich bin Beta-Tester. Wahrscheinlich zusammen mit 2342 anderen Leuten, aber immerhin ein limitierter und somit elitĂ€rer Benutzerkreis 🙂

Postbox baut auf Mozilla auf (so wie z.B. Thunderbird auch), macht aber alles besser, schöner und grĂŒner. Ich werde mal testen und berichten 🙂

Links:

Javascript: getter und setter

Nix spektakulĂ€res, bin ich gerade rĂŒbergestolpert. Nachdem ich mich ja schon ĂŒber Sichtbarkeit ausgelassen habe, kommt jetzt der Trick wie man getter und setter definiert.

Bisher hab ich das immer so gemacht:

function Field(val){
    /** @private */
    var value = val;

    /** @public*/
    this.getValue = function(){
        return value;
    };

    /** @public*/
    this.setValue = function(val){
        value = val;
    };
}

Ist auch wunderbar, allerdings bissle lĂ€stig das immer zu schreiben. Deshalb hier das ganze im „Mozilla Style“:

function Field(val){
    var value = val;

    this.__defineGetter__("value", function(){
        return value;
    });

    this.__defineSetter__("value", function(val){
        value = val;
    });
}

PotthĂ€sslich in der Tat, aber macht genau dasselbe wie der Code drĂŒber. Wenn man jetzt ignoriert das „value“ umbedingt private sein muss, dann sieht das sogar „schön“ aus:

function Field(val){
    this.value = val;
}

Field.prototype = {
    get value(){
        return this._value;
    },
    set value(val){
        this._value = val;
    }
};

Bringt mir jetzt nicht so besonders viel, aber vielleicht brauchts ja jemand anders.

Links:

MochiTest-Lite

Just a reminder to myself, because I want to try that extension:

Mochitest is a unit testing framework created by contributors to Mozilla projects. Mochitest is based on MochiKit , the incredibly useful Javascript library. MochiKit as the basis for writing unit tests has been proven very useful to developing Mozilla applications because of the thick layers of Javscript code in applications like Firefox.

What then, is Mochitest-Lite?
Mochitest, as it is used at Mozilla, requires Python to drive the command line interface, and automate the testing. Python is a very powerful tool for this purpose because, along with automation, it can overwrite and then rewrite configuration files like manifests and preferences, all while having a minimal impact on the application being tested. Mochitest also uses a client-server architecture to serve test documents to the application being tested. On top of that, an application needs to be built with the Mozilla build system to work with Mochitest.

This makes sense for large applications like Firefox, but what about a unit testing framework for developers of small XULrunner applications and extensions for existing Mozilla applications? These developers have different requirements. Mochitest-Lite is a way to get the same test writing tools and automation without the overhead of running Mochitest the same way it is used to test Mozilla code.

Javascript-Klassen und Objekte

Habe gerade noch das hier gefunden: Class-Based vs. Prototype-Based Languages

Ich hab zwar schon hier auf einen schönen Artikel verwiesen, aber jetzt nochmal das ganze aus meinem Munde.

Javascript is Objektorientiert. Schon immer. Glaub ich. 1995 das erstmal mit Netscape veröffentlicht, sind wir inzwischen bei Javascript 1.8 angekommen (seit 18. Dezember 2007). Javascript wird ĂŒbrigens von Mozilla „gemanaged“. Und Javascript ist nicht Ecmascript. Vielmehr ist Javascript ein Dialekt von Ecmascript.

Soviel dazu. Javascript implementiert ECMAScript, genauer ECMA-262, edition 3. Innerhalb davon gibt es keine „Klassen“ im eigentlichen Sinne. Viel eher werden direkt Objekte geboren.

function meinObjekt () {
    this.zahl = 1;
}

Und schon haben wir ein Objekt/Klasse mit dem Namen „meinObjekt“. Und eine Eigenschaft „zahl“ hat es auch.

var objekt = new meinObjekt();
window.alert(objekt.zahl);

Und somit kriegen wir ein Fenster mit einer „1“ drinnen. Es gibt auch private und öffentliche Eigenschaften/Methoden, allerdings ist Javascript da etwas bockig:

function meinObjekt () {
     var self = this;
 
    /** @private */
    var foobar = "private";
    var privateMethod = function () {
        window.alert(foobar  + " " + self.barfoo);
    };
 
    /** @public */
    this.barfoo = "public";
    this.public_method = function () {
        window.alert(foobar + " " + this.barfoo);
        private_method();
    };
}

Wer sich wundert was das „self“ soll, das liegt an Javascript. WĂ€hrend der Zugriff von öffentlichen auf private Methoden prima klappt, ist der Zugriff von privaten auf öffentliche Methoden … anders :). Hierzu wird einfach „this“ in „self“ kopiert und dann kann wie „this“ verwendet werden. Jippie.

Ich verwende den Spass gerade so:

var logger = function() {
};
logger.prototype = {
	log : function(msg) {
		console.log(this.getNow() + msg);
	},
	getNow : function() {
		return new Date();
	}
};
var foobar = new logger();
logger.log("foobar"); // prints "foobar"

Man spricht hier von „prototyp-basierter“ Objektorientierung. Interessant wirds in dem „neuen“ Standard ECMA-262, edition 4. Dort gibt es dann tatsĂ€chlich auch Klassen:

class C {
	var val // a variable property
	var large = Infinity // a variable property
	const x = 3.14 // a constant property
	function f(n) { return n+val*2 } // a method property
}

Da ja Javascript schön dynamisch ist, gibt es auch dynamische Klassen:

dynamic class C {
}
c = new C
c.x = 37 // adds a property
delete c.x // removes it again

Weiter gehts mit Vererbung, jetzt mit SchlĂŒsselwörtern wie „extends“:

class C {
	var val
	function f(n) { return n+1 }
}
class BetterC extends C {
	var large = 123456 // new value property
	override function f(n) { return n*3 } // overridden method property
}

Es gibt auch „final“ als SchlĂŒsselwort, eine so bezeichnete Klasse kann dann keine Kinder kriegen. Und zu guter Letzt werden auch Interfaces eingefĂŒhrt. Somit wird Ecmascript und somit auch Javascript an die „normale“ Objektorientierung herangefĂŒhrt. Die komplette Spezifikation des „neuen“ ECMAScripts findet sich unter:

http://www.ecmascript.org/es4/spec/overview.pdf

Javascript aus FF-Erweiterung in der Webseite benutzen

Langer Titel, kurzer Blogeintrag. Ich bastel gerade daran rum, diverse Javascript-Funktionen innerhalb der HTML-Seite zu benutzen. Das kann ziemlich hÀsslich werden:

var head = this.getContentDocument().getElementsByTagName("head")[0];
var script = this.getContentDocument().createElement("script");
var showImage = this.getContentDocument().createTextNode("function showImage(id) { "
		 + "var image = document.getElementById(id);"
		 + "image.style.display = \"inline\"; "
		 + "image.parentNode.previousSibling.addEventListener(\"mouseout\",
		 function() {"
		 + "window.setTimeout(fadeImage, 1500, id);"
		 + "}, false)"
		 + "}");
script.appendChild(showImage);
head.appendChild(script);

Alles doppelt escapen und so. Kein Spass. HÀsslich zum Lesen. Lösung:

var head = this.getContentDocument().getElementsByTagName("head")[0];
var script = this.getContentDocument().createElement("script");
script.setAttribute("src", "resource://jslibs/foobar.js");
script.setAttribute("type", "text/javascript");
head.appendChild(script);

Nix spektakulĂ€res, aber innerhalb der Datei „foobar.js“ kann man jetzt ‚ganz normales‘ Javascript schreiben. Ich freu mich ĂŒber sowas 😉

Wie man das Laden einer Seite abfĂ€ngt …

Ohne Witz. Seit 4 Wochen doktor ich daran rum und ENDLICH hab ich den richtigen (bzw. funktionellen) Weg gefunden wie man es macht:
overlay.xul:

<overlay id="extensionId" 
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script type="application/x-javascript" src="chrome://extensionId/content/myJs.js"/>
</overlay>
</pre>
<strong>myJs.js:</strong>
<pre lang="javascript">
Components.utils.import("resource://jslibs/test.js");
function startupWrapper() {
	var appcontent = document.getElementById("appcontent"); // browser
	if (appcontent) {
		appcontent.addEventListener("DOMContentLoaded", function() {
					justATest(getBrowser());
				}, true);
	}
}

test.js:

var EXPORTED_SYMBOLS = ["justATest"];
justATest = function(gBrowser) {
	var body = gBrowser.mCurrentBrowser.contentDocument.getElementsByTagName('body')[0];
}

Herzlichen GlĂŒckwunsch. Vorher hab ich „nur“ auf DOMContentLoaded gehört, das hat mir dann aber beschert, dass meine Funktion pro XUL-Dokument aufgerufen wurde, also z.B. fĂŒr die Web Developer Toolbar, die Locationbar, den Browser an sich und dann erst die Webseite. Vorher hab ich das klĂ€glich mit

if (gBrowser.mCurrentBrowser.contentDocument.documentURI != "about:blank") {
  // do something
}

abbeholfen, aber das ist ja doof 🙂 Jetzt gehts. Hooray

Javascript dynamisch laden

Heute habe ich mich hauptsÀchlich damit beschÀftigt, wie man Javascript-Dateien innerhalb von Javascript-Dateien includiert. Mein erster Ansatz war (etwas XUL-spezifisch, aber prinzipiell klappt das auch im HTML-DOM):

var jsFiles = [
    "chrome://myEntension/content/lib/domain.js",
		"chrome://myExtension/content/lib/helper.js",
    "chrome://myExtension/content/lib/js_locale.js",
		"chrome://myExtension/content/lib/urlParser.js"
    ];
var root = document.getElementById("someId")
for (var i = 0; i < jsFiles.length; i++) {
	var scriptElement = document.createElement("script");
	scriptElement.setAttribute("type", "application/x-javascript");
	scriptElement.setAttribute("src", jsFiles[i]);
  root.appendChild(scriptElement);
}

Das hat auch eigentlich soweit ganz gut geklappt, im DOM waren alle drin. Nur war irgendwie keine der includierten Funktionen … verfĂŒgbar. So als ob die Knoten in den DOM reingehĂ€ngt wurden, aber das src-Attribute nicht ausgelesen und .. geparst.

Nunja, nachdem ich damit eine Weile rumgedoktert habe, wurde im im Mozilla-IRC-Channel auf Components.utils.import hingewiesen. Das ist doch genau das was ich brauche. Also sieht das jetzt so aus:

var jsFiles = [
    "chrome://myEntension/content/lib/domain.js",
		"chrome://myExtension/content/lib/helper.js",
    "chrome://myExtension/content/lib/js_locale.js",
		"chrome://myExtension/content/lib/urlParser.js"
    ];
for (var i = 0; i < jsFiles.length; i++) {
  Components.utils.import(jsFiles[i]);
}

WunderhĂŒbsch. Klappt nur nicht, weil chrome://-URLs nicht erlaubt sind. Man braucht file:// oder resource://. Also schreibt man ins chrome.manifest:

resource jslibs content/lib/

und schreibt obige chrome://-URL um in:

"resource://jslibs/helper.js"

WunderprĂ€chtig. Jetzt klappts. Fast. In jede Datei die man importiert muss man noch reinschreiben was denn exportiert werden soll. Klingt umstĂ€ndlich, der tiefere Sinn hat sich mir noch verschlossen, aber erstmal egal fĂŒr die FunktionalitĂ€t:

var EXPORTED_SYMBOLS = ["LOG"];
function LOG(msg) {
    Components.classes["@mozilla.org/consoleservice;1"]
            .getService(Components.interfaces.nsIConsoleService);
            .logStringMessage(new Date().toLocaleString() + ": " + msg);
}

Am obigen EXPORTED_SYMBOLS gibt man an welche Variablen nach außen hin sichtbar sein sollen. Voila, es ist geschafft.