// Helper Functions for Constructor Class, which might be useful in other contexts, too
// therefore they are defined as separate functions and should be moved to a common library
function debug(msg) {
  //document.debugForm.debug.value += msg + "\n";
  //alert(msg);
} 

function getNumValFromPxStyle(str) {
  // strips the "px" from style values
  if (str.indexOf("px") != -1) {
    return Number(str.split("px")[0]);
  } else {
    return str;
  }
}

function getNumericStyleValue(val) {
  // checks if style value is numeric, otherwise returns 0
  val = getNumValFromPxStyle(val);
  return (isNaN(val))? 0 : Number(val);
}

function getFullWidthForId(tID) {
  // returns the full width of an element (width + horizontal margins) specified by passed in 'tID'
  var tWidth = Number(document.getElementById(tID).offsetWidth);
  var marginLeft = getNumericStyleValue(YAHOO.util.Dom.getStyle(tID, 'marginLeft'));
  var marginRight = getNumericStyleValue(YAHOO.util.Dom.getStyle(tID, 'marginRight'));
  return tWidth + marginRight + marginLeft;
}

function getFullHeightForId(tID) {
  // returns the full height of an element (height + vertical margins) specified by passed in 'tID'
  var tHeight = Number(document.getElementById(tID).offsetHeight);
  var marginTop = getNumericStyleValue(YAHOO.util.Dom.getStyle(tID, 'marginTop'));
  var marginBottom = getNumericStyleValue(YAHOO.util.Dom.getStyle(tID, 'marginBottom'));
  return tHeight + marginTop + marginBottom;
};

// Text-Scroller Class

// Constructor Function
TextScroller = function (id, idScrollCont, contElemClassName, dir, scrollDelay, _continousScroll, _speed, _delayOnStart, _zebra) {
  // Constructor
  this.contElemClassName = contElemClassName; // Class Name of content elements (innermost relevant elements). Used to "tag" all elements that must be regarded as
                                              // relevant entities in terms of scrolling (as opposed to possibly deeper nested elements
                                              // as part of one content-element entity
  this.scrollObjId = id;                      // Id of outer Container, which defines the viewport; parent of scroll container
  this.itemContainerId = idScrollCont;        // Id of inner container, which is parent to all content elements; element which is actually moved.
  this.clonedElemsCounter = 0;                // Keeps track of how many elements are cloned on initialization to fill out the space provided by viewport
                                              // SUBJECT TO CHANGE DUE TO REFACTORING!
  if (_zebra) {                               // Boolean flag, determines wether elements have an alterning background-color. Defaults to 'false'
    this.zebra = _zebra;
  } else {
    this.zebra = false;  
  }                        

  this.animTimer; // Timer for animation
  this.anim = new Object(); // Holds Yahoo Animation Object;
  this.runAnim = true; // Flag used for deciding wether the animation needs to be halted because of mouseover on a link

  this.continousScroll = _continousScroll; // Boolean Flag; used to determine the Instance's scroll behaviour.
                                           // When 'false' (default), the child elements of the scroll container are moved one 
                                           // after the other, to the left- or topmost position of the viewport, with a (customizable) delay between
                                           // each animation
                                           // When 'true' (needs to be passed explicitly to the constructor), the elements are moved continously at a 
                                           // certain speed (which is also customizable via argument to the constructor function
                                           // (otherwise, a default value is assumed.
  if (this.continousScroll) {
    this.baseSpeed = (_speed != null)? _speed : 7 / 100; // Pixels per Second, either passed in or default
    this.animParams = {motionTypeFunc: YAHOO.util.Easing.easeNone};
  } else {
    this.animParams = {speed: .5, motionTypeFunc: YAHOO.util.Easing.easeOut};
  }

  this.scrollDir = dir; // Scroll Direction. "hor" or "ver"

  if (scrollDelay != null) {          // Delay between the animation of two elements. When no value is passed to the 
    this.scrollDelay = scrollDelay;   // constructor, a default of 3 sec. is assumed.
  } else {
    this.scrollDelay = 3000;
  }
 
  if (_delayOnStart != null) { // Delay before the first animation starts. When no value is passed to the constructor,
                               // a vlaue of '0' is assumed (i.e. the animation starts immediately after the pages has fully loaded).
    this.delayOnStart = _delayOnStart;
  } else {
    this.delayOnStart = 0;
  }

  // Instance variables that can only be assigned a value upon initialization
  this.itemContainerElem;     // holds scrolling container element (determined upon init. via 'itemContainerId'
  this.wrapperWidth;          // width and height of outer container, which acts as viewport to the scrolling content
  this.wrapperHeight;
  
  // Listen for "page fully loaded" event, when triggered, start classes' "init"-method.
  YAHOO.util.Event.addListener(window, 'load', this.init, this, true); 
};

TextScroller.prototype.getContKidsArray = function () {
  // returns an array of all content elements, selected by posession of specific content-class
  return YAHOO.util.Dom.getElementsByClassName(this.contElemClassName, 'div', this.itemContainerId);
};

TextScroller.prototype.getAddedContentWidth = function () {
  // returns the added widths of all content elements
  var kids = this.getContKidsArray();
  var tWidth = 0;
  for (var i = 0; i < kids.length; i++) {
    var tID = kids[i].id;
    //debug(tID + ": " + Number(getFullWidthForId(tID)));
    tWidth += Number(getFullWidthForId(tID));
  }
  return tWidth;
};

TextScroller.prototype.getAddedContentHeight = function () {
  // returns the added heights of all content elements
  var kids = this.getContKidsArray();
  var tHeight = 0;
  for (var i = 0; i < kids.length; i++) {
    var tID = kids[i].id;
    //debug(tID + ": " + Number(getFullHeightForId(tID)));
    tHeight += Number(getFullHeightForId(tID));
  }
  return tHeight;
};

TextScroller.prototype.isContentBigEnough = function () {
  // checks, wether the added widths or heights of all content elements
  // exceed the viewport size 
  if (this.scrollDir == "hor") {
    return ( this.getAddedContentWidth() > (2*this.wrapperWidth)) ? true : false;
  } else if (this.scrollDir == "ver") {
    return ( this.getAddedContentHeight() > this.wrapperHeight ) ? true : false;
  }
};

// Methods to handle the content elements being moused over. When so, all further animation is prevented.
// When the mouse leaves an element, scrolling is reenabled again.
// Mouse Listeners are not included in the loaded HTML-Page, but are assigned to each element upon initialization.
TextScroller.prototype.handleOnMouseOver = function () {
  this.runAnim = false;
};

TextScroller.prototype.handleOnMouseOut = function () {
  var objRef = this;
  function scrollElement() {
    objRef.scrollElement();
  }

  this.runAnim = true;
  window.clearTimeout(this.animTimer);                            // be sure to kill all former animation cycles that may have allready started.
  this.animTimer = setTimeout(scrollElement, this.scrollDelay);   // ... and start over with a new animation
};

TextScroller.prototype.scrollElement = function () {
  // Duplicates current first content-element (left- or top-most) to the end,
  // then moves this element as far to left or top as it is wide or high.
  // Registers callback-function to be executed when animation is done (which removes
  // the duplicated element again)
  clearTimeout(this.animTimer);
  if (!this.runAnim) {
    return;
  }
  
  // Get current first element id...
  var kids = this.getContKidsArray();
  var tID = kids[0].id;
  var scrollBy;

  // ...clone it and append it to the end of the list...
  var contElemClone = document.getElementById(tID).cloneNode(true);
  // Change id of cloned object to avoid two elements having the same id!
  var tempId = contElemClone.id;
  // toggle between "-clone" suffix and plain id
  if ( tempId.indexOf("-clone") != -1 ) {
    contElemClone.id = tempId.split("-clone")[0];
  } else {
    contElemClone.id = tempId + "-clone";
  }
  
  if (this.zebra) { // Adjust elements' class for background-color, if necessary
    if (YAHOO.util.Dom.hasClass(kids[kids.length - 1], "zebra")) {
      YAHOO.util.Dom.removeClass(contElemClone, "zebra");
    } else {
      YAHOO.util.Dom.addClass(contElemClone, "zebra");
    }
  }

  this.itemContainerElem.appendChild(contElemClone);

  // ... then animate the elements container
  if (this.scrollDir == "hor") {
    scrollBy = getFullWidthForId(tID) * -1;
    // when scrolling is not element per element, recalculate scrolling speed for each element
    if (this.continousScroll) {
      this.animParams.speed = Math.abs(this.baseSpeed * scrollBy);
    }
    var attributes = {
      left: { by: scrollBy }
    };
  } else if (this.scrollDir == "ver") {
    scrollBy = getFullHeightForId(tID) * -1;
    var attributes = {
      top: { by: scrollBy }
    };
  } else {
    debug("'scrollDir' hat keinen gueltigen Wert.");
  }
  
  this.anim = new YAHOO.util.Anim(this.itemContainerId, attributes, this.animParams.speed, this.animParams.motionTypeFunc);
  // create back-reference to our object from Yahoo Animation Object
  this.anim.objRef = this;
   
  this.anim.onComplete.subscribe(this.handleOnElementScrollReady);
  this.anim.animate();
}

TextScroller.prototype.handleOnElementScrollReady = function () {
  // necessary to pass callback from Yahoo Animation Object back to our own object
  this.objRef.onElementScrollReady();
};


TextScroller.prototype.onElementScrollReady = function () {
  // Callback function that fires when scrolling by width or height of first element is done.
  // Removes duplicated element, adjusts position of container
  // (because removing the element actually shortens it)
  // and starts scrolling over (after delay).

  // Nested function for handling timeout-call
  var objRef = this;
  function scrollElement() {
    objRef.scrollElement();
  }

  // Get current first elements Id... 
  var kids = this.getContKidsArray();
  if(kids.length == 0) return;
  var tID = kids[0].id;

  // and adjust containers position by amount of the elements width or height
  // (Necessary, because this element will be removed from container, which 
  // shortens the containers dimension)
  if (this.scrollDir == "hor") {
    //var tScrollHor = this.anim.getAttribute('left');
    var newScrollLeft = 0; //Math.round(tScrollHor) - getFullWidthForId(tID);

if(document.location.search != '' && $('tsinfo') && this.itemContainerId.indexOf('olling_cont')>0) $('tsinfo').innerHTML += '<br>getLeft:'+this.anim.getAttribute('left')+' / ';
    this.anim.setAttribute("left", newScrollLeft, "px");

    this.itemContainerElem = document.getElementById(this.itemContainerId);

  } else if (this.scrollDir == "ver") {
    var tScrollVer = this.anim.getAttribute('top');
    var newScrollTop = 0; //Math.abs(tScrollVer) - getFullHeightForId(tID);

    this.anim.setAttribute("top", newScrollTop, "px");

  } else {
    debug("'scrollDir' hat keinen gueltigen Wert.");
  }

  // ... and actually remove it from the list...
  // ewent: test before remove
  if(document.getElementById(tID)) this.itemContainerElem.removeChild(document.getElementById(tID));
  
  // Start over
  if (this.runAnim) {
    this.animTimer = setTimeout(scrollElement, this.scrollDelay);
  }

  
};

// Currently not in use! TODO: Check if still needed, otherwise remove!
TextScroller.prototype.checkShortContent = function() {
  var kids = this.getContKidsArray();
  if (this.scrollDir == "hor") {
    var firstElemWidth = getFullWidthForId(kids[0].id);
    return ( this.getAddedContentWidth() > this.wrapperWidth ) ? false : true;
    //return ( this.getAddedContentWidth() - firstElemWidth > this.wrapperWidth ) ? false : true;
  } else if (this.scrollDir == "ver") {
    var firstElemHeight = getFullHeightForId(kids[0].id);
    return ( this.getAddedContentHeight() - firstElemHeight > this.wrapperHeight ) ? false : true;
  }
};

TextScroller.prototype.cloneContElem = function() {
  //debug(this.clonedElemsCounter);
  var kids = this.getContKidsArray();
  var tCounter = (YAHOO.util.Dom.hasClass(kids[kids.length - 1], "zebra"))? 1 : 0;
  var last = kids.length;
  for (i = 0; i < kids.length; i++) {
    var tID = kids[i].id;
    var contElemClone = document.getElementById(tID).cloneNode(true);
    contElemClone.id = tID + "-dbl" + String(last+i);

    if (this.zebra) { // Adjust elements' class for background-color, if necessary
      if (tCounter % 2 == 0) {
        YAHOO.util.Dom.addClass(contElemClone, "zebra");
      } else {
        YAHOO.util.Dom.removeClass(contElemClone, "zebra");
      }
    }
    
    this.itemContainerElem.appendChild(contElemClone);
    tCounter++;
  }
  // ewent: adjust resulting width
  this.itemContainerElem.style.width =  String((kids.length+1) * getFullWidthForId(tID)) + "px";
};

TextScroller.prototype.init = function () {
  // Called, when wrapper and animation container are ready. Checks if added element dimensions
  // are actually larger than viewport size, if so starts scrolling by delay.
  var objRef = this;
  function timeoutHandler() {
    objRef.scrollElement();
  }
  
  this.itemContainerElem = document.getElementById(this.itemContainerId);
  this.wrapperWidth = document.getElementById(this.scrollObjId).offsetWidth;
  this.wrapperHeight = document.getElementById(this.scrollObjId).offsetHeight;

  // SUBJECT TO CODE-REFACTORING: Get rid of arbitrary pixel values!!
  if (this.scrollDir == "hor") {
    this.itemContainerElem.style.width = String(this.getAddedContentWidth() + 2000 ) + "px";
    //debug("W: " + document.getElementById(this.itemContainerId).style.width);
  } else if (this.scrollDir == "ver") {
    this.itemContainerElem.style.height = this.getAddedContentHeight() + 400 + "px";
    //debug("H: " + document.getElementById(this.itemContainerId).style.height);
  }

  YAHOO.util.Dom.setStyle(this.itemContainerId, "left", "0px");
  
  var cnt=0;
  while(!this.isContentBigEnough()) {
    cnt++; if (cnt > 9) break;
    this.cloneContElem();
  }
  if (this.isContentBigEnough()) {
    /*
    if (this.checkShortContent()) {
      this.cloneContElem();
    }
    */
  
    //this.animTimer = setTimeout(timeoutHandler, this.delayOnStart);
    objRef.scrollElement();
  } 
  else if (this.continousScroll) {
    // erneuter init-Aufruf führt zu zwei parallel ausgeführten Animationsobjekten
    //this.init();
  }

  var kids = this.getContKidsArray();
  for (i = 0; i < kids.length; i++) {
    var links = kids[i].getElementsByTagName("a");
    for (j = 0; j < links.length; j++) {
      links[j].objRef = this;
      links[j].onmouseover = function () {
        objRef.handleOnMouseOver();
      }
      links[j].onmouseout = function () {
        objRef.handleOnMouseOut();
      }
    }
  }

};

TextScroller.prototype.kill = function () {
  this.runAnim = false;
  window.clearInterval(this.animTimer);
  this.anim.stop();
  this.anim.onComplete = null;
  this.anim = null;
  
  //this.scrollElement = null;  
  //this.onElementScrollReady = null;
  
  var kids = this.getContKidsArray();
  for (i = 0; i < kids.length; i++) {
    //alert(kids[i].id);
    if (kids[i]) {
      this.itemContainerElem.removeChild(kids[i]);
    }
    
  }
  //this.scrollObjId.removeChild(this.itemContainerElem);
  
};
