initial import
[roojs1] / Roo / QuickTips.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11 /**
12  * @class Roo.QuickTips
13  * Provides attractive and customizable tooltips for any element.
14  * @singleton
15  */
16 Roo.QuickTips = function(){
17     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
18     var ce, bd, xy, dd;
19     var visible = false, disabled = true, inited = false;
20     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
21     
22     var onOver = function(e){
23         if(disabled){
24             return;
25         }
26         var t = e.getTarget();
27         if(!t || t.nodeType !== 1 || t == document || t == document.body){
28             return;
29         }
30         if(ce && t == ce.el){
31             clearTimeout(hideProc);
32             return;
33         }
34         if(t && tagEls[t.id]){
35             tagEls[t.id].el = t;
36             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
37             return;
38         }
39         var ttp, et = Roo.fly(t);
40         var ns = cfg.namespace;
41         if(tm.interceptTitles && t.title){
42             ttp = t.title;
43             t.qtip = ttp;
44             t.removeAttribute("title");
45             e.preventDefault();
46         }else{
47             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
48         }
49         if(ttp){
50             showProc = show.defer(tm.showDelay, tm, [{
51                 el: t, 
52                 text: ttp, 
53                 width: et.getAttributeNS(ns, cfg.width),
54                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
55                 title: et.getAttributeNS(ns, cfg.title),
56                     cls: et.getAttributeNS(ns, cfg.cls)
57             }]);
58         }
59     };
60     
61     var onOut = function(e){
62         clearTimeout(showProc);
63         var t = e.getTarget();
64         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
65             hideProc = setTimeout(hide, tm.hideDelay);
66         }
67     };
68     
69     var onMove = function(e){
70         if(disabled){
71             return;
72         }
73         xy = e.getXY();
74         xy[1] += 18;
75         if(tm.trackMouse && ce){
76             el.setXY(xy);
77         }
78     };
79     
80     var onDown = function(e){
81         clearTimeout(showProc);
82         clearTimeout(hideProc);
83         if(!e.within(el)){
84             if(tm.hideOnClick){
85                 hide();
86                 tm.disable();
87                 tm.enable.defer(100, tm);
88             }
89         }
90     };
91     
92     var getPad = function(){
93         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
94     };
95
96     var show = function(o){
97         if(disabled){
98             return;
99         }
100         clearTimeout(dismissProc);
101         ce = o;
102         if(removeCls){ // in case manually hidden
103             el.removeClass(removeCls);
104             removeCls = null;
105         }
106         if(ce.cls){
107             el.addClass(ce.cls);
108             removeCls = ce.cls;
109         }
110         if(ce.title){
111             tipTitle.update(ce.title);
112             tipTitle.show();
113         }else{
114             tipTitle.update('');
115             tipTitle.hide();
116         }
117         el.dom.style.width  = tm.maxWidth+'px';
118         //tipBody.dom.style.width = '';
119         tipBodyText.update(o.text);
120         var p = getPad(), w = ce.width;
121         if(!w){
122             var td = tipBodyText.dom;
123             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
124             if(aw > tm.maxWidth){
125                 w = tm.maxWidth;
126             }else if(aw < tm.minWidth){
127                 w = tm.minWidth;
128             }else{
129                 w = aw;
130             }
131         }
132         //tipBody.setWidth(w);
133         el.setWidth(parseInt(w, 10) + p);
134         if(ce.autoHide === false){
135             close.setDisplayed(true);
136             if(dd){
137                 dd.unlock();
138             }
139         }else{
140             close.setDisplayed(false);
141             if(dd){
142                 dd.lock();
143             }
144         }
145         if(xy){
146             el.avoidY = xy[1]-18;
147             el.setXY(xy);
148         }
149         if(tm.animate){
150             el.setOpacity(.1);
151             el.setStyle("visibility", "visible");
152             el.fadeIn({callback: afterShow});
153         }else{
154             afterShow();
155         }
156     };
157     
158     var afterShow = function(){
159         if(ce){
160             el.show();
161             esc.enable();
162             if(tm.autoDismiss && ce.autoHide !== false){
163                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
164             }
165         }
166     };
167     
168     var hide = function(noanim){
169         clearTimeout(dismissProc);
170         clearTimeout(hideProc);
171         ce = null;
172         if(el.isVisible()){
173             esc.disable();
174             if(noanim !== true && tm.animate){
175                 el.fadeOut({callback: afterHide});
176             }else{
177                 afterHide();
178             } 
179         }
180     };
181     
182     var afterHide = function(){
183         el.hide();
184         if(removeCls){
185             el.removeClass(removeCls);
186             removeCls = null;
187         }
188     };
189     
190     return {
191         /**
192         * @cfg {Number} minWidth
193         * The minimum width of the quick tip (defaults to 40)
194         */
195        minWidth : 40,
196         /**
197         * @cfg {Number} maxWidth
198         * The maximum width of the quick tip (defaults to 300)
199         */
200        maxWidth : 300,
201         /**
202         * @cfg {Boolean} interceptTitles
203         * True to automatically use the element's DOM title value if available (defaults to false)
204         */
205        interceptTitles : false,
206         /**
207         * @cfg {Boolean} trackMouse
208         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
209         */
210        trackMouse : false,
211         /**
212         * @cfg {Boolean} hideOnClick
213         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
214         */
215        hideOnClick : true,
216         /**
217         * @cfg {Number} showDelay
218         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
219         */
220        showDelay : 500,
221         /**
222         * @cfg {Number} hideDelay
223         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
224         */
225        hideDelay : 200,
226         /**
227         * @cfg {Boolean} autoHide
228         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
229         * Used in conjunction with hideDelay.
230         */
231        autoHide : true,
232         /**
233         * @cfg {Boolean}
234         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
235         * (defaults to true).  Used in conjunction with autoDismissDelay.
236         */
237        autoDismiss : true,
238         /**
239         * @cfg {Number}
240         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
241         */
242        autoDismissDelay : 5000,
243        /**
244         * @cfg {Boolean} animate
245         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
246         */
247        animate : false,
248
249        /**
250         * @cfg {String} title
251         * Title text to display (defaults to '').  This can be any valid HTML markup.
252         */
253        /**
254         * @cfg {String} text
255         * Body text to display (defaults to '').  This can be any valid HTML markup.
256         */
257        /**
258         * @cfg {String} cls
259         * A CSS class to apply to the base quick tip element (defaults to '').
260         */
261        /**
262         * @cfg {Number} width
263         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
264         * minWidth or maxWidth.
265         */
266
267     /**
268      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
269      * or display QuickTips in a page.
270      */
271        init : function(){
272           tm = Roo.QuickTips;
273           cfg = tm.tagConfig;
274           if(!inited){
275               if(!Roo.isReady){ // allow calling of init() before onReady
276                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
277                   return;
278               }
279               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
280               el.fxDefaults = {stopFx: true};
281               // maximum custom styling
282               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
283               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
284               tipTitle = el.child('h3');
285               tipTitle.enableDisplayMode("block");
286               tipBody = el.child('div.x-tip-bd');
287               tipBodyText = el.child('div.x-tip-bd-inner');
288               //bdLeft = el.child('div.x-tip-bd-left');
289               //bdRight = el.child('div.x-tip-bd-right');
290               close = el.child('div.x-tip-close');
291               close.enableDisplayMode("block");
292               close.on("click", hide);
293               var d = Roo.get(document);
294               d.on("mousedown", onDown);
295               d.on("mouseover", onOver);
296               d.on("mouseout", onOut);
297               d.on("mousemove", onMove);
298               esc = d.addKeyListener(27, hide);
299               esc.disable();
300               if(Roo.dd.DD){
301                   dd = el.initDD("default", null, {
302                       onDrag : function(){
303                           el.sync();  
304                       }
305                   });
306                   dd.setHandleElId(tipTitle.id);
307                   dd.lock();
308               }
309               inited = true;
310           }
311           this.enable(); 
312        },
313
314     /**
315      * Configures a new quick tip instance and assigns it to a target element.  The following config options
316      * are supported:
317      * <pre>
318 Property    Type                   Description
319 ----------  ---------------------  ------------------------------------------------------------------------
320 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
321      * </ul>
322      * @param {Object} config The config object
323      */
324        register : function(config){
325            var cs = config instanceof Array ? config : arguments;
326            for(var i = 0, len = cs.length; i < len; i++) {
327                var c = cs[i];
328                var target = c.target;
329                if(target){
330                    if(target instanceof Array){
331                        for(var j = 0, jlen = target.length; j < jlen; j++){
332                            tagEls[target[j]] = c;
333                        }
334                    }else{
335                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
336                    }
337                }
338            }
339        },
340
341     /**
342      * Removes this quick tip from its element and destroys it.
343      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
344      */
345        unregister : function(el){
346            delete tagEls[Roo.id(el)];
347        },
348
349     /**
350      * Enable this quick tip.
351      */
352        enable : function(){
353            if(inited && disabled){
354                locks.pop();
355                if(locks.length < 1){
356                    disabled = false;
357                }
358            }
359        },
360
361     /**
362      * Disable this quick tip.
363      */
364        disable : function(){
365           disabled = true;
366           clearTimeout(showProc);
367           clearTimeout(hideProc);
368           clearTimeout(dismissProc);
369           if(ce){
370               hide(true);
371           }
372           locks.push(1);
373        },
374
375     /**
376      * Returns true if the quick tip is enabled, else false.
377      */
378        isEnabled : function(){
379             return !disabled;
380        },
381
382         // private
383        tagConfig : {
384            namespace : "ext",
385            attribute : "qtip",
386            width : "width",
387            target : "target",
388            title : "qtitle",
389            hide : "hide",
390            cls : "qclass"
391        }
392    };
393 }();
394
395 // backwards compat
396 Roo.QuickTips.tips = Roo.QuickTips.register;