499 lines
18 KiB
JavaScript
499 lines
18 KiB
JavaScript
require({cache:{
|
|
'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"}});
|
|
define("dijit/Tooltip", [
|
|
"dojo/_base/array", // array.forEach array.indexOf array.map
|
|
"dojo/_base/declare", // declare
|
|
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
|
|
"dojo/dom", // dom.byId
|
|
"dojo/dom-class", // domClass.add
|
|
"dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
|
|
"dojo/dom-style", // domStyle.set, domStyle.get
|
|
"dojo/_base/lang", // lang.hitch lang.isArrayLike
|
|
"dojo/_base/sniff", // has("ie")
|
|
"dojo/_base/window", // win.body
|
|
"./_base/manager", // manager.defaultDuration
|
|
"./place",
|
|
"./_Widget",
|
|
"./_TemplatedMixin",
|
|
"./BackgroundIframe",
|
|
"dojo/text!./templates/Tooltip.html",
|
|
"." // sets dijit.showTooltip etc. for back-compat
|
|
], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, has, win,
|
|
manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
|
|
|
|
/*=====
|
|
var _Widget = dijit._Widget;
|
|
var BackgroundIframe = dijit.BackgroundIframe;
|
|
var _TemplatedMixin = dijit._TemplatedMixin;
|
|
=====*/
|
|
|
|
// module:
|
|
// dijit/Tooltip
|
|
// summary:
|
|
// Defines dijit.Tooltip widget (to display a tooltip), showTooltip()/hideTooltip(), and _MasterTooltip
|
|
|
|
|
|
var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
|
|
// summary:
|
|
// Internal widget that holds the actual tooltip markup,
|
|
// which occurs once per page.
|
|
// Called by Tooltip widgets which are just containers to hold
|
|
// the markup
|
|
// tags:
|
|
// protected
|
|
|
|
// duration: Integer
|
|
// Milliseconds to fade in/fade out
|
|
duration: manager.defaultDuration,
|
|
|
|
templateString: template,
|
|
|
|
postCreate: function(){
|
|
win.body().appendChild(this.domNode);
|
|
|
|
this.bgIframe = new BackgroundIframe(this.domNode);
|
|
|
|
// Setup fade-in and fade-out functions.
|
|
this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
|
|
this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
|
|
},
|
|
|
|
show: function(innerHTML, aroundNode, position, rtl, textDir){
|
|
// summary:
|
|
// Display tooltip w/specified contents to right of specified node
|
|
// (To left if there's no space on the right, or if rtl == true)
|
|
// innerHTML: String
|
|
// Contents of the tooltip
|
|
// aroundNode: DomNode || dijit.__Rectangle
|
|
// Specifies that tooltip should be next to this node / area
|
|
// position: String[]?
|
|
// List of positions to try to position tooltip (ex: ["right", "above"])
|
|
// rtl: Boolean?
|
|
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
|
|
// means "rtl"; specifies GUI direction, not text direction.
|
|
// textDir: String?
|
|
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
|
|
|
|
|
|
if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
|
|
return;
|
|
}
|
|
|
|
// reset width; it may have been set by orient() on a previous tooltip show()
|
|
this.domNode.width = "auto";
|
|
|
|
if(this.fadeOut.status() == "playing"){
|
|
// previous tooltip is being hidden; wait until the hide completes then show new one
|
|
this._onDeck=arguments;
|
|
return;
|
|
}
|
|
this.containerNode.innerHTML=innerHTML;
|
|
|
|
this.set("textDir", textDir);
|
|
this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
|
|
|
|
var pos = place.around(this.domNode, aroundNode,
|
|
position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
|
|
|
|
// Position the tooltip connector for middle alignment.
|
|
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
|
|
var aroundNodeCoords = pos.aroundNodePos;
|
|
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
|
|
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
|
|
this.connectorNode.style.left = "";
|
|
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
|
|
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
|
|
}
|
|
|
|
// show it
|
|
domStyle.set(this.domNode, "opacity", 0);
|
|
this.fadeIn.play();
|
|
this.isShowingNow = true;
|
|
this.aroundNode = aroundNode;
|
|
},
|
|
|
|
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
|
|
// summary:
|
|
// Private function to set CSS for tooltip node based on which position it's in.
|
|
// This is called by the dijit popup code. It will also reduce the tooltip's
|
|
// width to whatever width is available
|
|
// tags:
|
|
// protected
|
|
this.connectorNode.style.top = ""; //reset to default
|
|
|
|
//Adjust the spaceAvailable width, without changing the spaceAvailable object
|
|
var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
|
|
|
|
node.className = "dijitTooltip " +
|
|
{
|
|
"MR-ML": "dijitTooltipRight",
|
|
"ML-MR": "dijitTooltipLeft",
|
|
"TM-BM": "dijitTooltipAbove",
|
|
"BM-TM": "dijitTooltipBelow",
|
|
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
|
|
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
|
|
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
|
|
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
|
|
"BR-BL": "dijitTooltipRight",
|
|
"BL-BR": "dijitTooltipLeft"
|
|
}[aroundCorner + "-" + tooltipCorner];
|
|
|
|
// reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
|
|
this.domNode.style.width = "auto";
|
|
var size = domGeometry.getContentBox(this.domNode);
|
|
|
|
var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
|
|
var widthWasReduced = width < size.w;
|
|
|
|
this.domNode.style.width = width+"px";
|
|
|
|
//Adjust width for tooltips that have a really long word or a nowrap setting
|
|
if(widthWasReduced){
|
|
this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
|
|
var scrollWidth = this.containerNode.scrollWidth;
|
|
this.containerNode.style.overflow = "visible"; //change it back
|
|
if(scrollWidth > width){
|
|
scrollWidth = scrollWidth + domStyle.get(this.domNode,"paddingLeft") + domStyle.get(this.domNode,"paddingRight");
|
|
this.domNode.style.width = scrollWidth + "px";
|
|
}
|
|
}
|
|
|
|
// Reposition the tooltip connector.
|
|
if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
|
|
var mb = domGeometry.getMarginBox(node);
|
|
var tooltipConnectorHeight = this.connectorNode.offsetHeight;
|
|
if(mb.h > spaceAvailable.h){
|
|
// The tooltip starts at the top of the page and will extend past the aroundNode
|
|
var aroundNodePlacement = spaceAvailable.h - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
|
|
this.connectorNode.style.top = aroundNodePlacement + "px";
|
|
this.connectorNode.style.bottom = "";
|
|
}else{
|
|
// Align center of connector with center of aroundNode, except don't let bottom
|
|
// of connector extend below bottom of tooltip content, or top of connector
|
|
// extend past top of tooltip content
|
|
this.connectorNode.style.bottom = Math.min(
|
|
Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
|
|
mb.h - tooltipConnectorHeight) + "px";
|
|
this.connectorNode.style.top = "";
|
|
}
|
|
}else{
|
|
// reset the tooltip back to the defaults
|
|
this.connectorNode.style.top = "";
|
|
this.connectorNode.style.bottom = "";
|
|
}
|
|
|
|
return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
|
|
},
|
|
|
|
_onShow: function(){
|
|
// summary:
|
|
// Called at end of fade-in operation
|
|
// tags:
|
|
// protected
|
|
if(has("ie")){
|
|
// the arrow won't show up on a node w/an opacity filter
|
|
this.domNode.style.filter="";
|
|
}
|
|
},
|
|
|
|
hide: function(aroundNode){
|
|
// summary:
|
|
// Hide the tooltip
|
|
|
|
if(this._onDeck && this._onDeck[1] == aroundNode){
|
|
// this hide request is for a show() that hasn't even started yet;
|
|
// just cancel the pending show()
|
|
this._onDeck=null;
|
|
}else if(this.aroundNode === aroundNode){
|
|
// this hide request is for the currently displayed tooltip
|
|
this.fadeIn.stop();
|
|
this.isShowingNow = false;
|
|
this.aroundNode = null;
|
|
this.fadeOut.play();
|
|
}else{
|
|
// just ignore the call, it's for a tooltip that has already been erased
|
|
}
|
|
},
|
|
|
|
_onHide: function(){
|
|
// summary:
|
|
// Called at end of fade-out operation
|
|
// tags:
|
|
// protected
|
|
|
|
this.domNode.style.cssText=""; // to position offscreen again
|
|
this.containerNode.innerHTML="";
|
|
if(this._onDeck){
|
|
// a show request has been queued up; do it now
|
|
this.show.apply(this, this._onDeck);
|
|
this._onDeck=null;
|
|
}
|
|
},
|
|
|
|
_setAutoTextDir: function(/*Object*/node){
|
|
// summary:
|
|
// Resolve "auto" text direction for children nodes
|
|
// tags:
|
|
// private
|
|
|
|
this.applyTextDir(node, has("ie") ? node.outerText : node.textContent);
|
|
array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this);
|
|
},
|
|
|
|
_setTextDirAttr: function(/*String*/ textDir){
|
|
// summary:
|
|
// Setter for textDir.
|
|
// description:
|
|
// Users shouldn't call this function; they should be calling
|
|
// set('textDir', value)
|
|
// tags:
|
|
// private
|
|
|
|
this._set("textDir", typeof textDir != 'undefined'? textDir : "");
|
|
if (textDir == "auto"){
|
|
this._setAutoTextDir(this.containerNode);
|
|
}else{
|
|
this.containerNode.dir = this.textDir;
|
|
}
|
|
}
|
|
});
|
|
|
|
dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){
|
|
// summary:
|
|
// Static method to display tooltip w/specified contents in specified position.
|
|
// See description of dijit.Tooltip.defaultPosition for details on position parameter.
|
|
// If position is not specified then dijit.Tooltip.defaultPosition is used.
|
|
// innerHTML: String
|
|
// Contents of the tooltip
|
|
// aroundNode: dijit.__Rectangle
|
|
// Specifies that tooltip should be next to this node / area
|
|
// position: String[]?
|
|
// List of positions to try to position tooltip (ex: ["right", "above"])
|
|
// rtl: Boolean?
|
|
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
|
|
// means "rtl"; specifies GUI direction, not text direction.
|
|
// textDir: String?
|
|
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
|
|
|
|
// after/before don't work, but they used to, so for back-compat convert them to after-centered, before-centered
|
|
if(position){
|
|
position = array.map(position, function(val){
|
|
return {after: "after-centered", before: "before-centered"}[val] || val;
|
|
});
|
|
}
|
|
|
|
if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
|
|
return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir);
|
|
};
|
|
|
|
dijit.hideTooltip = function(aroundNode){
|
|
// summary:
|
|
// Static method to hide the tooltip displayed via showTooltip()
|
|
return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
|
|
};
|
|
|
|
var Tooltip = declare("dijit.Tooltip", _Widget, {
|
|
// summary:
|
|
// Pops up a tooltip (a help message) when you hover over a node.
|
|
|
|
// label: String
|
|
// Text to display in the tooltip.
|
|
// Specified as innerHTML when creating the widget from markup.
|
|
label: "",
|
|
|
|
// showDelay: Integer
|
|
// Number of milliseconds to wait after hovering over/focusing on the object, before
|
|
// the tooltip is displayed.
|
|
showDelay: 400,
|
|
|
|
// connectId: String|String[]
|
|
// Id of domNode(s) to attach the tooltip to.
|
|
// When user hovers over specified dom node, the tooltip will appear.
|
|
connectId: [],
|
|
|
|
// position: String[]
|
|
// See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
|
|
position: [],
|
|
|
|
_setConnectIdAttr: function(/*String|String[]*/ newId){
|
|
// summary:
|
|
// Connect to specified node(s)
|
|
|
|
// Remove connections to old nodes (if there are any)
|
|
array.forEach(this._connections || [], function(nested){
|
|
array.forEach(nested, lang.hitch(this, "disconnect"));
|
|
}, this);
|
|
|
|
// Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
|
|
this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
|
|
function(id){ return dom.byId(id); });
|
|
|
|
// Make connections
|
|
this._connections = array.map(this._connectIds, function(id){
|
|
var node = dom.byId(id);
|
|
return [
|
|
this.connect(node, "onmouseenter", "_onHover"),
|
|
this.connect(node, "onmouseleave", "_onUnHover"),
|
|
this.connect(node, "onfocus", "_onHover"),
|
|
this.connect(node, "onblur", "_onUnHover")
|
|
];
|
|
}, this);
|
|
|
|
this._set("connectId", newId);
|
|
},
|
|
|
|
addTarget: function(/*DOMNODE || String*/ node){
|
|
// summary:
|
|
// Attach tooltip to specified node if it's not already connected
|
|
|
|
// TODO: remove in 2.0 and just use set("connectId", ...) interface
|
|
|
|
var id = node.id || node;
|
|
if(array.indexOf(this._connectIds, id) == -1){
|
|
this.set("connectId", this._connectIds.concat(id));
|
|
}
|
|
},
|
|
|
|
removeTarget: function(/*DomNode || String*/ node){
|
|
// summary:
|
|
// Detach tooltip from specified node
|
|
|
|
// TODO: remove in 2.0 and just use set("connectId", ...) interface
|
|
|
|
var id = node.id || node, // map from DOMNode back to plain id string
|
|
idx = array.indexOf(this._connectIds, id);
|
|
if(idx >= 0){
|
|
// remove id (modifies original this._connectIds but that's OK in this case)
|
|
this._connectIds.splice(idx, 1);
|
|
this.set("connectId", this._connectIds);
|
|
}
|
|
},
|
|
|
|
buildRendering: function(){
|
|
this.inherited(arguments);
|
|
domClass.add(this.domNode,"dijitTooltipData");
|
|
},
|
|
|
|
startup: function(){
|
|
this.inherited(arguments);
|
|
|
|
// If this tooltip was created in a template, or for some other reason the specified connectId[s]
|
|
// didn't exist during the widget's initialization, then connect now.
|
|
var ids = this.connectId;
|
|
array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
|
|
},
|
|
|
|
_onHover: function(/*Event*/ e){
|
|
// summary:
|
|
// Despite the name of this method, it actually handles both hover and focus
|
|
// events on the target node, setting a timer to show the tooltip.
|
|
// tags:
|
|
// private
|
|
if(!this._showTimer){
|
|
var target = e.target;
|
|
this._showTimer = setTimeout(lang.hitch(this, function(){this.open(target)}), this.showDelay);
|
|
}
|
|
},
|
|
|
|
_onUnHover: function(/*Event*/ /*===== e =====*/){
|
|
// summary:
|
|
// Despite the name of this method, it actually handles both mouseleave and blur
|
|
// events on the target node, hiding the tooltip.
|
|
// tags:
|
|
// private
|
|
|
|
// keep a tooltip open if the associated element still has focus (even though the
|
|
// mouse moved away)
|
|
if(this._focus){ return; }
|
|
|
|
if(this._showTimer){
|
|
clearTimeout(this._showTimer);
|
|
delete this._showTimer;
|
|
}
|
|
this.close();
|
|
},
|
|
|
|
open: function(/*DomNode*/ target){
|
|
// summary:
|
|
// Display the tooltip; usually not called directly.
|
|
// tags:
|
|
// private
|
|
|
|
if(this._showTimer){
|
|
clearTimeout(this._showTimer);
|
|
delete this._showTimer;
|
|
}
|
|
Tooltip.show(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight(), this.textDir);
|
|
|
|
this._connectNode = target;
|
|
this.onShow(target, this.position);
|
|
},
|
|
|
|
close: function(){
|
|
// summary:
|
|
// Hide the tooltip or cancel timer for show of tooltip
|
|
// tags:
|
|
// private
|
|
|
|
if(this._connectNode){
|
|
// if tooltip is currently shown
|
|
Tooltip.hide(this._connectNode);
|
|
delete this._connectNode;
|
|
this.onHide();
|
|
}
|
|
if(this._showTimer){
|
|
// if tooltip is scheduled to be shown (after a brief delay)
|
|
clearTimeout(this._showTimer);
|
|
delete this._showTimer;
|
|
}
|
|
},
|
|
|
|
onShow: function(/*===== target, position =====*/){
|
|
// summary:
|
|
// Called when the tooltip is shown
|
|
// tags:
|
|
// callback
|
|
},
|
|
|
|
onHide: function(){
|
|
// summary:
|
|
// Called when the tooltip is hidden
|
|
// tags:
|
|
// callback
|
|
},
|
|
|
|
uninitialize: function(){
|
|
this.close();
|
|
this.inherited(arguments);
|
|
}
|
|
});
|
|
|
|
Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
|
|
Tooltip.show = dijit.showTooltip; // export function through module return value
|
|
Tooltip.hide = dijit.hideTooltip; // export function through module return value
|
|
|
|
// dijit.Tooltip.defaultPosition: String[]
|
|
// This variable controls the position of tooltips, if the position is not specified to
|
|
// the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
|
|
// possible for `dijit/place::around()`. The recommended values are:
|
|
//
|
|
// * before-centered: centers tooltip to the left of the anchor node/widget, or to the right
|
|
// in the case of RTL scripts like Hebrew and Arabic
|
|
// * after-centered: centers tooltip to the right of the anchor node/widget, or to the left
|
|
// in the case of RTL scripts like Hebrew and Arabic
|
|
// * above-centered: tooltip is centered above anchor node
|
|
// * below-centered: tooltip is centered above anchor node
|
|
//
|
|
// The list is positions is tried, in order, until a position is found where the tooltip fits
|
|
// within the viewport.
|
|
//
|
|
// Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
|
|
// the screen so that there's no room above the target node. Nodes with drop downs, like
|
|
// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
|
|
// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
|
|
// is only room below (or above) the target node, but not both.
|
|
Tooltip.defaultPosition = ["after-centered", "before-centered"];
|
|
|
|
|
|
return Tooltip;
|
|
});
|