
  /**
    @file
    <pre>
      ----------------------------------------------------------------
      Status:       [ ] in Bearbeitung
                    [x] Release Candidate 
      ----------------------------------------------------------------
      Beschreibung: 
      ----------------------------------------------------------------
      Datum       Wer       Version   Was
      08.03.2010  garbe               Datei angelegt
      12.12.2011  spet      1.0.0     RC für Produktivstart
      13.01.2012  garbe               Autocomplete wird beim durchtappen 
                                      nicht mehr ausgeführt
      14.02.2012  garbe     1.0.1     RC
      ----------------------------------------------------------------
    </pre> 
  */
	

  /**
		@defgroup pb59_getInputFieldAutoComplete CHtml::getInputFieldAutoComplete
		@ingroup	pb59
			@defgroup pb59_getInputFieldAutoComplete_api API
			@ingroup	pb59_getInputFieldAutoComplete		
			@defgroup pb59_getInputFieldAutoComplete_noapi NO API
			@ingroup	pb59_getInputFieldAutoComplete			
		
		@defgroup pb59_getInputFieldDate CHtml::getInputFieldDate
		@ingroup	pb59
			@defgroup pb59_getInputFieldDate_api API
			@ingroup	pb59_getInputFieldDate
			@defgroup pb59_getInputFieldDate_noapi NO API
			@ingroup	pb59_getInputFieldDate				

		@defgroup pb59_getSelectList CHtml::getSelectList
		@ingroup	pb59
			@defgroup pb59_getSelectList_api API
			@ingroup	pb59_getSelectList
			@defgroup pb59_getSelectList_noapi NO API
			@ingroup	pb59_getSelectList
			
    @defgroup pb59_getTextArea CHtml::getTextArea
    @ingroup  pb59
      @defgroup pb59_getTextArea_api API
      @ingroup  pb59_getTextArea
			
		@defgroup pb59_getMultiselect CHtml::getMultiselect
		@ingroup	pb59
			@defgroup pb59_getMultiselect_api API
			@ingroup	pb59_getMultiselect
			@defgroup pb59_getMultiselect_noapi NO API
			@ingroup	pb59_getMultiselect				

		@defgroup pb59_getSwinglist CHtml::getSwinglist
		@ingroup	pb59
			@defgroup pb59_getSwinglist_api API
			@ingroup	pb59_getSwinglist
			@defgroup pb59_getSwinglist_noapi NO API
			@ingroup	pb59_getSwinglist
			
		@defgroup pb59_getTooltip CHtml::getTooltip
		@ingroup	pb59
			@defgroup pb59_getTooltip_noapi NO API
			@ingroup	pb59_getTooltip

		@defgroup pb59_CRTE CRTE
		@ingroup	pb59			
						
  */


	// Variablen deklarieren
  // Kalender
  var chtmlDatePicker;  // var chtmlCalendar;
  var chtmlDatePickerContainerID;
  var chtmlDateDialog; 
	// Auswahlliste
  var chtmlSelectList = new Object();
	// AutoComplete
  var chtmlAutoCompleteAjaxUrl 		 		= new Object();
  var chtmlAutoCompleteCloseFlag   		= new Object();	// wichtig ob bei onBlur des Textfeldes Auswahlliste geschlossen werden soll oder nicht
  var chtmlAutoCompleteLatestRequest 	= new Object();
  var chtmlAutoCompleteFocusPosition  = new Object(); // Element/Zeile, welche in Auswahlliste gerade markiert ist
  var chtmlAutoCompleteVarName        = new Object(); // Enthält Name der Variable mit der Textfeldinhalt übertragen werden soll
  var chtmlAutoCompleteMouseOver      = new Object(); // Enthält true | false je nachdem ob Mauszeiger innerhalb des AC-Divs oder nicht
  var chtmlAutoCompleteMinChar        = new Object(); // Anzahl der Zeichen, welche eingegeben werden müssen, damit AJAX-request ausgelöst wird
  
 
  //* -------------------- Kalender { --------------------  
  
	/**
  	@brief 		Zeigt Popupfenster zur Datumsauswahl an.    
  	@ingroup  pb59_getInputFieldDate_api 
    @param  	sContainerID    ID des Elements
    @param  	objDate         JS-Datumsobjekt, wenn gesetzt wird Zeitbereich im Popup auf das übergebene Datum zentriert, ansonsten auf das aktuelle Datum.
    @param    sLanguage       de, fr, en, es
    @return	 	TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldDate_showPopup(sContainerID, objDate, sLanguage) {
    
    // prüfen ob Dialog bereits initalisiert wurde
    if (chtmlDateDialog instanceof Object) {}
    else {
      // Dialog initialisieren
      chtmlDateDialog = $(jqId('chtmlDateDialog_' + sContainerID)).dialog({
        modal: true,
        title: ' ',
        height: 290,
        width: 330,
        resizable:false,
        autoOpen: false,
        zIndex: 12000
      });
    }
      
    // prüfen ob Datepicker bereits initialisiert wurde
    if (chtmlDatePicker instanceof Object) {}
    else {
      // Datepicker initialisieren
      chtmlDatePicker = $(jqId('chtmlDatePicker_' + sContainerID)).datepicker({
        onSelect: function(dateText, inst) { chtml_getInputFieldDate_onSelectDate(dateText, inst); },
        changeMonth: true,
        changeYear: true
      });
      
      // Sprache 
      chtml_getInputFieldDate_setLanguage(sLanguage);
    }
    
    // DatePicker: ContainerID setzen
    chtmlDatePickerContainerID = sContainerID;
    
    // DatePicker: Datum setzen
    chtmlDatePicker.datepicker('setDate', objDate);
    
    // DatePicker anzeigen
    chtmlDatePicker.datepicker('show');
    
    // Dialog anzeigen
    chtmlDateDialog.dialog('open');
  }


    
    
    
  /**
    @brief Befüllt die Felder (3/5) anhand der übergebenen Daten.
    @ingroup  pb59_getInputFieldDate_api     
    @param  sContainerID    ID des Elements
    @param  objDate         JS-Datumsobjekt
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldDate_setInputField(sContainerID, objDate) {

    // Datumsfelder ermitteln
    var obj    = $(jqId(sContainerID) + ' input[type=text]');
    
  	// Datum setzen
    obj[0].value = objDate.getDate();
    obj[1].value = (objDate.getMonth()+1);
    obj[2].value = objDate.getFullYear();
  	
  	if (typeof obj[3] != 'undefined') {obj[3].value = objDate.getHours();}
  	if (typeof obj[4] != 'undefined') {obj[4].value = objDate.getMinutes();}
  }

    
    
    
    
  /**
    @brief Liefert das Datumsobjekt zum aktuellen Container.
    @ingroup  pb59_getInputFieldDate_api     
    @param  sContainerID    ID des Elements
    @return objDate JS-Datumsobjekt oder null wenn Eingabe leer ist
  */
  function chtml_getInputFieldDate_getDate(sContainerID) {
  	
  	// Datumsfelder ermitteln
    var arInput    = $(jqId(sContainerID) + ' input[type=text]');
  	
  	// Inhalte ermitteln
  	var day   = arInput[0].value;
  	var month = arInput[1].value;
  	var year  = arInput[2].value;
  	var hour  = ((typeof arInput[3] == 'undefined') ? 0 : arInput[3].value);
  	var minute= ((typeof arInput[4] == 'undefined') ? 0 : arInput[4].value);
  	 
  	// validieren
  	var bInvalid = false;
  	if (day < 1 || day > 31 || isNaN(day))           {bInvalid = true;}
  	if (month < 1 || month > 12 || isNaN(month))     {bInvalid = true;}
  	if (year < 1 || year > 9999 || isNaN(year))      {bInvalid = true;}
  	if (hour < 0 || hour > 23 || isNaN(hour))        {hour = 0;}
  	if (minute < 0 || minute > 23 || isNaN(minute))  {minute = 0;}
  	// checkdate für JS
    if (!(month > 0 && month < 13 && year > 0 && year < 32768 && day > 0 && day <= (new Date(year, month, 0)).getDate())) {bInvalid = true;}  
  	if (bInvalid) {return;} 
  	
  	// Datumsobjekt erzeugen
  	var dRet = new Date();
  	dRet.setDate(day);
  	dRet.setMonth((Number(month)-1));
  	dRet.setFullYear(year);
  	dRet.setHours(hour);
  	dRet.setMinutes(minute);
  	dRet.setSeconds(0);
  	dRet.setMilliseconds(0);

		return dRet;
  }    
    
        
    

    
  /**
    @brief Ausführung der Aktionen, wenn ein Datum gewählt wurde | Dialog und Datepicker schließen
    @ingroup  pb59_getInputFieldDate_noapi     
  */    
  function chtml_getInputFieldDate_onSelectDate(dateText, inst) {
    
    // ContainerID
    sContainerID = chtmlDatePickerContainerID;
    
    // Datum
    date = chtmlDatePicker.datepicker('getDate');

    // Dialog ausblenden
    chtmlDateDialog.dialog('close');    
    
    // DatePicker ausblenden
    chtmlDatePicker.datepicker('hide');
    
    // Datumsfeld mit ausgewähltem Datum befüllen
    chtml_getInputFieldDate_setInputField(sContainerID, date);    
     
    // CF-function aufrufen
    chtml_getInputFieldDate_executeCF(sContainerID, date);
  }
  
  
  
  
  
  /**
    @brief Liefert ID zur Abfrage von Einstellungen (wichtig bei Kopiervorgängen)
    @ingroup pb59_getInputFieldDate_noapi
    @param  sContainerID    ID des Elements
    @return ID mit welcher Einstellungen abgefragt werden können | liefert null im Fehlerfall
  */    
  function chtml_getInputFieldDate_getSettingsId(sContainerID) {
    // Node-Objekt des Date-Elementes ermitteln
    var objInput    = $(jqId(sContainerID) + ' input.chtml_getinputfielddate_setting_id').get(0);
    if (typeof objInput != 'undefined') { return objInput.value; }
    return null;
  }

  
  
  
  
  /**
    @brief Setzt die Spracheinstellung zum Datepicker
    @ingroup  pb59_getInputFieldDate_noapi
    @param    sLanguage    de, en, fr, es 
  */
  function chtml_getInputFieldDate_setLanguage(sLanguage) {
    
    if (sLanguage == 'de') {
      // Deutsch
      $.datepicker.regional['de'] = {
        closeText: 'schließen',
        prevText: '&#x3c;zurück',
        nextText: 'Vor&#x3e;',
        currentText: 'heute',
        monthNames: ['Januar','Februar','März','April','Mai','Juni',
        'Juli','August','September','Oktober','November','Dezember'],
        monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun',
        'Jul','Aug','Sep','Okt','Nov','Dez'],
        dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
        dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
        dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
        weekHeader: 'Wo',
        dateFormat: 'dd.mm.yy',
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: ''};
      $.datepicker.setDefaults($.datepicker.regional['de']);
    } else if (sLanguage == 'en') {
      // english
      $.datepicker.regional['en-GB'] = {
        closeText: 'Done',
        prevText: 'Prev',
        nextText: 'Next',
        currentText: 'Today',
        monthNames: ['January','February','March','April','May','June',
        'July','August','September','October','November','December'],
        monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
        dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'],
        weekHeader: 'Wk',
        dateFormat: 'dd/mm/yy',
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: ''};
      $.datepicker.setDefaults($.datepicker.regional['en-GB']);
    } else if (sLanguage == 'fr') {
      // francais
      $.datepicker.regional['fr'] = {
        closeText: 'Fermer',
        prevText: '&#x3c;Préc',
        nextText: 'Suiv&#x3e;',
        currentText: 'Courant',
        monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin',
        'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
        monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun',
        'Jul','Aoû','Sep','Oct','Nov','Déc'],
        dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
        dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
        dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'],
        weekHeader: 'Sm',
        dateFormat: 'dd/mm/yy',
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: ''};
      $.datepicker.setDefaults($.datepicker.regional['fr']);
    } else if (sLanguage == 'es') {
      // espanol
      $.datepicker.regional['es'] = {
        closeText: 'Cerrar',
        prevText: '&#x3c;Ant',
        nextText: 'Sig&#x3e;',
        currentText: 'Hoy',
        monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio',
        'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
        monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun',
        'Jul','Ago','Sep','Oct','Nov','Dic'],
        dayNames: ['Domingo','Lunes','Martes','Mi&eacute;rcoles','Jueves','Viernes','S&aacute;bado'],
        dayNamesShort: ['Dom','Lun','Mar','Mi&eacute;','Juv','Vie','S&aacute;b'],
        dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','S&aacute;'],
        weekHeader: 'Sm',
        dateFormat: 'dd/mm/yy',
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: ''};
      $.datepicker.setDefaults($.datepicker.regional['es']);
    }
  }
  
  
  
  
  /**
    @brief    Funktion, die onChangeEvents eines Autocomplete-Elements behandelt
    @ingroup  pb59_getInputFieldDate_noapi
    @param    sContainerID    ID des Elements
    @param    objDate         Javascript Dateobject
    @return   Rückgabe der aufgerufenen Callbackfunktion (idR true oder false) | true, wenn keine Callbackfunktion zum Ereignis definiert wurde (damit aufrufende Funktion keinen Fehler erhält)
    chtml_getInputFieldDate_executeCF(sContainerID, objDate) {
      // Dynamische Generierung zur Laufzeit durch CHTML::getInputFieldDate()                                
    }
  */  
  // -------------------- } Kalender -------------------- */  

  
  
  
  
  //* -------------------- Textarea { --------------------
  /**
    @brief Fügt die übergebenen Tags in eine Textarea ein
    @ingroup  pb59_getTextArea_api     
    @param    sContainerID    ID der Textarea 
    @param    strTagBefore    Tag vor dem Cursor
    @param    strTagBehind    Tag nach dem Cursor
    @return FALSE im Fehlerfall, sonst TRUE
  */
  function chtml_getTextArea_insertTags(sContainerID, strTagBefore, strTagBehind) {
    
    var objTextarea = $(jqId(sContainerID)).get(0);   if (objTextarea === null) { return false; } 
    
    // sContainerID
    objTextarea.focus();
    var st = objTextarea.scrollTop;

    // IE
    if(typeof document.selection != 'undefined') {
      var range = document.selection.createRange();
      var insText = range.text;
      range.text = strTagBefore + insText + strTagBehind;
      range = document.selection.createRange();
      if (insText.length == 0) {
        range.move('character', -strTagBehind.length);
      } else {
        range.moveStart('character', strTagBefore.length + insText.length + strTagBehind.length);
      }
      range.select();

    // FF
    } else if(typeof objTextarea.selectionStart != 'undefined') {
      var start = objTextarea.selectionStart;
      var end = objTextarea.selectionEnd;
      var insText = objTextarea.value.substring(start, end);
      objTextarea.value = objTextarea.value.substr(0, start) + strTagBefore + insText + strTagBehind + objTextarea.value.substr(end);
      var pos;
      if (insText.length == 0) {
        pos = start + strTagBefore.length;
      } else {
        pos = start + strTagBefore.length + insText.length + strTagBehind.length;
      }
      objTextarea.selectionStart = pos;
      objTextarea.selectionEnd = pos;
    }

    objTextarea.scrollTop = st;
    
    return true;
  }
  // -------------------- } Textarea -------------------- */



  
    
  //* -------------------- Tooltip { --------------------    
    
  /** 
   @brief Generiert Tooltip und verknüpft HTML-Element mit Tooltip
   @ingroup pb59_getTooltip_noapi     
   @param  sContainerID    ID des Elements
   @param  sMsg            Anzuzeigender Text
   @param  iDelay          Angabe der Verzögerung bis Popup aufgeht in MS
   */
  function chtml_getTooltip_setTooltip(sContainerID, sMsg, iDelay, bTrack) {
    //    @param  sPosition       Position in der Form "<Wert1>", "<Wert2>" | Gültig für Wert1: before, after | Gültige für Wert2: above, below  
    
    $(jqId(sContainerID)).tooltip({ 
      track: bTrack,
      delay: iDelay, 
      showURL: false, 
      bodyHandler: function() { 
        return sMsg; 
      }, 
    });
  }

  // -------------------- } Tooltip -------------------- */
  
  
  

    
  //* -------------------- Auswahlliste { --------------------
    
  /**
    @brief Möglichkeit zum setzen der per Ajaxrequest aufgerufenen URL.
    @ingroup pb59_getSelectList_api    
    @param  sContainerID    ID des Elements
    @param  sUrlAjax        setzen der URL für den Ajaxrequest
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getSelectList_setUrlAjax(sContainerID, sUrlAjax) {
    // Auswahlliste mit AjaxUrl verknüpfen
    chtmlSelectList[sContainerID] = sUrlAjax;
	
	  return true;
  }





  /**
    @brief Liefert URL für Ajaxrequest.
    @ingroup pb59_getSelectList_noapi
    @param  sContainerID    ID des Elements
    @return Liefert hinterlegte URL des Ajaxaufrufs
  */    
  function chtml_getSelectList_getUrlAjax(sContainerID) {
  	return chtmlSelectList[sContainerID];
  }    
    
  
  
  
  
  /**
    @brief Führt Ajaxrequest aus und setzt Einträge in der Auswahlliste.
  	@ingroup pb59_getSelectList_api
    @param  sContainerID    ID des Elements
    @return TRUE, wenn erfolgreich ausgeführt | FALSE, bei Fehler
  */    
  function chtml_getSelectList_executeAjaxRequest(sContainerID) {

  	// URL für Ajax aufruf
  	var sUrlAjax = chtmlSelectList[sContainerID];

  	// Aufruf konfigurieren und ausführen 
    $.ajax({
      url: sUrlAjax,
      dataType: 'json',
      cache: false,
      success: function (data, textStatus, XMLHttpRequest) {
    	  // Auswahliste mit neuen Werten befüllen
        chtml_getSelectList_setListEntries(sContainerID, data, false);
      }
  	});
  }
    
    

    
    
    
  /**
    @brief Setzt Inhalt der Auswahlliste auf Basis der übergebenen Daten.
    @ingroup pb59_getSelectList_api
    @param  sContainerID    ID des Elements
    @param  arData          Anzuzeigenden Daten mit der Array-Struktur: array[<ID>] = <Wert>
    @param  bSetLastValue   TRUE, Setzt den bisher aktiven Wert, wieder auf aktiv, sofern er noch in der Auswahl enthalten ist | FALSE, erste Wert wird selektiert.
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */    
  function chtml_getSelectList_setListEntries(sContainerID, arData, bSetLastValue) {
  	
  	// TODO Probleme beim entfernen aller Einträge, wenn optgroups in der Liste sind
  	
    // Auswahlliste
    var objSel = $(jqId(sContainerID)).get(0);
    var iLastValueIndex = 0;
    
    
    // letzten Eintrag merken
    if (bSetLastValue) {
      var iLastValue = objSel.options[objSel.options.selectedIndex].value;
    }
    
    
		
	
		// Auswahlliste leeren
    for (var i=(objSel.length-1); i >= 0; i--) {
      // Eintrag entfernen
      objSel.removeChild(objSel[i]);
    }
      
    // Einträge hinzufügen
    var iEntries = 0;
		for (var key in arData) {
      // TODO: eventuell auf jquery-Funktionalität umstellen?
      
		  // Auswahllistenelement erzeugen
		  var newElement = document.createElement("option");
      newElement.text  = arData[key];
      newElement.value = key;
      
      // Element der Auswahlliste hinzufügen
      var iCnt = null;
      if (document.all) iCnt = objSel.length;
      objSel.add(newElement, iCnt);

      
      // Index des einzustellenden Eintrags merken
      if (bSetLastValue) {
        if (key == iLastValue) {iLastValueIndex = iEntries;}
      }
      
      iEntries++;
		}
		
		// letzten Eintrag wiederherstellen
    if (bSetLastValue) {
      objSel.options.selectedIndex = iLastValueIndex;
    } 
  }        
  
  // -------------------- } Auswahlliste -------------------- */
  

    
    
  //* -------------------- AutoComplete { --------------------
  
  /**
    @brief Möglichkeit zum setzen der per Ajaxrequest aufgerufenen URL.    
		@ingroup pb59_getInputFieldAutoComplete_api
    @param  sContainerID    ID des Elements
    @param  sUrlAjax        setzen der URL für den Ajaxrequest; <b>Hinweis:</b> Wird noch um den in der PHP-Funktion definierten Parameter und dem eingegebenen Wert aus dem Textfeld erweitert.
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldAutoComplete_setUrlAjax(sContainerID, sUrlAjax) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
    // AutoComplete mit AjaxUrl verknüpfen
  	chtmlAutoCompleteAjaxUrl[sContainerIDSetting] = sUrlAjax;
	
	  return true;
  }


  


	/**
	  @brief Liefert URL für Ajaxrequest.
		@ingroup pb59_getInputFieldAutoComplete_noapi
	  @param  sContainerID    ID des Elements
	  @return Liefert hinterlegte URL des Ajaxaufrufs mit Wert aus dem Textfeld | null wenn Parameter nicht ermittelt werden konnte
	*/    
	function chtml_getInputFieldAutoComplete_getUrlAjax(sContainerID) {
	  
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
		
		// Wert des Inputfeldes ermitteln
//		var objInput 	= $(jqId('input_' + sContainerID)).get(0);  	if (objInput === null) { return false; }
// return chtmlAutoCompleteAjaxUrl[sContainerIDSetting] + escape(objInput.value);
		
		// Wert an URL anhängen
		return chtmlAutoCompleteAjaxUrl[sContainerIDSetting];
	}

	
	
  

  /**
	  @brief Möglichkeit zum setzen des CloseFlag, wenn False wird bei onBlur des Textfeldes die Auswahlliste nicht ausgeblendet.
	  @ingroup pb59_getInputFieldAutoComplete_noapi    
	  @param  sContainerID    ID des Elements
	  @param  bCloseFlag      TRUE, wenn Auswahlliste beim Verlassen des Textfeldes ausgeblendet werden soll | FALSE, wenn nicht
	  @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
	*/
	function chtml_getInputFieldAutoComplete_setCloseFlag(sContainerID, bCloseFlag) {
	  
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
		chtmlAutoCompleteCloseFlag[sContainerIDSetting] = bCloseFlag;
	  return true;
	}	
	
	
	
	
	
	/**
	  @brief Liefert Wert zum "CloseFlag".
	  @ingroup pb59_getInputFieldAutoComplete_noapi
	  @param  sContainerID    ID des Elements
	  @return Liefert Wert
	*/    
	function chtml_getInputFieldAutoComplete_getCloseFlag(sContainerID) {
	  
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
		return chtmlAutoCompleteCloseFlag[sContainerIDSetting];
	}		

	
	
	
	
  /**
    @brief Funktion zum setzen der aktuellen Position des zu markierten Elements in der DIV-Auswahlliste
    @ingroup pb59_getInputFieldAutoComplete_noapi    
    @param  sContainerID    ID des Elements
    @param  iFocusPosition  Zahl, welche als aktueller Wert gespeichert werden soll
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldAutoComplete_setFocusPosition(sContainerID, iFocusPosition) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
    chtmlAutoCompleteFocusPosition[sContainerIDSetting] = iFocusPosition;
    return true;
  } 




  /**
    @brief Liefert Wert zur aktuellen Position in der DIV-Auswahlliste.
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @return Liefert entsprechende Zahl
  */    
  function chtml_getInputFieldAutoComplete_getFocusPosition(sContainerID) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);    
    
    return chtmlAutoCompleteFocusPosition[sContainerIDSetting];
  }	
  
  
  
  
  
  /**
    @brief Funktion zum setzen des Variablennamens zum übertragen des Feldinhalts
    @ingroup pb59_getInputFieldAutoComplete_noapi    
    @param  sContainerID    ID des Elements
    @param  sVarName        Variablenname
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldAutoComplete_setVarName(sContainerID, sVarName) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
    chtmlAutoCompleteVarName[sContainerIDSetting] = sVarName;
    return true;
  } 
  
  
  
  
  /**
    @brief Lefert Variablennamen zum übertragen des Feldinhalts
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @return Variablennamen
  */    
  function chtml_getInputFieldAutoComplete_getVarName(sContainerID) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);    
    
    return chtmlAutoCompleteVarName[sContainerIDSetting];
  }   

  
  
  
  
  /**
    @brief Setzten der MouseOver-Variable
    @ingroup pb59_getInputFieldAutoComplete_noapi    
    @param  sContainerID    ID des Elements
    @param  bMouseOver      TRUE, wenn Cursor innerhalb des AC-Divs | FALSE, wenn nicht
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldAutoComplete_setMouseOver(sContainerID, bMouseOver) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    // alert(sContainerIDSetting);
    
    chtmlAutoCompleteMouseOver[sContainerIDSetting] = bMouseOver;
    return true;
  } 
  
  
  
  
  
  /**
    @brief Liefert Wert der MouseOver-Variable
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @return Liefert hinterlegten Wert der MouseOver-Variable
  */    
  function chtml_getInputFieldAutoComplete_getMouseOver(sContainerID) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    // alert(sContainerIDSetting);
    return chtmlAutoCompleteMouseOver[sContainerIDSetting];
  }  
  
  
  
  

	/**
	  @brief Führt Ajaxrequest aus, füllt Vorschlagsliste mit Daten und zeigt diese an.
		@ingroup pb59_getInputFieldAutoComplete_api
	  @param  sContainerID    ID des Elements
	  @param  sync    				TRUE erfolgt Ausführung synchron | FALSE Daten laden asynchron
	  @param  sType           POST or GET | wenn Leerstring übergeben wird GET verwendet
	  @param  sData           zu übertragende Werte in der Form: key1=value1&key2=value2 oder {key1: 'value1', key2: 'value2'} \n
	                          Um die korrekte Übertragung von Sonderzeichen (z.B. &) muss sich nicht extra gekümmert werden.
    @param  iTimeout        Wartezeit in ms. Kurze Zeit warten um Ausführung bei weiteren Eingaben zu vermweiden. \n
                            Wenn gesetzt (0 ... n), wird die Anzahl der Millisekunden (iTimeout) gewartet und danach geprüft ob nicht bereits ein andere Request angefordert wurde. Wenn ja wird Request nicht ausgeführt und somit bei "zügiger" Eingabe nur der letzte. \n
    	                      Wenn Leerstring, erfolgt Ausführung des AjaxRequests
    @param  iRequestCounter Mitgeben um prüfen zu können ob Request nach warten noch ausgeführt werden soll
	  @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
	*/    
	function chtml_getInputFieldAutoComplete_executeAjaxRequest(sContainerID, sync, sType, sData, iTimeout, iRequestCounter) {

	  var iCurrentRequest = 0;
	  
	  // warten oder request ausführen?
	  if (isNaN(iTimeout) || iTimeout == '') {
      if (!isNaN(iRequestCounter) && iRequestCounter != '') {
        // prüfen ob ausführung erfolgten soll
        if (iRequestCounter == chtmlAutoCompleteLatestRequest[sContainerID]) {
          iCurrentRequest = iRequestCounter;
          // ausführen
        } else {/*alert(iRequestCounter + ': kein ausführen');*/return;}
      }
      else {
        // Ausführung ohne Prüfung und ohne Warten
        iCurrentRequest = chtml_getInputFieldAutoComplete_raiseRequestCounter(sContainerID);
      }
	  } else {
	    // Intialaufruf, d.h. requestCounter initialiseren
	    iCurrentRequest = chtml_getInputFieldAutoComplete_raiseRequestCounter(sContainerID);
	    window.setTimeout(function() { chtml_getInputFieldAutoComplete_executeAjaxRequest(sContainerID, sync, sType, sData, '', iCurrentRequest)}, iTimeout);

	    // keine Ausführung(warten)
      return true;
	  }
    
	  
	  // Textfeldinhalt als POST-Daten übergeben
	  var objInput  = $(jqId('input_' + sContainerID)).get(0);    if (objInput === null) { return false; }
	  sData[chtml_getInputFieldAutoComplete_getVarName(sContainerID)] = objInput.value;

	  
  	// URL für Ajax aufruf
  	var sUrlAjax 				= chtml_getInputFieldAutoComplete_getUrlAjax(sContainerID);
  	
    // Aufruf konfigurieren und ausführen 
    $.ajax({
      url: sUrlAjax,
      dataType: 'json',
      type: ((sType=='') ? 'GET' : sType),
      cache: false,
      data: sData,
      async: ((sync) ? false : true),
      success: function (data, textStatus, XMLHttpRequest) {
        
        // Nur wenn request auch der letzte Request war die Daten aktualisieren
        if (iCurrentRequest == chtmlAutoCompleteLatestRequest[sContainerID]) {

          // "Auswahllisten" DIV ermitteln
          var objSel    = $(jqId('select_' + sContainerID));
          var arDiv     = $('div', objSel);
          var objDivNew = arDiv[0];
          var objLastDiv = objDivNew; // vorhergehenden Div ermitteln für die Methode insertAfter

          // Alle Element entfernen
          for (var i=(arDiv.length-1); i > 0; i--) {
            arDiv[i].parentNode.removeChild(arDiv[i]);
          }
          
          // Einträge hinzufügen
          var iRow = 0;
          for (var key in data) {
            
            // vorhergehenden Div ermitteln für die Methode insertAfter
            objLastDiv = objDivNew;
            
            iRow++;
            
            // Vorlage kopieren
            objDivNew = $(arDiv[0]).clone(true).get(0);
            
            // Neue Zeile mit Werten befülllen
            objDivNew.innerHTML = data[key] + '<input type="hidden" value="' + key + '" /><input type="hidden" value="' + data[key] + '" /><input type="hidden" value="' + iRow + '" />';
            
            // Alten Zeiger zur Position in der Auswahlliste entfernen
            chtml_getInputFieldAutoComplete_setFocusPosition(sContainerID, 0);
            
            // Neue Zeile einfügen
            $(objDivNew).insertAfter(objLastDiv);

            $(objDivNew).css('display', '');
            
          }     

          // Wenn keine Treffer dann entsprechende Zeile einblenden
          if (iRow == 0) {
            
            objDivNew = $(arDiv[0]).clone(true).get(0);
            
            // Neue Zeile einfügen
            $(objDivNew).insertAfter(objLastDiv);

            $(objDivNew).css('display', '');
          }
          
          
          // Hochscrollen
          objSel.scrollTo($(arDiv[0]), 200, {axis:'y'});
          
          // Liste einblenden
          $(objSel).css('display', '');
        }
      },
      error: function (XMLHttpRequest, textStatus, errorThrown) {
         
        // Nur wenn request auch der letzte Request war die Daten aktualisieren
        if (iCurrentRequest == chtmlAutoCompleteLatestRequest[sContainerID]) {
          
          // "Auswahllisten" DIV ermitteln
          var objSel = $(jqId('select_' + sContainerID));
          var arDiv  = $('div', objSel);
  
          // Alle Element entfernen
          for (var i=(arDiv.length-1); i > 0; i--) {
            arDiv[i].parentNode.removeChild(arDiv[i]);
          }
          
          // Vorlage kopieren
          var objDivNew = $(arDiv[0]).clone(true).get(0);
          
          // Alten Zeiger zur Position in der Auswahlliste entfernen
          chtml_getInputFieldAutoComplete_setFocusPosition(sContainerID, 0);
          
          // Neue Zeile einfügen
          $(objDivNew).insertAfter(arDiv[0]);
          $(objDivNew).css('display', '');               
  
          // Liste einblenden
          $(objSel).css('display', '');
          
          return false;
        }
      }
    });
      
    return true;
	}
	
	
	
	
	
	
	
	/**
	  @brief Aktualisiert das Textfeld und das Hiddenfeld mit dem aktuell ausgewählten Wert
		@ingroup pb59_getInputFieldAutoComplete_noapi
	  @param  sContainerID    ID des Elements
	  @param  iRow            Zeile aus welche der zu übernehmende Wert stammt
	  @return Befüllt Textfeld und ID-Hiddenfield mit selektiertem Wert der Auswahlliste | null wenn ein Feld nicht gefunden werden konnte
	*/    
  function chtml_getInputFieldAutoComplete_setValueBySelect(sContainerID, iRow) {

    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);    

    // Felder ermitteln ermitteln
    var objSelect = $(jqId('select_' + sContainerID)).get(0);  if (objSelect === null) { return false; }
    var objInput  = $(jqId('input_' + sContainerID)).get(0);   if (objInput === null) { return false; }
    var objHidden = $(jqId('hidden_' + sContainerID)).get(0);  if (objHidden === null) { return false; }
    var sLabel    = '';
    var sValue    = '';
    
    // Aufschrift und Wert ermitteln
    var objSel  = $(jqId('select_' + sContainerID)).get(0);
    var arDiv   = $('div', objSel);
    if (arDiv[iRow] !== undefined) {
      // Inputfelder ermitteln
      var arInput = $('input', arDiv[iRow]);
      
      sValue = arInput[0].value;
      sLabel = arInput[1].value;
    }
    
    // Wert in Textfeld schreiben
    objInput.value = sLabel;
    
    // Wer übertragens
    objHidden.value = sValue;
    
    // Aufruf der CF | Aufruf erfolgt nur beim Verlassen und damit dem Bestätigen des Wertes
//    chtml_getInputFieldAutoComplete_executeCF(sContainerID, sLabel, objHidden.value);
  } 
	
	
	
	
	
	
	
  /**
    @brief Wird beim Loslassen einer Tasteneingabe aufgerufen.
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @param  event           Browser event-Object
    @param  sync            TRUE asynchrone AJAX-Ausführung | FALSE synchrone Ausführung
    @param  iListScroll     Anzahl Einträge, welche ohne Scrollen sichtbar sein sollen
    @param  iSelLineHeight  Höhe eine Zeile
    @param  sType           POST or GET | wenn Leerstring übergeben wird GET verwendet
    @param  sData           zu übertragende Werte in der Form: key1=value1&key2=value2 oder {key1: 'value1', key2: 'value2'}, auch Leerstring möglich
    @param  iTimeout        detaillierte Beschreibung siehe chtml_getInputFieldAutoComplete_executeAjaxRequest()  
  */  	
	function chtml_getInputFieldAutoComplete_onKeyUp(sContainerID, event, sync, iListScroll, iSelLineHeight, sType, sData, iTimeout) {

	  // Abbruch bei Escape oder Enter
//	  if (event.keyCode == 27 || event.keyCode == 13) { $(jqId('select_' + sContainerID)).css('display', 'none'); return false; }
	  
	  // Keine Ausführung bei Tab-Taste
	    // Führt dazu, dass Ajax ausgelöst wird, wenn per Tab durch das Formular navigiert wird.
	  if (event.keyCode == 9) {return;}
	  
	  
	  // Wenn Cursor - "DOWN" gedrückt wurde
		if (event.keyCode == 40 || event.keyCode == 38) {
		  
	    var objSel = $(jqId('select_' + sContainerID));
	    var arDiv  = $('div', objSel);
	    chtml_getInputFieldAutoComplete_setCloseFlag(sContainerID, false);
	    
	    // Aktuelle Position ermitteln
	    var iCurrent = chtml_getInputFieldAutoComplete_getFocusPosition(sContainerID);
	    
	    // Position um eins verändern
	    var iDirection = ((event.keyCode == 40) ? 'down' : 'up');
	    var iNew = chtml_getInputFieldAutoComplete_changeFocusPosition(sContainerID, iDirection);

	    // Farbliche Hervorhebung der alten Zeile entfernen
	    if (iCurrent > 0 && iCurrent !== undefined) {
	      $(arDiv[iCurrent]).css('backgroundColor', '#FFFFFF');
	    }

	    // Neue Zeile farblich hervorheben
	    $(arDiv[iNew]).css('backgroundColor', '#CEDEF9');
	    
	    // Übernimm ausgewählten Wert
	    chtml_getInputFieldAutoComplete_setValueBySelect(sContainerID, iNew);
	    
      // Scrollen
	    objSel.scrollTo($(arDiv[iNew]), 200, {axis:'y'});
	  } else { 
     // Nach der Eingabe von Zeichen einen bisher ausgewählten Wert zurücksetzen
     var objHidden = $(jqId('hidden_' + sContainerID)).get(0);  if (objHidden === null) { return false; }
     objHidden.value = '';
	      
     // bei allen anderen Tasten -> AjaxRequest ausführen 
     chtml_getInputFieldAutoComplete_executeAjaxRequest(sContainerID, sync, sType, sData, iTimeout);
	  } 
	  
	  chtml_getInputFieldAutoComplete_setCloseFlag(sContainerID, true);
	}	
	
	
	

	
  /**
    @brief Erhöht/Veringert Zählerstand um 1 ... bei Überlauf wird Wert entsprechend auf Anfang oder Ende gesetzt
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @param  sDirection      up | down
    @return Liefert neuen Wert zurück
  */    
  function chtml_getInputFieldAutoComplete_changeFocusPosition(sContainerID, sDirection) {
    
    // ---- setup ----
    var iFirst    = 1;
    var arDiv     = $('div', $(jqId('select_' + sContainerID)));
    var iLast     = (arDiv.length-1);
    var iCurrent  = chtml_getInputFieldAutoComplete_getFocusPosition(sContainerID);
    var iNew;
    
    // neuen Wert ermitteln
    if (iCurrent === undefined) {
      if (sDirection == 'down') {iNew = iFirst;} else {iNew = iLast;}
    } else {
      iNew = iCurrent;
      if (sDirection == 'down') {iNew++;} else {iNew--;}
    }

    // Überlauf?
    if (sDirection == 'down' && iNew > iLast) {iNew = iFirst;}
    if (sDirection == 'up' && iNew < iFirst) {iNew = iLast;}
    
    // Neuen Wert speichern und zurückgeben
    chtml_getInputFieldAutoComplete_setFocusPosition(sContainerID, iNew);
    return iNew;
  }   
	
	
	
	
	
  /**
    @brief Zählt je Ajaxaufruf einen Counter hoch. Ziel: nur letzte Ajaxausführung soll angezeigt werden
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @return Liefert neuen Wert zurück
  */      
	function chtml_getInputFieldAutoComplete_raiseRequestCounter(sContainerID) {
	  
	  var iCurrentRequest = 0;
	  
    // Zähler zum ermitteln des letzten AjaxAufrufs
      // Ziel: verhindern, das früher AutoCompleteAufruf Daten an Liste übergibt, wenn ein später Request schneller war
    if (chtmlAutoCompleteLatestRequest[sContainerID] === undefined) {
      chtmlAutoCompleteLatestRequest[sContainerID] = 0;
    } else {
      chtmlAutoCompleteLatestRequest[sContainerID]++;
      iCurrentRequest = chtmlAutoCompleteLatestRequest[sContainerID];
    }
    
    return iCurrentRequest;
	}
	
	
	

	
  /**
    @brief Ruft CF-Funktion entsprechenden Werten auf
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
  */    
  function chtml_getInputFieldAutoComplete_onBlur(sContainerID) {
    
    // Felder ermitteln ermitteln
    var objInput  = $(jqId('input_' + sContainerID)).get(0);   if (objInput === null) { return false; }
    var objHidden = $(jqId('hidden_' + sContainerID)).get(0);  if (objHidden === null) { return false; }
    var sLabel    = objInput.value;
    var sId   = objHidden.value;
    
    // Aufruf der CF
    chtml_getInputFieldAutoComplete_executeCF(sContainerID, sId, sLabel);
  } 	

  
  
  
  
  /**
    @brief Liefert ID zur Abfrage von Einstellungen (wichtig bei Kopiervorgängen)
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @return ID mit welcher Einstellungen abgefragt werden können | liefert null im Fehlerfall
  */    
  function chtml_getInputFieldAutoComplete_getSettingsId(sContainerID) {
    // Node-Objekt des AutoComplete-Elementes ermitteln 
    var objInput = $(jqId(sContainerID) + ' input.chtml_getautocomplete_setting_id').get(0);
    if (typeof objInput != 'undefined') { return objInput.value; }
    return null;
  }  
  
  
  
  
  /**
    @brief Möglichkeit zum Setzen der Mindestzeichenlänge bevor der Ajaxrequest ausgeführt wird.    
    @ingroup pb59_getInputFieldAutoComplete_api
    @param  sContainerID    ID des Elements
    @param  iMinChar        Zeichenanzahl, welche in das Textfeld eingegeben müssen, bevor der Ajaxrequest ausgeführt wird
    @return TRUE, wenn erfolgreich hinzugefügt | FALSE, bei Fehler
  */
  function chtml_getInputFieldAutoComplete_setMinChar(sContainerID, iMinChar) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
    // AutoComplete mit "minChar" verknüpfen
    chtmlAutoCompleteMinChar[sContainerIDSetting] = iMinChar;
  
    return true;
  }
  
  
  
  
  
  /**
    @brief Liefert "minChar"-Wert.
    @ingroup pb59_getInputFieldAutoComplete_noapi
    @param  sContainerID    ID des Elements
    @return Liefert "minChar"-Wert zur angegebenen ContainerID | null wenn Parameter nicht ermittelt werden konnte
  */    
  function chtml_getInputFieldAutoComplete_getMinChar(sContainerID) {
    
    // EinstellungsID ermitteln
    sContainerIDSetting = chtml_getInputFieldAutoComplete_getSettingsId(sContainerID);
    
    // Wert an URL anhängen
    return chtmlAutoCompleteMinChar[sContainerIDSetting];
  }  
  
  
	

  
  /**
    @brief    Funktion, die Pre- und Post-Events eines Autocomplete-Elements behandelt
    @ingroup  pb59_getInputFieldAutoComplete_noapi
    @param    sContainerID    ID des Elements
    @param    id              ID des ausgewählten Werts (ist leer, wenn das Textfeld per [Tab]-Taste verlassen wurde
    @param    text            Text/Label des ausgewählten Werts (entspricht bei Verwendung der [Tab]-Taste dem Inhalt des Textfelds   
    @return   Rückgabe der aufgerufenen Callbackfunktion (idR true oder false) | true, wenn keine Callbackfunktion zum Ereignis definiert wurde (damit aufrufende Funktion keinen Fehler erhält)
  chtml_getInputFieldAutoComplete_executeCF(sContainerID, id, text) {
    // Dynamische Generierung zur Laufzeit durch CHtml::getInputFieldAutoComplete()                                
  }
  */	

  
  function mig_test(sContainerId, id, label) {
    alert(id + ' | ' + label );
  }
	// -------------------- } AutoComplete -------------------- */
    
    
    
    
    
	/*          
	    ################################### Multiselect ###################################		        
	*/    

  /**
    @brief  	Gibt das Node-Objekt einer Zeile eines Multiselect-Elements zurück    
    @ingroup 	pb59_getMultiselect_api    
    @param  	sContainerID    ID des Multiselect-Elementes 
    @param  	iRow            Zeilennummer, beginnend bei 0 (-1 referenziert die Zeile des Templates)
    @return 	Node-Objekt der Zeile | null, wenn Zeile nicht gefunden wurde
  */ 
  function chtml_getMultiselect_getRow(sContainerID, iRow) {
    // Node-Objekt des Multiselect-Elementes ermitteln 
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }
    
    // Wenn sichtbare Zeile angefordert wurde
    if (iRow != -1) {
    	var arRows = $('> table > tbody > tr.chtml_multiselect_row', objMS);
    	if (arRows.get(iRow) === undefined) { return null; }
    	return arRows.get(iRow);
  	// Wenn Zeile des Templates angefordert wurde      	
    } else {
      var arRows = $('> table > tbody > tr.chtml_multiselect_row_tpl', objMS);    	
      if (arRows.get(0) === undefined) { return null; }
    	return arRows.get(0);
    }
  }  
   
  
  
  /**
    @brief 		Gibt das Node-Objekt einer Zelle eines Multiselect-Elements zurück	
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des Multiselect-Elements 
    @param  	iRow            Zeilennummer, beginnend bei 0 (-1 referenziert die Zeile des Templates)
    @param  	iCol            Spaltennummer, beginnend bei 0
    @return 	Node-Objekt der Zelle | null, wenn die Zelle nicht gefunden wurde
  */ 
  function chtml_getMultiselect_getCell(sContainerID, iRow, iCol) {
    // Node-Objekt der angeforderten Zeile des angeforderten Multiselect-Elementes ermitteln	
  	var objRow = chtml_getMultiselect_getRow(sContainerID, iRow);
  	if (objRow === null) { return null; }
  
  	var arCols = $('> td.chtml_multiselect_col', objRow);
  	if (arCols.get(iCol) === undefined) { return null; }
   	return arCols.get(iCol);	  
  }
  
  
    
  /**
    @brief 		Ermittelt die Anzahl der Zeilen, die ein Multiselect-Element aktuell beinhaltet.
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des Multiselect-Elementes
    @return 	Anzahl der Zeilen | null, wenn das Multiselect-Element nicht gefunden wurde
  */ 
  function chtml_getMultiselect_getRowCount(sContainerID) {
    // Node-Objekt des Multiselect-Elementes ermitteln
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }        	
    return $('> table > tbody > tr.chtml_multiselect_row', objMS).size();      
  }    
    
    
          
  /**
    @brief 		Ermittelt die Zeilennummer eines Node-Objektes einer Zeile
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param		objRow					Node-Objekt der Zeile
    @return 	Zeilennummer (beginnend bei 0) | null, wenn die Zeile innerhalb des Multiselect-Elementes nicht gefunden wurde
  */ 
  function chtml_getMultiselect_getRowIndexByRowObject(sContainerID, objRow) {
    // Node-Objekt des Multiselect-Elementes ermitteln
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }        
    var iRow = $('> table > tbody > tr.chtml_multiselect_row', objMS).index(objRow);
  	if (iRow >= 0) { return iRow; }
  	return null;
  }
  
  
    
  /**
    @brief 		Ermittelt die Zeilennummer eines Node-Objektes einer Zeile
    @ingroup 	pb59_getMultiselect_api    
    @param		objCell					Node-Objekt der Zelle
    @return 	Zeilennummer (beginnend bei 0) | null, wenn die Zeile innerhalb des Multiselect-Elementes nicht gefunden wurde
  */ 
  function chtml_getMultiselect_getRowIndexByCellObject(objCell) {    
    var iRow = $('> tr.chtml_multiselect_row', objCell.parentNode.parentNode).index(objCell.parentNode);
  	if (iRow >= 0) { return iRow; }
  	return null;
  }    
    
  
    
  /**
    @brief 		Ermittelt die Spaltennummer eines Node-Objektes einer Zeile
    @ingroup 	pb59_getMultiselect_api    
    @param		objCell					Node-Objekt der Zelle
    @return 	Spaltennummer (beginnend bei 0) | null, wenn die Spalte innerhalb des Multiselect-Elementes nicht gefunden wurde
  */ 
  function chtml_getMultiselect_getColIndexByCellObject(objCell) {
    var iCol = null;        
    iCol = $('> td.chtml_multiselect_col_buttons_col', objCell.parentNode).index(objCell);
    if (iCol >= 0) { return iCol; }    
    iCol = $('> td.chtml_multiselect_col', objCell.parentNode).index(objCell);
    if (iCol >= 0) { return iCol; }
    iCol = $('> td.chtml_multiselect_headline_col', objCell.parentNode).index(objCell);
    if (iCol >= 0) { return iCol; }    
  	return null;
  }    


  
  /**
    @brief 		Löscht eine Zeile eines Multiselect-Elementes
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param  	iRow            Zeilennummer, beginnend bei 0 (-1 referenziert die Zeile des Templates)
    @param		sButtonType			(optional) Schaltflächen-Typ (delete | trash), der die Funktion ausgelöst hat (wird zum Aufruf der richtigen Callbackfunktion benötigt) 
    @return 	true, wenn erfolgreich entfernt | false im Fehlerfall
  */ 
  function chtml_getMultiselect_delete(sContainerID, iRow, sButtonType) {
    // Node-Objekt der angeforderten Zeile des angeforderten Multiselect-Elementes ermitteln	
	  var objRow = chtml_getMultiselect_getRow(sContainerID, iRow);
	  if (objRow === null) { return false; }
	  // PRE-Callbackfunktion ausführen
	  if (!chtml_getMultiselect_executeCF(sContainerID, sButtonType, 'PRE', iRow, null)) { return false; }
	  // Zeile Löschen
	  objRow.parentNode.removeChild(objRow);
	  // POST-Callbackfunktion ausführen
	  chtml_getMultiselect_executeCF(sContainerID, sButtonType, 'POST', iRow, null);	  
	  return true;  	  
  }

    
    
  /**
    @brief 		Löscht eine Spalte eines Multiselect-Elementes
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param  	iCol            Spaltennummer, beginnend bei 0 (-1 referenziert die Spalte des Templates)
    @param		sButtonType			(optional) Schaltflächen-Typ (delete | trash), der die Funktion ausgelöst hat (wird zum Aufruf der richtigen Callbackfunktion benötigt) 
    @return 	true, wenn erfolgreich entfernt | false im Fehlerfall
  */ 
  function chtml_getMultiselect_delete_col(sContainerID, iCol, sButtonType) {

    // Node-Objekt des Multiselect-Elementes ermitteln
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }        
    
    // Alle Zeilen des Multiselect-Elements finden (sollte mind. 1 vorhanden sein)
    var jqAllRows = $('> table > tbody > tr', objMS);
		
    // Zeilen durchlaufen und je Zeile die Zelle löschen  
    for (var x = 0; x < jqAllRows.length; x++) {
    	// Spalten der aktuellen Zeile
    	var jqCellsTMP = $('> td', jqAllRows[x]);
    	// Zelle löschen
    	jqCellsTMP[iCol].parentNode.removeChild(jqCellsTMP[iCol]);
    }
    
	  return true;  	  
  }	 
    
    
    
  /**
    @brief 		Sortiert eine Zeile eines Multiselect-Elements nach oben oder unten
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param  	iRow            Zeilennummer, beginnend bei 0 (-1 referenziert die Zeile des Templates)
    @param  	sDirection      up (hoch / links) | down (runter / rechts)
    @param		sButtonType			(optional) Schaltflächen-Typ (up | down | left | right), der die Funktion ausgelöst hat (wird zum Aufruf der richtigen Callbackfunktion benötigt)
    @return 	true, wenn erfolgreich sortiert | false im Fehlerfall
  */ 
  function chtml_getMultiselect_sort(sContainerID, iRow, sDirection, sButtonType) {
  
    // Abbruch mit Fehler, wenn Sortierrichtung nicht ermittelt werden kann
    if ((sDirection != 'up') && (sDirection != 'down')) { return false; }
    
    // Node-Objekt der angeforderten Zeile des angeforderten Multiselect-Elementes ermitteln	
  	var objRow = chtml_getMultiselect_getRow(sContainerID, iRow);
  	if (objRow === null) { return false; }
  	
  	// Abbruch mit Erfolg, wenn oberste Zeile nach oben oder unterste Zeile nach unten sortiert werden soll
  	if (((iRow == 0) && (sDirection == 'up')) || ((iRow+1 == chtml_getMultiselect_getRowCount(sContainerID)) && (sDirection == 'down'))) {
  		return true;
  	}
  	
  	// Einstellungen zum Multiselect-Element ermitteln
  	var objSettings = chtml_getMultiselect_getSettings(sContainerID);
  	
  	// Index der Zeile ermitteln mit der getauscht werden soll und bestimmen, ob die aktuelle Zeile 
  	// über oder unter der Ziel-Zeile eingefügt werden soll
  	if (sDirection == 'up') { 
  		var iRowSwitch = iRow - 1;
  		var sPlacePosition = 'before';
  	} else { 
  		var iRowSwitch = iRow + 1;
  		var sPlacePosition = 'after';
  	}
  	
	  // PRE-Callbackfunktion ausführen
	  if (!chtml_getMultiselect_executeCF(sContainerID, sButtonType, 'PRE', iRow, {iRowTarget: iRowSwitch})) { return false; }
  	
  	// Node-Objekt der Ziel-Zeile, mit der getauscht werden soll, ermitteln
  	var objRowSwitch = chtml_getMultiselect_getRow(sContainerID, iRowSwitch);
  	if (objRowSwitch === null) { return false; }
  	
  	// Aktuelle Zeile über oder unter der Zielzeile einfügen
  	if (sPlacePosition == 'before') {
  		$(objRow).insertBefore(objRowSwitch);	  		
  	} else {
  		$(objRow).insertAfter(objRowSwitch);
  	}

  	// Wenn Unterstützung für CRTE-Objekte aktiv ist 
  	//  -> verschobenene CLEditoren im FF refreshen
  	//  -> kein Refresh im IE, da dieser keinen bötigt und ansonsten die Toolbar inaktiv darstellt 
  	if (objSettings.support_crte == 1) {
  		if (!$.browser.msie) {
  			$("textarea[cleSettingsID]", objRow).each(function() {
  				$(this).cleditor()[0].refresh().updateTextArea().updateFrame();
  				$(this).cleditor()[0].rebindEventListener();  				
      	});
  		}
  	}
  	
	  // POST-Callbackfunktion ausführen
  	// Achtung: Die Nummern der ereignisauslösenden und der Ziel-Zeile sind nun vertauscht, da Aortieraktion ausgeführt wurde!
	  chtml_getMultiselect_executeCF(sContainerID, sButtonType, 'POST', iRowSwitch, {iRowTarget: iRow});
  	
  	return true;
  }
  
    
    
  /**
    @brief 		Sortiert eine Spalte eines Multiselect-Elements nach rechts oder links
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param  	iCol            Spaltennummer, beginnend bei 0 (-1 referenziert die Spalte des Templates)
    @param  	sDirection      left | right
    @param		sButtonType			(optional) Schaltflächen-Typ (up | down | left | right), der die Funktion ausgelöst hat (wird zum Aufruf der richtigen Callbackfunktion benötigt)
    @return 	true, wenn erfolgreich sortiert | false im Fehlerfall
  */ 
  function chtml_getMultiselect_sort_col(sContainerID, iCol, sDirection, sButtonType) {
    	
    // Abbruch mit Fehler, wenn Sortierrichtung nicht ermittelt werden kann
    if ((sDirection != 'left') && (sDirection != 'right')) { return false; }
    
    // Node-Objekt des Multiselect-Elementes ermitteln
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }        
    
    // Alle Zeilen des Multiselect-Elements finden (sollte mind. 1 vorhanden sein)
    var jqAllRows = $('> table > tbody > tr', objMS);

    // Anzahl der Spalten ermitteln (einschl. rechte Funktionsspalte)
    var iColCount = $('> td', jqAllRows[0]).length;
        
		// Abbruch mit Erfolg, wenn linke Spalte nach links oder rechte Spalte nach rechts sortiert werden soll
		if (((iCol == 0) && (sDirection == 'left')) || ((iCol+2 >= iColCount) && (sDirection == 'right'))) {
			return true;
		}
		
  	// Einstellungen zum Multiselect-Element ermitteln
  	var objSettings = chtml_getMultiselect_getSettings(sContainerID);		
		
		// Index der Spalte ermitteln mit der getauscht werden soll und bestimmen, ob die aktuelle Spalte 
		// links oder rechts der Ziel-Spakte eingefügt werden soll
		if (sDirection == 'left') { 
			var iColSwitch = iCol - 1;
			var sPlacePosition = 'before';
		} else { 
			var iColSwitch = iCol + 1;
			var sPlacePosition = 'after';
		}
        
    // Zeilen durchlaufen und je Zeile die Zellen tauschen  
    for (var x = 0; x < jqAllRows.length; x++) {

    	// Spalten der aktuellen Zeile
    	var jqCellsTMP = $('> td', jqAllRows[x]);
	  	
    	// Aktuelle Zelle links oder rechts der Zielzelle einfügen
	  	if (sPlacePosition == 'before') {
	  		$(jqCellsTMP[iCol]).insertBefore(jqCellsTMP[iColSwitch]);	  		
	  	} else {
	  		$(jqCellsTMP[iCol]).insertAfter(jqCellsTMP[iColSwitch]);	  		
	  	}
	  	
	  	// Wenn Unterstützung für CRTE-Objekte aktiv ist 
	  	//  -> verschobenene CLEditoren im FF refreshen
	  	//  -> kein Refresh im IE, da dieser keinen bötigt und ansonsten die Toolbar inaktiv darstellt 
	  	if (objSettings.support_crte == 1) {
	  		if (!$.browser.msie) {
	  			$("textarea[cleSettingsID]", jqCellsTMP[iCol]).each(function() {
	  				$(this).cleditor()[0].refresh().updateTextArea().updateFrame();
	  				$(this).cleditor()[0].rebindEventListener();  				
	      	});
	  		}
	  	}	 
    }
  	
  	return true;
  }        
  
    
    
  /**
    @brief 		Fügt eine neue Zeile einem Multiselect-Element hinzu (Kopie des Templates)
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param  	iRow            Zeilennummer, beginnend bei 0, nach der die neue Zeile eingefügt werden soll (-1 kennzeichnet den Add-Button, der im Spaltenkopf eingebunden ist) 
    @return 	true, wenn erfolgreich eingefügt | false im Fehlerfall
  */ 
  function chtml_getMultiselect_add(sContainerID, iRow) {

    // Node-Objekt des Templates ermitteln	
  	var objRowTemplate = chtml_getMultiselect_getRow(sContainerID, -1);
  	if (objRowTemplate === null) { return false; }
  	
    // Node-Objekt der Zielzeile ermitteln, nach der die neue Zeile eingefügt werden soll
  	// (Entspricht dem Template, wenn -1 übergeben wurde)
  	var objRowDestination = chtml_getMultiselect_getRow(sContainerID, iRow);
  	if (objRowDestination === null) { return false; }

  	// Einstellungen zum Multiselect-Element ermitteln
  	var objSettings = chtml_getMultiselect_getSettings(sContainerID);
  	var maxRows = objSettings.list_max_length;
  	
  	// Abbruch ohne Fehler, wenn die maximale Anzahl an Zeilen erreicht ist
  	if ((maxRows != -1) && (chtml_getMultiselect_getRowCount(sContainerID) == maxRows)) { return true; }
  	  	
	  // PRE-Callbackfunktion ausführen
	  if (!chtml_getMultiselect_executeCF(sContainerID, 'add', 'PRE', iRow)) { return false; }
  	
  	// Template kopieren
  	var objRowNew = $(objRowTemplate).clone(true);
  	  	
  	// CSS-Klasse zur Kennzeichnung des Templates durch die Kennzeichnung einer Contentzeile ersetzen
  	$(objRowNew).removeClass('chtml_multiselect_row_tpl');
  	$(objRowNew).addClass('chtml_multiselect_row');  	
  	
  	// Platzhalter :id: durch Zufallszahl ersetzen und Platzhalter wieder anhängen, um erneutes Kopieren zu ermöglichen
  	var id = (Math.round(Math.random()*999999999999))+'_:id:';
  	$('> td.chtml_multiselect_col', objRowNew).each(function() { 
  		this.innerHTML = this.innerHTML.replace(/:id:/g, id);  		
  	});

  	// Falls die kopierte Zeile Formularelemente enthält, die durch das DBAF eingefügt wurden
  	// => Kennzeichnung, dass es sich bei der kopierten Zeile um ein Template handelt, entfernen
  	$('input.dbaf_is_template', objRowNew).each(function() { 
  		this.value = '0'; 
  	});
  	
  	// Kopie des Templates unter Ziel-Zeile einfügen  	
  	$(objRowNew).insertAfter(objRowDestination);
  	
  	// Neue Zeile sichtbar machen
  	$(objRowNew).css('display', '');
  	
  	// Wenn Unterstützung für CRTE-Objekte aktiv ist -> kopierte CLEditoren korrigieren
  	if (objSettings.support_crte == 1) {
  		chtml_CRTE_correctCopiedEditors(objRowNew);	
  	}
  	
	  // POST-Callbackfunktion ausführen
	  chtml_getMultiselect_executeCF(sContainerID, 'add', 'POST', iRow, {iRowTarget: iRow+1, iTplId: id});
  	
  	return true;
  }
  
    
    
  /**
    @brief 		Fügt eine neue Spalte einem Multiselect-Element hinzu (Kopie der Template-Zelle in jeder Zeile)
    @ingroup 	pb59_getMultiselect_api
    @param  	sContainerID    ID des betreffenden Multiselect-Elementes
    @param  	iCol            Spaltennummer, beginnend bei 0, nach der die neue Spalte eingefügt werden soll 
    @return 	true, wenn erfolgreich eingefügt | false im Fehlerfall
  */ 
  function chtml_getMultiselect_add_col(sContainerID, iCol) {

    // Node-Objekt des Multiselect-Elementes ermitteln
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }        
    
  	// Einstellungen zum Multiselect-Element ermitteln
  	var objSettings = chtml_getMultiselect_getSettings(sContainerID);    
    
    // Node-Objekt des Zellen- und des Spaltenkopf-Templates ermitteln	
  	var objCellTemplate = $('.chtml_multiselect_cell_tpl', objMS)[0];    
  	var objColheadlineTemplate = $('.chtml_multiselect_colheadline_tpl', objMS)[0];    
  	if (objCellTemplate == undefined) { return false; }
  	if (objColheadlineTemplate == undefined) { return false; }
    
    // Alle Zeilen des Multiselect-Elements finden (sollte mind. 1 vorhanden sein)
    var jqAllRows = $('> table > tbody > tr', objMS);
		
    // Zeilen durchlaufen und je Zeile die Template-Zelle einfügen löschen  
    for (var x = 0; x < jqAllRows.length; x++) {
    	// Spalten der aktuellen Zeile
    	var jqCellsTMP = $('> td', jqAllRows[x]);
    	var bGenerateRandomID = true;
    	var bRemoveDBAFTemplateFlag = true;
    	
    	// Template-Zelle kopieren
    	// > Wenn aktuelle Zeile den Spaltenüberschrifen entspricht
    	if ($(jqCellsTMP[0]).hasClass('chtml_multiselect_headline_col')) {
    		var objCellNew = $(objColheadlineTemplate).clone(true);
    	
    	// > Wenn aktuelle Zeile den Schaltflächen für Spaltenfunktionen entspricht    		
    	} else if ($(jqCellsTMP[0]).hasClass('chtml_multiselect_col_buttons_col')) {
      		var objCellNew = $(jqCellsTMP[0]).clone(true);
      		bGenerateRandomID = false;	// ID-Plathalter darf nicht ersetzt werden, da sonst Links nicht mehr funktionieren
    		
      // > Wenn aktuelle Zeile eine Template-Zeile ist
    	} else if ($(jqAllRows[x]).hasClass('chtml_multiselect_row_tpl')) {      		
      	bRemoveDBAFTemplateFlag = false;
      	var objCellNew = $(objCellTemplate).clone(true);
      		
    	// > Wenn aktuelle Zeile eine Content-Zeile ist
    	} else {
    		var objCellNew = $(objCellTemplate).clone(true);
    	}

    	// CSS-Klasse(n) zur Kennzeichnung des Templates dentfernen
			$(objCellNew).removeClass('chtml_multiselect_cell_tpl');
			$(objCellNew).removeClass('chtml_multiselect_colheadline_tpl');    	
    	
			// Platzhalter :id: durch Zufallszahl ersetzen und Platzhalter wieder anhängen, um erneutes Kopieren zu ermöglichen
			if (bGenerateRandomID) {
				var id = (Math.round(Math.random()*999999999999))+'_:id:';
				$(objCellNew)[0].innerHTML = $(objCellNew)[0].innerHTML.replace(/:id:/g, id);  		
			}
			
			// Falls die kopierte Zelle Formularelemente enthält, die durch das DBAF eingefügt wurden
			// => Kennzeichnung, dass es sich bei der kopierten Zeile um ein Template handelt, entfernen
			if (bRemoveDBAFTemplateFlag) {
				$('input.dbaf_is_template', objCellNew).each(function() { 
					this.value = '0'; 
				});
			}

	  	// Kopie des Templates rechts neben Ziel-Zelle einfügen  	
	  	$(objCellNew).insertAfter(jqCellsTMP[iCol]);
	  	
	  	// Wenn Unterstützung für CRTE-Objekte aktiv ist -> kopierte CLEditoren korrigieren
	  	if (objSettings.support_crte == 1) {
	  		chtml_CRTE_correctCopiedEditors(objCellNew);	
	  	}	  	
    }    			
  	
  	return true;
  }
        
    
    
  /**
    @brief		Stellt für kopierte oder verschobene CLEditoren sicher, dass diese wieder korrekt dargestellt werden
					    -> Entfernt die kopierte oder verschobene CLEditor-Struktur und erstellt ein frisches CLEditor-Objekt 
					       auf der Basis einer neuen Textarea mit den Einstellungen der Alten
    @ingroup 	pb59_CRTE
    @param  	objDOM			DOM-Objekt, unterhalb dem alle CLEditoren korrigiert werden sollen    
  */
  function chtml_CRTE_correctCopiedEditors(objDOM) {
		// DIV-Container kopierter CLEditoren durchlaufen
		$("DIV.cleditorMain", objDOM).each(function() {
			// Je Container für die enthaltene Textarea eine neue erstellen und auf derselben Ebene, wie den Container einfügen
			var objDIV = this;
			$("textarea[cleSettingsID]", objDIV).each(function() {								
				var newTextarea = $("<textarea></textarea>").insertAfter(objDIV); // Neue Textarea anlegen
				newTextarea.attr('id', $(this).attr("id"));												// ID der kopierten Textarea übernehmen
				newTextarea.attr('name', $(this).attr("name")); 									// Name übernehmen
				newTextarea.attr('cleSettingsID', $(this).attr("cleSettingsID")); // Einstellungs-ID übernehmen
				newTextarea.html($(this).html());																  // Content übernehmen
			});
			// Container entfernen
			$(objDIV).remove();
		});
		
		// Alle Textareas der neuen Zeile durchlaufen und für diese jeweils den CLE-Editor unter Verwendung der Einstellungs-ID instanzieren
		$("textarea[cleSettingsID]", objDOM).each(function() {
			crte_instanciateTextareaWithPredefinedSettings(this, $(this).attr("cleSettingsID"));			
    });			    														
  }



    
  /**
    @brief		Funktion, die Pre- und Post-Events eines Multiselect-Elements behandelt
    @ingroup 	pb59_getMultiselect_noapi
    @param  	sContainerID    ID des Multiselect-Elements
    @param  	sButtonType     Betreffender Schaltflächen-Typ: up, down, left, right, add, delete, trash, edit, preview, copy   
    @param		sPrePost			  Kennzeichnung, ob es sich um ein PRE- oder POST-Ereignis handelt (Gültige Werte: PRE, POST)
    @param  	iRowEvent       Nummer der Zeile, die das Ereignis ausgelöst hat (-1 im Falle des globalen Add-Buttons)
    @param  	arParams				(optional) Zusätzliche Parameter in Abhängigkeit des Schaltflächentyps
    													- Für Sortier-Schaltflächen (Typ up, down, left, right):
    														- arParams.iRowTarget		Nummer der Zeile mit der die ereignisauslösende Zeile vertauscht werden soll
    													- Für Hinzufügen-Schaltfläche (Typ add):
    														- arParams.iRowTarget 	Nummer der neuen Zeile (beginnend bei 0)
    													  - arParams.iTplId       Wert mit welchem die Platzhalter ":id:" in der Vorlagenzeile ersetzt wurden 
    @return 	Rückgabe der aufgerufenen Callbackfunktion (idR true oder false) | true, wenn keine Callbackfunktion zum Ereignis definiert wurde (damit aufrufende Funktion keinen Fehler erhält)
  function chtml_getMultiselect_executeCF(sContainerID, sButtonType, sPrePost, iRowEvent, arParams) {
		// Dynamische Generierung zur Laufzeit durch CHtml::getMultiselect()    														
  }
	*/
  
  
	
  /**
	  @brief		Gibt individuelle Einstellungen eines Multiselect-Elementes zurück
	  @ingroup 	pb59_getMultiselect_noapi
	  @param  	sContainerID    					ID des Multiselect-Elements
	  @return 	Object.list_max_length	 	Maximale Anzahl von Elementen (-1, wenn unbegrenzt)
	  								.cfid								ID zur Zuordnung von Callbackfunktionen
	  								.support_crte				1, wenn das Multiselect-Element CRTE-Objekte unterstützt | 0, sonst
	*/
	function chtml_getMultiselect_getSettings(sContainerID) {

		var objRet = new Object();
		
		// Node-Objekt des Multiselect-Elementes ermitteln
    var objMS = $(jqId(sContainerID));  if (objMS.get(0) === undefined) { return null; } else { objMS = objMS.get(0); }        

    // Einstellung zur maximalen Anzahl von Elementen ermitteln
  	var objSetting_list_max_length = $('> input.chtml_multiselect_setting_list_max_length', objMS).get();  	
  	if (objSetting_list_max_length[0] === undefined) { 
  		objRet.list_max_length = -1; 
  	} else {
  		objRet.list_max_length = parseInt(objSetting_list_max_length[0].value);
  	}

    // ID für Callbackfunktionen ermitteln
  	var objSetting_cfid = $('> input.chtml_multiselect_setting_cfid', objMS).get();  	
  	if (objSetting_cfid[0] === undefined) { 
  		objRet.cfid = ''; 
  	} else {
  		objRet.cfid = objSetting_cfid[0].value;
  	}
  	
    // Flag zur Unterstützung von CRTE-Objektenermitteln
  	var objSetting_support_crte = $('> input.chtml_multiselect_setting_support_crte', objMS).get();  	
  	if (objSetting_support_crte[0] === undefined) { 
  		objRet.support_crte = 0; 
  	} else {
  		objRet.support_crte = objSetting_support_crte[0].value;
  	}  	
  	
  	return objRet;		
	}
	
	
	
	
	
	

	
	
	
	/*          
  	################################### Schaukelliste ###################################		        
	*/
	
	/**
	  @brief 	  Verschiebt übergebene Einträge einer Liste in die andere Liste  
	  @ingroup 	pb59_getSwinglist_noapi
	  @param  	sContainerID    ID der Schaukelliste
	  @param		sSourceList			Quellliste, von der aus verschoben werden soll. Valide Werte:
	  													- "select_list" für Liste, in der zugeorndete Werte stehen
	  													- "not_select_list" für Liste, in der nicht zugeordnete Werte stehen
		@param		arValue	  			Array mit den IDs zu verschiebender Listeneinträge im Format arValue[] = <ID> (<ID> bezieht sich auf den Index des Parameters $arValue der PHP-Funktion)
		@param		bExecuteCF			true, wenn Callbackfunktionen ausgeführt werden sollen | false, sonst										
	  @return 	true, wenn erfolgreich hinzugefügt | false, bei Fehler
	*/
	function chtml_getSwingList_moveValues(sContainerID, sSourceList, arValue, bExecuteCF) {
	
		// ---- Parameter sSourceList validieren ---- 
		if ((sSourceList != 'select_list') && (sSourceList != 'not_select_list')) { return false; }
		if (sSourceList == 'select_list') { 
			var sDestinationList = 'not_select_list';
			var sCallbackAction = 'unselect';
		} else { 
			var sDestinationList = 'select_list';
			var sCallbackAction = 'select';
		} 
	
		// ---- Objekte der Quell- und Ziellisten ermitteln ----
		var objSourceList = 						$(jqId(sContainerID)+'_'+sSourceList).get(0);  if (objSourceList === undefined) { return false; }
		var objDestinationList = 				$(jqId(sContainerID)+'_'+sDestinationList).get(0);  if (objDestinationList === undefined) { return false; }
		var objSourceListHidden = 			$(jqId(sContainerID)+'_'+sSourceList+'_hidden').get(0);  if (objSourceListHidden === undefined) { return false; }
		var objDestinationListHidden = 	$(jqId(sContainerID)+'_'+sDestinationList+'_hidden').get(0);  if (objDestinationListHidden === undefined) { return false; }
		
		// ---- PRE-Callback-Funktion ausführen ----
		if (bExecuteCF) {
			if (!chtml_getSwingList_executeCF(sContainerID, sCallbackAction, 'PRE', {arValue: arValue})) { return false; }
		}
			
  	// ---- Abbrechen, wenn Array mit auszuwählenden Werten im falschen Format ist ----
  	if ((!arValue) || (!arValue.length)) { return false; }

  	// ---- Die IDs der auzuwählenden Werte über Index eines assoziativen Arrays verfügbar machen ----
  	var arSelectedIDs = new Object();
  	for (var x = 0; x < arValue.length; x++) {
  		arSelectedIDs[(arValue[x])] = true;			  		
  	}
  	
  	// ---- Werte der Quelliste durchlaufen und übergebene Datensätze in Zielliste verschieben ----
  	for (var x = 0; x < objSourceList.options.length; x++) {
  		if (arSelectedIDs[(objSourceList.options[x].value)] != undefined) {
	    	objDestinationList.options[objDestinationList.options.length] = new Option(objSourceList.options[x].text, objSourceList.options[x].value, false, false);
	    	objDestinationListHidden.options[objDestinationListHidden.options.length] = new Option(objSourceListHidden.options[x].text, objSourceListHidden.options[x].value, false, true); // Eintrag in versteckter Liste hinzufügen und selektieren
	    	objSourceList.options[x] = null;
	    	objSourceListHidden.options[x] = null;
	      x--;  			
  		}
  	}
  	
		// ---- POST-Callback-Funktion ausführen ----
		if (bExecuteCF) {
			chtml_getSwingList_executeCF(sContainerID, sCallbackAction, 'POST', {arValue: arValue});
		}
  	
	  return true;
	}		
	
	
	
	/**
	  @brief 	  Verschiebt selektierte Einträge einer Liste in die andere Liste  
	  @ingroup 	pb59_getSwinglist_noapi
	  @param  	sContainerID    ID der Schaukelliste
	  @param		sSourceList			Quellliste, von der aus verschoben werden soll. Valide Werte:
	  													- "select_list" für Liste, in der zugeordnete Werte stehen
	  													- "not_select_list" für Liste, in der nicht zugeordnete Werte stehen
	  @return 	true, wenn erfolgreich hinzugefügt | false, bei Fehler
	*/
	function chtml_getSwingList_moveSelectedValues(sContainerID, sSourceList) {

		// ---- Objekt der Quellliste ermitteln ----
		var objSourceList = $(jqId(sContainerID)+'_'+sSourceList).get(0);  if (objSourceList === undefined) { return false; }

		// ---- Array mit den IDs der selektierten Listeneinträge erstellen ----
		var arValue = new Array();
    for (var x = 0; x < objSourceList.options.length; x++) {      
      if (objSourceList.options[x].selected) {
      	arValue.push(objSourceList.options[x].value);      	
      }
    }
    
    // ---- Einträge verschieben ----
    return chtml_getSwingList_moveValues(sContainerID, sSourceList, arValue, true);
	}	


	  
  /**
    @brief 		Verschiebt Werte aus der Liste mit nicht zugeordneten Werte in die Liste zugeordneter Werte    
    @ingroup 	pb59_getSwinglist_api
    @param  	sContainerID    ID des Elements
    @param  	arValue         Array mit den IDs zu selektierender Datensätze im Format arValue[] = <ID> (<ID> bezieht sich auf den Index des Parameters $arValue der PHP-Funktion)
    @return 	true, wenn erfolgreich hinzugefügt | false, bei Fehler
  */
  function chtml_getSwingList_selectValues(sContainerID, arValue) {
    return chtml_getSwingList_moveValues(sContainerID, 'not_select_list', arValue, false);
  }

    
    
  /**
    @brief 		Verschiebt Werte aus der Liste mit zugeordneten Werte in die Liste nicht zugeordneter Werte    
    @ingroup 	pb59_getSwinglist_api
    @param  	sContainerID    ID des Elements
    @param  	arValue         Array mit den IDs zu selektierender Datensätze im Format arValue[] = <ID> (<ID> bezieht sich auf den Index des Parameters $arValue der PHP-Funktion)
    @return 	true, wenn erfolgreich hinzugefügt | false, bei Fehler
  */
  function chtml_getSwingList_unselectValues(sContainerID, arValue) {
    return chtml_getSwingList_moveValues(sContainerID, 'select_list', arValue, false);
  }

    
    
 	/**
	  @brief 	  Liefert alle Werte einer der beiden Listen einer Schaukelliste   
	  @ingroup 	pb59_getSwinglist_noapi
	  @param  	sContainerID    ID der Schaukelliste
	  @param		sList						Betreffende Liste. Valide Werte:
	  													- "select_list" für Liste, in der zugeordnete Werte stehen
	  													- "not_select_list" für Liste, in der nicht zugeordnete Werte stehen
		@return 	arValue					false im Fehlerfall | sonst: Array (Objekt) mit den Einträgen der Liste im Format arValue[<ID>] = <Wert> <br />
	  													- <ID> ist der beim Absenden des Formulars übertragene Wert 
	  													- <Wert> ist der in der Auswahlliste dargestellte Wert
	  													- Die zurückgegebenen Daten können mit einer for-in-Schleife durchlaufen werden => for (var id in arValue) { alert(arValue[id]); }
	*/
	function chtml_getSwingList_getValues(sContainerID, sList) {
	
		// ---- Parameter sList validieren ---- 
		if ((sList != 'select_list') && (sList != 'not_select_list')) { return false; }		 
	
		// ---- Objekt der Liste ermitteln ----
		var objList = $(jqId(sContainerID)+'_'+sList).get(0);  if (objList === undefined) { return false; }
		
  	// ---- Einträge der Liste durchlaufen und Rückgabestruktur zusammenstellen ----
  	var arValue = new Object();
  	for (var x = 0; x < objList.options.length; x++) {
  		arValue[(objList.options[x].value)] = objList.options[x].text;
  	}  			

	  return arValue;
	}	    
	
	  
	  
 	/**
	  @brief 	  Liefert die Menge zugeordneter Werte einer Schaukelliste   
	  @ingroup 	pb59_getSwinglist_api
	  @param  	sContainerID    ID der Schaukelliste
	  @return 	arValue					false im Fehlerfall | sonst: Array (Objekt) mit den Einträgen der Liste im Format arValue[<ID>] = <Wert> <br />
	  													- <ID> ist der beim Absenden des Formulars übertragene Wert 
	  													- <Wert> ist der in der Auswahlliste dargestellte Wert
	  													- Die zurückgegebenen Daten können mit einer for-in-Schleife durchlaufen werden => for (var id in arValue) { alert(arValue[id]); }
	*/
	function chtml_getSwingList_getSelectedValues(sContainerID) {
		return chtml_getSwingList_getValues(sContainerID, 'select_list');
	}	
	
	
		
 	/**
	  @brief 	  Liefert die Menge nicht zugeordneter Werte einer Schaukelliste   
	  @ingroup 	pb59_getSwinglist_api
	  @param  	sContainerID    ID der Schaukelliste
	  @return 	arValue					false im Fehlerfall | sonst: Array (Objekt) mit den Einträgen der Liste im Format arValue[<ID>] = <Wert> <br />
	  													- <ID> ist der beim Absenden des Formulars übertragene Wert 
	  													- <Wert> ist der in der Auswahlliste dargestellte Wert
	  													- Die zurückgegebenen Daten können mit einer for-in-Schleife durchlaufen werden => for (var id in arValue) { alert(arValue[id]); }
	*/
	function chtml_getSwingList_getUnselectedValues(sContainerID) {
		return chtml_getSwingList_getValues(sContainerID, 'not_select_list');
	}  
	
	
	
 	/**
	  @brief 	  Fügt einer der beiden Auswahllisten einer Schaukelliste neue Werte hinzu
	  					<b>Achtung: </b> Enthält eine der beiden Listen der Schaukelliste bereits einen Eintrag mit derselben ID, 
	  					wird unabhängig von der Zielliste ausschließlich der dargestellte Text zur ID verändert. 
	  @ingroup 	pb59_getSwinglist_noapi
	  @param  	sContainerID    ID der Schaukelliste
		@param		arValue					Array (Objekt) mit den hinzuzufügenden Werten im Format arValue[<ID>] = <Wert> <br />
															- <ID> ist der beim Absenden des Formulars übertragene Wert 
															- <Wert> ist der in der Auswahlliste dargestellte Wert	  													
	  @param		sList						Betreffende Liste. Valide Werte:
	  													- "select_list" für Liste, in der zugeordnete Werte stehen
	  													- "not_select_list" für Liste, in der nicht zugeordnete Werte stehen
		@return		false im Fehlerfall | true im Erfolgsfall	  													
	*/
	function chtml_getSwingList_addNewValues(sContainerID, arValue, sList) {

		// ---- Parameter validieren ---- 
		if ((sList != 'select_list') && (sList != 'not_select_list')) { return false; }		 
		if (sList == 'select_list') { var sListOther = 'not_select_list'; } else { var sListOther = 'select_list'; }
		if (typeof(arValue) != 'object') { return false; } 

		// ---- Objekte beider Auswahllisten und deren versteckter Listen erstellen ----
		var objList = 						$(jqId(sContainerID)+'_'+sList).get(0);  								if (objList === undefined) { return false; }
		var objListOther = 				$(jqId(sContainerID)+'_'+sListOther).get(0);  					if (objListOther === undefined) { return false; }
		var objListHidden = 			$(jqId(sContainerID)+'_'+sList+'_hidden').get(0);  			if (objListHidden === undefined) { return false; }
		var objListOtherHidden = 	$(jqId(sContainerID)+'_'+sListOther+'_hidden').get(0);  if (objListOtherHidden === undefined) { return false; }
		var arLists = [objList, objListOther];
		var arListsHidden = [objListHidden, objListOtherHidden];
		
  	// ---- Die IDs der neuen Listeneinträge in Array überführen ----
  	var arIDs = new Array;  	
  	for (var id in arValue) {
  		arIDs.push(id);  		
  	}
		
		// ---- Prüfen, ob die beiden Listen bereits Einträge enthalten, die neu eingestellt werden sollen ----
		// -> Ist das der Fall, wird ausschließlich die dargestellte Bezeichnung des Eintrages geändert
		var arValuesIgnore = new Object();
		for (var i = 0; i < arLists.length; i++) {															// Beide Listen der Schaukelliste nacheinander verwenden
		  for (var x = 0; x < arLists[i].options.length; x++) {										// Einträge der aktuellen Auswahlliste durchlaufen  
		  	if (arValue[(arLists[i].options[x].value)] != undefined) { 							// Wenn ID des aktuellen Eintrages in Array mit neuen Werten enthalten ist:
		  		arLists[i].options[x].text = arValue[(arLists[i].options[x].value)];				// Text des Eintrages der sichtbaren Liste ändern
		  		arListsHidden[i].options[x].text = arValue[(arLists[i].options[x].value)];	// Text des Eintrages der unsichtbaren Liste ändern	  			
		  		arValuesIgnore[(arLists[i].options[x].value)] = true;										// Eintrag zur Menge zu ignorierender Werte hinzufügen
	  		}
	  	}
		}
		
		// ---- Neue Werte durchlaufen und der spezifizierten Liste hinzufügen ----
		for (var id in arValue) {
			if (arValuesIgnore[id] != undefined) { continue; }	// Datensatz überspringen, falls dieser bereits in einer der beiden Listen enthalten ist und ignoriert werden soll
			objList.options[objList.options.length] = new Option(arValue[id], id, false, false);	// Eintrag der sichtbaren Zielliste hinzufügen			
			objListHidden.options[objListHidden.options.length] = new Option(arValue[id], id, false, true); // Eintrag der unsichtbaren Zielliste hinzufügen und selektieren			
		}
				
		// ---- Alle übergebenen Werte in Zielliste verschieben, falls diese dort noch nicht vorhanden sind ----
		chtml_getSwingList_moveValues(sContainerID, sListOther, arIDs, false);
		
		return true;
	}	  
		    

															
 	/**
	  @brief 	  Fügt der Liste mit zugeordneten Werten einer Schaukelliste neue Werte hinzu
	  					<b>Achtung: </b> Enthält eine der beiden Listen der Schaukelliste bereits einen Eintrag mit derselben ID, wird unabhängig von der Zielliste ausschließlich der dargestellte Text zur ID verändert. 
	  @ingroup 	pb59_getSwinglist_api
	  @param  	sContainerID    ID der Schaukelliste
		@param		arValue					Array (Objekt) mit den hinzuzufügenden Werten im Format arValue[<ID>] = <Wert> <br />
															- <ID> ist der beim Absenden des Formulars übertragene Wert 
															- <Wert> ist der in der Auswahlliste dargestellte Wert	  													
		@return		false im Fehlerfall | true im Erfolgsfall	  													
	*/
	function chtml_getSwingList_addNewSelectedValues(sContainerID, arValue) {
		return chtml_getSwingList_addNewValues(sContainerID, arValue, 'select_list');
	}	  
			
	
	
 	/**
	  @brief 	  Fügt der Liste mit nicht zugeordneten Werten einer Schaukelliste neue Werte hinzu
  						<b>Achtung: </b> Enthält eine der beiden Listen der Schaukelliste bereits einen Eintrag mit derselben ID, wird unabhängig von der Zielliste ausschließlich der dargestellte Text zur ID verändert. 
  	@ingroup 	pb59_getSwinglist_api
  	@param  	sContainerID    ID der Schaukelliste
		@param		arValue					Array (Objekt) mit den hinzuzufügenden Werten im Format arValue[<ID>] = <Wert> <br />
															- <ID> ist der beim Absenden des Formulars übertragene Wert 
															- <Wert> ist der in der Auswahlliste dargestellte Wert	  													
		@return		false im Fehlerfall | true im Erfolgsfall	  													
	*/
	function chtml_getSwingList_addNewUnselectedValues(sContainerID, arValue) {
		return chtml_getSwingList_addNewValues(sContainerID, arValue, 'not_select_list');
	}	  	
	
	
 	/**
	  @brief 	  Entfernt Einträge aus einer Schaukelliste, unabhängig davon in welcher der beiden Auswahllisten sich die Einträge befinden 	  					 
	  @ingroup 	pb59_getSwinglist_api
	  @param  	sContainerID    ID der Schaukelliste
		@param		arValue	  			Array mit den IDs zu verschiebender Listeneinträge im Format arValue[] = <ID> (<ID> bezieht sich auf den Index des Parameters $arValue der PHP-Funktion)		
		@return		false im Fehlerfall | true im Erfolgsfall	  													
	*/
	function chtml_getSwingList_deleteValues(sContainerID, arValue) {
	
		// ---- Parameter validieren ---- 
		if ((!arValue) || (!arValue.length)) { return false; }	
		
		// ---- Objekte beider Auswahllisten und deren versteckter Listen erstellen ----
		var objList 						= $(jqId(sContainerID)+'_select_list').get(0);  					if (objList === undefined) { return false; }
		var objListOther 				= $(jqId(sContainerID)+'_not_select_list').get(0);				if (objListOther === undefined) { return false; }
		var objListHidden 			= $(jqId(sContainerID)+'_select_list_hidden').get(0);  		if (objListHidden === undefined) { return false; }
		var objListOtherHidden 	= $(jqId(sContainerID)+'_not_select_list_hidden').get(0); if (objListOtherHidden === undefined) { return false; }
		var arLists = [objList, objListOther];
		var arListsHidden = [objListHidden, objListOtherHidden];

  	// ---- Die IDs der zu entfernenden Werte über Index eines assoziativen Arrays verfügbar machen ----
  	var arIDs = new Object();
  	for (var x = 0; x < arValue.length; x++) {
  		arIDs[(arValue[x])] = true;			  		
  	}
		
  	// ---- Die Einträge der beiden sichtbaren Auswahllisten durchlaufen und zu entfernende Einträge löschen ---- 
		for (var i = 0; i < arLists.length; i++) {										// Beide Listen der Schaukelliste nacheinander verwenden
		  for (var x = 0; x < arLists[i].options.length; x++) {					// Einträge der aktuellen Auswahlliste durchlaufen  
		  	if (arIDs[(arLists[i].options[x].value)] != undefined) { 		// Wenn ID des aktuellen Eintrages in Array mit zu entfernenden IDs enthalten ist:
		  		arLists[i].options[x] = null;																	// Eintrag aus sichtbarer Liste entfernen
		  		arListsHidden[i].options[x] = null;														// Eintrag aus unsichtbarer Liste entfernen
		  		x--;
	  		}
	  	}
		}
		
		return true;
	}	  	
			
		
	/**
	  @brief		Gibt individuelle Einstellungen einer Schaukelliste zurück
	  @ingroup 	pb59_getSwinglist_noapi
	  @param  	sContainerID    					ID der Schaukelliste
	  @return 	Object.cfid								ID zur Zuordnung von Callbackfunktionen
	*/
	function chtml_getSwingList_getSettings(sContainerID) {

		var objRet = new Object();
		
		// Node-Objekt der Schaukelliste ermitteln
    var objSL = $(jqId(sContainerID));  if (objSL.get(0) === undefined) { return null; } else { objSL = objSL.get(0); }        
    
    // ID für Callbackfunktionen ermitteln
  	var objSetting_cfid = $('> input.chtml_swinglist_setting_cfid', objSL).get(0);  	
  	if (objSetting_cfid === undefined) { 
  		objRet.cfid = ''; 
  	} else {
  		objRet.cfid = objSetting_cfid.value;
  	}
  	
  	return objRet;		
	}		
	
	 
	/**
	 	@brief		Funktion, die PRE- und POST-Events einer Schaukelliste behandelt 
	 	@ingroup 	pb59_getSwinglist_noapi		  
	  @param 		sContainerID			ID der Schaukelliste	  
	  @param 		sCallbackAction		Aktion für die eine Callbackfunktion ausgeführt werden soll. (Gültige Werte: select, unselect)	  											
		@param		sPrePost					Kennzeichnung, ob es sich um ein PRE- oder POST-Ereignis handelt (Gültige Werte: PRE, POST)
    @param  	arParams					(optional) Zusätzliche Parameter in Abhängigkeit des Schaltflächentyps
																- Für Pre- und Post-Ereignisse der Callbackactions select und unselect:
																	- arParams.arValue	Array mit den IDs zu selektierender oder deselekterender Datebsätze im Format arParams.arValue[] = <ID>
	 	@return		false im Fehlerfall | true im Erfolgsfall	
	function chtml_getSwingList_executeCF(sContainerID, sCallbackAction, sPrePost, arParams) {
		// Dynamische Generierung zur Laufzeit durch CHtml::getSwingList()
	}
	*/
