/**
 * 'foundations.js'
 * Contains some useful functions which wraps oft-used functionality
 * in separate functions, and creates wrappers for basic dom functionality
 * which sadly still has to be dealt with slightly differently between
 * some browsers (mostly IE vs. "the rest").
 *
 * @author Frode Danielsen
 * @version 1.0
 * @date 2/4-05
 **/

function concat() {
    var result = [];

    for (var i = 0; i < arguments.length; i++)
        for (var j = 0; j < arguments[i].length; j++)
            result.push(arguments[i][j]);

    return result;
}

function withoutFirst(sequence) {
    result = [];

    for (var i = 1; i < sequence.length; i++)
        result.push(sequence[i]);

    return result;
}

function cons(element, sequence) {
    return concat([element], sequence);
}

Function.prototype.bind = function(object) {
	var method = this;
    var preappliedArguments = withoutFirst(arguments);
	return function() {
		return method.apply(object, concat(preappliedArguments, arguments));
	};
};
Function.prototype.bindEventListener = function (object) {
    var method = this;
    var preappliedArguments = withoutFirst(arguments);
    return function (event) {
        return method.apply(object, cons(event || window.event, preappliedArguments));
    };
};

/**
 * Wrapper for adding an eventlistener to an element.
 * Most browsers support 'addEventListener', but IE chose 'attachEvent' and
 * does not support the optional fourth parameter concerning the capturing
 * moment of the event.
 **/
function _addEventListener(element, event, handler, capture) {
	if (element.addEventListener) {
		element.addEventListener(event, handler, capture);
	} else if (element.attachEvent) {
		element.attachEvent('on'+event, handler);
	}
}

function _removeEventListener(element, event, handler, capture) {
	if (element.removeEventListener) {
		element.removeEventListener(event, handler, capture);
	} else if (element.detachEvent) {
		element.detachEvent('on'+event, handler);
	}
}

/**
 * Wrapper for getting the target of an event. Basically IE calls this
 * the 'srcElement', while others use simply 'target'. Another thing is a
 * slight bug in Safari as of now, where the target can be the textnode of
 * an element, while we usually want the element surrounding the textnode.
 *
 * NOTE: Should specify this as only for certain events, and write the 
 * wrapper for mouseover/out events too.
 **/
function _getEventTarget(evt) {
	var t;
	
	if (evt.target) t = evt.target;
	else if (evt.srcElement) t = evt.srcElement;

	if (t.nodeType == 3) // gå rundt Safari-bøgg
		t = t.parentNode;

	return t;
}

function _getMousePosition(evt) {
	var posx = 0;
	var posy = 0;

	if (!evt) var evt = window.event;
	if (evt.pageX || evt.pageY) {
		posx = evt.pageX;
		posy = evt.pageY;
	} else if (evt.clientX || evt.clientY) {
		posx = evt.clientX + document.body.scrollLeft;
		posy = evt.clientY + document.body.scrollTop;
	}

	return new Array(posx, posy);
}

/**
 * Searches upward the DOM tree from the element specified as child, until
 * it reaches a parent element with the specified tag name. If none is found
 * (i.e. reaching the root element with no success so far), null is returned.
 **/
function _getParentWithTagName(child, tagName) {
	if (!child)
		return null;

	var to_match = new RegExp('^'+tagName+'$', 'i');
	var parent = child.parentNode;
	
	while (!parent.nodeName.match(to_match) && parent.parentNode) {
		parent = parent.parentNode;
	}
	
	if (!parent.nodeName.match(to_match)) {
		return null;
	}
	
	return parent;
}

/**
 * Complement to the DOM method getElementsByTagName, which filters
 * the elements based on their class name. Only elements whose class
 * attribute contains the specified className will be returned.
 * One can also filter on the tag name, so only a subset of all elements
 * with the specified class name gets returned.
 *
 * Until I learn/figure out how to add functions to DOM classes, I've
 * added a parameter to specify the root element to search within, which
 * defaults to the body element if not specified.
 **/
function _getElementsWithClassName(className, tagName, inRoot) {
	var elements = new Array();
	
	if (!inRoot) inRoot = document;
	if (!tagName) tagName = '*';
	
	var source = inRoot.getElementsByTagName(tagName);

	if (source) {
		for (var si = 0; si < source.length; si++) {
			if (source[si].className.indexOf(className) != -1) {
				elements.push(source[si]);
			}
		}
	}
	
	return elements;
}

/**
 * Object for ease of handling coordinates
 **/
function Coordinates(x, y) {
	this.x = x;
	this.y = y;
	
	this.toString = function() {
		return '[ Coordinates: x='+this.x+', y='+this.y+' ]';
	}
}

/**
 * EXPERIMENTAL
 * Finds the coordinates for an Element.
 **/
function _getElementCoordinates(elm) {
	var coords = new Coordinates(0, 0);

	if (elm.offsetParent) {
		while (elm.offsetParent) {
			coords.x += elm.offsetLeft;
			coords.y += elm.offsetTop;
			elm = elm.offsetParent;
		}
	} else if (elm.x && elm.y) {
		coords.x += elm.x;
		coords.y += elm.y;
	}
	
	return coords;
}

/**
 * EXPERIMENTAL
 * Finds the coordinates of the mouse from an event.
 **/
function _getMouseCoordinates(e) {
	var posx = 0;
	var posy = 0;
	
	if (!e) var e = window.event;
	if (e.pageX || e.pageY) {
		posx = e.pageX;
		posy = e.pageY;
	} else if (e.clientX || e.clientY) {
		posx = e.clientX + document.body.scrollLeft;
		posy = e.clientY + document.body.scrollTop;
	}
	
	return new Coordinates(posx, posy);
}

/*
function findClickedBox(e) {
	var posx = 0;
	var posy = 0;
	if (!e) var e = window.event;
	if (e.pageX || e.pageY)
	{
		posx = e.pageX;
		posy = e.pageY;
	} else if (e.clientX || e.clientY) {
		posx = e.clientX + document.body.scrollLeft;
		posy = e.clientY + document.body.scrollTop;
	}
	
	var theBox = null;
	for (var bi = 0; bi < boxes.length; bi++) {
		var x = findPosX(boxes[bi]);
		var y = findPosY(boxes[bi]);
		
		if (posx > x && posx < (x + 100)) {
			if (posy > y && posy < (y + 100)) {
				theBox = boxes[bi];
				break;
			}
		}
	}
	
	if (theBox != null && theBox.className == 'small')
		showBox(theBox);
}
*/