Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / pkgs / pkg-tips-debug.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.Tip\r
9  * @extends Ext.Panel\r
10  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and\r
11  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned\r
12  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.\r
13  * @constructor\r
14  * Create a new Tip\r
15  * @param {Object} config The configuration options\r
16  */\r
17 Ext.Tip = Ext.extend(Ext.Panel, {\r
18     /**\r
19      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).\r
20      */\r
21     /**\r
22      * @cfg {Number} width\r
23      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of\r
24      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.\r
25      */\r
26     /**\r
27      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).\r
28      */\r
29     minWidth : 40,\r
30     /**\r
31      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.\r
32      */\r
33     maxWidth : 300,\r
34     /**\r
35      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"\r
36      * for bottom-right shadow (defaults to "sides").\r
37      */\r
38     shadow : "sides",\r
39     /**\r
40      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value\r
41      * for this tip relative to its element of origin (defaults to "tl-bl?").\r
42      */\r
43     defaultAlign : "tl-bl?",\r
44     autoRender: true,\r
45     quickShowInterval : 250,\r
46 \r
47     // private panel overrides\r
48     frame:true,\r
49     hidden:true,\r
50     baseCls: 'x-tip',\r
51     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},\r
52     autoHeight:true,\r
53 \r
54     closeAction: 'hide',\r
55 \r
56     // private\r
57     initComponent : function(){\r
58         Ext.Tip.superclass.initComponent.call(this);\r
59         if(this.closable && !this.title){\r
60             this.elements += ',header';\r
61         }\r
62     },\r
63 \r
64     // private\r
65     afterRender : function(){\r
66         Ext.Tip.superclass.afterRender.call(this);\r
67         if(this.closable){\r
68             this.addTool({\r
69                 id: 'close',\r
70                 handler: this[this.closeAction],\r
71                 scope: this\r
72             });\r
73         }\r
74     },\r
75 \r
76     /**\r
77      * Shows this tip at the specified XY position.  Example usage:\r
78      * <pre><code>\r
79 // Show the tip at x:50 and y:100\r
80 tip.showAt([50,100]);\r
81 </code></pre>\r
82      * @param {Array} xy An array containing the x and y coordinates\r
83      */\r
84     showAt : function(xy){\r
85         Ext.Tip.superclass.show.call(this);\r
86         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){\r
87             this.doAutoWidth();\r
88         }\r
89         if(this.constrainPosition){\r
90             xy = this.el.adjustForConstraints(xy);\r
91         }\r
92         this.setPagePosition(xy[0], xy[1]);\r
93     },\r
94 \r
95     // protected\r
96     doAutoWidth : function(){\r
97         var bw = this.body.getTextWidth();\r
98         if(this.title){\r
99             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));\r
100         }\r
101         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr");\r
102         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));\r
103         \r
104         // IE7 repaint bug on initial show\r
105         if(Ext.isIE7 && !this.repainted){\r
106             this.el.repaint();\r
107             this.repainted = true;\r
108         }\r
109     },\r
110 \r
111     /**\r
112      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}\r
113      * anchor position value.  Example usage:\r
114      * <pre><code>\r
115 // Show the tip at the default position ('tl-br?')\r
116 tip.showBy('my-el');\r
117 \r
118 // Show the tip's top-left corner anchored to the element's top-right corner\r
119 tip.showBy('my-el', 'tl-tr');\r
120 </code></pre>\r
121      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to\r
122      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or\r
123      * {@link #defaultAlign} if specified).\r
124      */\r
125     showBy : function(el, pos){\r
126         if(!this.rendered){\r
127             this.render(Ext.getBody());\r
128         }\r
129         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));\r
130     },\r
131 \r
132     initDraggable : function(){\r
133         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);\r
134         this.header.addClass('x-tip-draggable');\r
135     }\r
136 });\r
137 \r
138 // private - custom Tip DD implementation\r
139 Ext.Tip.DD = function(tip, config){\r
140     Ext.apply(this, config);\r
141     this.tip = tip;\r
142     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);\r
143     this.setHandleElId(tip.header.id);\r
144     this.scroll = false;\r
145 };\r
146 \r
147 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {\r
148     moveOnly:true,\r
149     scroll:false,\r
150     headerOffsets:[100, 25],\r
151     startDrag : function(){\r
152         this.tip.el.disableShadow();\r
153     },\r
154     endDrag : function(e){\r
155         this.tip.el.enableShadow(true);\r
156     }\r
157 });/**\r
158  * @class Ext.ToolTip\r
159  * @extends Ext.Tip\r
160  * A standard tooltip implementation for providing additional information when hovering over a target element.\r
161  * @constructor\r
162  * Create a new Tooltip\r
163  * @param {Object} config The configuration options\r
164  */\r
165 Ext.ToolTip = Ext.extend(Ext.Tip, {\r
166     /**\r
167      * When a Tooltip is configured with the {@link #delegate} option to cause selected child elements of the {@link #target}\r
168      * Element to each trigger a seperate show event, this property is set to the DOM element which triggered the show.\r
169      * @type DOMElement\r
170      * @property triggerElement\r
171      */\r
172     /**\r
173      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor for mouseover events to trigger\r
174      * showing this ToolTip.\r
175      */\r
176     /**\r
177      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the mouse exits the target element\r
178      * or after the {@link #dismissDelay} has expired if set (defaults to true).  If {@link closable} = true a close\r
179      * tool button will be rendered into the tooltip header.\r
180      */\r
181     /**\r
182      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays after the mouse enters the\r
183      * target element (defaults to 500)\r
184      */\r
185     showDelay: 500,\r
186     /**\r
187      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the target element but before the\r
188      * tooltip actually hides (defaults to 200).  Set to 0 for the tooltip to hide immediately.\r
189      */\r
190     hideDelay: 200,\r
191     /**\r
192      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip automatically hides (defaults to 5000).\r
193      * To disable automatic hiding, set dismissDelay = 0.\r
194      */\r
195     dismissDelay: 5000,\r
196     /**\r
197      * @cfg {Array} mouseOffset An XY offset from the mouse position where the tooltip should be shown (defaults to [15,18]).\r
198      */\r
199     /**\r
200      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it moves over the target element (defaults to false).\r
201      */\r
202     trackMouse : false,\r
203     /**\r
204      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target element, false to\r
205      * anchor it relative to the mouse coordinates (defaults to true).  When anchorToTarget is\r
206      * true, use {@link #defaultAlign} to control tooltip alignment to the target element.  When\r
207      * anchorToTarget is false, use {@link #anchorPosition} instead to control alignment.\r
208      */\r
209     anchorToTarget: true,\r
210     /**\r
211      * @cfg {Number} anchorOffset A numeric pixel value used to offset the default position of the\r
212      * anchor arrow (defaults to 0).  When the anchor position is on the top or bottom of the tooltip,\r
213      * anchorOffset will be used as a horizontal offset.  Likewise, when the anchor position is on the\r
214      * left or right side, anchorOffset will be used as a vertical offset.\r
215      */\r
216     anchorOffset: 0,\r
217     /**\r
218      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery} selector which allows selection of individual elements\r
219      * within the {@link #target} element to trigger showing and hiding the ToolTip as the mouse moves within the target.</p>\r
220      * <p>When specified, the child element of the target which caused a show event is placed into the {@link #triggerElement} property\r
221      * before the ToolTip is shown.</p>\r
222      * <p>This may be useful when a Component has regular, repeating elements in it, each of which need a Tooltip which contains\r
223      * information specific to that element. For example:</p><pre><code>\r
224 var myGrid = new Ext.grid.gridPanel(gridConfig);\r
225 myGrid.on('render', function(grid) {\r
226     var store = grid.getStore();  // Capture the Store.\r
227     var view = grid.getView();    // Capture the GridView.\r
228     myGrid.tip = new Ext.ToolTip({\r
229         target: view.mainBody,    // The overall target element.\r
230         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.\r
231         trackMouse: true,         // Moving within the row should not hide the tip.\r
232         renderTo: document.body,  // Render immediately so that tip.body can be referenced prior to the first show.\r
233         listeners: {              // Change content dynamically depending on which element triggered the show.\r
234             beforeshow: function updateTipBody(tip) {\r
235                 var rowIndex = view.findRowIndex(tip.triggerElement);\r
236                 tip.body.dom.innerHTML = "Over Record ID " + store.getAt(rowIndex).id;\r
237             }\r
238         }\r
239     });\r
240 });</code></pre>\r
241      */\r
242 \r
243     // private\r
244     targetCounter: 0,\r
245 \r
246     constrainPosition: false,\r
247 \r
248     // private\r
249     initComponent: function(){\r
250         Ext.ToolTip.superclass.initComponent.call(this);\r
251         this.lastActive = new Date();\r
252         this.initTarget(this.target);\r
253         this.origAnchor = this.anchor;\r
254     },\r
255 \r
256     // private\r
257     onRender : function(ct, position){\r
258         Ext.ToolTip.superclass.onRender.call(this, ct, position);\r
259         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();\r
260         this.anchorEl = this.el.createChild({\r
261             cls: 'x-tip-anchor ' + this.anchorCls\r
262         });\r
263     },\r
264 \r
265     // private\r
266     afterRender : function(){\r
267         Ext.ToolTip.superclass.afterRender.call(this);\r
268         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1);\r
269     },\r
270 \r
271     /**\r
272      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.\r
273      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to\r
274      */\r
275     initTarget : function(target){\r
276         var t;\r
277         if((t = Ext.get(target))){\r
278             if(this.target){\r
279                 this.target = Ext.get(this.target);\r
280                 this.target.un('mouseover', this.onTargetOver, this);\r
281                 this.target.un('mouseout', this.onTargetOut, this);\r
282                 this.target.un('mousemove', this.onMouseMove, this);\r
283             }\r
284             this.mon(t, {\r
285                 mouseover: this.onTargetOver,\r
286                 mouseout: this.onTargetOut,\r
287                 mousemove: this.onMouseMove,\r
288                 scope: this\r
289             });\r
290             this.target = t;\r
291         }\r
292         if(this.anchor){\r
293             this.anchorTarget = this.target;\r
294         }\r
295     },\r
296 \r
297     // private\r
298     onMouseMove : function(e){\r
299         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;\r
300         if (t) {\r
301             this.targetXY = e.getXY();\r
302             if (t === this.triggerElement) {\r
303                 if(!this.hidden && this.trackMouse){\r
304                     this.setPagePosition(this.getTargetXY());\r
305                 }\r
306             } else {\r
307                 this.hide();\r
308                 this.lastActive = new Date(0);\r
309                 this.onTargetOver(e);\r
310             }\r
311         } else if (!this.closable && this.isVisible()) {\r
312             this.hide();\r
313         }\r
314     },\r
315 \r
316     // private\r
317     getTargetXY : function(){\r
318         if(this.anchor){\r
319             this.targetCounter++;\r
320             var offsets = this.getOffsets();\r
321             var xy = (this.anchorToTarget && !this.trackMouse) ?\r
322                 this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) :\r
323                 this.targetXY;\r
324 \r
325             var dw = Ext.lib.Dom.getViewWidth()-5;\r
326             var dh = Ext.lib.Dom.getViewHeight()-5;\r
327             var scrollX = (document.documentElement.scrollLeft || document.body.scrollLeft || 0)+5;\r
328             var scrollY = (document.documentElement.scrollTop || document.body.scrollTop || 0)+5;\r
329 \r
330             var axy = [xy[0] + offsets[0], xy[1] + offsets[1]];\r
331             var sz = this.getSize();\r
332             this.anchorEl.removeClass(this.anchorCls);\r
333 \r
334             if(this.targetCounter < 2){\r
335                 if(axy[0] < scrollX){\r
336                     if(this.anchorToTarget){\r
337                         this.defaultAlign = 'l-r';\r
338                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
339                     }\r
340                     this.anchor = 'left';\r
341                     return this.getTargetXY();\r
342                 }\r
343                 if(axy[0]+sz.width > dw){\r
344                     if(this.anchorToTarget){\r
345                         this.defaultAlign = 'r-l';\r
346                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
347                     }\r
348                     this.anchor = 'right';\r
349                     return this.getTargetXY();\r
350                 }\r
351                 if(axy[1] < scrollY){\r
352                     if(this.anchorToTarget){\r
353                         this.defaultAlign = 't-b';\r
354                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
355                     }\r
356                     this.anchor = 'top';\r
357                     return this.getTargetXY();\r
358                 }\r
359                 if(axy[1]+sz.height > dh){\r
360                     if(this.anchorToTarget){\r
361                         this.defaultAlign = 'b-t';\r
362                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
363                     }\r
364                     this.anchor = 'bottom';\r
365                     return this.getTargetXY();\r
366                 }\r
367             }\r
368 \r
369             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();\r
370             this.anchorEl.addClass(this.anchorCls);\r
371             this.targetCounter = 0;\r
372             return axy;\r
373         }else{\r
374             var mouseOffset = this.getMouseOffset();\r
375             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];\r
376         }\r
377     },\r
378 \r
379     getMouseOffset : function(){\r
380         var offset = this.anchor ? [0,0] : [15,18];\r
381         if(this.mouseOffset){\r
382             offset[0] += this.mouseOffset[0];\r
383             offset[1] += this.mouseOffset[1];\r
384         }\r
385         return offset;\r
386     },\r
387 \r
388     // private\r
389     getAnchorPosition : function(){\r
390         if(this.anchor){\r
391             this.tipAnchor = this.anchor.charAt(0);\r
392         }else{\r
393             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
394             if(!m){\r
395                throw "AnchorTip.defaultAlign is invalid";\r
396             }\r
397             this.tipAnchor = m[1].charAt(0);\r
398         }\r
399 \r
400         switch(this.tipAnchor){\r
401             case 't': return 'top';\r
402             case 'b': return 'bottom';\r
403             case 'r': return 'right';\r
404         }\r
405         return 'left';\r
406     },\r
407 \r
408     // private\r
409     getAnchorAlign : function(){\r
410         switch(this.anchor){\r
411             case 'top'  : return 'tl-bl';\r
412             case 'left' : return 'tl-tr';\r
413             case 'right': return 'tr-tl';\r
414             default     : return 'bl-tl';\r
415         }\r
416     },\r
417 \r
418     // private\r
419     getOffsets: function(){\r
420         var offsets, ap = this.getAnchorPosition().charAt(0);\r
421         if(this.anchorToTarget && !this.trackMouse){\r
422             switch(ap){\r
423                 case 't':\r
424                     offsets = [0, 9];\r
425                     break;\r
426                 case 'b':\r
427                     offsets = [0, -13];\r
428                     break;\r
429                 case 'r':\r
430                     offsets = [-13, 0];\r
431                     break;\r
432                 default:\r
433                     offsets = [9, 0];\r
434                     break;\r
435             }\r
436         }else{\r
437             switch(ap){\r
438                 case 't':\r
439                     offsets = [-15-this.anchorOffset, 30];\r
440                     break;\r
441                 case 'b':\r
442                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];\r
443                     break;\r
444                 case 'r':\r
445                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];\r
446                     break;\r
447                 default:\r
448                     offsets = [25, -13-this.anchorOffset];\r
449                     break;\r
450             }\r
451         }\r
452         var mouseOffset = this.getMouseOffset();\r
453         offsets[0] += mouseOffset[0];\r
454         offsets[1] += mouseOffset[1];\r
455 \r
456         return offsets;\r
457     },\r
458 \r
459     // private\r
460     onTargetOver : function(e){\r
461         if(this.disabled || e.within(this.target.dom, true)){\r
462             return;\r
463         }\r
464         var t = e.getTarget(this.delegate);\r
465         if (t) {\r
466             this.triggerElement = t;\r
467             this.clearTimer('hide');\r
468             this.targetXY = e.getXY();\r
469             this.delayShow();\r
470         }\r
471     },\r
472 \r
473     // private\r
474     delayShow : function(){\r
475         if(this.hidden && !this.showTimer){\r
476             if(this.lastActive.getElapsed() < this.quickShowInterval){\r
477                 this.show();\r
478             }else{\r
479                 this.showTimer = this.show.defer(this.showDelay, this);\r
480             }\r
481         }else if(!this.hidden && this.autoHide !== false){\r
482             this.show();\r
483         }\r
484     },\r
485 \r
486     // private\r
487     onTargetOut : function(e){\r
488         if(this.disabled || e.within(this.target.dom, true)){\r
489             return;\r
490         }\r
491         this.clearTimer('show');\r
492         if(this.autoHide !== false){\r
493             this.delayHide();\r
494         }\r
495     },\r
496 \r
497     // private\r
498     delayHide : function(){\r
499         if(!this.hidden && !this.hideTimer){\r
500             this.hideTimer = this.hide.defer(this.hideDelay, this);\r
501         }\r
502     },\r
503 \r
504     /**\r
505      * Hides this tooltip if visible.\r
506      */\r
507     hide: function(){\r
508         this.clearTimer('dismiss');\r
509         this.lastActive = new Date();\r
510         if(this.anchorEl){\r
511             this.anchorEl.hide();\r
512         }\r
513         Ext.ToolTip.superclass.hide.call(this);\r
514         delete this.triggerElement;\r
515     },\r
516 \r
517     /**\r
518      * Shows this tooltip at the current event target XY position.\r
519      */\r
520     show : function(){\r
521         if(this.anchor){\r
522             // pre-show it off screen so that the el will have dimensions\r
523             // for positioning calcs when getting xy next\r
524             this.showAt([-1000,-1000]);\r
525             this.origConstrainPosition = this.constrainPosition;\r
526             this.constrainPosition = false;\r
527             this.anchor = this.origAnchor;\r
528         }\r
529         this.showAt(this.getTargetXY());\r
530 \r
531         if(this.anchor){\r
532             this.syncAnchor();\r
533             this.anchorEl.show();\r
534             this.constrainPosition = this.origConstrainPosition;\r
535         }else{\r
536             this.anchorEl.hide();\r
537         }\r
538     },\r
539 \r
540     // inherit docs\r
541     showAt : function(xy){\r
542         this.lastActive = new Date();\r
543         this.clearTimers();\r
544         Ext.ToolTip.superclass.showAt.call(this, xy);\r
545         if(this.dismissDelay && this.autoHide !== false){\r
546             this.dismissTimer = this.hide.defer(this.dismissDelay, this);\r
547         }\r
548     },\r
549 \r
550     // private\r
551     syncAnchor : function(){\r
552         var anchorPos, targetPos, offset;\r
553         switch(this.tipAnchor.charAt(0)){\r
554             case 't':\r
555                 anchorPos = 'b';\r
556                 targetPos = 'tl';\r
557                 offset = [20+this.anchorOffset, 2];\r
558                 break;\r
559             case 'r':\r
560                 anchorPos = 'l';\r
561                 targetPos = 'tr';\r
562                 offset = [-2, 11+this.anchorOffset];\r
563                 break;\r
564             case 'b':\r
565                 anchorPos = 't';\r
566                 targetPos = 'bl';\r
567                 offset = [20+this.anchorOffset, -2];\r
568                 break;\r
569             default:\r
570                 anchorPos = 'r';\r
571                 targetPos = 'tl';\r
572                 offset = [2, 11+this.anchorOffset];\r
573                 break;\r
574         }\r
575         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);\r
576     },\r
577 \r
578     // private\r
579     setPagePosition : function(x, y){\r
580         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);\r
581         if(this.anchor){\r
582             this.syncAnchor();\r
583         }\r
584     },\r
585 \r
586     // private\r
587     clearTimer : function(name){\r
588         name = name + 'Timer';\r
589         clearTimeout(this[name]);\r
590         delete this[name];\r
591     },\r
592 \r
593     // private\r
594     clearTimers : function(){\r
595         this.clearTimer('show');\r
596         this.clearTimer('dismiss');\r
597         this.clearTimer('hide');\r
598     },\r
599 \r
600     // private\r
601     onShow : function(){\r
602         Ext.ToolTip.superclass.onShow.call(this);\r
603         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);\r
604     },\r
605 \r
606     // private\r
607     onHide : function(){\r
608         Ext.ToolTip.superclass.onHide.call(this);\r
609         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
610     },\r
611 \r
612     // private\r
613     onDocMouseDown : function(e){\r
614         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){\r
615             this.disable();\r
616             this.enable.defer(100, this);\r
617         }\r
618     },\r
619 \r
620     // private\r
621     onDisable : function(){\r
622         this.clearTimers();\r
623         this.hide();\r
624     },\r
625 \r
626     // private\r
627     adjustPosition : function(x, y){\r
628         if(this.contstrainPosition){\r
629             var ay = this.targetXY[1], h = this.getSize().height;\r
630             if(y <= ay && (y+h) >= ay){\r
631                 y = ay-h-5;\r
632             }\r
633         }\r
634         return {x : x, y: y};\r
635     },\r
636 \r
637     // private\r
638     onDestroy : function(){\r
639         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
640         Ext.ToolTip.superclass.onDestroy.call(this);\r
641     }\r
642 });/**\r
643  * @class Ext.QuickTip\r
644  * @extends Ext.ToolTip\r
645  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global\r
646  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.\r
647  * @constructor\r
648  * Create a new Tip\r
649  * @param {Object} config The configuration options\r
650  */\r
651 Ext.QuickTip = Ext.extend(Ext.ToolTip, {\r
652     /**\r
653      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).\r
654      */\r
655     /**\r
656      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).\r
657      */\r
658     interceptTitles : false,\r
659 \r
660     // private\r
661     tagConfig : {\r
662         namespace : "ext",\r
663         attribute : "qtip",\r
664         width : "qwidth",\r
665         target : "target",\r
666         title : "qtitle",\r
667         hide : "hide",\r
668         cls : "qclass",\r
669         align : "qalign",\r
670         anchor : "anchor"\r
671     },\r
672 \r
673     // private\r
674     initComponent : function(){\r
675         this.target = this.target || Ext.getDoc();\r
676         this.targets = this.targets || {};\r
677         Ext.QuickTip.superclass.initComponent.call(this);\r
678     },\r
679 \r
680     /**\r
681      * Configures a new quick tip instance and assigns it to a target element.  The following config values are\r
682      * supported (for example usage, see the {@link Ext.QuickTips} class header):\r
683      * <div class="mdetail-params"><ul>\r
684      * <li>autoHide</li>\r
685      * <li>cls</li>\r
686      * <li>dismissDelay (overrides the singleton value)</li>\r
687      * <li>target (required)</li>\r
688      * <li>text (required)</li>\r
689      * <li>title</li>\r
690      * <li>width</li></ul></div>\r
691      * @param {Object} config The config object\r
692      */\r
693     register : function(config){\r
694         var cs = Ext.isArray(config) ? config : arguments;\r
695         for(var i = 0, len = cs.length; i < len; i++){\r
696             var c = cs[i];\r
697             var target = c.target;\r
698             if(target){\r
699                 if(Ext.isArray(target)){\r
700                     for(var j = 0, jlen = target.length; j < jlen; j++){\r
701                         this.targets[Ext.id(target[j])] = c;\r
702                     }\r
703                 } else{\r
704                     this.targets[Ext.id(target)] = c;\r
705                 }\r
706             }\r
707         }\r
708     },\r
709 \r
710     /**\r
711      * Removes this quick tip from its element and destroys it.\r
712      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
713      */\r
714     unregister : function(el){\r
715         delete this.targets[Ext.id(el)];\r
716     },\r
717     \r
718     /**\r
719      * Hides a visible tip or cancels an impending show for a particular element.\r
720      * @param {String/HTMLElement/Element} el The element that is the target of the tip.\r
721      */\r
722     cancelShow: function(el){\r
723         var at = this.activeTarget;\r
724         el = Ext.get(el).dom;\r
725         if(this.isVisible()){\r
726             if(at && at.el == el){\r
727                 this.hide();\r
728             }\r
729         }else if(at && at.el == el){\r
730             this.clearTimer('show');\r
731         }\r
732     },\r
733 \r
734     // private\r
735     onTargetOver : function(e){\r
736         if(this.disabled){\r
737             return;\r
738         }\r
739         this.targetXY = e.getXY();\r
740         var t = e.getTarget();\r
741         if(!t || t.nodeType !== 1 || t == document || t == document.body){\r
742             return;\r
743         }\r
744         if(this.activeTarget && t == this.activeTarget.el){\r
745             this.clearTimer('hide');\r
746             this.show();\r
747             return;\r
748         }\r
749         if(t && this.targets[t.id]){\r
750             this.activeTarget = this.targets[t.id];\r
751             this.activeTarget.el = t;\r
752             this.anchor = this.activeTarget.anchor;\r
753             if(this.anchor){\r
754                 this.anchorTarget = t;\r
755             }\r
756             this.delayShow();\r
757             return;\r
758         }\r
759         \r
760         var ttp, et = Ext.fly(t), cfg = this.tagConfig;\r
761         var ns = cfg.namespace;\r
762         if(this.interceptTitles && t.title){\r
763             ttp = t.title;\r
764             t.qtip = ttp;\r
765             t.removeAttribute("title");\r
766             e.preventDefault();\r
767         } else{\r
768             ttp = t.qtip || et.getAttribute(cfg.attribute, ns);\r
769         }\r
770         if(ttp){\r
771             var autoHide = et.getAttribute(cfg.hide, ns);\r
772             this.activeTarget = {\r
773                 el: t,\r
774                 text: ttp,\r
775                 width: et.getAttribute(cfg.width, ns),\r
776                 autoHide: autoHide != "user" && autoHide !== 'false',\r
777                 title: et.getAttribute(cfg.title, ns),\r
778                 cls: et.getAttribute(cfg.cls, ns),\r
779                 align: et.getAttribute(cfg.align, ns)\r
780                 \r
781             };\r
782             this.anchor = et.getAttribute(cfg.anchor, ns);\r
783             if(this.anchor){\r
784                 this.anchorTarget = t;\r
785             }\r
786             this.delayShow();\r
787         }\r
788     },\r
789 \r
790     // private\r
791     onTargetOut : function(e){\r
792         this.clearTimer('show');\r
793         if(this.autoHide !== false){\r
794             this.delayHide();\r
795         }\r
796     },\r
797 \r
798     // inherit docs\r
799     showAt : function(xy){\r
800         var t = this.activeTarget;\r
801         if(t){\r
802             if(!this.rendered){\r
803                 this.render(Ext.getBody());\r
804                 this.activeTarget = t;\r
805             }\r
806             if(t.width){\r
807                 this.setWidth(t.width);\r
808                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));\r
809                 this.measureWidth = false;\r
810             } else{\r
811                 this.measureWidth = true;\r
812             }\r
813             this.setTitle(t.title || '');\r
814             this.body.update(t.text);\r
815             this.autoHide = t.autoHide;\r
816             this.dismissDelay = t.dismissDelay || this.dismissDelay;\r
817             if(this.lastCls){\r
818                 this.el.removeClass(this.lastCls);\r
819                 delete this.lastCls;\r
820             }\r
821             if(t.cls){\r
822                 this.el.addClass(t.cls);\r
823                 this.lastCls = t.cls;\r
824             }\r
825             if(this.anchor){\r
826                 this.constrainPosition = false;\r
827             }else if(t.align){ // TODO: this doesn't seem to work consistently\r
828                 xy = this.el.getAlignToXY(t.el, t.align);\r
829                 this.constrainPosition = false;\r
830             }else{\r
831                 this.constrainPosition = true;\r
832             }\r
833         }\r
834         Ext.QuickTip.superclass.showAt.call(this, xy);\r
835     },\r
836 \r
837     // inherit docs\r
838     hide: function(){\r
839         delete this.activeTarget;\r
840         Ext.QuickTip.superclass.hide.call(this);\r
841     }\r
842 });/**\r
843  * @class Ext.QuickTips\r
844  * <p>Provides attractive and customizable tooltips for any element. The QuickTips\r
845  * singleton is used to configure and manage tooltips globally for multiple elements\r
846  * in a generic manner.  To create individual tooltips with maximum customizability,\r
847  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>\r
848  * <p>Quicktips can be configured via tag attributes directly in markup, or by\r
849  * registering quick tips programmatically via the {@link #register} method.</p>\r
850  * <p>The singleton's instance of {@link Ext.QuickTip} is available via\r
851  * {@link #getQuickTip}, and supports all the methods, and all the all the\r
852  * configuration properties of Ext.QuickTip. These settings will apply to all\r
853  * tooltips shown by the singleton.</p>\r
854  * <p>Below is the summary of the configuration properties which can be used.\r
855  * For detailed descriptions see {@link #getQuickTip}</p>\r
856  * <p><b>QuickTips singleton configs (all are optional)</b></p>\r
857  * <div class="mdetail-params"><ul><li>dismissDelay</li>\r
858  * <li>hideDelay</li>\r
859  * <li>maxWidth</li>\r
860  * <li>minWidth</li>\r
861  * <li>showDelay</li>\r
862  * <li>trackMouse</li></ul></div>\r
863  * <p><b>Target element configs (optional unless otherwise noted)</b></p>\r
864  * <div class="mdetail-params"><ul><li>autoHide</li>\r
865  * <li>cls</li>\r
866  * <li>dismissDelay (overrides singleton value)</li>\r
867  * <li>target (required)</li>\r
868  * <li>text (required)</li>\r
869  * <li>title</li>\r
870  * <li>width</li></ul></div>\r
871  * <p>Here is an example showing how some of these config options could be used:</p>\r
872  * <pre><code>\r
873 // Init the singleton.  Any tag-based quick tips will start working.\r
874 Ext.QuickTips.init();\r
875 \r
876 // Apply a set of config properties to the singleton\r
877 Ext.apply(Ext.QuickTips.getQuickTip(), {\r
878     maxWidth: 200,\r
879     minWidth: 100,\r
880     showDelay: 50,\r
881     trackMouse: true\r
882 });\r
883 \r
884 // Manually register a quick tip for a specific element\r
885 Ext.QuickTips.register({\r
886     target: 'my-div',\r
887     title: 'My Tooltip',\r
888     text: 'This tooltip was added in code',\r
889     width: 100,\r
890     dismissDelay: 20\r
891 });\r
892 </code></pre>\r
893  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with\r
894  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary\r
895  * of supported attributes (optional unless otherwise noted):</p>\r
896  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the\r
897  * same as autoHide = true.</li>\r
898  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>\r
899  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>\r
900  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>\r
901  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>\r
902  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>\r
903  * <pre><code>\r
904 // Add a quick tip to an HTML button\r
905 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"\r
906      ext:qtip="This is a quick tip from markup!">&lt;/input>\r
907 </code></pre>\r
908  * @singleton\r
909  */\r
910 Ext.QuickTips = function(){\r
911     var tip, locks = [];\r
912     return {\r
913         /**\r
914          * Initialize the global QuickTips instance and prepare any quick tips.\r
915          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) \r
916          */\r
917         init : function(autoRender){\r
918             if(!tip){\r
919                 if(!Ext.isReady){\r
920                     Ext.onReady(function(){\r
921                         Ext.QuickTips.init(autoRender);\r
922                     });\r
923                     return;\r
924                 }\r
925                 tip = new Ext.QuickTip({elements:'header,body'});\r
926                 if(autoRender !== false){\r
927                     tip.render(Ext.getBody());\r
928                 }\r
929             }\r
930         },\r
931 \r
932         /**\r
933          * Enable quick tips globally.\r
934          */\r
935         enable : function(){\r
936             if(tip){\r
937                 locks.pop();\r
938                 if(locks.length < 1){\r
939                     tip.enable();\r
940                 }\r
941             }\r
942         },\r
943 \r
944         /**\r
945          * Disable quick tips globally.\r
946          */\r
947         disable : function(){\r
948             if(tip){\r
949                 tip.disable();\r
950             }\r
951             locks.push(1);\r
952         },\r
953 \r
954         /**\r
955          * Returns true if quick tips are enabled, else false.\r
956          * @return {Boolean}\r
957          */\r
958         isEnabled : function(){\r
959             return tip !== undefined && !tip.disabled;\r
960         },\r
961 \r
962         /**\r
963          * Gets the global QuickTips instance.\r
964          */\r
965         getQuickTip : function(){\r
966             return tip;\r
967         },\r
968 \r
969         /**\r
970          * Configures a new quick tip instance and assigns it to a target element.  See\r
971          * {@link Ext.QuickTip#register} for details.\r
972          * @param {Object} config The config object\r
973          */\r
974         register : function(){\r
975             tip.register.apply(tip, arguments);\r
976         },\r
977 \r
978         /**\r
979          * Removes any registered quick tip from the target element and destroys it.\r
980          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
981          */\r
982         unregister : function(){\r
983             tip.unregister.apply(tip, arguments);\r
984         },\r
985 \r
986         /**\r
987          * Alias of {@link #register}.\r
988          * @param {Object} config The config object\r
989          */\r
990         tips :function(){\r
991             tip.register.apply(tip, arguments);\r
992         }\r
993     }\r
994 }();