﻿/* <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";
    }
      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;
    }
  }
}