/*
balloon.js -- a DHTML library for balloon tooltips
$Id$
See http://www.gmod.org/wiki/index.php/Popup_Balloons
for documentation.
Copyright (c) 2007-2009 Sheldon McKay, Cold Spring Harbor Laboratory
This balloon tooltip package and associated files not otherwise copyrighted are
distributed under the MIT-style license:
http://opensource.org/licenses/mit-license.php
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// These global variables are necessary to avoid losing scope when
// setting the balloon timeout and for inter-object communication
var currentBalloonClass;
var balloonIsVisible;
var balloonIsSticky;
var balloonInvisibleSelects;
var balloonIsSuppressed;
var tooltipIsSuppressed;
//////////////////////////////////////////////////////////////////////////
// This is constructor that is called to initialize the Balloon object //
//////////////////////////////////////////////////////////////////////////
var Balloon = function () {
// Cursor tracking enabled by default
this.trackCursor = true;
// Track the cursor every time the mouse moves
document.onmousemove = this.setActiveCoordinates;
// scrolling aborts visible balloons
var myObject = this.isIE() ? window : document;
myObject.onscroll = function(){Balloon.prototype.nukeTooltip()};
// make balloons go away if the page is unloading or waiting
// to unload.
window.onbeforeunload = function(){
Balloon.prototype.nukeTooltip();
balloonIsSuppressed = true;
};
// for IE, the balloons can't start until the page is finished loading
// set a flag that will get toggled when loading is finished
if (this.isIE()) {
this.suppress = true;
}
return this;
}
//////////////////////////////////////////////////////////////////////////
// This is the method that is called on mouseover. It has a built-in //
// delay time to avoid balloons popping up on rapid mouseover events //
//////////////////////////////////////////////////////////////////////////
Balloon.prototype.showTooltip = function(evt,caption,sticky,width,height) {
// If the objext is not configured by now, fall back to default
if (!this.configured) {
BalloonConfig(this,'GBubble');
}
// Cursor tracking halts after one of these vertical
// or horizontal thresholds are reached
this.stopTrackingX = this.trackCursor ? 100 : 10;
this.stopTrackingY = this.trackCursor ? 50 : 10;
// Awful IE bug, page load aborts if the balloon is fired
// before the page is fully loaded.
if (this.isIE() && document.readyState.match(/complete/i)) {
this.suppress = false;
}
// All balloons have been suppressed, go no further
if (this.suppress || balloonIsSuppressed) {
return false;
}
// Non-sticky balloons suppressed
if (tooltipIsSuppressed && !sticky) {
return false;
}
// We use 1-100 scale for opacity internally
if (this.opacity && this.opacity < 1) {
this.opacity = parseInt(parseFloat(this.opacity) * 100);
}
else if (this.opacity && this.opacity == 1) {
this.opacity = 100;
}
else if (!this.opacity) {
this.opacity == 100;
}
// Sorry Konqueror, no fade-in or translucency for you!
if (this.isKonqueror()) {
this.allowFade = false;
this.opacity = 100;
}
// With IE, fading and translucency are not very compatible
// use opaque balloons if fadein is enabled
if (this.isIE() && this.allowFade) {
this.opacity = 100;
}
// Check for mouseover (vs. mousedown or click)
var mouseOver = evt.type.match('mouseover','i');
// if the firing event is a click, fade-in and a non-sticky balloon make no sense
if (!mouseOver) {
sticky = true;
this.fadeOK = false;
// case where hover and click events both trigger balloons
if (balloonIsVisible) {
this.hideTooltip();
}
}
else {
this.fadeOK = this.allowFade;
}
// Don't fire on mouseover if a non-sticky balloon is visible
if (balloonIsVisible && !balloonIsSticky && mouseOver) {
return false;
}
// Don't start a non-sticky balloon if a sticky one is visible
if (balloonIsVisible && balloonIsSticky && !sticky) {
return false;
}
// Ignore repeated firing of mouseover->mouseout events on
// the same element (Safari)
var el = this.getEventTarget(evt);
if (sticky && mouseOver && this.isSameElement(el,this.currentElement)) {
return false;
}
this.currentElement = el;
// remember the coordinates of the element
this.elCoords = this.getLoc(el,'region');
// attach a mouseout event handler to the target element
if (!sticky) {
var mouseoutFunc = el.onmouseout;
var closeBalloon = function() {
Balloon.prototype.hideTooltip();
// fall through to any onmouseout event specified elsewhere
if (mouseoutFunc) {
mouseoutFunc();
}
}
if (!mouseOver) {
el.onmouseup = function() {return false};
}
el.onmouseout = closeBalloon;
}
balloonIsSticky = sticky;
this.hideTooltip();
// request the contents synchronously (ie wait for result)
this.currentHelpText = this.getAndCheckContents(caption);
// no contents? abort.
if (!this.currentHelpText) {
return false;
}
this.width = width;
this.height = height;
this.actualWidth = null;
// make sure old balloons are removed
this.hideTooltip();
// Put the balloon contents and images into a visible (but offscreen)
// element so they will be preloaded and have a layout to
// calculate the balloon dimensions
this.container = document.createElement('div');
this.container.id = 'balloonPreloadContainer';
document.body.appendChild(this.container);
this.setStyle(this.container,'position','absolute');
this.setStyle(this.container,'top',-8888);
this.setStyle(this.container,'font-family',this.fontFamily);
this.setStyle(this.container,'font-size',this.fontSize);
// protect escaped '&'
this.currentHelpText = this.currentHelpText.replace(/\&/g, '&');
this.container.innerHTML = unescape(this.currentHelpText);
// make sure balloon image path is complete
if (this.images) {
// main background image
this.balloonImage = this.balloonImage ? this.images +'/'+ this.balloonImage : false;
this.ieImage = this.ieImage ? this.images +'/'+ this.ieImage : false;
// optional stems
this.upLeftStem = this.upLeftStem ? this.images +'/'+ this.upLeftStem : false;
this.upRightStem = this.upRightStem ? this.images +'/'+ this.upRightStem : false;
this.downLeftStem = this.downLeftStem ? this.images +'/'+ this.downLeftStem : false;
this.downRightStem = this.downRightStem ? this.images +'/'+ this.downRightStem : false;
this.closeButton = this.closeButton ? this.images +'/'+ this.closeButton : false;
this.images = false;
}
// The PNG alpha channels (shadow transparency) are not
// handled properly by IE < 6. Also, if opacity is set to
// < 1 (translucent balloons), any version of IE does not
// handle the image properly.
// Google chrome is a bit dodgey too
// If there is an IE image provided, use that instead.
if (this.ieImage && (this.isIE() || this.isChrome())) {
if (this.isOldIE() || this.opacity || this.allowFade) {
this.balloonImage = this.ieImage;
}
}
// preload balloon images
if (!this.preloadedImages) {
var images = new Array(this.balloonImage, this.closeButton);
if (this.ieImage) {
images.push(this.ieImage);
}
if (this.stem) {
images.push(this.upLeftStem,this.upRightStem,this.downLeftStem,this.downRightStem);
}
var len = images.length;
for (var i=0;i pageMid ? 'up' : 'down';
var hOrient = self.activeRight > pageCen ? 'left' : 'right';
// get the preloaded balloon contents
var helpText = self.container.innerHTML;
self.actualWidth = self.getLoc(self.container,'width');
if (!isNaN(self.actualWidth)) {
self.actualWidth += 10;
}
self.parent.removeChild(self.container);
var wrapper = document.createElement('div');
wrapper.id = 'contentWrapper';
self.contents.appendChild(wrapper);
wrapper.innerHTML = helpText;
// how and where to draw the balloon
self.setBalloonStyle(vOrient,hOrient,pageWidth,pageLeft);
// close control for balloon or box
if (balloonIsSticky) {
self.addCloseButton();
}
balloonIsVisible = true;
self.pending = false;
// in IE < 7, hide