/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 */

/**
 * Scroller.js
 *
 * File Path: /scripts/Effects/
 *
 * $Id: Scroller.js 174 2007-08-26 20:38:27Z topdog $
 *
 * LICENSE: copyright 2005 - 2007 Edward Vermillion - Doggydoo Codeworks.
 * Unless otherwise stated ALL RIGHTS ARE RESERVED. Use or reuse without prior
 * written permission from the author or Doggydoo Codeworks is prohibited.
 * Visit http://www.doggydoo.net/license/DDJS-V1.X.txt for the full license.
 * Installation and use of this software implies agreement to the full
 * license.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DOGGYDOO
 * CODEWORKS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @package     DDJS
 * @author      Edward Vermillion <evermillion@doggydoo.net>
 * @copyright   2005 - 2007 Edward Vermillion, Doggydoo Codeworks
 * @license     http://www.doggydoo.net/license/DDJS-1.x.txt
 * @version     1.0
 */

if (typeof DDJS == 'undefined' || typeof DDJS.Effects == 'undefined') {
    throw('DDJS.Effects.Scroller: Environment not properly initialized');
}

/**
 * @constructor DDJS.Effects.Scroller
 *
 * The constructor for a DDJS.Effects.Scroller object
 * @requires DDJS.Effects
 * @requires DDJS.Utils
 *
 * @param {HTMLElement} outer
 * @param {HTMLElement} inner
 * @return {Object}
 */
DDJS.Effects.Scroller = function (outer, inner) {

    /**
     * @property _outer
     *
     * The outer container for the scroller object
     *
     * @type {HTMLDivElement}
     */
    this._outer = outer;

    /**
     * @property _inner
     *
     * The inner scroller object
     *
     * @type {HTMLDivElement}
     */
    this._inner = inner;

    /**
     * @property _innerStyle
     *
     * The computed style object for the scrolled div
     *
     * @type {CSSStyleDeclaration)
     */
    this._innerStyle = null;

    /**
     * @property _scrollerID
     *
     * The Effects container id string for this Scroller object
     *
     * @type {String}
     */
    this._scrollerID = DDJS.Utils.generateObjectID();

    /**
     * @method _state
     *
     * The 'state' of the scroller object, one of 'start', 'end', 'scrolling' or
     * 'halted'
     *
     * @type {String}
     */
    this._state = 'start';

    /**
     * @property _vertScrollDistance
     *
     * The vertical distance that the scrolled object needs to be scrolled
     *
     * @type {Number}
     */
    this._vertScrollDistance = null;

    /**
     * @property _horzScrollDistance
     *
     * The horizontal distance that the scrolled object needs to be scrolled
     *
     * @type {Number}
     */
    this._horzScrollDistance = null;

    /**
     * @property _defaultGain
     *
     * The default setting for the gain. 
     *
     * @type {Number}
     */
    this._defaultGain = 2;

    /**
     * @property _gain
     *
     * The distance, in pixels, moved on each iteration
     *
     * @type {Number}
     */
    this._gain = null;

    /**
     * @property _defaultFrequency
     *
     * The default setting for the frequency
     *
     * @type {Number}
     */
    this._defaultFrequency = 10;

    /**
     * @property _frequency
     *
     * The time, in milliseconds, between iterations
     *
     * @type {Number}
     */
    this._frequency = this._defaultFrequency;

    /**
     * @property _scrollCount
     *
     * The number of times that _doScroll() has been called
     *
     * @type {Number}
     */
    this._scrollCount = 0;

    /**
     * @property _scrollAccel
     *
     * The number of times that _doScroll() is called before starting the
     * acceleration
     *
     * @type {Number}
     */
    this._scrollAccel = 100;

    /**
     * @property _timerID
     *
     * The setTimeout() id
     *
     * @type {Resource}
     */
    this._timerID = null;

    /**
     * @property _left
     *
     * The 'left' position of the scrolled element in pixels
     *
     * @type {Number}
     */
    this._left = 0;

    /**
     * @property _top
     *
     * The 'top' position of the scrolled element in pixels
     *
     * @type {Number}
     */
    this._top = 0;

    // Register with the effects container...
    DDJS.Effects.registerEffect(this, this._scrollerID);

	this._innerStyle = window.getComputedStyle(this._inner,'');

	if (this._innerStyle.fontSize.replace(/(?:px|pt)/, '') != 0) {
		this._defaultGain = Math.round(this._innerStyle.fontSize.replace(/(?:px|pt)/, '')/10);
	}
	this._gain = this._defaultGain

	this._vertScrollDistance = this._outer.scrollHeight - this._outer.offsetHeight;
	if (this._vertScrollDistance < 0) {
		this._vertScrollDistance = 0;
	}
	
	this._horzScrollDistance = this._outer.scrollWidth - this._outer.offsetWidth;
	if (this._horzScrollDistance < 0) {
		this._horzScrollDistance = 0;
	}

}; // End DDJS.Effects.Scroller


/**
 * @object DDJS.Effects.Scroller.prototype
 *
 * @prototype
 */
DDJS.Effects.Scroller.prototype = {
    
    /**
     * @method reinit
     * 
     * Re-inbitializes the scroller object to recalculate scroll distances
     * 
     * Used when an element that doesen't have layout is given layout, such as 
     * when the display of the outer element is changed from 'none' to 'block'.
     * 
     * @return {void}
     */
    reinit: function () {
        
        this._innerStyle = window.getComputedStyle(this._inner,'');

        if (this._innerStyle.fontSize.replace(/(?:px|pt)/, '') != 0) {
            this._defaultGain = Math.round(this._innerStyle.fontSize.replace(/(?:px|pt)/, '')/10);
        }
        this._gain = this._defaultGain
    
        this._vertScrollDistance = this._outer.scrollHeight - this._outer.offsetHeight;
        if (this._vertScrollDistance < 0) {
            this._vertScrollDistance = 0;
        }
        
        this._horzScrollDistance = this._outer.scrollWidth - this._outer.offsetWidth;
        if (this._horzScrollDistance < 0) {
            this._horzScrollDistance = 0;
        }
    },
    
    mouseScroll: function () {
        
        
    },
    
    /**
     * @method scrollToElement
     * 
     * Scrolls to a contained elements position, or the end of the scrolled
     * area. Similar to the scrollIntoView() javascript function.
     * 
     * @param {HTMLElement} elem
     * 
     * @return {void}
     */
    scrollToElement: function (elem) {
        
        if (elem == null) {
            return;
        }
        
        if (elem.parentNode == this._inner) {
        
            var elTop = elem.offsetTop;
            var elLeft = elem.offsetLeft;
            
            if (this._vertScrollDistance) {
            
                if (elTop > 0 && elTop < this._vertScrollDistance) {
                
                    this._top = -elTop;
                    
                } else if (elTop >= this._vertScrollDistance) {
                
                    this._top = -this._vertScrollDistance;
                }
                
            } else if (this._horzScrollDistance) {
            
                if (elLeft > 0 && elLeft < this._horzScrollDistance) {
                
                    this._left = -elLeft;
                    
                } else if (elLeft > this._horzScrollDistance) {
                
                    this._left = -this._horzScrollDistance;
                }
            }
            
            this._doScroll();
        }
    },

    /**
     * @method showScrollers
     *
     * Determine if the scroller arrows are needed. If the browser reports an
     * empty scroll distance we return false
     *
     * @return {Boolean}
     */
    showScrollers: function () {

        if (this._vertScrollDistance == 0 && this._horzScrollDistance == 0) {
            return false;
        }
        return true;
    },

    /**
     * @method setScrollerAccel
     *
     * Sets the scroller acceleration
     *
     * @param {Number} val
     * @return void
     */
    setScrollerAccel: function (val) {
        this._scrollAccel = Math.abs(val);
    },
    
    /**
     * @method setDefaultGain
     * 
     * Sets the default gain for the scroller object
     * 
     * @param {Integer} num     The value to set _defaultGain to
     * @param {Boolean} setGain If true will also set _gain
     */
    setDefaultGain: function (num, setGain) {
    	if (num != null) {
    		this._defaultGain = num;
    		if (setGain) {
    			this._gain = num;
    		}
    	}
    },

    /**
     * @method getState
     *
     * Returns the 'state' of the scroller object, one of 'start', 'end',
     * 'scrolling' or 'halted'
     *
     * @return String
     */
    getState: function () {
        return this._state;
    },

    /**
     * @method scroll
     *
     * Scrolls the element
     *
     * @param {String} direction
     * @return void
     */
    scroll: function (direction) {

        var callString = "DDJS.Effects.execEffect('"+this._scrollerID+"', 'scroll', '"+direction+"');";

        if (this._timerID != null) {
            window.clearTimeout(this._timerID);
            this._timerID = null;
        }

        if (direction == 'start' ) {

            if (this._vertScrollDistance) {
                if (this._top < 0) {
                    this._top = this._top + this._gain;
                    if (this._top > 0) {
                        this._top = 0;
                    }

                } else {
                    this._state = 'start';
                }

            } else if (this._horzScrollDistance) {
                if (this._left < 0) {
                    this._left = this._left + this._gain;
                    if (this._left > 0) {
                        this._left = 0;
                    }

                } else {
                    this._state = 'start';
                }
            }


        } else if (direction == 'end') {

            if (this._vertScrollDistance) {

                if (Math.abs(this._top) < this._vertScrollDistance) {
                    this._top = this._top - this._gain;
                    if (Math.abs(this._top) > this._vertScrollDistance) {
                        this._top = -this._vertScrollDistance;
                    }

                } else {
                    this._state = 'end';
                }

            } else if (this._horzScrollDistance) {

                if (Math.abs(this._left) < this._horzScrollDistance) {
                    this._left = this._left - this._gain;
                    if (Math.abs(this._left) > this._horzScrollDistance) {
                        this._left = -this._horzScrollDistance;
                    }

                } else {
                    this._state = 'end';
                }
            }
        }

        if ((direction == 'start' && this._state != 'start') ||
            (direction == 'end' && this._state != 'end')) {

            this._doScroll();

            if (this._scrollCount % this._scrollAccel == 0) {
                this._gain = Math.round(this._gain + 2);
            }

            this._timerID = window.setTimeout(callString, this._frequency);

        } else {

            window.clearTimeout(this._timerID);
            this._timerID = null;
        }

    },

    /**
     * @method halt
     *
     * Stops the scrolling action
     *
     * @return void
     */
    halt: function () {
        window.clearTimeout(this._timerID);
        this._timerID = null;
        this._scrollCount = 0;
        this._frequency = this._defaultFrequency;
        this._gain = this._defaultGain;
        this._state = 'halted';
    },

    /**
     * @method zoom
     *
     * 'Zooms' to the start or end of the element
     *
     * @param {String} direction
     * @return void
     */
    zoom: function (direction) {

        if (this._timerID != null) {
            window.clearTimeout(this._timerID);
            this._timerID = null;
        }

        if (direction == 'start') {
            this._left = 0;
            this._top = 0;

        } else if (direction = 'end') {

            if (this._vertScrollDistance) {
                this._top = -this._vertScrollDistance;

            } else if (this._horzScrollDistance) {
                this._left = -this._horzScrollDistance;
            }
        }
        this._doScroll();
    },

    /**
     * @method toString
     *
     * @return {String}
     */
    toString: function () {
        return '[Object] DDJS.Effects.Scroller';
    },

    /**
     * @method _doScroll
     *
     * Executes a single scroll step
     *
     * @return void
     * @private
     */
    _doScroll: function () {
        this._inner.style.top = this._top.toString() + 'px';
        this._inner.style.left = this._left.toString() + 'px';
        this._state = 'scrolling';
        this._scrollCount++;
    }

}; // End DDJS.Effects.Scroller.prototype



