/**
 * wftda.com ticker JavaScript.
 *
 * @package    WFTDA
 * @subpackage UI
 * @copyright  Copyright 2009 Spenlen Media, Inc. (http://spenlen.com)
 * @version    $Id$
 */


if (typeof WFTDA == 'undefined') {
    var WFTDA = {};    
}


/**
 * Manages the animation and mouse events for info tickers.
 *
 * @var Class
 */
WFTDA.Ticker = Class.create();
WFTDA.Ticker.prototype = {

  /**** Instance Variables ****/

    /**
     * Ticker container DIV element.
     * @var Element
     */
    ticker : null,
    
    /**
     * Back link A element.
     * @var Anchor
     */
    navBackLink : null,

    /**
     * Forward link A element.
     * @var Anchor
     */
    navForwardLink : null,
    
    /**
     * Ticker tape DIV element.
     * @var Element
     */
    tickerTape : null,
    
    /**
     * Total pixel width of the entire ticker tape.
     * @var int
     */
    tickerTapeWidth : 0,

    /**
     * Width of the visible portion of the ticker tape.
     * @var int
     */
    tickerTapeVisibleWidth : 0,

    /**
     * Minimum left offset of the ticker tape.
     * @var int
     */
    minLeft : 0,

    /**
     * Maximum left offset of the ticker tape.
     * @var int
     */
    maxLeft : 0,

    /**
     * Current left offset of the ticker tape.
     * @var int
     */
    currentLeft : 0,
    
    /**
     * New left offset for the animation transition.
     * @var int
     */
    newLeft : 0,
    
    /**
     * Animation starting timestamp. Used to calculate frame numbers.
     * @var int
     */
    baseTime : 0,

    /**
     * Animation transition function.
     * @var function
     */
    transitionFunction : null,
    
    /**
     * Duration of the transition animation in milliseconds.
     * @var
     */
    animationDuration : 500,

    /**
     * Animation window interval.
     * @var intervalID
     */
    animationInterval : null,




  /**** Initialization ****/

    /**
     * Establishes the effective width of the ticker and installs the various
     * event handlers responsible for maintaining the interactive navigation
     * elements.
     *
     * @var String  tickerName
     */
    initialize : function (tickerName)
    {
        this.ticker = $(tickerName + 'Ticker');
        if (! this.ticker) {
            alert('Cannot find ticker element: ' + tickerName);
            return;
        }

        this.tickerTape = this.ticker.down('.tickerTape');

        this.currentLeft = parseInt(this.tickerTape.getStyle('left'), 10);
        this.maxLeft = this.currentLeft;
        this.newLeft = this.currentLeft;
        
        var lastSection = this.tickerTape.childElements().last();
        if (! lastSection) {
            return;
        }
        var offset = Position.positionedOffset(lastSection);
        this.tickerTapeWidth = offset[0] + lastSection.getWidth() + this.maxLeft;
        this.tickerTape.style.width = this.tickerTapeWidth + 'px';
        
        this.recalculateExtents();

        this.navBackLink    = this.ticker.down('.tickerNavigation .back A');
        this.navForwardLink = this.ticker.down('.tickerNavigation .forward A');
        if (! Prototype.Browser.IE) {
            /* IE can't deal properly with setting transparency on a PNG image
             * that is already transparent.
             */
            this.navBackLink.setStyle({opacity: 0});
            this.navForwardLink.setStyle({opacity: 0});
        }
        this.hideOrShowNavigation();

        Event.observe(this.navBackLink,    'click',  this.navBackOnClick.bind(this));
        Event.observe(this.navForwardLink, 'click',  this.navForwardOnClick.bind(this));

        Event.observe(this.tickerTape, 'DOMMouseScroll', this.tickerTapeOnScroll.bind(this));  // Firefox
        Event.observe(this.tickerTape, 'mousewheel',     this.tickerTapeOnScroll.bind(this));
        
        Event.observe(window, 'resize', this.windowOnResize.bind(this));
    },



  /**** Event Handlers ****/

    /**
     * onclick handler for the navigation back link. Shifts the visible ticker
     * to the right one page at a time.
     *
     * @param Event event
     */
    navBackOnClick : function (event)
    {
        event.stop();
        this.animateTo(this.currentLeft + (this.tickerTapeVisibleWidth * 0.8));
    },

    /**
     * onclick handler for the navigation forward link. Shifts the visible ticker
     * to the left one page at a time.
     *
     * @param Event event
     */
    navForwardOnClick : function (event)
    {
        event.stop();
        this.animateTo(this.currentLeft - (this.tickerTapeVisibleWidth * 0.8));
    },

    /**
     * onscroll handler for the ticker tape area. Shifts the visible ticker to
     * the left or right by small amounts.
     */
    tickerTapeOnScroll : function (event)
    {
        event.preventDefault();

        var delta = 0;
        if (event.wheelDelta) {
            delta = event.wheelDelta / 120;
            if (window.opera) delta = -delta;
        } else if (event.detail) {
            delta = -event.detail / 3;
        }

        if (delta == 0) {
            return;
        } else {
            this.animateTo(this.newLeft + (this.tickerTapeVisibleWidth * (delta / 30)), Effect.Transitions.linear, 200);
        }
    },
    
    /**
     * onresizehandler for the window. Recalculates ticker extents and hides
     * or shows the navigation links links as needed.
     *
     * @param Event event
     */
    windowOnResize : function (event)
    {
        this.recalculateExtents();
        this.hideOrShowNavigation();
    },



  /**** Animation ****/

    /**
     * Recalculates the minimum and maximum visible extents of the ticker.
     * Keeps the ticker anchored at the right edge of the window if necessary.
     */
    recalculateExtents : function ()
    {
        this.tickerTapeVisibleWidth = this.ticker.getWidth();

        var newMinLeft = -(this.tickerTapeWidth - this.tickerTapeVisibleWidth);
        if (newMinLeft > this.maxLeft) {
            newMinLeft = this.maxLeft;
        }
        if (this.currentLeft <= this.minLeft) {
            var delta = this.minLeft - newMinLeft;
            if (delta < 0) {
                this.currentLeft -= delta;
                if (this.currentLeft > this.maxLeft) {
                    this.currentLeft = this.maxLeft;
                }
                this.tickerTape.style.left = this.currentLeft + 'px';
            }
        }
        this.minLeft = newMinLeft;
    },

    /**
     * Animates the ticker tape to the new location. Enforces the maximum extents
     * of the ticker view.
     *
     * @param float    left       New Left value
     * @param function transition (optional) Transition function. If omitted,
     *   uses Effect.Transitions.sinoidal
     * @param float    duration   (optional) Animation duration in milliseconds.
     *   If calculates a constant-time duration based on the distance to be traveled.
     */
    animateTo : function (left, transition, duration)
    {
        left = Math.round(left);
        
        if (left > this.maxLeft) {
            left = this.maxLeft;
        } else if (left < this.minLeft) {
            left = this.minLeft;
        }
        
        if (! transition) {
            transition = Effect.Transitions.sinoidal;
        }
        
        if (! duration) {
            duration = 800 * (Math.abs(this.currentLeft - left) / this.tickerTapeVisibleWidth)
        }
        
        if (this.animationInterval) {
            window.clearInterval(this.animationInterval);
        }
        
        this.newLeft  = left;
        this.baseTime = new Date().getTime();
        
        this.transitionFunction = transition;
        this.animationDuration  = duration;
        
        this.animationInterval = window.setInterval(this.renderFrame.bind(this), 15);

        this.hideOrShowNavigation();
    },
    
    /**
     * Renders the next frame of the ticker tape animation.
     */
    renderFrame : function ()
    {
        var now = new Date().getTime();
        var pos = (now - this.baseTime) / this.animationDuration;
        if (pos >= 1) {
            window.clearInterval(this.animationInterval);
            this.animationInterval = null;
            this.currentLeft = this.newLeft;
        } else {
            this.currentLeft += ((this.newLeft - this.currentLeft) * this.transitionFunction(pos));
        }        
        this.tickerTape.style.left = this.currentLeft + 'px';
    },

    /**
     * Hides or shows the navigation links as needed.
     */
    hideOrShowNavigation : function ()
    {
        if (this.newLeft >= this.maxLeft) {
            if (this.navBackLink.visible()) {
                if (Prototype.Browser.IE) {
                    this.navBackLink.hide();
                } else {
                    new Effect.Opacity(this.navBackLink,
                        {to: 0, duration: 0.5,
                         afterFinish: function () { this.navBackLink.hide(); }.bind(this)}
                        );
                }
            }
        } else {
            if (! this.navBackLink.visible()) {
                this.navBackLink.show();
                if (! Prototype.Browser.IE) {
                    new Effect.Opacity(this.navBackLink, {to: 1, duration: 0.5});
                }
            }
        }
        if (this.newLeft <= this.minLeft) {
            if (this.navForwardLink.visible()) {
                if (Prototype.Browser.IE) {
                    this.navForwardLink.hide();
                } else {
                    new Effect.Opacity(this.navForwardLink,
                        {to: 0, duration: 0.5,
                         afterFinish: function () { this.navForwardLink.hide(); }.bind(this)}
                        );
                }
            }
        } else {
            if (! this.navForwardLink.visible()) {
                this.navForwardLink.show();
                if (! Prototype.Browser.IE) {
                    new Effect.Opacity(this.navForwardLink, {to: 1, duration: 0.5});
                }
            }
        }
    }

};
