/* <gslb_version version="V1.0.4" /> */
/* Copyright © GalaSoft Laurent Bugnion 2007 */

//*****************************************************************************
//* gslb.fadednode.js
//*****************************************************************************
//* Control name            : FadedNode
//* Tested Targets          : Firefox 2, Internet Explorer 7
//* Language/Compiler       : ECMAScript V3
//* Author                  : Laurent Bugnion (LBu)
//* Created                 : 05.01.2007
//*****************************************************************************
//* Description: see constructor
//* Last Base Level: BL0003
//*****************************************************************************

//*****************************************************************************
//* Imports *******************************************************************
//*****************************************************************************

//*****************************************************************************
//* Constructor ***************************************************************
//*****************************************************************************

// Create namespace
if (!window.gslb) {
    window.gslb = {};
}

// ----------------------------------------------------------------------------
/// <summary>
/// Allows transitioning smoothly from one opacity level to an other for
/// DOM nodes. Supports CSS filter (IE) and CSS opacity (Mozilla, Firefox).
/// </summary>
/// <param name="nNode" type="Node">The DOM node to which the transitions
/// will be applied</param>
/// <param name="oOptions" type="Object">An Options object, constructed using literal notation.
/// All values are optional.
/// <br />iMinimum: The minimum level of opacity applied to the Node. Default: 0%.
/// <br />iMaximum: The maximum level of opacity applied to the Node. Default: 100%.
/// <br />iDeltaFade: The amount added or removed to the opacity on each iteration
/// during transitions (fading/unfading). Default: 1%.
/// <br />iTimerMilliseconds: The duration of one iteration (in milliseconds)
/// during transition (fading/unfading). Default: 10 milliseconds.
/// <br />bRemoveAfterFade: If true, when the node reaches the minimum level of
/// opacity, it will be removed (ie display : none), so that underlying controls
/// can be actioned. This is mostly useful when the minimum level is 0. To enable
/// this functionality, the Node must have CSS display set in its style. Default: true.
gslb.FadedNode = function(nNode, oOptions) {
    if (!nNode
    || !nNode.style == null
    || (!nNode.style.filter == null
      && !nNode.style.opacity == null)) {
        throw "Impossible to create faded node, please check the documentation";
    }

    //***************************************************************************
    //* Attributes **************************************************************
    //***************************************************************************

    this.m_nNode = nNode;

    this.m_bFading = false;
    this.m_bUnfading = false;
    this.m_iStart = gslb.FadedNode.getOpacity(nNode);
    this.m_iFadeFactor = this.m_iStart;

    // Options

    this.m_iMinimumFadeFactor = 0;
    this.m_iMaximumFadeFactor = 100;
    this.m_iDeltaFade = 1;
    this.m_iTimer = 10;
    this.m_bRemoveAfterFade = false;

    this.setOptions(oOptions);
}

//*****************************************************************************
//* Enums *********************************************************************
//*****************************************************************************

//*****************************************************************************
//* Constants *****************************************************************
//*****************************************************************************

/// <summary type="string">
/// This class's name.
/// </summary>
gslb.FadedNode._NAME = "gslb.FadedNode";
/// <summary type="string">
/// This class's version.
/// </summary>
gslb.FadedNode._VERSION = "V1.0.4";
/// <summary type="string">
/// This class's date.
/// </summary>
gslb.FadedNode._DATE = "17.11.2007";

//*****************************************************************************
//* Static attributes *********************************************************
//*****************************************************************************

//*****************************************************************************
//* Static methods ************************************************************
//*****************************************************************************

// Utilities ------------------------------------------------------------------

// ----------------------------------------------------------------------------
/// <summary>
/// Utility method, gets the opacity level from a given DOM Node.
/// </summary>
/// <param name="nNode" type="Node">A DOM node with either opacity (Mozilla,
/// Firefox) or filter (IE) set.</param>
/// <return type="int">The current level of opacity set in CSS in %.</return>
gslb.FadedNode.getOpacity = function(nNode) {
    if (!nNode
    || !nNode.style
    || (!nNode.style.filter
      && !nNode.style.opacity)) {
        return 100;
    }

    if (nNode.style.filter) {
        return parseInt(nNode.style.filter.substring(nNode.style.filter.indexOf("=") + 1), 10);
    }

    if (nNode.style.opacity) {
        return parseFloat(nNode.style.opacity) * 100;
    }
}

// ----------------------------------------------------------------------------
/// <summary>
/// Utility method, sets the opacity level to a given DOM Node.
/// </summary>
/// <param name="nNode" type="Node">A DOM node with either opacity (Mozilla,
/// Firefox) or filter (IE) set.</param>
/// <param name="iOpacityPercents">The new level of opacity in %.</param>
gslb.FadedNode.setOpacity = function(nNode, iOpacityPercents) {
    if (iOpacityPercents < 0) {
        iOpacityPercents = 0;
    }
    if (iOpacityPercents > 100) {
        iOpacityPercents = 100;
    }

    if (nNode.style.filter != null) {
        nNode.style.filter = "alpha(opacity=" + iOpacityPercents + ");";
    }
    if (nNode.style.opacity != null) {
        nNode.style.opacity = iOpacityPercents / 100;
    }
}

//*****************************************************************************
//* Methods *******************************************************************
//*****************************************************************************

// Status ---------------------------------------------------------------------

// ----------------------------------------------------------------------------
/// <summary>
/// Returns true if the Node reached the minimum level of opacity
/// </summary>
gslb.FadedNode.prototype.isFaded = function() {
    return (this.m_iFadeFactor == this.m_iMinimumFadeFactor);
}

// ----------------------------------------------------------------------------
/// <summary>
/// Returns true if the Node reached the maximum level of opacity
/// </summary>
gslb.FadedNode.prototype.isUnfaded = function() {
    return (this.m_iFadeFactor == this.m_iMaximumFadeFactor);
}

// Fading ---------------------------------------------------------------------

// ----------------------------------------------------------------------------
/// <summary>
/// Starts a transition from the maximum level of opacity to the minimum level.
/// Note: If the Node already reached the minimum level of opacity, or if a
/// transition is currently running, this method is not active.
/// </summary>
/// <param name="fnOnTransitionEnd" type="Function">A function to be executed
/// when the transition is finished. Can also be null.</param>
gslb.FadedNode.prototype.fade = function(fnOnTransitionEnd) {
    if (this.m_bFading
    || this.m_bUnfading
    || this.m_iFadeFactor == this.m_iMinimumFadeFactor) {
        if (fnOnTransitionEnd) {
            fnOnTransitionEnd();
        }
        return;
    }
    this.m_fnOnTransitionEnd = fnOnTransitionEnd;
    this.fading();
}

// ----------------------------------------------------------------------------
/// <summary>
/// Internal method only.
/// </summary>
gslb.FadedNode.prototype.fading = function() {
    if (this.m_iFadeFactor > this.m_iMinimumFadeFactor) {
        this.m_bFading = true;
        this.m_iFadeFactor -= this.m_iDeltaFade;
    }
    else {
        this.m_bFading = false;
        if (this.m_bRemoveAfterFade
      && this.m_nNode.style.display) {
            // Avoid that the underneath controls are blocked by the invisible layer.
            this.m_nNode.style.display = "none";
        }
    }

    gslb.FadedNode.setOpacity(this.m_nNode, this.m_iFadeFactor);

    if (this.m_bFading) {
        var that = this;
        setTimeout(function() { that.fading(); }, this.m_iTimer);
    }
    else {
        if (this.m_fnOnTransitionEnd) {
            this.m_fnOnTransitionEnd();
            this.m_fnOnTransitionEnd = null;
        }
    }
}

// ----------------------------------------------------------------------------
/// <summary>
/// Starts a transition from the minimum level of opacity to the maximum level.
/// Note: If the Node already reached the maximum level of opacity, or if a
/// transition is currently running, this method is not active.
/// </summary>
/// <param name="fnOnTransitionEnd" type="Function">A function to be executed
/// when the transition is finished. Can also be null.</param>
gslb.FadedNode.prototype.unfade = function(fnOnTransitionEnd) {
    if (this.m_bUnfading
    || this.m_bFading
    || this.m_iFadeFactor == this.m_iMaximumFadeFactor) {
        if (fnOnTransitionEnd) {
            fnOnTransitionEnd();
        }
        return;
    }
    if (this.m_nNode.style.display) {
        this.m_nNode.style.display = "block"
    }
    this.m_fnOnTransitionEnd = fnOnTransitionEnd;
    this.unfading();
}

// ----------------------------------------------------------------------------
/// <summary>
/// Internal method only.
/// </summary>
gslb.FadedNode.prototype.unfading = function() {
    if (this.m_iFadeFactor < this.m_iMaximumFadeFactor) {
        this.m_bUnfading = true;
        this.m_iFadeFactor += this.m_iDeltaFade;
    }
    else {
        this.m_bUnfading = false;
    }

    gslb.FadedNode.setOpacity(this.m_nNode, this.m_iFadeFactor);

    if (this.m_bUnfading) {
        var that = this;
        setTimeout(function() { that.unfading(); }, this.m_iTimer);
    }
    else {
        if (this.m_fnOnTransitionEnd) {
            this.m_fnOnTransitionEnd();
            this.m_fnOnTransitionEnd = null;
        }
    }
}

// ----------------------------------------------------------------------------
/// <summary>
/// Sets the new level of opacity for this faded node. The level is applied with a transition.
/// Note: It is possible to set the new opacity outside of the range defined by the options for this node.
/// </summary>
/// <param name="iOpacityPercents" type="int">The new level of opacity in %.</param>
/// <param name="fnOnTransitionEnd" type="Function">A function to be executed
/// when the transition is finished. Can also be null.</param>
gslb.FadedNode.prototype.setOpacity = function(iOpacityPercents, fnOnTransitionEnd) {
    if (iOpacityPercents > this.m_iFadeFactor) {
        // Unfading required
        var iCurrentMaximum = this.m_iMaximumFadeFactor;
        var bCurrentRemoveAfterFade = this.m_bRemoveAfterFade;
        this.m_iMaximumFadeFactor = iOpacityPercents;
        this.m_bRemoveAfterFade = false;
        // Use closure to reset the maximum and minimum
        var that = this;
        this.unfade(function() {
            that.resetOptions(this.m_iMinimumFadeFactor,
          iCurrentMaximum,
          bCurrentRemoveAfterFade);

            if (fnOnTransitionEnd) {
                fnOnTransitionEnd();
            }
        });
    }

    if (iOpacityPercents < this.m_iFadeFactor) {
        // Fading required
        var iCurrentMinimum = this.m_iMinimumFadeFactor;
        var bCurrentRemoveAfterFade = this.m_bRemoveAfterFade;
        this.m_iMinimumFadeFactor = iOpacityPercents;
        this.m_bRemoveAfterFade = false;
        // Use closure to reset the maximum and minimum
        var that = this;
        this.fade(function() {
            that.resetOptions(iCurrentMinimum,
          that.m_iMaximumFadeFactor,
          bCurrentRemoveAfterFade);

            if (fnOnTransitionEnd) {
                fnOnTransitionEnd();
            }
        });
    }
}

// ----------------------------------------------------------------------------
/// <summary>
/// Internal method only.
/// </summary>
gslb.FadedNode.prototype.resetOptions = function(iMinimum, iMaximum, bRemoveAfterFade) {
    this.m_iMinimumFadeFactor = iMinimum;
    this.m_iMaximumFadeFactor = iMaximum;
    this.m_bRemoveAfterFade = bRemoveAfterFade;
}

// Setter/Getter for options --------------------------------------------------

// ----------------------------------------------------------------------------

/// <summary>
/// Sets the options for this instance.
/// </summary>
/// <param name="oOptions">An Options object, constructed using literal notation.
/// All values are optional.
/// <br />iMinimum: The minimum level of opacity applied to the Node. Default: 0%.
/// <br />iMaximum: The maximum level of opacity applied to the Node. Default: 100%.
/// <br />iDeltaFade: The amount added or removed to the opacity on each iteration
/// during transitions (fading/unfading). Default: 1%.
/// <br />iTimerMilliseconds: The duration of one iteration (in milliseconds)
/// during transition (fading/unfading). Default: 10 milliseconds.
/// <br />bRemoveAfterFade: If true, when the node reaches the minimum level of
/// opacity, it will be removed (ie display : none), so that underlying controls
/// can be actioned. This is mostly useful when the minimum level is 0. To enable
/// this functionality, the Node must have CSS display set in its style. Default: true.
gslb.FadedNode.prototype.setOptions = function(oOptions) {
    if (oOptions) {
        if (oOptions.iMinimum != null) {
            this.m_iMinimumFadeFactor = oOptions.iMinimum;
        }

        if (oOptions.iMaximum != null) {
            this.m_iMaximumFadeFactor = oOptions.iMaximum;
        }

        if (oOptions.iDeltaFade != null) {
            this.m_iDeltaFade = oOptions.iDeltaFade;
        }

        if (oOptions.iTimerMilliseconds != null) {
            this.m_iTimer = oOptions.iTimerMilliseconds;
        }

        if (oOptions.bRemoveAfterFade != null) {
            this.m_bRemoveAfterFade = oOptions.bRemoveAfterFade;
        }
    }
}