Refactor drag & drop functionality. Don't rely on browser events anymore (#1484453)
| | |
| | | $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')); |
| | | } |
| | | |
| | |
| | | * @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); |
| | |
| | | $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<ul" . ($collapsed ? " style=\"display: none;\"" : "") . ">\n" . rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $maxlength, $realnames, $nestLevel+1) . "</ul>\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 .= "</li>\n"; |
| | | $idx++; |
| | | } |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | 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(); |
| | |
| | | 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(); |
| | | |
| | |
| | | |
| | | |
| | | 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; |
| | | } |
| | | |
| | | this.unfocus_folder = function(id) |
| | | { |
| | | var li; |
| | | if (this.drag_active && (li = this.get_folder_li(id))) |
| | | this.set_classname(li, 'droptarget', false); |
| | | |
| | | // 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.drag_move = function(e) |
| | | { |
| | | var li; |
| | | 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; |
| | |
| | | } |
| | | } |
| | | |
| | | // 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) |
| | |
| | | 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); |
| | | }; |
| | | |
| | | |
| | |
| | | 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') |
| | |
| | | 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 |
| | |
| | | { |
| | | var cell, td; |
| | | var new_row = document.createElement('TR'); |
| | | for(var n=0; n<row.childNodes.length; n++) |
| | | for(var n=0; n<row.cells.length; n++) |
| | | { |
| | | cell = row.childNodes[n]; |
| | | cell = row.cells[n]; |
| | | td = document.createElement('TD'); |
| | | |
| | | if (cell.className) |
| | |
| | | }, |
| | | |
| | | /** |
| | | * returns the event key code |
| | | */ |
| | | get_button: function(e) |
| | | { |
| | | e = e || window.event; |
| | | return e && (typeof e.button != 'undefined') ? e.button : (e && e.which ? e.which : 0); |
| | | }, |
| | | |
| | | /** |
| | | * returns modifier key (constants defined at top of file) |
| | | */ |
| | | get_modifier: function(e) |
| | |
| | | var elm = obj.offsetParent; |
| | | while(elm && elm!=null) |
| | | { |
| | | iX += elm.offsetLeft; |
| | | iY += elm.offsetTop; |
| | | iX += elm.offsetLeft - (elm.parentNode && elm.parentNode.scrollLeft ? elm.parentNode.scrollLeft : 0); |
| | | iY += elm.offsetTop - (elm.parentNode && elm.parentNode.scrollTop ? elm.parentNode.scrollTop : 0); |
| | | elm = elm.offsetParent; |
| | | } |
| | | } |
| | | |
| | | //if(bw.mac && bw.ie5) iX += document.body.leftMargin; |
| | | //if(bw.mac && bw.ie5) iY += document.body.topMargin; |
| | | |
| | | return {x:iX, y:iY}; |
| | | } |
| | | |
| | | // determine whether the mouse is over the given object or not |
| | | function rcube_mouse_is_over(ev, obj) |
| | | { |
| | | var mouse = rcube_event.get_mouse_pos(ev); |
| | | var pos = rcube_get_object_pos(obj); |
| | | |
| | | return ((mouse.x >= 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 |
| | |
| | | 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') |
| | |
| | | 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 |
| | |
| | | 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); |
| | | } |
| | | |
| | |
| | | { |
| | | 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; |
| | |
| | | * 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<this.events[evt].length; i++) |
| | | if (typeof(this.events[evt][i]) == 'function') |
| | | this.events[evt][i](this); |
| | | this.events[evt][i](this, p); |
| | | } |
| | | } |
| | | |
| | |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmdirectorylist'; |
| | | |
| | | $out = ''; |
| | | $local_id = '0'; |
| | | $current = get_input_value('_source', RCUBE_INPUT_GPC); |
| | | $line_templ = '<li id="%s" class="%s"><a href="%s"' . |
| | | ' onclick="return %s.command(\'list\',\'%s\',this)"' . |
| | | ' onmouseover="return %s.focus_folder(\'%s\')"' . |
| | | ' onmouseout="return %s.unfocus_folder(\'%s\')"' . |
| | | ' onmouseup="return %s.folder_mouse_up(\'%s\')">%s'. |
| | | "</a></li>\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 <ul> tag |
| | | $out = '<ul' . create_attrib_string($attrib, array('style', 'class', 'id')) . ">\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 |
| | |
| | | } // 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 .= '</ul>'; |
| | | |
| | | $OUTPUT->add_gui_object('folderlist', $attrib['id']); |
| | | |
| | | return $out; |
| | | return html::tag('ul', $attrib, $out, html::$common_attrib); |
| | | } |
| | | |
| | | |
| | |
| | | #directorylist |
| | | { |
| | | left: 20px; |
| | | width: 150px; |
| | | width: 170px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | #addresslist |
| | | { |
| | | left: 185px; |
| | | left: 200px; |
| | | width: 340px; |
| | | } |
| | | |
| | |
| | | { |
| | | font-size: 11px; |
| | | border-bottom: 1px solid #EBEBEB; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | #directorylist ul li a |
| | |
| | | padding-top: 2px; |
| | | padding-bottom: 2px; |
| | | text-decoration: none; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | #directorylist li.selected |
| | |
| | | { |
| | | position: absolute; |
| | | top: 85px; |
| | | left: 540px; |
| | | left: 555px; |
| | | right: 20px; |
| | | bottom: 40px; |
| | | border: 1px solid #999999; |
| | |
| | | font-weight: bold; |
| | | } |
| | | |
| | | #mailboxlist li.virtual > a |
| | | { |
| | | color: #666; |
| | | } |
| | | |
| | | #mailboxlist li.selected, |
| | | #mailboxlist li.droptarget li.selected |
| | | { |