]> git.proxmox.com Git - extjs.git/blob - extjs/classic/classic/src/tip/ToolTip.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / tip / ToolTip.js
1 /**
2 * ToolTip is a {@link Ext.tip.Tip} implementation that handles the common case of displaying a
3 * tooltip when hovering over a certain element or elements on the page. It allows fine-grained
4 * control over the tooltip's alignment relative to the target element or mouse, and the timing
5 * of when it is automatically shown and hidden.
6 *
7 * This implementation does **not** have a built-in method of automatically populating the tooltip's
8 * text based on the target element; you must either configure a fixed {@link #html} value for each
9 * ToolTip instance, or implement custom logic (e.g. in a {@link #beforeshow} event listener) to
10 * generate the appropriate tooltip content on the fly. See {@link Ext.tip.QuickTip} for a more
11 * convenient way of automatically populating and configuring a tooltip based on specific DOM
12 * attributes of each target element.
13 *
14 * # Basic Example
15 *
16 * @example
17 * Ext.getBody().appendChild({
18 * id: 'clearButton',
19 * html: 'Clear Button',
20 * style: 'display:inline-block;background:#A2C841;padding:7px;cursor:pointer;'
21 * });
22 *
23 * var tip = Ext.create('Ext.tip.ToolTip', {
24 * target: 'clearButton',
25 * html: 'Press this button to clear the form'
26 * });
27 *
28 * # Delegation
29 *
30 * In addition to attaching a ToolTip to a single element, you can also use delegation to attach
31 * one ToolTip to many elements under a common parent. This is more efficient than creating many
32 * ToolTip instances. To do this, point the {@link #target} config to a common ancestor of all the
33 * elements, and then set the {@link #delegate} config to a CSS selector that will select all the
34 * appropriate sub-elements.
35 *
36 * When using delegation, it is likely that you will want to programmatically change the content
37 * of the ToolTip based on each delegate element; you can do this by implementing a custom
38 * listener for the {@link #beforeshow} event. Example:
39 *
40 * @example
41 * var store = Ext.create('Ext.data.ArrayStore', {
42 * fields: ['company', 'price', 'change'],
43 * data: [
44 * ['3m Co', 71.72, 0.02],
45 * ['Alcoa Inc', 29.01, 0.42],
46 * ['Altria Group Inc', 83.81, 0.28],
47 * ['American Express Company', 52.55, 0.01],
48 * ['American International Group, Inc.', 64.13, 0.31],
49 * ['AT&T Inc.', 31.61, -0.48]
50 * ]
51 * });
52 *
53 * var grid = Ext.create('Ext.grid.Panel', {
54 * title: 'Array Grid',
55 * store: store,
56 * columns: [
57 * {text: 'Company', flex: 1, dataIndex: 'company'},
58 * {text: 'Price', width: 75, dataIndex: 'price'},
59 * {text: 'Change', width: 75, dataIndex: 'change'}
60 * ],
61 * height: 200,
62 * width: 400,
63 * renderTo: Ext.getBody()
64 * });
65 *
66 * var view = grid.getView();
67 * var tip = Ext.create('Ext.tip.ToolTip', {
68 * // The overall target element.
69 * target: view.el,
70 * // Each grid row causes its own separate show and hide.
71 * delegate: view.itemSelector,
72 * // Moving within the row should not hide the tip.
73 * trackMouse: true,
74 * // Render immediately so that tip.body can be referenced prior to the first show.
75 * renderTo: Ext.getBody(),
76 * listeners: {
77 * // Change content dynamically depending on which element triggered the show.
78 * beforeshow: function updateTipBody(tip) {
79 * tip.update('Over company "' + view.getRecord(tip.triggerElement).get('company') + '"');
80 * }
81 * }
82 * });
83 *
84 * # Alignment
85 *
86 * The following configuration properties allow control over how the ToolTip is aligned relative to
87 * the target element and/or mouse pointer:
88 *
89 * - {@link #anchor}
90 * - {@link #anchorToTarget}
91 * - {@link #anchorOffset}
92 * - {@link #trackMouse}
93 * - {@link #mouseOffset}
94 *
95 * # Showing/Hiding
96 *
97 * The following configuration properties allow control over how and when the ToolTip is automatically
98 * shown and hidden:
99 *
100 * - {@link #autoHide}
101 * - {@link #showDelay}
102 * - {@link #hideDelay}
103 * - {@link #dismissDelay}
104 */
105 Ext.define('Ext.tip.ToolTip', {
106 extend: 'Ext.tip.Tip',
107 alias: 'widget.tooltip',
108 alternateClassName: 'Ext.ToolTip',
109
110 /**
111 * @property {HTMLElement} triggerElement
112 * When a ToolTip is configured with the `{@link #delegate}`
113 * option to cause selected child elements of the `{@link #target}`
114 * Element to each trigger a separate show event, this property is set to
115 * the DOM element which triggered the show.
116 */
117 /**
118 * @cfg {HTMLElement/Ext.dom.Element/String} target
119 * The target element or string id to monitor for mouseover events to trigger
120 * showing this ToolTip.
121 */
122 /**
123 * @cfg {Boolean} [autoHide=true]
124 * True to automatically hide the tooltip after the
125 * mouse exits the target element or after the `{@link #dismissDelay}`
126 * has expired if set. If `{@link #closable} = true`
127 * a close tool button will be rendered into the tooltip header.
128 */
129 autoHide: true,
130
131 /**
132 * @cfg {Number} showDelay
133 * Delay in milliseconds before the tooltip displays after the mouse enters the target element.
134 */
135 showDelay: 500,
136 /**
137 * @cfg {Number} hideDelay
138 * Delay in milliseconds after the mouse exits the target element but before the tooltip actually hides.
139 * Set to 0 for the tooltip to hide immediately.
140 */
141 hideDelay: 200,
142 /**
143 * @cfg {Number} dismissDelay
144 * Delay in milliseconds before the tooltip automatically hides. To disable automatic hiding, set
145 * dismissDelay = 0.
146 */
147 dismissDelay: 5000,
148 /**
149 * @cfg {Number[]} [mouseOffset=[15,18]]
150 * An XY offset from the mouse position where the tooltip should be shown.
151 */
152 /**
153 * @cfg {Boolean} trackMouse
154 * True to have the tooltip follow the mouse as it moves over the target element.
155 */
156 trackMouse: false,
157 /**
158 * @cfg {String} anchor
159 * If specified, indicates that the tip should be anchored to a
160 * particular side of the target element or mouse pointer ("top", "right", "bottom",
161 * or "left"), with an arrow pointing back at the target or mouse pointer. If
162 * {@link #constrainPosition} is enabled, this will be used as a preferred value
163 * only and may be flipped as needed.
164 */
165 /**
166 * @cfg {Boolean} anchorToTarget
167 * True to anchor the tooltip to the target element, false to anchor it relative to the mouse coordinates.
168 * When `anchorToTarget` is true, use `{@link #defaultAlign}` to control tooltip alignment to the
169 * target element. When `anchorToTarget` is false, use `{@link #anchor}` instead to control alignment.
170 */
171 anchorToTarget: true,
172 /**
173 * @cfg {Number} anchorOffset
174 * A numeric pixel value used to offset the default position of the anchor arrow. When the anchor
175 * position is on the top or bottom of the tooltip, `anchorOffset` will be used as a horizontal offset.
176 * Likewise, when the anchor position is on the left or right side, `anchorOffset` will be used as
177 * a vertical offset.
178 */
179 anchorOffset: 0,
180
181 /**
182 * @cfg {String} delegate
183 *
184 * A {@link Ext.DomQuery DomQuery} simple selector which allows selection of individual elements within the
185 * `{@link #target}` element to trigger showing and hiding the ToolTip as the mouse moves within the
186 * target. See {@link Ext.dom.Query} for information about simple selectors.
187 *
188 * When specified, the child element of the target which caused a show event is placed into the
189 * `{@link #triggerElement}` property before the ToolTip is shown.
190 *
191 * This may be useful when a Component has regular, repeating elements in it, each of which need a
192 * ToolTip which contains information specific to that element.
193 *
194 * See the delegate example in class documentation of {@link Ext.tip.ToolTip}.
195 */
196
197 /**
198 * @private
199 */
200 targetCounter: 0,
201
202 quickShowInterval: 250,
203
204 /**
205 * @cfg {String} [hideAction="hide"]
206 * The method to use to hide the tooltip. Another useful method for this is `fadeOut`.
207 */
208 hideAction: 'hide',
209
210 /**
211 * @cfg {Number} [fadeOutDuration=1000]
212 * The number of milliseconds for the `fadeOut` animation. Only valid if `hideAction`
213 * is set to `fadeOut`.
214 */
215 fadeOutDuration: 1000,
216
217 ariaRole: 'tooltip',
218
219 initComponent: function() {
220 var me = this;
221 me.callParent(arguments);
222 me.lastActive = new Date();
223 me.setTarget(me.target);
224 me.origAnchor = me.anchor;
225 },
226
227 onRender: function(ct, position) {
228 var me = this;
229 me.callParent(arguments);
230 me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
231 //<debug>
232 if (me.sticky) {
233 // tell the spec runner to ignore this element when checking if the dom is clean
234 me.el.dom.setAttribute('data-sticky', true);
235 }
236 //</debug>
237 me.anchorEl = me.el.createChild({
238 role: 'presentation',
239 cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls
240 });
241 },
242
243 /**
244 * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.
245 * @param {String/HTMLElement/Ext.dom.Element} target The Element, HTMLElement, or
246 * ID of an element to bind to
247 */
248 setTarget: function(target) {
249 var me = this,
250 t = Ext.get(target),
251 tg;
252
253 if (me.target) {
254 tg = Ext.get(me.target);
255 if (Ext.supports.Touch) {
256 me.mun(tg, 'tap', me.onTargetOver, me);
257 } else {
258 me.mun(tg, {
259 mouseover: me.onTargetOver,
260 mouseout: me.onTargetOut,
261 mousemove: me.onMouseMove,
262 scope: me
263 });
264 }
265 }
266
267 me.target = t;
268 if (t) {
269 if (Ext.supports.Touch) {
270 me.mon(t, {
271 tap: me.onTargetOver,
272 scope: me
273 });
274 } else {
275 me.mon(t, {
276 mouseover: me.onTargetOver,
277 mouseout: me.onTargetOut,
278 mousemove: me.onMouseMove,
279 scope: me
280 });
281 }
282 }
283 if (me.anchor) {
284 me.anchorTarget = me.target;
285 }
286 },
287
288 /**
289 * @private
290 */
291 onMouseMove: function(e) {
292 var me = this,
293 t,
294 xy;
295
296 // If the event target is no longer in this tip's target (possibly due to rapidly churning content in target), ignore it.
297 if (!me.target || me.target.contains(e.target)) {
298 t = me.delegate ? e.getTarget(me.delegate) : (me.triggerElement = true);
299 if (t) {
300 me.targetXY = e.getXY();
301 if (t === me.triggerElement) {
302 if (!me.hidden && me.trackMouse) {
303 xy = me.getTargetXY();
304 if (me.constrainPosition) {
305 xy = me.el.adjustForConstraints(xy, me.el.parent());
306 }
307 me.setPagePosition(xy);
308 }
309 } else {
310 me.hide();
311 me.lastActive = new Date(0);
312 me.onTargetOver(e);
313 }
314 } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) {
315 me.delayHide();
316 }
317 }
318 },
319
320 /**
321 * @private
322 */
323 getTargetXY: function() {
324 var me = this,
325 mouseOffset,
326 offsets, xy, dw, dh, de, bd, scrollX, scrollY, axy, sz, constrainPosition;
327 if (me.delegate) {
328 me.anchorTarget = me.triggerElement;
329 }
330 if (me.anchor) {
331 me.targetCounter++;
332 offsets = me.getOffsets();
333 xy = (me.anchorToTarget && !me.trackMouse) ? me.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY;
334 dw = Ext.Element.getViewportWidth() - 5;
335 dh = Ext.Element.getViewportHeight() - 5;
336 de = document.documentElement;
337 bd = document.body;
338 scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5;
339 scrollY = (de.scrollTop || bd.scrollTop || 0) + 5;
340 axy = [xy[0] + offsets[0], xy[1] + offsets[1]];
341 sz = me.getSize();
342 constrainPosition = me.constrainPosition;
343
344 me.anchorEl.removeCls(me.anchorCls);
345
346 if (me.targetCounter < 2 && constrainPosition) {
347 if (axy[0] < scrollX) {
348 if (me.anchorToTarget) {
349 me.defaultAlign = 'l-r';
350 if (me.mouseOffset) {
351 me.mouseOffset[0] *= -1;
352 }
353 }
354 me.anchor = 'left';
355 return me.getTargetXY();
356 }
357 if (axy[0] + sz.width > dw) {
358 if (me.anchorToTarget) {
359 me.defaultAlign = 'r-l';
360 if (me.mouseOffset) {
361 me.mouseOffset[0] *= -1;
362 }
363 }
364 me.anchor = 'right';
365 return me.getTargetXY();
366 }
367 if (axy[1] < scrollY) {
368 if (me.anchorToTarget) {
369 me.defaultAlign = 't-b';
370 if (me.mouseOffset) {
371 me.mouseOffset[1] *= -1;
372 }
373 }
374 me.anchor = 'top';
375 return me.getTargetXY();
376 }
377 if (axy[1] + sz.height > dh) {
378 if (me.anchorToTarget) {
379 me.defaultAlign = 'b-t';
380 if (me.mouseOffset) {
381 me.mouseOffset[1] *= -1;
382 }
383 }
384 me.anchor = 'bottom';
385 return me.getTargetXY();
386 }
387 }
388
389 me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
390 me.anchorEl.addCls(me.anchorCls);
391 me.targetCounter = 0;
392 return axy;
393 } else {
394 mouseOffset = me.getMouseOffset();
395 return (me.targetXY) ? [me.targetXY[0] + mouseOffset[0], me.targetXY[1] + mouseOffset[1]] : mouseOffset;
396 }
397 },
398
399 /**
400 * Overrides Positionable's calculateConstrainedPosition to return a value that is
401 * valid for ToolTip
402 * @private
403 */
404 calculateConstrainedPosition: function(constrainTo) {
405 var me = this,
406 visible,
407 result,
408 floatParentBox;
409
410 // If this is a floating child, account for the fact that positioning will be relative to it
411 if (!constrainTo && me.isContainedFloater()) {
412 visible = me.isVisible();
413 if (!visible) {
414 me.el.show();
415 }
416 result = me.getTargetXY();
417 if (!visible) {
418 me.el.hide();
419 }
420 floatParentBox = me.floatParent.getTargetEl().getViewRegion();
421 result[0] -= floatParentBox.left;
422 result[1] -= floatParentBox.top;
423 } else {
424 result = me.callOverridden(arguments);
425 }
426 return result;
427 },
428
429 getMouseOffset: function() {
430 var me = this,
431 offset = me.anchor ? [0, 0] : [15, 18];
432 if (me.mouseOffset) {
433 offset[0] += me.mouseOffset[0];
434 offset[1] += me.mouseOffset[1];
435 }
436 return offset;
437 },
438
439 fadeOut: function () {
440 var me = this;
441
442 me.el.fadeOut({
443 duration: me.fadeOutDuration,
444 callback: function () {
445 me.hide();
446 me.el.setOpacity('');
447 }
448 });
449 },
450
451 /**
452 * @private
453 */
454 getAnchorPosition: function() {
455 var me = this,
456 m;
457 if (me.anchor) {
458 me.tipAnchor = me.anchor.charAt(0);
459 } else {
460 m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
461 //<debug>
462 if (!m) {
463 Ext.raise('The AnchorTip.defaultAlign value "' + me.defaultAlign + '" is invalid.');
464 }
465 //</debug>
466 me.tipAnchor = m[1].charAt(0);
467 }
468
469 switch (me.tipAnchor) {
470 case 't':
471 return 'top';
472 case 'b':
473 return 'bottom';
474 case 'r':
475 return 'right';
476 }
477 return 'left';
478 },
479
480 /**
481 * @private
482 */
483 getAnchorAlign: function() {
484 switch (this.anchor) {
485 case 'top':
486 return 'tl-bl';
487 case 'left':
488 return 'tl-tr';
489 case 'right':
490 return 'tr-tl';
491 default:
492 return 'bl-tl';
493 }
494 },
495
496 /**
497 * @private
498 */
499 getOffsets: function() {
500 var me = this,
501 mouseOffset,
502 offsets,
503 ap = me.getAnchorPosition().charAt(0);
504 if (me.anchorToTarget && !me.trackMouse) {
505 switch (ap) {
506 case 't':
507 offsets = [0, 9];
508 break;
509 case 'b':
510 offsets = [0, -13];
511 break;
512 case 'r':
513 offsets = [-13, 0];
514 break;
515 default:
516 offsets = [9, 0];
517 break;
518 }
519 } else {
520 switch (ap) {
521 case 't':
522 offsets = [-15 - me.anchorOffset, 30];
523 break;
524 case 'b':
525 offsets = [-19 - me.anchorOffset, -13 - me.el.dom.offsetHeight];
526 break;
527 case 'r':
528 offsets = [-15 - me.el.dom.offsetWidth, -13 - me.anchorOffset];
529 break;
530 default:
531 offsets = [25, -13 - me.anchorOffset];
532 break;
533 }
534 }
535 mouseOffset = me.getMouseOffset();
536 offsets[0] += mouseOffset[0];
537 offsets[1] += mouseOffset[1];
538
539 return offsets;
540 },
541
542 /**
543 * @private
544 */
545 onTargetOver: function(e) {
546 var me = this,
547 delegate = me.delegate,
548 t;
549
550 if (me.disabled || e.within(me.target.dom, true)) {
551 return;
552 }
553 t = delegate ? e.getTarget(delegate) : true;
554 if (t) {
555 me.triggerElement = t;
556 me.triggerEvent = e;
557 me.clearTimer('hide');
558 me.targetXY = e.getXY();
559 me.delayShow();
560 }
561 },
562
563 /**
564 * @private
565 */
566 delayShow: function (trackMouse) {
567 // When delaying, cache the XY coords of the mouse when this method was invoked, NOT when the deferred
568 // show is called because the mouse could then be in a completely different location. Only cache the
569 // coords when trackMouse is false.
570 //
571 // Note that the delayShow call could be coming from a caller which would internally be setting trackMouse
572 // (e.g., Ext.chart.Tip:showTip()). Because of this, the caller will pass along the original value for
573 // trackMouse (i.e., the value passed to the component constructor) to the delayShow method.
574 // See EXTJSIV-11292.
575 var me = this,
576 xy = me.el && (trackMouse === false || !me.trackMouse) && me.getTargetXY();
577
578 if (me.hidden && !me.showTimer) {
579 if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) {
580 me.show();
581 } else {
582 me.showTimer = Ext.defer(me.showFromDelay, me.showDelay, me, [xy]);
583 }
584 }
585 else if (!me.hidden && me.autoHide !== false) {
586 me.show(xy);
587 }
588 },
589
590 showFromDelay: function (xy) {
591 var me = this;
592 // Need to check this here since onDisable only gets called after render, which
593 // the show call below may trigger
594 if (me.disabled) {
595 return;
596 }
597
598 me.fromDelayShow = true;
599 me.show(xy);
600 delete me.fromDelayShow;
601 },
602
603 onShowVeto: function(){
604 this.callParent();
605 delete this.triggerElement;
606 this.clearTimer('show');
607 },
608
609 /**
610 * @private
611 */
612 onTargetOut: function(e) {
613 var me = this,
614 triggerEl = me.triggerElement,
615 // If we don't have a delegate, then the target is set
616 // to true, so set it to the main target.
617 target = triggerEl === true ? me.target : triggerEl;
618
619 // If disabled, moving within the current target, ignore the mouseout
620 // e.within is the only correct way to determine this.
621 if (me.disabled || !triggerEl || e.within(target, true)) {
622 return;
623 }
624 if (me.showTimer) {
625 me.clearTimer('show');
626 me.triggerElement = null;
627 }
628 if (me.autoHide !== false) {
629 me.delayHide();
630 }
631 },
632
633 /**
634 * @private
635 */
636 delayHide: function() {
637 var me = this;
638
639 if (!me.hidden && !me.hideTimer) {
640 me.hideTimer = Ext.defer(me[me.hideAction], me.hideDelay, me);
641 }
642 },
643
644 /**
645 * Hides this tooltip if visible.
646 */
647 hide: function() {
648 var me = this;
649 me.clearTimer('dismiss');
650 me.lastActive = new Date();
651 if (me.anchorEl) {
652 me.anchorEl.hide();
653 }
654 me.callParent(arguments);
655 delete me.triggerElement;
656 },
657
658 /**
659 * Shows this tooltip at the current event target XY position.
660 */
661 show: function (xy) {
662 var me = this;
663
664 // Show this Component first, so that sizing can be calculated
665 // pre-show it off screen so that the el will have dimensions
666 this.callParent();
667 if (this.hidden === false) {
668 if (me.anchor) {
669 me.anchor = me.origAnchor;
670 }
671
672 if (!me.calledFromShowAt) {
673 // If the caller was this.showFromDelay(), the XY coords may have been cached.
674 me.showAt(xy || me.getTargetXY());
675 }
676 }
677 },
678
679 /**
680 * @inheritdoc
681 */
682 showAt: function(xy) {
683 var me = this;
684 me.lastActive = new Date();
685 me.clearTimers();
686 me.calledFromShowAt = true;
687
688 // Only call if this is hidden. May have been called from show above.
689 if (!me.isVisible()) {
690 this.callParent(arguments);
691 }
692
693 // Show may have been vetoed.
694 if (me.isVisible()) {
695 me.setPagePosition(xy[0], xy[1]);
696 if (me.constrainPosition || me.constrain) {
697 me.doConstrain();
698 }
699 me.toFront(true);
700 me.el.syncUnderlays();
701 if (me.dismissDelay && me.autoHide !== false) {
702 me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);
703 }
704 }
705 delete me.calledFromShowAt;
706 },
707
708 /**
709 * @private
710 */
711 syncAnchor: function() {
712 var me = this,
713 anchorPos,
714 targetPos,
715 offset;
716 switch (me.tipAnchor.charAt(0)) {
717 case 't':
718 anchorPos = 'b';
719 targetPos = 'tl';
720 offset = [20 + me.anchorOffset, 1];
721 break;
722 case 'r':
723 anchorPos = 'l';
724 targetPos = 'tr';
725 offset = [ - 1, 12 + me.anchorOffset];
726 break;
727 case 'b':
728 anchorPos = 't';
729 targetPos = 'bl';
730 offset = [20 + me.anchorOffset, -1];
731 break;
732 default:
733 anchorPos = 'r';
734 targetPos = 'tl';
735 offset = [1, 12 + me.anchorOffset];
736 break;
737 }
738 me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset);
739 me.anchorEl.setStyle('z-index', parseInt(me.el.getZIndex(), 10) || 0 + 1).setVisibilityMode(Ext.Element.DISPLAY);
740 },
741
742 afterSetPosition: function(x, y) {
743 var me = this;
744 me.callParent(arguments);
745 if (me.anchor) {
746 me.syncAnchor();
747 if (!me.anchorEl.isVisible()) {
748 me.anchorEl.show();
749 }
750 } else {
751 me.anchorEl.hide();
752 }
753 },
754
755 _timerNames: {},
756
757 /**
758 * @private
759 */
760 clearTimer: function (name) {
761 var me = this,
762 names = me._timerNames,
763 propName = names[name] || (names[name] = name + 'Timer'),
764 timer = me[propName];
765
766 if (timer) {
767 clearTimeout(timer);
768 me[propName] = null;
769 }
770 },
771
772 /**
773 * @private
774 */
775 clearTimers: function() {
776 var me = this;
777 me.clearTimer('show');
778 me.clearTimer('dismiss');
779 me.clearTimer('hide');
780 },
781
782 onShow: function() {
783 var me = this;
784 me.callParent();
785 me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
786 },
787
788 onHide: function() {
789 var me = this;
790 me.callParent();
791 me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
792 },
793
794 /**
795 * @private
796 */
797 onDocMouseDown: function(e) {
798 var me = this;
799 if (!me.closable && !e.within(me.el.dom)) {
800 me.disable();
801 Ext.defer(me.doEnable, 100, me);
802 }
803 },
804
805 /**
806 * @private
807 */
808 doEnable: function() {
809 if (!this.destroyed) {
810 this.enable();
811 }
812 },
813
814 onDisable: function() {
815 this.callParent();
816 this.clearTimers();
817 this.hide();
818 },
819
820 beforeDestroy: function() {
821 var me = this;
822 me.clearTimers();
823 Ext.destroy(me.anchorEl);
824 delete me.anchorEl;
825 delete me.target;
826 delete me.anchorTarget;
827 delete me.triggerElement;
828 me.callParent();
829 },
830
831 onDestroy: function() {
832 Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
833 this.callParent();
834 }
835 });