/************************************************************************************/
/* @TITLE
/*	jslib::HtmlElement - HTML-DOM-Modul
/*
/* @SYNOPSIS
/*	<script type="text/javascript" src="/jslib/HtmlElement.js"></script>
/*
/* @DESCRIPTION
/*	Dieses Modul bietet grundlegende Funktionen zum Zugriff auf HTML-Elemente,
/*	Werte (lesen und schreiben) von Formular-Elementen. Die Funktionen bauen
/*	aufeinander auf, um auch bspw. beim Auslesen eines Eingabefeld-Wertes einen
/*	"sicheren" Zugriff auf das zugrundeliegende Element zu haben. Hierbei wird
/*	(in hierarchischer Kaskade) versucht, das Element zunächst über die ID, dann
/*	über den Namen im Formular zu ermitteln und bei Fehlschlag per Alert zu eskalieren,
/*	so dass Scriptfehler letztlich nicht auftreten können.
/*
/* @AUTHOR
/*	Marcus Bloch, Thilo Girmann
/*
/* @COPYRIGHT
/*	Copyright © 2004-2005 by CHAMAELEON AG (http://www.chamaeleon.de) - November 2005
/*************************************************************************************/

function findNamedElementInCollection(collection, name, propertyName, errorMessage) {
	/********************************************************************************
	/* @TITLE
	/*	Ein Element in einer Collection (oder einem Array) finden
	/*
	/* @DESCRIPTION
	/*	Diese Methode durchsucht eine Collection / ein Array nach einem Element, das
	/*	die angegebene Eigenschaft hat. Dabei wird zuerst versucht, das Element direkt
	/*	durch Verwendung der Eigenschaft als Index zu ermitteln.
	/*
	/* @PARAM collection
	/*	Zu durchsuchende(s) Collection oder Array
	/* @PARAM name
	/*	Wert der Eigenschaft
	/* @PARAM propertyName
	/*	(optional) Name der Eigenschaft (Default: "name")
	/* @PARAM errorMessage
	/*	(optional) Fehlermeldung, falls nichts gefunden wurde (Default: kein Fehler,
	/*	sondern einfach null zurückliefern)
	/********************************************************************************/
	if (!propertyName){
		propertyName = "name";
	}
	if (collection[name] && collection[name][propertyName] == name){
		return collection[name];
	}
	for (var i = 0; i < collection.length; i++){
		if (collection[i][propertyName] == name){
			return collection[i];
		}
	}
	if (errorMessage){
		throw new Error(errorMessage + " (Element mit " + propertyName + " = \"" + name + "\")");
	}
	else{
		return null;
	}
}

function getElement(name, formName, silent){
	/********************************************************************************
	/* @TITLE
	/*	Ein Element sicher ermitteln
	/*
	/* @DESCRIPTION
	/*	Diese Methode liefert entweder das Objekt mit ID name (wenn formName leer ist)
	/*	oder das benannte Objekt dieses Formulars zurück.
	/*	Dies ist insbesondere dann sinnvoll, wenn der IE zum Einsaz kommt, der leider
	/*	nicht zwischen ID und Name unterscheidet.
	/*	Es wird immer nur ein Element zurückgegeben, selbst wenn es mehrere mit der
	/*	gewünschten Eigenschaft gibt.
	/*
	/* @PARAM name
	/*	Der Name des zu suchenden Elements. Kann auch direkt mit dem Element selbst belegt sein,
	/*	das dann sofort zurückgegeben wird.
	/* @PARAM formName
	/*	(optional) Name des Formulars, in dem das Element gesucht werden soll
	/*	oder das Formularobjekt selber.
	/* @PARAM silent
	/*	(optional) Wenn gesetzt, wird keine Fehlermeldung ausgegeben, falls nichts
	/*	gefunden wurde.
	/*
	/* @SYNOPSIS
	/*	var tr = getElement("myTableRow");
	/********************************************************************************/

	/* Objekt übergeben? -> zurückliefern */
	if (typeof name == "object"){
		return name;
	}

	/* kein Formularname angegeben? -> es wird ID verlangt */
	if (!formName) {
		var el = document.getElementById(name);
		if (el && el.id == name){
			return el;
		}

		/* Entweder IE oder das Formularelement wurde nicht mit ID-Tag versehen, sondern hat nur einen Namen... */
		if(el = findNamedElementInCollection(document.getElementsByName(name), name, "id", undefined)){
			return el;
		}
		return findNamedElementInCollection(document.getElementsByName(name), name, "name", silent ? undefined : "Element konnte nicht gefunden werden");
	}

	/* Formularname angegeben? -> es wird Name verlangt */
	var form = getForm(formName, silent);
	if (form){
		return findNamedElementInCollection(form.elements, name, undefined, silent ? undefined : "Element in Formular namens " + formName + " nicht gefunden");
	}
	return null;
}

function getForm(formName, silent){
	/********************************************************************************
	/* @TITLE
	/*	Formular sicher ermitteln
	/*
	/* @DESCRIPTION
	/*	Diese Methode liefert das Formular des übergebenen Namens zurück.
	/*
	/* @PARAM formName
	/*	Name des Formulars, in dem das Element gesucht werden soll oder das
	/*	Formular-Objekt selber.
	/* @PARAM silent
	/*	(optional) Wenn gesetzt, wird keine Fehlermeldung ausgegeben, falls nichts
	/*	gefunden wurde.
	/********************************************************************************/

	return typeof formName == "object" ? formName : findNamedElementInCollection(document.forms, formName, undefined, silent ? undefined : "Formular nicht gefunden");
}

function getElements(name, formName, silent){
	/********************************************************************************
	/* @TITLE
	/*	Element-Array sicher ermitteln
	/*
	/* @DESCRIPTION
	/*	Diese Methode liefert eine Liste aller Objekte des angegebenen Namens
	/*	des Formulars zurück.
	/*	Ist name bereits ein Element oder Array, so wird es als Array eines Elementes
	/*	bzw. so wie es ist zurückgegeben.
	/*
	/* @PARAM name
	/*	Der Name des zu suchenden Elements. Kann auch direkt mit dem Element selbst belegt sein,
	/*	das dann sofort zurückgegeben wird.
	/* @PARAM formName
	/*	Name des Formulars, in dem das Element gesucht werden soll
	/*	oder das Formularobjekt selber.
	/* @PARAM silent
	/*	(optional) Wenn gesetzt, wird keine Fehlermeldung ausgegeben, falls nichts
	/*	gefunden wurde.
	/*
	/* @SYNOPSIS
	/*	var tabs = getElements("tabCard");
	/********************************************************************************/

	/* Objekt oder Array übergeben? -> zurückliefern */
	if (name.nodeName) {
		return [name];
	} else if (typeof name == "object" && name.length) {
		return name;
	}

	/* Formularname angegeben? -> es wird Name verlangt */
	var form = findNamedElementInCollection(document.forms, formName, undefined, silent ? undefined : "Formular nicht gefunden");
	if (form) {
		var elements = form.elements[name];

		if (elements && elements.nodeName)
			elements = [elements];
		if (elements && elements[0].name == name)
			return elements;

		/* Schnellermittlung ist fehlgeschlagen - probieren wir's auf die langsame Art */

		elements = [];

		for (var i = 0; i < form.elements.length; i++)
			if (collection[i].name == name)
				elements.push(collection[i]);

		if (!elements.length && errorMessage)
			throw new Error(errorMessage + " (Element mit " + propertyName + " = \"" + name + "\")");

		return elements;
	} else
		return null;
}


function setText(id, text){
	/********************************************************************************
	/* @TITLE
	/*	Text dynamisch einfügen
	/*
	/* @DESCRIPTION
	/*	Mit Hilfe dieser Methode kann Text als Textknoten an einen Strukturknoten
	/*	angehangen und evtl. vorhandener Text ersetzt werden.
	/*
	/* @PARAM id
	/*	Element oder eindeutiger Bezeichner des Elternknotens
	/* @PARAM text
	/*	Der einzufügende Text.
	/*
	/* @SYNOPSIS
	/*	// Zustand vorher: <div id="myDiv">Hallo Welt!</div>
	/*	setText("myDiv", "Hello World!");
	/*	//Zustand nachher: <div id="myDiv">Hello World!</div>
	/********************************************************************************/
	var node = getElement(id);

	if (node.nodeType == 3) {
		node.nodeValue = text;
	} else if (node.nodeName == "INPUT") {
		node.value = text;
	} else if (node.childNodes.length == 1 && node.childNodes[0].nodeType == 3) {
		node.childNodes[0].nodeValue = text;
	} else {
		while (node.childNodes.length)
			node.removeChild(node.childNodes[node.childNodes.length-1]);
		node.appendChild(document.createTextNode(text));
	}
}

function setHtml(id, text){
	/********************************************************************************
	/* @TITLE
	/*	HTML dynamisch einfügen
	/*
	/* @DESCRIPTION
	/*	Mit Hilfe dieser Methode kann der Inhalt eines Text als HTML festgelegt
	/*	bzw. ersetzt werden
	/*
	/* @PARAM id
	/*	Element oder eindeutiger Bezeichner des Elternknotens
	/* @PARAM text
	/*	Der einzufügende HTML-Code.
	/*
	/* @SYNOPSIS
	/*	// Zustand vorher: <div id="myDiv">Hallo Welt!</div>
	/*	setHtml("myDiv", "<u>Hello World!</u>");
	/*	// Zustand nachher: <div id="myDiv"><u>Hello World!</u></div>
	/********************************************************************************/
	var node = getElement(id);

	if (node.nodeType == 3 || node.nodeName == "INPUT") {
		throw new Error("Cannot set html content for text and input nodes");
	} else {
		node.innerHTML = text;
	}
}

var _focusObject = undefined;


function setData(element, data) {
	/********************************************************************************
	/* @TITLE
	/*	Befüllt ein HTML-Element mit den übergebenen Daten.
	/*
	/* @DESCRIPTION
	/*	Bei einem INPUT Element wird <code>data</code> als 'value' gesetzt. Bei einem
	/*	SELECT Element wird zunächst geprüft, ob eine Option mit diesem Wert existiert.
	/*	Diese wird ggf. als 'selected' gekennzeichnet. Existiert keine Option mit dem
	/*	in <code>data</code> angegebenen Wert, wird eine neue Option erzeugt und als
	/*	'selected' gekennzeichnet. Bei einer TEXTAREA wird <code>data</code> als
	/*	'innerHTML' eingesetzt.
	/*
	/* @PARAM element
	/*	Das zu befüllende Element
	/* @PARAM data
	/*	Daten zunm befüllen des Elements. Das ist normalerweise ein String, kann
	/*	bei einem SELECT aber auch ein Array sein.
	/*
	/* @RETURN
	/*	Das befüllte Element
	/********************************************************************************/
	if (!element || !element.name) { return; }

	switch (element.tagName.toLowerCase()) {
	case 'input':
		element.value = data;
		break;
	case 'select':
		var value, label;
		if (typeof(data)=='object'&&(data instanceof Array)) {
			value = data[0];
			label = data[1];
		} else {
			value = data;
			label = data;
		}
		for (var i = 0; i <	element.options.length; i++) {
			if (element.options[i].value == value) {
				element.options[i].selected = true;
				return element;
			}
		}
		newOpt = document.createElement('option');
		newOpt.value = value;
		newOpt.innerHTML = label;
		newOpt.selected = true;
		element.appendChild(newOpt);
		break;
	case 'textarea':
	default:
		element.innerHTML = data;
		break;
	}

	return element;
}


function focusDelayed(id){
	/********************************************************************************
	/* @TITLE
	/*	Element zeitverzögert fokussieren
	/*
	/* @DESCRIPTION
	/*	In Verbindung mit PopUp-Fenstern und Dialogboxen, die den Fokus auf sich
	/*	ziehen, ist mitunter ein zeitverzögertes Fokussieren von Formularelementen
	/*	notwendig, damit der Fokus nicht direkt wieder verloren geht.
	/*
	/* @PARAM id
	/*	ID, Name oder Referenz auf das Formularelement, das fokussiert werden soll.
	/*
	/* @SYNOPSIS
	/*	focusDelayed("nachname");
	/********************************************************************************/
	var obj;
	if (obj = getElement(id)){
		if (!_focusObject) {
			window.setTimeout("try { _focusObject.focus(); } catch(e) { }; _focusObject=undefined;", 0);
		}
		_focusObject = obj;
	}
}

function _getAbsoluteOffset(node) {
	var offset = node.offsetParent ? _getAbsoluteOffset(node.offsetParent) : {left:0, top:0};
	offset.left += node.offsetLeft;
	offset.top += node.offsetTop;
	return offset;
}

function getAbsoluteOffset(node) {
	/********************************************************************************
	/* @TITLE
	/*	Absolute Position eines Elements ermitteln
	/*
	/* @DESCRIPTION
	/*	Ermittelt die Position eines Elements im Anzeigefenster.
	/*	Zurückgegeben wird ein Hash mit den Attributen left, top, width, height
	/*
	/* @PARAM node
	/*	Das Element oder seine ID
	/********************************************************************************/
	if (typeof node == "string") {
		node = getElement(node);
	}

	var offset = _getAbsoluteOffset(node);
	offset.width = node.offsetWidth;
	offset.height = node.offsetHeight;
	return offset;
}

function setAbsoluteOffset(node, left, top, width, height) {
	/********************************************************************************
	/* @TITLE
	/*	Absolute Position eines Elements festlegen
	/*
	/* @DESCRIPTION
	/*	Legt die Position eines Elements im Anzeigefenster fest. Dazu ist es notwendig,
	/*	dass dieses mit "position:absolute" deklariert wurde.
	/*
	/* @PARAM node
	/*	Das Element oder seine ID
	/*
	/* @PARAM left
	/*	Der Abstand zum linken Rand in Pixeln oder undefined, falls dieser nicht
	/*	verändert werdne soll.
	/*
	/* @PARAM top
	/*	Der Abstand zum oberen Rand in Pixeln oder undefined, falls dieser nicht
	/*	verändert werdne soll.
	/*
	/* @PARAM width
	/*	Die Breite in Pixeln oder undefined, falls diese nicht verändert werden soll.
	/*
	/* @PARAM height
	/*	Die Höhe in Pixeln oder undefined, falls diese nicht verändert werden soll.
	/********************************************************************************/
	if (typeof node == "string") {
		node = getElement(node);
	}
	if (width != undefined) {
		node.style.width = width + "px";
	}
	if (height != undefined) {
		node.style.height = height + "px";
	}
	if (left != undefined || top != undefined) {
		var offset = node.offsetParent ? getAbsoluteOffset(node.offsetParent) : {left:0, top:0, width:0, height:0};
		if (left != undefined) {
			node.style.left = (left - offset.left) + "px";
		}
		if (top  != undefined) {
			node.style.top = (top - offset.top) + "px";
		}
	}
}

function scrollIntoViewIfOutside(node, posNode) {
	/********************************************************************************
	/* @TITLE
	/*	Element in sichtbaren Bereich scrollen, falls es außerhalb liegt
	/*
	/* @PARAM node
	/*	Das Element oder seine ID, das positioniert werden soll
	/*
	/* @PARAM posNode
	/*	Der IE verhält sich manchmal merkwürdig, falls dieser Befehl auf
	/*	Nicht-Block-Elemente angewendet wird. Hiermit kann in einem solchen Fall ein
	/*	anderes Element angegeben werden, das zur Ermittlung der Position verwendet
	/*	werden soll.
	/********************************************************************************/
	if (typeof node == "string") {
		node = getElement(node);
	}
	if (typeof posNode == "string") {
		posNode = getElement(posNode);
	}

	var offset = getAbsoluteOffset(posNode || node);
	var scrollPos = window.pageYOffset || (document.documentElement || document.body).scrollTop || 0;
	var height = window.innerHeight || (document.documentElement || document.body).clientHeight;
	if (scrollPos > offset.top) {
		node.scrollIntoView(true);
	} else if (offset.top + node.offsetHeight > scrollPos + height) {
		node.scrollIntoView(false);
	}
}

function setValue(node, value) {
	/********************************************************************************
	/* @TITLE
	/*	Wert eines Elementes setzen
	/*
	/* @PARAM node
	/*	Das Element (oder seine ID), dessen Wert zu setzen ist
	/*
	/* @PARAM value
	/*	Zu setzender String-Wert
	/********************************************************************************/
	node = getElement(node);
	if (node.value == undefined) {
		throw new Error("Node " + (node ? node.nodeName : node) + " does not have a value");
	} else if (node.nodeName == "SELECT") {
		node.value = value;
		if (node.value != value) {
			throw new Error("Could not set select box to " + value + " (missing option?)");
		}
	} else {
		node.value = value;
	}
}

function getValue(node) {
	/********************************************************************************
	/* @TITLE
	/*	Wert eines Elementes abfragen
	/*
	/* @PARAM node
	/*	Das Element (oder seine ID), dessen Wert abzufragen ist
	/********************************************************************************/
	node = getElement(node);
	if (node.value == undefined) {
		throw new Error("Node " + (node ? node.nodeName : node) + " does not have a value");
	} else {
		return node.value;
	}
}

function displayRow(row, visible) {
	/********************************************************************************
	/* @TITLE
	/*	Sichtbarkeit einer Tabellenzeile festlegen
	/*
	/* @DESCRIPTION
	/*	Diese Methode setzt in Abhängigkeit von dem verwendeten Browser die korrekte
	/*  DisplayStyle-Angabe für das Block-Rendering von <TR>-Tags.
	/*
	/* @SYNOPSIS
	/*	displayRow("myTableRow", true);
	/********************************************************************************/
	row = getElement(row);
	if (row.nodeName != "TR") {
		throw new Error("displayRow works with table rows only");
	}
	row.style.display = visible ? (isIE ? "block" : "table-row") : "none";
}

function blend(srcID, condition, targetID, equals){
	/********************************************************************************
	/* @TITLE
	/*	Sichtbarkeit wechseln
	/*
	/* @DESCRIPTION
	/*	Diese Methode blendet das mit @targetID bezeichnete Element ein, falls die
	/*	mit @srcID bezeichnete SelectBox die Bedingung @condition entweder erfüllt
	/*	(im Fall @equals == true) oder nicht erfüllt (im Fall @equals == false).
	/*	Somit kann die Bedingung in Verbindung mit dem Gleichheitsselektor wahlweise
	/*	als Allquantor oder als Existenzquantor verwendet werden!
	/*
	/* @PARAM srcID
	/*	ID, (eindeutiger) Name oder Objektreferenz der zu prüfenden SelectBox.
	/* @PARAM targetID
	/*	ID, (eindeutiger) Name oder Objektreferenz des Elements, dessen Sichtbarkeit zu wechseln ist.
	/* @PARAM condition
	/*	Die Bedingung, auf die geprüft wird.
	/* @PARAM equals
	/*	Falls 'true' und der Wert von srcElement == condition: targetElement einblenden
	/*	Falls 'true' und der Wert von srcElement != condition: targetElement ausblenden
	/*  Falls 'false': genau umgekehrt...
	/*
	/* @SYNOPSIS
	/*	blend("mySBox", x == y, "myNotherSBox", true);
	/********************************************************************************/
	getElement(targetID).style.display = equals ^ (getValue(srcID) != condition) ? "inline" : "none";
}

function forceFocus(id){
	/********************************************************************************
	/* @TITLE
	/*	Element zeitverzögert fokussieren
	/*
	/* @DESCRIPTION
	/*	In Verbindung mit PopUp-Fenstern und Dialogboxen, die den Fokus auf sich
	/*	ziehen, ist mitunter ein zeitverzögertes Fokussieren von Formularelementen
	/*	notwendig, damit der Fokus nicht direkt wieder verloren geht.
	/*
	/* @PARAM id
	/*	ID, Name oder Referenz auf das Formularelement, das fokussiert werden soll.
	/*
	/* @SYNOPSIS
	/*	forceFocus("nachname");
	/********************************************************************************/
	var fld = getElement(id);
	if(fld){
		window.setTimeout("getElement('"+ id +"').focus()", 250);
	}
}

