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