function Info_HenrikJensen_Homepage() {
	var p = {
		onReady:function() {
			doLoadContent();
			$("#content-loader").hide();
			// Anchor hash listener will update the view.
			$(window).bind('hashchange', p.onViewRefresh);
			p.onViewRefresh();
		},
		/* 
		View navigation.
		I will not postback. Postback is the little death That brings total Oblivion. 
		I will turn the inner eye. Nothing will be there. Only simplicity will remain.
		
		This is a simple on-demand, browser history friendly way of loading different 
		pages into my master layout. It works using anchor hashes, event handlers and
		a naming convention connecting the two.
		*/
		onViewRefresh:function(e) {
			var next = window.location.hash;
			if (!next || next === "" || next === "#") next = "#homepage";
			
			var viewName = next.slice(1);
			var eventName = Util.format("on{0}{1}", viewName.charAt(0).toUpperCase(), viewName.slice(1));
			
			var eventFn = p[eventName];
			eventFn && eventFn(e, function() {
				// Show requested view.
				var nextView = $(next);
				if (nextView.length == 0)
					throw Exception(Util.format("Unknown view '{0}'", next));
					
				var fn = function() {
					$(nextView).fadeIn("fast", function() {
						$("#footer").fadeIn("fast");
					});
				};
				
				$("#footer").fadeOut("fast");
				var current = $(".view:visible");
				if (current.length == 0)
					fn();
				else
					current.fadeOut("fast", fn());
			});
		},
		// View event
		onHomepage:function(e, fn) {
			fn();
		},
		// View event
		onProfessional:function(e, fn) {
			Util.load("professional.html", "professional", function() {
				fn();
			});
		},
		// View event
		onContact:function(e, fn) {
			Util.load("contact.html", "contact", function(isFirstLoad) {
				if (isFirstLoad) {
					$("#contact-message").cleditor({ 
						width:"680", height:"300",
						controls: "undo redo | bold italic underline | size | alignleft center alignright justify | bullets numbering | outdent indent"
					});
					$("#contact-send1").click(p.onContactSend);
					$("#contact-send2").click(p.onContactSend);
				}						
				fn();
			});
		},
		// Contact form event
		onContactSend:function(e) {
			function onContactFailed(error) {
				if (error != undefined) 
					error = parseInt(error, 10);
				
				var message = "Unknown";
				
				if (isNaN(error)) {
					message = "Sending message failed. I have informed my master of this embarrasing failure.";
				} else {
					// Invalid from email.
					if (error & 1 == 1 || error & 2 == 2 || error & 16 == 16) {
						message = "The 'From' email is missing or invalid. Please correct it and try again.";
						markField("email");
					}
					// Send operation failed.
					if (error & 32 == 32)
						message = "Sending message failed. Please try again."
					$("#contact_fail p").html(message);
					$("#contact_fail").fadeIn("fast");
				}
			}
			function markField(name) {
				$(Util.format("[name='{0}']", name)).addClass("invalid_field")
					.one("blur", function() {
						$(this).removeClass("invalid_field");
					});
			}
			$.post("/contact.form", $("#contact-form").serialize())
				.success(function(output) {
					if (output.error > 0)
						onContactFailed(output.error);
				})
				.error(function(e, jqxhr, settings, exception) { 
					onContactFailed(jqxhr);
				});
		}
	}
	
	function doLoadContent() {
		$.getJSON("/content.js?s=github:gist", function(data) {
			$(data).each(function() {
				var createdAt = new Date(Date.parse(this.created_at)).toLocaleDateString();
				var gist = $("#gist-tpl").clone().removeAttr("id").addClass("gist-item");
				var gistTpl = gist.html();
				gistTpl = Util.format(gistTpl, this.html_url, this.description, createdAt);
				gist.html(Util.format(gist.html(), this.html_url, this.description, createdAt));
				$("#gist-list").append(gist.show());
			});
		});
	}
	
	// -- Public
	
	this.attachTo = function(globalName) {
		window[globalName] = p;
		return window[globalName]; 
	}
}
