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