/**
 * A collection of general purpose functions and classes for use in the Web Framework.
 */


/**
 * Returns an array containing the name-value pairs encoded on the querystring of the current page.
 * @return {Object} A list of the name-value pairs on the querystring.
 */
function QueryValueList () {
	var list = {};
	var query = window.location.search.substring (1);
	var vars = query.split ('&');
	for (var idx = 0; idx < vars.length; idx++) {
		var pair = vars[idx].split ('=');
		list[unescape (pair[0])] = unescape (pair[1]);
	}
	return list;
};


/**
 * Transforms the given list of name-value pairs into a properly escaped querystring (?...&...)
 * @param {Object} parms A list of name-value pairs.
 */
toQueryString = function (parms) {
	var res = '', sep = '?';

	for (var idx in parms) {
		res += sep + escape (idx) + '=' + escape (parms[idx]);
		sep = '&';
	}

	return res;
};


fatalError = function (titletext, messagetext, additionaltext) {
	var div = HTML.createDiv ('fatalerror');

	var title = HTML.createHeading1 ('title');
	var titlenode = HTML.createTextNode (titletext);
	HTML.nest (div, title, titlenode);

	if (messagetext) {
		var message = HTML.createDiv('message');
		message.innerHTML = messagetext;
		
		HTML.nest (div, message);
	}

	if (additionaltext) {
		var adddiv = HTML.createDiv ('additional');
		adddiv.innerHTML = additionaltext;
		HTML.nest (div, adddiv);
	}

	var root = HTML.getElementById('WebFramework');
	HTML.add (root, div);
};


unfocusAll = function () {
	var temp = HTML.createTextInput ();
	var vp = windowViewport();
	temp.style.position = 'absolute';
	temp.style.left = '-5000px';
	temp.style.top = vp.y + 'px';
	HTML.add (HTML.body (), temp);
	temp.focus ();
	HTML.remove (HTML.body (), temp);
};


/**
 * Creates a callback function that ensures a correct binding between an object and the function
 * to be called.
 * @param {Object} context The desired context for the handler.
 * @param {Function} handler The function that should be called upon invocation.
 * @return {Function} A function that will call the given handler within the specified context
 *  (passing along any parameters it might receive)
 */
callback = function (context, handler) {
	return function () {
			return handler.apply (context, arguments);
		};
};


/**
 * Creates a (nested) javascript 'namespace', provided it does not exist yet.
 * @param {String} namespace The namespace to create (hierarchic namespaces are supported, for
 *  example: 'Utilities.XML')
 */
namespace = function (namespace) {
	var names = namespace.split ('.');
	var prev = window;
	for (var idx = 0; idx < names.length; idx++) {
		if (typeof (prev[names[idx]]) == 'undefined') {
			prev[names[idx]] = {};
		}
		prev = prev[names[idx]];
	};
};


/**
 * Determine whether the browser running this script is Internet Explorer 6. Note that
 * browser-sniffing is discouraged, so use as little as possible.
 * @return {Boolean} Returns whether the browser has been identified as Internet Explorer 6.
 */
isIE6 = function () {
	var ie6 = false;
	if (navigator.appName == 'Microsoft Internet Explorer') {
		var re = new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})');
		if (re.exec (navigator.userAgent) != null)
			ie6 = (parseFloat (RegExp.$1) < 7.0);
	}
	isIE6 = function (){ return ie6 };
	return ie6;
};


/**
 * Provides a hook for registering and notifying one or more listeners.
 */
EventHook = function () {
	this._handlers = [];
};

/**
 * Add a listener to the eventhook. No duplicate check is performed, so adding the same
 * handlers multiple times will result in multiple calls upon notification.
 * @param {Function} handler The function that should be call upon notification. 
 */
EventHook.prototype.add = function (handler) {
	this._handlers.push (handler);
};

/**
 * Removes all entries of the listener from the eventhook. If the handler was added
 * multiple times, all of them will be removed.
 * @param {Function} handler The function that should no longer receive notifications.
 */
EventHook.prototype.remove = function (handler) {
	var idx = 0;
	while (idx < this._handlers.length) {
		if (this._handlers[idx] == handler) {
			this._handlers.splice (idx, 1);
		} else {
			idx++;
		}
	}
};

/**
 * Calls all registered listeners with the specified parameters.
 * @param {Object} ... The parameters to pass onto the listeners. 
 */
EventHook.prototype.notify = function () {
	var handlers = this._handlers;
	for (var idx = 0; idx < handlers.length; idx++) {
		handlers[idx].apply (null, arguments);
	}
};


/**
 * Returns the position and dimensions of the viewport in relation to the document.
 * @return {Object} An object containing the following properties: x, y, width, height.
 */
windowViewport = function () {
	var x = 0, y = 0, w = 0, h = 0, b = document.body, d = document.documentElement;

	if (d && (d.scrollLeft || d.scrollTop)) {
		x = d.scrollLeft;
		y = d.scrollTop;
	} else if (b && (b.scrollLeft || b.scrollTop)) {
		x = b.scrollLeft;
		y = b.scrollTop;
	} else if (typeof(window.pageYOffset) == 'number') {
		x = window.pageXOffset;
		y = window.pageYOffset;
	} else if (window.scrollX || window.scrollY) {
		x = window.scrollX;
		y = window.scrollY;
	}
	
	if (d && (d.clientWidth || d.clientHeight)) {
		w = d.clientWidth;
		h = d.clientHeight;
	} else if (b && (b.clientWidth || b.clientHeight)) {
		w = b.clientWidth;
		h = b.clientHeight;
	} else if (typeof(window.innerWidth) == 'number') {
		// non-IE
		w = window.innerWidth;
		h = window.innerHeight;
	} 
	return {'x': x, 'y': y, 'width': w, 'height': h};
};



merge = function () {
	var adx, idx, base = {};
	for (adx = 0; adx < arguments.length; adx++) {
		var src = arguments[adx];
		for (idx in src) {
			base[idx] = src[idx];
		}
	}
	return base;
};

