//
// Improved Balloon Tooltips
// By: Dave Hennessey 2008-03-26
// 
// Show links or span "titles" as fancy ballons rather than
// the rather drab default browser display
//
// Based on the work of Allesandro Fulcinetti
//
// My first improvement is that the balloons always appear in the 
// current viewport, where Allesandro's sometimes went off screen
// to the right or bottom.
//
// My solution is to have the balloons appear to the top and left
// of each link.  If the balloon would appear off-screen to the top or left,
// I just change their orientation to bottom or right, and adjust the position.
//
// This requires "assembling" the balloon from parts at showBalloon() (onMouseOver),
// where Allesando's version stored static balloons in the the links at page load.
//
// This made it a lot more complicated.  There are 3 constituent parts of the balloon,
// but the top can be one of three different shapes depending on balloon orientation.
// Likewise for the bottom.  So, this requires 7 different .GIF files to cover 
// all orientations.   However, I eliminated the external CSS file and 
// migrated the CSS code into JavaScript.
//
// The default top left orientation requires a hack that I don't like,
// but I'm not sure that any perfect solution can be found. Read on...
//
// To tell whether the balloon would be off-screen to the top, I had to guesstimate
// how many lines the text in the balloon would occupy.  This depends on the size of 
// words in the text, the font-face, font-size, the word-wrap algorithm used
// by whatever browser you use, and the size of the words you use - if you 
// are doing a medical journal, or German, you'll have some really long words.
// 
// My simplistic guesstimate will probably be correct most of the time, but errs 
// on the side of caution - too many lines rather than too few.  This means that the 
// balloon may appear one line higher that it should in the top-left or
// top-right orientations.  This is way more acceptable than having the text appear
// outside the balloon.
//
// Firefox presented an unusual problem in that if a balloon covered another link,
// mouseOver would not subsequently "see" the previously covered link, and would 
// not fire the mouseOver event.  This required the complications seen in hideBalloon()
//
// My second improvement was to make this work with <SPAN>s rather than just with links.
// So all you have to do is to wrap your "trigger text" thusly:
// <SPAN title="this is some balloon text">Trigger text</SPAN>
//
// My third improvement is to provide a list of parameters at the top of 
// the script, to make it simple to adapt to differing circumstances.
//
// There are a bunch more little improvements, but I can't remember them all!
// 
// Dave
//

// ----------------------------------------------------------------------
// Global Variables
// Things you might want to adjust
// ----------------------------------------------------------------------
//
// Overall Dimensions
//
var balloonWidth = 200;			// Width of the balloon - pixels
var balloonPlainCapHeight = 10;	// Height of the "plain cap" - pixels
var balloonArrowCapHeight = 10;	// Height of the "arrow "cap" - pixels
var balloonArrowOffset = 40;		// Offset of arrow point from right edle of balloon - pixels

var balloonArrowStandoffX = 5;	// Standoff of arrow point from mouse pointer - pixels
var balloonArrowStandoffY = 5;	// Standoff of arrow point from mouse pointer - pixels

//
// Font Size Variables
//
//  Suggested: FontFamily: "Arial" FontSize=10; FontLeading=6; FontMultiplier=.67;
//
var balloonFontFamily = "Arial";
var balloonFontSize = 10;		// Size of the text - points
var balloonFontLeading = 6;		// Size of the text leading (usually .6 * FontSize) - points
var balloonFontMultiplier = .67;	// To approximate the average width of characters
						// Increase if balloons are too long (range about 0.5 to 1.0)
//
// Padding around the text 
//
var balloonTextPaddingV = 0;		// above / below - pixels
						// Note: balloonTextPaddingV MUST BE LESS THAN balloonArrowStandoffY
var balloonTextPaddingH = 7;		// left / right - pixels
						// Note: Large value might affect balloonFontMultiplier

//
// Do we want to process A tags and/or SPAN tags that have title="something" ?
// 0 = false, 1 = true;
//
balloonProcessAtags = 1;
balloonProcessSPANtags = 1;

//
// For SPANs, we need to change the color of the text so that people will recognize
//  as things to put their mouse on
//
spanReplacementColor = "#ff2200";


// ----------------------------------------------------------------------
//
// If you muck around below this line, you do so at your own peril...
//
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// called by BODY onLoad, this creates the balloon structure
// and renames the A 'title' attributes so the browser won't display them
// changes them to 'balloontip' attributes which we'll use at onMouseOver
// Same for SPANs, except browsers don't normally display "title=" for SPANs
// ----------------------------------------------------------------------
function startBalloons()
{
  var x;

  // 
  // create a new span element that will hold the entire balloon
  //
  balloon = document.createElement("span");
  balloon.setAttribute("name","balloon");
  balloon.setAttribute("id","balloon");
  balloon.style.display = "block";
  balloon.style.position = "absolute";
  balloon.style.width  = balloonWidth + 'px';
  //
  // set the opacity
  //
  balloon.style.filter="alpha(opacity:95)";
  balloon.style.KHTMLOpacity = "0.95";
  balloon.style.MozOpacity = "0.95";
  balloon.style.opacity = "0.95";  
  //
  // put this span under the body element
  //
  x = document.getElementsByTagName("body");
  x[0].appendChild(balloon);

  //
  // create a new span element that will hold the balloon top
  //
  balloonTop = document.createElement("span");
  balloonTop.setAttribute("name", "balloonTop");
  balloonTop.setAttribute("id", "balloonTop");
  balloonTop.style.display = "block";
  balloonTop.style.padding = "0px 0px 0px 0px";
  balloonTop.style.width  = balloonWidth + 'px';
  //
  // put this span under the balloon element
  //
  x = document.getElementById("balloon");
  x.appendChild(balloonTop);

  //
  // create a new span element that will hold the balloon middle
  //
  balloonMid = document.createElement("span");
  balloonMid.setAttribute("name", "balloonMid");
  balloonMid.setAttribute("id", "balloonMid");
  balloonMid.style.display = "block";
  balloonMid.style.padding = balloonTextPaddingV + 'px ' + balloonTextPaddingH + 'px ' + balloonTextPaddingV + 'px ' +  balloonTextPaddingH + 'px';
  balloonMid.style.fontFamily = balloonFontFamily;
  balloonMid.style.fontSize = balloonFontSize + 'pt';
  balloonMid.style.textAlign = "left";
  //
  // put this span under the balloon element
  //
  x = document.getElementById("balloon");
  x.appendChild(balloonMid);
  //
  // and create a text node for HTML under the balloonMid
  //
  x = document.getElementById("balloonMid");
  x.appendChild(document.createTextNode(""));
  
  //
  // create a new span element that will hold the balloon bottom
  //
  balloonBot = document.createElement("span");
  balloonBot.setAttribute("name", "balloonBot");
  balloonBot.setAttribute("id", "balloonBot");
  balloonBot.style.display = "block";
  balloonBot.style.padding = "0px 0px 0px 0px";
  balloonBot.style.width  = balloonWidth + 'px';
  //
  // and put this span under the balloon element
  //
  x = document.getElementById("balloon");
  x.appendChild(balloonBot);

  //
  // Prepare all the A tags for balloons
  // change the title attribute to a balloontip attribute
  // and set the mouseOver / mouseOut event handlers
  //
  if (balloonProcessAtags) {
    x = document.getElementsByTagName("a");
    for (i=0; i<x.length; i++) {
      if (x[i].title != "") {
        x[i].balloontip = x[i].title;
        x[i].removeAttribute("title");
        x[i].onmouseover = showBalloon;
        x[i].onmouseout  = hideBalloon;
      }
    }
  }

  //
  // Prepare all the SPAN tags for balloons
  // change the title attribute to a balloontip attribute
  // and set the mouseOver / mouseOut event handlers
  //
   if (balloonProcessSPANtags) {
    x = document.getElementsByTagName("span");
    for (i=0; i<x.length; i++) {
      if (x[i].title != "") {
        x[i].balloontip = x[i].title;
        x[i].removeAttribute("title");
        x[i].style.color = spanReplacementColor;
        x[i].onmouseover = showBalloon;
        x[i].onmouseout  = hideBalloon;
      }
    }
  }


} // END function start


// ----------------------------------------------------------------------
// onMouseOver event, display the balloons
// ----------------------------------------------------------------------
function showBalloon(e) 
{
  var posx=0;
  var posy=0;
  var balloontip;
  var orientationX;
  var orientationY;
  var textLines;
  var textPixels;
  var totalPixels;

  // Object Detection required
  // Firefox sets e to the event
  //   the target is e.target
  // IE you gotta get the event from window.event
  //   the target is window.event.srcElement
  //
  // posx, posy are the coordinates within the document
  // e.clientX, e.clientY are coordinates within the viewport

  if (e==null) {
    // IE
    e = window.event;
    balloontip = e.srcElement.balloontip;
  } else {
    // Firefox
    balloontip = e.target.balloontip;
  }

  if (e.pageX || e.pageY) {
    // Firefox (everyone but IE)
    posx = e.pageX;
    posy = e.pageY;

  } else if(e.clientX || e.clientY) {
    // IE, but Strict or Quirks mode?
    if (document.documentElement.scrollTop) {
      // IE Strict mode (also Firefox Strict)
      posx = e.clientX + document.documentElement.scrollLeft;
      posy = e.clientY + document.documentElement.scrollTop;
    } else {
      // IE Quirks mode (also Firefox Quirks)
      posx = e.clientX + document.body.scrollLeft;
      posy = e.clientY + document.body.scrollTop;
    }
  }

  //
  // Figure out how much space the balloontip text requires
  // in the balloontip window - rough calc
  //
  textLines = Math.ceil(balloontip.length / (balloonWidth / (balloonFontSize * balloonFontMultiplier)));
  textPixels  = textLines * (balloonFontSize + balloonFontLeading);
  // alert('balloontip.length: ' + balloontip.length + '\r\ntextLines: ' + textLines + '\r\ntextPixels: ' + textPixels);
  totalPixels = textPixels + balloonArrowCapHeight + balloonPlainCapHeight;


  //
  // default top-left orientation
  //
  // top-left = balloon above and to left of pointer
  // note - left means balloon-arrow points right
  orientationX = "left";
  orientationY = "top";  

  x = document.getElementById("balloon");
  x.style.top = (posy - (balloonArrowStandoffY + totalPixels)) + "px";

  // is the right edge of the balloon off the screen?
  // if so, kick the balloon left by the required amount
  // Modern browsers all support clientWidth
  y = document.getElementsByTagName("body");
  y = y[0];
  if ((y.clientWidth - posx) >= balloonArrowOffset) {
    x.style.left = (posx - (balloonWidth - balloonArrowOffset)) + "px";
  } else {
    x.style.left = ((posx - (balloonWidth - balloonArrowOffset)) - (balloonArrowOffset - (y.clientWidth - posx))) + "px";
  }

  //
  // is it off the left edge of screen?
  // change X orientation ro "right"
  //
  if (e.clientX < (balloonWidth - balloonArrowOffset)) {
    // off left edge of screen
    orientationX = "right";
    if (e.clientX < (balloonArrowOffset - balloonArrowStandoffX)) {
      x.style.left = (posx - balloonArrowOffset + balloonArrowStandoffX) + (balloonArrowOffset - e.clientX) + "px";
    } else {
      x.style.left = (posx - balloonArrowOffset + balloonArrowStandoffX) + "px";
    }
  }

  //
  // is it off the top edge of screen?
  // change Y orientation to "bottom"
  //
  if (e.clientY < (balloonArrowStandoffY + totalPixels)) {
    // off top of screen
    orientationY = "bottom";
    x.style.top = (posy + balloonArrowStandoffY) + "px";        
  }

  //
  // now assemble the balloon parts
  //
  if (orientationY == "top") {
    x = document.getElementById("balloonTop");
    x.style.backgroundImage = "url(balloons/pcap-top.gif)";
    x.style.height = balloonPlainCapHeight + 'px';

    if (orientationX == "right") {
      x = document.getElementById("balloonBot");
      x.style.backgroundImage = "url(balloons/acap-tr.gif)";
      x.style.height = balloonArrowCapHeight + 'px';
    } else {
      // orientationX == left
      x = document.getElementById("balloonBot");
      x.style.backgroundImage = "url(balloons/acap-tl.gif)";
      x.style.height = balloonArrowCapHeight + 'px';
    }

  } else {
    // orientationY = "bottom"
    x = document.getElementById("balloonBot");
    x.style.backgroundImage = "url(balloons/pcap-bot.gif)";
    x.style.height = balloonPlainCapHeight + 'px';

    if (orientationX == "right") {
      x = document.getElementById("balloonTop");
      x.style.backgroundImage = "url(balloons/acap-br.gif)";
      x.style.height = balloonArrowCapHeight + 'px';

    } else {
      // orientationX == "left"
      x = document.getElementById("balloonTop");
      x.style.backgroundImage = "url(balloons/acap-bl.gif)";
      x.style.height = balloonArrowCapHeight + 'px';
    }
  }

  //
  // the middle is the same for all orientations
  //
  x = document.getElementById("balloonMid");
  x.style.backgroundImage = "url(balloons/mid.gif)";
  x.style.backgroundRepeat = "repeat-y";
  x.innerHTML = balloontip;
 
} // END function showBalloon


// ----------------------------------------------------------------------
// onMouseOut, hide the balloon
// ----------------------------------------------------------------------
function hideBalloon(e)
{
  // Clean everything out
  // Firefox requires setting the height to 0
  // and then to "" for the Mid
  var x;
  x = document.getElementById("balloonBot");
  x.style.backgroundImage = "";
  x.style.height = "0px";

  x = document.getElementById("balloonMid");
  x.style.backgroundImage = "";
  x.innerHTML = "";
  x.style.height = "0px";
  x.style.height = "";

  x = document.getElementById("balloonTop");
  x.style.backgroundImage = "";
  x.style.height = "0px";

} // END function hideBalloon