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