//
// Copyright (c) 2008 melko
//
// Version 1.0.1
// ChildElement vom DIV MUSS ein <UL sein sowie der weitere Inhalt im <LI
//
// Parameter:
// id             -> ID vom DIV
// scrollInterval -> Scroll Intervall in Millisekunden (Bsp. 20)
// scrollSpeed    -> Scroll Geschwindigkeit (1 bis 10)
// horizontal     -> Angabe ob Horizontal od. Vertikal (true oder false)
// side           -> Angabe wie der Scroll gehen soll (Horizontal: 'left' oder 'rigt') (Vertical: 'top' oder 'bottom')
// abstandLinks   -> nur bei Angabe von side='right' bzw. side='bottom' den style.left vom <ul Element setzen (ist negativ)
// pauseAfter     -> Angabe nach wieviel Scroll-Items (<li's) Pause gemacht wird
// pauseTime      -> Angabe wie lange die Pause gelten soll in Millisekunden (Bsp. 3000 = 3 Sek.)
//
function TeaserTape(id, scrollInterval, scrollSpeed, horizontal, side, abstandLinks, pauseAfter, pauseTime) {

	// ID vom DIV
	this.elementId = id;
	
  // Scroll Intervall in Millisekunden (Bsp. 20)
	this.scrollInterval = scrollInterval;
	
	// Scroll Geschwindigkeit (1 bis 10)
  this.scrollSpeed = scrollSpeed;
	
	// Angabe ob Horizontal od. Vertikal (true oder false)
	this.horizontal = horizontal;
	
	// Angabe eine von diesen vier, abhängig von der horizontal-Angabe ('left', 'right' / 'top', 'bottom')
	this.side = side;

  // nur bei Angabe von side='right' bzw. side='bottom' den style.left vom <ul Element setzen (ist negativ)
	this.abstandLinks = abstandLinks;

  // Angabe nach wieviel Scroll-Items (<li's) Pause gemacht wird
  this.maxNodes = pauseAfter;

  // Angabe wie lange die Pause gelten soll in Millisekunden (Bsp. 3000 = 3 Sek.)
  this.pauseTime = pauseTime;

	// Die Gesamtanzahl an gescrollten Pixeln
	this.totalScroll = 0;

	// ID vom gerade gescrolltem <li
	this.scrollIntervalId = null;

	// gerade gescrollter <li
	this.currentChild = 0;

	// Wenn Pause gemacht wird
	this.isScrollPaused = false;

	// Bei Pause, setzen wir mit dieser Angabe wieder dort fort
	this.amountToScroll = 0;

  // innerHTML's von allen <ul Elementen 
  this.LiContent = new Array();
  
  // <li Element StyleClass
  this.LiClassName = new Array();

  // der aktuellen Node
	this.actNode = 0;
	
	// Anzahl an Nodes bei Start
	this.numNodes = 0;
	
	// Initialisierung
	this.init();
};


// Holt sich das DIV-Element anhand des ID's
// und deren <ul Element als this.container sowie
// schreibt den innerHTML der <li-Elemente in das Array this.LiContent
TeaserTape.prototype.getDiv = function() {

  // hole DIV
	var tickerTapeWrapper = document.getElementById(this.elementId);

  // setze Event nur wenn keine pauseZeit angegeben ist
  if (this.pauseTime == 0){
     xb.addEvent(tickerTapeWrapper, 'mouseenter', this.pauseScroll.simpleBind(this));
	   xb.addEvent(tickerTapeWrapper, 'mouseleave', this.resumeScroll.simpleBind(this));
  }

	// Hole <ul Element, d.h. Container
	this.container = tickerTapeWrapper.getElementsByTagName('ul')[0];

  // Befüllen vom ElementArray mit dem innerHTML 
	var x = 0;
	var elChild = this.container.firstChild;
	while (elChild != null) {
		if(elChild.nodeName != '#text'){
			this.LiContent[x] = elChild.innerHTML;
			// <li Style Class merken, falls vorhanden
      if(elChild.className != ''){
			 this.LiClassName[x] = elChild.className;
			}
		  x++;
    }
		elChild = elChild.nextSibling;
	}
}


// Fügt hinten drann an das <ul Element
// neue <li Elemente mit den gespeicherten innerHTML's
TeaserTape.prototype.update = function() {
  
  	// Alle innerHTML's durchgehen
  	for(var i = 0; i < this.LiContent.length; i++) {
  		
      // neues <li-Element generieren
  		var elListItem = document.createElement('li');
      
      // StyleClass setzen falls befüllt
      if(this.LiClassName[i] != ''){
        elListItem.className = this.LiClassName[i];
      }
      
      // innerHTML in das neue <li-Element setzen
      elListItem.innerHTML = this.LiContent[i];
  
  		// das neue <li-Element zum container hinzufügen
  		this.container.appendChild(elListItem);
  	}

	// Gesamtanzahl merken
	this.numNodes = this.container.childNodes.length;
	
}


// Pausiert den Scroll
TeaserTape.prototype.pauseScroll = function() {

	if(this.scrollIntervalId) {
		window.clearInterval(this.scrollIntervalId);
		this.scrollIntervalId = null;
		this.isScrollPaused = true;
		this.currentChild--;
	}
}

// Fortsetzen des Scrolls
TeaserTape.prototype.resumeScroll = function() {
	this.isScrollPaused = false;
}

// Die Scroll-Funktion
TeaserTape.prototype.scroll = function() {

	// Falls nicht pausiert und kein anderer Scroll gerade läuft
	if(!this.isScrollPaused && !this.scrollIntervalId) {

		  // Suche das Element, welches wir scrollen sollen 
  		while(true){
        if(this.container.childNodes[this.currentChild].nodeName != '#text'){
         	break;
       	}
       	this.currentChild++;
      }
      
      var element = this.container.childNodes[this.currentChild];

		// Ermitteln der Breite des einen <li-Elements
		if(this.amountToScroll == 0) {
			this.amountToScroll = this.horizontal ? this.getElementWidth(element) : this.getElementHeight(element);	
		}

		// den aktuellen Context speichern
		var context = this;

		// Beginne mit dem Scroll
		this.scrollIntervalId = window.setInterval(function() {

      // Immer die Anzahl an Pixel (scrollSpeed) hinzufügen
			context.totalScroll = context.totalScroll + context.scrollSpeed;
      context.amountToScroll = context.amountToScroll - context.scrollSpeed;

      // falls jetziger Scroll unter 0, dann auf 0 setzen
			if(context.amountToScroll < 0){
        context.totalScroll = context.totalScroll + context.amountToScroll;
        context.amountToScroll = 0;
      }

      // Horizontal
			if(!context.horizontal) {
			   if(context.side == 'bottom'){
            context.container.style.top = (context.totalScroll) + 'px';
         }
         else {
            context.container.style.top = (-context.totalScroll) + 'px';
         }
			} 
      // Vertical
      else {
				if(context.side == 'right'){
            context.container.style.left = (context.totalScroll) + 'px';
         }
         else {
            context.container.style.left = (-context.totalScroll) + 'px';
         }
			}
      
			if(context.amountToScroll == 0) {
				window.clearInterval(context.scrollIntervalId);
				context.scrollIntervalId = null;
			}
		}, this.scrollInterval);

    // Die Anzahl erhöhen
		this.currentChild++;
		this.actNode++;
    
    // Pause
    if(this.pauseTime != 0){
		  this.pauseIfNecessary();
		}
		// Update, falls keine Pausenzeit
		else{
      this.updateIfNecessary();
    }
	}
}

// Macht die Pause, falls notwendig
// d.h: wenn actNode (aktuelle Anzahl) größer dem Parameter pauseAfter, dann Pause machen
TeaserTape.prototype.pauseIfNecessary = function() {

	if(this.pauseTime > 0 && this.actNode > this.maxNodes) {		
		// Pause
		this.pauseScroll();

		this.pausedTime = 0;
		var context = this;
		this.paused = window.setInterval(function() {
			context.pausedTime++;
			if(context.pausedTime == 2) {
			  context.update();
				window.clearInterval(context.paused);
				context.isScrollPaused = false;
				context.actNode = 0;
			}
		}, this.pauseTime);
	}
}

// Ruft nur den Update auf, wenn die Anzahl an Start-Items minus der sichtbaren Nodes
// größer-gleich dem aktuellen Node ist
// d.h: numNodes (Anzahl an Start-<li-Elementen) - pauseAfter = limit
TeaserTape.prototype.updateIfNecessary = function() {
  
  var limit = this.numNodes - this.maxNodes;

	if(this.currentChild >= limit) {
		this.update();
	}
}

// Die Init-Funktion zum initialisieren des Teasers
TeaserTape.prototype.init = function() {
	// DIV holen
  this.getDiv();
  // <li-Elemente gleich am Anfang hinzufügen
	this.update();
	
	// Pause am Start
	this.actNode = this.maxNodes + 1;
	this.pauseIfNecessary();
	
	// Angabe "side" berichtigen
	if(this.horizontal){
    if(this.side != 'left' && this.side != 'right'){
      this.side = 'left';
    }
    if(this.side == 'right'){
      this.totalScroll = this.abstandLinks;
    }
  } else {
    if(this.side != 'top' && this.side != 'bottom'){
      this.side = 'top';
    }
    if(this.side == 'bottom'){
      this.totalScroll = this.abstandLinks;
    }
  }
  
  // Scroll Interval setzen
	var timeoutCallback = this.scroll.simpleBind(this);
	window.setInterval(function() { timeoutCallback(); }, 10);
}

// Ermittelt die Höhe des übergebenen Elements
TeaserTape.prototype.getElementHeight = function(element) {

	var height = element.offsetHeight;
	var topMargin = 0;
	var bottomMargin = 0;

	if (element.currentStyle) {
		topMargin		= element.currentStyle['marginTop'];
		bottomMargin	= element.currentStyle['marginBottom'];
	} else if (window.getComputedStyle) {
		topMargin = document.defaultView.getComputedStyle(element,null).getPropertyValue('margin-top');
		bottomMargin = document.defaultView.getComputedStyle(element,null).getPropertyValue('margin-bottom');
	}
	
	var isSafari = false;
	
	if(navigator.vendor && navigator.vendor.indexOf('Apple') > -1) {
		isSafari = true;
	}
	
	if(!isSafari) {
		topMargin = topMargin.replace('px', '');
		bottomMargin = bottomMargin.replace('px', '');
	}

	if(topMargin == 'auto') topMargin = 0;
	if(bottomMargin == 'auto') bottomMargin = 0;

	return parseFloat(height) + parseFloat(topMargin) + parseFloat(bottomMargin);
}


// Ermittelt die Breite des übergebenen Elements
TeaserTape.prototype.getElementWidth = function(element) {

	var height = element.offsetWidth;
	var leftMargin = 0;
	var rightMargin = 0;

	if (element.currentStyle) {
		leftMargin	= element.currentStyle['marginLeft'];
		rightMargin	= element.currentStyle['marginRight'];
	} else if (window.getComputedStyle) {
		leftMargin = document.defaultView.getComputedStyle(element,null).getPropertyValue('margin-left');
		rightMargin = document.defaultView.getComputedStyle(element,null).getPropertyValue('margin-right');
	}
	
	var isSafari = false;
	
	if(navigator.vendor && navigator.vendor.indexOf('Apple') > -1) {
		isSafari = true;
	}
	
	if(!isSafari) {
		leftMargin = leftMargin.replace('px', '');
		rightMargin = rightMargin.replace('px', '');
	}

	if(leftMargin == 'auto') leftMargin = 0;
	if(rightMargin == 'auto') rightMargin = 0;

	return parseFloat(height) + parseFloat(leftMargin) + parseFloat(rightMargin);
}

// Übernommen von Colin Ramsay
//
// Copyright (c) 2007 Colin Ramsay
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// Simple version of the Prototype library's bind() which will only work in
// this case, but doing it this way removes the need for Prototype's $A support.
Function.prototype.simpleBind = function() {

	var	__method = this;
	var args =	[arguments[1]];
	var object =	arguments[0];

	return function() {
		return __method.apply(object, args);
	}
}

var xb =
{
	evtHash: [],

	ieGetUniqueID: function(_elem)
	{
		if (_elem === window) { return 'theWindow'; }
		else if (_elem === document) { return 'theDocument'; }
		else { return _elem.uniqueID; }
	},

	addEvent: function(_elem, _evtName, _fn, _useCapture)
	{
		if (typeof _elem.addEventListener != 'undefined')
		{
			if (_evtName == 'mouseenter')
				{ _elem.addEventListener('mouseover', xb.mouseEnter(_fn), _useCapture); }
			else if (_evtName == 'mouseleave')
				{ _elem.addEventListener('mouseout', xb.mouseEnter(_fn), _useCapture); } 
			else
				{ _elem.addEventListener(_evtName, _fn, _useCapture); }
		}
		else if (typeof _elem.attachEvent != 'undefined')
		{
			var key = '{FNKEY::obj_' + xb.ieGetUniqueID(_elem) + '::evt_' + _evtName + '::fn_' + _fn + '}';
			var f = xb.evtHash[key];
			if (typeof f != 'undefined')
				{ return; }
			
			f = function()
			{
				_fn.call(_elem);
			};
		
			xb.evtHash[key] = f;
			_elem.attachEvent('on' + _evtName, f);
	
			// attach unload event to the window to clean up possibly IE memory leaks
			window.attachEvent('onunload', function()
			{
				_elem.detachEvent('on' + _evtName, f);
			});
		
			key = null;
			//f = null;   /* DON'T null this out, or we won't be able to detach it */
		}
		else
			{ _elem['on' + _evtName] = _fn; }
	},	

	removeEvent: function(_elem, _evtName, _fn, _useCapture)
	{
		if (typeof _elem.removeEventListener != 'undefined')
			{ _elem.removeEventListener(_evtName, _fn, _useCapture); }
		else if (typeof _elem.detachEvent != 'undefined')
		{
			var key = '{FNKEY::obj_' + xb.ieGetUniqueID(_elem) + '::evt' + _evtName + '::fn_' + _fn + '}';
			var f = xb.evtHash[key];
			if (typeof f != 'undefined')
			{
				_elem.detachEvent('on' + _evtName, f);
				delete xb.evtHash[key];
			}
		
			key = null;
			//f = null;   /* DON'T null this out, or we won't be able to detach it */
		}
	},
	
	mouseEnter: function(_pFn)
	{
		return function(_evt)
		{
			var relTarget = _evt.relatedTarget;				
			if (this == relTarget || xb.isAChildOf(this, relTarget))
				{ return; }

			_pFn.call(this, _evt);
		}
	},
	
	isAChildOf: function(_parent, _child)
	{
		if (_parent == _child) { return false };
		
		while (_child && _child != _parent)
			{ _child = _child.parentNode; }
		
		return _child == _parent;
	}	
};