]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/tip/ToolTip.js
bump version to 7.0.0-4
[extjs.git] / extjs / classic / classic / src / tip / ToolTip.js
CommitLineData
947f0963
TL
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 *
85 * # Alignment
86 *
87 * The following configuration properties allow control over how the ToolTip is aligned relative to
88 * the target element and/or mouse pointer:
89 *
90 * - {@link #anchor}
91 * - {@link #anchorToTarget}
92 * - {@link #trackMouse}
93 * - {@link #mouseOffset}
94 *
95 * # Showing/Hiding
96 *
97 * The following configuration properties allow control over how and when the ToolTip
98 * is automatically shown and hidden:
99 *
100 * - {@link #autoHide}
101 * - {@link #showDelay}
102 * - {@link #hideDelay}
103 * - {@link #dismissDelay}
104 */
105Ext.define('Ext.tip.ToolTip', {
106 extend: 'Ext.tip.Tip',
107 alias: 'widget.tooltip',
108 alternateClassName: 'Ext.ToolTip',
109
110 requires: ['Ext.util.Offset'],
111
112 /**
113 * @property {HTMLElement} triggerElement
114 * When a ToolTip is configured with the `{@link #delegate}`
115 * option to cause selected child elements of the `{@link #target}`
116 * Element to each trigger a separate show event, this property is set to
117 * the DOM element which triggered the show.
118 */
119
120 /**
121 * @cfg {HTMLElement/Ext.dom.Element/String} target
122 * The target element or string id to monitor for mouseover events to trigger
123 * showing this ToolTip.
124 */
125
126 /**
127 * @cfg {Boolean} [autoHide=true]
128 * True to automatically hide the tooltip after the
129 * mouse exits the target element or after the `{@link #dismissDelay}`
130 * has expired if set. If `{@link #closable} = true`
131 * a close tool button will be rendered into the tooltip header.
132 */
133 autoHide: true,
134
135 /**
136 * @cfg {Number} showDelay
137 * Delay in milliseconds before the tooltip displays after the mouse enters the target element.
138 */
139 showDelay: 500,
140
141 /**
142 * @cfg {Number} hideDelay
143 * Delay in milliseconds after the mouse exits the target element but before the tooltip
144 * actually hides. Set to 0 for the tooltip to hide immediately.
145 */
146 hideDelay: 200,
147
148 /**
149 * @cfg {Number} dismissDelay
150 * Delay in milliseconds before the tooltip automatically hides. To disable automatic hiding,
151 * set dismissDelay = 0.
152 */
153 dismissDelay: 5000,
154
155 /**
156 * @cfg {Number[]} [targetOffset=[0, 0]]
157 * When {@link #anchorToTarget} is being used to position this tip relative to its target
158 * element, this may be used as an extra XY offset from the target element.
159 */
160
161 /**
162 * @cfg {Number[]} [mouseOffset=[15, 18]]
163 * An XY offset from the mouse position where the tooltip should be shown.
164 */
165 mouseOffset: [15, 18],
166
167 /**
168 * @cfg {Boolean} trackMouse
169 * True to have the tooltip follow the mouse as it moves over the target element.
170 */
171 trackMouse: false,
172
173 /**
174 * @cfg {String} anchor
175 * If specified, indicates that the tip should be anchored to a
176 * particular side of the target element or mouse pointer ("top", "right", "bottom",
177 * or "left"), with an arrow pointing back at the target or mouse pointer. If
178 * {@link #constrainPosition} is enabled, this will be used as a preferred value
179 * only and may be flipped as needed.
180 */
181
182 /**
183 * @cfg {Boolean} anchorToTarget
184 * True to anchor the tooltip to the target element, false to anchor it relative to the mouse
185 * coordinates. When `anchorToTarget` is true, use `{@link #defaultAlign}` to control tooltip
186 * alignment to the target element. When `anchorToTarget` is false, use `{@link #anchor}`
187 * instead to control alignment.
188 */
189 anchorToTarget: true,
190
191 /**
192 * @cfg {String} delegate
193 *
194 * A {@link Ext.DomQuery DomQuery} simple selector which allows selection of individual elements
195 * within the `{@link #target}` element to trigger showing and hiding the ToolTip as the mouse
196 * moves within the target. See {@link Ext.dom.Query} for information about simple selectors.
197 *
198 * When specified, the child element of the target which caused a show event is placed into the
199 * `{@link #triggerElement}` property before the ToolTip is shown.
200 *
201 * This may be useful when a Component has regular, repeating elements in it, each of which need
202 * a ToolTip which contains information specific to that element.
203 *
204 * See the delegate example in class documentation of {@link Ext.tip.ToolTip}.
205 */
206
207 /**
208 * @cfg {Boolean} [showOnTap=false]
209 * On touch platforms, if {@link #showOnTap} is `true`, a tap on the target shows the tip.
210 * In this case any {@link #showDelay} is ignored.
211 *
212 * This is useful for adding tips on elements which do not have tap listeners. It would
213 * not be appropriate for a ToolTip on a {@link Ext.Button Button}.
214 */
215
216 /**
217 * @private
218 */
219 targetCounter: 0,
220
221 quickShowInterval: 250,
222
223 /**
224 * @cfg {String} [hideAction="hide"]
225 * The method to use to hide the tooltip. Another useful method for this is `fadeOut`.
226 */
227 hideAction: 'hide',
228
229 /**
230 * @cfg {Number} [fadeOutDuration=1000]
231 * The number of milliseconds for the `fadeOut` animation. Only valid if `hideAction`
232 * is set to `fadeOut`.
233 */
234 fadeOutDuration: 1000,
235
236 /**
237 * @cfg {String} defaultAlign
238 * A string which specifies how this ToolTip is to align with regard to its
239 * {@link #currentTarget} by means of identifying the point of the tooltip to
240 * join to the point of the target.
241 *
242 * By default, the tooltip shows at {@link #mouseOffset} pixels from the
243 * triggering pointer event. Using this config anchors the ToolTip to its target
244 * instead.
245 *
246 * This may take the following forms:
247 *
248 * - **Blank**: Defaults to aligning the element's top-left corner to the target's
249 * bottom-left corner ("tl-bl").
250 * - **Two anchors**: If two values from the table below are passed separated by a dash,
251 * the first value is used as the element's anchor point, and the second value is
252 * used as the target's anchor point.
253 * - **Two edge/offset descriptors:** An edge/offset descriptor is an edge initial
254 * (`t`/`r`/`b`/`l`) followed by a percentage along that side. This describes a
255 * point to align with a similar point in the target. So `'t0-b0'` would be
256 * the same as `'tl-bl'`, `'l0-r50'` would place the top left corner of this item
257 * halfway down the right edge of the target item. This allows more flexibility
258 * and also describes which two edges are considered adjacent when positioning a tip pointer.
259 *
260 * Following are all of the supported predefined anchor positions:
261 *
262 * Value Description
263 * ----- -----------------------------
264 * tl The top left corner
265 * t The center of the top edge
266 * tr The top right corner
267 * l The center of the left edge
268 * c The center
269 * r The center of the right edge
270 * bl The bottom left corner
271 * b The center of the bottom edge
272 * br The bottom right corner
273 *
274 * You can put a '?' at the end of the alignment string to constrain the positioned element
275 * to the {@link Ext.Viewport Viewport}. The element will attempt to align as specified,
276 * but the position will be adjusted to constrain to the viewport if necessary. Note that
277 * the element being aligned might be swapped to align to a different position than that
278 * specified in order to enforce the viewport constraints.
279 *
280 * Example Usage:
281 *
282 * // align the top left corner of the tooltip with the top right corner of its target.
283 * defaultAlign: 'tl-tr'
284 *
285 * // align the bottom right corner of the tooltip with the center left edge of its target.
286 * defaultAlign: 'br-l'
287 *
288 * // align the top center of the tooltip with the bottom left corner of its target.
289 * defaultAlign: 't-bl'
290 *
291 * // align the 25% point on the bottom edge of this tooltip
292 * // with the 75% point on the top edge of its target.
293 * defaultAlign: 'b25-t75'
294 */
295 defaultAlign: 'bl-tl',
296
297 ariaRole: 'tooltip',
298
299 alwaysOnTop: true,
300
301 initComponent: function() {
302 var me = this;
303
304 me.callParent();
305 me.setTarget(me.target);
306
307 // currentTarget is a flyweight which points to the activeTarget.
308 me.currentTarget = new Ext.dom.Fly();
309 },
310
311 onRender: function(ct, position) {
312 var me = this;
313
314 me.callParent(arguments);
315
316 //<debug>
317 if (me.sticky) {
318 // tell the spec runner to ignore this element when checking if the dom is clean
319 me.el.dom.setAttribute('data-sticky', true);
320 }
321 //</debug>
322
323 me.anchorEl = me.el.createChild({
324 role: 'presentation',
325 cls: Ext.baseCSSPrefix + 'tip-anchor'
326 });
327 },
328
329 show: function() {
330 // A programmatic show should align to the target
331 if (!this.currentTarget.dom && this.target) {
332 return this.showBy(this.target);
333 }
334
335 this.callParent();
336 },
337
338 /**
339 * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse
340 * moves over the element.
341 * @param {String/HTMLElement/Ext.dom.Element} target The Element, HTMLElement, or
342 * ID of an element to bind to
343 */
344 setTarget: function(target) {
345 var me = this,
346 listeners;
347
348 if (me.targetListeners) {
349 me.targetListeners.destroy();
350 }
351
352 if (target) {
353 me.target = target = Ext.get(target.el || target);
354 listeners = {
355 mouseover: 'onTargetOver',
356 mouseout: 'onTargetOut',
357 mousemove: 'onMouseMove',
358 tap: 'onTargetTap',
359 scope: me,
360 destroyable: true
361 };
362
363 me.targetListeners = target.on(listeners);
364 }
365 else {
366 me.target = null;
367 }
368 },
369
370 /**
371 * @private
372 */
373 onMouseMove: function(e) {
374 var me = this,
375 dismissDelay = me.dismissDelay;
376
377 // Always update pointerEvent, so that if there's a delayed show
378 // scheduled, it gets the latest pointer to align to.
379 me.pointerEvent = e;
380
381 if (me.isVisible() && me.currentTarget.contains(e.target)) {
382 // If they move the mouse, restart the dismiss delay
383 if (dismissDelay && me.autoHide !== false) {
384 me.clearTimer('dismiss');
385 me.dismissTimer = Ext.defer(me.hide, dismissDelay, me);
386 }
387
388 if (me.trackMouse) {
389 me.doAlignment(me.getAlignRegion());
390 }
391 }
392 },
393
394 /**
395 * @private
396 */
397 getAlignRegion: function() {
398 var me = this,
399 anchorEl = me.anchorEl,
400 align = me.getAnchorAlign(),
401 overlap,
402 alignSpec,
403 target,
404 mouseOffset = me.mouseOffset;
405
406 if (!me.anchorSize) {
407 anchorEl.addCls(Ext.baseCSSPrefix + 'tip-anchor-top');
408 anchorEl.show();
409
410 me.anchorSize = new Ext.util.Offset(
411 anchorEl.getWidth(false, true), anchorEl.getHeight(false, true)
412 );
413
414 anchorEl.removeCls(Ext.baseCSSPrefix + 'tip-anchor-top');
415 anchorEl.hide();
416 }
417
418 // Target region from the anchorTarget element unless trackMouse set
419 if ((me.anchor || me.align) && me.anchorToTarget && !me.trackMouse) {
420 target = me.currentTarget.getRegion();
421 }
422
423 // Here, we're either trackMouse: true, or we're not anchored to the target
424 // element, so we should show offset from the mouse.
425 // If we are being shown programatically, use 0, 0
426 else {
427 target = me.pointerEvent
428 ? me.pointerEvent.getPoint().adjust(
429 -Math.abs(mouseOffset[1]), Math.abs(mouseOffset[0]),
430 Math.abs(mouseOffset[1]), -Math.abs(mouseOffset[0])
431 )
432 : new Ext.util.Point();
433
434 if (!me.anchor) {
435 overlap = true;
436
437 if (mouseOffset[0] > 0) {
438 if (mouseOffset[1] > 0) {
439 align = 'tl-br';
440 }
441 else {
442 align = 'bl-tr';
443 }
444 }
445 else {
446 if (mouseOffset[1] > 0) {
447 align = 'tr-bl';
448 }
449 else {
450 align = 'br-tl';
451 }
452 }
453 }
454 }
455
456 alignSpec = {
457 align: me.convertPositionSpec(align),
458 axisLock: me.axisLock,
459 target: target,
460 overlap: overlap,
461 offset: me.targetOffset,
462 inside: me.constrainPosition
463 ? (me.constrainTo || Ext.getBody().getRegion().adjust(5, -5, -5, 5))
464 : null
465 };
466
467 if (me.anchor) {
468 alignSpec.anchorSize = me.anchorSize;
469 }
470
471 return me.getRegion().alignTo(alignSpec);
472 },
473
474 fadeOut: function() {
475 var me = this;
476
477 me.el.fadeOut({
478 duration: me.fadeOutDuration,
479 callback: function() {
480 me.hide();
481 me.el.setOpacity('');
482 }
483 });
484 },
485
486 /**
487 * @private
488 */
489 getAnchorAlign: function() {
490 switch (this.anchor) {
491 case 'top':
492 return 'tl-bl';
493
494 case 'left':
495 return 'tl-tr';
496
497 case 'right':
498 return 'tr-tl';
499
500 default:
501 return this.defaultAlign;
502 }
503 },
504
505 onTargetTap: function(e) {
506 // On hybrid mouse/touch systems, we want to show the tip on touch, but
507 // we don't want to show it if this is coming from a click event, because
508 // the mouse is already hovered. Tap occasionally hides - eg: pickers, menus.
509 if (this.showOnTap && e.pointerType !== 'mouse' && Ext.fly(e.target).isVisible(true)) {
510 this.onTargetOver(e);
511 }
512 },
513
514 /**
515 * @private
516 */
517 onTargetOver: function(e) {
518 var me = this,
519 delegate = me.delegate,
520 currentTarget = me.currentTarget,
521 fromElement = e.relatedTarget || e.fromElement,
522 newTarget,
523 myListeners = me.hasListeners;
524
525 if (me.disabled) {
526 return;
527 }
528
529 if (delegate) {
530 // Moving inside a delegate
531 if (currentTarget.contains(e.target)) {
532 return;
533 }
534
535 newTarget = e.getTarget(delegate);
536
537 // Mouseovers while within a target do nothing
538 if (newTarget && e.getRelatedTarget(delegate) === newTarget) {
539 return;
540 }
541 }
542 // Moved from outside the target
543 else if (!me.target.contains(fromElement)) {
544 newTarget = me.target.dom;
545 }
546 // Moving inside the target
547 else {
548 return;
549 }
550
551 // If pointer entered the target or a delegate child, then show.
552 if (newTarget) {
553 // If users need to see show events on target change, we must hide.
554 if ((myListeners.beforeshow || myListeners.show) && me.isVisible()) {
555 me.hide();
556 }
557
558 me.triggerElement = newTarget;
559 me.pointerEvent = e;
560 currentTarget.attach(newTarget);
561 me.handleTargetOver(newTarget, e);
562 }
563 // If over a non-delegate child, behave as in target out
564 else if (currentTarget.dom) {
565 me.handleTargetOut();
566 }
567 },
568
569 handleTargetOver: function(target, event) {
570 // Separated from onTargetOver so that subclasses can handle target over in any way.
571
572 // If we are showing on tap, show immediately
573 if (event.pointerType !== 'mouse') {
574 this.showFromDelay();
575 }
576 else {
577 this.delayShow();
578 }
579 },
580
581 /**
582 * @private
583 */
584 delayShow: function() {
585 var me = this;
586
587 me.clearTimer('hide');
588
589 if (me.hidden && !me.showTimer) {
590 if (me.delegate && Ext.Date.getElapsed(me.lastHidden) < me.quickShowInterval) {
591 me.showFromDelay();
592 }
593 else {
594 me.showTimer = Ext.defer(
595 me.showFromDelay,
596 me.pointerEvent.pointerType !== 'mouse' ? 0 : me.showDelay,
597 me
598 );
599 }
600 }
601 else if (!me.hidden && me.autoHide !== false) {
602 me.showFromDelay();
603 }
604 },
605
606 showFromDelay: function() {
607 var me = this;
608
609 // Need to check this here since onDisable only gets called after render, which
610 // the show call below may trigger
611 if (!me.disabled) {
612 me.fireEvent('hovertarget', me, me.currentTarget, me.currentTarget.dom);
613
614 if (me.isVisible()) {
615 me.realignToTarget();
616 }
617 else {
618 me.triggerElement = me.currentTarget.dom;
619 me.fromDelayShow = true;
620 me.show();
621 me.fromDelayShow = false;
622 }
623 }
624 },
625
626 /**
627 * @private
628 */
629 onTargetOut: function(e) {
630 // We have exited the current target
631 if (this.currentTarget.dom && !this.currentTarget.contains(e.relatedTarget)) {
632 this.handleTargetOut();
633 }
634 },
635
636 handleTargetOut: function() {
637 var me = this;
638
639 if (me.showTimer) {
640 me.clearTimer('show');
641 }
642
643 if (me.isVisible() && me.autoHide) {
644 me.delayHide();
645 }
646 },
647
648 /**
649 * @private
650 */
651 delayHide: function() {
652 var me = this;
653
654 if (!me.hidden && !me.hideTimer) {
655 me.clearTimer('dismiss');
656 me.hideTimer = Ext.defer(me[me.hideAction], me.hideDelay, me);
657 }
658 },
659
660 /**
661 * Hides this tooltip if visible.
662 */
663 hide: function() {
664 var me = this;
665
666 // Must also do this on hide in case it was dismissed while over
667 me.currentTarget.detach();
668
669 me.clearTimer('dismiss');
670 me.lastHidden = new Date();
671
672 if (me.anchorEl) {
673 me.anchorEl.hide();
674 }
675
676 me.callParent(arguments);
677
678 me.triggerElement = null;
679 },
680
681 /**
682 * Ensures this tooltip at the current event target XY position.
683 */
684 afterShow: function() {
685 this.callParent();
686 this.realignToTarget();
687 },
688
689 /**
690 * Realign this tooltip to the {@link #cfg-target}.
691 *
692 * @since 6.2.1
693 */
694 realignToTarget: function() {
695 var me = this;
696
697 me.clearTimers();
698
699 if (!me.calledFromShowAt) {
700 me.doAlignment(me.getAlignRegion());
701 }
702
703 if (me.dismissDelay && me.autoHide !== false) {
704 me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);
705 }
706 },
707
708 /**
709 * Shows this ToolTip aligned to the passed Component or element or event according to the
710 * {@link #anchor} config.
711 * @param {Ext.Component/Ext.event.Event/Ext.dom.Element} target The {@link Ext.Component}
712 * or {@link Ext.dom.Element}, or (Ext.event.Event} to show this ToolTip by.
713 */
714 showBy: function(target) {
715 var me = this;
716
717 me.align = me.defaultAlign;
718
719 if (target.isEvent) {
720 me.currentTarget.attach(target.target);
721 me.pointerEvent = target;
722 }
723 else {
724 me.currentTarget.attach(Ext.getDom(target.el || target));
725 me.triggerElement = me.currentTarget.dom;
726 }
727
728 if (me.isVisible()) {
729 me.realignToTarget();
730 }
731 else {
732 me.show();
733 }
734
735 return me;
736 },
737
738 _timerNames: {},
739
740 /**
741 * @private
742 */
743 clearTimer: function(name) {
744 var me = this,
745 names = me._timerNames,
746 propName = names[name] || (names[name] = name + 'Timer'),
747 timer = me[propName];
748
749 if (timer) {
750 Ext.undefer(timer);
751 me[propName] = null;
752
753 // We were going to show against the target, but now not.
754 if (name === 'show' && me.isHidden()) {
755 me.currentTarget.detach();
756 }
757 }
758 },
759
760 /**
761 * @private
762 */
763 clearTimers: function() {
764 var me = this;
765
766 me.clearTimer('show');
767 me.clearTimer('dismiss');
768 me.clearTimer('hide');
769 me.clearTimer('enable');
770 },
771
772 onShow: function() {
773 var me = this;
774
775 me.callParent();
776
777 me.mousedownListener = Ext.on({
778 mousedown: 'onDocMouseDown',
779 scope: me,
780 destroyable: true
781 });
782 },
783
784 onHide: function() {
785 var me = this;
786
787 me.callParent();
788
789 Ext.destroy(me.mousedownListener);
790 },
791
792 /**
793 * @private
794 */
795 onDocMouseDown: function(e) {
796 var me = this,
797 delegate = me.delegate;
798
799 if (e.within(me.el.dom)) {
800 // A real touch event inside the tip is the equivalent of
801 // mousing over the tip to keep it visible, so cancel the
802 // dismiss timer.
803 if (e.pointerType !== 'mouse' && me.allowOver) {
804 me.clearTimer('dismiss');
805 }
806 }
807 // Only respond to the mousedown if it's not on this tip, and it's not on a target.
808 // If it's on a target, onTargetTap will handle it.
809 else if (!me.closable) {
810 if (e.within(me.target) && (!delegate || e.getTarget(delegate))) {
811 me.delayHide();
812 }
813 else {
814 me.disable();
815 me.enableTimer = Ext.defer(me.enable, 100, me);
816 }
817 }
818 },
819
820 /**
821 * @private
822 */
823 doEnable: function() {
824 if (!this.destroyed) {
825 this.enable();
826 }
827 },
828
829 onDisable: function() {
830 this.callParent();
831 this.clearTimers();
832 this.hide();
833 },
834
835 doDestroy: function() {
836 var me = this;
837
838 me.clearTimers();
839
840 me.destroyMembers('mousedownListener', 'anchorEl');
841
842 me.callParent();
843 },
844
845 privates: {
846 /**
847 * Implementation for universal apps so that the Tooltip interface they are using works
848 * when common code uses the ToolTip API.
849 */
850 getTrackMouse: function() {
851 return this.trackMouse;
852 },
853
854 clipTo: function(clippingEl, sides) {
855 // Override because we also need to clip the anchor
856 var clippingRegion;
857
858 // Allow a Region to be passed
859 if (clippingEl.isRegion) {
860 clippingRegion = clippingEl;
861 }
862 else {
863 // eslint-disable-next-line max-len
864 clippingRegion = (clippingEl.isComponent ? clippingEl.el : Ext.fly(clippingEl)).getConstrainRegion();
865 }
866
867 this.callParent([clippingRegion, sides]);
868
869 // Clip the anchor to the same bounds
870 this.anchorEl.clipTo(clippingRegion, sides);
871 }
872 }
873});