source: trunk/web-app/js/HelpBalloon.js @ 449

Last change on this file since 449 was 449, checked in by gav, 15 years ago

Upgrade to Grails-1.2.1, all plugins upgraded to latest versions and configs adjusted to suite.

File size: 27.2 KB
Line 
1//
2// Copyright (c) 2008 Beau D. Scott | http://www.beauscott.com
3//
4// Permission is hereby granted, free of charge, to any person
5// obtaining a copy of this software and associated documentation
6// files (the "Software"), to deal in the Software without
7// restriction, including without limitation the rights to use,
8// copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the
10// Software is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice shall be
14// included in all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23// OTHER DEALINGS IN THE SOFTWARE.
24//
25
26/**
27 * HelpBalloon.js
28 * Prototype/Scriptaculous based help balloons / dialog balloons
29 * @version 2.0.1
30 * @requires prototype.js <http://www.prototypejs.org/>
31 * @author Beau D. Scott <beau_scott@hotmail.com>
32 * 4/10/2008
33 */
34var HelpBalloon = Object.extend(Class.create(), {
35        /**
36         * Enumerated value for dynamic rendering position.
37         * @static
38         */
39        POS_DYNAMIC: -1,
40        /**
41         * Enumerated value for the top-left rendering position.
42         * @static
43         */
44        POS_TOP_LEFT: 0,
45        /**
46         * Enumerated value for the top-right rendering position.
47         * @static
48         */
49        POS_TOP_RIGHT: 1,
50        /**
51         * Enumerated value for the bottom-left rendering position.
52         * @static
53         */
54        POS_BOTTOM_LEFT: 2,
55        /**
56         * Enumerated value for the bottom-right rendering position.
57         * @static
58         */
59        POS_BOTTOM_RIGHT: 3,
60        /**
61         * CSS Classname to look for when doing auto link associations.
62         * @static
63         */
64        ELEMENT_CLASS_NAME: 'HelpBalloon',
65        /**
66         * Global array of all HelpBalloon instances.
67         * (Cheaper than document.getElementByClassName with a property check)
68         * @static
69         * @private
70         */
71        _balloons: [],
72        /**
73         * Event listener that auto-associates anchors with a dynamic HelpBalloon.
74         * Also begins mouse movement registration
75         * @static
76         */
77        registerClassLinks: function(e) {
78                $A(document.getElementsByClassName(HelpBalloon.ELEMENT_CLASS_NAME))
79                        .each(function(obj){
80                        // Only apply any element with an href tag
81                        if(obj && obj.tagName && obj.href && obj.href != '')
82                        {
83                                new HelpBalloon({
84                                        icon:obj,
85                                        method: 'get'
86                                });
87                        }
88                });
89               
90                Event.observe(document, 'mousemove', HelpBalloon._trackMousePosition);
91               
92        },
93       
94        /**
95         * Private cache of the client's mouseX position
96         */
97        _mouseX: 0,
98       
99        /**
100         * Private cache of the client's mouseY position
101         */
102        _mouseY: 0,
103       
104        /**
105         * @param {Event} e
106         */
107        _trackMousePosition: function(e) {
108                if(!e) e = window.event;
109                HelpBalloon._mouseX = e.clientX;
110                HelpBalloon._mouseY = e.clientY;
111        }
112});
113
114//
115// Event for activating HelpBalloon classed links
116//
117Event.observe(window, 'load', HelpBalloon.registerClassLinks);
118
119HelpBalloon.prototype = {
120       
121//
122// Properties
123//     
124        /**
125         * Configuration options
126         * @var {HelpBalloon.Options}
127         */
128        options: null,
129
130        /**
131         * Containing element of the balloon
132         * @var {Element}
133         */
134        container: null,
135        /**
136         * Inner content container
137         * @var {Element}
138         */
139        inner: null,
140        /**
141         * A reference to the anchoring element/icon
142         * @var {Element}
143         */
144        icon: null,
145        /**
146         * Content container
147         * @var {Element}
148         */
149        content: null,
150        /**
151         * Closing button element
152         * @var {Element}
153         */
154        button: null,
155        /**
156         * The closer object. This can be the same as button, but could
157         * also be a div with a png loaded as the back ground, browser dependent.
158         * @var {Element}
159         */
160        closer: null,
161        /**
162         * Title container
163         * @var {Element}
164         */
165        titleContainer: null,
166        /**
167         * Background container (houses the balloon images
168         * @var {Element}
169         */
170        bgContainer: null,
171        /**
172         * Array of balloon image references
173         * @var {Array}
174         */
175        balloons: null,
176       
177        /**
178         * The local store of 'title'. Will change if the balloon is making a remote call
179         * unless options.title is specified
180         * @var {String}
181         */
182        _titleString: null,
183       
184        /**
185         * The balloons visibility state.
186         * @var {Boolean}
187         */
188        visible: false,
189       
190        /**
191         * Rendering status
192         * @var {Boolean}
193         */
194        drawn: false,
195
196        /**
197         * Stores the balloon coordinates
198         * @var {Object}
199         */
200        balloonCoords: null,
201               
202        /**
203         * Width,height of the balloons
204         * @var {Array}
205         */
206        balloonDimensions: null,
207       
208        /**
209         * ID for HelpBalloon
210         * @var {String}
211         */
212        id: null,
213       
214        /**
215         * Used at render time to measure the dimensions of the loaded balloon
216         * @private
217         */
218        _lastBalloon: null,
219
220//
221// Methods
222//
223
224        /**
225         * @param {Object} options
226         * @see HelpBalloon.Options
227         * @constructor
228         */
229        initialize: function(options)
230        {
231               
232                this.options = new HelpBalloon.Options();
233                Object.extend(this.options, options || {});
234
235                this._titleString = this.options.title;
236                this.balloonDimensions = [0,0];
237               
238                //
239                // Preload the balloon and button images so they're ready
240                // at render time
241                //
242                // 0 1
243                //  X
244                // 2 3
245                //
246                this.balloons = [];
247                for(var i = 0; i < 4; i++)
248                {
249                        var balloon = new Element('img', {
250                                src: this.options.balloonPrefix + i + this.options.balloonSuffix
251                        });
252                        this.balloons.push(balloon.src);
253                }
254               
255                this._lastBalloon = balloon;
256               
257                this.button = new Element('img', {
258                        src: this.options.button
259                });
260               
261                //
262                // Create the anchoring icon, or attach the balloon to the given icon element
263                // If a string is passed in, assume it's a URL, if it's an object, assume it's
264                // a DOM member.
265                //
266                if(typeof this.options.icon == 'string')
267                {
268                        this.icon = new Element('img', {
269                                src: this.options.icon,
270                                id: this.id + "_icon"
271                        });
272                        Element.setStyle(this.icon, this.options.iconStyle);
273                }
274                else
275                {
276                        //
277                        // Not a string given (most likely an object. Do not append the element
278                        // Kind of a hack for now, but I'll fix it in the next version.
279                        //
280                        this.icon = this.options.icon;
281                        this.options.returnElement = true;
282                }
283               
284                this.icon._HelpBalloon = this;
285                       
286                //
287                // Attach rendering events
288                //
289
290                for(i = 0; i < this.options.useEvent.length; i++)
291                        Event.observe(this.icon, this.options.useEvent[i], this.toggle.bindAsEventListener(this));
292               
293                this.container = new Element('div');
294                this.container._HelpBalloon = this;
295               
296                this.id = 'HelpBalloon_' + Element.identify(this.container);
297               
298                HelpBalloon._balloons.push(this);
299
300                //
301                // If we are not relying on other javascript to attach the anchoring icon
302                // to the DOM, we'll just do where the script is called from. Default behavior.
303                //
304                // If you want to use external JavaScript to attach it to the DOM, attach this.icon
305                //
306                if(!this.options.returnElement)
307                {
308                        document.write('<span id="' + this.id + '"></span>');
309                        var te = $(this.id);
310                        var p = te.parentNode;
311                        p.insertBefore(this.icon, te);
312                        p.removeChild(te);
313                }
314        },
315       
316        /**
317         * Toggles the help balloon
318         * @param {Object} e Event
319         */
320        toggle: function(event)
321        {
322                if(!event) event = window.event || {type: this.options.useEvent, target: this.icon};
323                var icon = Event.element(event);
324                Event.stop(event);
325                if(event.type == this.options.useEvent && !this.visible && icon == this.icon)
326                {
327                        this.show(event);
328                }
329                else
330                        this.hide();
331        },
332
333        /**
334         * Triggers the balloon to appear
335         */
336        show: function(event)
337        {
338                if(!this.visible){
339                        if(!event) event = window.event;
340                        if(!this.drawn || !this.options.cacheRemoteContent) this._draw();
341                        this._reposition(event);
342                        this._hideOtherHelps();
343                        if(this.options.showEffect)
344                        {
345                                this.options.showEffect(this.container, Object.extend(this.options.showEffectOptions, {
346                                        afterFinish: this._afterShow.bindAsEventListener(this)
347                                }));
348                        }
349                        else
350                        {
351                                this._afterShow();
352                        }
353                        Event.observe(window, 'resize', this._reposition.bindAsEventListener(this));
354                }
355        },
356       
357        /**
358         * Sets the container to block styling and hides the elements below the
359         * container (if in IE)
360         * @private
361         */
362        _afterShow: function()
363        {
364                Element.setStyle(this.container, {
365                        'display': 'block'
366                });
367                this._hideLowerElements();
368                this.visible = true;
369                if(this.options.autoHideTimeout)
370                {
371                        setTimeout(this._hideQueue.bind(this), this.options.autoHideTimeout);
372                }
373        },
374       
375        /**
376         * Checks the mouse position and triggers a hide after the time specified in autoHideTimeout
377         * if the mouse is not currently over the balloon, otherwise it requeue's a hide for later.
378         */
379        _hideQueue: function()
380        {
381                if(Position.within(this.container, HelpBalloon._mouseX, HelpBalloon._mouseY))
382                        setTimeout(this._hideQueue.bind(this), this.options.autoHideTimeout);
383                else
384                        this.hide();
385        },
386       
387        /**
388         * Hides the balloon
389         */
390        hide: function()
391        {
392                if(this.visible)
393                {
394                        this._showLowerElements();
395                        if(this.options.hideEffect)
396                        {
397                                this.options.hideEffect(this.container, Object.extend(this.options.hideEffectOptions, {
398                                        afterFinish: this._afterHide.bindAsEventListener(this)
399                                }));
400                        }
401                        else
402                        {
403                                this._afterHide();
404                        }
405                        Event.stopObserving(window, 'resize', this._reposition.bindAsEventListener(this));
406                }
407        },
408       
409        /**
410         * Sets the container's display to block
411         * @private
412         */
413        _afterHide: function()
414        {
415                Element.setStyle(this.container, {
416                        'display': 'none'
417                });
418                this.visible = false;
419        },
420       
421        /**
422         * Redraws the balloon based on the current coordinates of the icon.
423         * @private
424         */
425        _reposition: function(event)
426        {
427                if(this.icon.tagName.toLowerCase() == 'area' || !!this.icon.isMap)
428                {
429                        this.balloonCoords = Event.pointer(event);
430                }
431                else
432                {
433                        this.balloonCoords = this._getXY(this.icon);
434                        //Horizontal and vertical offsets in relation to the icon's 0,0 position.
435                        // Default is the middle of the object
436                        var ho = this.icon.offsetWidth / 2;
437                        var vo = this.icon.offsetHeight / 2;
438                       
439                        var offsets = this.options.anchorPosition.split(/\s+/gi);
440                        // Only use the first two specified values
441                        if(offsets.length > 2)
442                                offsets.length = 2;
443                       
444                        for(var i = 0; i < offsets.length; i++)
445                        {
446                                switch(offsets[i].toLowerCase())
447                                {
448                                        case 'left':
449                                                        ho = 0;
450                                                break;
451                                        case 'right':
452                                                        ho = this.icon.offsetWidth;
453                                                break;
454                                        case 'center':
455                                                        ho = this.icon.offsetWidth / 2;
456                                                break;
457                                        case 'top':
458                                                        vo = 0;
459                                                break;
460                                        case 'middle':
461                                                        vo = this.icon.offsetHeight / 2;
462                                                break;
463                                        case 'bottom':
464                                                        vo = this.icon.offsetHeight;
465                                                break;
466                                        default:
467                                                var numVal = parseInt(offsets[i]); 
468                                                if(!isNaN(numVal))
469                                                {
470                                                        // 0 = width, 1 = height (WxH)
471                                                        if(i == 0)
472                                                        {
473                                                                if(numVal < 0)
474                                                                {
475                                                                        ho = 0;
476                                                                }
477                                                                else
478                                                                {
479                                                                        if(numVal > this.icon.offsetWidth)
480                                                                                ho = this.icon.offsetWidth;
481                                                                        else
482                                                                                ho = numVal
483                                                                }
484                                                        }
485                                                        else
486                                                        {
487                                                                if(numVal < 0)
488                                                                {
489                                                                        vo = 0;
490                                                                }
491                                                                else
492                                                                {
493                                                                        if(numVal > this.icon.offsetHeight)
494                                                                                vo = this.icon.offsetHeight;
495                                                                        else
496                                                                                vo = numVal
497                                                                }
498                                                        }
499                                                }
500                                                break; 
501                                }
502                        }
503                        this.balloonCoords.x += ho;
504                        this.balloonCoords.y += vo;
505                }
506               
507                //
508                // Figure out what position to show based on available realestate
509                // unless
510                // 0 1
511                //  X
512                // 2 3
513                // Number indicates position of corner opposite anchor
514                //
515                var pos = 1;
516                if(this.options.fixedPosition == HelpBalloon.POS_DYNAMIC)
517                {
518                        var offsetHeight = this.balloonCoords.y - this.balloonDimensions[1];
519                        if(offsetHeight < 0)
520                                pos += 2;
521       
522                        var offsetWidth = this.balloonCoords.x + this.balloonDimensions[0];
523                        var ww = Prototype.Browser.IE ? document.body.clientWidth : window.outerWidth;
524                        if(offsetWidth > ww)
525                                pos -- ;
526                }
527                else
528                        pos = this.options.fixedPosition;
529
530                var zx = 0;
531                var zy = 0;
532               
533                //
534                // 0 1
535                //  X
536                // 2 3
537                //
538                switch(pos)
539                {
540                        case 0:
541                                zx = this.balloonCoords.x - this.balloonDimensions[0];
542                                zy = this.balloonCoords.y - this.balloonDimensions[1];
543                                break;
544                       
545                        case 1:
546                                zx = this.balloonCoords.x;
547                                zy = this.balloonCoords.y - this.balloonDimensions[1];
548                                break;
549                       
550                        case 2:
551                                zx = this.balloonCoords.x - this.balloonDimensions[0];
552                                zy = this.balloonCoords.y;
553                                break;
554                       
555                        case 3:
556                                zx = this.balloonCoords.x;
557                                zy = this.balloonCoords.y;
558                                break;
559                }
560                var containerStyle = {
561                        /*'backgroundRepeat': 'no-repeat',
562                        'backgroundColor': 'transparent',
563                        'backgroundPosition': 'top left',*/
564                        'left'  : zx + "px",
565                        'top'   : zy + "px",
566                        'width' : this.balloonDimensions[0] + 'px',
567                        'height' : this.balloonDimensions[1] + 'px'
568                }
569                if(Prototype.Browser.IE)
570                {
571                        //
572                        // Fix for IE alpha transparencies
573                        //
574                        if(this.balloons[pos].toLowerCase().indexOf('.png') > -1)
575                        {
576                                Element.setStyle(this.bgContainer, {
577                                        'left'          : '0px',
578                                        'top'           : '0px',       
579                                        'filter'        : "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.balloons[pos] + "', sizingMethod='scale')",
580                                        'width'         : this.balloonDimensions[0] + 'px',
581                                        'height'        : this.balloonDimensions[1] + 'px',
582                                        'position'      : 'absolute'
583                                });
584                        }
585                        else
586                                containerStyle['background'] = 'transparent url(' + this.balloons[pos] + ') top left no-repeat';
587                }
588                else
589                {
590                                containerStyle['background'] = 'transparent url(' + this.balloons[pos] + ') top left no-repeat';
591                }
592                Element.setStyle(this.container, containerStyle);
593        },
594
595        /**
596         * Renders the Balloon
597         * @private
598         */
599        _draw: function()
600        {
601                Element.setStyle(
602                        this.container, 
603                        Object.extend(this.options.balloonStyle, {
604                                'position':     'absolute',
605                                'display':              'none'
606                        })
607                );
608               
609                var url = this.options.dataURL;
610               
611                //
612                // Play nicely with anchor tags being used as the icon. Use it's specified href as our
613                // data URL unless one has already been used specified in this.options.dataURL.
614                // We'll also force a new request with this as it may be an image map.
615                //
616                if(this.icon.className == 'a')
617                {
618                        if(!this.options.dataURL && this.icon.href != ''){
619                                url = this.icon.href;
620                                this.options.cacheRemoteContent = false;
621                        }
622                }
623               
624                if(url && (!this.drawn || !this.options.cacheRemoteContent))
625                {
626                        var cont = new Ajax.Request(this.options.dataURL, {asynchronous: false, method: this.options.method});
627                        //
628                        // Expects the following XML format:
629                        // <HelpBalloon>
630                        //              <title>My Title</title>
631                        //              <content>My content</content>
632                        // </HelpBaloon>
633                        //
634                        var doHTML = false;
635                        if(cont.transport.responseXML)
636                        {
637                                var xml = cont.transport.responseXML.getElementsByTagName('HelpBalloon')[0];
638
639                                if(xml)
640                                {
641                                        if(!this.options.title)
642                                        {
643                                                xmlTitle = xml.getElementsByTagName('title')[0];
644                                                if(xmlTitle) this._titleString = xmlTitle.firstChild.nodeValue;
645                                        }
646
647                                        xmlContent = xml.getElementsByTagName('content')[0];
648                                        if(xmlContent) this.options.content = xmlContent.firstChild.nodeValue;
649                                }
650                                else
651                                        doHTML = true;
652                        }
653                        else
654                                doHTML = true;
655
656                        if(doHTML)
657                        {
658                                // Attempt to get the title from a <title/> HTML tag, unless the title option has been set. If so, use that.
659                                if(!this.options.title)
660                                {
661                                        var htmlTitle = cont.transport.responseText.match(/\<title\>([^\<]+)\<\/title\>/gi);
662                                        if(htmlTitle)
663                                        {
664                                                htmlTitle = htmlTitle.toString().replace(/\<title\>|\<\/title\>/gi, '');
665                                                this._titleString = htmlTitle;
666                                        }
667                                }
668                                this.options.content = cont.transport.responseText;
669                        }
670                }
671               
672                this.balloonDimensions[0] = this._lastBalloon.width;
673                this.balloonDimensions[1] = this._lastBalloon.height;
674               
675                var contentDimensions = [
676                        this.balloonDimensions[0] - (2 * this.options.contentMargin),
677                        this.balloonDimensions[1] - (2 * this.options.contentMargin)
678                ];
679               
680                var buttonDimensions = [
681                        this.button.width,
682                        this.button.height
683                ];
684               
685                //
686                // Create all the elements on demand if they haven't been created yet
687                //
688                if(!this.drawn)
689                {
690                        this.inner = new Element('div');
691               
692                        this.titleContainer = new Element('div');
693                        this.inner.appendChild(this.titleContainer);
694                       
695                        // PNG fix for IE
696                        if(Prototype.Browser.IE && this.options.button.toLowerCase().indexOf('.png') > -1)
697                        {
698                                this.bgContainer = new Element('div');
699                               
700                                // Have to create yet-another-child of container to house the background for IE... when it was set in
701                                // the main container, it for some odd reason prevents child components from being clickable.
702                                this.container.appendChild(this.bgContainer);
703                               
704                                this.closer =  new Element('div');
705                                Element.setStyle(this.closer, {
706                                        'filter':
707                                                "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.options.button + "', sizingMethod='scale')"
708                                });
709                        }
710                        else
711                        {
712                                this.closer = this.button;
713                        }
714                       
715                        Event.observe(this.closer, 'click', this.toggle.bindAsEventListener(this));
716                        this.inner.appendChild(this.closer);
717                       
718                        this.content =  new Element('div');
719                        this.inner.appendChild(this.content);
720                       
721                        this.container.appendChild(this.inner);
722                       
723                        document.getElementsByTagName('body')[0].appendChild(this.container);
724                       
725                        this.drawn = true;
726                }
727
728                // Reset the title element and reappend the title value (could have changed with a new URL)
729                this.titleContainer.innerHTML = '';
730                this.titleContainer.appendChild(document.createTextNode(this._titleString));
731               
732                // Reset content value:
733                this.content.innerHTML = this.options.content;
734
735                //
736                // Reapply styling to components as values might have changed
737                //
738               
739                Element.setStyle(this.inner, {
740                        'position':     'absolute',
741                        'top':                  this.options.contentMargin + 'px',
742                        'left':                 this.options.contentMargin + 'px',
743                        'width':                contentDimensions[0] + 'px',
744                        'height':               contentDimensions[1] + 'px'
745                });
746
747                Element.setStyle(this.titleContainer, {
748                        'width':                (contentDimensions[0] - buttonDimensions[0]) + 'px',
749                        'height':               buttonDimensions[1] + 'px',
750                        'position':             'absolute',
751                        'overflow':             'hidden',
752                        'top':                  '0px',
753                        'left':                 '0px'
754                });
755               
756                Element.setStyle(this.titleContainer, this.options.titleStyle);
757               
758                Element.setStyle(this.closer, {
759                        'width': buttonDimensions[0] + 'px',
760                        'height': buttonDimensions[1] + 'px',
761                        'cursor':       'pointer',
762                        'position':     'absolute',
763                        'top':          '0px',
764                        'right':        '0px'
765                });
766               
767                Element.setStyle(this.content, {
768                        'width':                contentDimensions[0] + 'px',
769                        'height':               (contentDimensions[1] - this.button.height) + 'px',
770                        'overflow':     'auto',
771                        'position':     'absolute',
772                        'top':                  buttonDimensions[1] + 'px',
773                        'left':                 '0px',
774                        'fontFamily':   'verdana',
775                        'fontSize':     '11px',
776                        'fontWeight':   'normal',
777                        'color':                'black'
778                });
779               
780        },
781
782        /**
783         * Gets the current position of the obj
784         * @param {Element} element to get position of
785         * @return Object of (x, y, x2, y2)
786         */
787        _getXY: function(obj)
788        {
789                var pos = Position.cumulativeOffset(obj)
790                var y = pos[1];
791                var x = pos[0];
792                var x2 = x + parseInt(obj.offsetWidth);
793                var y2 = y + parseInt(obj.offsetHeight);
794                return {'x':x, 'y':y, 'x2':x2, 'y2':y2};
795
796        },
797
798        /**
799         * Determins if the object is a child of the balloon element
800         * @param {Element} Element to check parentage
801         * @return {Boolean}
802         * @private
803         */
804        _isChild: function(obj)
805        {
806                var i = 15;
807                do{
808                        if(obj == this.container)
809                                return true;
810                        obj = obj.parentNode;
811                }while(obj && i--);
812                return false
813        },
814
815        /**
816         * Determines if the balloon is over this_obj object
817         * @param {Element} Object to look under
818         * @return {Boolean}
819         * @private
820         */
821        _isOver: function(this_obj)
822        {
823                if(!this.visible) return false;
824                if(this_obj == this.container || this._isChild(this_obj)) return false;
825                var this_coords = this._getXY(this_obj);
826                var that_coords = this._getXY(this.container);
827                if(
828                        (
829                         (
830                          (this_coords.x >= that_coords.x && this_coords.x <= that_coords.x2)
831                           ||
832                          (this_coords.x2 >= that_coords.x &&  this_coords.x2 <= that_coords.x2)
833                         )
834                         &&
835                         (
836                          (this_coords.y >= that_coords.y && this_coords.y <= that_coords.y2)
837                           ||
838                          (this_coords.y2 >= that_coords.y && this_coords.y2 <= that_coords.y2)
839                         )
840                        )
841
842                  ){
843                        return true;
844                }
845                else
846                        return false;
847        },
848
849        /**
850         * Restores visibility of elements under the balloon
851         * (For IE)
852         * TODO: suck yourself
853         * @private
854         */
855        _showLowerElements: function()
856        {
857                if(this.options.hideUnderElementsInIE)
858                {
859                        var elements = this._getWeirdAPIElements();
860                        for(var i = 0; i < elements.length; i++)
861                        {
862                                if(this._isOver(elements[i]))
863                                {
864                                        if(elements[i].style.visibility != 'visible' && elements[i].hiddenBy == this)
865                                        {
866                                                elements[i].style.visibility = 'visible';
867                                                elements[i].hiddenBy = null;
868                                        }
869                                }
870                        }
871                }
872        },
873
874        /**
875         * Hides elements below the balloon
876         * (For IE)
877         * @private
878         */
879        _hideLowerElements: function()
880        {
881                if(this.options.hideUnderElementsInIE)
882                {
883                        var elements = this._getWeirdAPIElements();
884                        for(var i = 0; i < elements.length; i++)
885                        {
886                                if(this._isOver(elements[i]))
887                                {
888                                        if(elements[i].style.visibility != 'hidden')
889                                        {
890                                                elements[i].style.visibility = 'hidden';
891                                                elements[i].hiddenBy = this;
892                                        }
893                                }
894                        }
895                }
896        },
897
898        /**
899         * Determines which elements need to be hidden
900         * (For IE)
901         * @return {Array} array of elements
902         */
903        _getWeirdAPIElements: function()
904        {
905                if(!Prototype.Browser.IE) return [];
906                var objs = ['select', 'input', 'object'];
907                var elements = [];
908                for(var i = 0; i < objs.length; i++)
909                {
910                        var e = document.getElementsByTagName(objs[i]);
911                        for(var j = 0; j < e.length; j++)
912                        {
913                                elements.push(e[j]);
914                        }
915                }
916                return elements;
917        },
918
919        /**
920         * Hides the other visible help balloons
921         * @param {Event} e
922         */
923        _hideOtherHelps: function(e)
924        {
925                if(this.options.hideOtherBalloonsOnDisplay)
926                {
927                        $A(HelpBalloon._balloons).each(function(obj){
928                                if(obj != this)
929                                {
930                                        obj.hide();
931                                }
932                        }.bind(this));
933                }
934        }
935};
936
937/**
938 * HelpBalloon.Options
939 * Helper class for defining options for the HelpBalloon object
940 * @author Beau D. Scott <beau_scott@hotmail.com>
941 */
942HelpBalloon.Options = Class.create();
943HelpBalloon.Options.prototype = {
944       
945        /**
946         * @constructor
947         * @param {Object} overriding options
948         */
949        initialize: function(values){
950                // Apply the overriding values to this
951                Object.extend(this, values || {});
952        },
953       
954        /**
955         * Show Effect
956         * The Scriptaculous (or compatible) showing effect function
957         * @var Function
958         */
959        showEffect: window.Scriptaculous ? Effect.Appear : null,
960       
961        /**
962         * Show Effect options
963         */
964        showEffectOptions: {duration: 0.2},
965       
966        /**
967         * Hide Effect
968         * The Scriptaculous (or compatible) hiding effect function
969         * @var Function
970         */
971        hideEffect: window.Scriptaculous ? Effect.Fade : null,
972       
973        /**
974         * Show Effect options
975         */
976        hideEffectOptions: {duration: 0.2},
977       
978        /**
979         * For use with embedding this object into another. If true, the icon is not created
980         * and not appeneded to the DOM at construction.
981         * Default is false
982         * @var {Boolean}
983         */
984        returnElement: false,
985       
986        /**
987         * URL to the anchoring icon image file to use. This can also be a direct reference
988         * to an existing element if you're using that as your anchoring icon.
989         * @var {Object}
990         */
991        icon: 'images/icon.gif',
992       
993        /**
994         * Alt text of the help icon
995         * @var {String}
996         */
997        altText: 'Click here for help with this topic.',
998       
999        /**
1000         * URL to pull the title/content XML
1001         * @var {String}
1002         */
1003        dataURL: null,
1004       
1005        /**
1006         * Static title of the balloon
1007         * @var {String}
1008         */
1009        title: null,
1010       
1011        /**
1012         * Static content of the balloon
1013         * @var {String}
1014         */
1015        content: null,
1016       
1017        /**
1018         * The event type to listen for on the icon to show the balloon.
1019         * Default 'click'
1020         * @var {String}
1021         */
1022        useEvent: ['click'],
1023       
1024        /**
1025         * Request method for dynamic content. (get, post)
1026         * Default 'get'
1027         * @var {String}
1028         */
1029        method: 'get',
1030       
1031        /**
1032         * Flag indicating cache the request result. If this is false, every
1033         * time the balloon is shown, it will retrieve the remote url and parse it
1034         * before the balloon appears, updating the content. Otherwise, it will make
1035         * the call once and use the same content with each subsequent showing.
1036         * Default true
1037         * @var {Boolean}
1038         */
1039        cacheRemoteContent: true,
1040       
1041        /**
1042         * Vertical and horizontal margin of the content pane
1043         * @var {Number}
1044         */
1045        contentMargin: 35,
1046       
1047        /**
1048         * X coordinate of the closing button
1049         * @var {Number}
1050         */
1051        buttonX: 246,
1052       
1053        /**
1054         * Y coordinate of the closing button
1055         * @var {Number}
1056         */
1057        buttonY: 35,
1058       
1059        /**
1060         * Closing button image path
1061         * @var {String}
1062         */
1063        button: 'images/button.png',
1064       
1065        /**
1066         * Balloon image path prefix. There are 4 button images, numerically named, starting with 0.
1067         * 0 1
1068         *  X
1069         * 2 3
1070         * X indicates the anchor corner
1071         * @var {String}
1072         */
1073        balloonPrefix: 'images/balloon-',
1074       
1075        /**
1076         * The image filename suffix, including the file extension
1077         * @var {String}
1078         */
1079        balloonSuffix: '.png',
1080       
1081        /**
1082         * Position of the balloon's anchor relative to the icon element.
1083         * Combine one horizontal indicator (left, center, right) and one vertical indicator (top, middle, bottom).
1084         * Numeric values can also be used in an X Y order. So a value of 9 13 would place the anchor 9 pixels from
1085         * the left and 13 pixels below the top. (0,0 is top left). If values are greater than the width or height
1086         * the width or height of the anchor are used instead. If less than 0, 0 is used.
1087         * Default is 'center middle'
1088         * @var {String}
1089         */
1090        anchorPosition: 'center middle',
1091       
1092        /**
1093         * Flag indicating whether to hide the elements under the balloon in IE.
1094         * Setting this to false can cause rendering issues in Internet Explorer
1095         * as some elements appear on top of the balloon if they're not hidden.
1096         * Default is true.
1097         * @var {Boolean}
1098         */
1099        hideUnderElementsInIE: true,
1100       
1101        /**
1102         * Default Balloon styling
1103         * @var {Object}
1104         */     
1105        balloonStyle: {},
1106       
1107        /**
1108         * Default Title Bar style
1109         * @var {Object}
1110         */
1111        titleStyle: {
1112                'color': 'black',
1113                'fontSize': '16px',
1114                'fontWeight': 'bold',
1115                'fontFamily': 'Verdana'
1116        },
1117       
1118        /**
1119         * Icon custom styling
1120         * @var {Object}
1121         */
1122        iconStyle: {
1123                'cursor': 'pointer'
1124        },
1125       
1126        /**
1127         * Flag indication whether to automatically hide any other visible HelpBalloon on the page before showing the current one.
1128         * @var {Boolean}
1129         */
1130        hideOtherBalloonsOnDisplay: true,
1131       
1132        /**
1133         * If you want the balloon to always display in a particular location, set this
1134         */
1135        fixedPosition: HelpBalloon.POS_DYNAMIC,
1136       
1137        /**
1138         * Number of milliseconds to hide the balloon after showing and after the mouse is not over the balloon.
1139         * A value of 0 means it will not auto-hide
1140         * @var {Number}
1141         */
1142        autoHideTimeout: 0
1143       
1144};
Note: See TracBrowser for help on using the repository browser.