// Constants, but not really
var REGEX_VALID_EMAIL		= /\b[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i,
	REGEX_VALID_ZIPCODE_NL	= /[0-9]{4} ?[a-z]{2}/i,
	REGEX_NOT_EMPTY			= /.+/;


// Containers
var mod				= {},
	cookies			= {},
	elements		= {};


/**
 * Sets the class "js" to the body element, so JavaScript dependant CSS can be applied
 * @method
 */

function jsEnabled() {
	$("body").addClass("js");
}

/**
 * Disables the page
 * @method
 */
function fade()
{
	var body = $("body");
	var fadeoverlay = $("#fade_overlay");

	body.toggleClass("hideflash");

	if (fadeoverlay.length > 0) 
	{
		fadeoverlay.remove();
	}
	else
	{
		body.append(
			$(document.createElement("div")).attr("id", "fade_overlay")
		);
	}
}



/**
 * Preload images
 * @method
 * @requires DOM
 */

function preload() 
{
	dom = new DOM();
	for (var i = 0; arguments[i] !== undefined; i++) {
		dom.create(
			"img",
			{
				src : arguments[i],
				alt : ""
			},
			null
		);
	}
}



/**
 * EventDispatcher
 * @constructor
 */
function EventDispatcher()
{
	// Public
	this.addEventListener		= addEventListener;
	this.removeEventListener	= removeEventListener;
	this.dispatchEvent			= dispatchEvent;

	// Private
	var events = {};

	/**
	 * addEventListener Adds and eventlistener
	 * @param {String} type
	 * @param {Function} listener
	 * @throws {Invalid Argument} When listener is missing or not a function
	 */
	function addEventListener(type, listener)
	{
		if (typeof listener != "function")
		{
			throw new Error("Invalid argument: listener is missing or not a function");
		}

		if (typeof events[type] == "undefined")
		{
			events[type] = [];
		}

		events[type].push(listener);
	}

	/**
	 * removeEventListener Deletes an eventlistener
	 * @param {String} type
	 * @param {Function} listener
	 */
	function removeEventListener(type, listener)
	{
		if (events[type] != undefined)
		{
			for (var i = 0; i < events[type].length; i++)
			{
				if (events[type][i] == listener)
				{
					delete events[type][i];

					break;
				}
			}
		}
	}

	/**
	 * dispatchEvent Dispatches an eventlistener
	 * @throws {Invalid argument} event is not an instance of CustomEvent
	 * @param {Event} event
	 * @event
	 */
	function dispatchEvent(event)
	{
		if (event.constructor != CustomEvent)
		{
			throw new Error("Invalid argument: event is not an instance of CustomEvent");
		}

		var type = event.type;

		if (events[type] != undefined)
		{
			for (var i = 0; i < events[type].length; i++)
			{
				events[type][i](event);
			}
		}
	}
}



/**
 * CustomEvent
 * @constructor
 * @param {String} type
 * @param {Object} target
 */
function CustomEvent(type, target)
{
	// Public
	this.type	= type;
	this.target	= target;
}



/**
 * Page
 * @extends EventDispatcher
 * @constructor
 */
function Page()
{
	// Call the prototype's constructor
	EventDispatcher.call(this);

	// Private
	var self				= this,
		pageLoaded			= false,
		requestVariables	= [];

	// Public
	this.setQueryString			= setQueryString;
	this.getRequestVariables	= getRequestVariables;

	this.queryString			= {};

	// Constructor
	setPageLoadedListeners();
	getQueryString();
	parseRequest();

	/**
	 * Checks if the DOM has loaded in Mozilla (Firefox), Opera 9 and Internet Explorer
	 * @see The <a href="http://dean.edwards.name/weblog/2006/06/again/#comment5338">dean.edwards.name</a>.
	 */
	function setPageLoadedListeners()
	{
		// W3C event model compliant user agent
		if (document.addEventListener)
		{
			// Mozilla/Opera 9
			document.addEventListener("DOMContentLoaded", dispatchPageLoaded, false);

			// Every other browser (except for IE)
			window.addEventListener("load", dispatchPageLoaded, false);
		}

		// Internet Explorer
		if (window.attachEvent)
		{
			window.attachEvent("onload", dispatchPageLoaded);
		}
	}

	/**
	 * Dispatches the page loaded event
	 * @event
	 * @param {Event} event
	 */
	function dispatchPageLoaded(event)
	{
		if (pageLoaded == false)
		{
			pageLoaded = true;

			self.dispatchEvent(new CustomEvent(Page.LOADED, self));
		}
	}

	/**
	 * Returns the query string
	 */
	function getQueryString()
	{
		if (location.search != "")
		{
			var queryString = location.search.substring(1).split("&"),
				keyValuePair;

			for (var i = 0; i < queryString.length; i++)
			{
				keyValuePair = queryString[i].split("=");

				self.queryString[keyValuePair[0]] = keyValuePair[1];
			}
		}
	}

	/**
	 * Sets the location.search
	 */
	function setQueryString()
	{
		var queryString = "?";

		for (var key in self.queryString)
		{
			queryString += key + "=" + self.queryString[key] + "&";
		}

		location.search = queryString.substring(0, queryString.length - 1);
	}

	/**
	 * Parse the URL into request variables
	 */
	function parseRequest()
	{
		var path	= unescape(location.pathname).split("/"),
			regEx	= /^[0-9]+$/;

		if (path[0] == "")
		{
			path.shift();
		}

		for (var i = path.length - 1; i != 0; --i)
		{
			if (regEx.test(path[i]) == false)
			{
				path.pop();
			}
		}

		requestVariables = path;
	}

	/**
	 * Get the request variables
	 * @return {Array} The request variables in an array, starting with the page name.
	 */
	function getRequestVariables()
	{
		return requestVariables;
	}
}

// Inheritance
Page.prototype = new EventDispatcher();

/** @constant */
Page.LOADED = "loaded";



/**
 * Returns the query string
 * @param {String} vouvoyer
 * @param {String} tutoyer
 * @return {String} vouvoyer if tutoiement is undefined or false, else tutoyer
 */
function getVousOuTu(vouvoyer, tutoyer)
{
	if (typeof tutoiement == "undefined" || tutoiement == false)
	{
		return vouvoyer;
	}

	return tutoyer;
}



/**
 * Controller, keeps a reference to the page object and the parameters
 * @constructor
 */
var controller = new function()
{
	var self 				= this,
		parameters 			= {},
		pageObjectReference = new Page();

	this.setPageObject 			= setPageObject;
	this.getPageObject 			= getPageObject;
	this.setParameter 			= setParameter;
	this.removeParameter 		= removeParameter;
	this.getParameter			= getParameter;
	this.getParameters			= getParameters;
	this.registerCommonObject	= registerCommonObject;
	this.getCookie				= getCookie;
	this.getCookies				= getCookies;
	this.setCookie				= setCookie;
	this.removeCookie			= removeCookie;

	getCookies();

	/**
	 * Sets the pageobject
	 * @param {Object} pageObject The pageObject
	 */
	function setPageObject(pageObject)
	{
		if (pageObject instanceof Page)
		{
			pageObjectReference	= pageObject;
		}
	}

	/**
	 * Gets the pageobject
	 * @return {Object} pageObjectReference The reference tot the page object
	 */
	function getPageObject()
	{
		return pageObjectReference;
	}

	/**
	 * Registers functions described as common
	 * @param {Function} commonObject The commonObject (function)
	 */
	function registerCommonObject(commonObject)
	{
		if (typeof commonObject != "function") 
		{
			throw new Error("Invalid argument: commonObject is missing or not a valid function");
		}

		pageObjectReference.addEventListener(Page.LOADED, commonObject, false);
	}

	/**
	 * Set a parameter
	 * @param {String} parameterName The name of the parameter
	 * @param {String} parameterValue The value of the parameter
	 */
	function setParameter(parameterName, parameterValue)
	{
		if (typeof parameterName != "string") 
		{
			throw new Error("Invalid argument: parameterName is missing or not a string");
		}

		if (typeof parameterValue != "string")
		{
			throw new Error("Invalid argument: parameterValue is missing or not a string");
		}

		parameters[parameterName] = parameterValue;
	}

	/**
	 * Get a parameter
	 * @param {String} parameterName The name of the parameter
	 * @throws {Invalid argument} parameterName is missing or not a string. 
	 * @return {String} The parameter value
	 */
	function getParameter(parameterName)
	{
		if (typeof parameterName != "string") 
		{
			throw new Error("Invalid argument: parameterName is missing or not a string");
		}

		if (parameters[parameterName] != "undefined") 
		{
			return parameters[parameterName];
		}
		else
		{
			return false;
		}
	}

	/**
	 * Remove a parameter
	 * @param {String} parameterName The name of the parameter
	 */
	function removeParameter(parameterName)
	{
		delete parameters[parameterName];
	}

	/**
	 * Get all parameters
	 * @return {Array} parameters All parameters
	 */
	function getParameters()
	{
		return parameters;
	}

	/**
	 * Get all cookies
	 * @return {Array} parameters All parameters
	 */
	function getCookies()
	{
		if (document.cookie != "")
		{
			var cookies			= document.cookie.split(";"),
				trimRegEx		= /^\s|\s$/g,
				keyValuePair	= null;

			for (var i = 0; i < cookies.length; i++)
			{
				keyValuePair = cookies[i].split("=");

				cookies[keyValuePair[0].replace(trimRegEx, "")] = keyValuePair[1];
			}
		}
	}

	/**
	 * Get one cookies by name
	 * @param {String} name the name of the cookie
	 * @return {String} The cookievalue
	 */
	function getCookie(name)
	{
	    var documentcookie = document.cookie;
	    var prefix = name + "=";
	    var begin = documentcookie.indexOf("; " + prefix);

	    if (begin == -1)
	    {
	        begin = documentcookie.indexOf(prefix);
	        if (begin != 0)
	        {
	        	return null;
	        }
	    }
	    else
	    {
	        begin += 2;
	    }

	    var end = document.cookie.indexOf(";", begin);

	    if (end == -1)
	    {
	        end = documentcookie.length;
	    }
	    return unescape(documentcookie.substring(begin + prefix.length, end));
	}

	/**
	 * Set a cookie
	 * @param {String} name The name of the cookie
	 * @param {String} value The value of the cookie
	 * @param {String} expiration The expiration of the cookie in minutes default ""
	 * @param {String} path The path of the cookie default ""
	 */
	function setCookie(name, value, expiration, path)
	{
		if (typeof expiration == "number")
		{
			var expirationDate = new Date();

			expirationDate.setMinutes(expirationDate.getMinutes() + expiration);

			expiration = "; expires=" + expirationDate.toUTCString();
		}
		else
		{
			expiration = "";
		}

		if (path === true)
		{
			path = "; path=/"
		}
		else
		{
			path = "";
		}
		document.cookie = name + "=" + escape(value) + path + expiration;

		getCookies();
	}

	/**
	 * Remove a cookie
	 * @param {String} name The name of the cookie
	 */
	function removeCookie(name)
	{
		setCookie(name, "", -1);

		getCookies();
	}
}