Roo/bootstrap/Tooltip.js
[roojs1] / Roo / bootstrap / Tooltip.js
1 /*
2  * - LGPL
3  *
4  * Tooltip
5  * 
6  */
7
8 /**
9  * @class Roo.bootstrap.Tooltip
10  * Bootstrap Tooltip class
11  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
12  * to determine which dom element triggers the tooltip.
13  * 
14  * It needs to add support for additional attributes like tooltip-position
15  * 
16  * @constructor
17  * Create a new Toolti
18  * @param {Object} config The config object
19  */
20
21 Roo.bootstrap.Tooltip = function(config){
22     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23     
24     this.alignment = Roo.bootstrap.Tooltip.alignment;
25     
26     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27         this.alignment = config.alignment;
28     }
29     
30 };
31
32 Roo.apply(Roo.bootstrap.Tooltip, {
33     /**
34      * @function init initialize tooltip monitoring.
35      * @static
36      */
37     currentEl : false,
38     currentTip : false,
39     currentRegion : false,
40     
41     //  init : delay?
42     
43     init : function()
44     {
45         Roo.get(document).on('mouseover', this.enter ,this);
46         Roo.get(document).on('mouseout', this.leave, this);
47          
48         
49         this.currentTip = new Roo.bootstrap.Tooltip();
50     },
51     
52     enter : function(ev)
53     {
54         var dom = ev.getTarget();
55         
56         //Roo.log(['enter',dom]);
57         var el = Roo.fly(dom);
58         Roo.log("Tooltip enter");
59         Roo.log(el);
60         Roo.log(dom);
61         if (this.currentEl) {
62             //Roo.log(dom);
63             //Roo.log(this.currentEl);
64             //Roo.log(this.currentEl.contains(dom));
65             if (this.currentEl == el) {
66                 return;
67             }
68             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
69                 return;
70             }
71
72         }
73         
74         if (this.currentTip.el) {
75             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
76         }    
77         //Roo.log(ev);
78         
79         if(!el || el.dom == document){
80             return;
81         }
82         
83         var bindEl = el; 
84         var pel = false;
85         if (!el.attr('tooltip')) {
86             pel = el.findParent("[tooltip]");
87             if (pel) {
88                 bindEl = Roo.get(pel);
89             }
90         }
91         
92        
93         
94         // you can not look for children, as if el is the body.. then everythign is the child..
95         if (!pel && !el.attr('tooltip')) { //
96             if (!el.select("[tooltip]").elements.length) {
97                 return;
98             }
99             // is the mouse over this child...?
100             bindEl = el.select("[tooltip]").first();
101             var xy = ev.getXY();
102             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
103                 //Roo.log("not in region.");
104                 return;
105             }
106             //Roo.log("child element over..");
107             
108         }
109         this.currentEl = el;
110         this.currentTip.bind(bindEl);
111         this.currentRegion = Roo.lib.Region.getRegion(dom);
112         this.currentTip.enter();
113         
114     },
115     leave : function(ev)
116     {
117         var dom = ev.getTarget();
118         //Roo.log(['leave',dom]);
119         if (!this.currentEl) {
120             return;
121         }
122         
123         
124         if (dom != this.currentEl.dom) {
125             return;
126         }
127         var xy = ev.getXY();
128         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
129             return;
130         }
131         // only activate leave if mouse cursor is outside... bounding box..
132         
133         
134         
135         
136         if (this.currentTip) {
137             this.currentTip.leave();
138         }
139         //Roo.log('clear currentEl');
140         this.currentEl = false;
141         
142         
143     },
144     alignment : {
145         'left' : ['r-l', [-2,0], 'right'],
146         'right' : ['l-r', [2,0], 'left'],
147         'bottom' : ['t-b', [0,2], 'top'],
148         'top' : [ 'b-t', [0,-2], 'bottom']
149     }
150     
151 });
152
153
154 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
155     
156     
157     bindEl : false,
158     
159     delay : null, // can be { show : 300 , hide: 500}
160     
161     timeout : null,
162     
163     hoverState : null, //???
164     
165     placement : 'bottom', 
166     
167     alignment : false,
168     
169     getAutoCreate : function(){
170     
171         var cfg = {
172            cls : 'tooltip',   
173            role : 'tooltip',
174            cn : [
175                 {
176                     cls : 'tooltip-arrow arrow'
177                 },
178                 {
179                     cls : 'tooltip-inner'
180                 }
181            ]
182         };
183         
184         return cfg;
185     },
186     bind : function(el)
187     {
188         this.bindEl = el;
189     },
190     
191     initEvents : function()
192     {
193         this.arrowEl = this.el.select('.arrow', true).first();
194         this.innerEl = this.el.select('.tooltip-inner', true).first();
195     },
196     
197     enter : function () {
198        
199         if (this.timeout != null) {
200             clearTimeout(this.timeout);
201         }
202         
203         this.hoverState = 'in';
204          //Roo.log("enter - show");
205         if (!this.delay || !this.delay.show) {
206             this.show();
207             return;
208         }
209         var _t = this;
210         this.timeout = setTimeout(function () {
211             if (_t.hoverState == 'in') {
212                 _t.show();
213             }
214         }, this.delay.show);
215     },
216     leave : function()
217     {
218         clearTimeout(this.timeout);
219     
220         this.hoverState = 'out';
221          if (!this.delay || !this.delay.hide) {
222             this.hide();
223             return;
224         }
225        
226         var _t = this;
227         this.timeout = setTimeout(function () {
228             //Roo.log("leave - timeout");
229             
230             if (_t.hoverState == 'out') {
231                 _t.hide();
232                 Roo.bootstrap.Tooltip.currentEl = false;
233             }
234         }, delay);
235     },
236     
237     show : function (msg)
238     {
239         if (!this.el) {
240             this.render(document.body);
241         }
242         // set content.
243         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
244         
245         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
246         
247         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
248         
249         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
250                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
251         
252         var placement = typeof this.placement == 'function' ?
253             this.placement.call(this, this.el, on_el) :
254             this.placement;
255             
256         var autoToken = /\s?auto?\s?/i;
257         var autoPlace = autoToken.test(placement);
258         if (autoPlace) {
259             placement = placement.replace(autoToken, '') || 'top';
260         }
261         
262         //this.el.detach()
263         //this.el.setXY([0,0]);
264         this.el.show();
265         //this.el.dom.style.display='block';
266         
267         //this.el.appendTo(on_el);
268         
269         var p = this.getPosition();
270         var box = this.el.getBox();
271         
272         if (autoPlace) {
273             // fixme..
274         }
275         
276         var align = this.alignment[placement];
277         
278         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
279         
280         if(placement == 'top' || placement == 'bottom'){
281             if(xy[0] < 0){
282                 placement = 'right';
283             }
284             
285             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
286                 placement = 'left';
287             }
288             
289             var scroll = Roo.select('body', true).first().getScroll();
290             
291             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
292                 placement = 'top';
293             }
294             
295             align = this.alignment[placement];
296             
297             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
298             
299         }
300         
301         var elems = document.getElementsByTagName('div');
302         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
303         for (var i = 0; i < elems.length; i++) {
304           var zindex = Number.parseInt(
305                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
306                 10
307           );
308           if (zindex > highest) {
309             highest = zindex;
310           }
311         }
312         
313         
314         
315         this.el.dom.style.zIndex = highest;
316         
317         this.el.alignTo(this.bindEl, align[0],align[1]);
318         //var arrow = this.el.select('.arrow',true).first();
319         //arrow.set(align[2], 
320         
321         this.el.addClass(placement);
322         this.el.addClass("bs-tooltip-"+ placement);
323         
324         this.el.addClass('in fade show');
325         
326         this.hoverState = null;
327         
328         if (this.el.hasClass('fade')) {
329             // fade it?
330         }
331         
332         
333         
334         
335         
336     },
337     hide : function()
338     {
339          
340         if (!this.el) {
341             return;
342         }
343         //this.el.setXY([0,0]);
344         this.el.removeClass(['show', 'in']);
345         //this.el.hide();
346         
347     }
348     
349 });
350  
351
352