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

/**
 * fader.js
 *
 * File Path: /DDJS/Effects/
 *
 * $Id: Fader.js 223 2008-02-09 19:10:29Z 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.Fader: Environment not properly initialized');
}

/**
 * @constructor DDJS.Effects.Fader
 *
 * Constructor for DDJS.Effects.Fader object
 *
 * @param {HTMLElement} obj
 * @return {Object}
 */
DDJS.Effects.Fader = function (obj) {
    
    this._enabled = true;

    /**
     * @property _gain
     *
     * The ammount of change in opacity per iteration
     *
     * @type {Number}
     * @private
     */
    this._gain = 5;

    /**
     * @property _frequency
     *
     * Time in milliseconds between each iteration
     *
     * @type {Number}
     * @private
     */
    this._frequency = 10;

    /**
     * @property _opacity
     *
     * Current opacity of the object.
     * 0 = fully hidden
     * 100 = fully shown
     *
     * @type {Number}
     * @private
     */
    this._opacity = null;
    
    this._minOpacity = 0;
    
    this._maxOpacity = 100;

    /**
     * @property _state
     *
     * The state of the object, one of 'hidden', 'hiding', 'showing', 'visible'
     *
     * @type {String}
     * @private
     */
    this._state = null;

    /**
     * @property _fadeObj
     *
     * Reference to the object that Fader is working on
     *
     * @type {HTMLElement}
     * @private
     */
    this._fadeObj = obj;

    /**
     * @property _objectID
     *
     * An id string used to keep track of this Fader object in the effects
     * object container
     *
     * @type {String}
     * @private
     */
    this._objectID = DDJS.Utils.generateObjectID();

    /**
     * @property _timeoutID
     *
     * Id returned by window.setTimeout()
     *
     * @type {Resource}
     * @private
     */
    this._timeoutID = null;

    /**
     * @property _callbackObj
     *
     * Object that recieves the 'finished' message for the hidden or visible
     * states
     *
     * @type {Object}
     * @private
     */
    this._callback = null;

    // Register with the effects container...
    DDJS.Effects.registerEffect(this, this._objectID);
    
    // See if we can determine the current state of the object...
    if (typeof window.getComputedStyle != 'undefined') {

        var styleInfo = window.getComputedStyle(this._fadeObj, null);
        
        this._opacity = styleInfo.opacity * 100;
        
        if (styleInfo.visibility == 'hidden') {

            this._state = 'hidden';

        } else {
            
            if (this._opacity < 100) {
            
                this._state = 'hidden';
                
            } else {
            
                this._state = 'visible';
            }
        }
    }

}; // End DDJS.Effects.Fader

/**
 * @object DDJS.Effects.Fader.prototype
 *
 * @prototype
 */
DDJS.Effects.Fader.prototype = {
    
    enable: function () {
        this._enabled = true;
    },
    
    disable: function () {
        this._enabled = false;
    },

    /**
     * @method setSpeed
     *
     * Set the 'speed' of the fade, total time and frames/second
     *
     * Fade 'smoothness' is controlled by the fps, lower numbers may have a
     * 'jerkier' appearance, but may be less resource intensive, which might
     * make things smoother for older processors.
     *
     * @param time  The total time, in seconds, from full on to full off
     * @param fps   Frames per Second in whole numbers between 1 and 1000
     *
     * @return void
     */
    setSpeed: function (time, fps) {

        if (fps > 0 && fps < 1000) {
        	
            this._frequency = Math.round(1000/fps);
            
        } else {
        	
            throw('DDJS.Effects.Fader.setSpeed(): fps out of range')
        }

        this._gain = Math.round(100/(fps * time));
    },
    
    /**
     * @method setMaxOpacity
     * 
     * Set the maximum opacity to fade to on show
     * 
     * The default maximum opacity is 100.
     * 
     * @param {Integer} op  Must be greater than 0 and <= 100.
     * 
     * @return void
     */
    setMaxOpacity: function (op) {
        
        if (op > 0 && op <= 100) {
            
            this._maxOpacity = op;
            
        } else {
            
            throw('DDJS.Effects.Fader.setMaxOpacity(): opacity out of range');
        }
    },
    
    /**
     * @method setMinOpacity
     * 
     * Set the minimum opacity to fade to on hide
     * 
     * The default minimum opacity is 0.
     * 
     * @param {Integer} op  Must be >= 0 and less than 100.
     * 
     * @return void
     */
    setMinOpacity: function (op) {
        
        if (op >= 0 && op < 100) {
            
            this._minOpacity = op;
            
        } else {
            
            throw('DDJS.Effects.Fader.setMinOpacity(): opacity out of range');
        }
    },

    /**
     * @method getState
     *
     * Returns the 'state' of the fader object, one of 'hidden', 'hiding',
     * 'showing' or 'shown'
     *
     * @return {String}
     */
    getState: function () {
        return this._state;
    },

    /**
     * @method fade
     *
     * Starts the fadeing of the object. Automatically determines if fade should
     * be in or out.
     *
     * @return void
     */
    fade: function () {
        
        if (this._enabled == false) {
            return;
        }
        
        if (this._state == 'hidden' || this._state == 'hiding') {

            if (this._timeoutID !== null || this._timeoutID !== '') {
                window.clearTimeout(this._timeoutID);
            }

            if (this._state == 'hidden') {
                
                if (this._minOpacity == 0) {
                    
                    this._opacity = 0;
                    this._setOpacity();
                    this._fadeObj.style.visibility = 'visible';
                }
            }

            this._state = 'showing';

            this._fadeIn();

        } else {

            this._state = 'hiding';

            if (this._timeoutID !== null || this._timeoutID !== '') {
                window.clearTimeout(this._timeoutID);
            }

            this._fadeOut();
        }
    },

    /**
     * @method setCallbackObj
     *
     * Sets the callback object
     *
     * @return void
     */
    setCallback: function (obj) {
        if (typeof obj == 'object') {
            this._callback = obj;
        }
    },

    /**
     * @method clearCallbackObj
     *
     * Clears the callback object
     *
     * @return void
     */
    clearCallback: function () {
        this._callback = null;
    },

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

    /**
     * @method _setOpacity
     *
     * Sets the opacity on the HTMLElement
     *
     * @return void
     * @private
     */
    _setOpacity: function () {

        switch (this._opacity) {
            case 100:
                this._opacity = 99.999;
                break;
            case 0:
                this._opacity = 0.001;
                break;
        }

        if (typeof this._fadeObj.style.opacity != 'undefined') {
            // Safari 1.2, newer Firefox and Mozilla, CSS3
            this._fadeObj.style.opacity = this._opacity/100;
            return;

        } else if (typeof this._fadeObj.style.filter != 'undefined') {
            // IE/Win
            this._fadeObj.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + this._opacity + ")";
            return;

        } else if (typeof this._fadeObj.style.MozOpacity != 'undefined') {
            // Older Mozilla and Firefox
            this._fadeObj.style.MozOpacity = this._opacity/100;
            return;

        } else if (typeof this._fadeObj.style.KHTMLOpacity != 'undefined') {
            // Safari<1.2, Konqueror
            this._fadeObj.style.KHTMLOpacity = this._opacity/100;
            return;
        } else {
            throw('DDJS.Effects.Fader: Could not set opacity');
        }
    },

    /**
     * @method _fadeIn
     *
     * Executes a single 'fade in' step
     *
     * This method calls itself as necessary to execute a full fade
     *
     * @return void
     * @private
     */
    _fadeIn: function () {

        this._opacity = this._opacity + this._gain;

        if (this._opacity < 100) {

            this._timeoutID = window.setTimeout("DDJS.Effects.execEffect('"+this._objectID+"', '_fadeIn', '');", this._frequency);

            this._setOpacity();

        } else {

            window.clearTimeout(this._timeoutID);
            this._timeoutID = null;

            this._opacity = 100;
            this._setOpacity();

            this._state = 'visible';

            if (this._callback) {
                this._callback.shown();
            }
        }
    },

    /**
     * @method _fadeOut
     *
     * Executes a single 'fade out' step
     *
     * This method calls itself as necessary to execute a full fade
     *
     * @return void
     * @private
     */
    _fadeOut: function () {

        this._opacity = this._opacity - this._gain;

        if (this._opacity > this._minOpacity) {

            this._timeoutID = window.setTimeout("DDJS.Effects.execEffect('"+this._objectID+"', '_fadeOut', '');", this._frequency);

            this._setOpacity();

        } else {

            window.clearTimeout(this._timeoutID);
            this._timeoutID = null;

            this._opacity = this._minOpacity;
            this._setOpacity();

            if (this._minOpacity == 0) {
                this._fadeObj.style.visibility = 'hidden';
            }
            this._state = 'hidden';

            if (this._callback) {
                this._callback.hidden();
            }
        }
    },

    /**
     * @method _stopFade
     *
     * Stops any current fade action by clearing the timeout id
     *
     * @return void
     * @private
     */
    _stopFade: function () {

        if (this._timeoutID !== null || this._timeoutID !== '') {

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

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


