Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / pkgs / pkg-tips-debug.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.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);
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.syncAnchor();
559             this.anchorEl.show();
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         }
578     },
579
580     // private
581     syncAnchor : function(){
582         var anchorPos, targetPos, offset;
583         switch(this.tipAnchor.charAt(0)){
584             case 't':
585                 anchorPos = 'b';
586                 targetPos = 'tl';
587                 offset = [20+this.anchorOffset, 2];
588                 break;
589             case 'r':
590                 anchorPos = 'l';
591                 targetPos = 'tr';
592                 offset = [-2, 11+this.anchorOffset];
593                 break;
594             case 'b':
595                 anchorPos = 't';
596                 targetPos = 'bl';
597                 offset = [20+this.anchorOffset, -2];
598                 break;
599             default:
600                 anchorPos = 'r';
601                 targetPos = 'tl';
602                 offset = [2, 11+this.anchorOffset];
603                 break;
604         }
605         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);
606     },
607
608     // private
609     setPagePosition : function(x, y){
610         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);
611         if(this.anchor){
612             this.syncAnchor();
613         }
614     },
615
616     // private
617     clearTimer : function(name){
618         name = name + 'Timer';
619         clearTimeout(this[name]);
620         delete this[name];
621     },
622
623     // private
624     clearTimers : function(){
625         this.clearTimer('show');
626         this.clearTimer('dismiss');
627         this.clearTimer('hide');
628     },
629
630     // private
631     onShow : function(){
632         Ext.ToolTip.superclass.onShow.call(this);
633         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);
634     },
635
636     // private
637     onHide : function(){
638         Ext.ToolTip.superclass.onHide.call(this);
639         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
640     },
641
642     // private
643     onDocMouseDown : function(e){
644         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){
645             this.disable();
646             this.doEnable.defer(100, this);
647         }
648     },
649     
650     // private
651     doEnable : function(){
652         if(!this.isDestroyed){
653             this.enable();
654         }
655     },
656
657     // private
658     onDisable : function(){
659         this.clearTimers();
660         this.hide();
661     },
662
663     // private
664     adjustPosition : function(x, y){
665         if(this.contstrainPosition){
666             var ay = this.targetXY[1], h = this.getSize().height;
667             if(y <= ay && (y+h) >= ay){
668                 y = ay-h-5;
669             }
670         }
671         return {x : x, y: y};
672     },
673     
674     beforeDestroy : function(){
675         this.clearTimers();
676         Ext.destroy(this.anchorEl);
677         delete this.anchorEl;
678         delete this.target;
679         delete this.anchorTarget;
680         delete this.triggerElement;
681         Ext.ToolTip.superclass.beforeDestroy.call(this);    
682     },
683
684     // private
685     onDestroy : function(){
686         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
687         Ext.ToolTip.superclass.onDestroy.call(this);
688     }
689 });
690
691 Ext.reg('tooltip', Ext.ToolTip);/**
692  * @class Ext.QuickTip
693  * @extends Ext.ToolTip
694  * @xtype quicktip
695  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global
696  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.
697  * @constructor
698  * Create a new Tip
699  * @param {Object} config The configuration options
700  */
701 Ext.QuickTip = Ext.extend(Ext.ToolTip, {
702     /**
703      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).
704      */
705     /**
706      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).
707      */
708     interceptTitles : false,
709
710     // private
711     tagConfig : {
712         namespace : "ext",
713         attribute : "qtip",
714         width : "qwidth",
715         target : "target",
716         title : "qtitle",
717         hide : "hide",
718         cls : "qclass",
719         align : "qalign",
720         anchor : "anchor"
721     },
722
723     // private
724     initComponent : function(){
725         this.target = this.target || Ext.getDoc();
726         this.targets = this.targets || {};
727         Ext.QuickTip.superclass.initComponent.call(this);
728     },
729
730     /**
731      * Configures a new quick tip instance and assigns it to a target element.  The following config values are
732      * supported (for example usage, see the {@link Ext.QuickTips} class header):
733      * <div class="mdetail-params"><ul>
734      * <li>autoHide</li>
735      * <li>cls</li>
736      * <li>dismissDelay (overrides the singleton value)</li>
737      * <li>target (required)</li>
738      * <li>text (required)</li>
739      * <li>title</li>
740      * <li>width</li></ul></div>
741      * @param {Object} config The config object
742      */
743     register : function(config){
744         var cs = Ext.isArray(config) ? config : arguments;
745         for(var i = 0, len = cs.length; i < len; i++){
746             var c = cs[i];
747             var target = c.target;
748             if(target){
749                 if(Ext.isArray(target)){
750                     for(var j = 0, jlen = target.length; j < jlen; j++){
751                         this.targets[Ext.id(target[j])] = c;
752                     }
753                 } else{
754                     this.targets[Ext.id(target)] = c;
755                 }
756             }
757         }
758     },
759
760     /**
761      * Removes this quick tip from its element and destroys it.
762      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
763      */
764     unregister : function(el){
765         delete this.targets[Ext.id(el)];
766     },
767     
768     /**
769      * Hides a visible tip or cancels an impending show for a particular element.
770      * @param {String/HTMLElement/Element} el The element that is the target of the tip.
771      */
772     cancelShow: function(el){
773         var at = this.activeTarget;
774         el = Ext.get(el).dom;
775         if(this.isVisible()){
776             if(at && at.el == el){
777                 this.hide();
778             }
779         }else if(at && at.el == el){
780             this.clearTimer('show');
781         }
782     },
783     
784     getTipCfg: function(e) {
785         var t = e.getTarget(), 
786             ttp, 
787             cfg;
788         if(this.interceptTitles && t.title && Ext.isString(t.title)){
789             ttp = t.title;
790             t.qtip = ttp;
791             t.removeAttribute("title");
792             e.preventDefault();
793         }else{
794             cfg = this.tagConfig;
795             ttp = t.qtip || Ext.fly(t).getAttribute(cfg.attribute, cfg.namespace);
796         }
797         return ttp;
798     },
799
800     // private
801     onTargetOver : function(e){
802         if(this.disabled){
803             return;
804         }
805         this.targetXY = e.getXY();
806         var t = e.getTarget();
807         if(!t || t.nodeType !== 1 || t == document || t == document.body){
808             return;
809         }
810         if(this.activeTarget && ((t == this.activeTarget.el) || Ext.fly(this.activeTarget.el).contains(t))){
811             this.clearTimer('hide');
812             this.show();
813             return;
814         }
815         if(t && this.targets[t.id]){
816             this.activeTarget = this.targets[t.id];
817             this.activeTarget.el = t;
818             this.anchor = this.activeTarget.anchor;
819             if(this.anchor){
820                 this.anchorTarget = t;
821             }
822             this.delayShow();
823             return;
824         }
825         var ttp, et = Ext.fly(t), cfg = this.tagConfig, ns = cfg.namespace;
826         if(ttp = this.getTipCfg(e)){
827             var autoHide = et.getAttribute(cfg.hide, ns);
828             this.activeTarget = {
829                 el: t,
830                 text: ttp,
831                 width: et.getAttribute(cfg.width, ns),
832                 autoHide: autoHide != "user" && autoHide !== 'false',
833                 title: et.getAttribute(cfg.title, ns),
834                 cls: et.getAttribute(cfg.cls, ns),
835                 align: et.getAttribute(cfg.align, ns)
836                 
837             };
838             this.anchor = et.getAttribute(cfg.anchor, ns);
839             if(this.anchor){
840                 this.anchorTarget = t;
841             }
842             this.delayShow();
843         }
844     },
845
846     // private
847     onTargetOut : function(e){
848
849         // If moving within the current target, and it does not have a new tip, ignore the mouseout
850         if (this.activeTarget && e.within(this.activeTarget.el) && !this.getTipCfg(e)) {
851             return;
852         }
853
854         this.clearTimer('show');
855         if(this.autoHide !== false){
856             this.delayHide();
857         }
858     },
859
860     // inherit docs
861     showAt : function(xy){
862         var t = this.activeTarget;
863         if(t){
864             if(!this.rendered){
865                 this.render(Ext.getBody());
866                 this.activeTarget = t;
867             }
868             if(t.width){
869                 this.setWidth(t.width);
870                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));
871                 this.measureWidth = false;
872             } else{
873                 this.measureWidth = true;
874             }
875             this.setTitle(t.title || '');
876             this.body.update(t.text);
877             this.autoHide = t.autoHide;
878             this.dismissDelay = t.dismissDelay || this.dismissDelay;
879             if(this.lastCls){
880                 this.el.removeClass(this.lastCls);
881                 delete this.lastCls;
882             }
883             if(t.cls){
884                 this.el.addClass(t.cls);
885                 this.lastCls = t.cls;
886             }
887             if(this.anchor){
888                 this.constrainPosition = false;
889             }else if(t.align){ // TODO: this doesn't seem to work consistently
890                 xy = this.el.getAlignToXY(t.el, t.align);
891                 this.constrainPosition = false;
892             }else{
893                 this.constrainPosition = true;
894             }
895         }
896         Ext.QuickTip.superclass.showAt.call(this, xy);
897     },
898
899     // inherit docs
900     hide: function(){
901         delete this.activeTarget;
902         Ext.QuickTip.superclass.hide.call(this);
903     }
904 });
905 Ext.reg('quicktip', Ext.QuickTip);/**
906  * @class Ext.QuickTips
907  * <p>Provides attractive and customizable tooltips for any element. The QuickTips
908  * singleton is used to configure and manage tooltips globally for multiple elements
909  * in a generic manner.  To create individual tooltips with maximum customizability,
910  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>
911  * <p>Quicktips can be configured via tag attributes directly in markup, or by
912  * registering quick tips programmatically via the {@link #register} method.</p>
913  * <p>The singleton's instance of {@link Ext.QuickTip} is available via
914  * {@link #getQuickTip}, and supports all the methods, and all the all the
915  * configuration properties of Ext.QuickTip. These settings will apply to all
916  * tooltips shown by the singleton.</p>
917  * <p>Below is the summary of the configuration properties which can be used.
918  * For detailed descriptions see {@link #getQuickTip}</p>
919  * <p><b>QuickTips singleton configs (all are optional)</b></p>
920  * <div class="mdetail-params"><ul><li>dismissDelay</li>
921  * <li>hideDelay</li>
922  * <li>maxWidth</li>
923  * <li>minWidth</li>
924  * <li>showDelay</li>
925  * <li>trackMouse</li></ul></div>
926  * <p><b>Target element configs (optional unless otherwise noted)</b></p>
927  * <div class="mdetail-params"><ul><li>autoHide</li>
928  * <li>cls</li>
929  * <li>dismissDelay (overrides singleton value)</li>
930  * <li>target (required)</li>
931  * <li>text (required)</li>
932  * <li>title</li>
933  * <li>width</li></ul></div>
934  * <p>Here is an example showing how some of these config options could be used:</p>
935  * <pre><code>
936 // Init the singleton.  Any tag-based quick tips will start working.
937 Ext.QuickTips.init();
938
939 // Apply a set of config properties to the singleton
940 Ext.apply(Ext.QuickTips.getQuickTip(), {
941     maxWidth: 200,
942     minWidth: 100,
943     showDelay: 50,
944     trackMouse: true
945 });
946
947 // Manually register a quick tip for a specific element
948 Ext.QuickTips.register({
949     target: 'my-div',
950     title: 'My Tooltip',
951     text: 'This tooltip was added in code',
952     width: 100,
953     dismissDelay: 20
954 });
955 </code></pre>
956  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with
957  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary
958  * of supported attributes (optional unless otherwise noted):</p>
959  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the
960  * same as autoHide = true.</li>
961  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>
962  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>
963  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>
964  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>
965  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>
966  * <pre><code>
967 // Add a quick tip to an HTML button
968 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"
969      ext:qtip="This is a quick tip from markup!">&lt;/input>
970 </code></pre>
971  * @singleton
972  */
973 Ext.QuickTips = function(){
974     var tip, locks = [];
975     return {
976         /**
977          * Initialize the global QuickTips instance and prepare any quick tips.
978          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) 
979          */
980         init : function(autoRender){
981             if(!tip){
982                 if(!Ext.isReady){
983                     Ext.onReady(function(){
984                         Ext.QuickTips.init(autoRender);
985                     });
986                     return;
987                 }
988                 tip = new Ext.QuickTip({elements:'header,body'});
989                 if(autoRender !== false){
990                     tip.render(Ext.getBody());
991                 }
992             }
993         },
994
995         /**
996          * Enable quick tips globally.
997          */
998         enable : function(){
999             if(tip){
1000                 locks.pop();
1001                 if(locks.length < 1){
1002                     tip.enable();
1003                 }
1004             }
1005         },
1006
1007         /**
1008          * Disable quick tips globally.
1009          */
1010         disable : function(){
1011             if(tip){
1012                 tip.disable();
1013             }
1014             locks.push(1);
1015         },
1016
1017         /**
1018          * Returns true if quick tips are enabled, else false.
1019          * @return {Boolean}
1020          */
1021         isEnabled : function(){
1022             return tip !== undefined && !tip.disabled;
1023         },
1024
1025         /**
1026          * Gets the global QuickTips instance.
1027          */
1028         getQuickTip : function(){
1029             return tip;
1030         },
1031
1032         /**
1033          * Configures a new quick tip instance and assigns it to a target element.  See
1034          * {@link Ext.QuickTip#register} for details.
1035          * @param {Object} config The config object
1036          */
1037         register : function(){
1038             tip.register.apply(tip, arguments);
1039         },
1040
1041         /**
1042          * Removes any registered quick tip from the target element and destroys it.
1043          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
1044          */
1045         unregister : function(){
1046             tip.unregister.apply(tip, arguments);
1047         },
1048
1049         /**
1050          * Alias of {@link #register}.
1051          * @param {Object} config The config object
1052          */
1053         tips :function(){
1054             tip.register.apply(tip, arguments);
1055         }
1056     }
1057 }();/**
1058  * @class Ext.slider.Tip
1059  * @extends Ext.Tip
1060  * Simple plugin for using an Ext.Tip with a slider to show the slider value. Example usage:
1061 <pre>
1062 new Ext.Slider({
1063     width: 214,
1064     minValue: 0,
1065     maxValue: 100,
1066     plugins: new Ext.slider.Tip()
1067 });
1068 </pre>
1069  * Optionally provide your own tip text by overriding getText:
1070  <pre>
1071  new Ext.Slider({
1072      width: 214,
1073      minValue: 0,
1074      maxValue: 100,
1075      plugins: new Ext.slider.Tip({
1076          getText: function(thumb){
1077              return String.format('<b>{0}% complete</b>', thumb.value);
1078          }
1079      })
1080  });
1081  </pre>
1082  */
1083 Ext.slider.Tip = Ext.extend(Ext.Tip, {
1084     minWidth: 10,
1085     offsets : [0, -10],
1086     
1087     init: function(slider) {
1088         slider.on({
1089             scope    : this,
1090             dragstart: this.onSlide,
1091             drag     : this.onSlide,
1092             dragend  : this.hide,
1093             destroy  : this.destroy
1094         });
1095     },
1096     
1097     /**
1098      * @private
1099      * Called whenever a dragstart or drag event is received on the associated Thumb. 
1100      * Aligns the Tip with the Thumb's new position.
1101      * @param {Ext.slider.MultiSlider} slider The slider
1102      * @param {Ext.EventObject} e The Event object
1103      * @param {Ext.slider.Thumb} thumb The thumb that the Tip is attached to
1104      */
1105     onSlide : function(slider, e, thumb) {
1106         this.show();
1107         this.body.update(this.getText(thumb));
1108         this.doAutoWidth();
1109         this.el.alignTo(thumb.el, 'b-t?', this.offsets);
1110     },
1111
1112     /**
1113      * Used to create the text that appears in the Tip's body. By default this just returns
1114      * the value of the Slider Thumb that the Tip is attached to. Override to customize.
1115      * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to
1116      * @return {String} The text to display in the tip
1117      */
1118     getText : function(thumb) {
1119         return String(thumb.value);
1120     }
1121 });
1122
1123 //backwards compatibility - SliderTip used to be a ux before 3.2
1124 Ext.ux.SliderTip = Ext.slider.Tip;