]>
Commit | Line | Data |
---|---|---|
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 | |
105 | Ext.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 |