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