JavaScript: Bildschirmauflösung

Das window.screen-Objekt in JavaScript enthält einige nützliche Informationen über Bildschirmkonfiguration und -auflösung des Besuchers. Diese kann man zum Beispiel nutzen um eine Designweiche zu implementieren, oder Statistiken zu sammeln.

Folgende Informationen kann man abrufen:

console.log("Bildschirmbreite: " + window.screen.width);
console.log("Bildschirmhöhe: " + window.screen.height);
console.log("Bildschirmbreite ohne Browserinterface: " + window.screen.availWidth);
console.log("Bildschirmhöhe ohne Browserinterface: " + window.screen.availHeight);
console.log("Farbtiefe: " + window.screen.colorDepth);
console.log("Bits pro Pixel: " + window.screen.pixelDepth); // nur Firefox
 

RC4 Haskell und JavaScript

RC4 in JavaScript: (Quelle)

/*
 * RC4 symmetric cipher encryption/decryption
 *
 * @license Public Domain
 * @param string key - secret key for encryption/decryption
 * @param string str - string to be encrypted/decrypted
 * @return string
 */
function rc4(key, str) {
	var s = [], j = 0, x, res = '';
	for (var i = 0; i < 256; i++) {
		s[i] = i;
	}
	for (i = 0; i < 256; i++) {
		j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
		x = s[i];
		s[i] = s[j];
		s[j] = x;
	}
	i = 0;
	j = 0;
	for (var y = 0; y < str.length; y++) {
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		x = s[i];
		s[i] = s[j];
		s[j] = x;
		res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]);
	}
	return res;
}

RC4 in Haskell:

module RC4
(
 encode,
 decode
)
where

import Data.Map (Map, (!))
import qualified Data.Map as Map
import Data.Char (ord, chr)
import Data.Bits (xor)

encode :: String -> String -> String
encode = rc4

decode :: String -> String -> String
decode = rc4

initS = Map.fromList $ zip [0 .. 255] [0 .. 255]

rc4 :: String -> String -> String
rc4 key str =
    resultStr
    where
      (_, s) = foldl mkBox (0, initS) [0 .. 255]
      mkBox (inpJ, inpS) i =
          (j, s'')
          where
            j = (inpJ + (inpS ! i) + (ord (key !! (i `mod` (length key))))) `mod` 256
            x = inpS ! i
            s' = Map.insert i (inpS ! j) inpS
            s'' = Map.insert j x s'

      (resultStr, _, _, _) = result

      result = foldl core ("", 0, 0, s) [0 .. ((length str) - 1)]
      core (res, inpI, inpJ, inpS) y =
          (res', i, j, s'')
          where
            i = (inpI + 1) `mod` 256
            j = (inpJ + (inpS ! i)) `mod` 256
            x = inpS ! i
            s' =  Map.insert i (inpS ! j) inpS
            s'' = Map.insert j x s'
            k = ((s'' ! i) + (s'' ! j)) `mod` 256
            res' = res ++ [chr $ (ord (str !! y)) `xor` (s'' ! k)]
 

JavaScript: Duplikate aus Array entfernen

Kurzer JavaScript Code-Snippet, um Duplikate aus einem JavaScript-Array zu entfernen:

/**
 * Remove duplicates from array
 * 
 */
var removeDuplicates = function(t) {
    t.sort();

    var elems = {};
    var newArray = [];

    for (var i = 0, len = t.length; i < len; i++)  {
        elems[t[i]] = 0;
    }

    for (var elem in elems) {
        newArray.push(elem);
    }

    return newArray;
};
 

Jint – JavaScripting für .NET

Viele Spiele bieten die Möglichkeit diese per Scripting zu erweitern. Dabei kann man etwa eine Map oder eine KI mit Scriptsprachen realisieren. Sehr beliebt ist hier LUA. Leider ist jedoch LUA unter den Leuten deutlich weniger bekannt und verbreitet als JavaScript, weshalb ich neulich nach einer Möglichkeit gesucht habe Scripting per JavaScript zu ermöglichen. Da ich derzeit an einem .NET Projekt arbeite, habe ich natürlich nach entsprechenden .NET Lösungen gesucht. Hierbei bin ich auf Jint – JavaScript Interpreter for .NET gestoßen. Das ganze bietet einen vollen JavaScript Interpreter, der so ziemlich die meisten JavaScript Features unterstützt (für mehr Info’s siehe Webseite). Das Coole daran ist, dass sich auch sehr einfach .NET Objekte und Funktionen in JavaScript aufrufen lassen und umgekehrt. Hier mal ein einfaches Beispiel:

string script = @"var distance = function(x1, y1, x2, y2)
{
	return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
};";

double dist = (double)new JintEngine()
			.Run(script)
			.CallFunction("distance", 5, 5, 5, 10);

Würde man script jetzt aus einer Datei lesen, könnte man dem Benutzer ermöglichen eine eigene Funktion zur Berrechnung einer Distanz zu definieren. Das gute hierbei ist auch, dass man genau Kontrollieren kann auf welche Objekte aus .NET der Benutzer Zugriff hat. (JEngine.AddPermission(), JEngine.AllowClr, JEngine.DisableSecurity())
Dadurch entstehen auch keine Sicherheitsprobleme. Man kann sogar die maximale Anzahl von Rekursionen begrenzen (JEngine.SetMaxRecursions).

Ein .NET Objekt an ein Script zu übergeben geht auch sehr einfach:

public class Randomizer {
	private Random r = new Random();
	
	public int getRandomInt()
	{
		return r.Next();
	}
}

int random123 = (int)new JintEngine()
	.setParameter("Randomizer", new Randomizer())
	.Run(@"var random_number = Randomizer.getRandomInt();
	return random_number + 123;");

Das ganze ist also wirklich einfach zu verwenden und bietet sehr viele Möglichkeiten. Man könnte ein Poker-Spiel programmieren und dann KIs per JavaScript ermöglichen, oder aber zum Beispiel Quests und NPCs in Rollenspielen per JavaScript programmieren. Ein weiterer Vorteil ist, dass die JavaScripts zur Laufzeit geladen werden können, und jeder Zeit neugeladen werden können. Das heißt auch das Testen geht wesentlich einfacher! 🙂

 

Keep your scope – jQuery.proxy

Wer kennt das nicht, man arbeitet mit jQuery und einem click-Event, und „verliert“ dadurch bei Objekten das „this“. Kleines Beispiel:

function MyObject() {
	this.hello = "Hello";
}

MyObject.prototype.clickHandler = function(event) {
	alert(this.hello);
};

var obj = new MyObject();

$('#clickMe').click(obj.clickHandler);

Wenn man jetzt auf ein gedachtes Element mit der ID „clickMe“ klickt, dann erscheint eine Fehlermeldung in JavaScript bzw. eine Alert-Box mit dem Inhalt „undefined“ – obwohl this.hello eigentlich doch „Hello“ sein sollte? Bei den jQuery Event-Handlern „verliert“ die Funktion ihr aktuelles Scope und das this zeigt dann auf den Sender des Events, in diesem Fall auf das Element mit der ID „clickMe“. Das ist zwar oft gewünscht, da man dieses nun zB mit $(this).text(‚Ich wurde geklicked‘); weiterverarbeiten kann, ist aber in meinem Beispiel oben sehr ungünstig. Deshalb bietet die jQuery-Library eine Funktion jQuery.proxy, welche eine Proxy-Funktion generiert mit der wir das Scope einer Funktion selbst beeinflussen können. Ersetzen wir Zeile 11 also mit folgendem:

$('#clickMe').click($.proxy(obj.clickHandler, obj));

Nun zeigt das this wärend unserem clickHandler-Aufruf wieder auf das eigentliche Objekt und eine Alert-Box mit dem Inhalt „Hello“ erscheint. Möchte man nun trotzdem auf das auslösende Element für das Event zugreifen, schafft

var calledBy = event.target;
// beispiel
$(calledBy).text("Link geklickt!");

abhilfe! Natürlich kann man auch seine eigene Proxy-Funktion mit direct-calling bauen, was aber den Code schon deutlich unübersichtlicher macht, allerdings viel mehr Flexibilität bietet (wieder Bezug auf 1. Beispielcode):

$('#clickMe').click((function(o) {
	return function(event) {
		o.clickHandler(event);
	};
})(obj));

Es lohnt sich also immer auch mal unter „Misc“ bzw. „Utilities“ in der Dokumentation einer Library nachsehen 😉

 

HTML5 WebSocket

HTML5 unterstützt jetzt das Aufbauen von „WebSocket“-Verbindungen. Eine WebSocket Verbindung basiert ähnlich wie HTTP auf dem TCP Protokoll, baut aber eine „dauerhafte“ Verbindung auf. Die Vorteile gegenüber HTTP ist, dass eine Aktion des Servers keine vorhergehende Anfrage des Clients mehr erfordert, wodurch der Server neue Informationen fasst in Echtzeit ausliefern kann. Dadurch kann man eine Menge Traffic und viele unnötige HTTP-Requests sparen, die etwa ein AJAX-JavaScript sonst schicken würde um neue Informationen zu erhalten.
Technisch gesehen baut bei einer WebSocket-Verbindung der Browser mit dem Server eine Verbindung auf, die nach einen Handshake dann bestehen bleibt und über die dann Binär- und Textdaten geschickt werden können.

Leider hat das Protokoll im Handshake einige Sicherheitsprobleme (gehabt?) und sich deshalb ständig verändert. Dieser Artikel bezieht sich also auf die Aktuellste Version (17). Diese wird derzeit von der aktuellsten Chrome Version und anderen Webkit Browsern unterstützt, in Firefox und Opera ist WebSocket wegen der Sicherheitsprobleme derzeit deaktiviert.

Um HTML5 WebSocket zu verwenden ist zunächst ein WebSocket-Server nötig. Ich habe hier nach einer PHP-Variante gesucht und habe dann nach einer Weile folgenden gefunden: php-websocket. Diesen sollte man sich laden und dann kann man ihn per

php -q php-websocket/server/server.php

starten. Jetzt ist ein WebSocket-Server auf „localhost:8000“ zu erreichen. Dort sind derzeit zwei Module geladen, einmal das Modul „echo“ und einmal das Modul „chat“. (Ich werde mich hier nicht mit der Entwicklung eines WebSocket-Server weiter befassen) Das „echo“ Modul nimmt einfach Anfragen entgegen und schickt diese dann an alle Verbundenen Sockets weiter.

Nun kümmern wir uns um die HTML5-Seite von WebSocket, dem User-Frontend. Dafür bauen wir uns erstmal eine einfache HTML5 Seite:





HTML5 WebSocket Test





// hier wird dann das WebSocket-Script reinkommen, siehe unten




Log

Chatbox

Say something

Für die einfache Verwendung des WebSockets habe ich mir eine kleine Wrapper-Library (websocketlib.js) programmiert:

/**
 * Small wrapper library for HTML5 WebSocket
 * 
 * @author Alexander Thiemann 
 * @version 1.0
 * 
 * @param string WebSocket host, eg. ws://localhost:8010/echo
 * @param function Function to be called when socket is connected
 * @param function Function to be called when socket recieves data, takes 1 argument message
 * @param function Function to be called for logging purposes, takes 1 argument log-message
 * 
 * @see http://blog.agrafix.net/2011/11/html5-websocket/
 */
var WebSocketLib = {
	init: function(host, onConnect, onRecv, onLog) {
		this.socket = new WebSocket(host);
		
		this.onLog = onLog;
		this.onRecv = onRecv;
		this.onConnect = onConnect;
		
		this.onLog('Initializing Socket. State: ' + this.socket.readyState);
		
		this.socket.onopen = this._onOpen;
		this.socket.onmessage = this._onMessage;
		this.socket.onclose = this._onClose;
	},
	
	send: function(message) {
		WebSocketLib.socket.send(message);
	},
	
	_onClose: function() {
		WebSocketLib.onLog('Socket Closed.');
	},
	
	_onMessage: function(msg) {
		WebSocketLib.onLog('Recieved new Message from Server: ' + msg.data);
		WebSocketLib.onRecv(msg.data);	
	},
	
	_onOpen: function() {
		WebSocketLib.onLog('Socket is Ready. State: ' + WebSocketLib.socket.readyState);
		
		if (WebSocketLib.socket.readyState == 1) {
			WebSocketLib.onConnect();
		}	
	}
}

Ich denke dort ist alles relativ selbsterklärend. Das WebSocket ist wirklich sehr leicht zu verwenden! Um diese Wrapper-Library zu verwenden, bauen wir in obiges HTML-Grundgerüst bei „// hier wird dann das WebSocket-Script reinkommen, siehe unten“ folgendes ein:

$(document).ready(function() {
	WebSocketLib.init('ws://localhost:8010/echo', 
	function() {
		sockReady();
	},
	function (t) {
		$('#chatbox').append($("

").text(t)); }, function (t) { $('#logbox').append($("

").text(t)); }); function sockReady() { $('#chat').submit(function(e) { e.preventDefault(); var input = $('input[name="message"]'); WebSocketLib.send(input.val()); input.val(""); }); } });

Zuerst warten wir, bis unser HTML-Dokument im Browser aufgebaut ist. Dann geben wir in Zeile 2 an, wo unser WebSocket-Server läuft. Die Funktion in Zeile 4 wird aufgerufen, sobald das WebSocket verbunden ist. Die Funktion Zeile 6-8 wird aufgerufen, wenn eine Nachricht zurückgegeben wird. Die Funktion Zeile 9-11 wird bei Log-Ereignissen (Hauptsächlich zum Debuggen) aufgerufen. Mit WebSocketLib.send(„Nachricht“) in Zeile 19 schicken wir eine Nachricht an den Server.

Wenn nun alles vorbereitet ist, der WebSocket-Server gestartet ist (siehe oben), können wir einem Browser, der WebSocket unterstützt (siehe oben), unsere HTML-Seite aufrufen. Die Log-Ausgabe im HTML-Fenster sieht wie folgt aus:

Initializing Socket. State: 0
Socket is Ready. State: 1

Im Consolenfenster des PHP-Server sehen wir:

2011-11-25 11:26:47 [info] [client 127.0.0.1:30601] Connected
2011-11-25 11:26:47 [info] [client 127.0.0.1:30601] Performing handshake
2011-11-25 11:26:47 [info] [client 127.0.0.1:30601] Handshake sent

Die Verbindung ist nun also aufgebaut. Jetzt sollten wir den „echo“-Server testen. Wir tippen etwas in das Textfeld und schicken es ab. Der Log sagt:

Recieved new Message from Server: test

Und unter „Chat“ steht jetzt „test“. Hat soweit also funktioniert! Jetzt können wir ein neues Browserfenster aufmachen, wieder den Verbindungsvorgang abwarten und dann dort eine Nachricht verschicken. Diese erscheint dann in allen verbundenen Client-Fenstern.

Eigentlich ziemlich einfach zu verwenden, und es öffnet viele neue Möglichkeiten vorallem in Kombination mit Canvas für HTML5-Multiplayer-Games… hoffen wir also, dass es bald von allen Browser voll unterstützt wird! 🙂

 

Sprechblasen mit HTML5, Canvas und JavaScript

Mit HTML5, Canvas und JavaScript kann man mittlerweile eine Menge schöner Grafiken direkt im Browser rendern. Da ich neulich eine flexible Sprechblase benötigt habe, hier der Code dafür:

/**
 * Draw a speech bubble on an HTML5-Canvas
 *
 * @author Alexander Thiemann 
 * @license CC BY-SA 2.0
 * 
 * @param ctx Canvas 2d context
 * @param text Text for the speech bubble
 * @param x bottom left x-coordinate of speech bubble
 * @param y bottom left y-coordinate of speech bubble
 *
 */
function speechBubble(ctx, text, x, y) {
	var messure = ctx.measureText(text);
	
	var w = messure.width;
	var h = 20;
	
	ctx.beginPath();
	ctx.strokeStyle="black";
	ctx.lineWidth="1";
	ctx.fillStyle="rgba(255, 255, 255, 0.8)";
	
	ctx.moveTo(x, y);
	ctx.lineTo(x + (w*0.2), y);
	ctx.lineTo(x + (w*0.2), y+10);
	ctx.lineTo(x + (w*0.3), y);
	ctx.lineTo(x + w, y);
	
	ctx.quadraticCurveTo(x + (w*1.1), y, x + (w*1.1), y-(h*0.2)); // corner: right-bottom
	
	ctx.lineTo(x + (w*1.1), y-(h*0.8)); // right
	
	ctx.quadraticCurveTo(x + (w*1.1), y-h, x + w, y-h); // corner: right-top
	
	ctx.lineTo(x, y-h); // top
	
	ctx.quadraticCurveTo(x - (w*0.1), y-h, x - (w*0.1), y-(h*0.8)); // corner: left-top
	
	ctx.lineTo(x - (w*0.1), y-(h*0.2)); // left
	
	ctx.quadraticCurveTo(x - (w*0.1), y, x, y); // corner: left-bottom
	
	ctx.fill();
	ctx.stroke();
	ctx.closePath();
	
	ctx.textAlign = 'left';
	ctx.fillStyle = '#000';
	ctx.fillText(text, x, y-6);
}

Verwendung:
HTML


JavaScript

var ctx = document.getElementById("bubble").getContext('2d');
speechBubble(ctx, "Das hier ist ein Test!", 5, 40);

Das Resultat:
Realisiert mit HTML5, Canvas und JavaScript

Ziemlich schick, oder? 😉

 

Manager’s Life V2

Die Entwicklung von Manager’s Life V2 geht langsam auf ein Release zu – deshalb wird die aktuelle Version am 25.11.2011 beendet! Wir bedanken uns bei allen, die bisher das Spiel gespielt haben und würden uns freuen euch wieder bei Version 2 begrüßen zu dürfen.

Das alte Spielkonzept wird in Version 2 komplett über den Haufen geworden und das Spiel wurde von Grund auf neu Programmiert. Erhalten bleibt eigentlich nur das Design! Ich möchte jetzt noch nicht zu viel verraten, aber es wird aufjedenfall ein Online-Spiel, von dem es in dieser Form erst sehr wenige gibt. Es nutzt die modernen HTML5 und JavaScript Techniken komplett aus, ist sehr grafisch und funktioniert auch auf mobilen Endgeräten.

Lasst euch überraschen!

Wer einen Beta-Key möchte, um in der Closed Beta mitzuspielen, schickt bitte eine Email an allypage@gmail.com. Die Keys sind jedoch nur sehr begrenzt erhältlich!

Manager’s Life

 

JavaScript und Geolocation

Seit Firefox 3.5, Opera 10.60 bzw. Safari/Webkit 5 ist es möglich per JavaScript mit Einverständniss des Benutzers seine Position zu ermitteln. Die Position wird dann entweder über eine angeschlossene GPS-Antenne oder über die verfügbaren WLans ermittelt. Die API dafür ist sehr leicht zu benutzen.

Zunächst sollte geprüft werden ob der benutzte Browser Geolocation unterstützt. Das geht wie folgt:

if (!navigator.geolocation) {
	alert('Sorry, dein Browser unterstützt keine Geolocation-API');
}

Jetzt ist es möglich die aktuelle Position abzufragen:

navigator.geolocation.getCurrentPosition(function(position) {  
	alert('Deine aktuelle Position ist: Lat: ' + position.coords.latitude + ' Long: ' + position.coords.longitude);  
}, function(error) { alert('Fehler beim finden der Position. Fehler: ' + error.message); });

und bei jeder Änderung der Position erneut ein Event auszulösen:

var = locationHandler = navigator.geolocation.watchPosition(function(position) {  
	alert('Deine neue Position ist: Lat: ' + position.coords.latitude + ' Long: ' + position.coords.longitude); 
}, function (e) {}, {enableHighAccuracy:true, maximumAge:120 * 1000, timeout:27000});

Hierbei kann man angeben

  • enableHighAccuray: ob man auf hohe Genauigkeit setzen möchte (sofern diese vom Gerät unterstützt wird)
  • maximumAge: (in ms) wie alt die letzte Position maximal sein darf – je kleiner dieser Wert, desto öfterst wird auf dem Gerät die aktuelle Position abgefragt.
  • timeout: (in ms) wann die Positionsbestimmung abgebrochen werden soll wenn sie nicht erfolgreich ist

Wenn man die Positionsüberwachung wieder abbrechen will, hilft folgender API-Call weiter:

navigator.geolocation.clearWatch(locationHandler);

Sehr nützlich, vorallem wenn man eine Webseite für Smartphones bastelt 😉