diff options
-rw-r--r-- | CHANGELOG | 8 | ||||
-rw-r--r-- | program/include/main.inc | 2 | ||||
-rw-r--r-- | program/js/app.js | 1267 | ||||
-rw-r--r-- | program/js/common.js | 123 | ||||
-rw-r--r-- | program/js/list.js | 704 | ||||
-rw-r--r-- | program/steps/addressbook/edit.inc | 2 | ||||
-rw-r--r-- | program/steps/addressbook/func.inc | 1 | ||||
-rw-r--r-- | program/steps/addressbook/save.inc | 22 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 1 | ||||
-rw-r--r-- | program/steps/settings/identities.inc | 1 | ||||
-rwxr-xr-x | skins/default/common.css | 12 |
11 files changed, 1202 insertions, 941 deletions
@@ -1,6 +1,14 @@ CHANGELOG RoundCube Webmail --------------------------- +2006/09/24 (thomasb) +---------- +- Partial client re-write with a common list class +- Re-enabled multi select of contacts (Bug #1484017) +- Enable contact editing right after creation (Bug #1459641) +- Updated Hungarian, Estonian and Traditional Chinese localization + + 2006/09/19 (thomasb) ---------- - Correct UTF-7 to UTF-8 conversion if mbstring is not available diff --git a/program/include/main.inc b/program/include/main.inc index c6e9857b0..b2e83b767 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -365,7 +365,7 @@ function load_gui() // add common javascripts $javascript = "var $JS_OBJECT_NAME = new rcube_webmail();\n"; - $javascript .= "$JS_OBJECT_NAME.set_env('comm_path', '$COMM_PATH');\n"; + $javascript .= sprintf("%s.set_env('comm_path', '%s');\n", $JS_OBJECT_NAME, str_replace('&', '&', $COMM_PATH)); if (isset($CONFIG['javascript_config'] )){ foreach ($CONFIG['javascript_config'] as $js_config_var){ diff --git a/program/js/app.js b/program/js/app.js index eedcd13e7..52050a9be 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3,21 +3,19 @@ | RoundCube Webmail Client Script | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | Copyright (C) 2005-2006, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ | Authors: Thomas Bruederli <roundcube@gmail.com> | | Charles McNulty <charles@charlesmcnulty.com> | +-----------------------------------------------------------------------+ - + | Requires: common.js, list.js | + +-----------------------------------------------------------------------+ + $Id$ */ -// Constants -var CONTROL_KEY = 1; -var SHIFT_KEY = 2; -var CONTROL_SHIFT_KEY = 3; var rcube_webmail_client; @@ -28,9 +26,6 @@ function rcube_webmail() this.buttons = new Object(); this.gui_objects = new Object(); this.commands = new Object(); - this.selection = new Array(); - this.last_selected = 0; - this.in_message_list = false; // create public reference to myself rcube_webmail_client = this; @@ -93,12 +88,13 @@ function rcube_webmail() // initialize webmail client this.init = function() { + var p = this; this.task = this.env.task; // check browser if (!bw.dom || !bw.xmlhttp_test()) { - location.href = this.env.comm_path+'&_action=error&_code=0x199'; + this.goto_url('error', '_code=0x199'); return; } @@ -116,13 +112,26 @@ function rcube_webmail() switch (this.task) { case 'mail': - var msg_list_frame = this.gui_objects.mailcontframe; - var msg_list = this.gui_objects.messagelist; - if (msg_list) + if (this.gui_objects.messagelist) { - msg_list_frame.onmousedown = function(e){return rcube_webmail_client.click_on_list(e);}; - this.init_messagelist(msg_list); + this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); + this.message_list.row_init = function(o){ p.init_message_row(o); }; + this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); }); + this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); }); + this.message_list.addEventListener('select', function(o){ p.msglist_select(o); }); + this.message_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); + this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; }); + + this.message_list.init(); this.enable_command('toggle_status', true); + + if (this.gui_objects.mailcontframe) + { + this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); }; + document.onmouseup = function(e){ return p.doc_mouse_up(e); }; + } + else + this.message_list.focus(); } // enable mail commands @@ -183,26 +192,36 @@ function rcube_webmail() case 'addressbook': - var contacts_list = this.gui_objects.contactslist; - var ldap_contacts_list = this.gui_objects.ldapcontactslist; + if (this.gui_objects.contactslist) + { + this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:false, keyboard:true}); + this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); }); + this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); }); + this.contact_list.init(); - if (contacts_list) - this.init_contactslist(contacts_list); - - if (ldap_contacts_list) - this.init_ldapsearchlist(ldap_contacts_list); + if (this.env.cid) + this.contact_list.highlight_row(this.env.cid); + + if (this.gui_objects.contactslist.parentNode) + { + this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); }; + document.onmouseup = function(e){ return p.doc_mouse_up(e); }; + } + else + this.contact_list.focus(); + } this.set_page_buttons(); - + if (this.env.cid) this.enable_command('show', 'edit', true); if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) this.enable_command('save', true); - + this.enable_command('list', 'add', true); - this.enable_command('ldappublicsearch', this.env.ldappublicsearch); + // this.enable_command('ldappublicsearch', this.env.ldappublicsearch); break; @@ -218,10 +237,17 @@ function rcube_webmail() if (this.env.action=='folders') this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true); - - var identities_list = this.gui_objects.identitieslist; - if (identities_list) - this.init_identitieslist(identities_list); + + if (this.gui_objects.identitieslist) + { + this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false}); + this.identity_list.addEventListener('select', function(o){ p.identity_select(o); }); + this.identity_list.init(); + this.identity_list.focus(); + + if (this.env.iid) + this.identity_list.highlight_row(this.env.iid); + } break; @@ -244,13 +270,6 @@ function rcube_webmail() // enable basic commands this.enable_command('logout', true); - // disable browser's contextmenus - // document.oncontextmenu = function(){ return false; } - - // load body click event - document.onmousedown = function(){ return rcube_webmail_client.reset_click(); }; - document.onkeydown = function(e){ return rcube_webmail_client.key_pressed(e, msg_list_frame); }; - // flag object as complete this.loaded = true; @@ -273,154 +292,25 @@ function rcube_webmail() } - // reset last clicked if user clicks on anything other than the message table - this.reset_click = function() - { - var id; - this.in_message_list = false; - for (var n=0; n<this.selection.length; n++) - { - id = this.selection[n]; - if (this.list_rows[id] && this.list_rows[id].obj) - { - this.set_classname(this.list_rows[id].obj, 'selected', false); - this.set_classname(this.list_rows[id].obj, 'unfocused', true); - } - } - }; - - this.click_on_list = function(e) - { - if (!e) - e = window.event; - - for (var n=0; n<this.selection.length; n++) + this.init_message_row = function(row) + { + var uid = row.uid; + if (uid && this.env.messages[uid]) { - id = this.selection[n]; - if (this.list_rows[id].obj) - { - this.set_classname(this.list_rows[id].obj, 'selected', true); - this.set_classname(this.list_rows[id].obj, 'unfocused', false); - } + row.deleted = this.env.messages[uid].deleted; + row.unread = this.env.messages[uid].unread; + row.replied = this.env.messages[uid].replied; } - var mbox_li; - if (mbox_li = this.get_mailbox_li()) - this.set_classname(mbox_li, 'unfocused', true); - - this.in_message_list = true; - e.cancelBubble = true; - }; - - this.key_pressed = function(e, msg_list_frame) { - if (this.in_message_list != true) - return true; - var keyCode = document.layers ? e.which : document.all ? event.keyCode : document.getElementById ? e.keyCode : 0; - var mod_key = this.get_modifier(e); - switch (keyCode) { - case 13: - this.command('show','',this); - break; - case 40: - case 38: - return this.use_arrow_key(keyCode, mod_key, msg_list_frame); - break; - case 46: - return this.use_delete_key(keyCode, mod_key, msg_list_frame); - break; - default: - return true; - } - return true; - } - - this.use_arrow_key = function(keyCode, mod_key, msg_list_frame) { - var scroll_to = 0; - if (keyCode == 40) { // down arrow key pressed - new_row = this.get_next_row(); - if (!new_row) return false; - scroll_to = (Number(new_row.offsetTop) + Number(new_row.offsetHeight)) - Number(msg_list_frame.offsetHeight); - } else if (keyCode == 38) { // up arrow key pressed - new_row = this.get_prev_row(); - if (!new_row) return false; - scroll_to = new_row.offsetTop; - } else {return true;} - - this.select_row(new_row.uid,mod_key,true); - - if (((Number(new_row.offsetTop)) < (Number(msg_list_frame.scrollTop))) || - ((Number(new_row.offsetTop) + Number(new_row.offsetHeight)) > (Number(msg_list_frame.scrollTop) + Number(msg_list_frame.offsetHeight)))) { - msg_list_frame.scrollTop = scroll_to; - } - return false; - }; - - this.use_delete_key = function(keyCode, mod_key, msg_list_frame){ - this.command('delete','',this); - return false; - } - - // get all message rows from HTML table and init each row - this.init_messagelist = function(msg_list) - { - if (msg_list && msg_list.tBodies[0]) + // set eventhandler to message icon + if ((row.icon = row.obj.cells[0].childNodes[0]) && row.icon.nodeName=='IMG') { - - this.message_rows = new Array(); - - var row; - for(var r=0; r<msg_list.tBodies[0].childNodes.length; r++) - { - row = msg_list.tBodies[0].childNodes[r]; - while (row && (row.nodeType != 1 || row.style.display == 'none')) { - row = row.nextSibling; - r++; - } - //row = msg_list.tBodies[0].rows[r]; - if (row) this.init_message_row(row); - } - } - - // alias to common rows array - this.list_rows = this.message_rows; - }; - - - // make references in internal array and set event handlers - this.init_message_row = function(row) - { - var uid, msg_icon; - - if (String(row.id).match(/rcmrow([0-9]+)/)) - { - uid = RegExp.$1; - row.uid = uid; - - this.message_rows[uid] = {id:row.id, obj:row, - classname:row.className, - deleted:this.env.messages[uid] ? this.env.messages[uid].deleted : null, - unread:this.env.messages[uid] ? this.env.messages[uid].unread : null, - replied:this.env.messages[uid] ? this.env.messages[uid].replied : null}; - - // set eventhandlers to table row - row.onmousedown = function(e){ return rcube_webmail_client.drag_row(e, this.uid); }; - row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.uid); }; - - if (document.all) - row.onselectstart = function() { return false; }; - - // set eventhandler to message icon - if ((msg_icon = row.cells[0].childNodes[0]) && row.cells[0].childNodes[0].nodeName=='IMG') - { - msg_icon.id = 'msgicn_'+uid; - msg_icon._row = row; - msg_icon.onmousedown = function(e) { rcube_webmail_client.command('toggle_status', this); }; - - // get message icon and save original icon src - this.message_rows[uid].icon = msg_icon; - } + var p = this; + row.icon.id = 'msgicn_'+row.uid; + row.icon._row = row.obj; + row.icon.onmousedown = function(e) { p.command('toggle_status', this); }; } - }; + }; // init message compose form: set focus and eventhandlers @@ -455,7 +345,7 @@ function rcube_webmail() else if (input_subject && input_subject.value=='') input_subject.focus(); else if (input_message) - this.set_caret2start(input_message); // input_message.focus(); + this.set_caret2start(input_message); // get summary of all field values this.cmp_hash = this.compose_field_hash(); @@ -468,116 +358,21 @@ function rcube_webmail() { var handler = function(e){ return rcube_webmail_client.ksearch_keypress(e,this); }; var handler2 = function(e){ return rcube_webmail_client.ksearch_blur(e,this); }; - + if (bw.safari) - { obj.addEventListener('keydown', handler, false); - // obj.addEventListener('blur', handler2, false); - } else if (bw.mz) { obj.addEventListener('keypress', handler, false); obj.addEventListener('blur', handler2, false); } else if (bw.ie) - { obj.onkeydown = handler; - //obj.attachEvent('onkeydown', handler); - // obj.attachEvent('onblur', handler2, false); - } - - obj.setAttribute('autocomplete', 'off'); - }; - - - - // get all contact rows from HTML table and init each row - this.init_contactslist = function(contacts_list) - { - if (contacts_list && contacts_list.tBodies[0]) - { - this.contact_rows = new Array(); - - var row; - for(var r=0; r<contacts_list.tBodies[0].childNodes.length; r++) - { - row = contacts_list.tBodies[0].childNodes[r]; - this.init_table_row(row, 'contact_rows'); - } - } - - // alias to common rows array - this.list_rows = this.contact_rows; - - if (this.env.cid) - this.highlight_row(this.env.cid); - }; - - - // get all contact rows from HTML table and init each row - this.init_ldapsearchlist = function(ldap_contacts_list) - { - if (ldap_contacts_list && ldap_contacts_list.tBodies[0]) - { - this.ldap_contact_rows = new Array(); - - var row; - for(var r=0; r<ldap_contacts_list.tBodies[0].childNodes.length; r++) - { - row = ldap_contacts_list.tBodies[0].childNodes[r]; - this.init_table_row(row, 'ldap_contact_rows'); - } - } - - // alias to common rows array - this.list_rows = this.ldap_contact_rows; - }; - - - // make references in internal array and set event handlers - this.init_table_row = function(row, array_name) - { - var cid; - - if (String(row.id).match(/rcmrow([0-9]+)/)) - { - cid = RegExp.$1; - row.cid = cid; - this[array_name][cid] = {id:row.id, - obj:row, - classname:row.className}; - - // set eventhandlers to table row - row.onmousedown = function(e) { rcube_webmail_client.in_selection_before=this.cid; return false; }; // fake for drag handler - row.onmouseup = function(e){ return rcube_webmail_client.click_row(e, this.cid); }; - } + obj.setAttribute('autocomplete', 'off'); }; - // get all contact rows from HTML table and init each row - this.init_identitieslist = function(identities_list) - { - if (identities_list && identities_list.tBodies[0]) - { - this.identity_rows = new Array(); - - var row; - for(var r=0; r<identities_list.tBodies[0].childNodes.length; r++) - { - row = identities_list.tBodies[0].childNodes[r]; - this.init_table_row(row, 'identity_rows'); - } - } - - // alias to common rows array - this.list_rows = this.identity_rows; - - if (this.env.iid) - this.highlight_row(this.env.iid); - }; - - /*********************************************************/ /********* client command interface *********/ @@ -621,7 +416,7 @@ function rcube_webmail() break; case 'logout': - location.href = this.env.comm_path+'&_action=logout'; + this.goto_url('logout'); break; // commands to switch task @@ -639,11 +434,6 @@ function rcube_webmail() if (this.env.search_request<0 || (this.env.search_request && props != this.env.mailbox)) this.reset_qsearch(); - // Reset message list header, unless returning from compose/read/etc - // don't know what this is good for (thomasb, 2006/07/25) - //if (this.env.mailbox != props && this.message_rows) - // this.clear_message_list_header(); - this.list_mailbox(props); } else if (this.task=='addressbook') @@ -711,11 +501,8 @@ function rcube_webmail() var uid = this.get_single_uid(); if (uid && (!this.env.uid || uid != this.env.uid)) { - if (this.env.mailbox==this.env.drafts_mailbox) - { - this.set_busy(true); - location.href = this.env.comm_path+'&_action=compose&_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); - } + if (this.env.mailbox == this.env.drafts_mailbox) + this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); else this.show_message(uid); } @@ -730,6 +517,9 @@ function rcube_webmail() case 'add': if (this.task=='addressbook') + this.load_contact(0, 'add'); + + /* LDAP stuff, has to be re-written with new address book if (!window.frames[this.env.contentframe].rcmail) this.load_contact(0, 'add'); else @@ -739,9 +529,10 @@ function rcube_webmail() else this.load_contact(0, 'add'); } + */ else if (this.task=='settings') { - this.clear_selection(); + this.identity_list.clear_selection(); this.load_identity(0, 'add-identity'); } break; @@ -819,11 +610,11 @@ function rcube_webmail() if (props._row.uid) { uid = props._row.uid; - this.dont_select = true; + this.message_list.dont_select = true; // toggle read/unread - if (this.message_rows[uid].deleted) { - flag = 'undelete'; - } else if (!this.message_rows[uid].unread) + if (this.message_list.rows[uid].deleted) { + flag = 'undelete'; + } else if (!this.message_list.rows[uid].unread) flag = 'unread'; } @@ -836,12 +627,12 @@ function rcube_webmail() break; case 'load-attachment': - var url = this.env.comm_path+'&_action=get&_mbox='+this.env.mailbox+'&_uid='+this.env.uid+'&_part='+props.part; + var qstring = '_mbox='+this.env.mailbox+'&_uid='+this.env.uid+'&_part='+props.part; // open attachment in frame if it's of a supported mimetype if (this.env.uid && props.mimetype && find_in_array(props.mimetype, this.mimetypes)>=0) { - this.attachment_win = window.open(url+'&_frame=1', 'rcubemailattachment'); + this.attachment_win = window.open(this.env.comm_path+'&_action=get'+url+'&_frame=1', 'rcubemailattachment'); if (this.attachment_win) { setTimeout(this.ref+'.attachment_win.focus()', 10); @@ -849,27 +640,25 @@ function rcube_webmail() } } - location.href = url; + this.goto_url('get', qstring+'&_download=1'); break; case 'select-all': - this.select_all(props); + this.message_list.select_all(props); break; case 'select-none': - this.clear_selection(); + this.message_list.clear_selection(); break; case 'nextmessage': if (this.env.next_uid) this.show_message(this.env.next_uid); - //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.next_uid+'&_mbox='+this.env.mailbox; break; case 'previousmessage': if (this.env.prev_uid) this.show_message(this.env.prev_uid); - //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.prev_uid+'&_mbox='+this.env.mailbox; break; case 'checkmail': @@ -894,14 +683,18 @@ function rcube_webmail() // use contact_id passed as command parameter if (props) a_cids[a_cids.length] = props; - // get selected contacts else { + var selection = this.contact_list.get_selection(); + for (var n=0; n<selection.length; n++) + a_cids[a_cids.length] = selection[n]; + + /* LDAP stuff, has to be re-written with new address book if (!window.frames[this.env.contentframe].rcmail.selection.length) { - for (var n=0; n<this.selection.length; n++) - a_cids[a_cids.length] = this.selection[n]; + for (var n=0; n<selection.length; n++) + a_cids[a_cids.length] = selection[n]; } else { @@ -910,6 +703,7 @@ function rcube_webmail() for (var n=0; n<frameRcmail.selection.length; n++) a_cids[a_cids.length] = frameRcmail.ldap_contact_rows[frameRcmail.selection[n]].obj.cells[1].innerHTML; } + */ } if (a_cids.length) url += '&_to='+a_cids.join(','); @@ -918,12 +712,13 @@ function rcube_webmail() } else if (props) - url += '&_to='+encodeURIComponent(props); + url += '&_to='+urlencode(props); // don't know if this is necessary... url = url.replace(/&_framed=1/, ""); - this.set_busy(true); + + alert(url) // need parent in case we are coming from the contact frame if (this.env.framed) @@ -997,19 +792,13 @@ function rcube_webmail() case 'reply': var uid; if (uid = this.get_single_uid()) - { - this.set_busy(true); - location.href = this.env.comm_path+'&_action=compose&_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : ''); - } + this.goto_url('compose', '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(command=='reply-all' ? '&_all=1' : ''), true); break; case 'forward': var uid; if (uid = this.get_single_uid()) - { - this.set_busy(true); - location.href = this.env.comm_path+'&_action=compose&_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); - } + this.goto_url('compose', '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); break; case 'print': @@ -1064,18 +853,18 @@ function rcube_webmail() // user settings commands case 'preferences': - location.href = this.env.comm_path; + this.goto_url(''); break; case 'identities': - location.href = this.env.comm_path+'&_action=identities'; + this.goto_url('identities'); break; case 'delete-identity': this.delete_identity(); case 'folders': - location.href = this.env.comm_path+'&_action=folders'; + this.goto_url('folders'); break; case 'subscribe': @@ -1201,6 +990,15 @@ function rcube_webmail() /*********************************************************/ + this.doc_mouse_up = function(e) + { + if (this.message_list) + this.message_list.blur(); + else if (this.contact_list) + this.contact_list.blur(); + }; + + // onmouseup handler for mailboxlist item this.mbox_mouse_up = function(mbox) { @@ -1216,300 +1014,61 @@ function rcube_webmail() }; - // onmousedown-handler of message list row - this.drag_row = function(e, id) + this.click_on_list = function(e) { - this.in_selection_before = this.in_selection(id) ? id : false; + if (this.message_list) + this.message_list.focus(); + else if (this.contact_list) + this.contact_list.focus(); - // don't do anything (another action processed before) - if (this.dont_select) - return false; - - // selects currently unselected row - if (!this.in_selection_before && !this.list_rows[id].clicked) - { - var mod_key = this.get_modifier(e); - this.select_row(id,mod_key,false); - } - - if (this.selection.length) - { - this.drag_start = true; - document.onmousemove = function(e){ return rcube_webmail_client.drag_mouse_move(e); }; - document.onmouseup = function(e){ return rcube_webmail_client.drag_mouse_up(e); }; - } + var mbox_li; + if (mbox_li = this.get_mailbox_li()) + this.set_classname(mbox_li, 'unfocused', true); - return false; + rcube_event.cancel(e); }; - // onmouseup-handler of message list row - this.click_row = function(e, id) + this.msglist_select = function(list) { - var mod_key = this.get_modifier(e); - - // don't do anything (another action processed before) - if (this.dont_select) + var selected = list.selection.length==1; + if (this.env.mailbox == this.env.drafts_mailbox) { - this.dont_select = false; - return false; - } - - // unselects currently selected row - if (!this.drag_active && this.in_selection_before==id && !this.list_rows[id].clicked) - this.select_row(id,mod_key,false); - - this.drag_start = false; - this.in_selection_before = false; - - // row was double clicked - if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && this.in_selection(id)) - { - if (this.env.mailbox==this.env.drafts_mailbox) - { - this.set_busy(true); - location.href = this.env.comm_path+'&_action=compose&_draft_uid='+id+'&_mbox='+urlencode(this.env.mailbox); - } - else - { - this.show_message(id); - } - return false; + this.enable_command('show', selected); + this.enable_command('delete', 'moveto', list.selection.length>0 ? true : false); } - else if (this.task=='addressbook') - { - if (this.contact_rows && this.selection.length==1) - { - this.load_contact(this.selection[0], 'show', true); - // change the text for the add contact button - var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A'); - for (i = 0; i < links.length; i++) - { - var onclickstring = new String(links[i].onclick); - if (onclickstring.search('\"add\"') != -1) - links[i].title = this.env.newcontact; - } - } - else if (this.contact_rows && this.contact_rows[id].clicked) - { - this.load_contact(id, 'show'); - return false; - } - else if (this.ldap_contact_rows && !this.ldap_contact_rows[id].clicked) - { - // clear selection - parent.rcmail.clear_selection(); - - // disable delete - parent.rcmail.set_button('delete', 'pas'); - - // change the text for the add contact button - var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A'); - for (i = 0; i < links.length; i++) - { - var onclickstring = new String(links[i].onclick); - if (onclickstring.search('\"add\"') != -1) - links[i].title = this.env.addcontact; - } - } - // handle double click event - else if (this.ldap_contact_rows && this.selection.length==1 && this.ldap_contact_rows[id].clicked) - this.command('compose', this.ldap_contact_rows[id].obj.cells[1].innerHTML); - else if (this.env.contentframe) - { - var elm = document.getElementById(this.env.contentframe); - elm.style.visibility = 'hidden'; - } - } - else if (this.task=='settings') + else { - if (this.selection.length==1) - this.command('edit', this.selection[0]); + this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected); + this.enable_command('delete', 'moveto', list.selection.length>0 ? true : false); } - - this.list_rows[id].clicked = true; - setTimeout(this.ref+'.list_rows['+id+'].clicked=false;', this.dblclick_time); - - return false; - }; - + }; - /*********************************************************/ - /********* (message) list functionality *********/ - /*********************************************************/ - - // get next and previous rows that are not hidden - this.get_next_row = function(){ - if (!this.list_rows) return false; - var last_selected_row = this.list_rows[this.last_selected]; - var new_row = last_selected_row.obj.nextSibling; - while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) { - new_row = new_row.nextSibling; - } - return new_row; - } - - this.get_prev_row = function(){ - if (!this.list_rows) return false; - var last_selected_row = this.list_rows[this.last_selected]; - var new_row = last_selected_row.obj.previousSibling; - while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) { - new_row = new_row.previousSibling; - } - return new_row; - } - - // highlight/unhighlight a row - this.highlight_row = function(id, multiple) + this.msglist_dbl_click = function(list) { - var selected = false - - if (this.list_rows[id] && !multiple) - { - this.clear_selection(); - this.selection[0] = id; - this.list_rows[id].obj.className += ' selected'; - selected = true; - } - - else if (this.list_rows[id]) - { - if (!this.in_selection(id)) // select row - { - this.selection[this.selection.length] = id; - this.set_classname(this.list_rows[id].obj, 'selected', true); - } - else // unselect row - { - var p = find_in_array(id, this.selection); - var a_pre = this.selection.slice(0, p); - var a_post = this.selection.slice(p+1, this.selection.length); - this.selection = a_pre.concat(a_post); - this.set_classname(this.list_rows[id].obj, 'selected', false); - this.set_classname(this.list_rows[id].obj, 'unfocused', false); - } - selected = (this.selection.length==1); - } - - // enable/disable commands for message - if (this.task=='mail') - { - if (this.env.mailbox==this.env.drafts_mailbox) - { - this.enable_command('show', selected); - this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false); - } - else - { - this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected); - this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false); - } - } - else if (this.task=='addressbook') - { - this.enable_command('edit', /*'print',*/ selected); - this.enable_command('delete', 'compose', this.selection.length>0 ? true : false); - } + var uid = list.get_single_selection(); + if (uid && this.env.mailbox == this.env.drafts_mailbox) + this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); + else if (uid) + this.show_message(uid); }; -// selects or unselects the proper row depending on the modifier key pressed - this.select_row = function(id,mod_key,with_mouse) { - if (!mod_key) { - this.shift_start = id; - this.highlight_row(id, false); - } else { - switch (mod_key) { - case SHIFT_KEY: { - this.shift_select(id,false); - break; } - case CONTROL_KEY: { - this.shift_start = id; - if (!with_mouse) - this.highlight_row(id, true); - break; - } - case CONTROL_SHIFT_KEY: { - this.shift_select(id,true); - break; - } - default: { - this.highlight_row(id, false); - break; - } - } - } - if (this.last_selected != 0 && this.list_rows[this.last_selected]) - this.set_classname(this.list_rows[this.last_selected].obj, 'focused', false); - - this.last_selected = id; - this.set_classname(this.list_rows[id].obj, 'focused', true); - }; - - this.shift_select = function(id, control) { - var from_rowIndex = this.list_rows[this.shift_start].obj.rowIndex; - var to_rowIndex = this.list_rows[id].obj.rowIndex; - - var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex); - var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex); - - // iterate through the entire message list - for (var n in this.list_rows) { - if ((this.list_rows[n].obj.rowIndex >= i) && (this.list_rows[n].obj.rowIndex <= j)) { - if (!this.in_selection(n)) - this.highlight_row(n, true); - } else { - if (this.in_selection(n) && !control) - this.highlight_row(n, true); - } - } - }; - - - this.clear_selection = function() + this.msglist_keypress = function(list) { - for(var n=0; n<this.selection.length; n++) - if (this.list_rows[this.selection[n]]) { - this.set_classname(this.list_rows[this.selection[n]].obj, 'selected', false); - this.set_classname(this.list_rows[this.selection[n]].obj, 'unfocused', false); - } - this.selection = new Array(); + if (list.key_pressed == list.ENTER_KEY) + this.command('show'); + else if (list.key_pressed == list.DELETE_KEY) + this.command('delete'); }; - // check if given id is part of the current selection - this.in_selection = function(id) - { - for(var n in this.selection) - if (this.selection[n]==id) - return true; - - return false; - }; - - // select each row in list - this.select_all = function(filter) - { - if (!this.list_rows || !this.list_rows.length) - return false; - - // reset selection first - this.clear_selection(); - - for (var n in this.list_rows) - { - if (!filter || this.list_rows[n][filter]==true) - { - this.last_selected = n; - this.highlight_row(n, true); - } - } + /*********************************************************/ + /********* (message) list functionality *********/ + /*********************************************************/ - return true; - }; - // when user doble-clicks on a row this.show_message = function(id, safe) @@ -1521,7 +1080,7 @@ function rcube_webmail() target = window.frames[this.env.contentframe]; add_url = '&_framed=1'; } - + if (safe) add_url = '&_safe=1'; @@ -1574,7 +1133,7 @@ function rcube_webmail() page = 1; add_url += '&_refresh=1'; this.env.current_page = page; - this.clear_selection(); + this.message_list.clear_selection(); } // also send search request to get the right messages @@ -1609,7 +1168,7 @@ function rcube_webmail() this.list_mailbox_remote = function(mbox, page, add_url) { // clear message list first - this.clear_message_list(); + this.message_list.clear(); // send request to server var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : ''); @@ -1618,38 +1177,6 @@ function rcube_webmail() }; - this.clear_message_list = function() - { - var table = this.gui_objects.messagelist; - - var tbody = document.createElement('TBODY'); - table.insertBefore(tbody, table.tBodies[0]); - table.removeChild(table.tBodies[1]); - - this.message_rows = new Array(); - this.list_rows = this.message_rows; - - }; - - - this.clear_message_list_header = function() - { - var table; - if (table = this.gui_objects.messagelist) - { - if (table.colgroup) - table.removeChild(table.colgroup); - if (table.tHead) - table.removeChild(table.tHead); - - var colgroup = document.createElement('COLGROUP'); - var thead = document.createElement('THEAD'); - table.insertBefore(colgroup, table.tBodies[0]); - table.insertBefore(thead, table.tBodies[0]); - } - }; - - this.expunge_mailbox = function(mbox) { var lock = false; @@ -1694,14 +1221,14 @@ function rcube_webmail() this.focus_mailbox = function(mbox) { var mbox_li; - if (this.drag_active && mbox != this.env.mailbox && (mbox_li = this.get_mailbox_li(mbox))) + if (this.drag_active && mbox != this.env.mailbox && (mbox_li = this.get_mailbox_li(mbox))) this.set_classname(mbox_li, 'droptarget', true); } this.unfocus_mailbox = function(mbox) { var mbox_li; - if (this.drag_active && (mbox_li = this.get_mailbox_li(mbox))) + if (this.drag_active && (mbox_li = this.get_mailbox_li(mbox))) this.set_classname(mbox_li, 'droptarget', false); } @@ -1709,7 +1236,8 @@ function rcube_webmail() this.move_messages = function(mbox) { // exit if no mailbox specified or if selection is empty - if (!mbox || !(this.selection.length || this.env.uid) || mbox==this.env.mailbox) + var selection = this.message_list.get_selection(); + if (!mbox || !(selection.length || this.env.uid) || mbox==this.env.mailbox) return; var a_uids = new Array(); @@ -1719,19 +1247,14 @@ function rcube_webmail() else { var id; - for (var n=0; n<this.selection.length; n++) + for (var n=0; n<selection.length; n++) { - id = this.selection[n]; + id = selection[n]; a_uids[a_uids.length] = id; - - // 'remove' message row from list (just hide it) - if (this.message_rows[id].obj) - this.message_rows[id].obj.style.display = 'none'; + this.message_list.remove_row(id); } - next_row = this.get_next_row(); - prev_row = this.get_prev_row(); - new_row = (next_row) ? next_row : prev_row; - if (new_row) this.select_row(new_row.uid,false,false); + + this.message_list.select_next(); } var lock = false; @@ -1742,13 +1265,16 @@ function rcube_webmail() lock = true; this.set_busy(true, 'movingmessage'); } + // send request to server this.http_request('moveto', '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+'&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : ''), lock); }; - this.permanently_remove_messages = function() { + this.permanently_remove_messages = function() + { // exit if no mailbox specified or if selection is empty - if (!(this.selection.length || this.env.uid)) + var selection = this.message_list.get_selection(); + if (!(selection.length || this.env.uid)) return; var a_uids = new Array(); @@ -1758,21 +1284,16 @@ function rcube_webmail() else { var id; - for (var n=0; n<this.selection.length; n++) + for (var n=0; n<selection.length; n++) { - id = this.selection[n]; + id = selection[n]; a_uids[a_uids.length] = id; - - // 'remove' message row from list (just hide it) - if (this.message_rows[id].obj) - this.message_rows[id].obj.style.display = 'none'; + this.message_list.remove_row(id); } - } - next_row = this.get_next_row(); - prev_row = this.get_prev_row(); - new_row = (next_row) ? next_row : prev_row; - if (new_row) this.select_row(new_row.uid,false,false); + this.message_list.select_next(); + } + // send request to server this.http_request('delete', '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+'&_from='+(this.env.action ? this.env.action : '')); } @@ -1782,8 +1303,10 @@ function rcube_webmail() this.delete_messages = function() { // exit if no mailbox specified or if selection is empty - if (!(this.selection.length || this.env.uid)) + var selection = this.message_list.get_selection(); + if (!(selection.length || this.env.uid)) return; + // if there is a trash mailbox defined and we're not currently in it: if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase()!=String(this.env.trash_mailbox).toLowerCase()) this.move_messages(this.env.trash_mailbox); @@ -1791,22 +1314,18 @@ function rcube_webmail() else if (this.env.trash_mailbox && String(this.env.mailbox).toLowerCase() == String(this.env.trash_mailbox).toLowerCase()) this.permanently_remove_messages(); // if there isn't a defined trash mailbox and the config is set to flag for deletion - else if (!this.env.trash_mailbox && this.env.flag_for_deletion) { + else if (!this.env.trash_mailbox && this.env.flag_for_deletion) + { flag = 'delete'; this.mark_message(flag); - if(this.env.action=="show"){ + if(this.env.action=="show") this.command('nextmessage','',this); - } else if (this.selection.length == 1) { - next_row = this.get_next_row(); - prev_row = this.get_prev_row(); - new_row = (next_row) ? next_row : prev_row; - if (new_row) this.select_row(new_row.uid,false,false); + else if (selection.length == 1) + this.message_list.select_next(); } // if there isn't a defined trash mailbox and the config is set NOT to flag for deletion - }else if (!this.env.trash_mailbox && !this.env.flag_for_deletion) { + else if (!this.env.trash_mailbox) this.permanently_remove_messages(); - } - return; }; @@ -1814,6 +1333,7 @@ function rcube_webmail() this.mark_message = function(flag, uid) { var a_uids = new Array(); + var selection = this.message_list.get_selection(); if (uid) a_uids[0] = uid; @@ -1822,16 +1342,18 @@ function rcube_webmail() else { var id; - for (var n=0; n<this.selection.length; n++) + for (var n=0; n<selection.length; n++) { - id = this.selection[n]; + id = selection[n]; a_uids[a_uids.length] = id; } } - switch (flag) { + + switch (flag) + { case 'read': case 'unread': - this.toggle_read_status(flag,a_uids); + this.toggle_read_status(flag, a_uids); break; case 'delete': case 'undelete': @@ -1841,66 +1363,70 @@ function rcube_webmail() }; // set class to read/unread - this.toggle_read_status = function(flag, a_uids) { + this.toggle_read_status = function(flag, a_uids) + { // mark all message rows as read/unread var icn_src; + var rows = this.message_list.rows; for (var i=0; i<a_uids.length; i++) { uid = a_uids[i]; - if (this.message_rows[uid]) + if (rows[uid]) { - this.message_rows[uid].unread = (flag=='unread' ? true : false); + rows[uid].unread = (flag=='unread' ? true : false); - if (this.message_rows[uid].classname.indexOf('unread')<0 && this.message_rows[uid].unread) + if (rows[uid].classname.indexOf('unread')<0 && rows[uid].unread) { - this.message_rows[uid].classname += ' unread'; - this.set_classname(this.message_rows[uid].obj, 'unread', true); + rows[uid].classname += ' unread'; + this.set_classname(rows[uid].obj, 'unread', true); if (this.env.unreadicon) icn_src = this.env.unreadicon; } - else if (!this.message_rows[uid].unread) + else if (!rows[uid].unread) { - this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*unread/, ''); - this.set_classname(this.message_rows[uid].obj, 'unread', false); + rows[uid].classname = rows[uid].classname.replace(/\s*unread/, ''); + this.set_classname(rows[uid].obj, 'unread', false); - if (this.message_rows[uid].replied && this.env.repliedicon) + if (rows[uid].replied && this.env.repliedicon) icn_src = this.env.repliedicon; else if (this.env.messageicon) icn_src = this.env.messageicon; } - if (this.message_rows[uid].icon && icn_src) - this.message_rows[uid].icon.src = icn_src; + if (rows[uid].icon && icn_src) + rows[uid].icon.src = icn_src; } } - this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag='+flag); - } + + this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag='+flag); + }; // mark all message rows as deleted/undeleted - this.toggle_delete_status = function(a_uids) { - if (this.env.read_when_deleted) { + this.toggle_delete_status = function(a_uids) + { + if (this.env.read_when_deleted) this.toggle_read_status('read',a_uids); - } + // if deleting message from "view message" don't bother with delete icon if (this.env.action == "show") return false; - if (a_uids.length==1){ - if(this.message_rows[a_uids[0]].classname.indexOf('deleted') < 0 ){ - this.flag_as_deleted(a_uids) - } else { - this.flag_as_undeleted(a_uids) - } + var rows = this.message_list.rows; + if (a_uids.length==1) { + if (rows[a_uids[0]] && rows[a_uids[0]].classname.indexOf('deleted') < 0) + this.flag_as_deleted(a_uids); + else + this.flag_as_undeleted(a_uids); + return true; } var all_deleted = true; - for (var i=0; i<a_uids.length; i++) { uid = a_uids[i]; - if (this.message_rows[uid]) { - if (this.message_rows[uid].classname.indexOf('deleted')<0) { + if (rows[uid]) { + if (rows[uid].classname.indexOf('deleted')<0) { all_deleted = false; break; } @@ -1913,59 +1439,67 @@ function rcube_webmail() this.flag_as_deleted(a_uids); return true; - } + }; - this.flag_as_undeleted = function(a_uids){ + + this.flag_as_undeleted = function(a_uids) + { // if deleting message from "view message" don't bother with delete icon if (this.env.action == "show") return false; var icn_src; + var rows = this.message_list.rows; for (var i=0; i<a_uids.length; i++) { uid = a_uids[i]; - if (this.message_rows[uid]) { - this.message_rows[uid].deleted = false; + if (rows[uid]) { + rows[uid].deleted = false; - if (this.message_rows[uid].classname.indexOf('deleted') > 0) { - this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*deleted/, ''); - this.set_classname(this.message_rows[uid].obj, 'deleted', false); + if (rows[uid].classname.indexOf('deleted') > 0) { + rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, ''); + this.set_classname(rows[uid].obj, 'deleted', false); } - if (this.message_rows[uid].unread && this.env.unreadicon) + if (rows[uid].unread && this.env.unreadicon) icn_src = this.env.unreadicon; - else if (this.message_rows[uid].replied && this.env.repliedicon) + else if (rows[uid].replied && this.env.repliedicon) icn_src = this.env.repliedicon; else if (this.env.messageicon) icn_src = this.env.messageicon; - if (this.message_rows[uid].icon && icn_src) - this.message_rows[uid].icon.src = icn_src; + if (rows[uid].icon && icn_src) + rows[uid].icon.src = icn_src; } } + this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=undelete'); return true; - } + }; + - this.flag_as_deleted = function(a_uids) { + this.flag_as_deleted = function(a_uids) + { // if deleting message from "view message" don't bother with delete icon if (this.env.action == "show") return false; + var rows = this.message_list.rows; for (var i=0; i<a_uids.length; i++) { uid = a_uids[i]; - if (this.message_rows[uid]) { - this.message_rows[uid].deleted = true; + if (rows[uid]) { + rows[uid].deleted = true; - if (this.message_rows[uid].classname.indexOf('deleted')<0) { - this.message_rows[uid].classname += ' deleted'; - this.set_classname(this.message_rows[uid].obj, 'deleted', true); + if (rows[uid].classname.indexOf('deleted')<0) { + rows[uid].classname += ' deleted'; + this.set_classname(rows[uid].obj, 'deleted', true); } - if (this.message_rows[uid].icon && this.env.deletedicon) - this.message_rows[uid].icon.src = this.env.deletedicon; + if (rows[uid].icon && this.env.deletedicon) + rows[uid].icon.src = this.env.deletedicon; } } + this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=delete'); return true; - } + }; this.get_mailbox_li = function(mbox) @@ -2034,9 +1568,9 @@ function rcube_webmail() this.set_spellcheck_state = function(s) { - this.spellcheck_ready = (s=='check_spelling' || s=='ready'); + this.spellcheck_ready = (s=='check_spelling' || s=='ready'); this.enable_command('spellcheck', this.spellcheck_ready); - }; + }; this.auto_save_start = function() @@ -2272,7 +1806,7 @@ function rcube_webmail() { if (value && mbox) { - this.clear_message_list(); + this.message_list.clear(); this.set_busy(true, 'searching'); this.http_request('search', '_search='+value+'&_mbox='+mbox, true); } @@ -2527,6 +2061,30 @@ function rcube_webmail() /*********************************************************/ + this.contactlist_keypress = function(list) + { + if (list.key_pressed == list.DELETE_KEY) + this.command('delete'); + }; + + + this.contactlist_select = function(list) + { + var id, frame; + if (id = list.get_single_selection()) + this.load_contact(id, 'show'); + else if (frame = document.getElementById(this.env.contentframe)) + frame.style.visibility = 'hidden'; + + this.enable_command('edit', id?true:false); + + if (list.selection.length) + this.enable_command('delete', 'compose', true); + + return false; + }; + + this.list_contacts = function(page) { var add_url = ''; @@ -2549,21 +2107,15 @@ function rcube_webmail() } this.set_busy(true, 'loading'); - location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url; + target.location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url; }; // send remote request to load contacts list this.list_contacts_remote = function(page) { - // clear list - var table = this.gui_objects.contactslist; - var tbody = document.createElement('TBODY'); - table.insertBefore(tbody, table.tBodies[0]); - table.tBodies[1].style.display = 'none'; - - this.contact_rows = new Array(); - this.list_rows = this.contact_rows; + // clear message list first + this.contact_list.clear(); // send request to server var url = page ? '&_page='+page : ''; @@ -2585,11 +2137,7 @@ function rcube_webmail() } else if (framed) return false; - - //if (this.env.framed && add_url=='') - - // add_url = '&_framed=1'; - + if (action && (cid || action=='add')) { this.set_busy(true); @@ -2602,7 +2150,8 @@ function rcube_webmail() this.delete_contacts = function() { // exit if no mailbox specified or if selection is empty - if (!(this.selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm'))) + var selection = this.contact_list.get_selection(); + if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm'))) return; var a_cids = new Array(); @@ -2612,18 +2161,15 @@ function rcube_webmail() else { var id; - for (var n=0; n<this.selection.length; n++) + for (var n=0; n<selection.length; n++) { - id = this.selection[n]; + id = selection[n]; a_cids[a_cids.length] = id; - - // 'remove' row from list (just hide it) - if (this.contact_rows[id].obj) - this.contact_rows[id].obj.style.display = 'none'; + this.contact_list.remove_row(id); } // hide content frame if we delete the currently displayed contact - if (this.selection.length==1 && this.env.contentframe) + if (selection.length==1 && this.env.contentframe) { var elm = document.getElementById(this.env.contentframe); elm.style.visibility = 'hidden'; @@ -2639,19 +2185,21 @@ function rcube_webmail() // update a contact record in the list this.update_contact_row = function(cid, cols_arr) { - if (!this.contact_rows[cid] || !this.contact_rows[cid].obj) - return false; - - var row = this.contact_rows[cid].obj; - for (var c=0; c<cols_arr.length; c++){ - if (row.cells[c]) - row.cells[c].innerHTML = cols_arr[c]; - } - return true; + var row; + if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj)) + { + for (var c=0; c<cols_arr.length; c++) + if (row.cells[c]) + row.cells[c].innerHTML = cols_arr[c]; + + return true; + } + + return false; }; - - + // load ldap search form + // deprecated this.ldappublicsearch = function(action) { var add_url = ''; @@ -2663,8 +2211,7 @@ function rcube_webmail() document.getElementById(this.env.contentframe).style.visibility = 'inherit'; } else - return false; - + return false; if (action == 'ldappublicsearch') target.location.href = this.env.comm_path+'&_action='+action+add_url; @@ -2695,13 +2242,18 @@ function rcube_webmail() } return false; } - /*********************************************************/ /********* user settings methods *********/ /*********************************************************/ + this.identity_select = function(list) + { + var id; + if (id = list.get_single_selection()) + this.load_identity(id, 'edit-identity'); + }; // load contact record this.load_identity = function(id, action) @@ -2731,11 +2283,12 @@ function rcube_webmail() this.delete_identity = function(id) { // exit if no mailbox specified or if selection is empty - if (!(this.selection.length || this.env.iid)) + var selection = this.identity_list.get_selection(); + if (!(selection.length || this.env.iid)) return; if (!id) - id = this.env.iid ? this.env.iid : this.selection[0]; + id = this.env.iid ? this.env.iid : selection[0]; /* // 'remove' row from list (just hide it) @@ -2747,10 +2300,7 @@ function rcube_webmail() */ // if (this.env.framed && id) - this.set_busy(true); - location.href = this.env.comm_path+'&_action=delete-identity&_iid='+id; - // else if (id) - // this.http_request('delete-identity', '_iid='+id); + this.goto_url('delete-identity', '_iid='+id, true); return true; }; @@ -2758,8 +2308,8 @@ function rcube_webmail() // tell server to create and subscribe a new mailbox this.create_folder = function(name) { - if (this.edit_folder) - this.reset_folder_rename(); + if (this.edit_folder) + this.reset_folder_rename(); var form; if ((form = this.gui_objects.editform) && form.elements['_folder_name']) @@ -2801,12 +2351,12 @@ function rcube_webmail() var id = this.get_folder_row_id(folder); // reset current renaming - if (temp = this.edit_folder) - { - this.reset_folder_rename(); - if (temp == id) - return; - } + if (temp = this.edit_folder) + { + this.reset_folder_rename(); + if (temp == id) + return; + } if (id && (row = document.getElementById(id))) { @@ -2857,8 +2407,8 @@ function rcube_webmail() // delete a specific mailbox with all its messages this.delete_folder = function(folder) { - if (this.edit_folder) - this.reset_folder_rename(); + if (this.edit_folder) + this.reset_folder_rename(); if (folder) this.http_request('delete-folder', '_mboxes='+urlencode(folder)); @@ -2883,7 +2433,7 @@ function rcube_webmail() if (!id || !(refrow = document.getElementById(refid))) { // Refresh page if we don't have a table row to clone - location.href = this.env.comm_path+'&_action=folders'; + this.goto_url('folders'); } else { @@ -3271,7 +2821,7 @@ function rcube_webmail() this.set_classname(mbox_li, 'selected', true); } } - + // also update mailbox name in window title if (document.title) { @@ -3317,9 +2867,9 @@ function rcube_webmail() // create a table row in the message list this.add_message_row = function(uid, cols, flags, attachment, attop) { - if (!this.gui_objects.messagelist || !this.gui_objects.messagelist.tBodies[0]) + if (!this.gui_objects.messagelist || !this.message_list) return false; - + var tbody = this.gui_objects.messagelist.tBodies[0]; var rowcount = tbody.rows.length; var even = rowcount%2; @@ -3331,8 +2881,8 @@ function rcube_webmail() var row = document.createElement('TR'); row.id = 'rcmrow'+uid; row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '')+(flags.deleted ? ' deleted' : ''); - - if (this.in_selection(uid)) + + if (this.message_list.in_selection(uid)) row.className += ' selected'; var icon = flags.deleted && this.env.deletedicon ? this.env.deletedicon: @@ -3358,13 +2908,8 @@ function rcube_webmail() col.className = 'icon'; col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" border="0" />' : ''; row.appendChild(col); - - if (attop && tbody.rows.length) - tbody.insertBefore(row, tbody.firstChild); - else - tbody.appendChild(row); - - this.init_message_row(row); + + this.message_list.insert_row(row, attop); }; @@ -3384,7 +2929,7 @@ function rcube_webmail() if (this.gui_objects.quotadisplay) this.gui_objects.quotadisplay.innerHTML = text; }; - + // update the mailboxlist this.set_unread_count = function(mbox, count, set_title) @@ -3433,7 +2978,7 @@ function rcube_webmail() // add row to contacts list - this.add_contact_row = function(cid, cols) + this.add_contact_row = function(cid, cols, select) { if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0]) return false; @@ -3446,7 +2991,7 @@ function rcube_webmail() row.id = 'rcmrow'+cid; row.className = 'contact '+(even ? 'even' : 'odd'); - if (this.in_selection(cid)) + if (this.contact_list.in_selection(cid)) row.className += ' selected'; // add each submitted col @@ -3458,8 +3003,7 @@ function rcube_webmail() row.appendChild(col); } - tbody.appendChild(row); - this.init_table_row(row, 'contact_rows'); + this.contact_list.insert_row(row); }; @@ -3474,78 +3018,25 @@ function rcube_webmail() { tinyMCE.execCommand('mceRemoveControl', true, textElementName); } - } + }; + + /********************************************************/ - /********* drag & drop methods *********/ + /********* remote request methods *********/ /********************************************************/ - this.drag_mouse_move = function(e) + this.goto_url = function(action, query, lock) { - if (this.drag_start) - { - if (!this.draglayer) - this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000}); - - // get subjects of selectedd messages - var names = ''; - var c, subject, obj; - for(var n=0; n<this.selection.length; n++) - { - if (n>12) // only show 12 lines - { - names += '...'; - break; - } - - if (this.message_rows[this.selection[n]].obj) - { - obj = this.message_rows[this.selection[n]].obj; - subject = ''; - - for(c=0; c<obj.childNodes.length; c++) - if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3) - { - subject = obj.childNodes[c].firstChild.data; - names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />'; - } - } - } - - this.draglayer.write(names); - this.draglayer.show(1); - } - - var pos = this.get_mouse_pos(e); - this.draglayer.move(pos.x+20, pos.y-5); - - this.drag_start = false; - this.drag_active = true; - - return false; - }; - + if (lock) + this.set_busy(true); - this.drag_mouse_up = function() - { - document.onmousemove = null; - - if (this.draglayer && this.draglayer.visible) - this.draglayer.show(0); - - this.drag_active = false; - - return false; + var querystring = query ? '&'+query : ''; + location.href = this.env.comm_path+'&_action='+action+querystring; }; - - /********************************************************/ - /********* remote request methods *********/ - /********************************************************/ - - this.http_sockets = new Array(); // find a non-busy socket or create a new one @@ -3677,91 +3168,16 @@ function rcube_webmail() // and return the message uid this.get_single_uid = function() { - return this.env.uid ? this.env.uid : (this.selection.length==1 ? this.selection[0] : null); + return this.env.uid ? this.env.uid : (this.message_list ? this.message_list.get_single_selection() : null); }; // same as above but for contacts this.get_single_cid = function() { - return this.env.cid ? this.env.cid : (this.selection.length==1 ? this.selection[0] : null); + return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null); }; -/* deprecated methods - - // check if Shift-key is pressed on event - this.check_shiftkey = function(e) - { - if(!e && window.event) - e = window.event; - - if(bw.linux && bw.ns4 && e.modifiers) - return true; - else if((bw.ns4 && e.modifiers & Event.SHIFT_MASK) || (e && e.shiftKey)) - return true; - else - return false; - } - - // check if Shift-key is pressed on event - this.check_ctrlkey = function(e) - { - if(!e && window.event) - e = window.event; - - if(bw.linux && bw.ns4 && e.modifiers) - return true; - else if (bw.mac) - return this.check_shiftkey(e); - else if((bw.ns4 && e.modifiers & Event.CTRL_MASK) || (e && e.ctrlKey)) - return true; - else - return false; - } -*/ - - // returns modifier key (constants defined at top of file) - this.get_modifier = function(e) - { - var opcode = 0; - e = e || window.event; - - if (bw.mac && e) - { - opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); - return opcode; - } - if (e) - { - opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); - return opcode; - } - if (e.cancelBubble) - { - e.cancelBubble = true; - e.returnValue = false; - } - else if (e.preventDefault) - e.preventDefault(); - } - - - this.get_mouse_pos = function(e) - { - if(!e) e = window.event; - var mX = (e.pageX) ? e.pageX : e.clientX; - var mY = (e.pageY) ? e.pageY : e.clientY; - - if(document.body && document.all) - { - mX += document.body.scrollLeft; - mY += document.body.scrollTop; - } - - return { x:mX, y:mY }; - }; - - this.get_caret_pos = function(obj) { if (typeof(obj.selectionEnd)!='undefined') @@ -3826,7 +3242,10 @@ function rcube_webmail() -// class for HTTP requests +/** + * Class for sending HTTP requests + * @constructor + */ function rcube_http_request() { this.url = ''; @@ -3889,7 +3308,7 @@ function rcube_http_request() { // default value for contentType if not provided contentType = typeof(contentType) != 'undefined' ? - contentType : 'application/x-www-form-urlencoded'; + contentType : 'application/x-www-form-urlencoded'; this.build(); diff --git a/program/js/common.js b/program/js/common.js index b0dc5564a..d5ecf8f89 100644 --- a/program/js/common.js +++ b/program/js/common.js @@ -3,7 +3,7 @@ | RoundCube common js library | | | | This file is part of the RoundCube web development suite | - | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | Copyright (C) 2005-2006, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -13,8 +13,16 @@ $Id$ */ +// Constants +var CONTROL_KEY = 1; +var SHIFT_KEY = 2; +var CONTROL_SHIFT_KEY = 3; -// default browsercheck + +/** + * Default browser check class + * @construcotr + */ function roundcube_browser() { this.ver = parseFloat(navigator.appVersion); @@ -92,10 +100,118 @@ function roundcube_browser() } +// static functions for event handling +var rcube_event = { + +/** + * returns modifier key (constants defined at top of file) + */ +get_modifier: function(e) +{ + var opcode = 0; + e = e || window.event; + + if (bw.mac && e) + { + opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); + return opcode; + } + if (e) + { + opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); + return opcode; + } +}, + +/** + * Return absolute mouse position of an event + */ +get_mouse_pos: function(e) +{ + if (!e) e = window.event; + var mX = (e.pageX) ? e.pageX : e.clientX; + var mY = (e.pageY) ? e.pageY : e.clientY; + + if (document.body && document.all) + { + mX += document.body.scrollLeft; + mY += document.body.scrollTop; + } + + return { x:mX, y:mY }; +}, + +/** + * Add an object method as event listener to a certain element + */ +add_listener: function(p) +{ + if (!p.object || !p.method) // not enough arguments + return; + if (!p.element) + p.element = document; + + if (!p.object._rc_events) + p.object._rc_events = []; + + var key = p.event + '*' + p.method; + if (!p.object._rc_events[key]) + p.object._rc_events[key] = function(e){ return p.object[p.method](e); }; + + if (p.element.addEventListener) + p.element.addEventListener(p.event, p.object._rc_events[key], false); + else if (p.element.attachEvent) + p.element.attachEvent('on'+p.event, p.object._rc_events[key]); + else + p.element['on'+p.event] = p.object._rc_events[key]; +}, + +/** + * Remove event listener + */ +remove_listener: function(p) +{ + if (!p.element) + p.element = document; + + var key = p.event + '*' + p.method; + if (p.object && p.object._rc_events && p.object._rc_events[key]) { + if (p.element.removeEventListener) + p.element.removeEventListener(p.event, p.object._rc_events[key], false); + else if (p.element.detachEvent) + p.element.detachEvent('on'+p.event, p.object._rc_events[key]); + else + p.element['on'+p.event] = null; + } +}, + +/** + * Prevent event propagation and bubbeling + */ +cancel: function(evt) +{ + var e = evt ? evt : window.event; + if (e.preventDefault) + e.preventDefault(); + if (e.stopPropagation) + e.stopPropagation(); + + e.cancelBubble = true; + e.returnValue = false; + return false; +} + +}; var rcube_layer_objects = new Array(); + +/** + * RoundCube generic layer (floating box) class + * + * @constructor + */ function rcube_layer(id, attributes) { this.name = id; @@ -263,6 +379,7 @@ function rcube_layer(id, attributes) } } + // check if input is a valid email address // By Cal Henderson <cal@iamcal.com> // http://code.iamcal.com/php/rfc822/ @@ -346,7 +463,7 @@ function find_in_array() // make a string URL safe function urlencode(str) { - return window.encodeURI ? encodeURI(str).replace(/&/g, '%26') : escape(str); + return window.encodeURIComponent ? encodeURIComponent(str) : escape(str); } diff --git a/program/js/list.js b/program/js/list.js new file mode 100644 index 000000000..88719049a --- /dev/null +++ b/program/js/list.js @@ -0,0 +1,704 @@ +/* + +-----------------------------------------------------------------------+ + | RoundCube List Widget | + | | + | This file is part of the RoundCube Webmail client | + | Copyright (C) 2006, RoundCube Dev, - Switzerland | + | Licensed under the GNU GPL | + | | + +-----------------------------------------------------------------------+ + | Authors: Thomas Bruederli <roundcube@gmail.com> | + | Charles McNulty <charles@charlesmcnulty.com> | + +-----------------------------------------------------------------------+ + | Requires: common.js | + +-----------------------------------------------------------------------+ + + $Id: list.js 344 2006-09-18 03:49:28Z thomasb $ +*/ + + +/** + * RoundCube List Widget class + * @contructor + */ +function rcube_list_widget(list, p) + { + // static contants + this.ENTER_KEY = 13; + this.DELETE_KEY = 46; + + this.list = list ? list : null; + this.frame = null; + this.rows = []; + this.selection = []; + + this.multiselect = false; + this.draggable = false; + this.keyboard = false; + + this.dont_select = false; + this.drag_active = false; + this.last_selected = 0; + this.in_selection_before = false; + this.focused = false; + this.drag_mouse_start = null; + this.dblclick_time = 600; + this.row_init = function(){}; + this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragend:[] }; + + // overwrite default paramaters + if (p && typeof(p)=='object') + for (var n in p) + this[n] = p[n]; + } + + +rcube_list_widget.prototype = { + + +/** + * get all message rows from HTML table and init each row + */ +init: function() +{ + if (this.list && this.list.tBodies[0]) + { + this.rows = new Array(); + + var row; + for(var r=0; r<this.list.tBodies[0].childNodes.length; r++) + { + row = this.list.tBodies[0].childNodes[r]; + while (row && (row.nodeType != 1 || row.style.display == 'none')) + { + row = row.nextSibling; + r++; + } + + this.init_row(row); + } + + this.frame = this.list.parentNode; + + // set body events + if (this.keyboard) + rcube_event.add_listener({element:document, event:'keydown', object:this, method:'key_press'}); + } +}, + + +/** + * + */ +init_row: function(row) +{ + // make references in internal array and set event handlers + if (row && String(row.id).match(/rcmrow([0-9]+)/)) + { + var p = this; + var uid = RegExp.$1; + row.uid = uid; + this.rows[uid] = {uid:uid, id:row.id, obj:row, classname:row.className}; + + // set eventhandlers to table row + row.onmousedown = function(e){ return p.drag_row(e, this.uid); }; + row.onmouseup = function(e){ return p.click_row(e, this.uid); }; + + if (document.all) + row.onselectstart = function() { return false; }; + + this.row_init(this.rows[uid]); + } +}, + + +/** + * + */ +clear: function() +{ + var tbody = document.createElement('TBODY'); + this.list.insertBefore(tbody, this.list.tBodies[0]); + this.list.removeChild(this.list.tBodies[1]); + this.rows = new Array(); +}, + + +/** + * 'remove' message row from list (just hide it) + */ +remove_row: function(uid) +{ + if (this.rows[uid].obj) + this.rows[uid].obj.style.display = 'none'; + + this.rows[uid] = null; +}, + + +/** + * + */ +insert_row: function(row, attop) +{ + var tbody = this.list.tBodies[0]; + + if (attop && tbody.rows.length) + tbody.insertBefore(row, tbody.firstChild); + else + tbody.appendChild(row); + + this.init_row(row); +}, + + + +/** + * Set focur to the list + */ +focus: function(e) +{ + this.focused = true; + for (var n=0; n<this.selection.length; n++) + { + id = this.selection[n]; + if (this.rows[id].obj) + { + this.set_classname(this.rows[id].obj, 'selected', true); + this.set_classname(this.rows[id].obj, 'unfocused', false); + } + } + + if (e || (e = window.event)) + rcube_event.cancel(e); +}, + + +/** + * remove focus from the list + */ +blur: function() +{ + var id; + this.focused = false; + for (var n=0; n<this.selection.length; n++) + { + id = this.selection[n]; + if (this.rows[id] && this.rows[id].obj) + { + this.set_classname(this.rows[id].obj, 'selected', false); + this.set_classname(this.rows[id].obj, 'unfocused', true); + } + } +}, + + +/** + * onmousedown-handler of message list row + */ +drag_row: function(e, id) +{ + this.in_selection_before = this.in_selection(id) ? id : false; + + // don't do anything (another action processed before) + if (this.dont_select) + return false; + + // selects currently unselected row + if (!this.in_selection_before) + { + var mod_key = rcube_event.get_modifier(e); + this.select_row(id, mod_key, false); + } + + if (this.draggable && this.selection.length) + { + this.drag_start = true; + this.drag_mouse_start = rcube_event.get_mouse_pos(e); + rcube_event.add_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'}); + rcube_event.add_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'}); + } + + return false; +}, + + +/** + * onmouseup-handler of message list row + */ +click_row: function(e, id) +{ + var now = new Date().getTime(); + var mod_key = rcube_event.get_modifier(e); + + // don't do anything (another action processed before) + if (this.dont_select) + { + this.dont_select = false; + return false; + } + + var dblclicked = now - this.rows[id].clicked < this.dblclick_time; + + // unselects currently selected row + if (!this.drag_active && this.in_selection_before == id && !dblclicked) + this.select_row(id, mod_key, false); + + this.drag_start = false; + this.in_selection_before = false; + + // row was double clicked + if (this.rows && dblclicked && this.in_selection(id)) + this.trigger_event('dblclick'); + else + this.trigger_event('click'); + + if (!this.drag_active) + rcube_event.cancel(e); + + this.rows[id].clicked = now; + return false; +}, + + +/** + * get next and previous rows that are not hidden + */ +get_next_row: function() +{ + if (!this.rows) + return false; + + var last_selected_row = this.rows[this.last_selected]; + var new_row = last_selected_row && last_selected_row.obj.nextSibling; + while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) + new_row = new_row.nextSibling; + + return new_row; +}, + +get_prev_row: function() +{ + if (!this.rows) + return false; + + var last_selected_row = this.rows[this.last_selected]; + var new_row = last_selected_row && last_selected_row.obj.previousSibling; + while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) + new_row = new_row.previousSibling; + + return new_row; +}, + + +// selects or unselects the proper row depending on the modifier key pressed +select_row: function(id, mod_key, with_mouse) +{ + var select_before = this.selection.join(','); + if (!this.multiselect) + mod_key = 0; + + if (!mod_key) + { + this.shift_start = id; + this.highlight_row(id, false); + } + else + { + switch (mod_key) + { + case SHIFT_KEY: + this.shift_select(id, false); + break; + + case CONTROL_KEY: + this.shift_start = id; + if (!with_mouse) + this.highlight_row(id, true); + break; + + case CONTROL_SHIFT_KEY: + this.shift_select(id, true); + break; + + default: + this.highlight_row(id, false); + break; + } + } + + // trigger event if selection changed + if (this.selection.join(',') != select_before) + this.trigger_event('select'); + + if (this.last_selected != 0 && this.rows[this.last_selected]) + this.set_classname(this.rows[this.last_selected].obj, 'focused', false); + + this.last_selected = id; + this.set_classname(this.rows[id].obj, 'focused', true); +}, + + +/** + * Alias method for select_row + */ +select: function(id) +{ + this.select_row(id, false); + this.scrollto(id); +}, + + +/** + * Select row next to the last selected one. + * Either below or above. + */ +select_next: function() +{ + var next_row = this.get_next_row(); + var prev_row = this.get_prev_row(); + var new_row = (next_row) ? next_row : prev_row; + if (new_row) + this.select_row(new_row.uid, false, false); +}, + + +/** + * Perform selection when shift key is pressed + */ +shift_select: function(id, control) +{ + var from_rowIndex = this.rows[this.shift_start].obj.rowIndex; + var to_rowIndex = this.rows[id].obj.rowIndex; + + var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex); + var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex); + + // iterate through the entire message list + for (var n in this.rows) + { + if ((this.rows[n].obj.rowIndex >= i) && (this.rows[n].obj.rowIndex <= j)) + { + if (!this.in_selection(n)) + this.highlight_row(n, true); + } + else + { + if (this.in_selection(n) && !control) + this.highlight_row(n, true); + } + } +}, + + +/** + * Check if given id is part of the current selection + */ +in_selection: function(id) +{ + for(var n in this.selection) + if (this.selection[n]==id) + return true; + + return false; +}, + + +/** + * Select each row in list + */ +select_all: function(filter) +{ + if (!this.rows || !this.rows.length) + return false; + + // reset selection first + this.clear_selection(); + + for (var n in this.rows) + { + if (!filter || this.rows[n][filter]==true) + { + this.last_selected = n; + this.highlight_row(n, true); + } + } + + return true; +}, + + +/** + * Unselect all selected rows + */ +clear_selection: function() +{ + for(var n=0; n<this.selection.length; n++) + if (this.rows[this.selection[n]]) + { + this.set_classname(this.rows[this.selection[n]].obj, 'selected', false); + this.set_classname(this.rows[this.selection[n]].obj, 'unfocused', false); + } + + this.selection = new Array(); +}, + + +/** + * Getter for the selection array + */ +get_selection: function() +{ + return this.selection; +}, + + +/** + * Return the ID if only one row is selected + */ +get_single_selection: function() +{ + if (this.selection.length == 1) + return this.selection[0]; + else + return null; +}, + + +/** + * Highlight/unhighlight a row + */ +highlight_row: function(id, multiple) +{ + if (this.rows[id] && !multiple) + { + this.clear_selection(); + this.selection[0] = id; + this.set_classname(this.rows[id].obj, 'selected', true) + } + else if (this.rows[id]) + { + if (!this.in_selection(id)) // select row + { + this.selection[this.selection.length] = id; + this.set_classname(this.rows[id].obj, 'selected', true); + } + else // unselect row + { + var p = find_in_array(id, this.selection); + var a_pre = this.selection.slice(0, p); + var a_post = this.selection.slice(p+1, this.selection.length); + this.selection = a_pre.concat(a_post); + this.set_classname(this.rows[id].obj, 'selected', false); + this.set_classname(this.rows[id].obj, 'unfocused', false); + } + } +}, + + +/** + * Handler for keyboard events + */ +key_press: function(e) +{ + if (this.focused != true) + return true; + + var keyCode = document.layers ? e.which : document.all ? event.keyCode : document.getElementById ? e.keyCode : 0; + var mod_key = rcube_event.get_modifier(e); + switch (keyCode) + { + case 40: + case 38: + return this.use_arrow_key(keyCode, mod_key); + break; + + default: + this.key_pressed = keyCode; + this.trigger_event('keypress'); + } + + return true; +}, + + +/** + * Special handling method for arrow keys + */ +use_arrow_key: function(keyCode, mod_key) +{ + var new_row; + if (keyCode == 40) // down arrow key pressed + new_row = this.get_next_row(); + else if (keyCode == 38) // up arrow key pressed + new_row = this.get_prev_row(); + + if (new_row) + { + this.select_row(new_row.uid, mod_key, true); + this.scrollto(new_row.uid); + } + + return false; +}, + + +/** + * Try to scroll the list to make the specified row visible + */ +scrollto: function(id) +{ + var row = this.rows[id].obj; + if (row && this.frame) + { + var scroll_to = Number(row.offsetTop); + + if (scroll_to < Number(this.frame.scrollTop)) + this.frame.scrollTop = scroll_to; + else if (scroll_to + Number(row.offsetHeight) > Number(this.frame.scrollTop) + Number(this.frame.offsetHeight)) + this.frame.scrollTop = (scroll_to + Number(row.offsetHeight)) - Number(this.frame.offsetHeight); + } +}, + + +/** + * Handler for mouse move events + */ +drag_mouse_move: function(e) +{ + if (this.drag_start) + { + // check mouse movement, of less than 3 pixels, don't start dragging + var m = rcube_event.get_mouse_pos(e); + if (!this.drag_mouse_start || (Math.abs(m.x - this.drag_mouse_start.x) < 3 && Math.abs(m.y - this.drag_mouse_start.y) < 3)) + return false; + + if (!this.draglayer) + this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000}); + + // get subjects of selectedd messages + var names = ''; + var c, subject, obj; + for(var n=0; n<this.selection.length; n++) + { + if (n>12) // only show 12 lines + { + names += '...'; + break; + } + + if (this.rows[this.selection[n]].obj) + { + obj = this.rows[this.selection[n]].obj; + subject = ''; + + for(c=0; c<obj.childNodes.length; c++) + if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3) + { + subject = obj.childNodes[c].firstChild.data; + names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />'; + } + } + } + + this.draglayer.write(names); + this.draglayer.show(1); + + this.drag_active = true; + this.trigger_event('dragstart'); + } + + if (this.drag_active && this.draglayer) + { + var pos = rcube_event.get_mouse_pos(e); + this.draglayer.move(pos.x+20, pos.y-5); + } + + this.drag_start = false; + + return false; +}, + + +/** + * Handler for mouse up events + */ +drag_mouse_up: function(e) +{ + document.onmousemove = null; + + if (this.draglayer && this.draglayer.visible) + this.draglayer.show(0); + + this.drag_active = false; + this.trigger_event('dragend'); + + rcube_event.remove_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'}); + rcube_event.remove_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'}); + + return rcube_event.cancel(e); +}, + + + +/** + * set/unset a specific class name + */ +set_classname: function(obj, classname, set) +{ + var reg = new RegExp('\s*'+classname, 'i'); + if (!set && obj.className.match(reg)) + obj.className = obj.className.replace(reg, ''); + else if (set && !obj.className.match(reg)) + obj.className += ' '+classname; +}, + + +/** + * Setter for object event handlers + * + * @param {String} Event name + * @param {Function} Handler function + * @return Listener ID (used to remove this handler later on) + */ +addEventListener: function(evt, handler) +{ + if (this.events[evt]) { + var handle = this.events[evt].length; + this.events[evt][handle] = handler; + return handle; + } + else + return false; +}, + + +/** + * Removes a specific event listener + * + * @param {String} Event name + * @param {Int} Listener ID to remove + */ +removeEventListener: function(evt, handle) +{ + if (this.events[evt] && this.events[evt][handle]) + this.events[evt][handle] = null; +}, + + +/** + * This will execute all registered event handlers + * @private + */ +trigger_event: function(evt) +{ + if (this.events[evt] && this.events[evt].length) { + for (var i=0; i<this.events[evt].length; i++) + if (typeof(this.events[evt][i]) == 'function') + this.events[evt][i](this); + } +} + + +}; + diff --git a/program/steps/addressbook/edit.inc b/program/steps/addressbook/edit.inc index a129d0094..245c02409 100644 --- a/program/steps/addressbook/edit.inc +++ b/program/steps/addressbook/edit.inc @@ -94,7 +94,7 @@ function get_form_tags($attrib) $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task'])); $hiddenfields->add(array('name' => '_action', 'value' => 'save')); - if ($_GET['_framed'] || $_POST['_framed']) + if ($GLOBALS['_framed']) $hiddenfields->add(array('name' => '_framed', 'value' => 1)); if ($CONTACT_RECORD['contact_id']) diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 6d3e95ea2..0df6df3e3 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -90,6 +90,7 @@ function rcmail_contacts_list($attrib) //$javascript .= sprintf("%s.set_env('contacts', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr)); $OUTPUT->add_script($javascript); + $OUTPUT->include_script('list.js'); // add some labels to client rcube_add_label('deletecontactconfirm'); diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index abea316e5..a50d0388a 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -62,9 +62,6 @@ if (!empty($_POST['_cid'])) if ($updated) { - $_action = 'show'; - show_message('successfullysaved', 'confirmation'); - if ($_framed) { // define list of cols to be displayed @@ -89,9 +86,11 @@ if (!empty($_POST['_cid'])) $_POST['_cid'], array2js($a_js_cols))); - // show confirmation - show_message('successfullysaved', 'confirmation'); } + + // show confirmation + show_message('successfullysaved', 'confirmation'); + rcmail_overwrite_action('show'); } else { @@ -184,9 +183,6 @@ else { if (!$ldap_form) { - $_action = 'show'; - $_GET['_cid'] = $insert_id; - if ($_framed) { // add contact row or jump to the page where it should appear @@ -198,11 +194,11 @@ else $_SESSION['user_id']); $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME); - $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n", + $commands .= sprintf("if(parent.%s)parent.%s.contact_list.select('%d');\n", $JS_OBJECT_NAME, $JS_OBJECT_NAME, $insert_id); - + // update record count display $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n", $JS_OBJECT_NAME, @@ -213,7 +209,8 @@ else } // show confirmation - show_message('successfullysaved', 'confirmation'); + show_message('successfullysaved', 'confirmation'); + $_GET['_cid'] = $insert_id; } else { @@ -231,7 +228,7 @@ else } // display the last insert id - $commands .= sprintf("if(parent.%s)parent.%s.select('%d');\n", + $commands .= sprintf("if(parent.%s)parent.%s.contact_list.select('%d');\n", $JS_OBJECT_NAME, $JS_OBJECT_NAME, $last_id); @@ -248,6 +245,7 @@ else // show confirmation show_message('successfullysaved', 'confirmation'); + rcmail_overwrite_action('show'); } else { diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index bfddb5aa8..18ce9eca8 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -495,6 +495,7 @@ function rcmail_message_list($attrib) $javascript .= sprintf("%s.set_env('messages', %s);", $JS_OBJECT_NAME, array2js($a_js_message_arr)); $OUTPUT->add_script($javascript); + $OUTPUT->include_script('list.js'); return $out; } diff --git a/program/steps/settings/identities.inc b/program/steps/settings/identities.inc index 8bad6859a..caaa45342 100644 --- a/program/steps/settings/identities.inc +++ b/program/steps/settings/identities.inc @@ -22,6 +22,7 @@ if ($USER_DATA = $DB->fetch_assoc($sql_result)) $PAGE_TITLE = sprintf('%s (%s@%s)', rcube_label('identities'), $USER_DATA['username'], $USER_DATA['mail_host']); +$OUTPUT->include_script('list.js'); // similar function as /steps/addressbook/func.inc::rcmail_contact_frame() diff --git a/skins/default/common.css b/skins/default/common.css index 6cec3cf9e..27ddfae6d 100755 --- a/skins/default/common.css +++ b/skins/default/common.css @@ -252,6 +252,18 @@ table.records-table tr.selected td background-color: #CC3333; } +table.records-table tr.focused td +{ + border-bottom: thin dotted; + border-top: thin dotted; +} + +table.records-table tr.unfocused td +{ + font-weight: bold; + color: #FFFFFF; + background-color: #929292; +} /***** roundcube webmail pre-defined classes *****/ |