2 +-----------------------------------------------------------------------+
3 | RoundCube common js library |
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 |
9 +-----------------------------------------------------------------------+
10 | Author: Thomas Bruederli <roundcube@gmail.com> |
11 +-----------------------------------------------------------------------+
13 $Id: common.js 2971 2009-09-20 06:53:11Z alec $
19 var CONTROL_SHIFT_KEY = 3;
23 * Default browser check class
26 function roundcube_browser()
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';
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;
45 this.dom = document.getElementById ? true : false;
46 this.dom2 = (document.addEventListener && document.removeEventListener);
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);
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);
62 this.opera = (window.opera) ? true : false;
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;
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;
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;
87 // test for XMLHTTP support
88 this.xmlhttp_test = function()
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;
97 // static functions for DOM event handling
101 * returns the event target element
103 get_target: function(e)
105 e = e || window.event;
106 return e && e.target ? e.target : e.srcElement;
110 * returns the event key code
112 get_keycode: function(e)
114 e = e || window.event;
115 return e && e.keyCode ? e.keyCode : (e && e.which ? e.which : 0);
119 * returns the event key code
121 get_button: function(e)
123 e = e || window.event;
124 return e && (typeof e.button != 'undefined') ? e.button : (e && e.which ? e.which : 0);
128 * returns modifier key (constants defined at top of file)
130 get_modifier: function(e)
133 e = e || window.event;
137 opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
142 opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
148 * Return absolute mouse position of an event
150 get_mouse_pos: function(e)
152 if (!e) e = window.event;
153 var mX = (e.pageX) ? e.pageX : e.clientX;
154 var mY = (e.pageY) ? e.pageY : e.clientY;
156 if (document.body && document.all)
158 mX += document.body.scrollLeft;
159 mY += document.body.scrollTop;
163 mX += e._offset.left;
167 return { x:mX, y:mY };
171 * Add an object method as event listener to a certain element
173 add_listener: function(p)
175 if (!p.object || !p.method) // not enough arguments
178 p.element = document;
180 if (!p.object._rc_events)
181 p.object._rc_events = [];
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); };
187 if (p.element.addEventListener)
188 p.element.addEventListener(p.event, p.object._rc_events[key], false);
189 else if (p.element.attachEvent)
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]);
197 p.element['on'+p.event] = p.object._rc_events[key];
201 * Remove event listener
203 remove_listener: function(p)
206 p.element = document;
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]);
215 p.element['on'+p.event] = null;
220 * Prevent event propagation and bubbeling
222 cancel: function(evt)
224 var e = evt ? evt : window.event;
225 if (e.preventDefault)
227 if (e.stopPropagation)
230 e.cancelBubble = true;
231 e.returnValue = false;
239 * rcmail objects event interface
241 function rcube_event_engine()
246 rcube_event_engine.prototype = {
249 * Setter for object event handlers
251 * @param {String} Event name
252 * @param {Function} Handler function
253 * @return Listener ID (used to remove this handler later on)
255 addEventListener: function(evt, func, obj)
259 if (!this._events[evt])
260 this._events[evt] = [];
262 var e = {func:func, obj:obj ? obj : window};
263 this._events[evt][this._events[evt].length] = e;
267 * Removes a specific event listener
269 * @param {String} Event name
270 * @param {Int} Listener ID to remove
272 removeEventListener: function(evt, func, obj)
274 if (typeof obj == 'undefined')
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;
283 * This will execute all registered event handlers
285 * @param {String} Event to trigger
286 * @param {Object} Event object/arguments
288 triggerEvent: function(evt, e)
291 if (typeof e == 'undefined')
293 else if (typeof e == 'object')
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);
305 // cancel event execution
306 if (typeof ret != 'undefined' && !ret)
312 this._event_exec = false;
316 } // end rcube_event_engine.prototype
321 * RoundCube generic layer (floating box) class
325 function rcube_layer(id, attributes)
329 // create a new layer in the current document
330 this.create = function(arg)
332 var l = (arg.x) ? arg.x : 0;
333 var t = (arg.y) ? arg.y : 0;
338 var parent = arg.parent;
341 obj = document.createElement('DIV');
348 position = 'absolute';
349 visibility = (vis) ? (vis==2) ? 'inherit' : 'visible' : 'hidden';
353 width = w.toString().match(/\%$/) ? w : w+'px';
355 height = h.toString().match(/\%$/) ? h : h+'px';
361 parent.appendChild(obj);
363 document.body.appendChild(obj);
372 this.create(attributes);
373 this.name = this.elm.id;
375 else // just refer to the object
376 this.elm = document.getElementById(id);
383 // ********* layer object properties *********
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;
394 // ********* layer object methods *********
397 // move the layer to a specific position
398 this.move = function(x, y)
402 this.css.left = Math.round(this.x)+'px';
403 this.css.top = Math.round(this.y)+'px';
406 // change the layers width and height
407 this.resize = function(w,h)
409 this.css.width = w+'px';
410 this.css.height = h+'px';
416 // show or hide the layer
417 this.show = function(a)
421 this.css.visibility = 'visible';
426 this.css.visibility = 'inherit';
431 this.css.visibility = 'hidden';
432 this.visible = false;
437 // write new content into a Layer
438 this.write = function(cont)
440 this.elm.innerHTML = cont;
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)
451 if (input && window.RegExp)
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;
472 // find a value in a specific array and returns the index
473 function find_in_array()
475 var args = find_in_array.arguments;
476 if(!args.length) return -1;
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;
482 if(!haystack.length) return -1;
484 for(var i=0; i<haystack.length; i++)
485 if(nocase && haystack[i].toLowerCase()==needle.toLowerCase())
487 else if(haystack[i]==needle)
494 // make a string URL safe
495 function urlencode(str)
497 return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
501 // get any type of html objects by id/name
502 function rcube_find_object(id, d)
507 if(d.getElementsByName && (e = d.getElementsByName(id)))
509 if(!obj && d.getElementById)
510 obj = d.getElementById(id);
514 if(!obj && d.images.length)
517 if (!obj && d.forms.length) {
518 for (f=0; f<d.forms.length; f++) {
519 if(d.forms[f].name == id)
521 else if(d.forms[f].elements[id])
522 obj = d.forms[f].elements[id];
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);
535 // determine whether the mouse is over the given object or not
536 function rcube_mouse_is_over(ev, obj)
538 var mouse = rcube_event.get_mouse_pos(ev);
539 var pos = $(obj).offset();
541 return ((mouse.x >= pos.left) && (mouse.x < (pos.left + obj.offsetWidth)) &&
542 (mouse.y >= pos.top) && (mouse.y < (pos.top + obj.offsetHeight)));
546 // cookie functions by GoogieSpell
547 function setCookie(name, value, expires, path, domain, secure)
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;
557 roundcube_browser.prototype.set_cookie = setCookie;
559 function getCookie(name)
561 var dc = document.cookie;
562 var prefix = name + "=";
563 var begin = dc.indexOf("; " + prefix);
566 begin = dc.indexOf(prefix);
567 if (begin != 0) return null;
571 var end = document.cookie.indexOf(";", begin);
574 return unescape(dc.substring(begin + prefix.length, end));
577 roundcube_browser.prototype.get_cookie = getCookie;
579 // tiny replacement for Firebox functionality
580 function rcube_console()
582 this.log = function(msg)
584 var box = rcube_find_object('dbgconsole');
587 if (msg.charAt(msg.length-1)=='\n')
588 msg += '--------------------------------------\n';
590 msg += '\n--------------------------------------\n';
592 // Konqueror doesn't allows to just change value of hidden element
594 box.innerText += msg;
595 box.value = box.innerText;
601 this.reset = function()
603 var box = rcube_find_object('dbgconsole');
605 box.innerText = box.value = '';
609 var bw = new roundcube_browser();
611 console = new rcube_console();
614 // Add escape() method to RegExp object
615 // http://dev.rubyonrails.org/changeset/7271
616 RegExp.escape = function(str)
618 return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
622 // Make getElementById() case-sensitive on IE
625 document._getElementById = document.getElementById;
626 document.getElementById = function(id)
629 var o = document._getElementById(id);
631 if (!o || o.id != id)
632 while ((o = document.all[i]) && o.id != id)