// Slide Show states
var SLIDESHOW_STATE_UNDEFINED = 0;
var SLIDESHOW_STATE_INIT = 1;
var SLIDESHOW_STATE_PAUSE = 2;
var SLIDESHOW_STATE_TRANSITION = 3;

// Slide Show events
var SLIDESHOW_EVENT_START_TRANSITION = 1;
var SLIDESHOW_EVENT_MIDDLE_TRANSITION = 2;
var SLIDESHOW_EVENT_END_TRANSITION = 3;


/*
 * Global structure
 */
var gaoSlideShows = new Array();

/*
 * Data structures
 */
 
/**
 * Slave slideShow
 * param _sSlaveId ID of the slave slide show
 * param _iEvent Event ID to catch
 */
function SlideShowSlave(_sSlaveId, _iEvent)
{
  this.sSlaveId = _sSlaveId; //< Id of the slave slideShow
  this.iEvent = _iEvent;     //< Event to catch
}


/**
 * SlideShow constructor
 * @param _sId ID of the object to use for the slide show
 */
function SlideShow(_sId)
{
  // Attributes
  this.sId = _sId;        //< ID of the Slide Show
  
  this.fMinOpacity = 0;   //< Min opacity to use
  this.fMaxOpacity = 100; //< Max opacity to use
  this.iTimeBeforeStart = 4000; //< Time in ms before the start of the slideshow
  this.iTimePause = 4000;       //< Time in ms to wait between each slide
  this.iTimeTransition = 200;   //< Duration time of the transition
  this.iSmoothTransition = 8;  //< Number of step in the transition (small number means less smooth)
  this.bAutoLoop = false;       //< Automatically loop to the next image when true
  
  this.oRoot = null;        //< Root of the slideshow object
  this.aoElements = null;   //< Array of elements used in the slideshow
  this.iCurrentIdx = 0;     //< Currently displayed element
  this.iNextIdx = 0;        //< Next displayed element
  
  this.oTimer = null;       //< Timer used during the life of the slideshow
  this.oEventTimer = null;  //< Timer used for event handling
  this.iState = SLIDESHOW_STATE_UNDEFINED; //< Current state of the object
  this.aoSlaves = new Array(); //< Array of slave elements
  
  this.fCurrentOpacity = this.fMaxOpacity; //< Current opacity of the slide show
  this.bAutoStart = false;
  this.bEventRaised = false;  //< An event has been raised
  
  this.sComputedLoopCallBack = 0; //< Computed callback loop (to optimize calls)
  this.fComputedOpacityStep = 0;  //< Computed opacity step
  this.fComputedTimeStep = 0;     //< Computed time step
  this.iComputedHalfTimeTransition = 0; //< Reprensents the time spent at the middle of the transition
}

/**
 * Pre compute time and opacity steps
 * @param _sId ID of the slide show
 */
function SlideShow_PreCompute(_sId)
{
  // get the element to pre compute
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    // If we are on Mac, we reduce speed to improve efficiency
    var sAgent = navigator.userAgent.toLowerCase();
    if (sAgent.indexOf("mac") != -1)
    {
      // We reduce the Smooth transition by four
      oSlideShow.iSmoothTransition /= 4;
            
      // And the Speed transition by two
      oSlideShow.iTimeTransition /= 2;
      
      // Protect values (must not be equals to 0 with fade)
      if (oSlideShow.iSmoothTransition < 1)
      {
        oSlideShow.iSmoothTransition = 1;
      }

      if (oSlideShow.iTimeTransition < 1)
      {
        oSlideShow.iTimeTransition = 1;
      }
    }

    // Precomputes callback
    oSlideShow.sComputedLoopCallBack = "SlideShow_Loop('" + _sId + "')";
    
    // Precomputes Opacity steps
    oSlideShow.fComputedOpacityStep = parseFloat(oSlideShow.fMaxOpacity - oSlideShow.fMinOpacity) / parseFloat(oSlideShow.iSmoothTransition);
    
    // Precomputes Time steps
    oSlideShow.fComputedTimeStep = parseFloat(oSlideShow.iTimeTransition) / parseFloat(oSlideShow.iSmoothTransition);

    // Precomputes Half transition time
    oSlideShow.iComputedHalfTimeTransition = oSlideShow.iTimeTransition / 2;
  }
}

/**
 * Returns a Slide Show object from an ID
 * @param _sId ID of the slide show
 * @return Returns the Slide Show object, else returns null
 */
function SlideShow_Get(_sId)
{
  // Default result
  var oRet = null;
  
  // Loop on the main array and try to retrieve the element by its ID
  for (var iKey = 0; (oRet == null) && (iKey < gaoSlideShows.length); iKey++)
  {
    // Have we found the element ?
    if (gaoSlideShows[iKey].sId == _sId)
    {
      // We returns the found element
      oRet = gaoSlideShows[iKey];
    }
  }
  
  // Returns the found element
  return oRet;
}

/**
 * Initializes Elements
 * @param _sId ID of the slide show
 */
function SlideShow_Setup(_sId)
{
  // get the element to Setup
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    // Try to get elements to slide (if they are available)
    if ((document.getElementById) &&
        (oSlideShow.oRoot = document.getElementById(_sId)))
    {
      // We have a pointer on the images, we hide everything
//    oSlideShow.oRoot.style.visibility = "hidden";
      
      // We set the default style
//      oSlideShow.oRoot.style.position = "relative";
      // For each sub nodes elements
      var aoSubList = oSlideShow.oRoot.childNodes;
      var idx = 0;
      // Try to remove all text nodes
      while (idx < aoSubList.length)
      {
        // Have we found a not standard element (like text, attributes, ...)
        if (aoSubList[idx].nodeType != 1)
        {
          // Remove it
          oSlideShow.oRoot.removeChild(aoSubList[idx]);
        }
        else
        {
          // Try with the next node
          idx++;
        }
      }
    
      // We stop try to get elements
      window.clearTimeout(oSlideShow.oTimer);
    }
    else
    {
      // If we are not able to get datas now, try in a while
      oSlideShow.oTimer = window.setTimeout("SlideShow_Setup('" + _sId + "')", 2);
    }
  }
}

/**
 * Raise an event and dispatch it to the registered slave
 * @param _sID ID of the slideshow to use
 * @param _iEvent Event to raise
 */
function SlideShow_RaiseEvent(_sId, _iEvent)
{
 // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);

  // Valid element ?
  if ((oSlideShow != null) && (oSlideShow.aoSlaves != null))
  {
    // Traverse the list of slaves
    for (var idx = 0; idx < oSlideShow.aoSlaves.length; idx++)
    {
      // Check if the slave has the raised event
      if (oSlideShow.aoSlaves[idx].iEvent == _iEvent)
      {
        // Get the slave slide Show
        var oSlave = SlideShow_Get(oSlideShow.aoSlaves[idx].sSlaveId);
        
        // Valid slave ?
        if (oSlave != null)
        {
          // Display the next Slide of the slave
          SlideShow_Next(oSlave.sId);
        }
      }
    }
  }
}

/**
 * Create a new Slide Show.
 * We can't return a pointer on the slide show since the setTimeOut() method
 * can't use callback with Full Objects. We can only create Strings.
 * @param _sID ID of the slideshow to create
 * @param _bAutoStart if true, automatically start the slideshow (false by default)
 * @return Returns true if the creation success, else returns false
 */
function SlideShow_Create(_sId, _bAutoStart)
{
  // Default return value
  var bRet = false;

  // We can create a new slide show if it doesn't exist
  if (SlideShow_Get(_sId) == null)
  {
    // We insert the element in the Hash Table
    var oSlideShow = new SlideShow(_sId);
    
    // Valid creation ?
    if (oSlideShow != null)
    {
      // Insert the element
      gaoSlideShows.push(oSlideShow);
      
      // Setup the slideShow
      SlideShow_Setup(_sId, _bAutoStart);
      
      // AutoStart has been defined ?
      if (_bAutoStart != 'undefined')
      {
        // We define if the slideshow has to start automatically
        oSlideShow.bAutoStart = _bAutoStart;
      }

      // Valid creation
      bRet = true;
    }
  }
  
  // Returns the creation status
  return bRet;
}

/** Synchronize a SlideShow with anoter
 * @param _sMasterId ID of the master slide show
 * @param _sSlaveId ID of the slave slide show
 * @param _iEvent ID of the event used for synchronization (SLIDESHOW_EVENT_START_TRANSITION by default)
 */
function SlideShow_Synchronize(_sMasterId, _sSlaveID, _iEvent)
{
  // Try to get the master element
  var oMaster = SlideShow_Get(_sMasterId);
  
  // Try to get the slave element
  var oSlave = SlideShow_Get(_sSlaveID);
   
  // Valid element ?
  if ((oMaster != null) && (oSlave != null))
  {
    // Stop everything on the slave slideshow
    // Stop all timers
    window.clearTimeout(oSlave.oTimer);
    window.clearTimeout(oSlave.oEventTimer);
    
    // Stop auto loop system
    oSlave.bIsSlave = true;
    
    // Create the slave object used with a default event
    var oEvtSlave = new SlideShowSlave(_sSlaveID, SLIDESHOW_EVENT_START_TRANSITION);

    // the event is defined ?
    if (_iEvent != 'undefined')
    {
      // Set a value
      oEvtSlave.iEvent = _iEvent;
    }
      
    // Insert the slave in the list of master's slaves
    oMaster.aoSlaves.push(oEvtSlave);
  }
}
 
  
// Accessors
  
/**
 * Sets the value of the minimum opacity
 * @param _sId ID of the slide show to use
 * @param _fMinOpacity Value of the minimum opacity
 */
function SlideShow_SetMinOpacity(_sId, _fMinOpacity)
{
  // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);
   
  // Valid element ?
  if (oSlideShow != null)
  {
    // Set the minimum opacity
    oSlideShow.fMinOpacity = _fMinOpacity;
    SlideShow_PreCompute(_sId);
  }
}

/**
 * Sets the value of the maximum opacity
 * @param _sId ID of the slide show to use
 * @param _fMaxOpacity Value of the maximum opacity
 */
function SlideShow_SetMaxOpacity(_sId, _fMaxOpacity)
{
  // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    oSlideShow.fMaxOpacity = _fMaxOpacity;
    SlideShow_PreCompute(_sId);
  }
}

/**
 * Sets the delay between the start call and the real start of the slideshow
 * @param _sId ID of the slide show to use
 * @param _iTimeBeforeStart Value of the delay (in milliseconds)
 */
function SlideShow_SetTimeBeforeStart(_sId, _iTimeBeforeStart)
{
  // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    oSlideShow.iTimeBeforeStart = _iTimeBeforeStart;
  }
}

/**
 * Sets the delay between two slides
 * @param _sId ID of the slide show to use
 * @param _iTimePause Delay in ms before the next slide
 */
function SlideShow_SetTimePause(_sId, _iTimePause)
{
  // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    oSlideShow.iTimePause = _iTimePause;
  }
}

/**
 * Sets the delay used by the transition effect
 * @param _sId ID of the slide show to use
 * @param _iTimeTransition Transition delay in ms
 */
function SlideShow_SetTimeTransition(_sId, _iTimeTransition)
{
  // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    oSlideShow.iTimeTransition = _iTimeTransition;
    SlideShow_PreCompute(_sId);
  }
}

/**
 * Sets the transition smooth value
 * @param _sId ID of the slide show to use
 * @param _iSmoothTransition Smooth transition value. (small numbers means more cuts)
 */
function SlideShow_SetSmoothTransition(_sId, _iSmoothTransition)
{
  // Try to get the element
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    oSlideShow.iSmoothTransition = _iSmoothTransition;
    SlideShow_PreCompute(_sId);
  }
}

/**
 * Fade an element (Cross browser compatible)
 * @param _oElement Element to fade
 * @param _fOpacity Opacity value of the element
 */
function SlideShow_FadeElement(_oElement, _fOpacity)
{
  // Valid parameter ?
  if (_oElement != null)
  {
    // Try for each browser
    
    // Mozilla's pre-CSS3 proprietary rule
    if (_oElement.style.MozOpacity != null)
    {
      _oElement.style.MozOpacity = (_fOpacity / 100) - .001;
    }
    // CSS3 compatible
    else if (_oElement.style.opacity != null)
    {
      _oElement.style.opacity = (_fOpacity / 100) - .001;
    }
    // IE's proprietary filter
    else if (_oElement.style.filter != null)
    {
      _oElement.style.filter = "alpha(opacity = " + _fOpacity + ")";

      // *** FIX IE BUG WITH WHITE DOTS IN IMAGE ***
      if (_fOpacity == 100)
      {
       _oElement.style.filter = null;
      }      
    }
  }
}

/**
 * Stop the slide show
 * @param _sId ID of the slide show to stop
 */
function SlideShow_Stop(_sId)
{
  // Get the Slide show from the ID
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    // Set the image as visible
    oSlideShow.fCurrentOpacity = oSlideShow.fMaxOpacity;
    SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iCurrentIdx], oSlideShow.fMaxOpacity);
    
    // Stop loop
    oSlideShow.bAutoLoop = false;
  }
}

/**
 * Force the transition to the next Slide
 * @param _sId ID of the slide show
 */
function SlideShow_Next(_sId)
{
  // Get the Slide show from the ID
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    // Stop current process
    if (oSlideShow.oTimer != null)
    {
      window.clearTimeout(oSlideShow.oTimer);
    }
    
    // Stop the event timer (if it exists ?)
    if (oSlideShow.oEventTimer != null)
    {
      window.clearTimeout(oSlideShow.oEventTimer);
    }
  
    // Are we in a transition State ?
    if (oSlideShow.iState == SLIDESHOW_STATE_TRANSITION)
    {
      // Go to the next Slide
      SlideShow_NextSlide(_sId);
    }
    
    // Start the Transition
    SlideShow_Loop(_sId);
  }
}

/**
 * Go to the next Slide of the slide show
 * @param _sId ID of the slide show to use
 */
function SlideShow_NextSlide(_sId)
{
  // Get the Slide show from the ID
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    oSlideShow.fCurrentOpacity = oSlideShow.fMinOpacity;
    SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iCurrentIdx], oSlideShow.fMinOpacity);
    SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iNextIdx], oSlideShow.fMaxOpacity);

    // We traverse all the elements and we increase their Z Order
    for (var idx = 0; idx < oSlideShow.aoElements.length; idx++)
    {
      // Increases the Z order;
      oSlideShow.aoElements[idx].style.zIndex++;
    }
    
    // We put the current element on the back
    oSlideShow.aoElements[oSlideShow.iCurrentIdx].style.zIndex = 1;
    oSlideShow.aoElements[oSlideShow.iCurrentIdx].style.visibility = 'hidden';
  
    // We set the max opacity to the back element
    SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iCurrentIdx], oSlideShow.fMinOpacity);

    // Go to the next element
    oSlideShow.iCurrentIdx = oSlideShow.iNextIdx
    oSlideShow.iNextIdx = (oSlideShow.iNextIdx + 1) % oSlideShow.aoElements.length;
    oSlideShow.aoElements[oSlideShow.iNextIdx].style.visibility = 'visible';

    // Reset opacity for the new image
    oSlideShow.fCurrentOpacity = oSlideShow.fMaxOpacity;
    SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iCurrentIdx], oSlideShow.fCurrentOpacity);
  }
}
  
/**
 * Main loop function. Auto fade datas
 * @param _sId ID of the slide show to use
 */
function SlideShow_Loop(_sId)
{
  // Get the Slide show from the ID
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    // Have we reached the max in the opacity change ?
    if (oSlideShow.fCurrentOpacity >= oSlideShow.fMinOpacity)
    {
      // We were not on transition ?
      if (oSlideShow.iState != SLIDESHOW_STATE_TRANSITION)
      {
        // Go to the transition state
        oSlideShow.iState = SLIDESHOW_STATE_TRANSITION;
        
        // Raise the start transition event
        SlideShow_RaiseEvent(_sId, SLIDESHOW_EVENT_START_TRANSITION);

        // We will raise the middle event transition in a while
        oSlideShow.oEventTimer = window.setTimeout("SlideShow_RaiseEvent('" + _sId + "', SLIDESHOW_EVENT_MIDDLE_TRANSITION)",oSlideShow.iComputedHalfTimeTransition);
      }
    
      // Fade the current element
      SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iCurrentIdx], oSlideShow.fCurrentOpacity);
      SlideShow_FadeElement(oSlideShow.aoElements[oSlideShow.iNextIdx], oSlideShow.fMaxOpacity - oSlideShow.fCurrentOpacity);

      // Increase the opacity
      oSlideShow.fCurrentOpacity -= oSlideShow.fComputedOpacityStep;
    
      // Wait before the next change
      oSlideShow.oTimer = window.setTimeout(oSlideShow.sComputedLoopCallBack, oSlideShow.fComputedTimeStep);
    }
    else
    {
      // Go to the next element
      SlideShow_NextSlide(_sId);
      
      // Raise the end Transition event
      SlideShow_RaiseEvent(_sId, SLIDESHOW_EVENT_END_TRANSITION);
      
      // We are in a pause
      oSlideShow.iState = SLIDESHOW_STATE_PAUSE;
    
      // AutoLoop ?
      if ((oSlideShow.bAutoLoop) && (!oSlideShow.bIsSlave))
      {
        // Make a pause before the next slide
        oSlideShow.oTimer = window.setTimeout(oSlideShow.sComputedLoopCallBack, oSlideShow.iTimePause);
      }
    }
  }
}

/**
 * Start the automatic slide show loop.
 * @param _sId ID of the slide show to start
 */
function SlideShow_Start(_sId)
{
  // Get the Slide show from the ID
  var oSlideShow = SlideShow_Get(_sId);
  
  // Valid element ?
  if (oSlideShow != null)
  {
    // Use auto Loop
    oSlideShow.bAutoLoop = true;
    
    // Start the loop
    oSlideShow.oTimer = window.setTimeout(oSlideShow.sComputedLoopCallBack, oSlideShow.iTimeBeforeStart);
  }
}


/**
 * Initialize the slide show
 * @param _sId ID of the object to use for the slide show
 */
function SlideShow_Init(_sId, _bAutoStart)  
{
  // Get the SlideShow
  var oSlideShow = SlideShow_Get(_sId);

  // Valide element ?
  if (oSlideShow != null)
  {
    // Set the initial state
    oSlideShow.iState = SLIDESHOW_STATE_INIT;
  
    // Call the setup (just to be sure) (problems can occurs with IE if not done)
    SlideShow_Setup(_sId);
    
    // Precomputes times and strings
    SlideShow_PreCompute(_sId);

    // Get the list of Images nodes
    oSlideShow.aoElements = oSlideShow.oRoot.childNodes;

    // Set the first image visible
    oSlideShow.aoElements[0].style.zIndex = oSlideShow.aoElements.length;
    oSlideShow.aoElements[0].style.visibility='visible';
    

    // Traverse all the nodes
    for(var idx = 1; idx < oSlideShow.aoElements.length; idx++)
    {
      // Set the initial style (not visible)
      oSlideShow.aoElements[idx].style.zIndex = oSlideShow.aoElements.length - idx;
		
      // Set their opacity to invisible
      SlideShow_FadeElement(oSlideShow.aoElements[idx], oSlideShow.fMinOpacity);
    }

    // Set the next image visible
    if(oSlideShow.aoElements.length > 1)
    {
      oSlideShow.aoElements[1].style.visibility = 'visible';
    }
	  
    // Initialize the loop system
    oSlideShow.iCurrentIdx = 0;
    oSlideShow.iNextIdx = 1;
  
    // Set the image as visible and on top
    oSlideShow.fCurrentOpacity = oSlideShow.fMaxOpacity;
    
    
    // Start the main loop if auto start is enabled
    if ((oSlideShow.bAutoStart) && (!oSlideShow.bIsSlave))
    {
      SlideShow_Start(_sId);
    }
  }
}


// onLoad event Handler.
// Automatic process to initialize the pages

// Initialize fader by hiding image object first
SlideShow_AddEvent(window, 'load', SlideShow_OnLoad)

// Called on initialization
function SlideShow_OnLoad()
{
  // We traverse the list of Elements, and we call the _Init method for each of them
  for (var idx = 0; idx < gaoSlideShows.length; idx++)
  {
    // Call the Init function with the id in parameters
    SlideShow_Init(gaoSlideShows[idx].sId);
  }
}

/**
 * addEvent Handler (cross browser compatible)
 * @param _oElement Name of the element where we add events
 * @param _sType Name of the element type
 * @param _fCallBack function to call when event received
 * @param _bUseCapture
 */
function SlideShow_AddEvent(_oElement, _sType, _fCallBack, _bUseCapture)
{
  // Can we use the function ?
  if (_oElement.addEventListener)
  {
    // Add a listener
    _oElement.addEventListener(_sType, _fCallBack, _bUseCapture);
  }
  // Browser compatible with attachEvent ?
  else if (_oElement.attachEvent)
  {
    // Add a listener
    _oElement.attachEvent("on" + _sType, _fCallBack);
  }
}
