/************************************************************\
* Smooth fading function - useful with onMouse... events     *
* version 1.4 - 5/7/2009                                     *
*                                                            *
* Originally written to stop elements flashing on and off    *
* repeatedly if the mouse moved over them and out again      *
* several times in rapid succession...                       *
*                                                            *
* Specifically, it provides a good, smooth transition        *
* between an element that was actively fading in one         *
* direction/speed and now we want to be doing something      *
* different.                                                 *
*                                                            *
* Copy and use freely, provided this notice is kept in place *
*      Written by Dave Thwaites - dave@thwaites.org.uk       *
\************************************************************/


/*
*
* Use:
* smoothFade(element, fadeTime, fadeTo, isText)
*  element should be the element to be faded
*  fadeTo is a value between 0 (for transparent) and 100 (for opaque)
*  fadeTime is the time it would take to complete the fade, in milliseconds
*   (can be set to zero for and instant change.  less than zero will be treated as zero)
*  isText is a true/false flag.  if set to true the text colour style will be changed,
*   rather than opacity (fadeTo==0 => white text; fadeTo=100 => black text)
*   Looks better in IE6...
*
* So you can do things like:
*
* smoothFade(document.getElementById('image'), 1000, 0)
*   ...to fade the element with the id 'image' to transparent over 1 second.
*
* onMouseOver="smoothFade(this, 100, 100)"
* onMouseOut="smoothFade(this, 800, 40)"
*   ...to fade an element quickly up to opaque when the mouse hovers over it, out fade down
*   to 40% opacity over half a second when the mouse leaves.
*   Note that if the mouse returns over the element while it is fading out, the fade
*   will change direction, starting from whatever opacity has been reached, and will
*   fade up at the same speed that it would normally.
*
*/


var fadeElements = new Array();
// fadeElements[x][0] = element object
// fadeElements[x][1] = fadeTo:     target opacity
// fadeElements[x][2] = fadeFrom:  'original' opacity
// fadeElements[x][3] = fadeSize:  'size' of the fade (ie. fadeTo - fadeFrom)
// fadeElements[x][4] = timeEnd:    end time - in milliseconds since epoch
// fadeElements[x][5] = timeStart: 'start' time - in milliseconds since epoch
// fadeElements[x][6] = fadeTime:   length of the fade, in milliseconds
// fadeElements[x][7] = interval timer id
//                      0 => not fading at the moment!
// fadeElements[x][8] = isText: if true, the element is pure text and we should change
//                        its color rather than its opacity


function smoothFade(element, fadeTime, fadeTo, isText) {
  // fadeTo should be a percentage (ie. in the range 0 - 100)
  // fadeTime is for the entire fade, in miliseconds

  // default values...
  if (fadeTo === undefined) fadeTo = 100;
  if (fadeTime === undefined) fadeTime = 1000;

  // look to see if we've already faded this element before...
  var elementNum = -1;
  var count = fadeElements.length;
  for (i=0; i<count; i++) {
   if (fadeElements[i][0] == element) elementNum = i;
  }

  if (elementNum == -1) {
   // element has not been faded before

   // this detection is here for two reasons
   // 1) it might be technically better to do fading of text this way
   //    (although it restricts us to fading black text...)
   // 2) IE 6 looks rubbish with opacity applied to text
//   isText = (element.nodeName.toLowerCase() == "span") || (element.nodeName.toLowerCase == "#text");

   if (isText === undefined) isText = false;
  } else {
   if (isText === undefined) isText = fadeElements[elementNum][8];
  }

  var fadeNow = getOpacity(element, isText);
  var fadeFrom;

  if (elementNum == -1) {
   // element has not been faded before
   elementNum = count;
   fadeElements[elementNum] = new Array(element, 100, 0, 0, 0, 0, 0, 0);
   fadeFrom = fadeNow;
  } else {
   fadeFrom = fadeElements[elementNum][1];
  }

  // check supplied values fall into sensible ranges...
  if (fadeFrom > 100) fadeFrom = 100;
  if (fadeFrom < 0) fadeFrom = 0;
  if (fadeTo > 100) fadeTo = 100;
  if (fadeTo < 0) fadeTo = 0;
  if (fadeTime < 0) fadeTime = 0;
  if (fadeTo == fadeFrom) {
   if (fadeTo < 50) fadeFrom = 100;
   else fadeFrom = 0;
   fadeTime = 0
  }

  var date = new Date();
  var timeNow = date.getTime();

  // this line will fail if fadeFrom == fadeTo (divide by zero)
  // but we should eliminate that possibility above
  var timeStart = timeNow - (fadeNow - fadeFrom)/(fadeTo - fadeFrom)*fadeTime;
  var timeEnd = timeStart + fadeTime;

  var fadeSize = fadeTo - fadeFrom;

  var stepTime;

  if (fadeTime == 0) {
   // instant fade!
   // set the interval timer to 1ms intervals... should only get called once!
   stepTime = 1;
   // this is to save us a 'divide by zero' error in the interval timer function below...
   fadeTime = 1;
   fadeSize = 0;
  } else {
   stepTime = Math.round(fadeTime / Math.abs((fadeTo - fadeFrom)/((fadeTo > fadeNow) ? 1 : -1)));
  }

  fadeElements[elementNum][1] = fadeTo;
  fadeElements[elementNum][2] = fadeFrom;
  fadeElements[elementNum][3] = fadeSize;
  fadeElements[elementNum][4] = timeEnd;
  fadeElements[elementNum][5] = timeStart;
  fadeElements[elementNum][6] = fadeTime;
  fadeElements[elementNum][8] = isText;

  if (fadeElements[elementNum][7] === 0 || fadeElements[elementNum][7] === undefined) { 
   // we need to set up an interval event for this element.
   // stepTime is the ideal interval time
   // different browsers (Firefox...) may not be able to respond fast enough,
   //  but we'll cope with that later!..
   fadeElements[elementNum][7] = setInterval("smoothFadeDo(" + elementNum + ")", stepTime); 
  }

}

// Firefox seems to have issues calling setTimeouts fast enough
// So... can we check the actual time when the function is called,
// and work out what the opacity value should be from that?
// ...Yup!

function smoothFadeDo(elementNum) {
//  var element =    fadeElements[elementNum][0];
//  var fadeTo =     fadeElements[elementNum][1];
//  var fadeFrom =   fadeElements[elementNum][2];
//  var fadeSize =   fadeElements[elementNum][3];
//  var timeEnd =    fadeElements[elementNum][4];
//  var timeStart =  fadeElements[elementNum][5];
//  var fadeTime =   fadeElements[elementNum][6];
//  var isText =     fadeElements[elementNum][8];
//  var fadeNow = getOpacity(element, isText);
  var date = new Date();
  var timeNow = date.getTime();

  if (timeNow >= fadeElements[elementNum][4]) {
   // all done!  Yay!
   setOpacity(fadeElements[elementNum][0], fadeElements[elementNum][1], fadeElements[elementNum][8]);
   clearInterval(fadeElements[elementNum][7]); 
   fadeElements[elementNum][7] = 0;
  } else {
   setOpacity(fadeElements[elementNum][0], Math.round(fadeElements[elementNum][1] - fadeElements[elementNum][3]*(fadeElements[elementNum][4]-timeNow)/fadeElements[elementNum][6]), fadeElements[elementNum][8]);
  }
}


function getOpacity(element, isText) {
  // default opacity is 100
  var opacity = 100;

  if (!isText || isText === undefined) {
   // if the element has an opacity set, get it
   if (element.style.opacity < 100) {
    opacity = element.style.opacity * 100;
   }
  } else {
   // this is a text element - use color as opacity!..
   if (element.style.color != "") {
    if (element.style.color.substr(0,1) == "#") {
     opacity = 100 - (hex2dec(element.style.color.substr(1,2))+hex2dec(element.style.color.substr(3,2))+hex2dec(element.style.color.substr(5,2)))/3 / 255 * 100;
    } else if (element.style.color.substr(0,3) == "rgb") {
     var offset = element.style.color.indexOf('(',0);
     opacity = parseInt(element.style.color.substr(offset+1));
     offset = element.style.color.indexOf(',', offset+1);
     opacity += parseInt(element.style.color.substr(offset+1));
     offset = element.style.color.indexOf(',', offset+1);
     opacity += parseInt(element.style.color.substr(offset+1));
     opacity = 100 - (opacity/3 / 255 * 100);
    }
   }
  }

  return opacity;
}


//change the opacity for different browsers
function setOpacity(element, opacity, isText) {
  if (!isText || isText === undefined) {
   // this is the propper way of doing it - supported by Chrome, Firefox
   element.style.opacity = (opacity / 100);
//   element.style.MozOpacity = (opacity / 100);
//   element.style.KhtmlOpacity = (opacity / 100);
   // IE 6+7+8 need this
   element.style.filter = "alpha(opacity=" + opacity + ")";
  } else {
   // this is a text element - use color as opacity!..
   // element.style.color = "#" + dec2hex(Math.round((100-opacity)/100*255) * 65793);
   var colour = Math.round((100-opacity)/100*255);
   element.style.color = "rgb(" + colour + ", " + colour + ", " + colour + ")";
  }
}

// These two functions taken from http://javascript.about.com/library/blh2d.htm
function dec2hex(d) {return d.toString(16);}
function hex2dec(h) {return parseInt(h,16);}
