/* Copyright (c) 2006-2010, ePages GmbH All Rights Reserved. */ dojo.provide("epages.widget.Tooltip"); dojo.require("epages.widget"); dojo.require("epages.string"); dojo.declare("epages.widget.Tooltip", [dijit._Widget, dijit._Templated], { /** * public properties */ disabledBodyClickHide : false, tooltipLayerFrame : undefined, tooltipAreaId : '', tooltipArea : undefined, tooltipNode : undefined, interactive : '', // String - Is the user able to use the content of the tooltip? i.e. if there's links in the tooltip text tooltipShowDelay : 0, tooltipHideDelay : 0, currentYPosition : 0, currentXPosition : 0, hideDelay : null, fadeDelay : null, showDelay : null, bgcolor : '', forceOrientation : '', // String - B - bottom, T - top , L - left, R - right (combinations are possible e.g BR) target : "default", findAreaMethod : "", /** * private properties */ _findAreaMethodMapping: { 'default' : 'parentNode', 'previous' : 'previousSibling', 'next' : 'nextSibling' }, // Object - which node should be used to connect to the tooltip activation events (mouseover-area) _doc : null, // DomNode - owner document, i.e. to calculate the widget position _bodyConnect : undefined, // to store event connect to document's body, i.e. for hiding the tooltip when clicked somewhere else on the page _windowConnect : undefined, // to store event connect to the window for capturing keypress events /** * dojo widget properties */ templateString : '<div class="Tooltip"><div dojoAttachPoint="containerNode"></div></div>', /** * dojo lifecycle methods */ postCreate: function() { // summary: widget lifecycle postCreate handler // description: // Initializes widget parameters and find dom element for displaying the tooltip widget // (see _findAreaMethodMapping values). Then create dom elements for the tooltip bubble // and connect the keyboard/mouse event handlers. this.inherited("postCreate", arguments); this.tooltipNode = this.domNode; // catch tooltip area node var tooltipArea = undefined; if(this.tooltipArea) { tooltipArea = this.tooltipArea; } else if(this.tooltipAreaId!="") { tooltipArea= $(this.tooltipAreaId); } else { var findAreaMethod = this._findAreaMethodMapping[this.findAreaMethod || this.target]; tooltipArea = this.domNode[findAreaMethod]; if(tooltipArea.nodeType!= 1) { if(findAreaMethod == 'previousSibling') { tooltipArea=tooltipArea.previousSibling; // skip spaces } if(findAreaMethod == 'nextSibling') { tooltipArea=tooltipArea.nextSibling; // skip spaces } } } if(this.bgcolor !=''){ this.domNode.style.backgroundColor = this.bgcolor; } if(!tooltipArea || tooltipArea.nodeType!=1 ){ console.warn('TooltipArea not found by using find method "'+this.findAreaMethod+'" in '+this.declaredClass); } else { this.tooltipArea = tooltipArea; this._doc = this.tooltipArea.ownerDocument; this.tooltipIsInteractive = epages.string.toBoolean(this.interactive); this.tooltipShowDelay = 600; this.tooltipHideDelay = this.tooltipIsInteractive ? 1400 : 400; this.tooltipLayerFrame = this._doc.createElement("div"); this.tooltipShadow = this._doc.createElement("div"); dojo.style(this.tooltipNode, "opacity", 0); this._bodyConnect = dojo.connect(dojo.body(),"onmouseup",this, "onBodyClick"); this._windowConnect = dojo.connect(window,"onkeypress", this,"onKeypress"); this.connect(this.tooltipArea, "onmousemove", "onElementMove"); this.connect(this.tooltipArea, "onmouseover", "onElementOver"); this.connect(this.tooltipArea, "onmouseout", "onElementOut"); this.connect(this.tooltipArea, "onmousedown", "onElementClick"); if(this.tooltipIsInteractive) { this.connect(this.tooltipNode, "onmouseover", "onTooltipOver"); this.connect(this.tooltipNode, "onmouseout", "onTooltipOut"); } } }, destroy: function(){ // summary: widget lifecycle destroy handler // description: // Disconnect event handlers to body and window of the widget-containing document. this.inherited("destroy", arguments); dojo.disconnect(this._bodyConnect); dojo.disconnect(this._windowConnect); }, /** * public methods */ setContent: function(/* string */ html) { this.containerNode.innerHTML=html; this.setPosition(); }, isActive: function() { // summary: Is the tooltip dom node visible? // returns: false if zero opacity, otherwise true return dojo.style(this.tooltipNode, "opacity") == 0? false : true; }, setPosition: function() { // summary: Move the tooltip // description: // Depending on the orientation mode set by the forceOrientation variable move the tooltip // domNode to the position determined by currentYPosition and currentXPosition. This depends // on the document parenting the tooltip node as well as possible window scroll bars. var forceRight = (this.forceOrientation.match('R')) ? true : false; var forceLeft = (this.forceOrientation.match('L')) ? true : false; var forceBottom = (this.forceOrientation.match('B')) ? true : false; var forceTop = (this.forceOrientation.match('T')) ? true : false; // horizontal position var docWidth = epages.Browser.engine == "WebKit" ? this._doc.body.clientWidth : this._doc.documentElement.clientWidth; var rightSideX = this.tooltipNode.offsetWidth + this.currentXPosition + 16 + 3 - (this._doc.documentElement.scrollLeft || this._doc.body.scrollLeft); // 16px = dist. from mouse ; 3px = shadow iframe if((rightSideX > docWidth && ! forceRight) || forceLeft) { var x = this.currentXPosition - this.tooltipNode.offsetWidth; if(forceLeft){ x-=16; } if(x < 0){ x=0; } this.tooltipNode.style.left = x + "px"; // layer > left align } else { var x = this.currentXPosition; if(forceRight){ x+=16; } this.tooltipNode.style.left = x + "px"; // layer > right align } // vertical position var docHeight = epages.Browser.engine == "WebKit" ? this._doc.body.clientHeight : this._doc.documentElement.clientHeight; var bottomSideX = this.tooltipNode.offsetHeight + this.currentYPosition + 16 + 3 - (this._doc.documentElement.scrollTop || this._doc.body.scrollTop); if((bottomSideX > docHeight && ! forceBottom) || forceTop) { // 16px = dist. from mouse ; 3px = shadow iframe var y = this.currentYPosition - this.tooltipNode.offsetHeight - 16; if(y < 0){ y=0; } this.tooltipNode.style.top = y + "px"; // layer > top align }else{ this.tooltipNode.style.top = this.currentYPosition + 16 + "px"; // layer > bottom align } this.tooltipLayerFrame.style.width = this.tooltipNode.offsetWidth + 2 + "px"; this.tooltipLayerFrame.style.height = this.tooltipNode.offsetHeight + 2 + "px"; }, show: function(/* int */ x, /* int */ y) { // summary: Set position & display the Tooltip // description: // Update the tooltip position and triggers the display of the tooltip. this.currentXPosition = x; this.currentYPosition = y; this.showTooltip(); }, showTooltip: function() { // summary: Display the Tooltip // description: // Hide the tooltip dom node before changing it. Then attach the layout classes and move the // tooltip frame to front. Finally attach the tooltip node to the dom tree and display it. dojo.addClass(this.tooltipNode, "HideElement"); if(!this.rendered){ dojo.addClass(this.tooltipNode, "Tooltip"); dojo.addClass(this.tooltipShadow, "TooltipShadow"); dojo.addClass(this.tooltipLayerFrame, "LayerIframe"); if(!this.tooltipIsInteractive) { this.tooltipLayerFrame.style.zIndex = "1"; this.tooltipNode.style.MozUserSelect = 'none'; } this.tooltipNode.appendChild(this.tooltipLayerFrame); // overlay this.tooltipNode.appendChild(this.tooltipShadow); this._doc.getElementsByTagName("body")[0].appendChild(this.tooltipNode); this.rendered = true; } var _this = this; if( this.fadeAction ) { this.fadeAction.stop(); } this.fadeAction = dojo.fadeIn({ node: this.tooltipNode, duration:200, beforeBegin: function() { _this.tooltipNode.style.display = "block"; _this.setPosition(); dojo.removeClass(_this.tooltipNode, "HideElement"); } }); this.fadeAction.play(); }, hide: function() { // summary: Hide the Tooltip // description: // After a fade, hide the tooltip. var _this = this; if( this.fadeAction ) { this.fadeAction.stop(); } this.fadeAction = dojo.fadeOut({ node:this.tooltipNode, duration: 200, onEnd: function() { _this.tooltipNode.style.display = "none"; } }); this.fadeAction.play(); }, /** * Event handlers */ onElementMove: function(mouseEvent) { if(!mouseEvent){ var mouseEvent = window.event; } this.currentYPosition = mouseEvent.clientY + (this._doc.documentElement.scrollTop || this._doc.body.scrollTop); this.currentXPosition = mouseEvent.clientX + (this._doc.documentElement.scrollLeft || this._doc.body.scrollLeft); }, onElementOver: function() { this.disabledBodyClickHide = true; this._resetTimeouts(); if(!this.isActive()) { this.showDelay = this._delayExec(this.showTooltip, this.tooltipShowDelay); } }, onElementOut: function() { this._resetTimeouts(); this.fadeDelay = this._delayExec(this.hide, this.tooltipHideDelay); this.disabledBodyClickHide = false; }, onElementClick: function() { this.disabledBodyClickHide = true; if(!this.isActive()) { this._resetTimeouts(); this.showTooltip(); } }, onTooltipOver: function() { this.disabledBodyClickHide = true; this._resetTimeouts(); }, onTooltipOut: function() { this.disabledBodyClickHide = false; this.onElementOut(); }, onBodyClick: function() { if(!this.disabledBodyClickHide && this.isActive()) { this._resetTimeouts(); this.hide(); } }, onKeypress: function(evt) { if(evt.keyCode == dojo.keys.ESCAPE) { this._resetTimeouts(); this.hide(); } }, /** * private methods */ _delayExec: function(task, delay) { return setTimeout(dojo.hitch(this, task), delay); }, _resetTimeouts: function() { clearTimeout(this.hideDelay); clearTimeout(this.fadeDelay); clearTimeout(this.showDelay); } } );