seeseekey.net - Invictus Deus Ex Machina

In Java­Script kann man ver­nünf­tig pro­gram­mie­ren, wenn man die schlech­ten Teile weg­lässt. Nicht umsonst gibt es das Buch Java­Script: The Good Parts. Dabei haben sich im Laufe der Zeit einige „Best Prac­tices“ ent­wi­ckelt, wel­che unter ande­rem in Form von Design Pat­terns vor­lie­gen. Auf der Web­seite http://shichuan.github.com/javascript-patterns/ kann man sich diese anschauen und so sehen, was man in Zukunft bes­ser machen kann. Das ent­spre­chende Git­Hub Pro­jekt ist dabei unter https://github.com/shichuan/javascript-patterns/ zu fin­den. Lei­der scheint es keine defi­nierte Lizenz für das Pro­jekt zu geben, so das der Lizenz­sta­tus im Moment augen­schein­lich noch unge­klärt ist.

Bei der Ent­wick­lung wun­dert man sich ab und an, was für inter­es­sante Pro­jekte in den Wei­ten des Net­zes so umher­schwir­ren. So unter ande­rem das Pro­jekt Script#. Dabei han­delt es sich um eine Erwei­te­rung für das Visual Stu­dio 2012 mit wel­cher es mög­lich ist C# Quell­text (so er gewis­sen Kri­te­rien genügt) in Java­Script umzu­wan­deln, bzw. zu compilieren.

Die Erwei­te­rung wel­che im Quell­text unter https://github.com/nikhilk/scriptsharp zu fin­den ist, steht dabei unter der Apa­che Lizenz und ist somit freie Soft­ware. Nach der Instal­la­tion fügt sie dem Visual Stu­dio neue Pro­jekt­ty­pen hinzu, mit wel­chen man anschlie­ßend arbei­ten kann. Lei­der gibt es von Script# keine Mono­De­ve­lop Vari­ante, so das man im Moment zwin­gend an das Visual Stu­dio gebun­den ist. Aller­dings fin­det sich in der Road­map fol­gen­der Satz:

In terms of code con­tri­bu­tion, it would be espe­cially inte­res­ting to see the deve­lop­ment of import libra­ries for com­mon libra­ries, so they are easily usa­ble right out of the box. It would also be inte­res­ting to see the deve­lop­ment of com­ple­men­tary tools/addins, adding sup­port to other IDEs (esp. Mono­De­ve­lop) and other such com­ple­men­tary pro­jects. Or you might be inte­res­ted in the very core, i.e. the com­pi­ler itself.

Also wenn sich jemand bemü­ßigt fühlt, das ganze für Mono­De­ve­lop in Angriff zu neh­men, der muss nun nicht mehr auf die Ein­la­dung war­ten. Die offi­zi­elle Pro­jekt­seite von Script# ist unter http://scriptsharp.com/ zu finden.

Bei melonJS han­delt es sich um eine Java­script Engine zur Spie­le­ent­wick­lung im Brow­ser mit­tels HTML5. Möchte man dort ein Level laden, so geschieht das in Form einer TMX Datei. In melonJS gibt es dabei zwei Metho­den zum laden der ent­spre­chen­den Daten. Diese sind „me.loader.preload“ und „me.loader.load“. Alle Bei­spiele wel­che man im Netz so fin­det nut­zen dabei immer die „pre­load“ 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 Pro­blem ist das man bei grö­ße­ren Spie­len mit ein paar hun­dert MiB Spiel­da­ten, das ganze schlecht kom­plett in den Spei­cher laden kann. Hier­für gibt es die „load“ Methode. Aller­dings ent­hält diese einige Feh­ler wel­che das ganze erschwe­ren. Die „pre­load“ Methode trägt jede TMX Datei in das Array „levels“ im „me.levelDirector“ ein. Bei der „load“ Methode pas­siert genau dies nicht. Des­halb muss man hier anders vor­ge­hen (bis der Feh­ler beho­ben ist). Ein Mini­mal­b­ei­spiel 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 dyna­misch, ohne auf die „pre­load“ Methode ange­wie­sen zu sein. Mög­lich wird dies durch den klei­nen Bug­fix in der „onload“ Methode:

me.levelDirector.addTMXLevel("desert"); //Bugfix

Die­ser sorgt dafür das der level­Di­rec­tor über die ent­spre­chende TMX Datei infor­miert wird und somit die Datei beim laden des Levels auch findet.

In Java­Script ist es durch­aus mög­lich 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
}

Inter­es­sant wird es nun, wenn man ver­sucht aus einer Zahl in die­sem Bei­spiel der „2“ wie­der einen Enum („APMSG_REGISTER_RESPONSE“) zu erzeu­gen. Hier­bei hilft die Funk­tion „getEn­um­Fro­m­In­te­ger“ wel­che 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 durchi­te­riert und der ent­spre­chende Schlüs­sel, wel­cher dem Inte­ger ent­spricht, zurückgegeben.

UKI

Wenn man native Appli­ka­tion ent­wi­ckelt, ist die Wahl der Kom­po­nen­ten für die gra­fi­sche Ober­flä­che schnell erle­digt. Meinst nimmt man das UI Kit, wel­ches dem „Plattform-Standard“ ent­spricht. Bei Web­sei­ten bzw. Web­bap­pli­ka­tio­nen ist die ganze Sache kom­pli­zier­ter. Hier gibt es eine Reihe von Lösun­gen, wie jQuery UI, JxLib, YUI und andere. Eine schöne Alter­na­tive zu den genann­ten Lösun­gen ist UKI.

Eine UKI Beispielanwendung

Die Biblio­thek setzt dabei auf direkt auf Java­script auf und benö­tigt keine wei­te­ren Frame­works. Das ganze ist unter http://ukijs.org/ zu fin­den. UKI ist dabei freie Soft­ware und steht unter der MIT-Lizenz.

Bei Open­Lay­ers han­delt es sich um eine JavaScript-Biliothek zur Dar­stel­lung von Kar­ten, im spe­zi­el­len zur Dar­stel­lung von Geo­da­ten. Die Biblio­thek steht dabei unter der BSD-Lizenz (2er Klau­sel Ver­sion). So fußt z.B. das Front­end von OpenStreetMap.org auf die­ser Biblio­thek. Mit Hilfe der Biblio­thek ist es ein leich­tes Web­an­wen­dun­gen zu schrei­ben wel­che Web Fea­ture Ser­vices oder Web Map Ser­vices nut­zen. Auch das Ein­bin­den von Open­Street­Map oder den Google Kar­ten ist kein Pro­blem. Wer vor hat in nächs­ter Zeit so eine Anwen­dung zu schrei­ben, der sollte sich Open­Layer anschauen. Die offi­zi­elle Seite ist dabei unter http://openlayers.org/ zu finden.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://de.wikipedia.org/wiki/BSD-Lizenz
http://de.wikipedia.org/wiki/OpenLayers

Mit dem Java­Script Frame­work Moo­Tools ist es mög­lich Klas­sen in Java­Script zu schrei­ben. In die­sen Klas­sen kann man auch pri­vate Metho­den defi­nie­ren. Dazu hängt man ein „.pro­tect()“ an die Metho­den an, wel­che pri­vat sein sol­len. Aus­se­hen 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 „split­Com­mand“ nur noch durch die Klasse selbst und nicht mehr von außen auf­ge­ru­fen werden.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://de.wikipedia.org/wiki/MooTools

Über Webso­ckets kann man Binär­da­ten ver­schi­cken, lei­der ist dies nicht ganz so ein­fach wie es sein sollte. Das erste Pro­blem ist, das es in Java­Script bis vor eini­ger Zeit keine „Binär­ty­pen“ gab. Aber dank eini­ger Dinge ist es mitt­ler­weile mög­lich Binär­da­ten per Java­Script und Webso­ckets zu ver­sen­den. Mein Pro­blem an der binä­ren Daten­über­tra­gung war bis­her, das es augen­schein­lich nir­gens ein kom­plet­tes Bei­spiel gibt, wel­che diese ein­fach mal demons­triert. Dabei ist das ganze rela­tiv 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 ers­ten Zei­len im Skript­teil wird zuerst ein Webso­cket ange­legt, wel­ches sich mit „ws://echo.websocket.org“ ver­bin­det. Die­ser Ser­ver gibt immer genau das zurück was er emp­fängt und eig­net sich somit aus­ge­zeich­net für die­sen klei­nen Test.

Danach wird der „bina­ry­Type“ des Webso­ckets auf „array­buf­fer“ gesetzt, da wir mit einem sol­chen arbei­ten wol­len. In der ver­knüpf­ten „OnO­pen“ Methode wird ein „Array­Buf­fer“ mit der pas­sen­den Größe ange­legt und mit die­sem ein „Data­View“ initia­li­siert. Mit die­sem ist es dann mög­lich pro­blem­los mög­lich die ent­spre­chen­den Werte in den „Array­Buf­fer“ 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 weg­ge­schickt und anschlie­ßend wie­der in der „OnMes­sage“ Funk­tion emp­fan­gen. Dort wird der Daten­typ ermit­telt (für den Fall das man mit meh­re­ren Typen arbei­tet) und anschlie­ßend wird das Paket wie­der Binär aus­ein­an­der genommen.

Wei­tere Infor­ma­tio­nen 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 Moo­Tools han­delt es sich um ein Frame­work mit wel­chem man sehr schön Klas­sen basierte Struk­tu­ren in Java­Script abbil­den kann. Bei der Defi­ni­tion von benut­zer­de­fi­nier­ten Events ist die Doku­men­ta­tion lei­der etwas dünn, so das ich dort etwas pro­bie­ren musste, bis ich zum rich­ti­gen Ergeb­nis gekom­men bin.

Gege­ben sei dabei ein Pro­jekt mit einer „index.html“ Datei sowie der Java­Script Datei „explosive.js“ wel­che die Klasse „Explo­sive“ ent­hält. Die Klasse sieht dabei im Quell­text 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 die­ser Klasse gibt es die Eigen­schaft „tnte­qui­va­lent“ wel­che die Spreng­kraft unser Bombe defi­niert und im Kon­struk­tor gesetzt wird. Die Funk­tio­nen „deto­nate“ und „deto­nate­Wi­t­h­Info“ zün­den die Bombe und sen­den das Event „explode“ wahl­weise mit der Infor­ma­tio­nen über die Sprengkraft.

Die dazu pas­sende „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 Funk­tion „onEx­plode“ ange­legt, wel­che das Event am Ende ent­ge­gen­neh­men soll. Dar­un­ter wird eine Instanz der Klasse „Explo­sive“ mit dem Namen „bomb“ ange­legt. Die­ser Instanz sagen wir das dass Event „explode“ an die Funk­tion „onEx­plode“ wei­ter­ge­reicht wer­den soll.

Nun wer­den noch zwei But­tons defi­niert, wel­che die aus­lö­sen­den Funk­tio­nen auf­ru­fen. Ein­mal wird die Funk­tion „onEx­plode“ mit einem Para­me­ter und ein­mal ohne auf­ge­ru­fen. In die­sem Para­me­ter steckt in die­sem Fall die Spreng­kraft der Bombe, so das diese mit aus­ge­ge­ben wer­den kann. Und schon ist man mit sei­nem ers­ten Event fertig.

Wei­tere Infor­ma­tio­nen gibt es unter:
http://de.wikipedia.org/wiki/MooTools

Wer test­ba­sierte unter Java­Script betrei­ben möchte, stößt da auf eine Menge Frame­works wie z.B. Jas­mine, wel­ches für sich für „Beha­vior Dri­ven Deve­lop­ment“ eig­net. Per­sön­lich suchte ich aller­dings ein Frame­work für ganz nor­ma­les Unit-Testing aller xUnit.

Schluss­end­lich bin ich bei „Enhance JS“ gelan­det. Mit dem Frame­work ist es mög­lich schnell eigene Tests zu schrei­ben, um die Funk­ti­ons­fä­hig­keit der ent­spre­che­nen Java­Script Anwen­dung zu tes­ten. Bei Java­Script ist dies bei grö­ße­ren Pro­jek­ten alleine schon wegen der dyna­mi­schen Typi­sie­rung von Vorteil.

Die Test­aus­gabe von Ench­ange JS

Der Quell­text, des unter der Apa­che Lizenz ste­hen­den Frame­works, ist dabei auf Git­hub zu fin­den. Die offi­zi­elle Seite kann unter http://www.enhance-js.com besucht werden.

Wei­tere Infor­ma­tio­nen 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