1 /*
  2  * JBoss, Home of Professional Open Source
  3  * Copyright ${year}, Red Hat, Inc. and individual contributors
  4  * by the @authors tag. See the copyright.txt in the distribution for a
  5  * full listing of individual contributors.
  6  *
  7  * This is free software; you can redistribute it and/or modify it
  8  * under the terms of the GNU Lesser General Public License as
  9  * published by the Free Software Foundation; either version 2.1 of
 10  * the License, or (at your option) any later version.
 11  *
 12  * This software is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15  * Lesser General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU Lesser General Public
 18  * License along with this software; if not, write to the Free
 19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21  */
 22 
 23 
 24 (function ($, rf) {
 25 
 26     rf.ui = rf.ui || {};
 27 
 28     rf.ui.TooltipMode = {
 29         client : "client",
 30         ajax : "ajax",
 31         DEFAULT: "client"
 32     };
 33 
 34     var TooltipMode = rf.ui.TooltipMode;
 35 
 36     var DEFAULT_OPTIONS = {
 37         jointPoint : "AA",
 38         direction : "AA",
 39         offset : [10, 10],
 40         attached : true,
 41         mode : TooltipMode.DEFAULT,
 42         hideDelay : 0,
 43         hideEvent : "mouseleave",
 44         showDelay : 0,
 45         showEvent : "mouseenter",
 46         followMouse : true
 47     };
 48 
 49     var SHOW_ACTION = {
 50 
 51         /**
 52          *
 53          * @return {void}
 54          * */
 55         exec : function (tooltip, event) {
 56             var mode = tooltip.mode;
 57             if (mode == TooltipMode.ajax) {
 58                 return this.execAjax(tooltip, event);
 59             } else if (mode == TooltipMode.client) {
 60                 return this.execClient(tooltip, event);
 61             } else {
 62                 rf.log.error("SHOW_ACTION.exec : unknown mode (" + mode + ")");
 63             }
 64         },
 65 
 66         /**
 67          * @protected
 68          *
 69          * @return {Boolean} false
 70          * */
 71         execAjax : function (tooltip, event) {
 72             tooltip.__loading().show();
 73             tooltip.__content().hide();
 74             tooltip.__show(event);
 75 
 76             rf.ajax(tooltip.id, null, $.extend({}, tooltip.options["ajax"], {}));
 77 
 78             return true;
 79         },
 80 
 81         /**
 82          * @protected
 83          *
 84          * @return {undefined}
 85          *             - false - if process has been terminated
 86          *             - true  - in other cases
 87          * */
 88         execClient : function (tooltip, event) {
 89             tooltip.__show(event);
 90 
 91             return tooltip.__fireShow();
 92         }
 93     };
 94 
 95     rf.ui.Tooltip = rf.BaseComponent.extendClass({
 96             // class name
 97             name:"Tooltip",
 98 
 99             /**
100              * @class Tooltip
101              * @name Tooltip
102              *
103              * @constructor
104              * @param {String} componentId - component id
105              * */
106             init : function (componentId, options) {
107                 $super.constructor.call(this, componentId);
108                 this.namespace = "." + rf.Event.createNamespace(this.name, this.id);
109                 this.options = $.extend(this.options, DEFAULT_OPTIONS, options || {});
110                 this.attachToDom();
111 
112                 this.mode = this.options.mode;
113                 this.target = this.options.target;
114                 this.shown = false;
115 
116                 this.__addUserEventHandler("hide");
117                 this.__addUserEventHandler("show");
118                 this.__addUserEventHandler("beforehide");
119                 this.__addUserEventHandler("beforeshow");
120                 this.popupId = this.id + ':wrp';
121                 this.popup = new rf.ui.Popup(this.popupId, {
122                         attachTo: this.target,
123                         attachToBody: true,
124                         positionType: "TOOLTIP",
125                         positionOffset: this.options.offset,
126                         jointPoint: this.options.jointPoint,
127                         direction: this.options.direction
128                     });
129 
130                 var handlers = {};
131                 handlers[this.options.showEvent + this.namespace] = this.__showHandler;
132                 handlers[this.options.hideEvent + this.namespace] = this.__hideHandler;
133 
134                 rf.Event.bindById(this.target, handlers, this);
135 
136                 if (this.options.hideEvent == 'mouseleave') {
137                     rf.Event.bindById(this.popupId, this.options.hideEvent + this.namespace, this.__hideHandler, this);
138                 }
139             },
140 
141             /***************************** Public Methods  ****************************************************************/
142             /**
143              * @methodOf
144              * @name PanelMenuItem#hide
145              *
146              * TODO ...
147              *
148              * @return {void} TODO ...
149              */
150             hide: function () {
151 
152                 var tooltip = this;
153                 if (tooltip.hidingTimerHandle) {
154                     window.clearTimeout(tooltip.hidingTimerHandle);
155                     tooltip.hidingTimerHandle = undefined;
156                 }
157                 if (this.shown) {
158                     this.__hide();
159                 }
160             },
161 
162             __hideHandler: function(event) {
163                 if (event.type == 'mouseleave' && this.__isInside(event.relatedTarget)) {
164                     return;
165                 }
166 
167                 this.hide();
168 
169                 if (this.options.followMouse) {
170                     rf.Event.unbindById(this.target, "mousemove" + this.namespace);
171                 }
172 
173             },
174 
175             /**
176              * @private
177              * @return {void} TODO ...
178              */
179             __hide: function () {
180                 var tooltip = this;
181                 this.__delay(this.options.hideDelay, function () {
182                     tooltip.__fireBeforeHide();
183                     tooltip.popup.hide();
184                     tooltip.shown = false;
185                     tooltip.__fireHide();
186                 });
187             },
188 
189             __mouseMoveHandler: function(event) {
190                 this.saveShowEvent = event;
191                 if (this.shown) {
192                     this.popup.show(this.saveShowEvent);
193                 }
194             },
195 
196             __showHandler: function(event) {
197                 this.show(event);
198                 var tooltip = this;
199 
200                 if (tooltip.options.followMouse) {
201                     rf.Event.bindById(tooltip.target, "mousemove" + tooltip.namespace, tooltip.__mouseMoveHandler, tooltip);
202                 }
203             },
204 
205             /**
206              * @methodOf
207              * @name PanelMenuItem#show
208              *
209              * TODO ...
210              *
211              * @return {void} TODO ...
212              */
213             show: function (event) {
214                 var tooltip = this;
215                 if (tooltip.hidingTimerHandle) {
216                     window.clearTimeout(tooltip.hidingTimerHandle);
217                     tooltip.hidingTimerHandle = undefined;
218                 }
219 
220                 if (!this.shown) {
221                     SHOW_ACTION.exec(this, event);
222                 }
223 
224             },
225 
226             onCompleteHandler : function () {
227                 this.__content().show();
228                 this.__loading().hide();
229 
230                 return this.__fireShow();
231             },
232 
233             /**
234              * @private
235              * @return {void} TODO ...
236              */
237             __show: function (event) {
238                 var tooltip = this;
239                 this.__delay(this.options.showDelay, function () {
240                     if (!tooltip.options.followMouse) {
241                         tooltip.saveShowEvent = event;
242                     }
243                     if (!tooltip.shown) {
244                         tooltip.__fireBeforeShow();
245                         tooltip.popup.show(tooltip.saveShowEvent);
246                     }
247                     //for showing tooltip in followMouse mode
248                     tooltip.shown = true;
249                 });
250             },
251 
252             /***************************** Private Methods ****************************************************************/
253             __delay : function (delay, action) {
254                 var tooltip = this;
255 
256                 if (delay > 0) {
257                     tooltip.hidingTimerHandle = window.setTimeout(function() {
258                         action();
259 
260                         if (tooltip.hidingTimerHandle) {
261                             window.clearTimeout(tooltip.hidingTimerHandle);
262                             tooltip.hidingTimerHandle = undefined;
263                         }
264                     }, delay);
265                 } else {
266                     action();
267                 }
268             },
269 
270             __detectAncestorNode: function(leaf, element) {
271                 // Return true if "element" is "leaf" or one of its parents
272                 var node = leaf;
273                 while (node != null && node != element) {
274                     node = node.parentNode;
275                 }
276                 return (node != null);
277             },
278 
279             __loading : function () {
280                 return $(document.getElementById(this.id + ":loading"));
281             },
282 
283             __content : function () {
284                 return $(document.getElementById(this.id + ":content"));
285             },
286 
287             __fireHide : function () {
288                 return rf.Event.fireById(this.id, "hide", { id: this.id });
289             },
290 
291             __fireShow : function () {
292                 return rf.Event.fireById(this.id, "show", { id: this.id });
293             },
294 
295             __fireBeforeHide : function () {
296                 return rf.Event.fireById(this.id, "beforehide", { id: this.id });
297             },
298 
299             __fireBeforeShow : function () {
300                 return rf.Event.fireById(this.id, "beforeshow", { id: this.id });
301             },
302 
303             /**
304              * @private
305              * */
306             __addUserEventHandler : function (name) {
307                 var handler = this.options["on" + name];
308                 if (handler) {
309                     rf.Event.bindById(this.id, name + this.namespace, handler);
310                 }
311             },
312 
313             __contains: function(id, elt) {
314                 while (elt) {
315                     if (id == elt.id) {
316                         return true;
317                     }
318 
319                     elt = elt.parentNode;
320                 }
321                 return false;
322             },
323 
324             __isInside: function(elt) {
325                 return this.__contains(this.target, elt) || this.__contains(this.popupId, elt);
326             },
327 
328             destroy: function () {
329                 rf.Event.unbindById(this.popupId, this.namespace);
330                 rf.Event.unbindById(this.target, this.namespace);
331                 this.popup.destroy();
332                 this.popup = null;
333                 $super.destroy.call(this);
334             }
335         });
336 
337     // define super class link
338     var $super = rf.ui.Tooltip.$super;
339 })(RichFaces.jQuery, RichFaces);