From f89f03cd6ae4a1b3f98e39c2e01d9e40f8a286b4 Mon Sep 17 00:00:00 2001 From: thomascube Date: Fri, 12 Sep 2008 11:12:05 +0000 Subject: [PATCH] Refactor drag & drop functionality. Don't rely on browser events anymore (#1484453) --- program/include/main.inc | 88 +++++++++++++++++------------- program/js/app.js | 72 +++++++++++++----------- program/js/common.js | 26 +++++++-- program/js/list.js | 15 +++-- program/steps/addressbook/func.inc | 48 +++------------- skins/default/addresses.css | 9 ++- skins/default/mail.css | 5 ++ 7 files changed, 142 insertions(+), 121 deletions(-) diff --git a/program/include/main.inc b/program/include/main.inc index a6ad93d2b..87c727700 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -929,11 +929,11 @@ function rcmail_mailbox_list($attrib) $out = $select->show(); } else { - $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $attrib['maxlength'], $attrib['realnames']), html::$common_attrib); - } - - if ($type=='ul') { + $js_mailboxlist = array(); + $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib); + $RCMAIL->output->add_gui_object('mailboxlist', $attrib['id']); + $RCMAIL->output->set_env('mailboxes', $js_mailboxlist); $RCMAIL->output->set_env('collapsed_folders', $RCMAIL->config->get('collapsed_folders')); } @@ -973,64 +973,68 @@ function rcmail_mailbox_select($p = array()) * @access private */ function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='') - { +{ $pos = strpos($folder, $delm); - if ($pos !== false) - { + if ($pos !== false) { $subFolders = substr($folder, $pos+1); $currentFolder = substr($folder, 0, $pos); - } - else - { + $virtual = !isset($arrFolders[$currentFolder]); + } + else { $subFolders = false; $currentFolder = $folder; - } + $virtual = false; + } $path .= $currentFolder; - if (!isset($arrFolders[$currentFolder])) - { + if (!isset($arrFolders[$currentFolder])) { $arrFolders[$currentFolder] = array( 'id' => $path, 'name' => rcube_charset_convert($currentFolder, 'UTF-7'), + 'virtual' => $virtual, 'folders' => array()); - } + } + else + $arrFolders[$currentFolder]['virtual'] = $virtual; if (!empty($subFolders)) rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm); - } +} /** * Return html for a structured list <ul> for the mailbox tree * @access private */ -function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $realnames=false, $nestLevel=0) - { +function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel=0) +{ global $RCMAIL, $CONFIG; + + $maxlength = intval($attrib['maxlength']); + $realnames = (bool)$attrib['realnames']; + $msgcounts = $RCMAIL->imap->get_cache('messagecount'); $idx = 0; $out = ''; - foreach ($arrFolders as $key => $folder) - { + foreach ($arrFolders as $key => $folder) { $zebra_class = (($nestLevel+1)*$idx) % 2 == 0 ? 'even' : 'odd'; $title = null; - if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) + if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) { $foldername = rcube_label($folder_class); - else - { + } + else { $foldername = $folder['name']; // shorten the folder name to a given length - if ($maxlength && $maxlength>1) - { + if ($maxlength && $maxlength > 1) { $fname = abbreviate_string($foldername, $maxlength); if ($fname != $foldername) $title = $foldername; $foldername = $fname; - } } + } // make folder name safe for ids and class names $folder_id = asciiwords($folder['id'], true); @@ -1054,35 +1058,45 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, $maxlength, $ $classes[] = 'selected'; $collapsed = preg_match('/&'.rawurlencode($folder['id']).'&/', $RCMAIL->config->get('collapsed_folders')); + $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0; + + if ($folder['virtual']) + $classes[] = 'virtual'; + else if ($unread) + $classes[] = 'unread'; $js_name = JQ($folder['id']); + $html_name = Q($foldername . ($unread ? " ($unread)" : '')); + $link_attrib = $folder['virtual'] ? array() : array( + 'href' => rcmail_url('', array('_mbox' => $folder['id'])), + 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name), + 'title' => $title, + ); + $out .= html::tag('li', array( 'id' => "rcmli".$folder_id, 'class' => join(' ', $classes), 'noclose' => true), - html::a(array( - 'href' => rcmail_url('', array('_mbox' => $folder['id'])), - 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name), - 'onmouseover' => sprintf("return %s.focus_folder('%s')", JS_OBJECT_NAME, $js_name), - 'onmouseout' => sprintf("return %s.unfocus_folder('%s')", JS_OBJECT_NAME, $js_name), - 'onmouseup' => sprintf("return %s.folder_mouse_up('%s')", JS_OBJECT_NAME, $js_name), - 'title' => $title, - ), Q($foldername)) . + html::a($link_attrib, $html_name) . (!empty($folder['folders']) ? html::div(array( 'class' => ($collapsed ? 'collapsed' : 'expanded'), 'style' => "position:absolute", 'onclick' => sprintf("%s.command('collapse-folder', '%s')", JS_OBJECT_NAME, $js_name) ), ' ') : '')); - if (!empty($folder['folders'])) - $out .= "\n\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $realnames, $nestLevel+1) . "\n"; + $jslist[$folder_id] = array('id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual']); + + if (!empty($folder['folders'])) { + $out .= html::tag('ul', array('style' => ($collapsed ? "display:none;" : null)), + rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1)); + } $out .= "\n"; $idx++; - } + } return $out; - } +} /** diff --git a/program/js/app.js b/program/js/app.js index 3812a73de..36072e384 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -133,6 +133,7 @@ function rcube_webmail() 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; if (p.preview_timer) clearTimeout(p.preview_timer); }); + this.message_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); }); this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; }); this.message_list.init(); @@ -252,6 +253,7 @@ function rcube_webmail() 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.addEventListener('dragstart', function(o){ p.drag_active = true; }); + this.contact_list.addEventListener('dragmove', function(o, e){ p.drag_move(e); }); this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; }); this.contact_list.init(); @@ -1158,27 +1160,43 @@ function rcube_webmail() this.doc_mouse_up = function(e) - { - if (this.message_list) + { + var model, li; + + if (this.message_list) { this.message_list.blur(); - else if (this.contact_list) + model = this.env.mailboxes; + } + else if (this.contact_list) { this.contact_list.blur(); - }; - - this.focus_folder = function(id) - { - var li; - if (this.drag_active && this.check_droptarget(id) && (li = this.get_folder_li(id))) - this.set_classname(li, 'droptarget', true); + model = this.env.address_sources; + } + + // handle mouse release when dragging + if (this.drag_active && model) { + for (var k in model) { + if ((li = this.get_folder_li(k)) && rcube_mouse_is_over(e, li.firstChild) && this.check_droptarget(k)) { + this.set_classname(li, 'droptarget', false); + this.command('moveto', model[k].id); + break; + } + } } + }; - this.unfocus_folder = function(id) - { + this.drag_move = function(e) + { var li; - if (this.drag_active && (li = this.get_folder_li(id))) - this.set_classname(li, 'droptarget', false); + var model = this.task == 'mail' ? this.env.mailboxes : this.env.address_sources; + + if (this.gui_objects.folderlist && model) { + for (var k in model) { + if (li = this.get_folder_li(k)) + this.set_classname(li, 'droptarget', (rcube_mouse_is_over(e, li.firstChild) && this.check_droptarget(k))); + } } - + }; + this.collapse_folder = function(id) { var div; @@ -1211,16 +1229,6 @@ function rcube_webmail() } } - // onmouseup handler for folder list item - this.folder_mouse_up = function(id) - { - if (this.drag_active) - { - this.unfocus_folder(id); - this.command('moveto', id); - } - }; - this.click_on_list = function(e) { if (this.message_list) @@ -1232,7 +1240,7 @@ function rcube_webmail() if (mbox_li = this.get_folder_li()) this.set_classname(mbox_li, 'unfocused', true); - rcube_event.cancel(e); + return rcube_event.get_button(e) == 2 ? true : rcube_event.cancel(e); }; @@ -1303,7 +1311,7 @@ function rcube_webmail() this.check_droptarget = function(id) { if (this.task == 'mail') - return (id != this.env.mailbox); + return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual); else if (this.task == 'addressbook') return (id != this.env.source && this.env.address_sources[id] && !this.env.address_sources[id].readonly); else if (this.task == 'settings') @@ -3009,12 +3017,12 @@ function rcube_webmail() row.id = id; if (before && (before = this.get_folder_row_id(before))) - tbody.insertBefore(row, document.getElementById(before)); + tbody.insertBefore(row, document.getElementById(before)); else - tbody.appendChild(row); + tbody.appendChild(row); if (replace) - tbody.removeChild(replace); + tbody.removeChild(replace); } // add to folder/row-ID map @@ -3136,9 +3144,9 @@ function rcube_webmail() { var cell, td; var new_row = document.createElement('TR'); - for(var n=0; n= pos.x) && (mouse.x < (pos.x + obj.offsetWidth)) && + (mouse.y >= pos.y) && (mouse.y < (pos.y + obj.offsetHeight))); +} + /** * Return the currently applied value of a css property diff --git a/program/js/list.js b/program/js/list.js index f33c67c7b..926d98aa7 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -51,7 +51,7 @@ function rcube_list_widget(list, p) this.drag_mouse_start = null; this.dblclick_time = 600; this.row_init = function(){}; - this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragend:[] }; + this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragmove:[], dragend:[] }; // overwrite default paramaters if (p && typeof(p)=='object') @@ -221,7 +221,11 @@ drag_row: function(e, id) var evtarget = rcube_event.get_target(e); if (this.dont_select || (evtarget && (evtarget.tagName == 'INPUT' || evtarget.tagName == 'IMG'))) return false; - + + // accept right-clicks + if (rcube_event.get_button(e) == 2) + return true; + this.in_selection_before = this.in_selection(id) ? id : false; // selects currently unselected row @@ -576,7 +580,7 @@ key_press: function(e) this.key_pressed = keyCode; this.trigger_event('keypress'); - if (this.key_pressed == list.BACKSPACE_KEY) + if (this.key_pressed == this.BACKSPACE_KEY) return rcube_event.cancel(e); } @@ -704,6 +708,7 @@ drag_mouse_move: function(e) { var pos = rcube_event.get_mouse_pos(e); this.draglayer.move(pos.x+20, pos.y-5); + this.trigger_event('dragmove', e); } this.drag_start = false; @@ -784,12 +789,12 @@ removeEventListener: function(evt, handle) * This will execute all registered event handlers * @private */ -trigger_event: function(evt) +trigger_event: function(evt, p) { if (this.events[evt] && this.events[evt].length) { for (var i=0; i%s'. - "\n"; + $line_templ = html::tag('li', array('id' => 'rcmli%s', 'class' => '%s'), + html::a(array('href' => '%s', 'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s')); - // allow the following attributes to be added to the
    tag - $out = '\n"; if (strtolower($CONFIG['address_book_type']) != 'ldap') { - $out .= sprintf($line_templ, - 'rcmli'.$local_id, - !$current ? 'selected' : '', - Q(rcmail_url(null, array('_source' => 0))), - JS_OBJECT_NAME, - $local_id, - JS_OBJECT_NAME, - $local_id, - JS_OBJECT_NAME, - $local_id, - JS_OBJECT_NAME, - $local_id, - rcube_label('personaladrbook')); + $out .= sprintf($line_templ, $local_id, (!$current ? 'selected' : ''), + Q(rcmail_url(null, array('_source' => $local_id))), $local_id, rcube_label('personaladrbook')); } // end if else { // DB address book not used, see if a source is set, if not use the @@ -92,30 +76,16 @@ function rcmail_directory_list($attrib) } // end if } // end else - foreach ((array)$CONFIG['ldap_public'] as $id => $prop) - { + foreach ((array)$CONFIG['ldap_public'] as $id => $prop) { $js_id = JQ($id); $dom_id = preg_replace('/[^a-z0-9\-_]/i', '', $id); - $out .= sprintf($line_templ, - 'rcmli'.$dom_id, - $current == $id ? 'selected' : '', - Q(rcmail_url(null, array('_source' => $id))), - JS_OBJECT_NAME, - $js_id, - JS_OBJECT_NAME, - $js_id, - JS_OBJECT_NAME, - $js_id, - JS_OBJECT_NAME, - $js_id, - !empty($prop['name']) ? Q($prop['name']) : Q($id)); + $out .= sprintf($line_templ, $dom_id, ($current == $id ? 'selected' : ''), + Q(rcmail_url(null, array('_source' => $id))), $js_id, (!empty($prop['name']) ? Q($prop['name']) : Q($id))); } - $out .= '
'; - $OUTPUT->add_gui_object('folderlist', $attrib['id']); - return $out; + return html::tag('ul', $attrib, $out, html::$common_attrib); } diff --git a/skins/default/addresses.css b/skins/default/addresses.css index 27612c304..8ae298781 100644 --- a/skins/default/addresses.css +++ b/skins/default/addresses.css @@ -45,12 +45,13 @@ #directorylist { left: 20px; - width: 150px; + width: 170px; + overflow: hidden; } #addresslist { - left: 185px; + left: 200px; width: 340px; } @@ -79,6 +80,7 @@ { font-size: 11px; border-bottom: 1px solid #EBEBEB; + white-space: nowrap; } #directorylist ul li a @@ -88,6 +90,7 @@ padding-top: 2px; padding-bottom: 2px; text-decoration: none; + white-space: nowrap; } #directorylist li.selected @@ -127,7 +130,7 @@ { position: absolute; top: 85px; - left: 540px; + left: 555px; right: 20px; bottom: 40px; border: 1px solid #999999; diff --git a/skins/default/mail.css b/skins/default/mail.css index 2de775688..7807ca231 100644 --- a/skins/default/mail.css +++ b/skins/default/mail.css @@ -354,6 +354,11 @@ td.formlinks a:visited font-weight: bold; } +#mailboxlist li.virtual > a +{ + color: #666; +} + #mailboxlist li.selected, #mailboxlist li.droptarget li.selected {