]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/Tooltip.js.uncompressed.js
2 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n"}});
3 define("dijit/Tooltip", [
4 "dojo/_base/array", // array.forEach array.indexOf array.map
5 "dojo/_base/declare", // declare
6 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
7 "dojo/dom", // dom.byId
8 "dojo/dom-class", // domClass.add
9 "dojo/dom-geometry", // domGeometry.position
10 "dojo/dom-style", // domStyle.set, domStyle.get
11 "dojo/_base/lang", // lang.hitch lang.isArrayLike
14 "dojo/sniff", // has("ie")
15 "./_base/manager", // manager.defaultDuration
20 "dojo/text!./templates/Tooltip.html",
21 "./main" // sets dijit.showTooltip etc. for back-compat
22 ], function(array
, declare
, fx
, dom
, domClass
, domGeometry
, domStyle
, lang
, mouse
, on
, has
,
23 manager
, place
, _Widget
, _TemplatedMixin
, BackgroundIframe
, template
, dijit
){
29 // TODO: Tooltip should really share more positioning code with TooltipDialog, like:
30 // - the orient() method
31 // - the connector positioning code in show()
32 // - the dijitTooltip[Dialog] class
34 // The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
35 // with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
37 var MasterTooltip
= declare("dijit._MasterTooltip", [_Widget
, _TemplatedMixin
], {
39 // Internal widget that holds the actual tooltip markup,
40 // which occurs once per page.
41 // Called by Tooltip widgets which are just containers to hold
47 // Milliseconds to fade in/fade out
48 duration
: manager
.defaultDuration
,
50 templateString
: template
,
52 postCreate: function(){
53 this.ownerDocumentBody
.appendChild(this.domNode
);
55 this.bgIframe
= new BackgroundIframe(this.domNode
);
57 // Setup fade-in and fade-out functions.
58 this.fadeIn
= fx
.fadeIn({ node
: this.domNode
, duration
: this.duration
, onEnd
: lang
.hitch(this, "_onShow") });
59 this.fadeOut
= fx
.fadeOut({ node
: this.domNode
, duration
: this.duration
, onEnd
: lang
.hitch(this, "_onHide") });
62 show: function(innerHTML
, aroundNode
, position
, rtl
, textDir
){
64 // Display tooltip w/specified contents to right of specified node
65 // (To left if there's no space on the right, or if rtl == true)
67 // Contents of the tooltip
68 // aroundNode: DomNode|dijit/place.__Rectangle
69 // Specifies that tooltip should be next to this node / area
70 // position: String[]?
71 // List of positions to try to position tooltip (ex: ["right", "above"])
73 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
74 // means "rtl"; specifies GUI direction, not text direction.
76 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
79 if(this.aroundNode
&& this.aroundNode
=== aroundNode
&& this.containerNode
.innerHTML
== innerHTML
){
83 if(this.fadeOut
.status() == "playing"){
84 // previous tooltip is being hidden; wait until the hide completes then show new one
85 this._onDeck
=arguments
;
88 this.containerNode
.innerHTML
=innerHTML
;
91 this.set("textDir", textDir
);
94 this.containerNode
.align
= rtl
? "right" : "left"; //fix the text alignment
96 var pos
= place
.around(this.domNode
, aroundNode
,
97 position
&& position
.length
? position
: Tooltip
.defaultPosition
, !rtl
, lang
.hitch(this, "orient"));
99 // Position the tooltip connector for middle alignment.
100 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
101 var aroundNodeCoords
= pos
.aroundNodePos
;
102 if(pos
.corner
.charAt(0) == 'M' && pos
.aroundCorner
.charAt(0) == 'M'){
103 this.connectorNode
.style
.top
= aroundNodeCoords
.y
+ ((aroundNodeCoords
.h
- this.connectorNode
.offsetHeight
) >> 1) - pos
.y
+ "px";
104 this.connectorNode
.style
.left
= "";
105 }else if(pos
.corner
.charAt(1) == 'M' && pos
.aroundCorner
.charAt(1) == 'M'){
106 this.connectorNode
.style
.left
= aroundNodeCoords
.x
+ ((aroundNodeCoords
.w
- this.connectorNode
.offsetWidth
) >> 1) - pos
.x
+ "px";
108 // Not *-centered, but just above/below/after/before
109 this.connectorNode
.style
.left
= "";
110 this.connectorNode
.style
.top
= "";
114 domStyle
.set(this.domNode
, "opacity", 0);
116 this.isShowingNow
= true;
117 this.aroundNode
= aroundNode
;
120 orient: function(/*DomNode*/ node
, /*String*/ aroundCorner
, /*String*/ tooltipCorner
, /*Object*/ spaceAvailable
, /*Object*/ aroundNodeCoords
){
122 // Private function to set CSS for tooltip node based on which position it's in.
123 // This is called by the dijit popup code. It will also reduce the tooltip's
124 // width to whatever width is available
128 this.connectorNode
.style
.top
= ""; //reset to default
130 var heightAvailable
= spaceAvailable
.h
,
131 widthAvailable
= spaceAvailable
.w
;
133 node
.className
= "dijitTooltip " +
135 "MR-ML": "dijitTooltipRight",
136 "ML-MR": "dijitTooltipLeft",
137 "TM-BM": "dijitTooltipAbove",
138 "BM-TM": "dijitTooltipBelow",
139 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
140 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
141 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
142 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
143 "BR-BL": "dijitTooltipRight",
144 "BL-BR": "dijitTooltipLeft"
145 }[aroundCorner
+ "-" + tooltipCorner
];
147 // reset width; it may have been set by orient() on a previous tooltip show()
148 this.domNode
.style
.width
= "auto";
150 // Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
151 // Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
152 // negative number since that causes an exception on IE.
153 var size
= domGeometry
.position(this.domNode
);
155 // workaround strange IE9 bug where setting width to offsetWidth causes words to wrap
159 var width
= Math
.min((Math
.max(widthAvailable
,1)), size
.w
);
161 domGeometry
.setMarginBox(this.domNode
, {w
: width
});
163 // Reposition the tooltip connector.
164 if(tooltipCorner
.charAt(0) == 'B' && aroundCorner
.charAt(0) == 'B'){
165 var bb
= domGeometry
.position(node
);
166 var tooltipConnectorHeight
= this.connectorNode
.offsetHeight
;
167 if(bb
.h
> heightAvailable
){
168 // The tooltip starts at the top of the page and will extend past the aroundNode
169 var aroundNodePlacement
= heightAvailable
- ((aroundNodeCoords
.h
+ tooltipConnectorHeight
) >> 1);
170 this.connectorNode
.style
.top
= aroundNodePlacement
+ "px";
171 this.connectorNode
.style
.bottom
= "";
173 // Align center of connector with center of aroundNode, except don't let bottom
174 // of connector extend below bottom of tooltip content, or top of connector
175 // extend past top of tooltip content
176 this.connectorNode
.style
.bottom
= Math
.min(
177 Math
.max(aroundNodeCoords
.h
/2 - tooltipConnectorHeight/2, 0),
178 bb
.h
- tooltipConnectorHeight
) + "px";
179 this.connectorNode
.style
.top
= "";
182 // reset the tooltip back to the defaults
183 this.connectorNode
.style
.top
= "";
184 this.connectorNode
.style
.bottom
= "";
187 return Math
.max(0, size
.w
- widthAvailable
);
192 // Called at end of fade-in operation
196 // the arrow won't show up on a node w/an opacity filter
197 this.domNode
.style
.filter
="";
201 hide: function(aroundNode
){
205 if(this._onDeck
&& this._onDeck
[1] == aroundNode
){
206 // this hide request is for a show() that hasn't even started yet;
207 // just cancel the pending show()
209 }else if(this.aroundNode
=== aroundNode
){
210 // this hide request is for the currently displayed tooltip
212 this.isShowingNow
= false;
213 this.aroundNode
= null;
216 // just ignore the call, it's for a tooltip that has already been erased
222 // Called at end of fade-out operation
226 this.domNode
.style
.cssText
=""; // to position offscreen again
227 this.containerNode
.innerHTML
="";
229 // a show request has been queued up; do it now
230 this.show
.apply(this, this._onDeck
);
235 _setAutoTextDir: function(/*Object*/node
){
237 // Resolve "auto" text direction for children nodes
241 this.applyTextDir(node
, has("ie") ? node
.outerText
: node
.textContent
);
242 array
.forEach(node
.children
, function(child
){this._setAutoTextDir(child
); }, this);
245 _setTextDirAttr: function(/*String*/ textDir
){
247 // Setter for textDir.
249 // Users shouldn't call this function; they should be calling
250 // set('textDir', value)
254 this._set("textDir", textDir
);
256 if (textDir
== "auto"){
257 this._setAutoTextDir(this.containerNode
);
259 this.containerNode
.dir
= this.textDir
;
264 dijit
.showTooltip = function(innerHTML
, aroundNode
, position
, rtl
, textDir
){
266 // Static method to display tooltip w/specified contents in specified position.
267 // See description of dijit/Tooltip.defaultPosition for details on position parameter.
268 // If position is not specified then dijit/Tooltip.defaultPosition is used.
270 // Contents of the tooltip
271 // aroundNode: place.__Rectangle
272 // Specifies that tooltip should be next to this node / area
273 // position: String[]?
274 // List of positions to try to position tooltip (ex: ["right", "above"])
276 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
277 // means "rtl"; specifies GUI direction, not text direction.
279 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
281 // After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
282 // Possibly remove this in 2.0. Alternately, get before/after to work.
284 position
= array
.map(position
, function(val
){
285 return {after
: "after-centered", before
: "before-centered"}[val
] || val
;
289 if(!Tooltip
._masterTT
){ dijit
._masterTT
= Tooltip
._masterTT
= new MasterTooltip(); }
290 return Tooltip
._masterTT
.show(innerHTML
, aroundNode
, position
, rtl
, textDir
);
293 dijit
.hideTooltip = function(aroundNode
){
295 // Static method to hide the tooltip displayed via showTooltip()
296 return Tooltip
._masterTT
&& Tooltip
._masterTT
.hide(aroundNode
);
299 var Tooltip
= declare("dijit.Tooltip", _Widget
, {
301 // Pops up a tooltip (a help message) when you hover over a node.
302 // Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
305 // Text to display in the tooltip.
306 // Specified as innerHTML when creating the widget from markup.
309 // showDelay: Integer
310 // Number of milliseconds to wait after hovering over/focusing on the object, before
311 // the tooltip is displayed.
314 // connectId: String|String[]|DomNode|DomNode[]
315 // Id of domNode(s) to attach the tooltip to.
316 // When user hovers over specified dom node(s), the tooltip will appear.
319 // position: String[]
320 // See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
324 // CSS expression to apply this Tooltip to descendants of connectIds, rather than to
325 // the nodes specified by connectIds themselves. Useful for applying a Tooltip to
326 // a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
327 // Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
329 // The application must require() an appropriate level of dojo/query to handle the selector.
332 // TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
333 // So, change connectId to a "", remove addTarget()/removeTarget(), etc.
335 _setConnectIdAttr: function(/*String|String[]}DomNode|DomNode[]*/ newId
){
337 // Connect to specified node(s)
339 // Remove connections to old nodes (if there are any)
340 array
.forEach(this._connections
|| [], function(nested
){
341 array
.forEach(nested
, function(handle
){ handle
.remove(); });
344 // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
345 this._connectIds
= array
.filter(lang
.isArrayLike(newId
) ? newId
: (newId
? [newId
] : []),
346 function(id
){ return dom
.byId(id
, this.ownerDocument
); }, this);
349 this._connections
= array
.map(this._connectIds
, function(id
){
350 var node
= dom
.byId(id
, this.ownerDocument
),
351 selector
= this.selector
,
352 delegatedEvent
= selector
?
353 function(eventType
){ return on
.selector(selector
, eventType
); } :
354 function(eventType
){ return eventType
; },
357 on(node
, delegatedEvent(mouse
.enter
), function(){
360 on(node
, delegatedEvent("focusin"), function(){
363 on(node
, delegatedEvent(mouse
.leave
), lang
.hitch(self
, "_onUnHover")),
364 on(node
, delegatedEvent("focusout"), lang
.hitch(self
, "_onUnHover"))
368 this._set("connectId", newId
);
371 addTarget: function(/*OomNode|String*/ node
){
373 // Attach tooltip to specified node if it's not already connected
375 // TODO: remove in 2.0 and just use set("connectId", ...) interface
377 var id
= node
.id
|| node
;
378 if(array
.indexOf(this._connectIds
, id
) == -1){
379 this.set("connectId", this._connectIds
.concat(id
));
383 removeTarget: function(/*DomNode|String*/ node
){
385 // Detach tooltip from specified node
387 // TODO: remove in 2.0 and just use set("connectId", ...) interface
389 var id
= node
.id
|| node
, // map from DOMNode back to plain id string
390 idx
= array
.indexOf(this._connectIds
, id
);
392 // remove id (modifies original this._connectIds but that's OK in this case)
393 this._connectIds
.splice(idx
, 1);
394 this.set("connectId", this._connectIds
);
398 buildRendering: function(){
399 this.inherited(arguments
);
400 domClass
.add(this.domNode
,"dijitTooltipData");
404 this.inherited(arguments
);
406 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
407 // didn't exist during the widget's initialization, then connect now.
408 var ids
= this.connectId
;
409 array
.forEach(lang
.isArrayLike(ids
) ? ids
: [ids
], this.addTarget
, this);
412 getContent: function(/*DomNode*/ node
){
414 // User overridable function that return the text to display in the tooltip.
417 return this.label
|| this.domNode
.innerHTML
;
420 _onHover: function(/*DomNode*/ target
){
422 // Despite the name of this method, it actually handles both hover and focus
423 // events on the target node, setting a timer to show the tooltip.
426 if(!this._showTimer
){
427 this._showTimer
= this.defer(function(){ this.open(target
); }, this.showDelay
);
431 _onUnHover: function(){
433 // Despite the name of this method, it actually handles both mouseleave and blur
434 // events on the target node, hiding the tooltip.
439 this._showTimer
.remove();
440 delete this._showTimer
;
445 open: function(/*DomNode*/ target
){
447 // Display the tooltip; usually not called directly.
452 this._showTimer
.remove();
453 delete this._showTimer
;
456 var content
= this.getContent(target
);
460 Tooltip
.show(content
, target
, this.position
, !this.isLeftToRight(), this.textDir
);
462 this._connectNode
= target
; // _connectNode means "tooltip currently displayed for this node"
463 this.onShow(target
, this.position
);
468 // Hide the tooltip or cancel timer for show of tooltip
472 if(this._connectNode
){
473 // if tooltip is currently shown
474 Tooltip
.hide(this._connectNode
);
475 delete this._connectNode
;
479 // if tooltip is scheduled to be shown (after a brief delay)
480 this._showTimer
.remove();
481 delete this._showTimer
;
485 onShow: function(/*===== target, position =====*/){
487 // Called when the tooltip is shown
494 // Called when the tooltip is hidden
502 // Remove connections manually since they aren't registered to be removed by _WidgetBase
503 array
.forEach(this._connections
|| [], function(nested
){
504 array
.forEach(nested
, function(handle
){ handle
.remove(); });
507 this.inherited(arguments
);
511 Tooltip
._MasterTooltip
= MasterTooltip
; // for monkey patching
512 Tooltip
.show
= dijit
.showTooltip
; // export function through module return value
513 Tooltip
.hide
= dijit
.hideTooltip
; // export function through module return value
515 Tooltip
.defaultPosition
= ["after-centered", "before-centered"];
518 lang.mixin(Tooltip, {
519 // defaultPosition: String[]
520 // This variable controls the position of tooltips, if the position is not specified to
521 // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
522 // possible for `dijit/place.around()`. The recommended values are:
524 // - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
525 // in the case of RTL scripts like Hebrew and Arabic
526 // - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
527 // in the case of RTL scripts like Hebrew and Arabic
528 // - above-centered: tooltip is centered above anchor node
529 // - below-centered: tooltip is centered above anchor node
531 // The list is positions is tried, in order, until a position is found where the tooltip fits
532 // within the viewport.
534 // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
535 // the screen so that there's no room above the target node. Nodes with drop downs, like
536 // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
537 // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
538 // is only room below (or above) the target node, but not both.