// Returns a value stored in a cookie.
GetCookie = function(cookieName)
{
    var search = cookieName + "=";
    var retVal = "";
    
    if (document.cookie.length > 0)
    {
        var offset = document.cookie.indexOf(search);
        
        if (offset != -1)
        {
            offset += search.length;
            var end = document.cookie.indexOf(";", offset);
            
            if (end == -1)
                end = document.cookie.length;
    
            retVal = unescape(document.cookie.substring(offset, end))
        }
    }
    
    return retVal;
}

// Returns an object specified by its ID.
GetObject = function(obj)
{
    if(document.getElementById)
    {
        var retObj = document.getElementById(obj);
        return retObj;
    }
    else if(document.all)
    {
        retObj = document.all(obj);
        return retObj;
    }
    else
    {
        e = "ComplaceoUtils.UnknownPlatformException";
        throw(e);
    }
}

// Attaches an event.
AddEvent = function(type, listener)
{
    if(window.addEventListener)
        window.addEventListener(type, listener, false);
    else if (window.attachEvent)
        window.attachEvent("on" + type, listener);
    else
    {
        e = "ComplaceoUtils.UnknownPlatformException";
        throw(e);
    }
}

// Manages scroller objects. Its responsability is to create and return scroller 
// objects and maintain a collection of all the created scrollers to save their 
// state to a cookie later.
function ComplaceoScrollerManager()
{
    this.Scrollers = new Array();
    
    this.CreateScroller = function(id)
    {
        var retObj = new ComplaceoScroller(id);
        this.Scrollers[this.Scrollers.length] = retObj;
        return retObj;
    }
    
    this.SaveScrollersStates = function()
    {
        // The "this" keyword at this point doesn't seem to be a valid reference
        // to the object instantiated on the main page, we therefore need to create
        // a new reference to it, however, it is very important that the name of
        // the variable on the main page is exactly "ScrollerManager" so the eval
        // function can "find" it.
        complaceoScrollerManager = eval("ScrollerManager");
        
        for(var i = 0; i < complaceoScrollerManager.Scrollers.length; i++)
        {
            if(complaceoScrollerManager.Scrollers[i].PersistPosition)
            {
                scrollPos = complaceoScrollerManager.Scrollers[i].Scroll();
                toSave = complaceoScrollerManager.Scrollers[i].Id + "_LastPos=" + scrollPos;
                document.cookie = toSave;
            }
        }
    }
    
    AddEvent("unload", this.SaveScrollersStates );
}

function ComplaceoScroller(id)
{
    //################################################# Properties
    /////////////////////////////////////////////////////// Public
    this.Id = id;                               // ID of the scroller, allows multiple scrollers to be instantiated on the same page.
    this.AutoStart = true;                      // Defines whether the scroller should start scrolling automatically.
    this.Speed = 20;                            // Scroller speed (in milliseconds).
    this.PixelsPerInterval = 1;                 // Defines the amount of pixels the scroller should scroll by.
    this.PersistPosition = true;                // Defines whether the scroller should remember its last position (true or false).
    this.PauseOnItem = true;                    // Defines whether the scroller should stop on each item.
    this.PauseLength = 2000;                    // Amount of time the scroller should stop scrolling for when this.PauseOnItem is set to true.
    this.Scroll = function()                    // Returns the top property (for vertical objects) or the left property (for horizontal objects) of the scrolling element.
    {
        var divObject = GetObject(this.Id + "_div");
        var retVal = divObject.style.top;
        return retVal;
    }

    ////////////////////////////////////////////////////// Private
    var _timer = null;
    var _pauseTimer = null;
    var _isScrolling = false;
    var _isPaused = false;
    var _initialTableHeight = null;
    var _stops = new Array();
    var _nextStopIndex = null;
    var _nextStop = null;

    //#################################################### Methods
    /////////////////////////////////////////////////////// Public

    // Initialises the scroller object.
    this.Init = function()
    {
        // Reference the required objects.
        var divContainerObject = GetObject(this.Id);
        var divAnimatedObject = GetObject(this.Id + "_div");
        var tableObject = GetObject(this.Id + "_table");

        // Set the initial position of the animated div.
        if (this.PersistPosition && GetCookie(this.Id + "_LastPos") != "")
            divAnimatedObject.style.top = GetCookie(this.Id + "_LastPos");
        else
            divAnimatedObject.style.top = "1px"; // Set to 1px to make sure it pauses on the very first item

        // Initialise private fields.
        _initialTableHeight = tableObject.offsetHeight;

        if(this.PauseOnItem)
        {
            _stops = this.GetStops(tableObject);
            _nextStopIndex = this.GetNextStopIndex(); // Requires the animated div to be already positionned and _stops to be initialised
            _nextStop = _stops[_nextStopIndex]; // Requires _nextStopIndex to be initialised
        }

        // Duplicate the items. The total size of the table that contains our items 
        // must be at least the size of the visible area + 1 time the size of the table 
        // without duplicates. That's because the animated div only scrolls up to the 
        // size of 1 table without duplicates, and then is reset to its initial position 0.
        // Requires _initialTableHeight to be initialised.
        this.DuplicateItems(divContainerObject, tableObject);

        // Start scrolling.
        if (this.AutoStart)
            this.StartScrolling();
    }

    ////////////////////////////////////////////////////// Private

    // Returns a collection of all the stops for a given table object.
    this.GetStops = function(tableObject)
    {
        var retCol = new Array();

        // Set the default first stop of the collection.
        retCol[0] = 0;

        // Set the collection of stops (omitting the first item as it's always 0 by default).
        for(var i = 1; i < tableObject.rows.length; i++)
            retCol[i] = (-1 * tableObject.rows[i - 1].offsetHeight) + retCol[i - 1];

        return retCol;
    }

    // Returns the initial next stop index.
    this.GetNextStopIndex = function()
    {
        var retVal = null;

        // Get the current position (top) of animated div.
        var scrollDistance = parseInt(this.Scroll(), 10);

        // Set the next stop to 0 if the position of the animated div is smaller than the 
        // last stop in the collection.
        if(scrollDistance <= _stops[_stops.length - 1])
        {
            retVal = 0;
        }
        else
        {
            // Otherwise iterate through the collection of stops until the total distance 
            // already scrolled is greater than one of the stops.
            for(var i = 0; i < _stops.length; i++)
            {
                if(scrollDistance > _stops[i])
                {
                    retVal = i;
                    break;
                }
            }
        }

        return retVal;
    }

    // Duplicates the items of a given table until its total height is at least its
    // inital height + the height of the given div object.
    this.DuplicateItems = function(divObject, tableObject)
    {
        var noOfRows = tableObject.rows.length;
        var noOfRepeats = 0;
        var tbody = tableObject.getElementsByTagName("TBODY")[0];

        if (tableObject.offsetHeight > 0)
        {
            if (tableObject.offsetHeigh > divObject.offsetHeight)
                noOfRepeats = 1;
            else
                noOfRepeats = divObject.offsetHeight / tableObject.offsetHeight + 2;
        }

        for(var j = 0; j < noOfRepeats; j++)
        {
            for(var i = 0; i < noOfRows; i++)
            {
                newRow = document.createElement("tr");
                var innerHTML = tableObject.rows[i].innerHTML;
                newRow.innerHTML = innerHTML;
                tbody.appendChild( tableObject.rows[i].cloneNode(true));
            }
        }
    }

    // Starts scrolling.
    this.StartScrolling = function ()
    {
        if(!_isScrolling)
        {
            _timer = window.setInterval(this.Id + ".MoveLayer()", this.Speed);
            _isScrolling = true;
           _isPaused = false;
        }
    }

    // Stops scrolling.
    this.StopScrolling = function()
    {
        if(_isScrolling)
        {
            clearInterval(_timer);
            _isScrolling = false;
        }
        if(_isPaused)
            clearTimeout(_pauseTimer);
    }

    // Moves the div object up or pauses.
    this.MoveLayer = function()
    {
        // Reference the required object.
        var divObject = GetObject(this.Id+ "_div");

        // Scroll up by the amount of pixels specified by this.PixelsPerInterval or set to 0px.
        divObject.style.top = (parseInt(divObject.style.top, 10) <= (-1 * _initialTableHeight)) ? "0px" : (parseInt(divObject.style.top, 10) - this.PixelsPerInterval) + "px";

        if(this.PauseOnItem)
        {
            // Pause the scrolling if it reaches the next stop. 
            if(parseInt(divObject.style.top, 10) == _nextStop)
            {
                _isPaused = true;

                // Stop scrolling.
                this.StopScrolling();

                // Get the next stop index.
                _nextStopIndex = this.GetNextStopIndex();
                // Set the next stop to item that matches the next stop index in the collection of stops.
                _nextStop = _stops[_nextStopIndex];

                // Starts scrolling again after the time specified by this.PauseLength is over.
                _pauseTimer = window.setTimeout(this.Id + ".StartScrolling()", this.PauseLength);
            }
        }
    }
}