/**
2 * LinkedSelection ist ein Klasse zur Steuerung dynamisch verketteter Auswahllisten
3 * @param inputSelects ein Array mit den IDs der Auswahllisten in hierarchischer Reihenfolge
4 * Bsp: [ 'select1', 'select2', 'select3' ]
5 * @param callback Funktion, welche beim Abschließen (und Ändern) der Auswahl aufgerufen werden soll
6 * @param data das Daten-Objekt in JSON
7 * Bsp: { 'select1':['wert1','text1'], 'select2':['wert5','text5'] }
8 **/
function LinkedSelection( inputSelects, callback, data )
{
 var self = this; /* um aus EventHandlern auf diese Instanz zugreifen zu können */
 var selects = new Array(); /* Liste der verketteten Auswahllisten */

 /**
 * Die Funktion changeHandler wird dem onchange-Handler jeder Auswahlliste zugewiesen.
 * Wenn eine gültige Auswahl getroffen wurde, soll entweder die als nächste
 * Auswahlliste (nextSelect) bekannte Auswahlliste mit Daten befüllt werden,
 * oder die Callback-Funktion ausgeführt werden.
 **/
 var changeHandler = function()
 {
 var value = this.selectedValue();

 // Auf die nächste Auswahlliste folgende Auswahllisten müssen wieder
 // in den default-Zustand versetzt werden
 if( typeof(this.nextSelect) == 'object' )
 {
 for( var i = this.nextSelect.selectID + 1; i < selects.length; i++ )
 selects[i].replaceOptions( new Array() );
 }

 // Abbrechen, wenn ein Dummy-Wert ausgewählt wurde
 if( value == '--' )
 {
 if( this.selectID < selects.length )
 selects[ this.selectID +1 ].replaceOptions( new Array() );

 return;
 }

 if( typeof(this.nextSelect) == 'object' )
 {
 /*
 * nextSelect ist eine Auswahlliste
 */

 // Wenn keine Daten zur gemachten Auswahl zur Verfügung stehen,
 // müssen wir sicherstellen, dass wir auf keine nicht vorhandenen Objekte zugreifen.
 if( !data[ this.nextSelect.id ][ value ] )
 {
 if( !data[ this.nextSelect.id ] )
 data[ this.nextSelect.id ] = {};

 data[ this.nextSelect.id ][ value ] = new Array();
 }

 // Neue Optionen in der nächsten Auswahlliste setzen
 this.nextSelect.replaceOptions( data[ this.nextSelect.id ][ value ] );

 // Wenn die Auswahlstrecke nicht beendet ist, muss die Callback-Funktion
 // dennoch aufgerufen werden, damit entsprechend auf Änderungen
 // reagiert werden kann.
 callback( new Array() );
 }
 else
 {
/*
 * Die Auswahlstrecke ist absolviert
 */

 // Wahlen der einzelnen Listen in ein Array schreiben um
 // dieses an die Callback-Funktion zu übergeben.
 var selected = new Array();
 for( var i = 0; i < selects.length; i++ )
 {
 selected.push( { 'id' : selects[i].id,
 'value': selects[i].selectedValue(),
 'text' : selects[i].selectedText() } );
 }
 callback( selected );
 }
 };

 /**
 * replaceOptions ersetzt die aktuellen Optionen der Auswahlliste durch
 * die im Array newOptions gelieferten Daten. Wenn ein leeres Array übergeben
 * wird, wird die default-Option "--" gesetzt.
 * @param newOptions ein Array mit den neuen Optionen
 * Bsp: [ ['value1','text1'], ['value2','text2'], ]
 **/
 var replaceOptions = function( newOptions )
 {
 /*
 * Diese Funktion setzt bewusst DOM-Methoden ein und verzichtet
 * auf die vom Options-Objekt gegebenen Möglichkeiten.
 */

 // alte Optionen der Auswahlliste löschen
 var opts = this.getElementsByTagName( 'option' );
 while( opts.length > 0 )
 this.removeChild( opts[0] );

 // wenn keine neuen Optionen übergeben wurden, default-Option setzen
 // andernfalls "Bitte wählen" voranstellen
 if( newOptions.length == 0)
 this.addOption( '--', '------' );
 else
 this.addOption( '', '- Ihre Auswahl -' );

 // neue Optionen in die Auswahlliste schreiben
 for( var i = 0; i < newOptions.length; i++ )
 this.addOption( newOptions[i][0], newOptions[i][1] );
 };

 /*
 * Fügt der Auswahlliste eine neue Option hinzu
 * @param value Wert der neuen Option
 * @param text Name der neuen Option
 */
 var addOption = function( value, text )
 {
 var opt = document.createElement( 'option' );
 opt.value = value;
 opt.appendChild( document.createTextNode( text ) );
 this.appendChild( opt );
 };

 /**
 * holt den Wert der aktuell gewählten Option
 * @returns den Value der aktuell gewählten Option
 **/
 var selectedValue = function()
 {
 return this.options[ this.selectedIndex ].value;
 };

 /**
 * holt den Text (Name) der aktuell gewählten Option
 * @returns den Text der aktuell gewählten Option
 **/
 var selectedText = function()
 {
 return this.options[ this.selectedIndex ].text;
 };

 /**
 * Selektiere die Option mit dem Wert value, wenn keine Option mit dem Wert
 * value existiert, wird die Auswahl nicht geändert.
 * @param value der Wert den eine Option haben muss, um ausgewählt zu werden.
 **/
 var selectByValue = function( value )
 {
 for( var i = 0; i < this.options.length; i++ )
 {
 if( this.options[i].value == value )
 this.selectedIndex = i;
 }
 }

 /**
 * Initialisiere den Manager für verkettete Auswahllisten.
 * Findet Auswahllisten anhand der (per inputSelects) bekannten IDs.
 * Bestückt die Auswahllisten mit den nötigen Funktionen und Event-Handlern
 **/
 this.init = function()
 {
 // bestücke bestehende selects
 for( var i = 0; i < inputSelects.length; i++ )
 {
 var t = document.getElementById( inputSelects[i] );

 // ignoriere falsche IDs
 if(!t)
 continue;

 // neue Funktionen und Event-Handler zuweisen und in selects registrieren
 t.replaceOptions = replaceOptions;
 t.addOption = addOption;
 t.selectedValue = selectedValue;
 t.selectedText = selectedText;
 t.selectByValue = selectByValue;
 t.selectID = selects.length;
 t.onchange = changeHandler;
 selects.push( t );

 // registriere Auswahlliste als nextSelect bei der vorhergehenden
 if( selects.length > 1 )
 selects[ selects.length-2 ].nextSelect = t;
 }
 };

 // initialisieren!
 this.init();
}
