/***************************************************
* JavaScript-Framework für interaktive Lernaufgaben
****************************************************
*
* V 0.7 (2007/09/21)
*
* Dieses Script wandelt Teile einer Website
* in interaktive Quiz-Aufgaben um. Dazu orientiert
* es sich an CSS-Klassen einzelner HTML-Elemente.
* Dadurch können interaktive Aufgaben auf Websiten
* in einem einfachen WYSIWYG-Editor erstellt
* werden. Die Interaktion geschieht dann mittels
* dieses nachgeladenen Javascripts.
*
* SOFTWARE LICENSE: LGPL
* (C) 2007 Felix Riesterer
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* 
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*
* Felix Riesterer (Felix.Riesterer@gmx.net)
*/

var Quiz = {

	triggerClass : "-quiz",	/* Variable, in der das Suffix der CSS-Klasse steht,
		auf die das Script reagiert, um eine Übung als solche zu erkennen und umzuwandeln.
		Es gibt derzeit folgende Übungen, deren Präfixe wie folgt lauten:
		* Zuordnungs-Quiz
			-> class="zuordnungs-quiz"
		* Lückentext-Quiz
			-> class="lueckentext-quiz"
		* Memory-Quiz
			-> class="memory-quiz"
		* Multiple Choice - Quiz
			-> class="multiplechoice-quiz"
		* Schüttel-Quiz
			-> class="schuettel-quiz"
		*/

	poolClass : "daten-pool", // CSS-Klasse für das Element, in welchem die zu ziehenden Felder liegen

	feldClass : "feld", // CSS-Klasse für Datenfelder

	bewertungsClass : "quiz-bewertung", // CSS-Klasse für den Textabsatz mit den Bewertungsergebnissen

	highlightClass : "anvisiert", // CSS-Klasse für das Ziel-Highlighting

	highlightElm : null, // hier steht später eine Referenz auf das HTML-Element, welches gerade als potenzielles Ziel anvisiert wird

	baseURL : false, // enthält später den Pfad zum Ordner dieses Scripts

	draggableClass : "quiz-beweglich", // CSS-Klasse, die ein Element für Drag&Drop freigibt, damit es beweglich wird.

	draggedClass : "quiz-gezogen", // CSS-Klasse, wenn ein Element gerade bewegt wird.

	dragMode : false, // entscheidet, ob ein Element bei onmousedown gezogen werden soll, oder nicht

	dragElm : null, // hier steht später eine Referenz auf das HTML-Element in dem der mousedown stattfand

	dragElmOldVisibility : "", // hier steht später der originale Wert des gezogenen Elements (wird für's Highlighten verändert)

	mouseLastCoords : {
		// wird später mit den Mauskoordinaten überschrieben werden
		left : 0,
		top : 0
	},

	// Anzahl mouseover-Events, nach denen das Drag-Element unsichtbar geschaltet wird (reduziert das Flimmern beim Draggen)
	visibilityCountDefault : 5,

	// Hier findet später der Countdown statt, um das Drag-Element nicht bei jedem mouseover-Event unsichtbar zu schalten
	visibilityCount : 0,

	// Platzhalter für Eventhandler
	oldWinOnLoad : "leer",
	oldDocOnMouseMove : "leer",
	oldDocOnMouseOver : "leer",

	// Alle Quizze auf einer Seite werden hier beim Initialisieren abgespeichert
	alleQuizze : new Object(),

	// Das gerade benutze Quiz
	aktivesQuiz : null,

	init : function () {
		// baseURL herausfinden
		var i, script, scripts;
		scripts = document.getElementsByTagName("script");

		for (i = 0; i< scripts.length; i++)
			if (scripts[i].src && scripts[i].src.match(/\/quiz.js$/))
				Quiz.baseURL = scripts[i].src.substr(0, scripts[i].src.lastIndexOf("/") + 1);

		// Mehrsprachigkeit einbinden
		script = document.createElement("script");
		script.type = "text/javascript";
		script.src = Quiz.baseURL + "multilingual.js";
		document.getElementsByTagName("head")[0].appendChild(script);

		// Die Initialisierung könnte mehrfach benötigt werden, die folgenden Umleitungen dürfen aber nur einmal gemacht werden!
		if (Quiz.oldDocOnMouseMove == "leer") {
			Quiz.oldDocOnMouseMove = window.document.onmousemove;
			window.document.onmousemove = function (e) {
				if (typeof(Quiz.oldDocOnMouseMove) == "function")
					Quiz.oldDocOnMouseMove(e);
				Quiz.whileDrag(e);
			}
		}

		// OnMouseOver-Handler nur einmal eintragen
		if (Quiz.oldDocOnMouseOver == "leer") {
			Quiz.oldDocOnMouseOver = window.document.onmouseover;
			window.document.onmouseover = function (e) {
				if (typeof(Quiz.oldDocOnMouseOver) == "function")
					Quiz.oldDocOnMouseOver(e);
				Quiz.einBlender(e);
			}
		}

		// OnLoad-Handler nur einmal eintragen
		if (Quiz.oldWinOnLoad == "leer") {
			Quiz.oldWinOnLoad = window.onload;
			window.onload = function () {
				if (typeof(Quiz.oldWinOnLoad) == "function")
					Quiz.oldWinOnLoad();
				Quiz.initQuizze();
			}
		}
	},

/*
=================
 Quiz - Funktionen
=================
 */


	/* Diese Funktion erzeugt ein Zuordnungs-Quiz. Dazu braucht sie eine Tabelle innerhalb eines
	Elternelements mit dem CSS-Klassen-Präfix "matching", z.B. "matching-quiz", wenn "-quiz"
	das Suffix der Quiz.triggerClass ist.
	Die Tabelle mit den Daten enthält genau zwei Spalten (ohne <th>!), in denen die Wertepaare stehen. */

	zuordnungsQuiz : function (div) {
		var i, test, tabelle, gefunden, daten;

		var quiz = {
			// Objekt-Gestalt eines Zuordnungs-Quizzes
			name : "Quiz-x", // Platzhalter - hier steht später etwas anderes.
			typ : "Zuordnungs-Quiz",
			loesungsClass : "loesungs-paar",
			element : div, // Referenz auf das DIV-Element, in welchem sich das Quiz befindet
			daten : { }, // Hier stehen später Wertepaare.
			felder : new Array, // Hier stehen später Referenzen auf SPAN-Elemente
			auswertungsButton : null, // Hier steht später das HTML-Element des Auswertungs-Buttons.
			versuche : 0, // Speichert die Anzahl Versuche, die für die Lösung gebraucht wurden.
			sprache : (div.lang && div.lang != "") ? div.lang : "de", // deutsche Meldungen als Voreinstellung

			// Funktion zum Auswerten der Drag&Drop-Aktionen des Benutzers
			dragNDropAuswerten : function (element, ziel) {
				var vorgaenger, test;
				var pool = Quiz.getElementsByClassName(Quiz.poolClass, this.element)[0];

				// Element einpflanzen
				element.parentNode.removeChild(element);
				ziel.appendChild(element);
				test = Quiz.getElementsByClassName(Quiz.draggableClass, ziel);

				// War bereits ein Element hier eingefügt? -> Zurück in den Pool damit!
				if (test.length > 1) {
					vorgaenger = ziel.removeChild(test[0]);
					pool.appendChild(vorgaenger);
				}

				// Auswertungsbutton entfernen, falls vorhanden
				if (this.auswertungsButton.parentNode)
					this.auswertungsButton.parentNode.removeChild(this.auswertungsButton);

				// letztes Element verwendet -> Auswertungs-Button anbieten
				if (Quiz.getElementsByClassName(Quiz.draggableClass, pool).length < 1)
					pool.appendChild(this.auswertungsButton);
			},

			// Funktion zum Auswerten der Zuordnungen
			auswerten : function () {
				var loesungen = Quiz.getElementsByClassName(this.loesungsClass, this.element);
				var pool = Quiz.getElementsByClassName(Quiz.poolClass, this.element)[0];
				var i, test, element;

				// Anzahl Lösungsversuche um eins erhöhen
				this.versuche++;

				// Zuordnungen einzeln überprüfen
				for (i = 0; i < loesungen.length; i++) {
					test = loesungen[i].getElementsByTagName("span");

					// Stimmen die IDs mit Ausnahme des letzten Zeichens überein?
					if (test[0].id.substr(0, test[0].id.length -1) != test[1].id.substr(0, test[1].id.length -1)) {
						// Nein! Zweites Element zurück in den Pool!
						element = test[1].parentNode.removeChild(test[1]);
						pool.appendChild(element);
					}
				}

				// Auswertungsbutton entfernen, falls vorhanden
				if (this.auswertungsButton.parentNode)
					this.auswertungsButton.parentNode.removeChild(this.auswertungsButton);

				// Sind keine Felder mehr im Pool? -> Quiz erfolgreich gelöst!
				if (pool.getElementsByTagName("span").length < 1) {
					// Eventhandler entfernen
					this.element.onmousedown = null;
					this.element.onmousemove = null;
					this.element.onmouseup = null;
					pool.parentNode.removeChild(pool);

					// Bewegungscursor entfernen
					loesungen = Quiz.getElementsByClassName(Quiz.draggableClass, this.element);
					test = new RegExp(" ?" + Quiz.draggableClass);
					for (i = 0; i < loesungen.length; i++) {
						loesungen[i].className = loesungen[i].className.replace(test, "");
						loesungen[i].style.cursor = "";
					}

					// Erfolgsmeldung ausgeben
					test = document.createElement("p");
					test.className = Quiz.bewertungsClass;
					test.innerHTML = Quiz.meldungen[this.sprache]["lob" + (this.versuche > 2 ? 3 : this.versuche)]
						+ " "
						+ Quiz.meldungen[this.sprache]["ergebnis" + (this.versuche > 2 ? 3 : this.versuche)].replace(/%n/i, this.versuche);
					this.element.appendChild(test);
				}
			},

			// Funktion zum Mischen und Austeilen der Wörter
			init : function () {
				var loesung, pool, feld, i, j, paar, werte, zufall, gemischte;

				// Spracheinstellungen auf deutsch zurück korrigieren, falls entsprechende Sprachdaten fehlen
				if (!Quiz.meldungen[this.sprache])
					this.sprache = "de";

				/* Jeder Wert aus den Wertepaaren der Daten wird zu einem SPAN-Element ("Feld") und erhält eine ID.
				Die ID eines solchen Feldes enthält den Namen des Quizes, die laufende Nummer des Wertepaares, dem er entstammt
				und entweder ein "a" oder ein "b". Dadurch kann später die Zuordnung ausgewertet werden, da die ID bis auf den
				letzten Buchstaben übereinstimmen muss, wenn die Zuordnung stimmen soll. */

				pool = document.createElement("p"); // Behälter für die beweglichen Teile
				pool.className = Quiz.poolClass;
				this.element.appendChild(pool); // ins Dokument einfügen

				i = 0; // Wertepaare durchgehen, Felder erzeugen und im Pool ablegen
				werte = new Array();
				for (paar in this.daten) {
					werte[0] = paar;
					werte[1] = this.daten[paar];

					for (j = 0; j < 2; j++) {
						feld = document.createElement("span");
						feld.id = this.name + "_" + i + ((j == 0) ? "a" : "b");
						feld.className = Quiz.feldClass;
						feld.innerHTML = werte[j];
						feld.style.cursor = "move";
						this.felder[(i * 2) + j] = feld;
					}

					i++;
				}

				// Felder mischen und verteilen!
				werte = new Array(); // Hier werden bereits benutzte Felder markiert
				gemischte = new Array(); // korrespondierende Felder hier eintragen
				for (j = 0; j < i; j++) {
					loesung = document.createElement("p");
					loesung.className = this.loesungsClass;

					// Ein Feld auswählen, das selbst (oder dessen Partner) noch nicht in einem Lösungs-Absatz steht
					paar = true; // Wertepaar schon verwendet?
					while (paar) {
						zufall = Math.floor(Math.random() * i * 2);
						paar = werte[zufall];
					}

					werte[zufall] = true;
					if (zufall / 2 == Math.ceil(zufall / 2)) {
						// Verwendete Zufallszahl eintragen, damit das Wertepaar nicht erneut verwendet wird!
						werte[zufall + 1] = true;
						gemischte[gemischte.length] = this.felder[zufall + 1];
					} else {
						// Verwendete Zufallszahl eintragen, damit das Wertepaar nicht erneut verwendet wird!
						werte[zufall - 1] = true;
						gemischte[gemischte.length] = this.felder[zufall - 1];
					}

					// Feld in einen Lösungsbereich eintragen
					this.felder[zufall].style.cursor = "";
					loesung.appendChild(this.felder[zufall]);
					pool.parentNode.insertBefore(loesung, pool);
				}

				// zuzuordnende Felder vermischt ausgeben
				while (i > 0) {
					paar = "leer"; // Feld zum Eintragen gefunden?
					while (paar == "leer") {
						zufall = Math.floor(Math.random() * gemischte.length);
						paar = gemischte[zufall];
					}

					pool.appendChild(gemischte[zufall]);
					gemischte[zufall] = "leer";
					i--;
				}

				// ID für das umgebende DIV-Element vergeben
				this.element.id = this.name;

				// Eventhandler für bewegliche Felder einrichten
				this.element.onmousedown = Quiz.startDrag;
				this.element.onmouseover = Quiz.highlight;
				this.element.onmouseup = Quiz.stopDrag;

				werte = Quiz.getElementsByClassName(Quiz.feldClass, pool);
				for (i = 0; i < werte.length; i++)
					werte[i].className += " " + Quiz.draggableClass;

				// Auswertungs-Button erzeugen
				test = document.createElement("span");
				test.className = "auswertungs-button";
				test.innerHTML = '<a href="javascript:Quiz.alleQuizze.'
					+ this.name
					+ '.auswerten()">'
					+ Quiz.meldungen[this.sprache].pruefen
					+ '</a>';
				this.auswertungsButton = test;
			}
		};

		// Laufende Nummer ermitteln -> Quiz-Name wird "quiz" + laufende Nummer
		i = 0;
		for (test in Quiz.alleQuizze)
			i++;
		quiz.name = "quiz" + i;

		// Gibt es Quiz-Daten?
		tabelle = div.getElementsByTagName("table");

		if (tabelle.length < 1)
			return false;

		// Daten sind also vorhanden? -> Auswerten
		test = tabelle[0].getElementsByTagName("tr"); // Tabellenzeilen nach Daten durchforsten
		gefunden = new Array();
		for (i = 0; i < test.length; i++) {
			daten = test[i].getElementsByTagName("td");
			if (daten.length > 1) {
				gefunden[0] = (daten[0] && daten[0].innerHTML) ? daten[0].innerHTML : "";
				gefunden[1] = (daten[1] && daten[1].innerHTML) ? daten[1].innerHTML : "";

				if (daten[0] != "" && daten[1] != "")
					quiz.daten[gefunden[0]] = gefunden[1];
			}
		}

		// Keine brauchbare Daten? -> Verwerfen!
		i = 0;
		for (test in quiz.daten)
			i++;

		if (i < 1)
			return false;

		// Quiz in die Liste aufnehmen und initialisieren
		Quiz.alleQuizze[quiz.name] = quiz;
		tabelle[0].parentNode.removeChild(tabelle[0]);
		Quiz.alleQuizze[quiz.name].init();

		return true;
	},


	/* Diese Funktion erzeugt ein Lückentext-Quiz. Dazu braucht sie ein Elternelement mit dem
	CSS-Klassen-Präfix "lueckentext", z.B. "lueckentext-quiz", wenn "-quiz" das Suffix der Quiz.triggerClass ist.
	Die mit <strong>, <em>, <b> oder <i> ausgezeichneten Textstellen werden durch Drag&Drop-Felder ersetzt. Sollten
	Lösungshinweise in Klammern stehen, so werden die Textstellen durch Eingabefelder ersetzt. */

	lueckentextQuiz : function (div) {
		var i, j, test, inhalt, daten, feld, ids = 0;

		var quiz = {
			// Objekt-Gestalt eines Lückentext-Quizzes
			name : "Quiz-x", // Platzhalter - hier steht später etwas anderes.
			typ : "Lückentext-Quiz",
			loesungsClass : "luecke",
			lueckenPlatzhalter : "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;", // Leerzeichen als Platzhalter für Lücken
			element : div, // Referenz auf das DIV-Element, in welchem sich das Quiz befindet
			felder : new Array, // Hier stehen später Referenzen auf SPAN-Elemente
			inputs : new Array, // Hier stehen später Referenzen auf die Text-Eingabefelder und ihre Lösungen
			auswertungsButton : null, // Hier steht später das HTML-Element des Auswertungs-Buttons.
			versuche : 0, // Speichert die Anzahl Versuche, die für die Lösung gebraucht wurden.
			sprache : (div.lang && div.lang != "") ? div.lang : "de", // deutsche Meldungen als Voreinstellung

			// Funktion zum Auswerten der Drag&Drop-Aktionen des Benutzers
			dragNDropAuswerten : function (element, ziel) {
				var vorgaenger, test, ok, i;
				var pool = Quiz.getElementsByClassName(Quiz.poolClass, this.element)[0];

				if (element && ziel) {
					// Element bewegen
					test = new RegExp(this.loesungsClass, "");
					// Zuerst überflüssige Leerzeichen im Ziel-Element entfernen?
					if (ziel.className.match(test) && Quiz.getElementsByClassName(Quiz.draggableClass, ziel).length < 1)
						ziel.innerHTML = ""; // Leerzeichen in einer Lücke zuvor entfernen

					// Bewegliches Element ausschneiden
					vorgaenger = element.parentNode;
					vorgaenger.removeChild(element);

					// Entleertes Element mit Leerzeichen auffüllen?
					if (vorgaenger.className.match(test) && Quiz.getElementsByClassName(Quiz.draggableClass, vorgaenger).length < 1)
						vorgaenger.innerHTML = this.lueckenPlatzhalter; // Leerzeichen in einer Lücke als Platzhalter einfügen

					// Bewegliches Element einpflanzen
					ziel.appendChild(element);
					test = Quiz.getElementsByClassName(Quiz.draggableClass, ziel);

					// War bereits ein Element hier eingefügt? -> Zurück in den Pool damit!
					if (test.length > 1) {
						vorgaenger = ziel.removeChild(test[0]);
						pool.appendChild(vorgaenger);
					}
				}

				// Auswertungsbutton entfernen, falls vorhanden
				if (this.auswertungsButton.parentNode)
					this.auswertungsButton.parentNode.removeChild(this.auswertungsButton);

				// Auswertungs-Button anbieten?
				if (Quiz.getElementsByClassName(Quiz.draggableClass, pool).length < 1) {
					// letztes Element verwendet -> Alle Eingabefelder ausgefüllt?
					ok = true; // Wir gehen jetzt einmal davon aus...
					test = this.element.getElementsByTagName("input");
					for (i = 0; i < test.length; i++) {
						if (test[i].value == "")
							ok = false; // Aha, ein Eingabefeld war leer!
					}

					if (ok)
						pool.appendChild(this.auswertungsButton);
				}
			},

			// Funktion zum Auswerten der Lösungen
			auswerten : function () {
				var loesungen = new Array();
				var pool = Quiz.getElementsByClassName(Quiz.poolClass, this.element)[0];
				var i, j, test, element, ok;

				// Anzahl Lösungsversuche um eins erhöhen
				this.versuche++;

				// Drag&Drop-Felder überprüfen
				loesungen = Quiz.getElementsByClassName(this.loesungsClass, this.element);

				if (loesungen.length > 0) {
					// Es gibt Drag&Drop-Felder zu überprüfen...
					for (i = 0; i < loesungen.length; i++) {
						test = new RegExp("^" + loesungen[i].id.replace(/^([^_]+_\d+)\w+.*$/, "$1"), "")
						element = Quiz.getElementsByClassName(Quiz.draggableClass, loesungen[i])[0];

						if (!element.id.match(test)) {
							// Falsche Zuordnung! Zurück in den Pool damit!
							element.parentNode.removeChild(element);
							pool.appendChild(element);
							loesungen[i].innerHTML = this.lueckenPlatzhalter;
						}
					}
				}

				// Eingabefelder überprüfen
				loesungen = Quiz.getElementsByClassName(this.loesungsClass + "_i", this.element);
				ok = true; // Wir gehen einmal davon aus, dass alles richtig ist...

				for (i = 0; i < loesungen.length; i++) {
					for (j = 0; j < this.inputs.length; j++) {
						element = document.getElementById(loesungen[i].id + "i");
						if (element.id == this.inputs[j].element.getElementsByTagName("input")[0].id) {
							// Inhalt prüfen
							test = this.inputs[j].loesung;
							element.value = element.value.replace(/^ */, "").replace(/ +$/, ""); // Inhalt trimmen
							if (element.value != test) {
								ok = false; // Falsche Eingabe!
								element.value = "";
							}
						}
					}
				}

				// Auswertungsbutton entfernen
				this.auswertungsButton.parentNode.removeChild(this.auswertungsButton);

				// Sind keine Felder mehr im Pool? -> Quiz erfolgreich gelöst!
				if (pool.getElementsByTagName("span").length < 1 && ok) {
					// Eventhandler entfernen
					this.element.onmousedown = null;
					this.element.onmousemove = null;
					this.element.onmouseup = null;
					pool.parentNode.removeChild(pool);

					// Elementen die Beweglichkeit nehmen
					loesungen = Quiz.getElementsByClassName(Quiz.draggableClass, this.element);
					test = new RegExp(" ?" + Quiz.draggableClass, "");
					for (i = 0; i < loesungen.length; i++) {
						loesungen[i].className = loesungen[i].className.replace(test);
						loesungen[i].style.cursor = "";
					}

					// Eingabefelder durch gelöste Felder ersetzen
					loesungen = Quiz.getElementsByClassName(this.loesungsClass + "_i", this.element);
					for (i = 0; i < loesungen.length; i++) {
						element = document.createElement("span");
						element.className = this.loesungsClass;
						element.innerHTML = document.getElementById(loesungen[i].id + "i").value;
						loesungen[i].parentNode.insertBefore(element, loesungen[i]);
						loesungen[i].parentNode.removeChild(loesungen[i]);
					}

					// Erfolgsmeldung ausgeben
					test = document.createElement("p");
					test.className = Quiz.bewertungsClass;
					test.innerHTML = Quiz.meldungen[this.sprache]["lob" + (this.versuche > 2 ? 3 : this.versuche)]
						+ " "
						+ Quiz.meldungen[this.sprache]["ergebnis" + (this.versuche > 2 ? 3 : this.versuche)].replace(/%n/i, this.versuche);
					this.element.appendChild(test);
				}
			},

			// Funktion zum Erstellen der Lücken, Mischen und Austeilen der beweglichen Wörter, bzw Umwandeln der Wörter zu Engabefeldern
			init : function () {
				var input, felder, benutzte, pool, zufall, luecke, test, i;

				// Spracheinstellungen auf deutsch zurück korrigieren, falls entsprechende Sprachdaten fehlen
				if (!Quiz.meldungen[this.sprache])
					this.sprache = "de";

				/* Jeder markierte Textabschnitt (zum Markieren dienen die Elemente <i>, <b>, <em> und <strong>) wird zu entweder
				einem beweglichen SPAN-Element ("Feld"), oder (wenn eine öffnende Klammer für Hilfsangaben entahlten sind)
				einem Input-Feld. Das markierende Element (also das <i>, <b> etc.) wird ersetzt durch ein <span>-Element mit der
				CSS_Klasse, die in quiz.loesungsClass definiert wurde.

				Beispiel1: <p>Eine Henne legt ein <i>Ei</i>.</p>
				wird zu 
				<p>Eine Henne legt ein <span class="luecke" id="......"> nbsp; nbsp; nbsp; </span>.</p>
				->"Ei" wird zu <span id="quiz0_xb" class="beweglich">Ei</span> und landet im Pool.

				Beispiel2: <p>Eine Henne <b>legt (legen)</b> ein Ei.</p>
				wird zu 
				<p>Eine Henne <span class="luecke"><input type="text" id="......" /></span> (legen) ein Ei.</p>

				Die ID eines solchen Feldes enthält den Namen des Quizes, die laufende Nummer des Wertepaares, dem er entstammt
				und entweder ein "a" oder ein "b". Dadurch kann später die Zuordnung ausgewertet werden, da die ID bis auf den
				letzten Buchstaben übereinstimmen muss, wenn die Zuordnung stimmen soll. */

				// Wenn es Drag&Drop-Felder gibt, dann wird ein Pool benötigt
				if (this.felder.length > 0 || this.inputs.length >0) {
					pool = document.createElement("p"); // Behälter für die beweglichen Teile
					pool.className = Quiz.poolClass;
					this.element.appendChild(pool); // ins Dokument einfügen

					// Felder im Pool ablegen und Lücken erzeugen
					benutzte = new Array(); // Hier werden bereits benutzte Felder markiert

					for (i = 0; i < this.felder.length; i++) {
						test = true;
						while (test) {
							zufall = Math.floor(Math.random() * this.felder.length);
							test = benutzte[zufall];
						}

						pool.appendChild(this.felder[zufall].element);
						luecke = document.createElement("span");
						luecke.innerHTML = this.lueckenPlatzhalter;
						luecke.id = this.felder[zufall].element.id + "_" + this.loesungsClass;
						luecke.className = this.loesungsClass;

						// Lücke ins Dokument schreiben
						this.felder[zufall].original.parentNode.insertBefore(luecke, this.felder[zufall].original);
						this.felder[zufall].original.parentNode.removeChild(this.felder[zufall].original);

						// Feld als benutzt markieren
						benutzte[zufall] = true;
					}

					// Eventhandler für bewegliche Felder einrichten
					this.element.onmousedown = Quiz.startDrag;
					this.element.onmouseover = Quiz.highlight;
					this.element.onmouseup = Quiz.stopDrag;

					felder = Quiz.getElementsByClassName(Quiz.feldClass, pool);
					for (i = 0; i < felder.length; i++) {
						felder[i].className += " " + Quiz.draggableClass;
						felder[i].style.cursor = "move";
					}
				}

				// falls Eingabefelder vorhanden -> einbinden
				if (this.inputs.length > 0) {
					for (input in this.inputs) {
						this.inputs[input].original.parentNode.insertBefore(this.inputs[input].element, this.inputs[input].original);
						this.inputs[input].original.parentNode.removeChild(this.inputs[input].original);
					}
				}

				// ID für das umgebende DIV-Element vergeben
				this.element.id = this.name;

				// Auswertungs-Button erzeugen
				test = document.createElement("span");
				test.className = "auswertungs-button";
				test.innerHTML = '<a href="javascript:Quiz.alleQuizze.'
					+ this.name
					+ '.auswerten()">'
					+ Quiz.meldungen[this.sprache].pruefen
					+ '</a>';
				this.auswertungsButton = test;
			}
		};

		// Laufende Nummer ermitteln -> Quiz-Name wird "quiz" + laufende Nummer
		i = 0;
		for (test in Quiz.alleQuizze)
			i++;
		quiz.name = "quiz" + i;

		// Gibt es Quiz-Daten?
		daten = {
			bolds : div.getElementsByTagName("b"),
			italics : div.getElementsByTagName("i"),
			strongs : div.getElementsByTagName("strong"),
			ems : div.getElementsByTagName("em")
		}

		// keine potentiellen Daten gefunden? -> abbrechen!
		if (daten.bolds.length < 1
			&& daten.italics.length < 1
			&& daten.strongs.length < 1
			&& daten.ems.length < 1
		)
			return false;

		// Daten sind also vorhanden? -> Auswerten
		for (i in daten) {
			for (j = 0; j < daten[i].length; j++) {

				if (daten[i][j].innerHTML.match(/\(/)) {
					// Eingabefeld!
					test = document.createElement("span");
					test.className = quiz.loesungsClass + "_i";
					test.id = quiz.name + "_" + ids;

					// Lösungsinhalt "säubern"
					inhalt = daten[i][j].innerHTML.replace(/[\t\r\n]/g, " ");
					inhalt = inhalt.replace(/^([^(]+).*$/, "$1");
					inhalt = inhalt.replace(/(nbsp; | &nbsp;)/, " ");
					inhalt = inhalt.replace(/ +/, " ");
					inhalt = inhalt.replace(/^ +/, "");
					inhalt = inhalt.replace(/ +$/, "");

					test.innerHTML = '<input type="text" id="' + test.id + 'i"'
						+ ' onkeyup="Quiz.alleQuizze.' + quiz.name + '.dragNDropAuswerten()"'
						+ ' /> '
						+ daten[i][j].innerHTML.replace(/^[^(]*(\(.*) *$/, "$1");

					quiz.inputs[quiz.inputs.length] = {
						element : test,
						original : daten[i][j],
						loesung : inhalt
					};

					ids++; // verwendete ID eintragen, damit keine doppelten IDs entstehen

				} else {
					// Drag&Drop-Feld!
					if (daten[i][j].innerHTML != "") {
						// Feld ist nicht leer
						test = document.createElement("span");
						test.className = Quiz.feldClass;
						test.innerHTML += daten[i][j].innerHTML.replace(/^ *([^ ](.*[^ ])?) *$/, "$1");
						test.id = "";

						// Gibt es bereits Felder mit identischem Inhalt? Deren IDs müssen bis auf die Buchstaben am Ende übereinstimmen!
						for (feld in quiz.felder) {
							if (quiz.felder[feld].element.innerHTML == test.innerHTML)
								// ID übernehmen!
								test.id = quiz.felder[feld].element.id;
						}

						if (test.id == "") {
							test.id = quiz.name + "_" + ids + "a";
							ids++;
						} else {
							// übernommene ID eines bereits existierenden Feldes ändern
							test.id = test.id.substr(0, test.id.length - 1) + String.fromCharCode(test.id.charCodeAt(test.id.length - 1));
						}

						quiz.felder[quiz.felder.length] = {
							element : test,
							original : daten[i][j]
						}
					}
				}
			}
		}

		// Keine brauchbare Daten? -> Verwerfen!
		i = 0;
		for (test in quiz.felder)
			i++;
		for (test in quiz.inputs)
			i++;

		if (i < 1)
			return false;

		// Quiz in die Liste aufnehmen und initialisieren
		Quiz.alleQuizze[quiz.name] = quiz;
		Quiz.alleQuizze[quiz.name].init();

		return true;
	},


	/* Diese Funktion erzeugt ein Memory-Quiz. Dazu braucht sie eine Tabelle innerhalb eines Elternelementes
	mit dem CSS-Klassen-Präfix "memory", z.B. "memory-quiz", wenn "-quiz" das Suffix der Quiz.triggerClass ist.
	In der Tabelle stehen die Set-Daten: Die Anzahl an Spalten steht für die Anzahl der Felder pro Set, die Anzahl
	der Zeilen ist die Anzahl der Sets. */

	memoryQuiz : function (div) {
		var i, j, test, daten, feld, tabelle, gefunden;

		var quiz = {
			// Objekt-Gestalt eines Memory-Quizzes
			name : "Quiz-x", // Platzhalter - hier steht später etwas anderes.
			typ : "Memory-Quiz",
			inhaltsClass : "feld-inhalt", // CSS-Klasse für den Inhalt eines Feldes
			aktivClass : "aktiv", // CSS-Klasse für ein aktiviertes Feld
			fertigClass : "entfernt", // CSS-Klasse für ein Feld, das aussortiert wurde
			element : div, // Referenz auf das DIV-Element, in welchem sich das Quiz befindet
			angeklickt : null, // Referenz auf das angeklickte Element innerhalb des DIVs
			felder : new Array, // Hier stehen später Referenzen auf SPAN-Elemente.
			setGroesse : 2, // Anzahl der zu einem Set gehörenden Felder
			versuche : 0, // Speichert die Anzahl Versuche, die für die Lösung gebraucht wurden.
			sprache : (div.lang && div.lang != "") ? div.lang : "de", // deutsche Meldungen als Voreinstellung

			// Funktion zum Aufdecken eines Feldes (kommt über Eventhandler onclick)
			aufdecken : function (e) {
				var test, muster, i;
				if (!e)
					e = window.event;

				if (e.target)
					this.angeklickt = e.target; // W3C DOM

				if (e.srcElement) {
					this.angeklickt = e.srcElement; // IE
				}

				// Nur bei Klick auf ein Feld (oder eines seiner Nachfahren-Elemente) reagieren!
				var muster = new RegExp("(^|\\s)" + Quiz.feldClass + "(\\s|$)");
				var test = this.angeklickt;

				while (!test.className || !test.className.match(muster)) {
					test = test.parentNode;
					if (test == document.body)
						return false;
				}

				Quiz.aktivesQuiz = Quiz.alleQuizze[test.id.replace(/^([^_]+).+$/, "$1")];
				Quiz.aktivesQuiz.angeklickt = test; // das angeklickte Feld abspeichern

				// Feld wurde angeklickt -> aufdecken?
				test = Quiz.getElementsByClassName(Quiz.aktivesQuiz.aktivClass, Quiz.aktivesQuiz.element);
				if (test.length >= Quiz.aktivesQuiz.setGroesse) {
					// Nein, denn es sind schon alle Felder für ein Set aufgedeckt!
					return false;
				} else {
					// Das aktuelle Set ist noch nicht vollständig aufgedeckt...
					muster = new RegExp("(^|\\s)" + Quiz.aktivesQuiz.aktivClass + "(\\s|$)", "");

					if (!Quiz.aktivesQuiz.angeklickt.className.match(muster))
						// OK, Feld wurde noch nicht aufgedeckt. -> aufdecken
						Quiz.aktivesQuiz.angeklickt.className += " " + Quiz.aktivesQuiz.aktivClass;
						if (Quiz.getElementsByClassName(Quiz.aktivesQuiz.aktivClass, Quiz.aktivesQuiz.element).length >= Quiz.aktivesQuiz.setGroesse)
							// Alle Felder für ein Feld wurden aufgedeckt! -> auswerten
							window.setTimeout("Quiz.alleQuizze." + Quiz.aktivesQuiz.name + ".auswerten()", 1500);
				}
			},

			// Funktion zum Auswerten eines aufgedeckten Sets
			auswerten : function () {
				var i, ok, test, muster;

				// Anzahl Lösungsversuche um eins erhöhen
				this.versuche++;

				// aufgedeckte Felder ermitteln
				test = Quiz.getElementsByClassName(this.aktivClass, this.element);

				// IDs der Felder vergleichen
				muster = new RegExp(test[0].id.replace(/^([^_]+_\d+).*$/, "$1"), ""); // ID des ersten Feldes ohne letzten Buchstaben
				ok = true; // Wir gehen von einer Übereinstimmung aus...
				for (i = 0; i < test.length; i++)
					if (!test[i].id.match(muster))
						ok = false;

				// IDs haben übereingestimmt?
				muster = new RegExp(" ?" + this.aktivClass, "");
				for (i = 0; i < test.length; i++) {
					if (ok) {
						// Ja. -> aufgedekte Felder "entfernen"
						test[i].className = this.fertigClass;
					} else {
						// Nein! -> Felder wieder umdrehen!
						test[i].className = test[i].className.replace(muster, "");
					}
				}

				// Alle Felder abgeräumt?
				test = Quiz.getElementsByClassName(Quiz.feldClass, this.element);
				if (test.length < 1) {
					// Ja! Gratulieren und nachfragen
					var nachfrage = Quiz.meldungen[this.sprache]["lob" + (this.versuche > 2 ? 3 : this.versuche)]
						+ " "
						+ Quiz.meldungen[this.sprache].alleGefunden
						+ "\n"
						+ Quiz.meldungen[this.sprache]["ergebnis" + (this.versuche > 2 ? 3 : this.versuche)].replace(/%n/i, this.versuche)
						+ "\n"
						+ Quiz.meldungen[this.sprache].erneut;

					if (confirm(nachfrage)) {
						test = Quiz.getElementsByClassName(Quiz.poolClass, this.element);
						if (test.length > 0)
							test[0].parentNode.removeChild(test[0]);
						this.init();
					}
				}
			},

			// Funktion zum Mischen und Austeilen der Wörter
			init : function () {
				var pool, i, sets, zufall, test;

				// Spracheinstellungen auf deutsch zurück korrigieren, falls entsprechende Sprachdaten fehlen
				if (!Quiz.meldungen[this.sprache])
					this.sprache = "de";

				/* Jeder Wert aus den Set-Daten wird zu einem SPAN-Element ("Feld") und erhält eine ID.
				Die ID eines solchen Feldes enthält den Namen des Quizes, die laufende Nummer des Sets, dem das Feld entstammt
				und einen "laufenden Buchstaben". Dadurch kann später die Zuordnung ausgewertet werden, da die ID bis auf den
				letzten Buchstaben übereinstimmen muss, wenn die Zuordnung stimmen soll. */

				pool = document.createElement("p"); // Behälter für die beweglichen Teile
				pool.className = Quiz.poolClass;
				this.element.appendChild(pool); // ins Dokument einfügen

				// Felder vermischt in den Pool schreiben
				sets = new Array(); // Hier werden bereits benutzte Felder markiert

				for (i = 0; i < this.felder.length; i++) {
					test = true;
					while (test) {
						zufall = Math.floor(Math.random() * this.felder.length);
						test = sets[zufall];
					}

					pool.appendChild(this.felder[zufall].cloneNode(true));
					sets[zufall] = true; // Feld als benutzt markieren
				}

				// Elternelement vorbereiten
				this.element.onclick = Quiz.alleQuizze[this.name].aufdecken; // Eventhandler vergeben
				this.element.id = this.name; // ID vergeben
				this.versuche = 0; // Anzahl Versuche zurücksetzen
			}
		}


		// Laufende Nummer ermitteln -> Quiz-Name wird "quiz" + laufende Nummer
		i = 0;
		for (test in Quiz.alleQuizze)
			i++;
		quiz.name = "quiz" + i;

		// Gibt es Quiz-Daten?
		tabelle = div.getElementsByTagName("table");

		if (tabelle.length < 1)
			// Keine Tabelle für Quiz-Daten gefunden! -> abbrechen
			return false;

		// Daten sind also vorhanden? -> Auswerten
		test = tabelle[0].getElementsByTagName("tr"); // Tabellenzeilen nach Daten durchforsten
		for (i = 0; i < test.length; i++) {
			daten = test[i].getElementsByTagName("td");
			if (daten.length > 1) {
				quiz.setGroesse = daten.length;
				for (j = 0; j < daten.length; j++) {
					feld = document.createElement("span");
					feld.className = Quiz.feldClass;
					feld.innerHTML = '<span class="' + quiz.inhaltsClass + '">' + daten[j].innerHTML + "</span>";
					feld.id = quiz.name + "_" + i + String.fromCharCode(j + 97);

					// Feld abspeichern
					quiz.felder[quiz.felder.length] = feld;
				}
			}
		}

		// Keine brauchbare Daten? -> Verwerfen!
		i = 0;
		for (test in quiz.felder)
			i++;

		if (i < 1)
			return false;

		// Quiz in die Liste aufnehmen und initialisieren
		Quiz.alleQuizze[quiz.name] = quiz;
		tabelle[0].parentNode.removeChild(tabelle[0]);
		Quiz.alleQuizze[quiz.name].init();

		return true;
	},


	/* Diese Funktion erzeugt ein Multiple Choice - Quiz. Dazu braucht sie Textabsätze innerhalb eines Elternelementes
	mit dem CSS-Klassen-Präfix "multiplechoice", z.B. "multiplechoice-quiz", wenn "-quiz" das Suffix der Quiz.triggerClass ist.
	In den Textabsätzen stehen die jeweiligen Quiz-Fragen, die Antworten stehen am Ende der Absätze in runden Klammern. Falsche
	Antworten haben innerhalb der Klammer gleich als erstes Zeichen ein Ausrufezeichen, richtige Antworten nicht.
	Textabsätze ohne Klammernpaar am Ende werden nicht als Quiz-Fragen interpretiert. */

	multiplechoiceQuiz : function (div) {
		var i, test, daten, fragen;

		var quiz = {
			// Objekt-Gestalt eines Multiple Choice - Quizzes
			name : "Quiz-x", // Platzhalter - hier steht später etwas anderes.
			typ : "Multiple Choice - Quiz",
			loesungsClass : "quiz-antworten", // CSS-Klasse für das Elternelement mit den Antworten
			element : div, // Referenz auf das DIV-Element, in welchem sich das Quiz befindet
			fragen : new Array(), // Hier stehen später die Fragen zusammen mit ihren Antworten
			sprache : (div.lang && div.lang != "") ? div.lang : "de", // deutsche Meldungen als Voreinstellung

			// Funktion zum Auswerten eines aufgedeckten Sets
			auswerten : function () {
				var i, j, anzahl, test, liPunkt, antworten, richtigkeit, ok;

				// Antwort-Blöcke ermitteln
				antworten = Quiz.getElementsByClassName(this.loesungsClass, this.element);
				richtigkeit = 0; // Anzahl der gezählten Treffer
				anzahl = 0; // Anzahl der möglichen richtigen Antworten

				for (i = 0; i < antworten.length; i++) {
					// Jeden Antwortblock einzeln durchgehen
					test = antworten[i].getElementsByTagName("input");
					ok = 0; // Anzahl Treffer abzüglich falscher Treffer

					for (j = 0; j < test.length; j++) {
						// <li>-Element ermitteln, um es später einzufärben
						liPunkt = test[j].parentNode;
						while (!liPunkt.tagName || liPunkt.tagName.toLowerCase() != "li")
							liPunkt = liPunkt.parentNode;

						// Checkbox unveränderlich machen
						test[j].disabled = "disabled";

						if (test[j].id.match(/_f$/)) {
							// Aha, eine Falschantwort...
							if (test[j].checked) {
								// ... wurde fälschlicherweise angewählt!
								liPunkt.className = "falsch";
								ok--;
							}

						} else {
							// Aha, eine richtige Antwort...
							liPunkt.className = "richtig";
							anzahl++; // Anzahl der möglichen richtigen Antworten erhöhen

							if (test[j].checked) {
								// ...wurde korrekt angewählt
								ok++;
							}
						}
					}

					if (ok < 0)
						ok = 0;

					richtigkeit += ok; // richtige Treffer merken
				}

				richtigkeit = (anzahl > 0) ?
					richtigkeit = Math.floor(richtigkeit / anzahl * 1000) / 10
					: 0;

				// Auswertung ins Dokument schreiben
				test = document.createElement("p");
				test.className = Quiz.bewertungsClass;
				test.innerHTML = Quiz.meldungen[this.sprache].ergebnisProzent.replace(/%n/i, richtigkeit);
				this.element.appendChild(test);

				// Auswertungs-Button entfernen
				test = Quiz.getElementsByClassName("auswertungs-button", this.element);
				if (test.length > 0)
					this.element.removeChild(test[0]);
			},

			// Funktion zum Anzeigen der Fragen und der vermischten möglichen Antworten
			init : function () {
				var frage, antworten, i, j, zufall, html, ID, benutzt;

				// Spracheinstellungen auf deutsch zurück korrigieren, falls entsprechende Sprachdaten fehlen
				if (!Quiz.meldungen[this.sprache])
					this.sprache = "de";

				/* Jede Antwort wird zu einem Listen-Element innerhalb einer geordneten Liste. Die Liste erhält
				die CSS-Klasse quiz.loesungsClass.
				Die Listenelemente erhalten eine ID, die sich nur am letzten Buchstaben unterscheidet. Die ID einer
				Falschantwort erhält zusätzlich ein "_f". */

				for (i = 0; i < this.fragen.length; i++) {
					// Frage in das Dokument schreiben
					frage = document.createElement("p");
					frage.innerHTML = this.fragen[i].frage;
					this.element.insertBefore(frage, this.fragen[i].original);

					// Antworten zusammenstellen und vermischt ausgeben
					antworten = document.createElement("ol");
					antworten.className = this.loesungsClass;
					html = "";
					benutzt = new Array(); // Hier werden bereits benutzte Fragen markiert.

					for (j = 0; j < this.fragen[i].antworten.length; j++) {
						test = true;
						while (test) {
							zufall = Math.floor(Math.random() * this.fragen[i].antworten.length);
							test = benutzt[zufall];
						}

						ID = this.name + "_" + i + String.fromCharCode(j + 97);
						if (this.fragen[i].antworten[zufall].match(/^\!/))
							ID += "_f"; // Falschantwort markieren

						html += '<li><input type="checkbox" id="' + ID + '">'
						+ '<label for="' + ID + '"> '
						+ this.fragen[i].antworten[zufall].replace(/^\!/, "")
						+ "</label></li>";
						benutzt[zufall] = true;
					}

					html += "</ol>";
					antworten.innerHTML += html;
					this.element.insertBefore(frage, this.fragen[i].original);
					this.element.insertBefore(antworten, this.fragen[i].original);
					this.element.removeChild(this.fragen[i].original);
				}

				// Auswertungsbutton anzeigen
				test = document.createElement("p");
				test.className = "auswertungs-button";
				test.innerHTML = '<a href="javascript:Quiz.alleQuizze.'
					+ this.name
					+ '.auswerten()">'
					+ Quiz.meldungen[this.sprache].pruefen
					+ '</a>';

				this.element.appendChild(test);

				// ID für das umgebende DIV-Element vergeben
				this.element.id = this.name;
			}
		}


		// Laufende Nummer ermitteln -> Quiz-Name wird "quiz" + laufende Nummer
		i = 0;
		for (test in Quiz.alleQuizze)
			i++;
		quiz.name = "quiz" + i;

		// Gibt es Quiz-Daten?
		fragen = div.getElementsByTagName("p");

		if (fragen.length < 1)
			// Keine Textabsätze für Quiz-Daten gefunden! -> abbrechen
			return false;

		// Daten sind also vorhanden? -> Auswerten
		for (i = 0; i < fragen.length; i++) {
			// Textabsatz durchforsten
			daten = {
				frage : "",
				antworten : new Array(),
				original : null // Referenz auf den originalen Textabsatz, um ihn später zu entfernen
			};

			test = fragen[i].innerHTML.replace(/[\t\r\n]/g, " ");

			// Zeilenumbrüche und überflüssige Leerzeichen trimmen
			test = test.replace(/(<br>|<br\/>|<br \/>|&bnsp;| )*$/ig, "");

			while (test.match(/\)$/)) {
				daten.antworten[daten.antworten.length] = test.replace(/^.*\(([^\(\)]*)\)$/, "$1");

				// extrahierte Antwort aus dem String entfernen
				test = test.replace(/^(.*)\([^\(\)]*\)$/, "$1");
				test = test.replace(/(<br>|<br\/>|<br \/>|&bnsp;|[ \t\r\n])*$/i, ""); // wieder trimmen
			}

			// Passende Fragen im aktuellen Textabsatz gefunden?
			if (daten.antworten.length > 0) {
				// Ja! Frage mit dazu ...
				daten.frage = test;
				daten.original = fragen[i]; // Referenz zum ursprünglichen Textabsatz

				// ... und Daten ins Quiz übertragen
				quiz.fragen[quiz.fragen.length] = daten;
			}
		}

		// Keine brauchbare Daten? -> Verwerfen!
		i = 0;
		for (test in quiz.fragen)
			i++;

		if (i < 1)
			return false;

		// Quiz in die Liste aufnehmen und initialisieren
		Quiz.alleQuizze[quiz.name] = quiz;
		Quiz.alleQuizze[quiz.name].init();

		return true;
	},


	/* Diese Funktion erzeugt ein Schüttel-Quiz. Dazu braucht sie ein Elternelement mit dem
	CSS-Klassen-Präfix "schuettel", z.B. "schuettel-quiz", wenn "-quiz" das Suffix der Quiz.triggerClass ist.
	Die mit <strong>, <em>, <b> oder <i> ausgezeichneten Textstellen werden durch Drag&Drop-Felder ersetzt. Sollten
	Lösungshinweise in Klammern stehen, so werden die Textstellen durch Eingabefelder ersetzt. */

	schuettelQuiz : function (div) {
		var i, j, test, inhalt, daten;

		var quiz = {
			// Objekt-Gestalt eines Schüttel-Quizzes
			name : "Quiz-x", // Platzhalter - hier steht später etwas anderes.
			typ : "Schüttel-Quiz",
			loesungsClass : "luecke",
			element : div, // Referenz auf das DIV-Element, in welchem sich das Quiz befindet
			felder : new Array, // Hier stehen später Referenzen auf die Text-Eingabefelder und ihre Lösungen
			auswertungsButton : null, // Hier steht später das HTML-Element des Auswertungs-Buttons.
			versuche : 0, // Speichert die Anzahl Versuche, die für die Lösung gebraucht wurden.
			sprache : (div.lang && div.lang != "") ? div.lang : "de", // deutsche Meldungen als Voreinstellung

			// Funktion zum Auswerten der Lösungen
			auswerten : function (klick) {
				var felder, loesungen, nummer, eingabe, test, i, ok;

				if (klick) {
					// Auswertungs-Button wurde geklickt! Auswerten!
					felder = Quiz.getElementsByClassName(this.loesungsClass, this.element);
					ok = true; // Mal davon ausgehen, dass alles richtig ist...

					// Anzahl Lösungsversuche um eins erhöhen
					this.versuche++;

					for (i = 0; i < felder.length; i++) {
						eingabe = felder[i].getElementsByTagName("input");
						if (eingabe.length > 0) {
							eingabe = eingabe[0];
							nummer = eingabe.id.replace(/^.*_(\d+)$/, "$1");

							if (eingabe.value.toLowerCase() == this.felder[nummer].loesung.toLowerCase()) {
								// Eingabefeld wurde richtig ausgefüllt!
								felder[i].innerHTML = this.felder[nummer].loesung;
							} else {
								// Eingabefeld wurde falsch ausgefüllt! -> leeren
								eingabe.value = "";
								ok = false;
							}
						}
					}

					if (ok) {
						// Quiz wurde korrekt gelöst! -> Erfolgsmeldung ausgeben
						eingabe = document.createElement("p");
						eingabe.className = Quiz.bewertungsClass;
						eingabe.innerHTML = Quiz.meldungen[this.sprache]["lob" + (this.versuche > 2 ? 3 : this.versuche)]
							+ " "
							+ Quiz.meldungen[this.sprache]["ergebnis" + (this.versuche > 2 ? 3 : this.versuche)].replace(/%n/i, this.versuche);
						this.element.appendChild(eingabe);
					}
				}

				// Auswertungsbutton entfernen, falls vorhanden
				if (this.auswertungsButton.parentNode)
					this.auswertungsButton.parentNode.removeChild(this.auswertungsButton);

				// Eingabefelder überprüfen
				loesungen = this.element.getElementsByTagName("input");
				ok = loesungen.length > 0; // Mal davon ausgehen, dass alle ausgefüllt sind... wenn es welche gibt!

				for (i = 0; i < loesungen.length; i++) {
					if (loesungen[i].value == "")
						ok = false; // Feld war leer!
				}

				// Sind alle Eingabefelder ausgefüllt?
				if (ok) {
					// Ja. -> Button ins Dokument schreiben
					this.element.appendChild(this.auswertungsButton);
				}
			},

			// Funktion zum Umwandeln der Wörter zu Engabefeldern
			init : function () {
				var benutzte, zufall, luecke, loesung, test, i, j;

				// Spracheinstellungen auf deutsch zurück korrigieren, falls entsprechende Sprachdaten fehlen
				if (!Quiz.meldungen[this.sprache])
					this.sprache = "de";

				/* Jeder markierte Textabschnitt (zum Markieren dienen die Elemente <i>, <b>, <em> und <strong>) wird durch ein
				SPAN-Element, in welchem sich ein Input-Element mit einer Lösucngsvorgabe in Klammern befindet, ersetzt.
				Es erhält die CSS_Klasse, die in quiz.loesungsClass definiert wurde.

				Beispiel: <p>Eine <i>Henne</i> legt ein.</p>
				wird zu
				<p>Eine <span class="luecke"><input id="......" /> (eeHnn)</span> legt ein Ei.</p>

				Die ID eines solchen Input-Elements korrespondiert mit der laufenden Nummer des Daten-Eintrages in
				"quiz.felder". */

				for (i = 0; i < this.felder.length; i++) {
					// Lücke mit Eingabe-Element vorbereiten
					luecke = document.createElement("span");
					luecke.className = this.loesungsClass;

					// Lösung verschlüsseln
					loesung = "";
					benutzte = new Array();

					for (j = 0; j < this.felder[i].loesung.length; j++) {
						test = true;
						while (test) {
							zufall = Math.floor(Math.random() * this.felder[i].loesung.length);
							test = benutzte[zufall];
						}

						loesung += this.felder[i].loesung.substr(zufall, 1);
						benutzte[zufall] = true; // Zeichen wurde benutzt
					}

					// Lücke fertigstellen und abspeichern
					luecke.innerHTML = '<input id="' + this.name + "_" + i + '"'
						+ ' onkeyup="Quiz.alleQuizze.' + this.name + '.auswerten()"'
						+ ' />'
						+ "&nbsp;(" + loesung.toLowerCase() + ")";
					this.felder[i].element = luecke;
				}

				// Alle im Dokument markierten Wörter in Eingabefelder umwandeln
				for (i = 0; i < this.felder.length; i++) {
					this.felder[i].original.parentNode.insertBefore(this.felder[i].element, this.felder[i].original);
					this.felder[i].original.parentNode.removeChild(this.felder[i].original);
				}

				// ID für das umgebende DIV-Element vergeben
				this.element.id = this.name;

				// Auswertungs-Button erzeugen
				test = document.createElement("p");
				test.className = "auswertungs-button";
				test.innerHTML = '<a href="javascript:Quiz.alleQuizze.'
					+ this.name
					+ '.auswerten(this)">'
					+ Quiz.meldungen[this.sprache].pruefen
					+ '</a>';
				this.auswertungsButton = test;
			}
		};

		// Laufende Nummer ermitteln -> Quiz-Name wird "quiz" + laufende Nummer
		i = 0;
		for (test in Quiz.alleQuizze)
			i++;
		quiz.name = "quiz" + i;

		// Gibt es Quiz-Daten?
		daten = {
			bolds : div.getElementsByTagName("b"),
			italics : div.getElementsByTagName("i"),
			strongs : div.getElementsByTagName("strong"),
			ems : div.getElementsByTagName("em")
		}

		// keine potentiellen Daten gefunden? -> abbrechen!
		if (daten.bolds.length < 1
			&& daten.italics.length < 1
			&& daten.strongs.length < 1
			&& daten.ems.length < 1
		)
			return false;

		// Daten sind also vorhanden? -> Auswerten
		for (i in daten) {
			for (j = 0; j < daten[i].length; j++) {
				// Lösungsinhalt "säubern"
				inhalt = daten[i][j].innerHTML.replace(/[\t\r\n]/g, " ");
				inhalt = inhalt.replace(/^<\/?[^>]+>$/, "");
				inhalt = inhalt.replace(/(nbsp; | &nbsp;)/, " ");
				inhalt = inhalt.replace(/ +/, " ");
				inhalt = inhalt.replace(/^ +/, "");
				inhalt = inhalt.replace(/ +$/, "");

				quiz.felder[quiz.felder.length] = {
					element : null,
					original : daten[i][j],
					loesung : inhalt
				};
			}
		}

		// Keine brauchbare Daten? -> Verwerfen!
		i = 0;
		for (test in quiz.felder)
			i++;

		if (i < 1)
			return false;

		// Quiz in die Liste aufnehmen und initialisieren
		Quiz.alleQuizze[quiz.name] = quiz;
		Quiz.alleQuizze[quiz.name].init();

		return true;
	},


	/* Diese Funktion erzeugt ein Kreuzwort-Quiz. Dazu braucht sie ein Elternelement mit dem
	CSS-Klassen-Präfix "kreuzwort", z.B. "kreuzwort-quiz", wenn "-quiz" das Suffix der Quiz.triggerClass ist.
	Die Daten für das Quiz müssen in einer zweispaltigen Tabelle stehen:
	1. Zelle enthält das Lösungswort,
	2. Zelle enthält eine Lösungshilfe
	*/

	kreuzwortQuiz : function (div) {
		var i, tabelle, test, daten, gefunden;

		var quiz = {
			// Objekt-Gestalt eines Kreuzwort-Quizzes
			name : "Quiz-x", // Platzhalter - hier steht später etwas anderes.
			typ : "Kreuzwort-Quiz",
			loesungsClass : "feld",
			element : div, // Referenz auf das DIV-Element, in welchem sich das Quiz befindet
			eingabe : document.createElement("div"), // Eingabebereich
			tabelle : null, // Referenz auf das HTML-Element, in dem das Kreuzworträtsel angezeigt wird
			daten : new Array, // Hier stehen später Objekte, die die Quiz-Daten enthalten.
			auswertungsButton : null, // Hier steht später das HTML-Element des Auswertungs-Buttons.
			versuche : 0, // Speichert die Anzahl Versuche, die für die Lösung gebraucht wurden.
			sprache : (div.lang && div.lang != "") ? div.lang : "de", // deutsche Meldungen als Voreinstellung

			// Funktion zum Auswerten der Lösungen
			auswerten : function (werte) {
				var i, p, button, zelle, alleZellen, test = true;

				if (werte.wort && werte.quizItem) {
					// Es wurde ein Button geklickt...
					werte.wort = werte.wort.replace(/Ä/, "AE");
					werte.wort = werte.wort.replace(/Ö/, "OE");
					werte.wort = werte.wort.replace(/Ü/, "UE");
					werte.wort = werte.wort.replace(/ß/, "SS");

					// Eintragen
					for (i = 0; i < werte.quizItem.wort.length; i++) {
						zelle = werte.quizItem.richtung == "waagrecht" ?
							this.tabelle.getElementsByTagName("tr")[werte.quizItem.y] :
							this.tabelle.getElementsByTagName("tr")[werte.quizItem.y + i];
						if (zelle)
							zelle = werte.quizItem.richtung == "waagrecht" ?
								zelle.getElementsByTagName("td")[werte.quizItem.x + i] :
								zelle.getElementsByTagName("td")[werte.quizItem.x];
						if (zelle) {
							// Buchstaben in die Zellen eintragen
							zelle.lastChild.nodeValue = werte.wort.substr(i, 1);
						}
					}

					// Auswertungsbutton anzeigen?
					alleZellen = Quiz.getElementsByClassName(this.loesungsClass, this.tabelle);
					for (i = 0; i < alleZellen.length; i++) {
						if (!alleZellen[i].lastChild.nodeValue || alleZellen[i].lastChild.nodeValue == "" || alleZellen[i].lastChild.nodeValue == String.fromCharCode(160))
							test = false;
					}

					if (test)
						// Alles ausgefüllt! -> Auswertungs-Button anzeigen!
						this.eingabe.appendChild(this.auswertungsButton);
				}

				// Auswertungsbutton geklickt?
				if (werte == "auswertungs-button") {
					// Auswerten!
					this.versuche++;
					test = true;
					alleZellen = Quiz.getElementsByClassName(this.loesungsClass, this.tabelle);

					for (i = 0; i < alleZellen.length; i++) {
						if (alleZellen[i].lastChild.nodeValue != alleZellen[i].id.replace(/^.*(\w)$/, "$1")) {
							// Falsche Eingabe! -> Löschen
							alleZellen[i].lastChild.nodeValue = String.fromCharCode(160);
							test = false;
						}
					}

					if (test) {
						// Alles richtig!
						p = document.createElement("p");
						p.className = Quiz.bewertungsClass;
						p.appendChild(document.createTextNode(
							Quiz.meldungen[this.sprache]["lob" + (this.versuche > 2 ? 3 : this.versuche)]
							+ " "
							+ Quiz.meldungen[this.sprache]["ergebnis" + (this.versuche > 2 ? 3 : this.versuche)].replace(/%n/i, this.versuche)
						));
						this.eingabe.parentNode.insertBefore(p, this.eingabe);

						// Auswertungs-Button und alle Eventhandler entfernen
						this.eingabe.parentNode.removeChild(this.eingabe);
						this.element.onmousedown = null;
						this.element.onmouseup = null;

					} else {
						// zurück zum Ausfüllen
						this.eingabe.style.display = "none";
					}
				}
			},

			// Funktion zum Eintagen von Kreuzwort-Wörtern
			eintragen : function (werte) {
				var i, p, text, eingabefeld;

				// Auswertungsbutton entfernen falls vorhanden
				if (this.auswertungsButton.parentNode == this.eingabe)
					this.eingabe.removeChild(this.auswertungsButton);

				while (this.eingabe.getElementsByTagName("form").length > 0)
					this.eingabe.removeChild(this.eingabe.getElementsByTagName("form")[0]);

				for (i = 0; i < werte.length; i++) {
					this.eingabe.appendChild(werte[i].form);

					eval("window.setTimeout(function () {"
						+ "Quiz.alleQuizze." + this.name + ".eingabe.getElementsByTagName('input')[0].focus();"
						+ "}, 300);"
					);
				}

				this.eingabe.style.display = "block";
			},

			// Funktion zum Errichten der Tabelle des Kreuzwort-Quiz und zum Einrichten der Eventhandler
			init : function () {
				var abgelegte = new Array();
				var grid, versuche, test, zufall, i, wort, a, eingepasst, x, y, nummer, p, button, input, frm, buchstaben, b;

				// Spracheinstellungen auf deutsch zurück korrigieren, falls entsprechende Sprachdaten fehlen
				if (!Quiz.meldungen[this.sprache])
					this.sprache = "de";

				// Gitter erzeugen
				test = 0;
				for (i = 0; i < this.daten.length; i++) {
					test += this.daten[i].wort.length; // Anzahl Buchstaben insgesamt
				}

				grid = new Array(test);
				for (i = 0; i < test; i++)
					grid[i] = new Array(test);

				versuche = 0; // Anzahl gescheiterter Versuche, ein Wort einzupassen

				// Wörter in zufälliger Reihenfolge ins Gitter einpassen - wenn es gelingt
				while (abgelegte.length < this.daten.length && versuche < (this.daten.length - abgelegte.length) * (this.daten.length - abgelegte.length) +2) {

					test = true; // neue Zufallszahl erzwingen
					while (test) {
						zufall = Math.floor(Math.random() * this.daten.length);
						test = false; // annehmen, dass die Zufallszahl noch nicht benutzt wurde

						// Wort bereits abgelegt?
						for (i = 0; i < abgelegte.length; i++) {
							if (abgelegte[i].wort == this.daten[zufall].wort)
								test = true;
						}
					}

					// Jetzt haben wir ein Wort zum Einpassen in das Gitter
					wort = this.daten[zufall];

					// geeignete Stelle finden, um es einzupassen
					if (abgelegte.length > 0) {

						// bereits eingesetzte Wörter der Reihe nach durchgehen
						for (a = 0; a < abgelegte.length; a++) {

							// schon eine passende Stelle zum Einpassen ermittelt?
							if (wort.x >= 0 && wort.y >= 0)
								break; // Ja! -> abbrechen!

							// zufälligen Buchstaben des Wortes nehmen und nach Übereinstimmungen mit eingepasstem Wort suchen
							buchstaben = new Array(); // benutzte Buchstaben leeren
							for (b = 0; b < wort.wort.length; b++) {

								// schon eine passende Stelle zum Einpassen ermittelt?
								if (wort.x >= 0 && wort.y >= 0)
									break; // Ja! -> abbrechen!

								test = true; // neue Zufallszahl erzwingen
								while (test) {
									zufall = Math.floor(Math.random() * wort.wort.length);
									test = buchstaben[zufall];
								}

								buchstaben[zufall] = true; // Buchstaben als benutzt markieren

								// Buchstabe in beiden Wörtern vorhanden?
								if (abgelegte[a].wort.indexOf(wort.wort.substr(zufall, 1)) >= 0) {

									// Ja! -> jeden Buchstaben des bereits eingepassten Wortes durchgehen
									for (eingepasst = 0; eingepasst < abgelegte[a].wort.length; eingepasst++) {

										// Wort bereits erfolgreich eingepasst?
										if (wort.x >= 0 && wort.y >= 0)
											break; // Ja! -> abbrechen

										if (abgelegte[a].wort.substr(eingepasst, 1) == wort.wort.substr(zufall, 1)) {
											// übereinstimmender Buchstabe ermittelt! -> Wort testweise ins Gitter einpassen
											test = true; // davon ausgehen, dass das Wort passt...

											// Position des Buchstabens im Gitter ermitteln
											x = abgelegte[a].richtung == "waagrecht" ? abgelegte[a].x + eingepasst : abgelegte[a].x;
											y = abgelegte[a].richtung == "senkrecht" ? abgelegte[a].y + eingepasst : abgelegte[a].y;

											// Richtung des einzupassenden Wortes orthogonal zum bereits eingepassten
											wort.richtung = abgelegte[a].richtung == "waagrecht" ? "senkrecht" : "waagrecht";

											// im Gitter die Startposition des einzupassenden Wortes ermitteln
											if (wort.richtung == "senkrecht")
												y = y - zufall; // "zufall" ist der Abstand des entsprechenden Buchstabens zum Wortbeginn
											else
												x = x - zufall;

											// zu belegende Felder im Gitter prüfen
											for (i = 0; i < wort.wort.length; i++) {
												if (wort.richtung == "waagrecht") {
													if (grid[y][x + i] && grid[y][x + i] != wort.wort.substr(i, 1)) {
														test = false;
													}

													// nebenan frei? (Kosmetik)
													if (!grid[y][x + i] && (grid[y - 1][x + i] || grid[y + 1][x + i]))
														test = false; // leider nein

												} else {
													if (grid[y + i][x] && grid[y + i][x] != wort.wort.substr(i, 1)) {
														test = false;
													}

													// nebenan frei? (Kosmetik)
													if (!grid[y + i][x] && (grid[y + i][x - 1] || grid[y + i][x + 1]))
														test = false; // leider nein
												}
											}

											// Ist vor und nach dem Wort noch Platz? (Kosmetik)
											if (wort.richtung == "waagrecht") {
												if (grid[y][x - 1] || grid[y][x + wort.wort.length])
													test = false;
											} else {
												if (grid[y - 1][x] || grid[y + wort.wort.length][x])
													test = false;
											}

											if (test) {
												// hat gepasst! -> Wort übernehmen lassen!
												wort.x = x;
												wort.y = y;
												break; // weitere Tests abbrechen
											}
										}
									}
								}
							}
						}

						// hat nicht gepasst ?
						if (wort.x < 0 && wort.y < 0) {
							versuche++; // merken -> momentan keine geeignete Stelle möglich

							// gibt es für ein Wort überhaupt keine geeignete Stelle?
							if (versuche > (this.daten.length - abgelegte.length) * (this.daten.length - abgelegte.length)) {
								// ein freies Plätzchen für aktuelles Wort finden! -> oben (0), links(1), unten(2) oder rechts(3)
								zufall = Math.floor(Math.random() * 4);

								if (zufall & 1 == 1) {
									// links / rechts
									y = Math.floor(grid.length / 2) - Math.floor(wort.wort.length / 2);
									x = (zufall & 2) == 2 ? 0 : grid[0].length; // Wert muss unter- bzw. überboten werden, daher ist er zu klein/groß

								} else {
									// oben / unten
									x = Math.floor(grid[0].length / 2);
									y = (zufall & 2) == 2 ? 0 : grid.length;
								}

								wort.richtung = (zufall & 1) == 0 ? "waagrecht" : "senkrecht";

								// Koordinaten der abgelegten Wörter durchgehen, um freie Stelle zu ermitteln
								for (i = 0; i < abgelegte.length; i++){

									if ((zufall & 1) == 1) {
										// links / rechts einschränken
										if ((zufall & 2) == 0 && abgelegte[i].x < x)
											x = abgelegte[i].x;

										if ((zufall & 2) == 2) {
											test = abgelegte[i].x;
											if (abgelegte[i].richtung == "waagrecht")
												test += abgelegte[i].wort.length;

											if (test > x)
												x = test;
										}

									} else {
										// oben / unten einschränken
										if ((zufall & 2) == 0 && abgelegte[i].y < y)
											y = abgelegte[i].y;

										if ((zufall & 2) == 2) {
											test = abgelegte[i].y;
											if (abgelegte[i].richtung == "senkrecht")
												test += abgelegte[i].wort.length;

											if (test > y)
												y = test;
										}
									}
								}

								// geeignete Position zum Einpassen gefunden!
								wort.x = (zufall & 2) == 0 ? x - 2 : x + 2;
								wort.y = (zufall & 2) == 0 ? y - 2 : y + 2;

							}
						}

					} else {
						// es ist das erste Wort -> direkt (senkrecht) einpassen
						wort.x = Math.floor(grid[0].length / 2);
						wort.y = Math.floor(grid.length / 2) - Math.floor(wort.wort.length / 2);
						wort.richtung = "senkrecht";
					}

					// abspeichern wenn Wort erfolgreich eingepasst werden konnte
					if (wort && wort.x >= 0 && wort.y >= 0) {
						abgelegte[abgelegte.length] = wort;
						versuche = 0;

						// Buchstaben in das Gitter eintragen
						for (i = 0; i < wort.wort.length; i++) {
							if (wort.richtung == "waagrecht")
								grid[wort.y][wort.x + i] = wort.wort.substr(i, 1);
							else
								grid[wort.y + i][wort.x] = wort.wort.substr(i, 1);
						}
					}
				}

				// Gitter fertig bestückt! -> Beschneiden
				a = {
					x : {
						min : grid[0].length,
						max : 0
					},

					y : {
						min : grid.length,
						max : 0
					}
				};

				for (y = 0; y < grid.length; y++) {
					for (x = 0; x < grid[0].length; x++) {
						// Zelle befüllt? -> Koordinaten benutzen!
						if (grid[y][x]) {

							// min-Werte bei Bedarf verkleinern!
							a.x.min = (a.x.min > x) ? x : a.x.min;
							a.y.min = (a.y.min > y) ? y : a.y.min;

							// max-Werte  bei Bedarf erhöhen
							a.x.max = (a.x.max < x) ? x : a.x.max;
							a.y.max = (a.y.max < y) ? y : a.y.max;
						}
					}
				}

				// min/max-Maße ermittelt -> Gitterinhalt in beschnittenes Gitter übertragen
				test = new Array(a.y.max - a.y.min + 1);
				for (y = 0; y < (a.y.max - a.y.min + 1); y++) { // zeilenweise
					test[y] = new Array(a.x.max - a.x.min + 1);
					for (x = 0; x < (a.x.max - a.x.min + 1); x++) { // spaltenweise
						if (grid[y + a.y.min][x + a.x.min])
							test[y][x] = grid[y + a.y.min][x + a.x.min]; // Inhalt übertragen
					}
				}

				grid = test; // altes Gitter durch neues ersetzen

				// eingetragene Koordinaten der Wörter korrigieren
				for (i = 0; i < abgelegte.length; i++) {
					abgelegte[i].x = abgelegte[i].x - a.x.min;
					abgelegte[i].y = abgelegte[i].y - a.y.min;
				}

				// eingepasste Wörter in den Daten speichern
				this.daten = abgelegte;

				// Tabelle befüllen
				for (y = 0; y < grid.length; y++) {
					test = this.tabelle.insertRow(this.tabelle.getElementsByTagName("tr").length <= 0 ? 0 : this.tabelle.getElementsByTagName("tr").length);

					for (x = 0; x < grid[0].length; x++) {
						i = document.createElement("td");
						i.id = this.name + "_" + x + "_" + y;

						if (grid[y][x]) {
							i.className = this.loesungsClass;
							i.id += "_" + grid[y][x];
						}

						// Zelleninhalt vorbereiten
						a = document.createTextNode(String.fromCharCode(160));
						i.appendChild(a);

						// Zelle in Zeile einhängen
						test.appendChild(i);
					}
				}

				// Einfügemarken und Eventhandler einrichten, dabei Eingabefelder (<form>-Elemente) für jedes Wort erzeugen und in die Objekte eintragen
				a = 1;
				for (i = 0; i < this.daten.length; i++) {
					x = this.daten[i].x;
					y = this.daten[i].y;
					nummer = a; // Nummer der aktuellen Einfügemarke merken
					test = this.tabelle.getElementsByTagName("tr")[y];

					if (test) {
						test = test.getElementsByTagName("td")[x];

						if (test) {
							// bereits eine Einfügemarke vorhanden?
							test.style.cursor = "pointer";
							eingepasst = test.getElementsByTagName("span");

							if (eingepasst.length < 1) {
								// Nein! -> Einfügemarke erstellen
								eingepasst = document.createElement("span");
								eingepasst.className = "einfuegemarke";
								eingepasst.appendChild(document.createTextNode(a));
								test.insertBefore(eingepasst, test.firstChild);
								a++; // Ziffer der Einfügemarke erhöhen
							} else {
								// Nummer der vorhandenen Einfügemarke merken
								nummer = eingepasst[0].innerHTML;
							}

							// Eventhandler einrichten / erweitern
							if (typeof(test.onclick) == "function") {
								// weiteren Wert in die onclick-Funktion schreiben
								wort = test.onclick.toString();
								wort = wort.replace(/(\][^\]]+)$/, ",Quiz.alleQuizze." + this.name + ".daten[" + i + "]$1");
								eval("test.onclick = " + wort);

							} else {
								eval("test.onclick = function () {"
									+ "Quiz.alleQuizze."
									+ this.name
									+ ".eintragen([Quiz.alleQuizze."
									+ this.name
									+ ".daten["
									+ i
									+ "]]) };"
								);
							}
						}
					}

					// Textabsatz für Eingabefeld erzeugen
					p = document.createElement("p");

					// Eingabefeld
					input = document.createElement("input");
					input.type = "text";
					input.maxLength = this.daten[i].wort.length;
					input.onclick = function () {
						this.focus();
						return false;
					};

					// Button zum Eintragen
					button = document.createElement("input");
					button.type = "submit";
					button.value = Quiz.meldungen[this.sprache].eintragen;

					// Textabsatz befüllen
					p.appendChild(
						document.createTextNode(""
							+ nummer
							+ " "
							+ Quiz.meldungen[this.sprache][this.daten[i].richtung]
							+ ": "
							+ this.daten[i].hilfe
						)
					);
					p.appendChild(document.createElement("br"));
					p.appendChild(input);
					p.appendChild(document.createTextNode(String.fromCharCode(160)));
					p.appendChild(button);

					// In ein Formular einbetten
					frm = document.createElement("form");
					frm.action = "#";
					frm.appendChild(p);
					eval ("frm.onsubmit = function () {"
						+ "var quiz = Quiz.alleQuizze." + this.name + ";"
						+ "quiz.auswerten({"
						+ 	"wort : this.getElementsByTagName('input')[0].value.toUpperCase(),"
						+ 	"quizItem : quiz.daten[" + i + "]"
						+ "});"
						+ "quiz.eingabe.removeChild(this);"
						+ "if (quiz.eingabe.getElementsByTagName('p').length < 1 && quiz.eingabe.getElementsByTagName('input').length < 1)"
						+ 	"quiz.eingabe.style.display = 'none';"
						+ "else {"
						+ "window.setTimeout("
						+ 	"function () {"
						+ 	"if (quiz.eingabe.getElementsByTagName('input').length > 0)"
						+ 		"quiz.eingabe.getElementsByTagName('input')[0].focus();"
						+ 	"}, 300); }"
						+ "return false;"
						+ "};"
					);

					// Eingabefeld abspeichern
					this.daten[i].form = frm;
				}

				// Daten und Gitter abspeichern
				this.daten = abgelegte;
				this.grid = grid;

				// Eingabebereich erstellen
				this.eingabe.className = "eingabe " + Quiz.draggableClass; // Element wurde bereis bei der Objektgestalt erzeugt!
				x = document.createElement("span"); // "schließen"-Schaltfläche
				x.className = "schliessen-button";
				x.style.cursor = "pointer";
				x.onclick = function () {
					this.parentNode.parentNode.style.display = "";
				};
				a = document.createElement("div");
				a.className = "eingabe-header";
				a.appendChild(x);
				this.eingabe.appendChild(a);
				this.tabelle.parentNode.insertBefore(this.eingabe, this.tabelle.nextSibling);

				// Eingabefeld durch Eventhandler für bewegliche Felder zu einem Fensterimitat machen
				this.element.onmousedown = Quiz.startDrag;
				this.element.onmouseup = Quiz.stopDrag;

				// Auswertungsbutton erstellen
				button = document.createElement("a");
				button.href = "javascript:Quiz.alleQuizze." + this.name + ".auswerten('auswertungs-button')";
				button.appendChild(document.createTextNode(Quiz.meldungen[this.sprache].pruefen));

				this.auswertungsButton = document.createElement("p");
				this.auswertungsButton.className = "auswertungs-button";
				this.auswertungsButton.appendChild(button);

				// ID für das umgebende DIV-Element vergeben
				this.element.id = this.name;

				// Für die Druckausgabe eine Liste der Lösungshilfen ausgeben
				test = document.createElement("div");
				test.className = "uebersicht";
				this.element.appendChild(test);

				// Listen ausgeben
				nummer = 0;
				while (nummer < 2) {
					// Liste erzeugen
					x = document.createElement("dl");
					y = document.createElement("dt");
					y.appendChild(document.createTextNode(nummer == 0 ? Quiz.meldungen[this.sprache].senkrecht : Quiz.meldungen[this.sprache].waagrecht));
					x.appendChild(y);

					// passende Lösungshilfen ausfiltern
					p = new RegExp(" *" + y.innerHTML + ":", "i");
					for (i = 0; i < abgelegte.length; i++) {
						if (abgelegte[i].richtung == y.innerHTML.toLowerCase()) {
							wort = abgelegte[i].form.innerHTML.replace(/(&nbsp;|<[^>]+>)/g, "");
							wort = wort.replace(p, ":");
							a = document.createElement("dd");
							a.appendChild(document.createTextNode(wort));
							x.appendChild(a);
						}
					}

					test.appendChild(x);
					nummer++; // nächste Liste
				}
			}
		};

		// Laufende Nummer ermitteln -> Quiz-Name wird "quiz" + laufende Nummer
		i = 0;
		for (test in Quiz.alleQuizze)
			i++;
		quiz.name = "quiz" + i;

		// Gibt es Quiz-Daten?
		tabelle = div.getElementsByTagName("table");

		if (tabelle.length < 1)
			return false;

		// Daten sind also vorhanden? -> Auswerten
		test = tabelle[0].getElementsByTagName("tr"); // Tabellenzeilen nach Daten durchforsten
		gefunden = new Array();
		for (i = 0; i < test.length; i++) {
			daten = test[i].getElementsByTagName("td");
			if (daten.length > 1) {
				gefunden[0] = (daten[0] && daten[0].innerHTML) ? daten[0].innerHTML : "";
				gefunden[1] = (daten[1] && daten[1].innerHTML) ? daten[1].innerHTML : "";

				// Lösungswort in reine Großbuchstaben umwandeln
				gefunden[0] = gefunden[0].replace(/ä/i, "ae");
				gefunden[0] = gefunden[0].replace(/ö/i, "oe");
				gefunden[0] = gefunden[0].replace(/ü/i, "ue");
				gefunden[0] = gefunden[0].replace(/ß/, "ss").toUpperCase();

				if (daten[0] != "" && daten[1] != "")
					quiz.daten[quiz.daten.length] = {
						wort : gefunden[0],
						x : -1,
						y : -1,
						hilfe : gefunden[1],
						form : null
					};
			}
		}

		// Keine brauchbare Daten? -> Verwerfen!
		i = 0;
		for (test in quiz.daten)
			i++;

		if (i < 1)
			return false;

		// originale Tabelle durch leere Tabelle ersetzen
		quiz.tabelle = document.createElement("table");
		quiz.tabelle.className = "gitter";
		tabelle[0].parentNode.insertBefore(quiz.tabelle, tabelle[0].nextSibling);
		tabelle[0].parentNode.removeChild(tabelle[0]);

		// Quiz in die Liste aufnehmen und initialisieren
		Quiz.alleQuizze[quiz.name] = quiz;
		Quiz.alleQuizze[quiz.name].init();

		return true;
	},

	meldungen : {
		// deutsche Meldungen (Voreinstellung)
		de : {
			pruefen : 'prüfen!',
			lob1 : 'Ausgezeichnet!',
			lob2 : 'Gut gemacht!',
			lob3 : 'Das war nicht schlecht!',
			ergebnis1 : 'Die Aufgabe wurde gleich beim ersten Versuch erfolgreich gelöst!',
			ergebnis2 : 'Die Aufgabe wurde nach nur zwei Versuchen erfolgreich gelöst!',
			ergebnis3 : 'Die Aufgabe wurde nach %n Versuchen erfolgreich gelöst!',
			alleGefunden : 'Alle Sets gefunden!', // Memory-Quiz - Es können auch Triplets, Quartette und mehr gefunden werden müssen
			erneut : 'Wie wär\'s mit einer neuen Runde?',
			ergebnisProzent : 'Die Antworten sind zu %n% richtig.', // Multiple-Choice-Quiz
			senkrecht : 'Senkrecht', // Kreuzworträtsel
			waagrecht : 'Waagrecht'
		}
	},


/*
==================
 weitere Funktionen
==================
 */
	getElementsByClassName : function (className, element) {
		element = element ? element : document;

		var muster = new RegExp("(^|\\s)" + className + "(\\s|$)");
		var alles = element.getElementsByTagName("*");
		var gefunden = new Array();
		var i;

		for (i = 0; i < alles.length; i++) {
			if (alles[i] && alles[i].className && alles[i].className != "") {
				if (alles[i].className.match(muster))
					gefunden[gefunden.length] = alles[i];
			}
		}

		return gefunden;
	},

	initQuizze : function () {
		// Initialisierung der Quizze
		var i, j, elemente, gefunden, typ, ok, css;
		var quizBereiche = new Array();
		var muster = new RegExp(Quiz.triggerClass);
		var divs = document.getElementsByTagName("div");

		// Alle DIVs daraufhin überprüfen, ob sie eine CSS-Klasse haben, die auf ein Quiz schließen lässt
		for (i = 0; i < divs.length; i++) {
			if (divs[i] && divs[i].className && divs[i].className.match(muster)) {
				quizBereiche[quizBereiche.length] = divs[i];
			}
		}

		// Alle Quiz-Bereiche gefunden -> Initialisieren
		if (quizBereiche.length > 0) {
			for (i = 0; i < quizBereiche.length; i++) {
				ok = false; // Initialisierung ok?
				typ = quizBereiche[i].className.replace(/([^ ,]+)-quiz/, "$1");

				if (typeof(Quiz[typ + "Quiz"]) == "function")
					ok = Quiz[typ + "Quiz"](quizBereiche[i]);

				// Initialisierung OK? -> Warnungen entfernen
				if (ok) {
					gefunden = Quiz.getElementsByClassName("js-hinweis", quizBereiche[i]);
					if (gefunden.length > 0)
						gefunden[0].parentNode.removeChild(gefunden[0]);
				}
			}
		}

		ok = false; // Wurde überhaupt ein Quiz initialisiert? Mal nicht davon ausgehen...
		for (i in Quiz.alleQuizze)
			ok = true;

		// Wenn mindestens ein Quiz initialisiert wurde, dann Seite "bestücken".
		if (ok) {
			// CSS für Quizbereiche einbinden
			css = document.createElement("link");
			css.rel = "stylesheet";
			css.type = "text/css";
			css.media = "screen, projection";
			css.href = Quiz.baseURL + "css/quiz.css";
			document.getElementsByTagName("head")[0].appendChild(css);

			// Print-CSS für Quizbereiche einbinden
			css = document.createElement("link");
			css.rel = "stylesheet";
			css.type = "text/css";
			css.media = "print";
			css.href = Quiz.baseURL + "css/quiz-print.css";
			document.getElementsByTagName("head")[0].appendChild(css);
		}
	},

	/* Funktionen für Drag&Drop-Mechanismus */
	auswahl : function (element, ziel) {
		if (!Quiz.baseURL)
			Quiz.init();

		if (ziel) {
			// Drag&Drop hat stattgefunden!
			Quiz.aktivesQuiz.dragNDropAuswerten(element, ziel);
		}

		// User-Eingabe war "nur ein Klick"...
		return false;
	},

	startDrag : function (e) {
		if (!e)
			e = window.event;

		if (e.target)
			Quiz.dragElm = e.target; // W3C DOM

		if (e.srcElement) {
			Quiz.dragElm = e.srcElement; // IE
		}

		// Nur bei Klick auf ein entsprechend ausgezeichnetes Element (oder eines seiner Nachfahren-Elemente) Drag&Drop-Verhalten zeigen!
		var muster = new RegExp("(^|\\s)" + Quiz.draggableClass + "(\\s|$)");
		var test = Quiz.dragElm;

		while (!test.className || !test.className.match(muster)) {
			test = test.parentNode;
			if (test == document.body)
				break;
		}

		if (test != document.body && test.className.match(muster)) {
			Quiz.dragElm = test;
			Quiz.dragMode = true;

			// aktives Quiz eintragen
			Quiz.aktivesQuiz = Quiz.alleQuizze[Quiz.dragElm.id.replace(/^([^_]+).+/, "$1")];
		}

		return !Quiz.dragMode;
	},

	whileDrag : function (e) {
		var top, left, dx, dy, offsetX, offsetY, element;

		if (!e)
			e = window.event;

		left = e.clientX,
		top = e.clientY

		Quiz.IE = (window.document.compatMode && window.document.compatMode == "CSS1Compat") ?
			window.document.documentElement : window.document.body || null;

		if (Quiz.IE && typeof (Quiz.IE.scrollLeft) == "number") {
			left += Quiz.IE.scrollLeft;
			top +=  Quiz.IE.scrollTop;
		}

		// Abstand zu den letzten Mauskoordinaten berechnen
		dx = Quiz.mouseLastCoords.left - left;
		dy = Quiz.mouseLastCoords.top - top;

		// Mauskoordinaten speichern
		Quiz.mouseLastCoords.left = left;
		Quiz.mouseLastCoords.top = top;

		// falls gerade kein Element gezogen wird, hier beenden
		if (!Quiz.dragElm || !Quiz.dragMode)
			return true;

		// falls das zu ziehende Element noch nicht "losgelöst" wurde, dieses beweglich machen
		if (!Quiz.dragged) {
			Quiz.dragElmOldVisibility = Quiz.dragElm.style.visibility;

			// Nur Felder neu positionieren
			if (Quiz.dragElm.className.match(Quiz.feldClass) || Quiz.dragElm.style.left == "") {
				Quiz.dragElm.style.top = "0px";
				Quiz.dragElm.style.left = "0px";
			}

			// Anti-Markierungs-Effekt für IE
			Quiz.oldDocOnSelectStart = window.document.onselectstart;
			Quiz.oldDocOnDragStart = window.document.ondragstart;
			window.document.onselectstart = function () { return false;};
			window.document.ondragstart = function () { return false;};

			Quiz.dragElm.className += " " + Quiz.draggedClass;
		}

		if (Quiz.visibilityCount < 1)
			// Durchscheinen, damit ein mouseover-Event des unterhalb liegenden Elementes möglich wird
			Quiz.dragElm.style.visibility = "hidden";

		// zu ziehendes Element bewegen
		left = parseInt(Quiz.dragElm.style.left);
		top = parseInt(Quiz.dragElm.style.top);
		Quiz.dragElm.style.left = left - dx + "px";
		Quiz.dragElm.style.top = top - dy + "px";
		Quiz.dragged = true;

		// Zähler zurücksetzen
		Quiz.visibilityCount = Quiz.visibilityCount < 1 ? Math.ceil(Quiz.visibilityCountDefault) : Quiz.visibilityCount -1;

		return true;
	},

	// Drag&Drop beenden
	stopDrag : function (e) {
		var returnVal;
		var muster;

		if (!Quiz.dragElm || !Quiz.dragElm.className)
			return false;

		// Anti-Markier-Effekt beenden
		if (Quiz.oldDocOnSelectStart || typeof(window.document.onselectstart) == "function")
			window.document.onselectstart = Quiz.oldDocOnSelectStart;

		if (Quiz.oldDocOnDragStart || typeof(window.document.ondragstart) == "function")
			window.document.ondragstart = Quiz.oldDocOnDragStart;

		// bewegtes Element wieder eingliedern
		muster = new RegExp(" ?" + Quiz.draggedClass);
		Quiz.dragElm.className = Quiz.dragElm.className.replace(muster, "");

		// Sichtbarkeit wurde nur verändert, wenn das Element wirklich gezogen wurde...
		if (Quiz.dragged) {
			Quiz.dragElm.style.visibility = Quiz.dragElmOldVisibility;
			Quiz.dragElmOldVisibility = "";
		}

		// Position (nur!) bei Feldern wieder zurückstellen
		muster = new RegExp("(^|\\s)" + Quiz.feldClass + "(\\s|$)");
		if (Quiz.dragElm.className.match(muster) && Quiz.dragged) {
			Quiz.dragElm.style.top = "";
			Quiz.dragElm.style.left = "";
		}

		// Rückgabewert bereitstellen
		returnVal = Quiz.dragged ?
			// für Drag&Drop
			Quiz.auswahl(Quiz.dragElm, Quiz.highlightElm) :
			// für einen simplen Klick (zweiter Parameter false!)
			Quiz.auswahl(Quiz.dragElm, false);

		// Variablen wieder löschen
		Quiz.dragElm = null;
		Quiz.dragged = false;
		Quiz.dragMode = false;

		// gehighlightetes Element wieder abstellen
		if (Quiz.highlightElm) {
			muster = new RegExp(" ?" + Quiz.highlightClass);
			Quiz.highlightElm.className = Quiz.highlightElm.className.replace(muster, "");
			Quiz.highlightElm = null;
		}

		return returnVal;
	},

	highlight : function (e) {
		var old = Quiz.highlightElm;
		var test, original, muster;

		if (!Quiz.dragMode)
			// Kein Drag&Drop-Vorgang!
			return true;

		if (!Quiz.dragElm.style.visibility || Quiz.dragElm.style.visibility != "hidden")
			// Das zu ziehende Element ist gerade nicht auf unsichtbar geschaltet! Kein Highlighting möglich!
			return true;

		if (!e)
			e = window.event;

		if (e.target)
			original = e.target; // W3C DOM

		if (e.srcElement)
			original = e.srcElement; // IE

		// tatsächliches Drag&Drop-Elements ermitteln (und nicht eines seiner Kinder akzeptieren)
		test = original;
		while (test != Quiz.dragElm && test != document.body)
			test = test.parentNode;

		// befinden wir uns innerhalb des richtigen Quizzes?
		test = original;
		while (!test.tagName || (test.tagName.toLowerCase() != "div" && test.tagName.toLowerCase() != "body"))
			test = test.parentNode;

		if (!Quiz.aktivesQuiz || test.tagName.toLowerCase() == "body" || (test.tagName.toLowerCase() == "div" && test.id != Quiz.aktivesQuiz.name))
			// Falsches Quiz! Beenden!
			return true

		// anvisiertes Lösungs-Element highlighten
		muster = new RegExp(
			"(^|\\s)("
			+ Quiz.aktivesQuiz.loesungsClass
			+ "|"
			+ Quiz.poolClass
			+ ")(\\s|$)");

		// wenn aktuelles Element nicht die benötigte CSS-Klasse hat -> Element im DOM-Baum aufwärts suchen gehen...
		test = original;
		while (!test.className || (!test.className.match(muster) && test != Quiz.aktivesQuiz.element))
			test = test.parentNode;

		// passendes Element gefunden?
		if (!test.className.match(muster))
			// Nein! -> beenden
			return true;

		Quiz.highlightElm = test;

		// Highlighten!
		if (old) {
			// altes Highlight entfernen, falls vorhanden
			muster = new RegExp(" ?" + Quiz.highlightClass, "");
			old.className = old.className.replace(muster, "");
		}

		// neues Element highlighten
		Quiz.highlightElm.className += " " + Quiz.highlightClass;

		return true;
	},

	einBlender : function (e) {
		if (Quiz.dragElm)
			Quiz.dragElm.style.visibility = Quiz.dragElmOldVisibility;

		return true;
	}
};

// initialisieren
Quiz.init();
