(function() {
  
  var getScrollX = function() {
    scrollSetup();
    return getScrollX();
  };

  var getScrollY = function() {
    scrollSetup();
    return getScrollY();
  };

  var scrollSetup = (function(){
    var global = this;

    return function() {
      var readScroll,
          readScrollY = 'scrollTop',
          readScrollX = 'scrollLeft';

      if (typeof global.pageXOffset == 'number') {
        readScroll = global;
        readScrollY = 'pageYOffset';
        readScrollX = 'pageXOffset';

      } else if ((typeof document.compatMode === 'string') &&
                 (document.compatMode.indexOf('CSS') >= 0) &&
                 (document.documentElement) &&
                 (typeof document.documentElement.scrollLeft=='number')) {

        readScroll =  document.documentElement;

      } else if ((document.body) &&
                 (typeof document.body.scrollLeft === 'number')) {
        readScroll =  document.body;    
      } else {
        getScrollX = getScrollY = function() {return NaN;};
        return;
      }
      getScrollX = function() {return readScroll[readScrollX];};
      getScrollY = function() {return readScroll[readScrollY];};  
    };

  })();


  var windowHeight = function() {
    if (window.innerHeight) {
      return window.innerHeight;
    }
    else if (document.documentElement.clientHeight) {
      return document.documentElement.clientHeight;
    }
    else if (document.body.clientHeight) {
      return document.body.clientHeight;
    }
  };


  var windowWidth = function() {
    if (window.innerWidth) {
      return window.innerWidth;
    }
    else if (document.documentElement.clientWidth) {
      return document.documentElement.clientWidth;
    }
    else if (document.body.clientWidth) {
      return document.body.clientWidth;
    }
  };


  // --------------------------
  
  var global = this;
  var zIndex = 1000;
  var padding = 50; // pixels
  // The transparent fade borders are borderSize pixels wide
  var borderSize = 40; // pixels

  var currentBubbleId; // only one bubble can be open at a time

  // -----------------

  // The styles critical to the functioning of the bubble popup widget. 
  // The width of the popup can be controlled elsewhere by setting
  // .helpBubbleContent{width:350};
  // or it can be set in the style attribute of the html element for the content
  document.write(
    '<style type="text/css">',
    '.helpBubbleShell {position:absolute;margin:0;padding:'+padding+'px;overflow:hidden;}',
    '.helpBubbleContent {display:none;}',
    '.helpBubbleShell .helpBubbleContent {display:block;}',
    '.helpBubbleContent {width:350px;}',
    '.closeLink {padding:0;margin-top:-12px;margin-bottom:0px;}',
    '<\/style>');
    
  // ------------------

  // IE6 has trouble with png alpha transparency. We need a few lines to workaround the problem
  // using a "filter". When IE6 is finally dead then just "background-image:url(shadow.png);"
  // can be moved to the css file which styles the bubble help

  // See http://groups.google.com/group/comp.lang.javascript/msg/39cb067b6b34a819
  var isWindowsIE5To6 = (
    (typeof clientInformation == 'object')&&
    (typeof CollectGarbage == 'function')&&
    (typeof ActiveXObject == 'function')&&
    (typeof external == 'object')&&
    (typeof ScriptEngine == 'function')&&
    (typeof showModalDialog == 'object')&&
    (
      (document.documentElement)&&
      (typeof document.documentElement.currentStyle == 'object')&&
      (typeof document.documentElement.behaviorUrns == 'object')&&
      (typeof document.documentElement.filters == 'object')
    ) &&
    typeof XMLHttpRequest == 'undefined'
  );

  var imagePrefix = '/ssimgs/common/bubbleShadow';

  document.write('<style type="text/css">');
    if (isWindowsIE5To6) {
      for (var i=1; i<=4; i++) {
        document.write(".helpBubbleShell .IE"+i+" {filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+imagePrefix+i+".png', sizingMethod='crop');}");
      }
    } else {
      for (var i=1; i<=4; i++) {
        document.write(".helpBubbleShell .IE"+i+" {background-image:url("+imagePrefix+i+".png);}");
      }
    }
  document.write('<\/style>');

  // --------------
  
  function trimHelpBubbleId(id) {
    return id.replace(/^.*#/, '');
  }

  function findAncestorWithClassName(el, className) {
    var re = new RegExp("(^|\\s+)"+className+"(\\s+|$)");
    while (el) {
      if (el.className && el.className.match(re)) {
        return el;
      }
      el = el.parentNode;
    }
  }

  // id is a string to avoid making
  // IE circular memory leak
  function makeCloseLink(id) {
    return function(e) {
      e = e || event;
      if (e.preventDefault) {
        e.preventDefault();
      }
      else {
        e.returnValue = false;
      }
      hideHelpBubble(id);
      return false;
    }
  }

  function getElementPosition(el) {
    var x = 0,
        y = 0;
    for (var e = el; e; e=e.offsetParent) {
      x += e.offsetLeft;
      y += e.offsetTop;
    }
    for (e=el.parentNode; e && e!=document.body; e=e.parentNode) {
      if (e.scrollLeft) {
        x -= e.scrollLeft;
      }
      if (e.scrollTop) {
        y -= e.scrollTop;
      }
    }
    return {x:x, y:y};
  }

  // link parameter is the DOM anchor element that has the name of that
  // help bubble as it's href attribute value 
  global.showHelpBubble = function(link) {
    if (currentBubbleId) {return;}

    var id = trimHelpBubbleId(link.href);
    currentBubbleId = id;

    var el = document.getElementById(id);
    if (!el) {
      return;
    }
    if (findAncestorWithClassName(el, 'helpBubbleShell')) {
      hideHelpBubble(id);
    }
    
    var pos = getElementPosition(link);
    var shell = document.createElement('div');
    var innerShell = document.createElement('div');
    innerShell.style.position='relative';
    innerShell.style.zIndex = zIndex+1;
    shell.appendChild(innerShell);

    shell.style.visibility = 'hidden';
    shell.className = "helpBubbleShell";


    var closeLinkPara = document.createElement('p');
    closeLinkPara.className = 'closeLink'
    var closeLink = document.createElement('a');
    closeLink.href="#closeHelpBubble";
    closeLink.onclick = makeCloseLink(id);
    closeLink.innerHTML = 'close';
    closeLinkPara.appendChild(closeLink);
    innerShell.appendChild(closeLinkPara);
    
    innerShell.appendChild(el);

    shell.style.zIndex = zIndex;

    // insert at top of page in case the page has not
    // yet finished parsing
    document.body.insertBefore(shell, document.body.firstChild);

    // // if the bubble will go above the top of the page then put the bubble below the link
    // var fixedShift = 5;
    // var fadeShift = 12;
    // var top = pos.y-shell.offsetHeight-fixedShift+fadeShift;
    // var vside = (top < 0) ? 'Below' : 'Above';
    // if (vside == 'Above') {
    //   shell.style.top = top+'px';
    // }
    // else {
    //   shell.style.top = pos.y + link.offsetHeight + fixedShift - fadeShift + 'px';
    // }
    // 
    // // if the middle of the element is in the left of the viewport then want to show bubble help in right
    // var side = ( (pos.x+(link.offsetWidth/2))< (document.body.offsetWidth/2)) ? 'Right' : 'Left';
    // shell.className += ' ' + side + vside + 'HelpBubble';
    // // for a very narrow link, 40 may be too much shift so use 75% of the link's width.
    // var fixedShift = Math.min(40, ((link.offsetWidth)*0.75));
    // var shift = (side == 'Left') ? (fixedShift - shell.offsetWidth) : (link.offsetWidth - fixedShift);
    // shell.style.left = (pos.x + shift)+'px';

    var fadeShift = 12;
    shell.style.top = Math.max(0, (getScrollY() + (windowHeight()/2) - (shell.offsetHeight/2))) + 'px';
    shell.style.left = Math.max(0, ((windowWidth() - shell.offsetWidth) / 2 + getScrollX()-fadeShift)) + 'px';


    function makeIeElement(top, left, width, height, number) {
      var el1 = document.createElement('div');
      el1.style.width=width+"px";
      el1.style.height=height+"px";
      el1.className="IE"+number;
      el1.style.position = 'absolute';
      el1.style.top=top+'px';
      el1.style.left=left+'px';
      shell.appendChild(el1);
    }

    closeLinkPara.style.width = (el.offsetWidth)+'px';
    
    shellWidth = (el.offsetWidth+2*padding);
    shell.style.width = shellWidth +'px';

    makeIeElement(0, 0, shellWidth-borderSize, shell.offsetHeight-borderSize, 1);
    makeIeElement(shell.offsetHeight-borderSize, 0, shellWidth-borderSize, borderSize, 4);
    makeIeElement(shell.offsetHeight-borderSize, shellWidth-borderSize, borderSize, borderSize,  3);
    makeIeElement(0, shellWidth-borderSize, borderSize, shell.offsetHeight-borderSize, 2);

    shell.style.visibility = '';
    zIndex+=2;
  }

  // id parameter can be the id of the help bubble to hid
  // or it can be an DOM element that is inside the help bubble to close.
  global.hideHelpBubble = function(id) {
    var el;
    if (typeof id == 'string') {
      el = document.getElementById(trimHelpBubbleId(id));
    }
    else { // assume a DOM element
      el = findAncestorWithClassName(id, 'helpBubbleContent');
    }
    if (!el) {
      return;
    }

    var shell = findAncestorWithClassName(el, 'helpBubbleShell');
  
    document.body.insertBefore(el, document.body.firstChild);
    document.body.removeChild(shell);
    currentBubbleId = null;
  };

})();
