]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/Tooltip.js
remove call-by-reference to comply with php 5.4
[tt-rss.git] / lib / dijit / Tooltip.js
CommitLineData
2f01fe57 1/*
81bea17a 2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
2f01fe57
AD
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
5*/
6
7
81bea17a
AD
8if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9dojo._hasResource["dijit.Tooltip"] = true;
2f01fe57
AD
10dojo.provide("dijit.Tooltip");
11dojo.require("dijit._Widget");
12dojo.require("dijit._Templated");
81bea17a
AD
13
14
15dojo.declare(
16 "dijit._MasterTooltip",
17 [dijit._Widget, dijit._Templated],
18 {
19 // summary:
20 // Internal widget that holds the actual tooltip markup,
21 // which occurs once per page.
22 // Called by Tooltip widgets which are just containers to hold
23 // the markup
24 // tags:
25 // protected
26
27 // duration: Integer
28 // Milliseconds to fade in/fade out
29 duration: dijit.defaultDuration,
30
31 templateString: dojo.cache("dijit", "templates/Tooltip.html", "<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" dojoAttachPoint=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" dojoAttachPoint=\"connectorNode\"></div\n></div>\n"),
32
33 postCreate: function(){
34 dojo.body().appendChild(this.domNode);
35
36 this.bgIframe = new dijit.BackgroundIframe(this.domNode);
37
38 // Setup fade-in and fade-out functions.
39 this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
40 this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
41 },
42
43 show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
44 // summary:
45 // Display tooltip w/specified contents to right of specified node
46 // (To left if there's no space on the right, or if rtl == true)
47
48 if(this.aroundNode && this.aroundNode === aroundNode){
49 return;
50 }
51
52 // reset width; it may have been set by orient() on a previous tooltip show()
53 this.domNode.width = "auto";
54
55 if(this.fadeOut.status() == "playing"){
56 // previous tooltip is being hidden; wait until the hide completes then show new one
57 this._onDeck=arguments;
58 return;
59 }
60 this.containerNode.innerHTML=innerHTML;
61
62 var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
63
64 // show it
65 dojo.style(this.domNode, "opacity", 0);
66 this.fadeIn.play();
67 this.isShowingNow = true;
68 this.aroundNode = aroundNode;
69 },
70
71 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
72 // summary:
73 // Private function to set CSS for tooltip node based on which position it's in.
74 // This is called by the dijit popup code. It will also reduce the tooltip's
75 // width to whatever width is available
76 // tags:
77 // protected
78 this.connectorNode.style.top = ""; //reset to default
79
80 //Adjust the spaceAvailable width, without changing the spaceAvailable object
81 var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
82
83 node.className = "dijitTooltip " +
84 {
85 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
86 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
87 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
88 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
89 "BR-BL": "dijitTooltipRight",
90 "BL-BR": "dijitTooltipLeft"
91 }[aroundCorner + "-" + tooltipCorner];
92
93 // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
94 this.domNode.style.width = "auto";
95 var size = dojo.contentBox(this.domNode);
96
97 var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
98 var widthWasReduced = width < size.w;
99
100 this.domNode.style.width = width+"px";
101
102 //Adjust width for tooltips that have a really long word or a nowrap setting
103 if(widthWasReduced){
104 this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
105 var scrollWidth = this.containerNode.scrollWidth;
106 this.containerNode.style.overflow = "visible"; //change it back
107 if(scrollWidth > width){
108 scrollWidth = scrollWidth + dojo.style(this.domNode,"paddingLeft") + dojo.style(this.domNode,"paddingRight");
109 this.domNode.style.width = scrollWidth + "px";
110 }
111 }
112
113 // Reposition the tooltip connector.
114 if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
115 var mb = dojo.marginBox(node);
116 var tooltipConnectorHeight = this.connectorNode.offsetHeight;
117 if(mb.h > spaceAvailable.h){
118 // The tooltip starts at the top of the page and will extend past the aroundNode
119 var aroundNodePlacement = spaceAvailable.h - (aroundNodeCoords.h / 2) - (tooltipConnectorHeight / 2);
120 this.connectorNode.style.top = aroundNodePlacement + "px";
121 this.connectorNode.style.bottom = "";
122 }else{
123 // Align center of connector with center of aroundNode, except don't let bottom
124 // of connector extend below bottom of tooltip content, or top of connector
125 // extend past top of tooltip content
126 this.connectorNode.style.bottom = Math.min(
127 Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
128 mb.h - tooltipConnectorHeight) + "px";
129 this.connectorNode.style.top = "";
130 }
131 }else{
132 // reset the tooltip back to the defaults
133 this.connectorNode.style.top = "";
134 this.connectorNode.style.bottom = "";
135 }
136
137 return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
138 },
139
140 _onShow: function(){
141 // summary:
142 // Called at end of fade-in operation
143 // tags:
144 // protected
145 if(dojo.isIE){
146 // the arrow won't show up on a node w/an opacity filter
147 this.domNode.style.filter="";
148 }
149 },
150
151 hide: function(aroundNode){
152 // summary:
153 // Hide the tooltip
154
155 if(this._onDeck && this._onDeck[1] == aroundNode){
156 // this hide request is for a show() that hasn't even started yet;
157 // just cancel the pending show()
158 this._onDeck=null;
159 }else if(this.aroundNode === aroundNode){
160 // this hide request is for the currently displayed tooltip
161 this.fadeIn.stop();
162 this.isShowingNow = false;
163 this.aroundNode = null;
164 this.fadeOut.play();
165 }else{
166 // just ignore the call, it's for a tooltip that has already been erased
167 }
168 },
169
170 _onHide: function(){
171 // summary:
172 // Called at end of fade-out operation
173 // tags:
174 // protected
175
176 this.domNode.style.cssText=""; // to position offscreen again
177 this.containerNode.innerHTML="";
178 if(this._onDeck){
179 // a show request has been queued up; do it now
180 this.show.apply(this, this._onDeck);
181 this._onDeck=null;
182 }
183 }
184
185 }
186);
187
188dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
189 // summary:
190 // Display tooltip w/specified contents in specified position.
191 // See description of dijit.Tooltip.defaultPosition for details on position parameter.
192 // If position is not specified then dijit.Tooltip.defaultPosition is used.
193 if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
194 return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
2f01fe57 195};
81bea17a
AD
196
197dijit.hideTooltip = function(aroundNode){
198 // summary:
199 // Hide the tooltip
200 if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
201 return dijit._masterTT.hide(aroundNode);
2f01fe57 202};
81bea17a
AD
203
204dojo.declare(
205 "dijit.Tooltip",
206 dijit._Widget,
207 {
208 // summary:
209 // Pops up a tooltip (a help message) when you hover over a node.
210
211 // label: String
212 // Text to display in the tooltip.
213 // Specified as innerHTML when creating the widget from markup.
214 label: "",
215
216 // showDelay: Integer
217 // Number of milliseconds to wait after hovering over/focusing on the object, before
218 // the tooltip is displayed.
219 showDelay: 400,
220
221 // connectId: String|String[]
222 // Id of domNode(s) to attach the tooltip to.
223 // When user hovers over specified dom node, the tooltip will appear.
224 connectId: [],
225
226 // position: String[]
227 // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
228 position: [],
229
230 _setConnectIdAttr: function(/*String*/ newId){
231 // summary:
232 // Connect to node(s) (specified by id)
233
234 // Remove connections to old nodes (if there are any)
235 dojo.forEach(this._connections || [], function(nested){
236 dojo.forEach(nested, dojo.hitch(this, "disconnect"));
237 }, this);
238
239 // Make connections to nodes in newIds.
240 var ary = dojo.isArrayLike(newId) ? newId : (newId ? [newId] : []);
241 this._connections = dojo.map(ary, function(id){
242 var node = dojo.byId(id);
243 return node ? [
244 this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
245 this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
246 this.connect(node, "onfocus", "_onTargetFocus"),
247 this.connect(node, "onblur", "_onTargetBlur")
248 ] : [];
249 }, this);
250
251 this._set("connectId", newId);
252
253 this._connectIds = ary; // save as array
254 },
255
256 addTarget: function(/*DOMNODE || String*/ node){
257 // summary:
258 // Attach tooltip to specified node if it's not already connected
259
260 // TODO: remove in 2.0 and just use set("connectId", ...) interface
261
262 var id = node.id || node;
263 if(dojo.indexOf(this._connectIds, id) == -1){
264 this.set("connectId", this._connectIds.concat(id));
265 }
266 },
267
268 removeTarget: function(/*DOMNODE || String*/ node){
269 // summary:
270 // Detach tooltip from specified node
271
272 // TODO: remove in 2.0 and just use set("connectId", ...) interface
273
274 var id = node.id || node, // map from DOMNode back to plain id string
275 idx = dojo.indexOf(this._connectIds, id);
276 if(idx >= 0){
277 // remove id (modifies original this._connectIds but that's OK in this case)
278 this._connectIds.splice(idx, 1);
279 this.set("connectId", this._connectIds);
280 }
281 },
282
283 buildRendering: function(){
284 this.inherited(arguments);
285 dojo.addClass(this.domNode,"dijitTooltipData");
286 },
287
288 startup: function(){
289 this.inherited(arguments);
290
291 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
292 // didn't exist during the widget's initialization, then connect now.
293 var ids = this.connectId;
294 dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
295 },
296
297 _onTargetMouseEnter: function(/*Event*/ e){
298 // summary:
299 // Handler for mouseenter event on the target node
300 // tags:
301 // private
302 this._onHover(e);
303 },
304
305 _onTargetMouseLeave: function(/*Event*/ e){
306 // summary:
307 // Handler for mouseleave event on the target node
308 // tags:
309 // private
310 this._onUnHover(e);
311 },
312
313 _onTargetFocus: function(/*Event*/ e){
314 // summary:
315 // Handler for focus event on the target node
316 // tags:
317 // private
318
319 this._focus = true;
320 this._onHover(e);
321 },
322
323 _onTargetBlur: function(/*Event*/ e){
324 // summary:
325 // Handler for blur event on the target node
326 // tags:
327 // private
328
329 this._focus = false;
330 this._onUnHover(e);
331 },
332
333 _onHover: function(/*Event*/ e){
334 // summary:
335 // Despite the name of this method, it actually handles both hover and focus
336 // events on the target node, setting a timer to show the tooltip.
337 // tags:
338 // private
339 if(!this._showTimer){
340 var target = e.target;
341 this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
342 }
343 },
344
345 _onUnHover: function(/*Event*/ e){
346 // summary:
347 // Despite the name of this method, it actually handles both mouseleave and blur
348 // events on the target node, hiding the tooltip.
349 // tags:
350 // private
351
352 // keep a tooltip open if the associated element still has focus (even though the
353 // mouse moved away)
354 if(this._focus){ return; }
355
356 if(this._showTimer){
357 clearTimeout(this._showTimer);
358 delete this._showTimer;
359 }
360 this.close();
361 },
362
363 open: function(/*DomNode*/ target){
364 // summary:
365 // Display the tooltip; usually not called directly.
366 // tags:
367 // private
368
369 if(this._showTimer){
370 clearTimeout(this._showTimer);
371 delete this._showTimer;
372 }
373 dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
374
375 this._connectNode = target;
376 this.onShow(target, this.position);
377 },
378
379 close: function(){
380 // summary:
381 // Hide the tooltip or cancel timer for show of tooltip
382 // tags:
383 // private
384
385 if(this._connectNode){
386 // if tooltip is currently shown
387 dijit.hideTooltip(this._connectNode);
388 delete this._connectNode;
389 this.onHide();
390 }
391 if(this._showTimer){
392 // if tooltip is scheduled to be shown (after a brief delay)
393 clearTimeout(this._showTimer);
394 delete this._showTimer;
395 }
396 },
397
398 onShow: function(target, position){
399 // summary:
400 // Called when the tooltip is shown
401 // tags:
402 // callback
403 },
404
405 onHide: function(){
406 // summary:
407 // Called when the tooltip is hidden
408 // tags:
409 // callback
410 },
411
412 uninitialize: function(){
413 this.close();
414 this.inherited(arguments);
415 }
416 }
417);
418
419// dijit.Tooltip.defaultPosition: String[]
420// This variable controls the position of tooltips, if the position is not specified to
421// the Tooltip widget or *TextBox widget itself. It's an array of strings with the following values:
422//
423// * before: places tooltip to the left of the target node/widget, or to the right in
424// the case of RTL scripts like Hebrew and Arabic
425// * after: places tooltip to the right of the target node/widget, or to the left in
426// the case of RTL scripts like Hebrew and Arabic
427// * above: tooltip goes above target node
428// * below: tooltip goes below target node
429//
430// The list is positions is tried, in order, until a position is found where the tooltip fits
431// within the viewport.
432//
433// Be careful setting this parameter. A value of "above" may work fine until the user scrolls
434// the screen so that there's no room above the target node. Nodes with drop downs, like
435// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
436// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
437// is only room below (or above) the target node, but not both.
438dijit.Tooltip.defaultPosition = ["after", "before"];
439
2f01fe57 440}