/* ****************************************************************************************** *
 * Das Script kann frei verwendet werden, dieser Kommentar sowie die Nennung des Nicks
 * muss jedoch erhalten bleiben.
 *
 *                                                                           (c) Quaese, 2007
 * ****************************************************************************************** */


/* ****************************************************************************************** *
 * Slider-Objekt
 * Die Geschwindigkeit des Sliders passt sich an die Position des Mauszeigers innerhalb des
 * Slide-Objektes an. D.h. je weiter an die Ränder, desto schneller die Bewegung
 * ****************************************************************************************** */
function qpSlider(strID, blnHref, objClasses, arrBilder){
  // Styleabschnitt
  this.SliderPadding = "3px 10px 3px 10px";               // Padding in Pixeln (Angabe wie in CSS, Bsp: 12px oder 12px 10px oder 12px 10px 6px oder 10px 11px 12px 13px)

  // Styles - Bildobjekt
  this.imgStyle = {};
  // Falls eine Klasse für die Bilder übergeben wurde
  if((typeof objClasses.classImg != "undefined") && (objClasses.classImg != "")){
    this.imgStyle.className = objClasses.classImg;
  }else{
  	// Styleeigenschaften definieren - Eigenschaften müssen in JS-Syntax angegeben werden (z.B. borderTopColor)
    this.imgStyle.border  = "1px solid #ccc";
    this.imgStyle.padding = "3px 3px";
  }

  // Styles - Linkobjekt
  this.hrefStyle = {};
  // Falls eine Klasse für die Links übergeben wurde
  if((typeof objClasses.classHref != "undefined") && (objClasses.classHref != "")){
    this.hrefStyle.className = objClasses.classHref;
  }else{
  	// Styleeigenschaften definieren - Eigenschaften müssen in JS-Syntax angegeben werden (z.B. borderTopColor)
    this.hrefStyle.border  = "0px solid #ccc";
    this.hrefStyle.padding = "0px 3px";
    this.hrefStyle.textDecoration = "none";
  }

	this.name = "slider";

	this.id = strID;                                        // ID des SliderHolders
  this.objSlideHolder = document.getElementById(this.id); // SliderHolder-Objekt
  this.objSlideElem = null;                               // Inneres Sliderelement (wird verschoben)
  this.intSlideDistance = 0;                              //
  this.intOffsetParent = 0;                               // Absolutes linksseitiges Offset

  this.arrBilder = arrBilder;                             // Array das die Quellen der Bilder der SlideShow enthält
  this.arrImg    = new Array();                           // Mehrdimensionales Array, das die Image-Objekte und URLs für die SlideShow enthält
                                                          // [0] - Bildobjekt, [0].src - Quelle, [0].loaded - Indikator f. Ladezustand
                                                          // [1].url - URL, [1].text - Linktext
  this.blnWithHref = (blnHref != false)? true : false;    //
  this.intPreCount = 0;                                   // Zähler für bereits geladene Bilder

  this.intRaster    = 8;                                  // Anzahl der MouseOver-Bereiche
  this.intStepWidth = 1;                                  // Schrittweite beim Sliden
  this.intMilliSecs = 40;                                 // Verzögerung in Millisekunden
  this.arrRaster = new Array(this.intRaster+1);           // Rasterarray (Bereiche, in denen sich die Slidegeschwindigkeit ändert)

  this.intMouseX;                                         // X-Position der Maus im Dokument
  this.hTimer = null;                                     // Timerhandle für Timeout-Methode

  this.isIE = (document.all && !window.opera);            // Test, ob es sich um einen IE handelt

  // Preloader starten
  this.loadImg();

  // this-Objekt an closure-Variable
  var objThis = this;

  // Event-Funktionen
  this.objSlideHolder.onmouseover = function(){ objThis.handleMouseOver();};
  this.objSlideHolder.onmousemove = function(event){ objThis.handleMouseMove(event);};
  this.objSlideHolder.onmouseout  = function(){ objThis.handleMouseOut();};
}


/* ***************************************************************************************** *
 * Methode zum Initialisieren des Preloaders
 * ***************************************************************************************** */
qpSlider.prototype.loadImg = function(){
	this.intPreCount = 0;

  // Ausgangsarray mit Quellen durchlaufen
	for(var i=0; i<this.arrBilder.length; i++){
		this.arrImg[i] = new Array();
	  this.arrImg[i][0]         = new Image();                        // Neues Bildobjekt
	  this.arrImg[i][0].loaded  = 0;                                  // Indikator als nicht geladen markieren
    this.arrImg[i][0].onerror = this.doError.bind(this, {intI: i}); // Fehlerfunktion und akt. Index daran "binden"
    this.arrImg[i][0].src     = this.arrBilder[i][0];               // Quelle dazufuegen
  }

  // closure-Variable
  var objThis = this;
  // Funktion zum erneuten Erstellen des Bilderarrays zeitverzögert aufrufen
  this.hTimer = window.setTimeout(function(){objThis.reorganizeArray();}, 200);
}


/* ***************************************************************************************** *
 * Errorhandler, falls ein Bild nicht existiert (Indikator auf 2 setzen)
 * ***************************************************************************************** */
qpSlider.prototype.doError = function(objIndex){
  this.arrImg[objIndex.intI][0].loaded = 2;  // Bild als fehlerhaft markieren
}


/* ***************************************************************************************** *
 * Methode zum Initialisieren des Preloaders
 * ***************************************************************************************** */
qpSlider.prototype.reorganizeArray = function(){

  var arrHelp = new Array();
  var intHelp = 0;

  // Bisheriges Bilderarray durchlaufen
  for(var i=0; i<this.arrImg.length; i++){
  	// Falls ein Bildobjekt als fehlerhaft markiert ist
    if(this.arrImg[i][0].loaded != 2){
    	var intIndexHelp = arrHelp.length;
	    arrHelp[intIndexHelp] = new Array();
      arrHelp[intIndexHelp][0] = this.arrImg[i][0];
      arrHelp[intIndexHelp][1] = this.arrBilder[i][1];
      intHelp++;
    }
  }

  // Altes Array löschen und mit neuem Feld füllen
  this.arrImg.length = 0;
  this.arrImg = arrHelp;

  // closure-Variable
  var objThis = this;
  // Testfunktion zeitverzögert erneut aufrufen
  this.hTimer = window.setTimeout(function(){objThis.checkLoad();}, 10);
}


/* ***************************************************************************************** *
 * Preloader-Funktion zum Testen, ob die Bilder bereits geladen wurden. Sind alle Bilder
 * geladen, wird die Initialisierungs-Funktion des Sliders aufgerufen.
 * ***************************************************************************************** */
qpSlider.prototype.checkLoad = function(){
	// Falls alle Bilder geladen wurden
  if(this.intPreCount == this.arrImg.length){
  	// Init-Funktion aufrufen
    this.initSlider();
    return;
  }

  // Quellarray durchlaufen
  for(var i=0; i<this.arrImg.length; i++){
  	// Falls ein Bild nicht existiert
  	if(this.arrImg[i][0].loaded == 2){
    	this.arrImg[i][0].width = 1;   // Auf einen Wert grösser Null setzen -> nachfolgende Bedingung kann wahr werden
    }

  	// Wenn das aktuelle Bild fertig geladen wurde
  	if(!this.arrImg[i][0].loaded && (this.arrImg[i][0].width > 0)){
    	this.arrImg[i][0].loaded = 1;  // Indikator des akt. Bildes als geladen markieren
      this.intPreCount++;            // Anzahl der geladenen Bilder inkrementieren
      this.checkLoad();              // Testfunktion erneut aufrufen
      return;
    }
  }

  // closure-Variable
  var objThis = this;
  // Testfunktion zeitverzögert erneut aufrufen
  this.hTimer = window.setTimeout(function(){objThis.checkLoad();}, 10);
}


/* ********************************************************************************************************** *
 * Slide-Element initialisieren
 * Die Methode erstellt und initialisiert die Elemente, die fürs Sliden notwendig sind. Zunächst ein DIV
 * das verschoben werden kann, weiter die Bilder die enthalten sein sollen. Diese Elemente werden dann in
 * den SlideHolder eingehängt.
 * Ausserdem wird ein Raster erstellt (Anzahl in this.intRaster). Dieses wird bis zur Mitte des SlideHolders
 * berechnet, so dass am Ende doppelt so viele Teilbereiche existieren.
 * ********************************************************************************************************** */
qpSlider.prototype.initSlider = function(){
	// CSS-Eigenschaften des SlideHolders setzen
  this.objSlideHolder.style.overflow = "hidden";
  this.objSlideHolder.style.position = "relative";

/* **** [START - Slide-Elemente generieren] ***************************************************************** */
	this.objSlideElem = document.createElement("div");     // Inneres Slideelement ermitteln
  this.objSlideElem.id = this.id + "_inner";             // ID
  this.objSlideElem.style.whiteSpace = "nowrap";         // Zeilenumbruch unterbinden
  this.objSlideElem.style.padding = this.SliderPadding;  // Paddingwert

  if(this.isIE) this.objSlideElem.style.height = "1%";   // Anzeigeprobleme im IE beheben

  // Bilderarray durchlaufen
  for(var i=0; i<this.arrImg.length; i++){
  	var objImg = document.createElement("img");          // Bildobjekt erstellen
    objImg.src = this.arrImg[i][0].src;                  // Quelle zuweisen

    // Styles bzw. class zuweisen
    this.setCSS(this.imgStyle, objImg);

    // Falls die Bilder in Links eingebunden werden sollen UND eine URL übergeben wurde
    if(this.blnWithHref && (this.arrImg[i].length>1) && (typeof(this.arrImg[i][1].url)== "string") && (this.arrImg[i][1].url.length > 0)){
	    var objHref = document.createElement("a");         // Hyperlink-Objekt erstellen
	    objHref.href = this.arrImg[i][1].url;              // URL zuweisen

	    // Styles bzw. class zuweisen
    	this.setCSS(this.hrefStyle, objHref);

      // Falls ein neues Ziel für den Link angegeben wurde -> an Linkobjekt zuweisen
  		if((typeof this.arrImg[i][1].target != "undefined") && (this.arrImg[i][1].target != "")){
      	objHref.target = this.arrImg[i][1].target;
      }

	    objHref.appendChild(objImg);                       // In Hyperlink-Objekt anhängen
	    this.objSlideElem.appendChild(objHref);            // In Slideelement anhängen
    }else{
	    this.objSlideElem.appendChild(objImg);             // In Slideelement anhängen
    }
  }

  this.objSlideHolder.appendChild(this.objSlideElem);    // Slideelement in äusseren Slideholder einhängen

  // Länge des Slider-Elementes ermitteln
  this.objSlideElem.width = 0;

  // Alle enthaltenen Bilder durchlaufen und Breiten aufaddieren
  for(var i=0; i<this.objSlideElem.getElementsByTagName("img").length; i++){
  	var objHelpImg = this.objSlideElem.getElementsByTagName("img")[i];

    // Breite des Bildobjektes
  	this.objSlideElem.width += objHelpImg.offsetWidth;
    // margin-Werte der Bildobjekte
    this.objSlideElem.width += (isNaN(parseInt(this.readStyles(objHelpImg, "margin-left", "marginLeft"))))? 0 : parseInt(this.readStyles(objHelpImg, "margin-left", "marginLeft"));
    this.objSlideElem.width += (isNaN(parseInt(this.readStyles(objHelpImg, "margin-right", "marginRight"))))? 0 : parseInt(this.readStyles(objHelpImg, "margin-right", "marginRight"));

    // Falls Links enthalten sind (margin- und padding-Werte des Linkobjektes hinzuaddieren)
    if(objHelpImg.parentNode.nodeName.toLowerCase() == "a"){
    	var objHelpLink = objHelpImg.parentNode;
	  	this.objSlideElem.width += (isNaN(parseInt(this.readStyles(objHelpLink, "margin-left", "marginLeft"))))? 0 : parseInt(this.readStyles(objHelpLink, "margin-left", "marginLeft"));
      this.objSlideElem.width += (isNaN(parseInt(this.readStyles(objHelpLink, "margin-right", "marginRight"))))? 0 : parseInt(this.readStyles(objHelpLink, "margin-right", "marginRight"));
	  	this.objSlideElem.width += (isNaN(parseInt(this.readStyles(objHelpLink, "padding-left", "paddingLeft"))))? 0 : parseInt(this.readStyles(objHelpLink, "padding-left", "paddingLeft"));
      this.objSlideElem.width += (isNaN(parseInt(this.readStyles(objHelpLink, "padding-right", "paddingRight"))))? 0 : parseInt(this.readStyles(objHelpLink, "padding-right", "paddingRight"));
    }
  }

  // Zur Länge eventuelle Paddingwerte addieren
  this.objSlideElem.width += (isNaN(parseInt(this.readStyles(this.objSlideElem, "padding-left", "paddingLeft"))))? 0 : parseInt(this.readStyles(this.objSlideElem, "padding-left", "paddingLeft"));
  this.objSlideElem.width += (isNaN(parseInt(this.readStyles(this.objSlideElem, "padding-right", "paddingRight"))))? 0 : parseInt(this.readStyles(this.objSlideElem, "padding-right", "paddingRight"));

  // Distanz um die das Slide-Element nach links verschoben werden kann
  this.intSlideDistance = (this.objSlideElem.width - this.objSlideHolder.offsetWidth);

  // Zentriert positionieren
  this.objSlideElem.style.marginLeft = -Math.round(this.objSlideElem.width/2) + Math.abs(this.objSlideHolder.offsetWidth/2) + "px";
/* **** [ENDE  - Slide-Elemente generieren] ***************************************************************** */

/* **** [START - Rasterung anlegen] ************************************************************************* */
	// Teilgrösse = Rasterweite (= Hälfte Sliderbereich / Anzahl der Raster)
  var intPart = Math.round(this.objSlideHolder.offsetWidth/2) / this.intRaster;
  // Rasterarray erstellen -> enthält die Rastergrenzen
	for(var i=0; i<this.arrRaster.length; i++){
  	this.arrRaster[i] = Math.round(i * intPart);
  }
/* **** [ENDE  - Rasterung anlegen] ************************************************************************* */

/* **** [START - Absolutes linksseitiges Offset ermitteln] ************************************************** */
	// Startwert
  this.intOffsetParent = this.objSlideHolder.offsetLeft;
  // Elternelement mit Offset ermitteln
  var objHelpOffset = this.objSlideHolder.offsetParent;

  // Solange OffsetElemente existieren
  while(objHelpOffset){
  	this.intOffsetParent += objHelpOffset.offsetLeft;  // Offset aufaddieren
    objHelpOffset = objHelpOffset.offsetParent;        // Nächstes OffsetElement
  }
/* **** [ENDE - Absolutes linksseitiges Offset ermitteln] ************************************************** */
}


/* ********************************************************************************************************** *
 * Setzen der CSS-Eigenschaften bzw. zuweisen einer CSS-Klasse
 * Die Funktion weist einem Objekt die CSS-Eigenschaften zu, die im Objekt objStyle enthalten sind. Die
 * Eigenschaften des Objektes müssen dabei genauso heissen, wie die CSS-Eigenschaften in JS-Syntax.
 * Enthält das Style-Objekt eine Eigenschaft namens "className", so wird eine CSS-Klasse zugewiesen.
 *
 * Parameter: objStyle - CSS-Style-Objekt, das alle gewünschten CSS-Eigenschaften enthält
 *            objElem  - Element, welches die CSS-Eigenschaften erhalten soll
 * ********************************************************************************************************** */
qpSlider.prototype.setCSS = function(objStyle, objElem){
  for(var strEntry in objStyle){
    // Falls es sich nicht um das Klassenattribut handelt
    if(strEntry != "className")
      // Styleeigenschaft zuweisen
      objElem.style[strEntry] = objStyle[strEntry];
    else
      // Klassenname zuweisen
      objElem.className = objStyle[strEntry];
  }
}


/* ********************************************************************************************************** *
 * Slidefunktion
 * Die Methode ermittelt zunächst die aktuelle Mausposition im Slideholder bezogen auf seine Mitte
 * (-width/2 .. 0 .. width/2).
 * Anschliessend wird das Rasterarray durchlaufen und wenn die passenden Grenzen gefunden wurden, das Slide-
 * element um die entsprechende Distanz verschoben.
 * ********************************************************************************************************** */
qpSlider.prototype.doSlide = function(){

  // Auslösendes Element ermitteln (this = äusseres Slide-DIV)
  var objSrc = this.objSlideHolder;

	// Ermittelt die Mausposition ausgehend von der Mitte des SlideHolders -width/2 .. 0 .. width/2
  var intMousePos = ((this.intMouseX - this.intOffsetParent)-Math.round(objSrc.offsetWidth/2));

  // Vorzeichen ermitteln (links von Mitte < 0 => 1; sonst -1)
  var intSignum = (intMousePos >= 0)? -1 : 1;
  // Absolutbetrag der Mausposition
  var intMousePos_abs = Math.abs(intMousePos);

  // Rasterung durchlaufen
  for(var i=0; i<this.intRaster; i++){
  	// Falls sich die berechnete Mouseposition innerhalb eines Rasterintervalls befindet
    if((intMousePos_abs > this.arrRaster[i]) && (intMousePos_abs < this.arrRaster[i+1])){
    	// Neuen Positionswert berechnen (= alter Wert + i*Raster)
    	var intHelp = parseInt(document.getElementById(this.objSlideElem.id).style.marginLeft) + intSignum*i*this.intStepWidth;
      // Falls sich das SlideElement noch im Anzeigebereich befindet
    	if(((intHelp) <= 0) && (Math.abs(intHelp) <= this.intSlideDistance)){
	    	document.getElementById(this.objSlideElem.id).style.marginLeft = intHelp + "px";
      // Falls eine Grenze erreicht wurde
      }else{
      	// Slide-Element an Maximalwert setzen
    		document.getElementById(this.objSlideElem.id).style.marginLeft = (intHelp > 0)? 0+"px":-1*(this.objSlideElem.width-Math.abs(this.objSlideHolder.offsetWidth))+"px";
        // Funktion verlassen -> kein neues Timeout
        return;
      }
    }
  }

  var objThis = this;
  objThis.hTimer = window.setTimeout(function(){objThis.doSlide();}, this.intMilliSecs);
}


/* ********************************************************************************************************** *
 * MouseOver-Handler
 * Die Methode stoppt zuerst einen evtl. noch laufenden Timer und ruft dann die Slidefunktion auf.
 * ********************************************************************************************************** */
qpSlider.prototype.handleMouseOver = function(){
	if(this.hTimer != null){
  	window.clearTimeout(this.hTimer);
  }

  this.doSlide();
}


/* ********************************************************************************************************** *
 * MouseMove-Handler
 * Die Methode ermittelt die aktuelle Zeigerposition und übergibt diese an die Objektvarialbe this.intMouseX
 * ********************************************************************************************************** */
qpSlider.prototype.handleMouseMove = function(objEvent){
  if((typeof(objEvent) == 'undefined') || (objEvent == null)){
    objEvent = window.event;
  }
  // Aktuelle Mauskoordinaten bei Mausbewegung ermitteln
  this.intMouseX = document.all ? objEvent.clientX : objEvent.pageX;
}


/* ********************************************************************************************************** *
 * MouseOut-Handler
 * Die Methode löscht einen evtl. gerade laufenden Timer und stoppt damit das Sliden
 * ********************************************************************************************************** */
qpSlider.prototype.handleMouseOut = function(){
	if(this.hTimer != null){
  	window.clearTimeout(this.hTimer);
  }
}


/* ********************************************************************************************************** *
 * Hilfsmethode: Auslesen eines berechneten Styles (CSS)
 * Die Methode ermittelt die Stylewerte eines Dokumentes, nachdem es fertig geladen wurde. Also die Werte,
 * die aktuell zur Positionierung verwendet werden.
 *
 * Parameter: objElem   - (Objekt) Element, dessen Wert ermittelt werden soll
 *            strCSS    - (String) Bezeichnung der CSS-Eigenschaft in CSS-Syntax, z.B. margin-left
 *            strCSS_JS - (String) Bezeichnung der CSS-Eigenschaft in JS-Syntax, z.B. marginLeft
 * ********************************************************************************************************** */
qpSlider.prototype.readStyles = function(objElem, strCSS, strCSS_JS){
  var strReturn;

  //  Falls der Brower die Methode "getComputedStyle" kennt (W3C-DOM)
  if(window.getComputedStyle){
    strReturn = window.getComputedStyle(objElem, null).getPropertyValue(strCSS);
  }

  //  Falls der Browser die Methode "currentStyle" kennt (neuere IEs)
  else if(objElem.currentStyle){
    strReturn = objElem.currentStyle[strCSS_JS];
  }

  return strReturn;
}


/* ************* [START - bind] *********************************************** */
// Kapselt eine Funktion (= Closure)
Function.prototype.bind = function(){
  var objThis = this;
  objObject = arguments[0];

  // Argumenten-Array
  var arrArgs = new Array();
  // Falls zusätzliche Argumente übergeben wurden
  if(arguments.length > 1){
  	// Zusatzargumente durchlaufen und in Argumenten-Array schreiben
    for(var i=1; i<arguments.length; i++){
    	arrArgs[i-1] = arguments[i];
    }
  }
  // Funktion zurückgeben
  return function(){
  	// Klassenobjekt an Funktionsobjekt binden
    // und zusätzliche Argumente als Array übergeben
    objThis.apply(objObject, arrArgs);
  }
}


// Objekt erstellen
objSlider = new qpSlider( "slideHolder",                                    // ID für Sliderholder
                           true,                                            // true - Bilder verlinken, false - nur Bilder
                           {classImg: "slideImg", classHref: "slideLink"},  // CSS-Klassennamen (Bilder, Links)
                           // Bilder- u. Linkarray
                           [["beispiele/slideshow/blume_start.jpg", {}],
                            ["beispiele/slideshow/blume_01.jpg", {url: "http://www.quaese.de", text: "Quaese"}],
                            ["beispiele/slideshow/blume_02.jpg", {url: "http://www.tutorials.de", text: "tutorials.de", target: "_blank"}],
                            ["beispiele/slideshow/blume_03.jpg", {url: "http://www.quaese.de", text: "Quaese"}],
                            ["beispiele/slideshow/blume_04.jpg", {url: "http://www.tutorials.de", text: "tutorials.de", target: "_blank"}],
                            ["beispiele/slideshow/blume_05.jpg", {url: "http://www.quaese.de", text: "Quaese"}],
                            ["beispiele/slideshow/blume_06.jpg", {url: "http://www.tutorials.de", text: "tutorials.de", target: "_blank"}],
                            ["beispiele/slideshow/blume_07.jpg", {url: "http://www.quaese.de", text: "Quaese"}],
                            ["beispiele/slideshow/blume_08.jpg", {url: "http://www.tutorials.de", text: "tutorials.de", target: "_blank"}],
                            ["beispiele/slideshow/blume_ende.jpg", {}]
                           ]
                         );
