/* 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, '&amp'); 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