RoundCube Webmail
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

603 lines
16 KiB

20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
  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. $Id$
  13. */
  14. // Constants
  15. var CONTROL_KEY = 1;
  16. var SHIFT_KEY = 2;
  17. var CONTROL_SHIFT_KEY = 3;
  18. /**
  19. * Default browser check class
  20. * @construcotr
  21. */
  22. function roundcube_browser()
  23. {
  24. this.ver = parseFloat(navigator.appVersion);
  25. this.appver = navigator.appVersion;
  26. this.agent = navigator.userAgent;
  27. this.name = navigator.appName;
  28. this.vendor = navigator.vendor ? navigator.vendor : '';
  29. this.vendver = navigator.vendorSub ? parseFloat(navigator.vendorSub) : 0;
  30. this.product = navigator.product ? navigator.product : '';
  31. this.platform = String(navigator.platform).toLowerCase();
  32. this.lang = (navigator.language) ? navigator.language.substring(0,2) :
  33. (navigator.browserLanguage) ? navigator.browserLanguage.substring(0,2) :
  34. (navigator.systemLanguage) ? navigator.systemLanguage.substring(0,2) : 'en';
  35. this.win = (this.platform.indexOf('win')>=0) ? true : false;
  36. this.mac = (this.platform.indexOf('mac')>=0) ? true : false;
  37. this.linux = (this.platform.indexOf('linux')>=0) ? true : false;
  38. this.unix = (this.platform.indexOf('unix')>=0) ? true : false;
  39. this.dom = document.getElementById ? true : false;
  40. this.dom2 = (document.addEventListener && document.removeEventListener);
  41. this.ie = (document.all) ? true : false;
  42. this.ie4 = (this.ie && !this.dom);
  43. this.ie5 = (this.dom && this.appver.indexOf('MSIE 5')>0);
  44. this.ie6 = (this.dom && this.appver.indexOf('MSIE 6')>0);
  45. this.mz = (this.dom && this.ver>=5); // (this.dom && this.product=='Gecko')
  46. this.ns = ((this.ver<5 && this.name=='Netscape') || (this.ver>=5 && this.vendor.indexOf('Netscape')>=0));
  47. this.ns6 = (this.ns && parseInt(this.vendver)==6); // (this.mz && this.ns) ? true : false;
  48. this.ns7 = (this.ns && parseInt(this.vendver)==7); // this.agent.indexOf('Netscape/7')>0);
  49. this.safari = (this.agent.toLowerCase().indexOf('safari')>0 || this.agent.toLowerCase().indexOf('applewebkit')>0);
  50. this.konq = (this.agent.toLowerCase().indexOf('konqueror')>0);
  51. this.opera = (window.opera) ? true : false;
  52. if(this.opera && window.RegExp)
  53. this.vendver = (/opera(\s|\/)([0-9\.]+)/i.test(navigator.userAgent)) ? parseFloat(RegExp.$2) : -1;
  54. else if(!this.vendver && this.safari)
  55. this.vendver = (/(safari|applewebkit)\/([0-9]+)/i.test(this.agent)) ? parseInt(RegExp.$2) : 0;
  56. else if((!this.vendver && this.mz) || this.agent.indexOf('Camino')>0)
  57. this.vendver = (/rv:([0-9\.]+)/.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
  58. else if(this.ie && window.RegExp)
  59. this.vendver = (/msie\s+([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
  60. else if(this.konq && window.RegExp)
  61. this.vendver = (/khtml\/([0-9\.]+)/i.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
  62. // get real language out of safari's user agent
  63. if(this.safari && (/;\s+([a-z]{2})-[a-z]{2}\)/i.test(this.agent)))
  64. this.lang = RegExp.$1;
  65. this.dhtml = ((this.ie4 && this.win) || this.ie5 || this.ie6 || this.ns4 || this.mz);
  66. this.vml = (this.win && this.ie && this.dom && !this.opera);
  67. this.pngalpha = (this.mz || (this.opera && this.vendver>=6) || (this.ie && this.mac && this.vendver>=5) ||
  68. (this.ie && this.win && this.vendver>=5.5) || this.safari);
  69. this.opacity = (this.mz || (this.ie && this.vendver>=5.5 && !this.opera) || (this.safari && this.vendver>=100));
  70. this.cookies = navigator.cookieEnabled;
  71. // test for XMLHTTP support
  72. this.xmlhttp_test = function()
  73. {
  74. var activeX_test = new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");
  75. this.xmlhttp = (window.XMLHttpRequest || (window.ActiveXObject && activeX_test())) ? true : false;
  76. return this.xmlhttp;
  77. }
  78. }
  79. // static functions for event handling
  80. var rcube_event = {
  81. /**
  82. * returns the event target element
  83. */
  84. get_target: function(e)
  85. {
  86. e = e || window.event;
  87. return e && e.target ? e.target : e.srcElement;
  88. },
  89. /**
  90. * returns the event key code
  91. */
  92. get_keycode: function(e)
  93. {
  94. e = e || window.event;
  95. return e && e.keyCode ? e.keyCode : (e && e.which ? e.which : 0);
  96. },
  97. /**
  98. * returns modifier key (constants defined at top of file)
  99. */
  100. get_modifier: function(e)
  101. {
  102. var opcode = 0;
  103. e = e || window.event;
  104. if (bw.mac && e)
  105. {
  106. opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
  107. return opcode;
  108. }
  109. if (e)
  110. {
  111. opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
  112. return opcode;
  113. }
  114. },
  115. /**
  116. * Return absolute mouse position of an event
  117. */
  118. get_mouse_pos: function(e)
  119. {
  120. if (!e) e = window.event;
  121. var mX = (e.pageX) ? e.pageX : e.clientX;
  122. var mY = (e.pageY) ? e.pageY : e.clientY;
  123. if (document.body && document.all)
  124. {
  125. mX += document.body.scrollLeft;
  126. mY += document.body.scrollTop;
  127. }
  128. return { x:mX, y:mY };
  129. },
  130. /**
  131. * Add an object method as event listener to a certain element
  132. */
  133. add_listener: function(p)
  134. {
  135. if (!p.object || !p.method) // not enough arguments
  136. return;
  137. if (!p.element)
  138. p.element = document;
  139. if (!p.object._rc_events)
  140. p.object._rc_events = [];
  141. var key = p.event + '*' + p.method;
  142. if (!p.object._rc_events[key])
  143. p.object._rc_events[key] = function(e){ return p.object[p.method](e); };
  144. if (p.element.addEventListener)
  145. p.element.addEventListener(p.event, p.object._rc_events[key], false);
  146. else if (p.element.attachEvent)
  147. p.element.attachEvent('on'+p.event, p.object._rc_events[key]);
  148. else
  149. p.element['on'+p.event] = p.object._rc_events[key];
  150. },
  151. /**
  152. * Remove event listener
  153. */
  154. remove_listener: function(p)
  155. {
  156. if (!p.element)
  157. p.element = document;
  158. var key = p.event + '*' + p.method;
  159. if (p.object && p.object._rc_events && p.object._rc_events[key]) {
  160. if (p.element.removeEventListener)
  161. p.element.removeEventListener(p.event, p.object._rc_events[key], false);
  162. else if (p.element.detachEvent)
  163. p.element.detachEvent('on'+p.event, p.object._rc_events[key]);
  164. else
  165. p.element['on'+p.event] = null;
  166. }
  167. },
  168. /**
  169. * Prevent event propagation and bubbeling
  170. */
  171. cancel: function(evt)
  172. {
  173. var e = evt ? evt : window.event;
  174. if (e.preventDefault)
  175. e.preventDefault();
  176. if (e.stopPropagation)
  177. e.stopPropagation();
  178. e.cancelBubble = true;
  179. e.returnValue = false;
  180. return false;
  181. }
  182. };
  183. var rcube_layer_objects = new Array();
  184. /**
  185. * RoundCube generic layer (floating box) class
  186. *
  187. * @constructor
  188. */
  189. function rcube_layer(id, attributes)
  190. {
  191. this.name = id;
  192. // create a new layer in the current document
  193. this.create = function(arg)
  194. {
  195. var l = (arg.x) ? arg.x : 0;
  196. var t = (arg.y) ? arg.y : 0;
  197. var w = arg.width;
  198. var h = arg.height;
  199. var z = arg.zindex;
  200. var vis = arg.vis;
  201. var parent = arg.parent;
  202. var obj;
  203. obj = document.createElement('DIV');
  204. with(obj)
  205. {
  206. id = this.name;
  207. with(style)
  208. {
  209. position = 'absolute';
  210. visibility = (vis) ? (vis==2) ? 'inherit' : 'visible' : 'hidden';
  211. left = l+'px';
  212. top = t+'px';
  213. if(w) width = w+'px';
  214. if(h) height = h+'px';
  215. if(z) zIndex = z;
  216. }
  217. }
  218. if(parent) parent.appendChild(obj);
  219. else document.body.appendChild(obj);
  220. this.elm = obj;
  221. };
  222. // create new layer
  223. if(attributes!=null)
  224. {
  225. this.create(attributes);
  226. this.name = this.elm.id;
  227. }
  228. else // just refer to the object
  229. this.elm = document.getElementById(id);
  230. if(!this.elm)
  231. return false;
  232. // ********* layer object properties *********
  233. this.css = this.elm.style;
  234. this.event = this.elm;
  235. this.width = this.elm.offsetWidth;
  236. this.height = this.elm.offsetHeight;
  237. this.x = parseInt(this.elm.offsetLeft);
  238. this.y = parseInt(this.elm.offsetTop);
  239. this.visible = (this.css.visibility=='visible' || this.css.visibility=='show' || this.css.visibility=='inherit') ? true : false;
  240. this.id = rcube_layer_objects.length;
  241. this.obj = 'rcube_layer_objects['+this.id+']';
  242. rcube_layer_objects[this.id] = this;
  243. // ********* layer object methods *********
  244. // move the layer to a specific position
  245. this.move = function(x, y)
  246. {
  247. this.x = x;
  248. this.y = y;
  249. this.css.left = Math.round(this.x)+'px';
  250. this.css.top = Math.round(this.y)+'px';
  251. }
  252. // move the layer for a specific step
  253. this.shift = function(x,y)
  254. {
  255. x = Math.round(x*100)/100;
  256. y = Math.round(y*100)/100;
  257. this.move(this.x+x, this.y+y);
  258. }
  259. // change the layers width and height
  260. this.resize = function(w,h)
  261. {
  262. this.css.width = w+'px';
  263. this.css.height = h+'px';
  264. this.width = w;
  265. this.height = h;
  266. }
  267. // cut the layer (top,width,height,left)
  268. this.clip = function(t,w,h,l)
  269. {
  270. this.css.clip='rect('+t+' '+w+' '+h+' '+l+')';
  271. this.clip_height = h;
  272. this.clip_width = w;
  273. }
  274. // show or hide the layer
  275. this.show = function(a)
  276. {
  277. if(a==1)
  278. {
  279. this.css.visibility = 'visible';
  280. this.visible = true;
  281. }
  282. else if(a==2)
  283. {
  284. this.css.visibility = 'inherit';
  285. this.visible = true;
  286. }
  287. else
  288. {
  289. this.css.visibility = 'hidden';
  290. this.visible = false;
  291. }
  292. }
  293. // write new content into a Layer
  294. this.write = function(cont)
  295. {
  296. this.elm.innerHTML = cont;
  297. }
  298. // set the given color to the layer background
  299. this.set_bgcolor = function(c)
  300. {
  301. if(!c || c=='#')
  302. c = 'transparent';
  303. this.css.backgroundColor = c;
  304. }
  305. // set the opacity of a layer to the given ammount (in %)
  306. this.set_opacity = function(v)
  307. {
  308. if(!bw.opacity)
  309. return;
  310. var op = v<=1 ? Math.round(v*100) : parseInt(v);
  311. if(bw.ie)
  312. this.css.filter = 'alpha(opacity:'+op+')';
  313. else if(bw.safari)
  314. {
  315. this.css.opacity = op/100;
  316. this.css.KhtmlOpacity = op/100;
  317. }
  318. else if(bw.mz)
  319. this.css.MozOpacity = op/100;
  320. }
  321. }
  322. // check if input is a valid email address
  323. // By Cal Henderson <cal@iamcal.com>
  324. // http://code.iamcal.com/php/rfc822/
  325. function rcube_check_email(input, inline)
  326. {
  327. if (input && window.RegExp)
  328. {
  329. var qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
  330. var dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
  331. var atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
  332. var quoted_pair = '\\x5c[\\x00-\\x7f]';
  333. var domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d';
  334. var quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22';
  335. var sub_domain = '('+atom+'|'+domain_literal+')';
  336. var word = '('+atom+'|'+quoted_string+')';
  337. var domain = sub_domain+'(\\x2e'+sub_domain+')*';
  338. var local_part = word+'(\\x2e'+word+')*';
  339. var addr_spec = local_part+'\\x40'+domain;
  340. var delim = '[,;\s\n]';
  341. var reg1 = inline ? new RegExp('(^|<|'+delim+')'+addr_spec+'($|>|'+delim+')', 'i') : new RegExp('^'+addr_spec+'$', 'i');
  342. return reg1.test(input) ? true : false;
  343. }
  344. return false;
  345. }
  346. // find a value in a specific array and returns the index
  347. function find_in_array()
  348. {
  349. var args = find_in_array.arguments;
  350. if(!args.length) return -1;
  351. var haystack = typeof(args[0])=='object' ? args[0] : args.length>1 && typeof(args[1])=='object' ? args[1] : new Array();
  352. var needle = typeof(args[0])!='object' ? args[0] : args.length>1 && typeof(args[1])!='object' ? args[1] : '';
  353. var nocase = args.length==3 ? args[2] : false;
  354. if(!haystack.length) return -1;
  355. for(var i=0; i<haystack.length; i++)
  356. if(nocase && haystack[i].toLowerCase()==needle.toLowerCase())
  357. return i;
  358. else if(haystack[i]==needle)
  359. return i;
  360. return -1;
  361. }
  362. // make a string URL safe
  363. function urlencode(str)
  364. {
  365. return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
  366. }
  367. // get any type of html objects by id/name
  368. function rcube_find_object(id, d)
  369. {
  370. var n, f, obj, e;
  371. if(!d) d = document;
  372. if(d.getElementsByName && (e = d.getElementsByName(id)))
  373. obj = e[0];
  374. if(!obj && d.getElementById)
  375. obj = d.getElementById(id);
  376. if(!obj && d.all)
  377. obj = d.all[id];
  378. if(!obj && d.images.length)
  379. obj = d.images[id];
  380. if(!obj && d.forms.length)
  381. for(f=0; f<d.forms.length; f++)
  382. {
  383. if(d.forms[f].name == id)
  384. obj = d.forms[f];
  385. else if(d.forms[f].elements[id])
  386. obj = d.forms[f].elements[id];
  387. }
  388. if(!obj && d.layers)
  389. {
  390. if(d.layers[id]) obj = d.layers[id];
  391. for(n=0; !obj && n<d.layers.length; n++)
  392. obj = rcube_find_object(id, d.layers[n].document);
  393. }
  394. return obj;
  395. }
  396. // return the absolute position of an object within the document
  397. function rcube_get_object_pos(obj)
  398. {
  399. if(typeof(obj)=='string')
  400. obj = rcube_find_object(obj);
  401. if(!obj) return {x:0, y:0};
  402. var iX = (bw.layers) ? obj.x : obj.offsetLeft;
  403. var iY = (bw.layers) ? obj.y : obj.offsetTop;
  404. if(bw.ie || bw.mz)
  405. {
  406. var elm = obj.offsetParent;
  407. while(elm && elm!=null)
  408. {
  409. iX += elm.offsetLeft;
  410. iY += elm.offsetTop;
  411. elm = elm.offsetParent;
  412. }
  413. }
  414. //if(bw.mac && bw.ie5) iX += document.body.leftMargin;
  415. //if(bw.mac && bw.ie5) iY += document.body.topMargin;
  416. return {x:iX, y:iY};
  417. }
  418. /**
  419. * Return the currently applied value of a css property
  420. *
  421. * @param {Object} html_element Node reference
  422. * @param {String} css_property Property name to read in Javascript notation (eg. 'textAlign')
  423. * @param {String} mozilla_equivalent Equivalent property name in CSS notation (eg. 'text-align')
  424. * @return CSS property value
  425. * @type String
  426. */
  427. function get_elements_computed_style(html_element, css_property, mozilla_equivalent)
  428. {
  429. if (arguments.length==2)
  430. mozilla_equivalent = css_property;
  431. var el = html_element;
  432. if (typeof(html_element)=='string')
  433. el = rcube_find_object(html_element);
  434. if (el && el.currentStyle)
  435. return el.currentStyle[css_property];
  436. else if (el && document.defaultView && document.defaultView.getComputedStyle)
  437. return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozilla_equivalent);
  438. else
  439. return false;
  440. }
  441. // cookie functions by GoogieSpell
  442. function setCookie(name, value, expires, path, domain, secure)
  443. {
  444. var curCookie = name + "=" + escape(value) +
  445. (expires ? "; expires=" + expires.toGMTString() : "") +
  446. (path ? "; path=" + path : "") +
  447. (domain ? "; domain=" + domain : "") +
  448. (secure ? "; secure" : "");
  449. document.cookie = curCookie;
  450. }
  451. roundcube_browser.prototype.set_cookie = setCookie;
  452. function getCookie(name)
  453. {
  454. var dc = document.cookie;
  455. var prefix = name + "=";
  456. var begin = dc.indexOf("; " + prefix);
  457. if (begin == -1)
  458. {
  459. begin = dc.indexOf(prefix);
  460. if (begin != 0) return null;
  461. }
  462. else
  463. begin += 2;
  464. var end = document.cookie.indexOf(";", begin);
  465. if (end == -1)
  466. end = dc.length;
  467. return unescape(dc.substring(begin + prefix.length, end));
  468. }
  469. roundcube_browser.prototype.get_cookie = getCookie;
  470. // tiny replacement for Firebox functionality
  471. function rcube_console()
  472. {
  473. this.box = rcube_find_object('console');
  474. this.log = function(msg)
  475. {
  476. if (this.box)
  477. this.box.value += str+'\n--------------------------------------\n';
  478. };
  479. this.reset = function()
  480. {
  481. if (this.box)
  482. this.box.value = '';
  483. };
  484. }
  485. var bw = new roundcube_browser();
  486. if (!window.console)
  487. console = new rcube_console();
  488. // Add escape() method to RegExp object
  489. // http://dev.rubyonrails.org/changeset/7271
  490. RegExp.escape = function(str)
  491. {
  492. return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
  493. }