/**
 * Die Klasse ClassDialog ist eine sehr flexible JS-Klasse um modale Popup-Dialoge anzuzeigen. Der Begriff "modal" ist in diesem Kontext
 * nicht ganz richtig, da es in JavaScript nicht moeglich ist, den Script-Fluss ohne Weiteres anzuhalten und erst nach der Eingabe vom
 * Benutzer weiter laufen lassen, wie man es von echten modalen Dialogen kennt (alert, confirm, prompt). Aus diesem Grund wird die
 * Unterbrechung des Scripts simuliert, in dem die Verarbeitung der Benutzereingaben in einer callback-Funktion weitergefuert wird. Der 
 * Konstrukter der Klasse erwartet folgende Parameter:
 * 
 * String dialogId - Die Id des div-Elementes, das zum modalen Dialog gemacht wird. Es muss unbeding ein div-Element sein, das auserden die
 *                   Eigenschaften display:none und position:absolute besitzen muss.
 * 
 * Array  params   - Ein Array von Objekten. Jedes Objekt repraesentiert einen Button (anklickbares Element) innerhalb des modalen Dialogs,
 *                   den Rueckgabewert, der beim Klick auf einen Button erzeugt wird und die callback-Methode, die beim Klick auf den Button
 *                   aufgerufen wird. 
 *                   
 * Boolean 
 *    supressShow  - Ein boolscher Wert der angibt, ob der Dialog direkt beim Erzeugen angezeigt werden soll, oder erst spaeter durch den 
 *                   Aufruf der Methode show(). Dieser Parameter is optional. Wenn nichts angegeben wird, wird angenommen, dass der Dialog
 *                   direkt beim Erzeugen eines ClassDialog-Objekts angezeigt werden soll. Bei true wird die Anzeige unterdrueckt. Der Wert
 *                   false hat die gleiche Wirkung, wie ein leerer Parameter
 * 
 * Beispiel: Wir haben ein div-Element mit der ID 'save_popup_div', das ein Input-Feld (ID = 'save_popup_input') und zwei
 *           Buttons hat (OK und Abbrechen, die IDs sind entsprechend 'save_popup_ok' und 'save_popup_abort'). Dieses div wollen wir
 *           zu einem modalen Dialog machen. Beim Klicken auf den Button 'Abbrechen' soll der Dialog einfach geschlossen werden. Beim
 *           Klick auf 'OK' soll der Inhalt des Input-Feldes an die Methode saveProduct() des Objekts controller weitergereicht werden 
 *           und der Dialog geschlossen werden. Dieses Verhalten erzeugen wir, indem wir ein Objekt der Klasse ClassDialog wie folgt erzeugen:               
 *           
 *           var dialog = new ClassDialog('save_popup_div', 
 *                                        new Array({type:'button', id:'save_popup_abort', returnValue:false, close:true},
 *                                                  {id:'save_popup_ok', returnValue:'value(save_popup_input)', callback:'controller.saveProduct'}),
 *                                        false
 *                                        );
 *           
 *           Der erste Parameter ist die ID des div-Elements, das zu einem modalen Dialog gemacht werden soll. Danach folgt ein Array mit zwei
 *           anonymen Objekten, die die zwei Buttons repraesentieren. Zunaechst schauen wir uns das erste Objekt genauer an. Darin wurden
 *           mehrere Attribute definiert. Das Attribut 'type' gibt den Typ des anlickbaren Elements an, der in 99% aller Faelle den Wert 
 *           'button' haben wird und aus diesem Grund optional gemacht wurde. Wenn kein Typ angegeben wird, nimmt die Klasse an, dass es sich
 *           um den Typ 'button' handelt. In der ersten Version der Klasse gibt es sogar gar keine andere Typen. Diese Typunterscheidung wurde
 *           eingebaut um spaeter eventuelle Erweiterungen der Klasse einfacher zu gestallten. 
 *           Das zweite Attribut 'id' gibt die ID des Buttons an, deswegen muss jeder Button immer eine EINDEUTIGE ID haben. Das Attribut
 *           'returnValue' gibt an, welchen Wert der Dialog beim Klick auf diesen Button liefern soll. Der Wert dieses Attributs auf vier 
 *           verschiede Arten angeben. Die zwei einfachsten Varianten sind die Angabe eines boolschen Werts oder eines Strings, die beim Klick 
 *           auf den ensprechenden Button an die callback-Funktion weitergereicht werden. Das sieht dann wie folgt aus:
 *               
 *           {type:'button', id:'save_popup_abort', returnValue:true, close:true}
 *           oder
 *           {type:'button', id:'save_popup_abort', returnValue:false, close:true}
 *           oder
 *           {type:'button', id:'save_popup_abort', returnValue:'irgendein wert', close:true} 
 * 
 *           In den beiden ersten Faellen wird true bzw. false an die callback-Funktion weitergereicht. Im letzten Fall wird der String
 *           'irgendein Wert' verwendet.
 *           
 *           Wenn man den Wert eines input-Feldes innerhalb des Dialogs als Return-Wert haben moechte (z.B. Simulation eines Prompt-Dialogs), 
 *           wird es schon komplizierter. Aber auch dafuer gibt es eine einfache Moeglichkeit. Dies wird erreicht in dem man folgende Syntax 
 *           verwendet:
 * 
 *           {type:'button', id:'save_popup_abort', returnValue:'value("input_feld_id")', close:true}
 * 
 *           Das Wichtigste dabei ist der String 'value("input_feld_id")', der bedeutet, dass der Wert (Value) des Feldes mit der Id 
 *           'input_feld_id' an die callback-Funktion weitergereicht wird.
 * 
 *           Die letzte und zugleich komplizierteste Methode Rueckgabewerte zu bestimmen wird wahrscheinlich eher selten gebraucht. Es handelt
 *           sich um Faelle bei denen der Rueckgabewert an bestimmte Bedingungen geknuepft ist. Zum Beispiel kann man sich ein Dialog vorstellen,
 *           der mehrere Buttons, input-Felder und evtl. auch Checkboxen enthaelt. Der Rueckgabewert soll vom Text in den input-Feldern und den
 *           Checkboxen abhaengen. In diesem Fall brauchen wird eine separate Funktion gebraucht, die die Eingaben ausliest und den Rueckgabewert
 *           berechnet. Die Funktion kann eine "lose" Funktion mit beliebigen Parametern sein oder auch eine Klassen-Methode. Um diese Funktion
 *           an den Rueckgabewert eines Buttons zu "binden" wird folgende Syntax verwendet:
 * 
 *           {type:'button', id:'save_popup_abort', returnValue:'func("functionName", "param1", "param2", "paramN")', close:true}
 *     
 *           Der Ausdruck 'func("functionName", "param1", "param2", "paramN")' bedeutet, dass beim Klick auf den Button die Funktion mit dem
 *           Namen "functionName" mit den Parametern "param1", "param2" und "paramN" aufgerufen und der zurueckgelieferte Wert in die 
 *           Result-Variable des Dialog-Objekts geschrieben. Erst danach wird die callback-Methode aufgerufen. 
 * 
 *           Der letzte Attribut des anonymen Objects heisst 'close' und kann die Werte true oder false haben. Dieses Attribut ist optional, d.h. 
 *           wenn es nicht definiert wird, gilt das als ein true. Der Wert des Attributs bestimmt, ob der Dialog beim Klicken auf das entsprechende
 *           Element geschlossen wird oder nicht.     
 *  
 * @author Alfred Brose
 * @version 1.0
 * @requires jQuery http://jquery.com
 * @requires jqModal http://dev.iceburg.net/jquery/jqModal/
 */
function ClassDialog(dialogId, params, supressShow){
    var element = null;
    var result = null;
    //Gibt an, ob Exceptions ausgegeben werden sollen
    var debugOn = false;
    //In dem Attribut elements werden alle registrierte Elemente als Objekte gespeichert
    var elements = new Object();

    // I'm adding browser detection because the popups don't seem to work
    // with IE 6.
    var browserName;
    var browserVersion;
    var browserDetector = new ClassBrowserDetector();
    browserName = browserDetector.getBrowserName();
    browserVersion = browserDetector.getBrowserVersion();
    
    //Bestimmt ob der Dialog automatisch nach dem Aufruf des Konstruktors angezeigt werden soll, oder erst nach dem Aufruf der Methode show()
    //Keine Angabe oder false - Der Dialog wird automatisch angezeigt
    //true                    - Der Dialog wird erst nach Aufruf der Methode show angezeigt 
    var autoShow = !Boolean(autoShow);
    
    //Handler auf die Funktion __destruct() zur Uebergabe an jqm
    var __destruct = function(hash){destruct(hash)};   
    
    //Konstruktor-Aufruf 
    construct();

    /******************************** Methoden-Definitionen *************************************/
    
    /**
     *Zeigt den Dialog an
     */ 
    this.show = function(){
        if(element != null){     
          element.jqmShow();
        }
    }
    
    /*
     * Versteckt den dialog
     */
    this.hide = function(){
    	if(element != null){
    		element.jqmHide();
    	}
    }


	function showCenterDiv(Yheight, Xwidth, divid) { 
        
		// determine how much the user scrolled 
		var scrolledX, scrolledY, documentX, documentY; 
		if( self.pageYoffset ) { 
			scrolledX = self.pageXoffset; 
			scrolledY = self.pageYoffset; 
		} else if( document.documentElement && document.documentElement.scrollTop ) { 
			scrolledX = document.documentElement.scrollLeft; 
			scrolledY = document.documentElement.scrollTop; 
		} else if( document.body ) { 
			scrolledX = document.body.scrollLeft; 
			scrolledY = document.body.scrollTop; 
		} 

		documentX = (self.innerWidth || (document.documentElement.clientWidth || document.body.clientWidth));
		documentY = (self.innerHeight || (document.documentElement.clientHeight || document.body.clientHeight));

		// determine the coordinates of the center of the browser's window 
		var centerX, centerY; 
		if( self.innerHeight ) { 
			centerX = self.innerWidth; 
			centerY = self.innerHeight; 
		} else if( document.documentElement && document.documentElement.clientHeight ) { 
			centerX = document.documentElement.clientWidth; 
			centerY = document.documentElement.clientHeight; 
		} else if( document.body ) { 
			centerX = document.body.clientWidth; 
			centerY = document.body.clientHeight; 
		} 
		
		var leftoffset = scrolledX + (centerX - Xwidth) /2; 
		var topoffset = scrolledY + (centerY - Yheight) /2; 
		
		if(Yheight > documentY) {
			topoffset = (Yheight - documentY) / 8;
		}

		var o = document.getElementById(divid);  	
		var r = o.style;
 
		r.position='absolute'; 
		r.top = topoffset + 'px'; 
		r.left = leftoffset + 'px';
		r.margin = 0;

		return true;
	}


    /**
     * Destruktor; Setzt alle Attribute zurueck
     */
    function destruct(hash){
       //Leere die Attribute
       result = null;
       element = null;
       //elements = new Object();    
       hash.w.hide(); 
       hash.o.remove();
    };
    
    /**
     * Konstruktor
     */
    function construct(){
      elements = new Object();
	    //Pruefen ob dialogId auch wirklich eine ID eines existenten div-Elements ist
	    if($('div#'+dialogId).size() > 0){
	       //Wenn ja, wird aus dem Div-Element mit der ID dialogId ein modales Element gemacht. 
	       //siehe jQuery-Plugin jqModal.
	       element = $('div#'+dialogId);
	       
	       var height = element.height();
	       var width  = element.width();
	       
		   showCenterDiv(height,width,dialogId);
				       
	       //if(browserName == 'IE' && browserVersion<=6) {
	       // element.jqm({overlay: 0, modal:true, css:{cursor:'pointer'}, onHide:__, overlayClass:''});
	       // element.css('background-color','#dfdfdf');
	       //}
	       //else {
	        element.jqm({overlay: 60, modal:true, css:{cursor:'pointer'}, onHide:__destruct, overlayClass:'content_darkened'});
	       //}
	    }else{
	       return false;
	    }
    

	    //Pruefen, ob params ein nicht leeres Array ist
	    if(params.length > 0){
	      for(var i=0; i<params.length; i++){
	        var param = params[i];
	        //Pruefen ob das Element im params-Array ein Objekt ist und die Id im DOM vorhanden ist  
	        if(typeof param == 'object' && $('#' + param.id).size() > 0){
	           //Speichere das Element im elements-Attribut
	           //Ab hier operieren wir nicht mehr auf dem temporaeren params-Object (Parameter der Klasse), sondern mit den Objekten im
	           //Attribut elements
	           elements[param.id] = param;
	
	           
	           //Wenn type nicht definiert wurde, wird der Typ auf den default-Wert 'button' gesetzt
	           if(!elements[param.id].hasOwnProperty('type')){
	               elements[param.id].type = "button";
	           }
	           
	           //Wenn close nicht definiert wurde, wird der close auf den default-Wert 'true' gesetzt
	           if(!elements[param.id].hasOwnProperty('close')){
	               elements[param.id].close = true;
	           }
	           
	           //Abhaengig vom Typ des Elements, werden die auszufuerenden Aktionen an die Elemente eingehaengt
	           switch(elements[param.id].type){
	               case 'button':
	                 //Pruefen, ob beim Klick auf diesen Button das Dialog-Fenster geschlossen werden soll
	                 if(elements[param.id].close == true){
	                     $('#' + param.id).one("click", function(){
	                       try{
	                         element.jqmHide();
	                         if(browserName == 'IE') {
	                             $("select").css("display","");   //IE6 work around
	                         }                                                      
	                       }catch(e){
	                          if(debugOn){
	                              throw e;
	                          } 
	                       }
	                      });
	                 }
	                 //Einhaengen der Aktion beim Klick auf diesen Button, die den Return-Wert bestimmt; 
	                 if(elements[param.id].returnValue == true || elements[param.id].returnValue == false){
	                   //Wenn returnValue true oder false ist wird ensprechend true oder false zurueckgegeben
	                   
	                   //11.12.2008 alfredftk: Aenderung der Bind-Methode von one auf bind. k�nnte evtl. Probleme bereiten
	                   $('#'+param.id).bind("click", function(){
	                                                 result = elements[this.id].returnValue;
	                                                 }); 
	                 }else{
	                   //Ansonsten muss ueberprueft werden, welche Art von Return erzeugt werden soll
	                   //dazu wird der substring bis zur ersten oefnenden Klammer ausgelesen. Das Element am Index 1 ist dann der gesuchte Typ
	                   //Der Typ kann einer der folgenden Strings sein
	                   //func   - bedeutet, dass eine Funktion den Rueckgabewert liefert. Voller string: func(functionName, param1, param2, param3)
	                   //value  - bedeutet, dass der Value des Elementes mit der Id, die mituebergeben wird, als Rueckgabewert gesetzt wird
	                   //         voller string: value('elementId')
	                   var returnTypeRegEx = /(^.*)\(/;
	                   try{
	                     elements[param.id].returnType = returnTypeRegEx.exec(elements[param.id].returnValue)[1];
	                     //Auslesen des Parameterstrings der Rueckgabefunktion
	                     //Die Parameter sind im String zwischen den zwei Klammern angegeben
	                     var returnParamsRegEx = /\((.*)\)$/;
	                     elements[param.id].returnParams = returnParamsRegEx.exec(elements[param.id].returnValue)[1];
	                   } catch(e){
	                     elements[param.id].returnType = 'string';
	                   }
	                   
	                   switch(elements[param.id].returnType){
	                       case 'func':
	                         //Der Funktionsname ist der Teil bis zu ersten auftretenden Komma, also muss er von den restlichen Parametern
	                         //getrennt werden
	                         //Funktionsname bestimmen
	                         
	                         //11.12.2008 alfredftk: Jetzt koennen auch Funktionen ohne Parameter gebindet werden. Dazu ueberbpruefen wir
	                         //ganz am anfang ob der Parameterstring ein Komma enthaellt und verfahren dann entsprechen weiter.
	                         //wenn ein Komma vorhanden ist, fahren wir wie gewoehnlich fort, ansonsten werden der Funtionsname und Parameter
	                         //anders gesetzt
	                         if(elements[param.id].returnParams.match(/,+?/)) {
	                         var functionNameRegEx = /(^.*?),/;
	                         elements[param.id].functionName = functionNameRegEx.exec(elements[param.id].returnParams)[1];
	                         
	                         //Fnktionsname abschneiden
	                         elements[param.id].functionParams = elements[param.id].returnParams.replace(/(^.+?,)/, '');
	                         
	                         //Funktionsname von evtl. Anfuerungszeichen befreien
	                         elements[param.id].functionName = elements[param.id].functionName.replace(/"|'/gi, "")
	                         }
	                         else {
	                         	//Funktionsname ist der gesamte Parameterstring
	                         	elements[param.id].functionName = elements[param.id].returnParams;
                            
                              //Parameter sind leer
                              elements[param.id].functionParams = "";
                            
                              //Funktionsname von evtl. Anfuerungszeichen befreien
                              elements[param.id].functionName = elements[param.id].functionName.replace(/"|'/gi, "")
	                         }
	                         
	                         
	                         //Funktion aufrufen
	                         //11.12.2008 alfredftk: Aenderung der Bind-Methode von one auf bind. k�nnte evtl. Probleme bereiten
	                             $('#'+param.id).bind("click", function(){ 
	                                                              try{
	                                                                result = eval(elements[this.id].functionName + '(' + elements[this.id].functionParams + ')');
	                                                                //Pruefen, ob result den Typ boolean oder string hat. Wenn nicht, wird eine Exception geworfen
	                                                                //alert(typeof result);
	                                                                if(typeof result == 'string'){
	                                                                    result = "'" + result + "'";
	                                                                }else if(typeof result == 'boolean'){
	                                                                    //do nix                                                                   
	                                                                }else{
	                                                                    throw "The return type of the function " + elements[this.id].functionName + "() should be boolean or string!!";
	                                                                }
	                                                              }catch(e){
	                                                               if(debugOn){
	                                                                 throw e;
	                                                               }
	                                                              }
	                                                            });
	                       break;
	                       
	                       case 'value':
	                         //Die ID des Elementes, dessen Wert zurueckgegeben werden soll wird ausgelesen
	                         //und evtl. vorhandene Anfuerungszeichen oder Hochkommata abgeschnitten
	                         elements[param.id].elementID = elements[param.id].returnParams.replace(/"|'/gi, "");
	                         
	                         //Pruefen, ob es ein Element mit dieser ID im DOM gibt
	                         if($('#' + elements[param.id].elementID).size() > 0){
	                         	//11.12.2008 alfredftk: Aenderung der Bind-Methode von one auf bind. k�nnte evtl. Probleme bereiten
	                            $('#'+param.id).bind("click", function(){
	                                  //Der Wert des input-Feldes muss als String mit Hochkommatas "verpackt" werden, damit es spaeter keine
	                                  //Probleme bei der Uebergabe an eval() gibt
	                                  result = "'" + $('#' + elements[this.id].elementID).val() + "'"; 
	                                });   
	                         }else{
	                            //do nix
	                         }
	                       break;
	                       
	                       default:
	                         //Der return-Typ ist weder eine Funktion, noch der Wert eines Feldes. Also wird angenommen, dass der gesamte 
	                         //String-Wert des Attributs returnValue als return-Wert verwendet werden soll.
	                         //11.12.2008 alfredftk: Aenderung der Bind-Methode von one auf bind. k�nnte evtl. Probleme bereiten
	                         $('#'+param.id).bind("click", function(){
	                                  //Evtl. vorhandene Anfuerungszeichen oder Hochkommata werden maskiert.
	                                  result = "'" + elements[this.id].returnValue.replace(/("|')/gi, "\\$1") + "'";
	                                }); 
	                       break;
	                   }
	                 }
	                 
	                 //Einhaengen der Callback-Methode, die nach Beenden des Dialogs aufgerufen wird.
	                 if(typeof elements[param.id].callback == 'string'){
	                   $('#'+param.id).one("click", function(event){
	                       try{
	                         var func = new Function(elements[this.id].callback + '(' + result + ')');
	                         func();
	                       }catch(e){
	                         if(debugOn){
	                             throw e;
	                         }
	                       }
	                           
	                     });
	                 }  
	               break;
	               
	               default:
	               //do nix
	               break;
	           }
	        }else{
	          continue;
	        }    
	      } 
	    } 
	    //Wenn autoShow true ist, wird der Dialog am Ende des Konstruktors angezeigt. Ansonsten muss die Methode show von aussen aufgerufen werden
	    if(autoShow){
	    	 if(browserName == 'IE') {
	           $("select").css("display","none");
	           $("select.popup").css("display","");
	       }      
	       element.jqmShow();
	    }
	    return true;
    }// Ende __construct   
	    
}


/**
 * ClassBrowserDetector erknnt den eingesetzten Browser-Typ und Version
 */
function ClassBrowserDetector(){
  //Private Attribute
  var userAgent = navigator.userAgent;
  var browserName;
  var browserVersion;
  
  if(userAgent.match('MSIE')){
      browserName = 'IE';
      //Bestimmen der Version des IE. Dazu wird die Ausgabe von navigator.userAgent 
      //in einzelne Teile zerlegt, getrennt durch ';'
      arrParts = userAgent.split(';');
      //Der Teil am Index 1 sieht folgendermassen aus: MSIE X.X wobei X eine Zahl ist.
      //um die Version herauszufinden muss der String MSIE abgeschnitten werden.
      version = arrParts[1].split('MSIE');
      //Am Index 1 in version ist die richtige Versionsnummer gespeichert.
      browserVersion = version[1];
      
  }else if(userAgent.match('Firefox')){
      browserName = 'Firefox';
      //Im Firefox ist die Versionsnummer ganz am Ende in userAgent angegeben
      var arrParts = userAgent.split('/');
      //Hole das letzte Element
      browserVersion = arrParts[arrParts.length-1];
      
  }else if(userAgent.match('Opera')){
      browserName = 'Opera';
      //Im Opera muss man den userAgent-String zwei mal teilen
      arrParts = userAgent.split('(');
      version = arrParts[0].split('/');
      browserVersion = version[1];
  }

  //Public methoden
  this.getBrowserName = function(){
    return browserName;
  };
  
  this.getBrowserVersion = function(){
    return browserVersion;
  };
  
}