function IsBubbleVisible(targetId) {
  var target = $(targetId);
  if (target.size() != 1) {
    return false;
  }
  var visible = $.data(target[0], 'bubble-visible');
  return (typeof visible != 'undefined') && visible;
}

function HideBubble(targetId) {
  var target = $(targetId);
  if (target.size() != 1) {
    return;
  }
  if (IsBubbleVisible(targetId)) {
    $.data(target[0], 'bubble-visible', false);
    var bubble = $.data(target[0], 'bubble');
    bubble.stop(true, false);
    bubble.fadeOut('fast');
  }
}

function ShowBubble(targetId) {
  var target = $(targetId);
  if (target.size() != 1) {
    return;
  }
  if (IsBubbleVisible(targetId)) {
    return;
  }
  
  var bubble = $.data(target[0], 'bubble');
  RepositionBubble(target, bubble, $.data(target[0], 'bubble-default-up'));
  
  $.data(target[0], 'bubble-visible', true);
  bubble.stop(true, false);
  bubble.fadeIn('fast');
}

function CreateBubble(targetId, content, defaultUp, hideOnOutsideClick) {
  var target = $(targetId);
  if (target.size() != 1) {
    return;
  }
  if (typeof $.data(target[0], 'bubble-visible') != 'undefined' && $.data(target[0], 'bubble-visible') != null) {
    return;
  }
  if (typeof defaultUp === 'undefined') {
    defaultUp = true;
  }
  if (typeof hideOnOutsideClick === 'undefined') {
    hideOnOutsideClick = true;
  }

  var bubbleTemplate = " \
  <table class='bubble'> \
      <tr> \
        <td class='bubble-top-left'></td> \
        <td> \
          <table class='bubble-top-center-table'> \
            <tr> \
              <td class='bubble-top-center-left'>&nbsp;</td> \
              <td class='bubble-top-tail'></td> \
              <td class='bubble-top-center-right'>&nbsp;</td> \
            </tr> \
          </table> \
        </td> \
        <td class='bubble-top-right'></td> \
      </tr> \
      <tr> \
        <td class='bubble-left'></td> \
        <td class='bubble-center'><div class='bubble-content'></div></td> \
        <td class='bubble-right'></td> \
      </tr> \
      <tr> \
        <td class='bubble-bottom-left'></td> \
        <td> \
          <table class='bubble-bottom-center-table'> \
            <tr> \
              <td class='bubble-bottom-center-left'>&nbsp;</td> \
              <td class='bubble-bottom-tail'></td> \
              <td class='bubble-bottom-center-right'>&nbsp;</td> \
            </tr> \
          </table> \
        </td> \
        <td class='bubble-bottom-right'></td> \
      </tr> \
    </table> \
  ";
  var bubble = $(bubbleTemplate);

  bubble.addClass('bubble-content-' + target.attr('id'));
  bubble.find('.bubble-content').append(content.html());
  bubble.insertAfter(target);

  RepositionBubble(target, bubble, defaultUp);

  $(window).resize(function() {
    setTimeout(function() { RepositionBubble(target, bubble, defaultUp); }, 5);
  });
  $(window).scroll(function() {
    setTimeout(function() { RepositionBubble(target, bubble, defaultUp); }, 5);
  });

  $.data(target[0], 'bubble', bubble);
  $.data(target[0], 'bubble-visible', false);
  $.data(target[0], 'bubble-default-up', defaultUp);

  if (hideOnOutsideClick) {
    $(bubble).hover(function() { $.data(target[0], 'bubble-inside', true); },
                    function() { $.data(target[0], 'bubble-inside', false); });
    $(target[0]).hover(function() { $.data(target[0], 'bubble-inside', true); },
                       function() { $.data(target[0], 'bubble-inside', false); });
    $("body").mouseup(function() { if (!$.data(target[0], 'bubble-inside')) HideBubble(targetId); });
  }
}

function RepositionBubble(target, bubble, defaultUp) {
  var targetsPosition = target.position();
  var targetsOffset = target.offset();

  var bubblePosition = targetsPosition;

  var downSpace = $(window).height() - (targetsOffset.top - $(document).scrollTop()) - target.height();
  var upSpace = targetsOffset.top - $(document).scrollTop();

  var canDown = downSpace >= bubble.height();
  var canUp = upSpace >= bubble.height();

  var goUp = downSpace < upSpace;
  if (defaultUp) {
    if (canUp) {
      goUp = true;
    } else if (canDown) {
      goUp = false;
    }
  } else {
    if (canDown) {
      goUp = false;
    } else if (canUp) {
      goUp = true;
    }
  }
  
  if (goUp) {
    bubblePosition.top -= bubble.height();
    bubble.find('.bubble-top-tail').removeClass('bubble-tail-up');
    bubble.find('.bubble-bottom-tail').addClass('bubble-tail-down');
  } else {
    bubblePosition.top += target.height();
    bubble.find('.bubble-top-tail').addClass('bubble-tail-up');
    bubble.find('.bubble-bottom-tail').removeClass('bubble-tail-down');
  }
  
  bubblePosition.left += target.width()/2;
  bubblePosition.left -= bubble.width()/2;

  bubble.css('top', bubblePosition.top + 'px');
  bubble.css('left', bubblePosition.left + 'px');
}

