/* --- JavaScript --- */
/* --- General --- */


/* --- add functions to onload event: addLoadEvent(functionName); --- */
function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

/* ============== */
/* === EVENTS === */
/* ============== */

/* === addEvent/removeEvent written by Dean Edwards (2005) with input from Tino Zijdel, Matthias Miller, Diego Perini: http://dean.edwards.name/weblog/2005/10/add-event/ === */
function addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		// assign each event handler a unique ID
		if (!handler.$$guid) handler.$$guid = addEvent.guid++;
		// create a hash table of event types for the element
		if (!element.events) element.events = {};
		// create a hash table of event handlers for each element/event pair
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			// store the existing event handler (if there is one)
			if (element["on" + type]) {
				handlers[0] = element["on" + type];
			}
		}
		// store the event handler in the hash table
		handlers[handler.$$guid] = handler;
		// assign a global event handler to do all the work
		element["on" + type] = handleEvent;
	}
};
// a counter used to create unique IDs
addEvent.guid = 1;

function removeEvent(element, type, handler) {
	if (element.removeEventListener) {
		element.removeEventListener(type, handler, false);
	} else {
		// delete the event handler from the hash table
		if (element.events && element.events[type]) {
			delete element.events[type][handler.$$guid];
		}
	}
};

function handleEvent(event) {
	var returnValue = true;
	// grab the event object (IE uses a global event object)
	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
	// get a reference to the hash table of event handlers
	var handlers = this.events[event.type];
	// execute each event handler
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};

function fixEvent(event) {
	// add W3C standard event methods
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
	this.cancelBubble = true;
};
/* --- addEvent/removeEvent --- */

var stopDefault = function( e ) {
	if ( e && e.preventDefault ) e.preventDefault();	// Prevent default browser action (W3C)
	else window.event.returnValue = false;				// IE version
	return false;
}

/* =========== */
/* === DOM === */
/* =========== */

var create = function( elem ) {
	return document.createElementNS ? document.createElementNS( 'http://www.w3.org/1999/xhtml', elem ) : document.createElement( elem );
}

var before = function( parent, before, elem ) {
	if ( elem == null ) {								// Check to see if no parent was provided
		elem = before;
		before = parent;
		parent = before.parentNode;
	}
	parent.insertBefore( checkElem( elem ), before );
}

var append = function( parent, elem ) {
	parent.appendChild( checkElem( elem ));
}

var checkElem = function( elem ) {
	return elem && elem.constructor == String ? document.createTextNode( elem ) : elem;	// if a string was provided, convert it into a text node
}

var remove = function( elem ) {
	if ( elem ) elem.parentNode.removeChild( elem );
}

var empty = function( elem ) {
	while ( elem.firstChild ) remove( elem.firstChild );
}

/* --- finding elements --- */
var id = function( name ) {
	return document.getElementById( name );	// -> HTML element
};

var tags = function( name, elem ) {
	return ( elem || document ).getElementsByTagName( name );	// -> Array
};

var withClass = function( name, sel, sel2 ) {												// sel and sel2 are both optional and can be either an element or a tag (one of each)
	var haveClass = [];
	var regExp = new RegExp( "(^|\\s)" + name + "(\\s|$)" );								// allows for multiple class names
	var tagName = sel.constructor == String ? sel : sel2;
	var elem = sel.constructor == String ? sel2 : sel;
	var elements = ( elem || document).getElementsByTagName( tagName || "*" );		// use document if no element has been specified and get all elements if no tag name has been specified
	for ( var e = 0; e < elements.length; e++) {
		var element = elements[ e ];
		if ( hasClass( element, name )) haveClass[ haveClass.length ] = element;
	}
	return haveClass;																					// -> Array
};

var hasClass = function( elem, name ) {
	if ( !name && elem.className != "") return true;			// if no className has been specified any className will do | -> Boolean
	var regExp = new RegExp( "(^|\\s)" + name + "(\\s|$)" );	// allows for multiple class names
	if ( regExp.test( elem.className )) return true;			// -> Boolean
	return false;															// -> Boolean
};

var prev = function( elem ) {
	do {
		elem = elem.previousSibling;
	} while ( elem && elem.nodeType != 1 );
	return elem;
}

var next = function( elem ) {
	do {
		elem = elem.nextSibling;
	} while ( elem && elem.nodeType != 1 );
	return elem;
}

var first = function( elem ) {
	elem = elem.firstChild;
	return elem && elem.nodeType != 1 ? next( elem ) : elem;
}

var last = function( elem ) {
	elem = elem.lastChild;
	return elem && elem.nodeType != 1 ? next( elem ) : elem;
}


/* --- get/set attributes --- */
var attr = function( elem, attrib ) {													// attrib can be a String (get) or an Object (set)
	if ( attrib.constructor == String ) {
		name = validName( attrib );
		return elem[ name ] || elem.getAttribute( name ) || '';					// -> String (attribute value)
	}
	else if ( attrib.constructor == Object ) {
		for ( n in attrib ) {
			var value = attrib[ n ];
			name = validName( n );
			if ( name == "className" || name == "rel" || name == "rev" ) {
				addToAttr( elem, name, value );
			}
			else {
				elem[ name ] = value;														// do it quick if possible
				if ( elem.setAttribute ) elem.setAttribute( name, value );		// XML fallback
			}
		}
		return true;																			// -> Boolean (values are set)
	}
	function validName( name ) {
		return { 'for': 'htmlFor', 'class': 'className' }[ name ] || name;	// rename in case of 'for' or 'class' attribute
	};
	return '';																					// -> String (empty: attrib was neither a String or Object)
};

var addClass = function( elem, name ) {
	addToAttr( elem, "className", name );
}

var removeClass = function( elem, name ) {
	removeFromAttr( elem, "className", name );
}

var addToAttr = function( elem, attr, value ) {	// can be used for class, rel and rev attributes
	removeFromAttr( elem, attr, value );			// make sure there won't be any doubles
	elem[ attr ] += " " + value;
}

var removeFromAttr = function( elem, attr, value ) {						// can be used for class, rel and rev attributes
	var remain = [];
	var values = elem[ attr ].split(/\s+/);									// seperate class names (devided by one or more whitespaces)
	for ( var v = 0; v < values.length; v++ ) {
		if ( values[ v ] != value ) remain[ remain.length ] = values[ v ];
	}
	elem[ attr ] = remain.join( " " );
}


/* --- check for CSS support --- */
var cssSupport = function() {
	if ( !document.styleSheets ) return false;									// styleSheets object is not supported
	var css = document.styleSheets;
	for ( var s = 0; s < css.length; s++) {
		if ( s == 0 ) {
			if ( !( css[ 0 ].cssRules || css[ 0 ].rules )) return false;	// both methods (cssRules/rules) are not supported
		}
		if ( !css[ s ].disabled ) return true;										// at least one of the stylesheets is not disabled
	}
	return false;																			// stylesheets are all disabled or not supported at all
}

/* --- place labels over corresponding form field --- */
var overLabel = function() {
	var labels = withClass( "overLabel", "label" );
	if ( !labels ) return;
	
	for ( var l = 0; l < labels.length; l++ ) {
		var label = labels[ l ];
		if ( !label.htmlFor ) continue;
		
		var overControl = id( label.htmlFor );
		if ( !overControl ) continue;
		
		label.forControl = overControl;																					// make reference from label to corresponding control
		
		if ( overControl.value === "" ) addClass( overControl.parentNode, "inactive" );					// make sure label is only placed on top of control in case it has no value which is not always the case after a reload
		
		addEvent( overControl, "focus", function() { removeClass( this.parentNode, "inactive" )} );
		addEvent( overControl, "blur", function() { if ( this.value === "" ) addClass( this.parentNode, "inactive" )} );
		addEvent( label, "click", function() { this.forControl.focus()} );							// give focus to corresponding control (needed for Safari)
	}
	
	addClass( document.body, "jsLabelsOn" );															// CSS hook to turn it on
}


/* --- call functions only if the used methods are supported --- */
if (document.getElementById && document.getElementsByTagName) {
	if (cssSupport()) {
		addLoadEvent(overLabel);
	}
}