]> git.donarmstrong.com Git - roundcube.git/blob - program/js/common.js.src
Imported Upstream version 0.3.1
[roundcube.git] / program / js / common.js.src
1 /*
2  +-----------------------------------------------------------------------+
3  | RoundCube common js library                                           |
4  |                                                                       |
5  | This file is part of the RoundCube web development suite              |
6  | Copyright (C) 2005-2007, RoundCube Dev, - Switzerland                 |
7  | Licensed under the GNU GPL                                            |
8  |                                                                       |
9  +-----------------------------------------------------------------------+
10  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
11  +-----------------------------------------------------------------------+
12  
13  $Id: common.js 2971 2009-09-20 06:53:11Z alec $
14 */
15
16 // Constants
17 var CONTROL_KEY = 1;
18 var SHIFT_KEY = 2;
19 var CONTROL_SHIFT_KEY = 3;
20
21
22 /**
23  * Default browser check class
24  * @construcotr
25  */
26 function roundcube_browser()
27   {
28   this.ver = parseFloat(navigator.appVersion);
29   this.appver = navigator.appVersion;
30   this.agent = navigator.userAgent;
31   this.name = navigator.appName;
32   this.vendor = navigator.vendor ? navigator.vendor : '';
33   this.vendver = navigator.vendorSub ? parseFloat(navigator.vendorSub) : 0;
34   this.product = navigator.product ? navigator.product : '';
35   this.platform = String(navigator.platform).toLowerCase();
36   this.lang = (navigator.language) ? navigator.language.substring(0,2) :
37               (navigator.browserLanguage) ? navigator.browserLanguage.substring(0,2) :
38               (navigator.systemLanguage) ? navigator.systemLanguage.substring(0,2) : 'en';
39
40   this.win = (this.platform.indexOf('win')>=0) ? true : false;
41   this.mac = (this.platform.indexOf('mac')>=0) ? true : false;
42   this.linux = (this.platform.indexOf('linux')>=0) ? true : false;
43   this.unix = (this.platform.indexOf('unix')>=0) ? true : false;
44
45   this.dom = document.getElementById ? true : false;
46   this.dom2 = (document.addEventListener && document.removeEventListener);
47
48   this.ie = (document.all) ? true : false;
49   this.ie4 = (this.ie && !this.dom);
50   this.ie5 = (this.dom && this.appver.indexOf('MSIE 5')>0);
51   this.ie8 = (this.dom && this.appver.indexOf('MSIE 8')>0);
52   this.ie7 = (this.dom && this.appver.indexOf('MSIE 7')>0);
53   this.ie6 = (this.dom && !this.ie8 && !this.ie7 && this.appver.indexOf('MSIE 6')>0);
54
55   this.mz = (this.dom && this.ver>=5);  // (this.dom && this.product=='Gecko')
56   this.ns = ((this.ver<5 && this.name=='Netscape') || (this.ver>=5 && this.vendor.indexOf('Netscape')>=0));
57   this.ns6 = (this.ns && parseInt(this.vendver)==6);  // (this.mz && this.ns) ? true : false;
58   this.ns7 = (this.ns && parseInt(this.vendver)==7);  // this.agent.indexOf('Netscape/7')>0);
59   this.safari = (this.agent.toLowerCase().indexOf('safari')>0 || this.agent.toLowerCase().indexOf('applewebkit')>0);
60   this.konq   = (this.agent.toLowerCase().indexOf('konqueror')>0);
61
62   this.opera = (window.opera) ? true : false;
63
64   if(this.opera && window.RegExp)
65     this.vendver = (/opera(\s|\/)([0-9\.]+)/i.test(navigator.userAgent)) ? parseFloat(RegExp.$2) : -1;
66   else if(!this.vendver && this.safari)
67     this.vendver = (/(safari|applewebkit)\/([0-9]+)/i.test(this.agent)) ? parseInt(RegExp.$2) : 0;
68   else if((!this.vendver && this.mz) || this.agent.indexOf('Camino')>0)
69     this.vendver = (/rv:([0-9\.]+)/.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
70   else if(this.ie && window.RegExp)
71     this.vendver = (/msie\s+([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
72   else if(this.konq && window.RegExp)
73     this.vendver = (/khtml\/([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
74
75
76   // get real language out of safari's user agent
77   if(this.safari && (/;\s+([a-z]{2})-[a-z]{2}\)/i.test(this.agent)))
78     this.lang = RegExp.$1;
79
80   this.dhtml = ((this.ie4 && this.win) || this.ie5 || this.ie6 || this.ns4 || this.mz);
81   this.vml = (this.win && this.ie && this.dom && !this.opera);
82   this.pngalpha = (this.mz || (this.opera && this.vendver>=6) || (this.ie && this.mac && this.vendver>=5) ||
83                    (this.ie && this.win && this.vendver>=5.5) || this.safari);
84   this.opacity = (this.mz || (this.ie && this.vendver>=5.5 && !this.opera) || (this.safari && this.vendver>=100));
85   this.cookies = navigator.cookieEnabled;
86   
87   // test for XMLHTTP support
88   this.xmlhttp_test = function()
89     {
90     var activeX_test = new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");
91     this.xmlhttp = (window.XMLHttpRequest || (window.ActiveXObject && activeX_test())) ? true : false;
92     return this.xmlhttp;
93     }
94   }
95
96
97 // static functions for DOM event handling
98 var rcube_event = {
99
100 /**
101  * returns the event target element
102  */
103 get_target: function(e)
104 {
105   e = e || window.event;
106   return e && e.target ? e.target : e.srcElement;
107 },
108
109 /**
110  * returns the event key code
111  */
112 get_keycode: function(e)
113 {
114   e = e || window.event;
115   return e && e.keyCode ? e.keyCode : (e && e.which ? e.which : 0);
116 },
117
118 /**
119  * returns the event key code
120  */
121 get_button: function(e)
122 {
123   e = e || window.event;
124   return e && (typeof e.button != 'undefined') ? e.button : (e && e.which ? e.which : 0);
125 },
126
127 /**
128  * returns modifier key (constants defined at top of file)
129  */
130 get_modifier: function(e)
131 {
132   var opcode = 0;
133   e = e || window.event;
134
135   if (bw.mac && e)
136     {
137     opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
138     return opcode;    
139     }
140   if (e)
141     {
142     opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
143     return opcode;
144     }
145 },
146
147 /**
148  * Return absolute mouse position of an event
149  */
150 get_mouse_pos: function(e)
151 {
152   if (!e) e = window.event;
153   var mX = (e.pageX) ? e.pageX : e.clientX;
154   var mY = (e.pageY) ? e.pageY : e.clientY;
155
156   if (document.body && document.all)
157   {
158     mX += document.body.scrollLeft;
159     mY += document.body.scrollTop;
160   }
161
162   if (e._offset) {
163     mX += e._offset.left;
164     mY += e._offset.top;
165   }
166
167   return { x:mX, y:mY };
168 },
169
170 /**
171  * Add an object method as event listener to a certain element
172  */
173 add_listener: function(p)
174 {
175   if (!p.object || !p.method)  // not enough arguments
176     return;
177   if (!p.element)
178     p.element = document;
179
180   if (!p.object._rc_events)
181     p.object._rc_events = [];
182   
183   var key = p.event + '*' + p.method;
184   if (!p.object._rc_events[key])
185     p.object._rc_events[key] = function(e){ return p.object[p.method](e); };
186
187   if (p.element.addEventListener)
188     p.element.addEventListener(p.event, p.object._rc_events[key], false);
189   else if (p.element.attachEvent)
190     {
191     // IE allows multiple events with the same function to be applied to the same object
192     // forcibly detach the event, then attach
193     p.element.detachEvent('on'+p.event, p.object._rc_events[key]);
194     p.element.attachEvent('on'+p.event, p.object._rc_events[key]);
195     }
196   else
197     p.element['on'+p.event] = p.object._rc_events[key];
198 },
199
200 /**
201  * Remove event listener
202  */
203 remove_listener: function(p)
204 {
205   if (!p.element)
206     p.element = document;
207
208   var key = p.event + '*' + p.method;
209   if (p.object && p.object._rc_events && p.object._rc_events[key]) {
210     if (p.element.removeEventListener)
211       p.element.removeEventListener(p.event, p.object._rc_events[key], false);
212     else if (p.element.detachEvent)
213       p.element.detachEvent('on'+p.event, p.object._rc_events[key]);
214     else
215       p.element['on'+p.event] = null;
216   }
217 },
218
219 /**
220  * Prevent event propagation and bubbeling
221  */
222 cancel: function(evt)
223 {
224   var e = evt ? evt : window.event;
225   if (e.preventDefault)
226     e.preventDefault();
227   if (e.stopPropagation)
228     e.stopPropagation();
229
230   e.cancelBubble = true;
231   e.returnValue = false;
232   return false;
233 }
234
235 };
236
237
238 /**
239  * rcmail objects event interface
240  */
241 function rcube_event_engine()
242 {
243   this._events = {};
244 }
245
246 rcube_event_engine.prototype = {
247
248 /**
249  * Setter for object event handlers
250  *
251  * @param {String}   Event name
252  * @param {Function} Handler function
253  * @return Listener ID (used to remove this handler later on)
254  */
255 addEventListener: function(evt, func, obj)
256 {
257   if (!this._events)
258     this._events = {};
259   if (!this._events[evt])
260     this._events[evt] = [];
261     
262   var e = {func:func, obj:obj ? obj : window};
263   this._events[evt][this._events[evt].length] = e;
264 },
265
266 /**
267  * Removes a specific event listener
268  *
269  * @param {String} Event name
270  * @param {Int}    Listener ID to remove
271  */
272 removeEventListener: function(evt, func, obj)
273 {
274   if (typeof obj == 'undefined')
275     obj = window;
276     
277   for (var h,i=0; this._events && this._events[evt] && i < this._events[evt].length; i++)
278     if ((h = this._events[evt][i]) && h.func == func && h.obj == obj)
279       this._events[evt][i] = null;
280 },
281
282 /**
283  * This will execute all registered event handlers
284  *
285  * @param {String} Event to trigger
286  * @param {Object} Event object/arguments
287  */
288 triggerEvent: function(evt, e)
289 {
290   var ret, h;
291   if (typeof e == 'undefined')
292     e = this;
293   else if (typeof e == 'object')
294     e.event = evt;
295   
296   if (this._events && this._events[evt] && !this._event_exec) {
297     this._event_exec = true;
298     for (var i=0; i < this._events[evt].length; i++) {
299       if ((h = this._events[evt][i])) {
300         if (typeof h.func == 'function')
301           ret = h.func.call ? h.func.call(h.obj, e) : h.func(e);
302         else if (typeof h.obj[h.func] == 'function')
303           ret = h.obj[h.func](e);
304               
305         // cancel event execution
306         if (typeof ret != 'undefined' && !ret)
307           break;
308       }
309     }
310   }
311
312   this._event_exec = false;
313   return ret;
314 }
315
316 }  // end rcube_event_engine.prototype
317
318
319
320 /**
321  * RoundCube generic layer (floating box) class
322  *
323  * @constructor
324  */
325 function rcube_layer(id, attributes)
326 {
327   this.name = id;
328   
329   // create a new layer in the current document
330   this.create = function(arg)
331     {
332     var l = (arg.x) ? arg.x : 0;
333     var t = (arg.y) ? arg.y : 0;
334     var w = arg.width;
335     var h = arg.height;
336     var z = arg.zindex;
337     var vis = arg.vis;
338     var parent = arg.parent;
339     var obj;
340
341     obj = document.createElement('DIV');
342
343     with(obj)
344       {
345       id = this.name;
346       with(style)
347         {
348         position = 'absolute';
349         visibility = (vis) ? (vis==2) ? 'inherit' : 'visible' : 'hidden';
350         left = l+'px';
351         top = t+'px';
352         if (w)
353           width = w.toString().match(/\%$/) ? w : w+'px';
354         if (h)
355           height = h.toString().match(/\%$/) ? h : h+'px';
356         if(z) zIndex = z;
357         }
358       }
359
360     if (parent)
361       parent.appendChild(obj);
362     else
363       document.body.appendChild(obj);
364
365     this.elm = obj;
366     };
367
368
369   // create new layer
370   if(attributes!=null)
371     {
372     this.create(attributes);
373     this.name = this.elm.id;
374     }
375   else  // just refer to the object
376     this.elm = document.getElementById(id);
377
378
379   if(!this.elm)
380     return false;
381
382
383   // ********* layer object properties *********
384
385   this.css = this.elm.style;
386   this.event = this.elm;
387   this.width = this.elm.offsetWidth;
388   this.height = this.elm.offsetHeight;
389   this.x = parseInt(this.elm.offsetLeft);
390   this.y = parseInt(this.elm.offsetTop);
391   this.visible = (this.css.visibility=='visible' || this.css.visibility=='show' || this.css.visibility=='inherit') ? true : false;
392
393
394   // ********* layer object methods *********
395
396
397   // move the layer to a specific position
398   this.move = function(x, y)
399     {
400     this.x = x;
401     this.y = y;
402     this.css.left = Math.round(this.x)+'px';
403     this.css.top = Math.round(this.y)+'px';
404     }
405
406   // change the layers width and height
407   this.resize = function(w,h)
408     {
409     this.css.width  = w+'px';
410     this.css.height = h+'px';
411     this.width = w;
412     this.height = h;
413     }
414
415
416   // show or hide the layer
417   this.show = function(a)
418     {
419     if(a==1)
420       {
421       this.css.visibility = 'visible';
422       this.visible = true;
423       }
424     else if(a==2)
425       {
426       this.css.visibility = 'inherit';
427       this.visible = true;
428       }
429     else
430       {
431       this.css.visibility = 'hidden';
432       this.visible = false;
433       }
434     }
435
436
437   // write new content into a Layer
438   this.write = function(cont)
439     {
440     this.elm.innerHTML = cont;
441     }
442
443 }
444
445
446 // check if input is a valid email address
447 // By Cal Henderson <cal@iamcal.com>
448 // http://code.iamcal.com/php/rfc822/
449 function rcube_check_email(input, inline)
450   {
451   if (input && window.RegExp)
452     {
453     var qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
454     var dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
455     var atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
456     var quoted_pair = '\\x5c[\\x00-\\x7f]';
457     var domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d';
458     var quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22';
459     var sub_domain = '('+atom+'|'+domain_literal+')';
460     var word = '('+atom+'|'+quoted_string+')';
461     var domain = sub_domain+'(\\x2e'+sub_domain+')*';
462     var local_part = word+'(\\x2e'+word+')*';
463     var addr_spec = local_part+'\\x40'+domain;
464     var delim = '[,;\s\n]';
465     var reg1 = inline ? new RegExp('(^|<|'+delim+')'+addr_spec+'($|>|'+delim+')', 'i') : new RegExp('^'+addr_spec+'$', 'i');
466     return reg1.test(input) ? true : false;
467     }
468   return false;
469   }
470   
471
472 // find a value in a specific array and returns the index
473 function find_in_array()
474   {
475   var args = find_in_array.arguments;
476   if(!args.length) return -1;
477
478   var haystack = typeof(args[0])=='object' ? args[0] : args.length>1 && typeof(args[1])=='object' ? args[1] : new Array();
479   var needle = typeof(args[0])!='object' ? args[0] : args.length>1 && typeof(args[1])!='object' ? args[1] : '';
480   var nocase = args.length==3 ? args[2] : false;
481
482   if(!haystack.length) return -1;
483
484   for(var i=0; i<haystack.length; i++)
485     if(nocase && haystack[i].toLowerCase()==needle.toLowerCase())
486       return i;
487     else if(haystack[i]==needle)
488       return i;
489
490   return -1;
491   }
492
493
494 // make a string URL safe
495 function urlencode(str)
496 {
497   return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
498 }
499
500
501 // get any type of html objects by id/name
502 function rcube_find_object(id, d)
503 {
504   var n, f, obj, e;
505   if(!d) d = document;
506
507   if(d.getElementsByName && (e = d.getElementsByName(id)))
508     obj = e[0];
509   if(!obj && d.getElementById)
510     obj = d.getElementById(id);
511   if(!obj && d.all)
512     obj = d.all[id];
513
514   if(!obj && d.images.length)
515     obj = d.images[id];
516
517   if (!obj && d.forms.length) {
518     for (f=0; f<d.forms.length; f++) {
519       if(d.forms[f].name == id)
520         obj = d.forms[f];
521       else if(d.forms[f].elements[id])
522         obj = d.forms[f].elements[id];
523     }
524   }
525
526   if (!obj && d.layers) {
527     if (d.layers[id]) obj = d.layers[id];
528     for (n=0; !obj && n<d.layers.length; n++)
529       obj = rcube_find_object(id, d.layers[n].document);
530   }
531
532   return obj;
533 }
534
535 // determine whether the mouse is over the given object or not
536 function rcube_mouse_is_over(ev, obj)
537 {
538   var mouse = rcube_event.get_mouse_pos(ev);
539   var pos = $(obj).offset();
540
541   return ((mouse.x >= pos.left) && (mouse.x < (pos.left + obj.offsetWidth)) &&
542     (mouse.y >= pos.top) && (mouse.y < (pos.top + obj.offsetHeight)));
543 }
544
545
546 // cookie functions by GoogieSpell
547 function setCookie(name, value, expires, path, domain, secure)
548   {
549   var curCookie = name + "=" + escape(value) +
550       (expires ? "; expires=" + expires.toGMTString() : "") +
551       (path ? "; path=" + path : "") +
552       (domain ? "; domain=" + domain : "") +
553       (secure ? "; secure" : "");
554   document.cookie = curCookie;
555   }
556
557 roundcube_browser.prototype.set_cookie = setCookie;
558
559 function getCookie(name)
560   {
561   var dc = document.cookie;
562   var prefix = name + "=";
563   var begin = dc.indexOf("; " + prefix);
564   if (begin == -1)
565     {
566     begin = dc.indexOf(prefix);
567     if (begin != 0) return null;
568     }
569   else
570     begin += 2;  
571   var end = document.cookie.indexOf(";", begin);
572   if (end == -1)
573     end = dc.length;
574   return unescape(dc.substring(begin + prefix.length, end));
575   }
576
577 roundcube_browser.prototype.get_cookie = getCookie;
578
579 // tiny replacement for Firebox functionality
580 function rcube_console()
581 {
582   this.log = function(msg)
583   {
584     var box = rcube_find_object('dbgconsole');
585
586     if (box) {
587       if (msg.charAt(msg.length-1)=='\n')
588         msg += '--------------------------------------\n';
589       else
590         msg += '\n--------------------------------------\n';
591
592       // Konqueror doesn't allows to just change value of hidden element
593       if (bw.konq) {
594         box.innerText += msg;
595         box.value = box.innerText;
596       } else
597         box.value += msg;
598     }
599   };
600
601   this.reset = function()
602   {
603     var box = rcube_find_object('dbgconsole');
604     if (box)
605       box.innerText = box.value = '';
606   };
607 }
608
609 var bw = new roundcube_browser();
610 if (!window.console) 
611   console = new rcube_console();
612
613
614 // Add escape() method to RegExp object
615 // http://dev.rubyonrails.org/changeset/7271
616 RegExp.escape = function(str)
617   {
618   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
619   }
620
621
622 // Make getElementById() case-sensitive on IE
623 if (bw.ie)
624   {
625   document._getElementById = document.getElementById;
626   document.getElementById = function(id)
627     {
628     var i = 0;
629     var o = document._getElementById(id);
630
631     if (!o || o.id != id)
632       while ((o = document.all[i]) && o.id != id)
633         i++;
634
635     return o;
636     }
637   }