// Javascript to add glossary tooltips.
// Various chunks of code have been extracted from :-
// The JavaScript Anthology, 101 Essential Tips, Tricks & Hacks,
// By James Edwards & Cameron Adams, Published by SitePoint.
// REM  23/06/06

function addListeners(event)
{
  addGlossaryTips();
}

function addGlossaryTips()
{
  var tips = getElementsByAttribute("class", "gloss");

  for (var i = 0; i < tips.length; i++) {
     addEvent(tips[i], 'mouseover', showGlossaryTip, false);
     addEvent(tips[i], 'mouseout', hideGlossaryTip, false);
  }

  return true;
}

function getElementsByAttribute(attribute, attributeValue)
{
  var elementArray = new Array();
  var matchedArray = new Array();

  if (document.all) {
     elementArray = document.all;
  } else {
     elementArray = document.getElementsByTagName("*");
  }

  for (var i = 0; i < elementArray.length; i++) {
     if (attribute == "class") {
        var pattern = new RegExp("(^| )" + attributeValue + "( |$)");
        if (pattern.test(elementArray[i].className)) {
           matchedArray[matchedArray.length] = elementArray[i];
        }
     } else if (attribute == "for") {
        if (elementArray[i].getAttribute("htmlFor") ||
            elementArray[i].getAttribute("for")) {
           if (elementArray[i].htmlFor == attributeValue) {
              matchedArray[matchedArray.length] = elementArray[i];
           }
        }
     } else if (elementArray[i].getAttribute(attribute) == attributeValue) {
         matchedArray[matchedArray.length] = elementArray[i];
     }
  }

  return matchedArray;
}

function showGlossaryTip(event) 
{
  if (typeof event == "undefined") {
     event = window.event;
  }

  // Find target element with class=gloss 
  var target = getEventTarget(event);
  var pattern = new RegExp("(^| )gloss( |$)");
  while ( target.className == null || !(pattern.test(target.className)) ){
     pattern.lastIndex = 0;  // Ensure the test starts at beginning of string
     target = target.parentNode;
  }

  // Create a div 'tooltip' element
  var tip = document.createElement("div");

  var glossTitle = target.getAttribute("title");
  var glossContent = target.getAttribute("content");
  var glossImage = target.getAttribute("image");
  
  // Point element tooltip to new div element, and clear original
  // title to ensure that default tooltip is not displayed by browser.
  target.tooltip = tip;
  target.setAttribute("title", "");

  // Setup the new glossary tooltip
  tip.setAttribute("id", "glossTip");
  tip.className = "glosstip";

  if (glossTitle != null) {
     var glossHead = document.createElement("h1");
     glossHead.setAttribute("id", "glossTipH1");
     glossHead.className = "REMpg-tdH";
     glossHead.appendChild(document.createTextNode(glossTitle));
     tip.appendChild(glossHead);
  }

  if (glossContent != null) {
     var glossPara = document.createElement("p");
     glossPara.setAttribute("id", "glossTipP");
     glossPara.className = "REMpg-ptext";
     glossPara.appendChild(document.createTextNode(glossContent));
     tip.appendChild(glossPara);
  }

  if (glossImage != null ) {
     var glossImg = document.createElement("img");
     glossImg.setAttribute("id", "glossTipImg");
     glossImg.setAttribute("src", glossImage);
     glossImg.className = "REMpg-img";
     tip.appendChild(glossImg);
  }

  // Determine the position to display the glossary tooltip
  var scrollingPosition = getScrollingPosition();   // scrollingPosition[left,top]
  var cursorPosition = [0,0];   // cursorPosition[left,top];

  if (typeof event.pageX != "undefined" && typeof event.x != "undefined") {
     cursorPosition[0] = event.pageX;
     cursorPosition[1] = event.pageY;
  } else {
     cursorPosition[0] = event.clientX + scrollingPosition[0];
     cursorPosition[1] = event.clientY + scrollingPosition[1];
  }

  tip.style.position = "absolute";
  tip.style.left = cursorPosition[0] + 10 + "px";
  tip.style.top = cursorPosition[1] + 10 + "px";

  // Append a hidden glossarytip so that its position can be calculated
  // and any adjustments made to ensure it displays on the page
  tip.style.visibility = "hidden";
  document.getElementsByTagName("body")[0].appendChild(tip);

  var viewportSize = getViewportSize();   // viewportSize[width,height]

  var tipWidth = tip.offsetWidth;
  var maxWidth = viewportSize[0] / 3;
  if ( tip.offsetWidth > maxWidth ) {
     tip.style.width = maxWidth;
     tipWidth = maxWidth;
  }

  // Check and adjust if necessary the left cursor postion
  if ( (cursorPosition[0] + 10 + tipWidth) > (viewportSize[0] - 25) ) {
     tip.style.width = maxWidth;
     tipWidth = maxWidth;
     tip.style.left = cursorPosition[0] - 10 - tipWidth + "px";
  } else if ( (cursorPosition[0] - scrollingPosition[0] + 10 + tipWidth) 
	            > (viewportSize[0] - 25) ) {
     tip.style.left = scrollingPosition[0] + viewportSize[0] - 25 - tipWidth + "px";
  } else {
     tip.style.left = cursorPosition[0] + 10 + "px";
  }
  // Check and adjust if necessary the top cursor position
  if ( ( cursorPosition[1] - scrollingPosition[1] + 10 + tip.offsetHeight )
            > ( viewportSize[1] - 25 ) ) {
     if (event.clientX > (viewportSize[0] - 25 - tipWidth)) {
        tip.style.top = cursorPosition[1] - tip.offsetHeight - 10 + "px";
     } else {
        tip.style.top = scrollingPosition[1] + viewportSize[1] - 25 - tip.offsetHeight + "px";
     }
  } else {
     tip.style.top = cursorPosition[1] + 10 + "px";
  }

  // Set element back to visible
  tip.style.visibility = "visible";

  return true;
}

function getScrollingPosition()
{
  var position = [0, 0];
  if (typeof window.pageYOffset != 'undefined') {
     position = [window.pageXOffset, window.pageYOffset];
  } else if (typeof document.documentElement.scrollTop != 'undefined' &&
               document.documentElement.scrollTop > 0) {
     position = [document.documentElement.scrollLeft, document.documentElement.scrollTop];
  } else if (typeof document.body.scrollTop != 'undefined') {
     position = [document.body.scrollLeft, document.body.scrollTop];
  }
  return position;
}

function getViewportSize()
{
  var size = [0, 0];
  if (typeof window.innerWidth != 'undefined') {
     size = [window.innerWidth, window.innerHeight];
  } else if (typeof document.documentElement != 'undefined' &&
             typeof document.documentElement.clientWidth != 'undefined' &&
              document.documentElement.clientWidth != 0) {
     size = [document.documentElement.clientWidth, document.documentElement.clientHeight];
  } else {
     size = [document.getElementsByTagName('body')[0].clientWidth, document.getElementsByTagName('body')[0].clientHeight];
  }

  return size;
}

function hideGlossaryTip(event) 
{
  if (typeof event == "undefined") {
     event = window.event;
  }

  // Find target element with class=gloss 
  var target = getEventTarget(event);
  var pattern = new RegExp("(^| )gloss( |$)");
  while ( target.className == null || !(pattern.test(target.className)) ){
     pattern.lastIndex = 0;  // Ensure the test starts at beginning of string
     target = target.parentNode;
  }

  if (target.tooltip != null ) {
     var tooltipHeading = document.getElementById("glossTipH1");
     if (tooltipHeading != null ) {
        var tooltipTitle = tooltipHeading.firstChild.nodeValue;
        target.setAttribute("title", tooltipTitle);
        var glossaryTip = document.getElementById("glossTip");
        var parent = glossaryTip.parentNode;
        parent.removeChild(glossaryTip);
     }
  }

  return false;
}

function getEventTarget(event)
{
  var targetElement = null;
  if (typeof event.target != "undefined") {
     targetElement = event.target;
  } else {
     targetElement = event.srcElement;
  }

  while (targetElement.nodeType == 3 && targetElement.parentNode != null) {
     targetElement = targetElement.parentNode;
  }

  return targetElement;
}

// cross-browser event handling for IE5+, NS6+ and Mozilla/Gecko 
// By Scott Andrew  
function addEvent(obj, evType, fn, useCapture) 
{
  if (obj.addEventListener) {
     obj.addEventListener(evType, fn, useCapture);
     return true;
  } else if (obj.attachEvent) {
     var r = obj.attachEvent('on' + evType, fn);
     return r;
  } else {
     obj['on' + evType] = fn;
  }
}

// Assign element event listeners in a listener assigned to
// the window's load event. This ensures that all the elements
// are defined prior to assigning the element event listeners.
addEvent(window, 'load', addListeners, false);

