Stronghold 3 Review

Moin Moin,

ich hatte das Glück das Amazon meine vorbestellte Version von Stronghold 3 schon am Samstag verschickt hat. Somit ist das ganze heute per Post angekommen und ich habe mir gedacht mal ein Review zu machen. Viel Spass damit 😉

Stronghold 3 Review from Eric Lanfer on Vimeo.

 

 

— Update: Jetzt auch in HD auf Youtube, da auf vimeo nicht genug platz für 30 min 1080p war 🙂

Part 1: http://youtu.be/nU7RIhW8k5g

Part 2: http://youtu.be/89i-Y3a7U1w

 

AJAX und der „Vor“ und „Zurück“ Button

Kleiner Hinweis: Dieser Blogpost nimmt an, das jQuery verwendet wird 😉

Dank AJAX kann man eine Menge Traffic einsparen – allerdings hat AJAX leider ein paar Nachteile: Die Browsernavigation, also die „Vor“ und „Zurück“ Knöpfe des Browsers, funktionieren nicht mehr.
Hier mal ein Beispiel:

HTML:

Inhalt ändern
Hier ist Inhalt 1

JavaScript:

$("a").live("click", function (e) {
    e.preventDefault();
    
    $('#content').load(e.target.href, function() {
        // nothing here yet ;)
    });
});

Klickt man nun auf „Inhalt ändern“, erscheint der Inhalt von content.html im content-div. Wenn jetzt der Benutzer allerdings wieder „Hier ist Inhalt 1“ sehen möchte, kann er nicht den „Zurück“-Button seines Browsers verwenden.

Um dieses Problem zu lösen gibt es zwei Möglichkeiten:

  • Einen hash an die aktuelle URL anhängen, etwa so: /#!content.html
  • Das HTML5 window.history-Objekt bemühen

Da die zweite Methode leider (noch) nicht von jedem Browser unterstützt wird, aber die wesentlich hübschere Methode ist, habe ich mir ein Objekt geschrieben, was beides kann!

/**
 * navigatorHistory
 *
 * Fixes navigator history for ajax-based sites
 *
 * @author agrafix 
 */
var navigatorHistory = {
	/**
	 * Title of site
	 */
	siteTitle: '',
	
	/**
	 * function to load content, needs to be set before hook()-call
	 */
	loaderFunc: null,

	/**
	 * check if the browser supports pushState
	 */
	isSupported : function() {
		return (typeof (window.history.pushState) == 'function');
	},

	/**
	 * add url to history
	 */
	add : function(url) {

		if (this.isSupported()) {
			
			window.history
					.pushState(null, this.siteTitle, url);
		} else {
			window.location.hash = "#!" + url;
		}
	},

	/**
	 * listen for back-forward button events
	 */
	hook : function() {
		if (!this.loaderFunc) {
			alert("No loaderFunc defined!");
			return;
		}
	
		if (this.isSupported()) {
			// doesn't work with jquery, no idea why :O
			window.addEventListener("popstate", function(e) {
				navigatorHistory.loaderFunc(document.location.pathname, true);
			});
		} else {
			$(window).bind('hashchange', function() {
				var location = (window.location.hash).replace(/^#!/, '');
				navigatorHistory.loaderFunc(location, true);
			});
		}

	}
};

Die Benutzung in Verbindung mit dem obigen HTML-Schnipsel ist denkbar einfach:

// funktion zum laden der Seiten definieren
navigatorHistory.loaderFunc = function(url, nohistory) {
	if (!nohistory) {
		navigatorHistory.add(url);
	}
	
	$('#content').load(url);
};

// seiten titel
navigatorHistory.siteTitle = "Deine Seite";

// auf vor bzw. zurück-button klicks hören
navigatorHistory.hook();

// die eigentliche ajax-funktion
$("a").live("click", function (e) {
    e.preventDefault();
	
	navigatorHistory.loaderFunc(e.target.href);
});

Eigentlich relativ einfach einzubauen und es macht eine Seite deutlich benutzerfreundlicher!

 

Unix-Timestamp in JavaScript formatieren

Um ein Unix-Timestamp mit JavaScript zu formatieren, habe ich mir folgendes Objekt geschrieben:

var timeFormat = {
    /**
     * Output format.
     *
     * d - current day
     * m - current month
     * Y - current Year
     *
     * H - current hour (24 hour system)
     * i - current minute
     * s - current second
     */
    displayFormat: 'd.m.Y - H:i:s',

    /**
     * Format given unix-timestamp
     */
    format: function(timestamp) {
        // convert unix timestamp to javascript date object
        var d = new Date(timestamp * 1000);
        var output = this.displayFormat;
        
        output = output.replace(/d/g, this.padZero(d.getDate()))
            .replace(/m/g, this.padZero(d.getMonth()+1))
            .replace(/Y/g, d.getFullYear())
            .replace(/H/g, this.padZero(d.getHours()))
            .replace(/i/g, this.padZero(d.getMinutes()))
            .replace(/s/g, this.padZero(d.getSeconds()));

        
        return output;

    },
   
    /**
     * add zero paddings to numbers smaller than 10
     */
    padZero: function(number) {
        if (number < 10) {
            return "0" + number.toString();
        }
        
        return number;
    }
};

Die Benutzung ist ziemlich einfach:

// schreibt 13.09.2011 - 13:30:23
document.write(timeFormat.format(1318473023));

Man kann das Ausgabeformat über das timeFormat.displayFormat Feld steuern:

timeFormat.displayFormat = 'H:i'; // gibt nur Stunden und Minuten aus
document.write(timeFormat.format(1318473023)); // schreibt 13:30

Wie bereits oben beschrieben sind folgende Zeichen möglich (auch mehrfach)

    /**
     * Ausgabeformat
     *
     * d - aktueller Tag
     * m - aktueller Monat
     * Y - aktuelles Jahr
     *
     * H - aktuelle Stunde
     * i - aktuelle Minute
     * s - aktuelle Sekunde
     */
 

JavaScript Timers mit jQuery

Timer in JavaScript zu realisieren ist eigentlich recht leicht, dazu braucht mal nur die Funktionen window.setTimeout bzw. window.setInterval mit entsprechenden Argumenten zu füttern.

// funktion verzögert aufrufen
function machWas() {
   alert('alert() mit 1000ms verzögerung!');
}
window.setTimeout(machWas, 1000);

// funktion alle X Millisekunden aufruden
function machWasOft() {
   alert('alle 1000ms ein alert()');
}
window.setInterval(machWasOft, 1000);

Doch was ist, wenn ich jetzt einen solchen Timer ändern oder abbrechen möchte? Das ist eigentlich auch nicht schwer – die Funktionen setTimeout/setInterval geben ein Objekt zurück über das sie sich wieder beenden lassen.

var x = 0;
function machWasAbbrechbarOft() {
    x += 1;
    if (x > 200) {
        window.clearInterval(running); // beende das "Interval" nach 201 Ausführungen
    }
}
var running = window.setInterval(machWasAbbrechbarOft, 1000);

Das funktioniert in kleinen JavaScript Codes sicher ohne Probleme, wenn man allerdings an einem großem JavaScript Projekt arbeitet, macht es die Arbeit nicht gerade leichter wenn man immer dieses Rückgabe-Objekt (im Beispiel running) irgendwo speichern muss, vorallem wenn man von überall im Code darauf zugreifen will.

Um dieses Problem zu lösen habe ich ein jQuery Timer Plugin gefunden. (Siehe http://plugins.jquery.com/project/timers) Wer an einem größerem JavaScript Projekt arbeitet, sollte meiner Meinung nach aufjedenfall auf ein Framework setzen – jQuery ist hier meine Wahl von daher bietet sich dieses Plugin an.

Mit diesem Plugin geht das „Interval“ setzen dann wie folgt:

$(document).everyTime(1000, "machWasOftAbbrechbar", function(i) { // alle 1000ms
  alert('Hallo ich bin der ' + i + 'te Aufruf!'); // machWasOftAbbrechbar, hier die funktion die aufgerufen werden soll
}, 200); // 200 mal ausführen

Der zweite Parameter, machWasOftAbbrechbar, stellt hierbei ein sogenanntes „label“ dar, über das man den gestarten Timer dann wieder abbrechen kann:

$(document).stopTime("machWasOftAbbrechbar"); // stoppe machWasOftAbbrechbar

Viel mehr gibt’s eigentlich nicht dazu zu sagen – die Dokumentation dazu findet man, genau wie das Plugin selbst, unter http://plugins.jquery.com/project/timers.

 

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 😉

 

Abstand von zwei GPS-Koordinaten (SQL Funktion)

Für mein aktuelles Projekt muss meine Datenbank den Abstand zwischen zwei GPS-Koordinaten berechnen. Dies Funktioniert ganz einfach mit meiner folgenden Funktion GPS_DISTANCE:

DELIMITER |;
CREATE FUNCTION GPS_DISTANCE(lat1 DOUBLE, long1 DOUBLE, lat2 DOUBLE, long2 DOUBLE)
	RETURNS DOUBLE
	DETERMINISTIC
		BEGIN
			DECLARE dist DOUBLE;
			SET dist = ACOS(SIN(RADIANS(lat1)) * SIN(RADIANS(lat2)) + COS(RADIANS(lat1)) * COS(RADIANS(lat2)) * COS(RADIANS(long2)-RADIANS(long1))) * 6371 * 1000;
			RETURN dist;
		END|

Die Parameter sind jeweils die GPS-Länge und die GPS-Breite (in Grad). Der Rückgabewert ist der Abstand in Metern.