In JavaScript kann man vernünftig programmieren, wenn man die schlechten Teile weglässt. Nicht umsonst gibt es das Buch JavaScript: The Good Parts. Dabei haben sich im Laufe der Zeit einige „Best Practices“ entwickelt, welche unter anderem in Form von Design Patterns vorliegen. Auf der Webseite http://shichuan.github.com/javascript-patterns/ kann man sich diese anschauen und so sehen, was man in Zukunft besser machen kann. Das entsprechende GitHub Projekt ist dabei unter https://github.com/shichuan/javascript-patterns/ zu finden. Leider scheint es keine definierte Lizenz für das Projekt zu geben, so das der Lizenzstatus im Moment augenscheinlich noch ungeklärt ist.
Bei der Entwicklung wundert man sich ab und an, was für interessante Projekte in den Weiten des Netzes so umherschwirren. So unter anderem das Projekt Script#. Dabei handelt es sich um eine Erweiterung für das Visual Studio 2012 mit welcher es möglich ist C# Quelltext (so er gewissen Kriterien genügt) in JavaScript umzuwandeln, bzw. zu compilieren.
Die Erweiterung welche im Quelltext unter https://github.com/nikhilk/scriptsharp zu finden ist, steht dabei unter der Apache Lizenz und ist somit freie Software. Nach der Installation fügt sie dem Visual Studio neue Projekttypen hinzu, mit welchen man anschließend arbeiten kann. Leider gibt es von Script# keine MonoDevelop Variante, so das man im Moment zwingend an das Visual Studio gebunden ist. Allerdings findet sich in der Roadmap folgender Satz:
In terms of code contribution, it would be especially interesting to see the development of import libraries for common libraries, so they are easily usable right out of the box. It would also be interesting to see the development of complementary tools/addins, adding support to other IDEs (esp. MonoDevelop) and other such complementary projects. Or you might be interested in the very core, i.e. the compiler itself.
Also wenn sich jemand bemüßigt fühlt, das ganze für MonoDevelop in Angriff zu nehmen, der muss nun nicht mehr auf die Einladung warten. Die offizielle Projektseite von Script# ist unter http://scriptsharp.com/ zu finden.
Bei melonJS handelt es sich um eine Javascript Engine zur Spieleentwicklung im Browser mittels HTML5. Möchte man dort ein Level laden, so geschieht das in Form einer TMX Datei. In melonJS gibt es dabei zwei Methoden zum laden der entsprechenden Daten. Diese sind „me.loader.preload“ und „me.loader.load“. Alle Beispiele welche man im Netz so findet nutzen dabei immer die „preload“ Methode:
var g_resources= [
{ name: "desert1", type: "image", src: "desert1.png" },
{ name: "desert", type: "tmx", src: "desert.tmx" },
{ name: "player_male_base", type: "image", src: "player_male_base.png" },
{ name: "fog", type: "image", src: "fog.png" }
];
...
me.loader.preload(g_resources);
Das Problem ist das man bei größeren Spielen mit ein paar hundert MiB Spieldaten, das ganze schlecht komplett in den Speicher laden kann. Hierfür gibt es die „load“ Methode. Allerdings enthält diese einige Fehler welche das ganze erschweren. Die „preload“ Methode trägt jede TMX Datei in das Array „levels“ im „me.levelDirector“ ein. Bei der „load“ Methode passiert genau dies nicht. Deshalb muss man hier anders vorgehen (bis der Fehler behoben ist). Ein Minimalbeispiel zum laden eines Levels sieht damit so aus:
<!DOCTYPE html>
<html>
<head>
<title>melonJS minimal sample</title>
<meta charset="utf-8" />
</head>
<body>
<script type="text/javascript" src="melonJS.js"></script>
<script type="text/javascript">
var jsApp = {
start: function() {
if (!me.video.init('jsapp', 640, 480, false, '1', false))
{
alert("Sorry but your browser does not support html 5 canvas.");
return;
}
me.loader.onload = this.loaded.bind(this);
//lade manuell
me.loader.load({name: "desert1", type:"image", src: "desert1.png"}, this.onload);
me.loader.load({name: "desert", type:"tmx", src: "desert.tmx"}, this.onload);
//zeige Ladebildschirm
me.state.change(me.state.LOADING);
},
onload: function(data) {
me.levelDirector.addTMXLevel("desert"); //Bugfix
me.levelDirector.loadLevel("desert");
},
loaded: function() {
me.state.set(me.state.PLAY, new PlayScreen());
me.state.change(me.state.PLAY);
}
}
var PlayScreen = me.ScreenObject.extend({
});
//starte wenn der Browser bereit ist
window.onReady(function() {
jsApp.start();
});
</script>
</body>
</html>
Damit lädt man ein Level dynamisch, ohne auf die „preload“ Methode angewiesen zu sein. Möglich wird dies durch den kleinen Bugfix in der „onload“ Methode:
me.levelDirector.addTMXLevel("desert"); //Bugfix
Dieser sorgt dafür das der levelDirector über die entsprechende TMX Datei informiert wird und somit die Datei beim laden des Levels auch findet.
In JavaScript ist es durchaus möglich Enums anzulegen:
var Protocol =
{
PAMSG_REGISTER:0x0000, // D version, S username, S password, S email, S captcha response
APMSG_REGISTER_RESPONSE:0x0002, // B error, S updatehost, S Client data URL, B Character slots
PAMSG_UNREGISTER:0x0003, // S username, S password
}
Interessant wird es nun, wenn man versucht aus einer Zahl in diesem Beispiel der „2“ wieder einen Enum („APMSG_REGISTER_RESPONSE“) zu erzeugen. Hierbei hilft die Funktion „getEnumFromInteger“ welche wie folgt aussieht:
function getEnumFromInteger(enumWithValues, integer)
{
var keys = Object.keys(enumWithValues);
for (var i=0; i<keys.length; i++)
{
if(enumWithValues[keys[i]]==integer) return keys[i];
}
return null;
}
Damit wird der Enum durchiteriert und der entsprechende Schlüssel, welcher dem Integer entspricht, zurückgegeben.
Wenn man native Applikation entwickelt, ist die Wahl der Komponenten für die grafische Oberfläche schnell erledigt. Meinst nimmt man das UI Kit, welches dem „Plattform-Standard“ entspricht. Bei Webseiten bzw. Webbapplikationen ist die ganze Sache komplizierter. Hier gibt es eine Reihe von Lösungen, wie jQuery UI, JxLib, YUI und andere. Eine schöne Alternative zu den genannten Lösungen ist UKI.
Die Bibliothek setzt dabei auf direkt auf Javascript auf und benötigt keine weiteren Frameworks. Das ganze ist unter http://ukijs.org/ zu finden. UKI ist dabei freie Software und steht unter der MIT-Lizenz.
Bei OpenLayers handelt es sich um eine JavaScript-Biliothek zur Darstellung von Karten, im speziellen zur Darstellung von Geodaten. Die Bibliothek steht dabei unter der BSD-Lizenz (2er Klausel Version). So fußt z.B. das Frontend von OpenStreetMap.org auf dieser Bibliothek. Mit Hilfe der Bibliothek ist es ein leichtes Webanwendungen zu schreiben welche Web Feature Services oder Web Map Services nutzen. Auch das Einbinden von OpenStreetMap oder den Google Karten ist kein Problem. Wer vor hat in nächster Zeit so eine Anwendung zu schreiben, der sollte sich OpenLayer anschauen. Die offizielle Seite ist dabei unter http://openlayers.org/ zu finden.
Weitere Informationen gibt es unter:
http://de.wikipedia.org/wiki/BSD-Lizenz
http://de.wikipedia.org/wiki/OpenLayers
Mit dem JavaScript Framework MooTools ist es möglich Klassen in JavaScript zu schreiben. In diesen Klassen kann man auch private Methoden definieren. Dazu hängt man ein „.protect()“ an die Methoden an, welche privat sein sollen. Aussehen könnte das ganze dann z.B. so:
var MessageIn = new Class({
id: 0,
parts: [],
//Konstruktor
initialize: function(message) {
this.parts = this.splitCommand(message);
this.id=parseInt(this.parts[0], 16);
this.parts.splice(0, 1); //Erstes Element entfernen
},
//Methoden
getPart: function(index) {
return this.parts[index];
},
//Private Methoden
splitCommand: function(command) {
ret=new Array();
...
return ret;
}.protect()
});
Dadurch kann die Methode „splitCommand“ nur noch durch die Klasse selbst und nicht mehr von außen aufgerufen werden.
Weitere Informationen gibt es unter:
http://de.wikipedia.org/wiki/MooTools
Über Websockets kann man Binärdaten verschicken, leider ist dies nicht ganz so einfach wie es sein sollte. Das erste Problem ist, das es in JavaScript bis vor einiger Zeit keine „Binärtypen“ gab. Aber dank einiger Dinge ist es mittlerweile möglich Binärdaten per JavaScript und Websockets zu versenden. Mein Problem an der binären Datenübertragung war bisher, das es augenscheinlich nirgens ein komplettes Beispiel gibt, welche diese einfach mal demonstriert. Dabei ist das ganze relativ einfach:
<!DOCTYPE html>
<html>
<head>
<title>Websocket Binary Test</title>
<meta charset="utf-8" />
</head>
<body>
<script type="text/javascript">
websocket = new WebSocket("ws://echo.websocket.org");
websocket.binaryType = "arraybuffer"; //Binärtyp auf arraybuffer setzen
//OnOpen verdraten
websocket.onopen = function (e) {
//Array zusammenbauen
var message = new ArrayBuffer(9);
var dataViewMessage = new DataView(message);
dataViewMessage.setInt8(0, 25); //Command ID
dataViewMessage.setInt16(1, 11); //Account ID
dataViewMessage.setInt32(5, 43333020); //Anzahl der Credits
//message per Websocket wegschicken
websocket.send(message);
}
//OnMessage verdraten
websocket.onmessage = function (wsPackage) {
//Datentyp ermitteln
if(wsPackage.data instanceof ArrayBuffer) alert("ArrayBuffer");
else if(wsPackage.data instanceof Blob) alert("Blob");
else if(typeof wsPackage.data === "string") alert("string");
//Daten empfangen und auseinander bauen
var dataViewPackage = new DataView(wsPackage.data);
alert(dataViewPackage.getInt8(0));
alert(dataViewPackage.getInt16(1));
alert(dataViewPackage.getInt32(5));
}
</script>
</body>
</html>
Im den ersten Zeilen im Skriptteil wird zuerst ein Websocket angelegt, welches sich mit „ws://echo.websocket.org“ verbindet. Dieser Server gibt immer genau das zurück was er empfängt und eignet sich somit ausgezeichnet für diesen kleinen Test.
Danach wird der „binaryType“ des Websockets auf „arraybuffer“ gesetzt, da wir mit einem solchen arbeiten wollen. In der verknüpften „OnOpen“ Methode wird ein „ArrayBuffer“ mit der passenden Größe angelegt und mit diesem ein „DataView“ initialisiert. Mit diesem ist es dann möglich problemlos möglich die entsprechenden Werte in den „ArrayBuffer“ zu setzen.
Dies geschieht dann mit den Zeilen:
dataViewMessage.setInt8(0, 25); //Command ID dataViewMessage.setInt16(1, 11); //Account ID dataViewMessage.setInt32(5, 43333020); //Anzahl der Credits
Am Ende wird das ganze weggeschickt und anschließend wieder in der „OnMessage“ Funktion empfangen. Dort wird der Datentyp ermittelt (für den Fall das man mit mehreren Typen arbeitet) und anschließend wird das Paket wieder Binär auseinander genommen.
Weitere Informationen gibt es unter:
http://www.websocket.org/echo.html
http://www.html5rocks.com/en/tutorials/webgl/typed_arrays/
https://developer.mozilla.org/en-US/docs/JavaScript_typed_arrays
http://msdn.microsoft.com/de-de/library/br212463%28v=vs.94%29.aspx
https://developer.mozilla.org/en-US/docs/JavaScript_typed_arrays/ArrayBuffer
http://stackoverflow.com/questions/11390021/transferring-files-with-javascript-through-websockets
Bei MooTools handelt es sich um ein Framework mit welchem man sehr schön Klassen basierte Strukturen in JavaScript abbilden kann. Bei der Definition von benutzerdefinierten Events ist die Dokumentation leider etwas dünn, so das ich dort etwas probieren musste, bis ich zum richtigen Ergebnis gekommen bin.
Gegeben sei dabei ein Projekt mit einer „index.html“ Datei sowie der JavaScript Datei „explosive.js“ welche die Klasse „Explosive“ enthält. Die Klasse sieht dabei im Quelltext so aus:
var Explosive = new Class({
Implements: [Events],
tntequivalent: 0,
//Constructor
initialize: function(equivalent) {
this.tntequivalent = equivalent;
},
//Methods
detonate: function(){
this.fireEvent('explode');
},
detonateWithInfo: function(){
this.fireEvent('explode', this.tntequivalent);
}
});
In dieser Klasse gibt es die Eigenschaft „tntequivalent“ welche die Sprengkraft unser Bombe definiert und im Konstruktor gesetzt wird. Die Funktionen „detonate“ und „detonateWithInfo“ zünden die Bombe und senden das Event „explode“ wahlweise mit der Informationen über die Sprengkraft.
Die dazu passende „index.html“ sieht dabei so aus:
<!DOCTYPE html>
<html>
<head>
<title>Mootools Event Test</title>
<meta charset="utf-8" />
<script type="text/javascript" src="mootools-core-1.4.5-full-nocompat.js"></script>
<script type="text/javascript" src="explosive.js"></script>
</head>
<body>
<script type="text/javascript">
function onExplode(object)
{
if(object==null)
{
alert("Bomb is exploded.");
}
else
{
alert("Bomb is exploded, with " + object + " tons of TNT equivalent.");
}
}
var bomb = new Explosive(100);
bomb.addEvent('explode', onExplode);
</script>
<form>
<input type="button" value="Detonate bomb" onClick="bomb.detonate();">
<input type="button" value="Detonate bomb with info" onClick="bomb.detonateWithInfo();">
</form>
</body>
</html>
Hier wird im JavaScript-Bereich der Klasse eine Funktion „onExplode“ angelegt, welche das Event am Ende entgegennehmen soll. Darunter wird eine Instanz der Klasse „Explosive“ mit dem Namen „bomb“ angelegt. Dieser Instanz sagen wir das dass Event „explode“ an die Funktion „onExplode“ weitergereicht werden soll.
Nun werden noch zwei Buttons definiert, welche die auslösenden Funktionen aufrufen. Einmal wird die Funktion „onExplode“ mit einem Parameter und einmal ohne aufgerufen. In diesem Parameter steckt in diesem Fall die Sprengkraft der Bombe, so das diese mit ausgegeben werden kann. Und schon ist man mit seinem ersten Event fertig.
Weitere Informationen gibt es unter:
http://de.wikipedia.org/wiki/MooTools
Wer testbasierte unter JavaScript betreiben möchte, stößt da auf eine Menge Frameworks wie z.B. Jasmine, welches für sich für „Behavior Driven Development“ eignet. Persönlich suchte ich allerdings ein Framework für ganz normales Unit-Testing aller xUnit.
Schlussendlich bin ich bei „Enhance JS“ gelandet. Mit dem Framework ist es möglich schnell eigene Tests zu schreiben, um die Funktionsfähigkeit der entsprechenen JavaScript Anwendung zu testen. Bei JavaScript ist dies bei größeren Projekten alleine schon wegen der dynamischen Typisierung von Vorteil.
Der Quelltext, des unter der Apache Lizenz stehenden Frameworks, ist dabei auf Github zu finden. Die offizielle Seite kann unter http://www.enhance-js.com besucht werden.
Weitere Informationen gibt es unter:
http://de.wikipedia.org/wiki/XUnit
http://de.wikipedia.org/wiki/Behavior_Driven_Development
http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks


