thomascube
2012-01-02 c6db4aa46bd285ef7b3d63cba5e957373a116485
commit | author | age
c7dcb3 1 /**
T 2  * Roundcube functions for default skin interface
74d4c7 3  *
T 4  * Copyright (c) 2011, The Roundcube Dev Team
5  *
6  * The contents are subject to the Creative Commons Attribution-ShareAlike
7  * License. It is allowed to copy, distribute, transmit and to adapt the work
8  * by keeping credits to the original autors in the README file.
9  * See http://creativecommons.org/licenses/by-sa/3.0/ for details.
10  *
11  * $Id$
c7dcb3 12  */
T 13
14
15 function rcube_mail_ui()
16 {
68e13c 17   var env = {};
c7dcb3 18   var popups = {};
T 19   var popupconfig = {
20     forwardmenu:        { editable:1 },
21     searchmenu:         { editable:1, callback:searchmenu },
22     listoptions:        { editable:1 },
23     dragmessagemenu:    { sticky:1 },
24     groupmenu:          { above:1 },
25     mailboxmenu:        { above:1 },
26     composeoptionsmenu: { editable:1, overlap:1 },
27     // toggle: #1486823, #1486930
28     'attachment-form':  { editable:1, above:1, toggle:!bw.ie&&!bw.linux },
29     'upload-form':      { editable:1, toggle:!bw.ie&&!bw.linux }
30   };
31
32   var me = this;
918bb9 33   var mailviewsplit;
74d4c7 34   var compose_headers = {};
c7dcb3 35
T 36   // export public methods
68e13c 37   this.set = setenv;
c7dcb3 38   this.init = init;
bab043 39   this.init_tabs = init_tabs;
68e13c 40   this.show_about = show_about;
c7dcb3 41   this.show_popup = show_popup;
T 42   this.set_searchmod = set_searchmod;
74d4c7 43   this.show_uploadform = show_uploadform;
T 44   this.show_header_row = show_header_row;
45   this.hide_header_row = hide_header_row;
68e13c 46
T 47
48   /**
49    *
50    */
51   function setenv(key, val)
52   {
53     env[key] = val;
54   }
c7dcb3 55
T 56   /**
57    *
58    */
59   function init()
60   {
61     if (rcmail.env.task == 'mail') {
74d4c7 62       rcmail.addEventListener('menu-open', show_listoptions);
c7dcb3 63       rcmail.addEventListener('menu-save', save_listoptions);
74d4c7 64
T 65       var dragmenu = $('#dragmessagemenu');
66       if (dragmenu.length) {
67         rcmail.gui_object('message_dragmenu', 'dragmessagemenu');
68         popups.dragmessagemenu = dragmenu;
69       }
918bb9 70
T 71       var previewframe = $('#mailpreviewframe').is(':visible');
72       $('#mailpreviewtoggle').addClass(previewframe ? 'enabled' : 'closed').click(function(e){ toggle_preview_pane(e); return false });
28e18c 73       $('#maillistmode').addClass(rcmail.env.threading ? '' : 'selected').click(function(e){ switch_view_mode('list'); return false });
T 74       $('#mailthreadmode').addClass(rcmail.env.threading ? 'selected' : '').click(function(e){ switch_view_mode('thread'); return false });
918bb9 75
c7dcb3 76       if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
28e18c 77         layout_messageview();
543ccb 78         $("#all-headers").resizable({ handles: 's', minHeight: 50 });
74d4c7 79       }
T 80       else if (rcmail.env.action == 'compose') {
543ccb 81         rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 100); });
68e13c 82         rcmail.addEventListener('aftersend-attachment', show_uploadform);
74d4c7 83         layout_composeview();
T 84
bab043 85         $('#composeoptionstoggle').parent().click(function(){
T 86           $('#composeoptionstoggle').toggleClass('enabled');
74d4c7 87           $('#composeoptions').toggle();
T 88           layout_composeview();
89           return false;
bab043 90         }).css('cursor', 'pointer');
74d4c7 91
T 92         new rcube_splitter({ id:'composesplitterv', p1:'#composeview-left', p2:'#composeview-right',
c6db4a 93           orientation:'v', relative:true, start:248, min:170, size:12 }).init();
918bb9 94       }
T 95       else if (rcmail.env.action == 'list' || !rcmail.env.action) {
96           mailviewsplit = new rcube_splitter({ id:'mailviewsplitter', p1:'#mailview-top', p2:'#mailview-bottom',
97             orientation:'h', relative:true, start:310, min:150, size:0, offset:-22 });
98           if (previewframe)
99             mailviewsplit.init();
847d31 100
T 101           rcmail.addEventListener('setquota', update_quota);
918bb9 102       }
847d31 103
918bb9 104       if ($('#mailview-left').length) {
T 105         new rcube_splitter({ id:'mailviewsplitterv', p1:'#mailview-left', p2:'#mailview-right',
106           orientation:'v', relative:true, start:248, min:150, size:12, callback:render_mailboxlist, render:resize_leftcol }).init();
c7dcb3 107       }
28e18c 108     }
T 109     else if (rcmail.env.task == 'settings') {
4f1b7a 110       rcmail.addEventListener('init', function(){
T 111         var tab = '#settingstabpreferences';
112         if (rcmail.env.action)
113           tab = '#settingstab' + (rcmail.env.action.indexOf('identity')>0 ? 'identities' : rcmail.env.action.replace(/\./g, ''));
28e18c 114
4f1b7a 115         $(tab).addClass('selected')
T 116           .children().first().removeAttr('onclick').click(function() { return false; });
117       });
918bb9 118
T 119       if (rcmail.env.action == 'folders') {
120         new rcube_splitter({ id:'folderviewsplitter', p1:'#folderslist', p2:'#folder-details',
121           orientation:'v', relative:true, start:305, min:150, size:12 }).init();
122       }
123       else if (rcmail.env.action.indexOf('identit') >= 0) {
124         new rcube_splitter({ id:'identviewsplitter', p1:'#identitieslist', p2:'#identity-details',
125           orientation:'v', relative:true, start:305, min:150, size:12 }).init();
126       }
c7dcb3 127     }
bab043 128     else if (rcmail.env.task == 'addressbook') {
68e13c 129       rcmail.addEventListener('afterupload-photo', show_uploadform);
T 130
131       if (rcmail.env.action == '') {
132         new rcube_splitter({ id:'addressviewsplitterd', p1:'#addressview-left', p2:'#addressview-right',
133           orientation:'v', relative:true, start:226, min:150, size:12, render:resize_leftcol }).init();
134         new rcube_splitter({ id:'addressviewsplitter', p1:'#addresslist', p2:'#contacts-box',
135           orientation:'v', relative:true, start:296, min:220, size:12 }).init();
136       }
822a1e 137     }
T 138     else if (rcmail.env.task == 'login') {
12bf0f 139       if (bw.ie && bw.vendver < 9) {
822a1e 140         var popup = $('<div>')
T 141           .addClass('readtext')
142           .html("Roundcube will not work well with the crappy browser ya' using. Get yourself a new internet browsing software and don't come back without!<p>Sincerly,<br/>the Roundcube Dev Team</p>")
143           .appendTo(document.body)
144           .dialog({
145             dialogClass: 'alert',
146             closeOnEscape: true,
147             title: "No way, are you serious?",
148             close: function() {
149               popup.dialog('destroy').remove();
150             },
151             width: 450
152           });
153       }
bab043 154     }
T 155
156     // turn a group of fieldsets into tabs
157     $('.tabbed').each(function(idx, elem){ init_tabs(elem); })
c7dcb3 158
T 159     $(document.body).bind('mouseup', function(e){
160       var config, obj, target = e.target;
161       for (var id in popups) {
162         obj = popups[id];
163         config = popupconfig[id];
164         if (obj.is(':visible')
165           && target.id != id+'link'
166           && !config.toggle
167           && (!config.editable || !target_overlaps(target, obj.get(0)))
168           && (!config.sticky || !rcube_mouse_is_over(e, obj.get(0)))
169         ) {
170           var myid = id+'';
171           window.setTimeout(function(){ show_popupmenu(myid, false) }, 10);
172         }
173       }
174     })
175     .bind('keyup', function(e){
176       if (e.keyCode == 27) {
177         for (var id in popups) {
178           if (popups[id].is(':visible'))
179             show_popup(id, false);
180         }
181       }
182     });
28e18c 183     
T 184     $(window).resize(resize);
185   }
186
187   /**
188    * Update UI on window resize
189    */
190   function resize()
191   {
192     if (rcmail.env.task == 'mail' && (rcmail.env.action == 'show' || rcmail.env.action == 'preview')) {
193       layout_messageview();
194     }
74d4c7 195     if (rcmail.env.task == 'mail' && rcmail.env.action == 'compose') {
T 196       layout_composeview();
197     }
c7dcb3 198   }
T 199
200   /**
201    * Adjust UI objects of the mail view screen
202    */
28e18c 203   function layout_messageview()
c7dcb3 204   {
T 205     $('#messagecontent').css('top', ($('#messageheader').outerHeight() + 10) + 'px');
206     $('#message-objects div a').addClass('button');
207     
208     if (!$('#attachment-list li').length) {
b540ed 209       $('div.rightcol').hide();
T 210       $('div.leftcol').css('margin-right', '0');
c7dcb3 211     }
T 212   }
918bb9 213
T 214
215   function render_mailboxlist(splitter)
216   {
217   }
218
219
220   function resize_leftcol(splitter)
221   {
222     if (splitter)
223       $('#quicksearchbar input').css('width', (splitter.pos - 70) + 'px');
74d4c7 224   }
T 225
226
227   function layout_composeview()
228   {
229     var body = $('#composebody'),
230       bottom = $('#composeview-bottom'),
231       w, h;
232
233     bottom.css('height', (bottom.parent().height() - bottom.position().top) + 'px');
234
235     w = body.parent().width() - 8;
236     h = body.parent().height() - 36;
237     body.width(w).height(h);
238
239     if (window.tinyMCE && tinyMCE.get('composebody')) {
bab043 240       $('#composebody_tbl').width((w+12)+'px').height('').css('margin-top', '1px');
T 241       $('#composebody_ifr').width((w+12)+'px').height((h-22)+'px');
74d4c7 242     }
T 243     else {
244       $('#googie_edit_layer').height(h+'px');
245     }
918bb9 246   }
T 247
c7dcb3 248
847d31 249   function update_quota(p)
T 250   {
251     var y = p.total ? Math.ceil(p.percent / 100 * 20) * 24 : 0;
252     $('#quotadisplay').css('background-position', '0 -'+y+'px');
253   }
254
255
c7dcb3 256   /**
T 257    * Trigger for popup menus
258    */
259   function show_popup(popup, show, config)
260   {
261     // auto-register menu object
262     if (config || !popupconfig[popup])
263       popupconfig[popup] = $.extend(popupconfig[popup] || {}, config);
264
265     var visible = show_popupmenu(popup, show),
266       config = popupconfig[popup];
267     if (typeof config.callback == 'function')
268       config.callback(visible);
269   }
270
271   /**
272    * Show/hide a specific popup menu
273    */
274   function show_popupmenu(popup, show)
275   {
276     var obj = popups[popup],
277       config = popupconfig[popup],
278       ref = $('#'+popup+'link'),
279       above = config.above;
280
281     if (!obj) {
282       obj = popups[popup] = $('#'+popup);
283       obj.appendTo(document.body);  // move them to top for proper absolute positioning
284     }
285
286     if (!obj || !obj.length)
287       return false;
288
289     if (typeof show == 'undefined')
290       show = obj.is(':visible') ? false : true;
291     else if (config.toggle && show && obj.is(':visible'))
292       show = false;
293
294     if (show && ref) {
295       var parent = ref.parent(),
296         win = $(window),
297         pos;
298
299       if (parent.hasClass('dropbutton'))
300         ref = parent;
301
302       pos = ref.offset();
303       ref.offsetHeight = ref.outerHeight();
304       if (!above && pos.top + ref.offsetHeight + obj.height() > win.height())
305         above = true;
306       if (pos.left + obj.width() > win.width())
307         pos.left = win.width() - obj.width() - 12;
308
309       obj.css({ left:pos.left, top:(pos.top + (above ? -obj.height() : ref.offsetHeight)) });
310     }
311
312     obj[show?'show':'hide']();
313
314     // hide drop-down elements on buggy browsers
315     if (bw.ie6 && config.overlap) {
316       $('select').css('visibility', show?'hidden':'inherit');
317       $('select', obj).css('visibility', 'inherit');
318     }
319     
320     return show;
321   }
322
323   /**
324    *
325    */
326   function target_overlaps(target, elem)
327   {
328     while (target.parentNode) {
329       if (target.parentNode == elem)
330         return true;
331       target = target.parentNode;
332     }
333     return false;
334   }
335
336
918bb9 337   /**
T 338    * Show/hide the preview pane
339    */
c7dcb3 340   function toggle_preview_pane(e)
T 341   {
b540ed 342     var button = $(e.target),
T 343       frame = $('#mailpreviewframe'),
344       visible = !frame.is(':visible'),
918bb9 345       splitter = mailviewsplit.pos || parseInt(bw.get_cookie('mailviewsplitter') || 320),
b540ed 346       topstyles, bottomstyles, uid;
T 347
348     frame.toggle();
c7dcb3 349     button.removeClass().addClass(visible ? 'enabled' : 'closed');
T 350
b540ed 351     if (visible) {
918bb9 352       $('#mailview-top').css({ bottom:'auto' });
T 353       $('#mailview-bottom').css({ height:'auto' });
354
b540ed 355       rcmail.env.contentframe = 'messagecontframe';
T 356       if (uid = rcmail.message_list.get_single_selection())
357         rcmail.show_message(uid, false, true);
918bb9 358
T 359       // let the splitter set the correct size and position
360       if (mailviewsplit.handle) {
361         mailviewsplit.handle.show();
362         mailviewsplit.resize();
363       }
364       else
365         mailviewsplit.init();
b540ed 366     }
T 367     else {
368       rcmail.env.contentframe = null;
369       rcmail.show_contentframe(false);
370
918bb9 371       $('#mailview-top').css({ height:'auto', bottom:'28px' });
T 372       $('#mailview-bottom').css({ top:'auto', height:'26px' });
373
374       if (mailviewsplit.handle)
375         mailviewsplit.handle.hide();
376     }
b540ed 377
T 378     if (visible && uid && rcmail.message_list)
379       rcmail.message_list.scrollto(uid);
380
381     rcmail.command('save-pref', { name:'preview_pane', value:(visible?1:0) });
c7dcb3 382   }
T 383
384
918bb9 385   /**
T 386    *
387    */
a4be51 388   function switch_view_mode(mode)
T 389   {
390     if (rcmail.env.threading != (mode == 'thread'))
391       rcmail.set_list_options(null, undefined, undefined, mode == 'thread' ? 1 : 0);
392
393     $('#maillistmode, #mailthreadmode').removeClass('selected');
394     $('#mail'+mode+'mode').addClass('selected');
395   }
396
397
c7dcb3 398   /**** popup callbacks ****/
T 399
400   function searchmenu(show)
401   {
402     if (show && rcmail.env.search_mods) {
403       var n, all,
404         obj = popups['searchmenu'],
405         list = $('input:checkbox[name="s_mods[]"]', obj),
406         mbox = rcmail.env.mailbox,
407         mods = rcmail.env.search_mods;
408
409       if (rcmail.env.task == 'mail') {
410         mods = mods[mbox] ? mods[mbox] : mods['*'];
411         all = 'text';
412       }
413       else {
414         all = '*';
415       }
416
417       if (mods[all])
418         list.map(function() {
419           this.checked = true;
420           this.disabled = this.value != all;
421         });
422       else {
423         list.prop('disabled', false).prop('checked', false);
424         for (n in mods)
425           $('#s_mod_' + n).prop('checked', true);
426       }
427     }
428   }
429
430
918bb9 431   /**
T 432    *
433    */
74d4c7 434   function show_listoptions()
T 435   {
436     var $dialog = $('#listoptions');
437
438     // close the dialog
439     if ($dialog.is(':visible')) {
440       $dialog.dialog('close');
441       return;
442     }
443
444     // set form values
445     $('input[name="sort_col"][value="'+rcmail.env.sort_col+'"]').prop('checked', true);
446     $('input[name="sort_ord"][value="DESC"]').prop('checked', rcmail.env.sort_order == 'DESC');
447     $('input[name="sort_ord"][value="ASC"]').prop('checked', rcmail.env.sort_order != 'DESC');
448     $('input[name="view"][value="thread"]').prop('checked', rcmail.env.threading ? true : false);
449     $('input[name="view"][value="list"]').prop('checked', rcmail.env.threading ? false : true);
450
451     // list columns
452     var found, cols = $('input[name="list_col[]"]');
453     for (var i=0; i < cols.length; i++) {
454       if (cols[i].value != 'from') {
455         found = $.inArray(cols[i].value, rcmail.env.coltypes) != -1;
456       }
457       else {
458         found = ($.inArray('from', rcmail.env.coltypes) != -1
459           || $.inArray('to', rcmail.env.coltypes) != -1);
460       }
461       $(cols[i]).prop('checked', found);
462     }
463
464     $dialog.dialog({
465       modal: true,
466       resizable: false,
467       closeOnEscape: true,
468       title: null,
469       close: function() {
470         $dialog.dialog('destroy').hide();
471       },
472       width: 650
473     }).show();
474   }
475
476
477   /**
478    *
479    */
c7dcb3 480   function save_listoptions()
T 481   {
74d4c7 482     $('#listoptions').dialog('close');
c7dcb3 483
T 484     var sort = $('input[name="sort_col"]:checked').val(),
485       ord = $('input[name="sort_ord"]:checked').val(),
486       thread = $('input[name="view"]:checked').val(),
487       cols = $('input[name="list_col[]"]:checked')
488         .map(function(){ return this.value; }).get();
489
490     rcmail.set_list_options(cols, sort, ord, thread == 'thread' ? 1 : 0);
491   }
492
493
918bb9 494   /**
T 495    *
496    */
c7dcb3 497   function set_searchmod(elem)
T 498   {
499     var all, m, task = rcmail.env.task,
500       mods = rcmail.env.search_mods,
501       mbox = rcmail.env.mailbox;
502
503     if (!mods)
504       mods = {};
505
506     if (task == 'mail') {
507       if (!mods[mbox])
508         mods[mbox] = rcube_clone_object(mods['*']);
509       m = mods[mbox];
510       all = 'text';
511     }
512     else { //addressbook
513       m = mods;
514       all = '*';
515     }
516
517     if (!elem.checked)
518       delete(m[elem.value]);
519     else
520       m[elem.value] = 1;
521
522     // mark all fields
523     if (elem.value != all)
524       return;
525
526     $('input:checkbox[name="s_mods[]"]').map(function() {
527       if (this == elem)
528         return;
529
530       this.checked = true;
531       if (elem.checked) {
532         this.disabled = true;
533         delete m[this.value];
534       }
535       else {
536         this.disabled = false;
537         m[this.value] = 1;
538       }
539     });
540   }
74d4c7 541
T 542
543   function show_uploadform()
544   {
545     var $dialog = $('#upload-dialog');
546
547     // close the dialog
548     if ($dialog.is(':visible')) {
549       $dialog.dialog('close');
550       return;
551     }
f5521a 552     
T 553     // add icons to clone file input field
354bf1 554     if (rcmail.env.action == 'compose' && !$dialog.data('extended')) {
f5521a 555       $('<a>')
T 556         .addClass('iconlink add')
557         .attr('href', '#add')
558         .html('Add')
559         .appendTo($('input[type="file"]', $dialog).parent())
560         .click(add_uploadfile);
561       $dialog.data('extended', true);
562     }
74d4c7 563
T 564     $dialog.dialog({
565       modal: true,
566       resizable: false,
567       closeOnEscape: true,
568       title: $dialog.attr('title'),
569       close: function() {
570         try { $('#upload-dialog form').get(0).reset(); }
571         catch(e){ }  // ignore errors
572
573         $dialog.dialog('destroy').hide();
f5521a 574         $('div.addline', $dialog).remove();
74d4c7 575       },
T 576       width: 480
577     }).show();
578
579     if (!document.all)
f5521a 580       $('input[type=file]', $dialog).first().click();
74d4c7 581   }
T 582
f5521a 583   function add_uploadfile(e)
T 584   {
585     var div = $(this).parent();
586     var clone = div.clone().addClass('addline').insertAfter(div);
587     clone.children('.iconlink').click(add_uploadfile);
588     clone.children('input').val('');
589
590     if (!document.all)
591       $('input[type=file]', clone).click();
592   }
593
594
74d4c7 595   /**
T 596    *
597    */
598   function show_header_row(which)
599   {
600     if (compose_headers[which])
601       $('#_' + which).val(compose_headers[which]);
602     $('#compose-' + which).show();
603     $('#' + which + '-link').hide();
68e13c 604     layout_composeview();
74d4c7 605     return false;
T 606   }
607
608   /**
609    *
610    */
611   function hide_header_row(which)
612   {
613     // copy and clear field value
614     var field = $('#_' + which);
615     compose_headers[which] = field.val();
616     field.val('');
617
618     $('#compose-' + which).hide();
619     $('#' + which + '-link').show();
68e13c 620     layout_composeview();
T 621     return false;
74d4c7 622   }
bab043 623
T 624
625   /**
626    * Fieldsets-to-tabs converter
627    */
628   function init_tabs(elem, current)
629   {
630     var id = elem.id,
631       content = $(elem),
632       fs = content.children('fieldset');
633
634     if (!fs.length)
635       return;
636
637     if (!id) {
638       id = 'rcmtabcontainer';
639       elem.attr('id', id);
640     }
641
642     // first hide not selected tabs
643     current = current || 0;
644     fs.each(function(idx) { if (idx != current) $(this).hide(); });
645
646     // create tabs container
647     var tabs = $('<div>').addClass('tabsbar').prependTo(content);
648
649     // convert fildsets into tabs
650     fs.each(function(idx) {
651       var tab, a, elm = $(this), legend = elm.children('legend');
652
653       // create a tab
654       a   = $('<a>').text(legend.text()).attr('href', '#');
655       tab = $('<span>').attr({'id': 'tab'+idx, 'class': 'tablink'})
656         .click(function() { show_tab(id, idx); return false })
657
658       // remove legend
659       legend.remove();
660       // style fieldset
661       elm.addClass('tab');
662       // style selected tab
663       if (idx == current)
664         tab.addClass('selected');
665
666       // add the tab to container
667       tab.append(a).appendTo(tabs);
668     });
669   }
670
671   function show_tab(id, index)
672   {
673     var fs = $('#'+id).children('fieldset');
674
675     fs.each(function(idx) {
676       // Show/hide fieldset (tab content)
677       $(this)[index==idx ? 'show' : 'hide']();
678       // Select/unselect tab
679       $('#tab'+idx).toggleClass('selected', idx==index);
680     });
681   }
68e13c 682
T 683   /**
684    * Show about page as jquery UI dialog
685    */
686   function show_about(elem)
687   {
688     var frame = $('<iframe>').attr('id', 'aboutframe')
689       .attr('src', rcmail.url('settings/about'))
690       .appendTo(document.body);
691
692     var h = Math.floor($(window).height() * 0.75);
693     var buttons = {};
694     var supportln = $('#supportlink');
695     if (supportln.length && (env.supporturl = supportln.attr('href')))
696       buttons[supportln.html()] = function(e){ env.supporturl.indexOf('mailto:') < 0 ? window.open(env.supporturl) : location.href = env.supporturl };
697
698     frame.dialog({
699       modal: true,
700       resizable: false,
701       closeOnEscape: true,
702       title: elem ? elem.title || elem.innerHTML : null,
703       close: function() {
704         frame.dialog('destroy').remove();
705       },
706       buttons: buttons,
707       width: 640,
708       height: h
709     }).width(640);
710   }
c7dcb3 711 }
T 712
918bb9 713
T 714
715 /**
716  * Roundcube splitter GUI class
717  *
718  * @constructor
719  */
720 function rcube_splitter(p)
721 {
722   this.p = p;
723   this.id = p.id;
724   this.horizontal = (p.orientation == 'horizontal' || p.orientation == 'h');
725   this.halfsize = (p.size !== undefined ? p.size : 10) / 2;
726   this.pos = p.start || 0;
727   this.min = p.min || 20;
728   this.offset = p.offset || 0;
729   this.relative = p.relative ? true : false;
730   this.drag_active = false;
731   this.render = p.render;
732   this.callback = p.callback;
733
734   var me = this;
735
736   this.init = function()
737   {
738     this.p1 = $(this.p.p1);
739     this.p2 = $(this.p.p2);
740
741     // create and position the handle for this splitter
742     this.p1pos = this.relative ? this.p1.position() : this.p1.offset();
743     this.p2pos = this.relative ? this.p2.position() : this.p2.offset();
744     this.handle = $('<div>')
745       .attr('id', this.id)
746       .attr('unselectable', 'on')
747       .addClass('splitter ' + (this.horizontal ? 'splitter-h' : 'splitter-v'))
748       .appendTo(this.p1.parent())
749       .bind('mousedown', onDragStart);
750
751     if (this.horizontal) {
752       var top = this.p1pos.top + this.p1.outerHeight();
753       this.handle.css({ left:'0px', top:top+'px' });
754     }
755     else {
756       var left = this.p1pos.left + this.p1.outerWidth();
757       this.handle.css({ left:left+'px', top:'0px' });
758     }
759
760     this.elm = this.handle.get(0);
761
762     // listen to window resize on IE
763     if (bw.ie)
764       $(window).resize(function(e){ onResize(e) });
765
766     // read saved position from cookie
767     var cookie = bw.get_cookie(this.id);
768     if (cookie && !isNaN(cookie)) {
769       this.pos = parseFloat(cookie);
770       this.resize();
771     }
772     else if (this.pos) {
773       this.resize();
774       this.set_cookie();
775     }
776   };
777
778   /**
779    * Set size and position of all DOM objects
780    * according to the saved splitter position
781    */
782   this.resize = function()
783   {
784     if (this.horizontal) {
785       this.p1.css('height', Math.floor(this.pos - this.p1pos.top - this.halfsize) + 'px');
786       this.p2.css('top', Math.ceil(this.pos + this.halfsize + 2) + 'px');
787       this.handle.css('top', Math.round(this.pos - this.halfsize + this.offset)+'px');
788       if (bw.ie) {
789         var new_height = parseInt(this.p2.parent().outerHeight(), 10) - parseInt(this.p2.css('top'), 10) - (bw.ie8 ? 2 : 0);
822a1e 790         this.p2.css('height', (new_height > 0 ? new_height : 0) + 'px');
918bb9 791       }
T 792     }
793     else {
794       this.p1.css('width', Math.floor(this.pos - this.p1pos.left - this.halfsize) + 'px');
795       this.p2.css('left', Math.ceil(this.pos + this.halfsize) + 'px');
796       this.handle.css('left', Math.round(this.pos - this.halfsize + this.offset + 3)+'px');
797       if (bw.ie) {
798         var new_width = parseInt(this.p2.parent().outerWidth(), 10) - parseInt(this.p2.css('left'), 10) ;
799         this.p2.css('width', (new_width > 0 ? new_width : 0) + 'px');
800       }
801     }
802
803     this.p2.resize();
804     this.p1.resize();
805
5fea6b 806     // also resize iframe covers
T 807     if (this.drag_active) {
808       $('iframe').each(function(i, elem) {
809         var pos = $(this).offset();
810         $('#iframe-splitter-fix-'+i).css({ top: pos.top+'px', left: pos.left+'px', width:elem.offsetWidth+'px', height: elem.offsetHeight+'px' });
811       });
812     }
813
918bb9 814     if (typeof this.render == 'function')
T 815       this.render(this);
816   };
817
818   /**
819    * Handler for mousedown events
820    */
821   function onDragStart(e)
822   {
823     // disable text selection while dragging the splitter
824     if (bw.konq || bw.chrome || bw.safari)
825       document.body.style.webkitUserSelect = 'none';
826
827     me.p1pos = me.relative ? me.p1.position() : me.p1.offset();
828     me.p2pos = me.relative ? me.p2.position() : me.p2.offset();
829     me.drag_active = true;
830
831     // start listening to mousemove events
832     $(document).bind('mousemove.'+this.id, onDrag).bind('mouseup.'+this.id, onDragStop);
833
834     // enable dragging above iframes
5fea6b 835     $('iframe').each(function(i, elem) {
T 836       $('<div>')
837         .attr('id', 'iframe-splitter-fix-'+i)
838         .addClass('iframe-splitter-fix')
918bb9 839         .css({ background: '#fff',
5fea6b 840           width: elem.offsetWidth+'px', height: elem.offsetHeight+'px',
918bb9 841           position: 'absolute', opacity: '0.001', zIndex: 1000
T 842         })
843         .css($(this).offset())
844         .appendTo('body');
845       });
846   };
847
848   /**
849    * Handler for mousemove events
850    */
851   function onDrag(e)
852   {
853     if (!me.drag_active)
854       return false;
855
856     var pos = rcube_event.get_mouse_pos(e);
857
858     if (me.relative) {
859       var parent = me.p1.parent().offset();
860       pos.x -= parent.left;
861       pos.y -= parent.top;
862     }
863
864     if (me.horizontal) {
865       if (((pos.y - me.halfsize) > me.p1pos.top) && ((pos.y + me.halfsize) < (me.p2pos.top + me.p2.outerHeight()))) {
866         me.pos = Math.max(me.min, pos.y - me.offset);
867         me.resize();
868       }
869     }
870     else {
871       if (((pos.x - me.halfsize) > me.p1pos.left) && ((pos.x + me.halfsize) < (me.p2pos.left + me.p2.outerWidth()))) {
872         me.pos = Math.max(me.min, pos.x - me.offset);
873         me.resize();
874       }
875     }
876
877     me.p1pos = me.relative ? me.p1.position() : me.p1.offset();
878     me.p2pos = me.relative ? me.p2.position() : me.p2.offset();
879     return false;
880   };
881
882   /**
883    * Handler for mouseup events
884    */
885   function onDragStop(e)
886   {
887     // resume the ability to highlight text
888     if (bw.konq || bw.chrome || bw.safari)
889       document.body.style.webkitUserSelect = 'auto';
890
891     // cancel the listening for drag events
892     $(document).unbind('.'+me.id);
893     me.drag_active = false;
894
895     // remove temp divs
5fea6b 896     $('div.iframe-splitter-fix').remove();
918bb9 897
T 898     me.set_cookie();
899
900     if (typeof me.callback == 'function')
901       me.callback(me);
902
903     return bw.safari ? true : rcube_event.cancel(e);
904   };
905
906   /**
907    * Handler for window resize events
908    */
909   function onResize(e)
910   {
911     if (me.horizontal) {
912       var new_height = parseInt(me.p2.parent().outerHeight(), 10) - parseInt(me.p2[0].style.top, 10) - (bw.ie8 ? 2 : 0);
913       me.p2.css('height', (new_height > 0 ? new_height : 0) +'px');
914     }
915     else {
916       var new_width = parseInt(me.p2.parent().outerWidth(), 10) - parseInt(me.p2[0].style.left, 10);
917       me.p2.css('width', (new_width > 0 ? new_width : 0) + 'px');
918     }
919   };
920
921   /**
922    * Saves splitter position in cookie
923    */
924   this.set_cookie = function()
925   {
926     var exp = new Date();
927     exp.setYear(exp.getFullYear() + 1);
928     bw.set_cookie(this.id, this.pos, exp);
929   };
930
931 } // end class rcube_splitter
932
933