// ToolTips for ShtetlMaster// Warren Blatt, June 2007var objTT   = null;  // ToolTip pop-upvar objTrig = null;  // Target trigger elementvar ShtetTTcache = {};   // Cache of HTML text// **************************************************************************// AJAX basics:function fetchData (url, dataRequested)  {    var dataRequest;    if (dataRequested)       { dataRequest = "msg="+ dataRequested; }          var pageRequest = false    if (window.XMLHttpRequest)       { pageRequest = new XMLHttpRequest() }    else if (window.ActiveXObject)      {         try { pageRequest = new ActiveXObject("Msxml2.XMLHTTP")	} 	catch (e) 	  {            try { pageRequest = new ActiveXObject("Microsoft.XMLHTTP") }            catch (e) {}          }      }    else return false    pageRequest.onreadystatechange = function()       {	filterData (pageRequest, dataRequested) }     if (dataRequested)       {			pageRequest.open ('POST', url, true);  	pageRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');    	pageRequest.send(dataRequest);      }    else       {        pageRequest.open('GET', url, true)        pageRequest.send(null)	      }  }// **************************************************************************var TTInited = false;function TTInit()  {    objTT = document.createElement("div");    document.body.appendChild(objTT);      var otts = objTT.style;    otts.visibility = "hidden";    otts.position = "absolute";    otts.fontSize = "0.8em";    otts.fontFamily = "Helvetica, Arial, sans-serif";    otts.backgroundColor = "#fff";    otts.margin = "0 10px";    otts.padding = "5px";    otts.width = "310px";    otts.opacity = "0.95";    otts.border = "outset 3px #B3A3C2";    otts.zIndex = "+200";        TTInited = true;  }// **************************************************************************function getX (e)  {    var x = 0;    while (e)      {        x += e.offsetLeft;        e = e.offsetParent;      }    return x;  }function getY (e)  {    var y = 0;    while (e)      {        y += e.offsetTop;        e = e.offsetParent;      }    return y;  }function getTarget (evt)  {    evt = (evt) ? evt : ((window.event) ? event : null);    if ( ! evt )      { return null; }        var elem = (evt.target) ? evt.target :       ((evt.srcElement) ? evt.srcElement : null);    if (elem.nodeType == 3)      { elem = elem.parentNode; }    return elem;  }function getUSBGNobj (obj)  {    while ( obj && ( ! obj.getAttribute('usbgn') ) )      {        obj = obj.parentNode;      }    return obj;  }// **************************************************************************// OnMouseOver event handler:function ShtetTTUp(evt)   {    if ( ! TTInited )       { TTInit(); }    objTrig = getTarget(evt);    if ( ! objTrig )      {         alert ('Null objTrig.  Event = ' + evt);         return;       }        objTrig = getUSBGNobj(objTrig);    if ( ! objTrig )      {         // No parent object with a "USBGN" attribute.        return;       }            var usbgn_c = objTrig.getAttribute('usbgn');    if ( usbgn_c == -1 )      {        // -1 is a special case dummy placeholder.        return;      }        // Check if there is cached data:    if ( ShtetTTcache[usbgn_c] )       {        displayToolTip(ShtetTTcache[usbgn_c], usbgn_c);      }    else      {        // Call to retrieve the data:        rURL = '/Ajax/dataPage.asp';        fetchData (rURL, usbgn_c);        // Display "Loading..." initial ToolTip:        objTT.innerHTML = '<B>Loading . . .</B>';        // Position the ToolTip:        // The default position is directly below the trigger object, left aligned.        // The final X,Y position can only be calculated AFTER the text is retrieved.        var xPos = getX(objTrig);        var yPos = getY(objTrig) + objTrig.offsetHeight;        // If there's no room for the full ToolTip at the right,         //   shift it left a bit, to align its right edge with the window.        winWidth = getWindowWidth();        if ( xPos + objTT.offsetWidth + 30 >= winWidth )          {            xPos = winWidth - objTT.offsetWidth - 30;          }         objTT.style.left = xPos + 'px';        objTT.style.top  = yPos + 'px';        objTT.style.visibility = 'visible';      }        // Change the background color of the trigger object:    var bgc = objTrig.style.backgroundColor;    objTrig.setAttribute('saveBgc', bgc);    objTrig.style.backgroundColor = "#B3A3C2";  }// **************************************************************************// OnMouseOut event handler:function ShtetTTHide()   {    // Remove the tooltip:    if ( objTT )      {        objTT.style.visibility = 'hidden';        objTT.innerHTML = '';      }        // Revert the trigger's background color:    if ( objTrig )      { objTrig.style.backgroundColor = objTrig.getAttribute('saveBgc'); }        // Clear the global object pointer:    objTrig = null;  }// **************************************************************************// Called from filterData() and ShtetTTUp():function displayToolTip(HTMLtext, usbgn)  {    // If the mouse has been moved out of the trigger object,    //   then do nothing.    if ( ! objTrig )      { return; }          // If the current trigger object is NOT the one that requested     //   this data (i.e. the mouse has moved into another trigger),    //   then do nothing.    var usbgn_temp = objTrig.getAttribute('usbgn');    if ( usbgn != usbgn_temp )      {         // objTT.innerHTML += "'" + usbgn_temp + "' ";         // objTT.innerHTML += "'" + usbgn + "'";        return;       }        // The final X,Y position can only be calculated AFTER the     //   text is retrieved from the server.    objTT.innerHTML = HTMLtext;        // The default position is directly below the trigger object, left aligned.    xPos = getX(objTrig);    yPos = (getY(objTrig) + objTrig.offsetHeight);        winWidth  = getWindowWidth();    winHeight = getWindowHeight();    winX = getScrollX();    winY = getScrollY();    TTWidth  = objTT.offsetWidth;    TTHeight = objTT.offsetHeight;    // If there's insufficient room beneath the trigger object,    //   place the ToolTip to the right, top aligned with trigger.    if ( (yPos + TTHeight) >= (winY + winHeight) )      {        yPos = yPos - objTrig.offsetHeight;        xPos = xPos + objTrig.offsetWidth;      }        // If there's still no room vertically, then    //   push the ToolTip up, to align with window bottom.    if ( (yPos + TTHeight) > (winY + winHeight))       {        yPos = (winY + winHeight) - TTHeight;      }          // If there's no room for the full ToolTip at the right,     //   shift it left a bit, to align its right edge with the window.    if ( xPos + TTWidth + 30 >= winWidth )      {        xPos = winWidth - TTWidth - 30;              // If the left-shift causes the ToolTip to overlap the trigger,        //   move the Tooltip to the left of the trigger.        if ( yPos <= getY(objTrig) )          {            xPos = getX(objTrig) - TTWidth - 25;                        // If the resulting xPos is off the screen,            //   then we need to revert to the inital default position:            //   display partial ToolTip below the target.            if ( xPos < winX )              {                xPos = getX(objTrig);                yPos = getY(objTrig) + objTrig.offsetHeight;              }          }      }        objTT.style.left = xPos + 'px';    objTT.style.top  = yPos + 'px';    objTT.style.visibility = 'visible';  }// **************************************************************************function filterData (Req, usbgn_code)  {    // if ( Req.readyState == 2 )    //   objTT.innerHTML += "<B>Debug 2: Data Loaded</B><BR>";    // if ( Req.readyState == 3 )    //   objTT.innerHTML += "<B>Debug 3: Data Ready</B><BR>";                  if ( Req.readyState == 4 )      {         // objTT.innerHTML += "<B>Debug 4: Data Completed</B><BR>";        // objTT.innerHTML += "<pre>"+Req.getAllResponseHeaders()+"</pre>";        // objTT.innerHTML += "Status: " + Req.status + " " + Req.statusText + "<BR>";        // objTT.innerHTML += "responseXML: X" + Req.responseXML + "X<BR>";         // objTT.innerHTML += "responseText: X" + Req.responseText + "X<BR>";                 if ( Req.status==200 || window.location.href.indexOf("http")==-1 )          {             displayToolTip(Req.responseText, usbgn_code);            ShtetTTcache[usbgn_code] = Req.responseText;  // cache data          }        else          { objTT.innerHTML = "Error: " + Req.status + " " + Req.statusText; }                   // objTT.innerHTML += "End<BR>";       }    else if ( Req.readyState > 4 )      {        objTT.innerHTML = "Invalid readyState:" + Req.readyState;      }  }  // **************************************************************************//// Utilities, based on "JavaScript & DHTML Cookbook", 13.7, pp. 383-385://// Return the available content height/width space in browser windowfunction getWindowHeight()   {    if (window.innerHeight)       { return window.innerHeight; } //    else if (isIE6CSS) //      {//        // measure the html element's clientHeight//        return document.body.parentElement.clientHeight;//      }     else if (document.body && document.body.clientHeight)       { return document.body.clientHeight; }    return 0;  }function getWindowWidth()   {    if (window.innerWidth)       { return window.innerWidth; } //    else if (isIE6CSS) //      {//        // measure the html element's clientWidth//        return document.body.parentElement.clientWidth;//      }     else if (document.body && document.body.clientWidth)       { return document.body.clientWidth; }    return 0;  }function getScrollX()  {    var scrollX = 0;    if (document.body && typeof document.body.scrollLeft != "undefined")       {        scrollX += document.body.scrollLeft;        if (document.body.parentNode &&             typeof document.body.parentNode.scrollLeft != "undefined")           { scrollX += document.body.parentNode.scrollLeft; }      }     else if (typeof window.pageXOffset != "undefined")       {        scrollX += window.pageXOffset;      }    return scrollX;  }  function getScrollY()  {    var scrollY = 0;    if (document.body && typeof document.body.scrollTop != "undefined")       {        scrollY += document.body.scrollTop;        if (document.body.parentNode &&             typeof document.body.parentNode.scrollTop != "undefined")           { scrollY += document.body.parentNode.scrollTop; }      }     else if (typeof window.pageYOffset != "undefined")       {        scrollY += window.pageYOffset;      }    return scrollY;  }// **************************************************************************  // Utility for debugging:function showDim(evt)  {    var obj = (evt.target) ? evt.targetd : ((evt.srcElement) ? evt.srcElement : null );    if (obj)      {         s = 'Obj Width: ' + obj.offsetWidth + 'px; Obj Height: ' + obj.offsetHeight + 'px\n';         s += 'Obj X: ' + obj.offsetLeft + ' Obj Y: ' + obj.offsetTop + '\n';         s += 'Window Width: ' + getWindowWidth() + ' Window Height: ' + getWindowHeight() + '\n';         s += 'Window X: ' + getScrollX() + ' Window Y: ' + getScrollY();         alert (s);      }    else      return (null);  }// **************************************************************************// getElements(classname, tagname, root):// Return an array of DOM elements that are members of the specified class,// have the specified tagname, and are descendants of the specified root.// // If no classname is specified, elements are returned regardless of class.// If no tagname is specified, elements are returned regardless of tagname.// If no root is specified, the document object is used.  // If the specified root is a string, it is an element id, and // the root element is looked up using getElementsById().// From "JavaScript: The Definitive Guide", pp. 322-324.function getElements(classname, tagname, root)   {    // If no root was specified, use the entire document.    // If a string was specified, look it up.    if ( ! root)       root = document;    else if (typeof root == "string")       root = document.getElementById(root);        // If no tagname was specified, use all tags.    if (!tagname) tagname = "*";    // Find all descendants of the specified root with the specified tagname.    var all = root.getElementsByTagName(tagname);    // If no classname was specified, we return all tags.    if (!classname) return all;    // Otherwise, we filter the elements by classname.    var elements = [];  // Start with an empty array    for (var i = 0; i < all.length; i++)       {        var element = all[i];        if (isMember(element, classname)) // isMember() is defined below            elements.push(element);       // Add class members to our array      }    // Note that we always return an array, even if it is empty.    return elements;    // Determine whether the specified element is a member of the specified    // class.  This function is optimized for the common case in which the     // className property contains only a single classname.  But it also     // handles the case in which it is a list of whitespace-separated classes.    function isMember(element, classname)       {        var classes = element.className;  // Get the list of the element's classes        if (!classes) return false;             // No classes defined        if (classes == classname) return true;  // Exact match        // We didn't match exactly, so if there is no whitespace, then         // this element is not a member of the class        var whitespace = /\s+/;        if (!whitespace.test(classes)) return false;        // If we get here, the element is a member of more than one class,        // so we've got to check them individually.        var c = classes.split(whitespace);  // Split with whitespace delimiter        for (var i = 0; i < c.length; i++)  // Loop through classes          {            if (c[i] == classname) return true;  // and check for matches          }        return false;  // None of the classes matched      }  }// **************************************************************************// Here is the initialization which needs to be run to //   set the event handlers for all elements of class "shtet".var shelems = getElements ("shtet");for (var i = 0; i < shelems.length; i++)  {     shelems[i].onmouseover = ShtetTTUp;    shelems[i].onmouseout  = ShtetTTHide;  }  // **************************************************************************