From 3c309af0663f1673f921682c6ae3f673426e1397 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 31 Jan 2013 13:49:35 +0100 Subject: - Refactored the hierarchical mailboxlist control into a separate widget class - Build address book directories list as hierarchical list - Make address book groups collapsible using the new new treelist widget - Use encoded identifiers for address book directory list items --- program/js/app.js | 206 ++++++++++++++++++------------------------------------ 1 file changed, 67 insertions(+), 139 deletions(-) (limited to 'program/js/app.js') diff --git a/program/js/app.js b/program/js/app.js index 6d5bdfe74..dbb65eea4 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -4,7 +4,7 @@ | | | This file is part of the Roundcube Webmail client | | Copyright (C) 2005-2013, The Roundcube Dev Team | - | Copyright (C) 2011-2012, Kolab Systems AG | + | Copyright (C) 2011-2013, Kolab Systems AG | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -459,9 +459,22 @@ function rcube_webmail() this.display_message(this.pending_message[0], this.pending_message[1], this.pending_message[2]); // map implicit containers - if (this.gui_objects.folderlist) + if (this.gui_objects.folderlist) { this.gui_containers.foldertray = $(this.gui_objects.folderlist); + // init treelist widget + if (window.rcube_treelist_widget) { + this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, { + id_prefix: 'rcmli', + id_encode: this.html_identifier_encode, + id_decode: this.html_identifier_decode, + check_droptarget: function(node){ return !node.virtual && ref.check_droptarget(node.id) } + }); + this.treelist.addEventListener('collapse', function(node){ ref.folder_collapsed(node) }); + this.treelist.addEventListener('expand', function(node){ ref.folder_collapsed(node) }); + } + } + // activate html5 file drop feature (if browser supports it and if configured) if (this.gui_objects.filedrop && this.env.filedrop && ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) { $(document.body).bind('dragover dragleave drop', function(e){ return ref.document_drag_hover(e, e.type == 'dragover'); }); @@ -1263,11 +1276,12 @@ function rcube_webmail() this.html_identifier = function(str, encode) { - str = String(str); - if (encode) - return Base64.encode(str).replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_'); - else - return str.replace(this.identifier_expr, '_'); + return encode ? this.html_identifier_encode(str) : String(str).replace(this.identifier_expr, '_'); + }; + + this.html_identifier_encode = function(str) + { + return Base64.encode(String(str)).replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_'); }; this.html_identifier_decode = function(str) @@ -1320,29 +1334,9 @@ function rcube_webmail() if (this.preview_read_timer) clearTimeout(this.preview_read_timer); - // save folderlist and folders location/sizes for droptarget calculation in drag_move() - if (this.gui_objects.folderlist && model) { - this.initialBodyScrollTop = bw.ie ? 0 : window.pageYOffset; - this.initialListScrollTop = this.gui_objects.folderlist.parentNode.scrollTop; - - var k, li, height, - list = $(this.gui_objects.folderlist); - pos = list.offset(); - - this.env.folderlist_coords = { x1:pos.left, y1:pos.top, x2:pos.left + list.width(), y2:pos.top + list.height() }; - - this.env.folder_coords = []; - for (k in model) { - if (li = this.get_folder_li(k)) { - // only visible folders - if (height = li.firstChild.offsetHeight) { - pos = $(li.firstChild).offset(); - this.env.folder_coords[k] = { x1:pos.left, y1:pos.top, - x2:pos.left + li.firstChild.offsetWidth, y2:pos.top + height, on:0 }; - } - } - } - } + // prepare treelist widget for dragging interactions + if (this.treelist) + this.treelist.drag_start(); }; this.drag_end = function(e) @@ -1350,87 +1344,28 @@ function rcube_webmail() this.drag_active = false; this.env.last_folder_target = null; - if (this.folder_auto_timer) { - clearTimeout(this.folder_auto_timer); - this.folder_auto_timer = null; - this.folder_auto_expand = null; - } - - // over the folders - if (this.gui_objects.folderlist && this.env.folder_coords) { - for (var k in this.env.folder_coords) { - if (this.env.folder_coords[k].on) - $(this.get_folder_li(k)).removeClass('droptarget'); - } - } + if (this.treelist) + this.treelist.drag_end(); }; this.drag_move = function(e) { - if (this.gui_objects.folderlist && this.env.folder_coords) { - var k, li, div, check, oldclass, + if (this.gui_objects.folderlist) { + var drag_target, oldclass, layerclass = 'draglayernormal', - mouse = rcube_event.get_mouse_pos(e), - pos = this.env.folderlist_coords, - // offsets to compensate for scrolling while dragging a message - boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop, - moffset = this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop; + mouse = rcube_event.get_mouse_pos(e); if (this.contact_list && this.contact_list.draglayer) oldclass = this.contact_list.draglayer.attr('class'); - mouse.y += -moffset-boffset; - - // if mouse pointer is outside of folderlist - if (mouse.x < pos.x1 || mouse.x >= pos.x2 || mouse.y < pos.y1 || mouse.y >= pos.y2) { - if (this.env.last_folder_target) { - $(this.get_folder_li(this.env.last_folder_target)).removeClass('droptarget'); - this.env.folder_coords[this.env.last_folder_target].on = 0; - this.env.last_folder_target = null; - } - if (layerclass != oldclass && this.contact_list && this.contact_list.draglayer) - this.contact_list.draglayer.attr('class', layerclass); - return; + // mouse intersects a valid drop target on the treelist + if (this.treelist && (drag_target = this.treelist.intersects(mouse, true))) { + this.env.last_folder_target = drag_target; + layerclass = 'draglayer' + (this.check_droptarget(drag_target) > 1 ? 'copy' : 'normal'); } - - // over the folders - for (k in this.env.folder_coords) { - pos = this.env.folder_coords[k]; - if (mouse.x >= pos.x1 && mouse.x < pos.x2 && mouse.y >= pos.y1 && mouse.y < pos.y2) { - if (check = this.check_droptarget(k)) { - li = this.get_folder_li(k); - div = $(li.getElementsByTagName('div')[0]); - - // if the folder is collapsed, expand it after 1sec and restart the drag & drop process. - if (div.hasClass('collapsed')) { - if (this.folder_auto_timer) - clearTimeout(this.folder_auto_timer); - - this.folder_auto_expand = this.env.mailboxes[k].id; - this.folder_auto_timer = setTimeout(function() { - rcmail.command('collapse-folder', rcmail.folder_auto_expand); - rcmail.drag_start(null); - }, 1000); - } - else if (this.folder_auto_timer) { - clearTimeout(this.folder_auto_timer); - this.folder_auto_timer = null; - this.folder_auto_expand = null; - } - - $(li).addClass('droptarget'); - this.env.folder_coords[k].on = 1; - this.env.last_folder_target = k; - layerclass = 'draglayer' + (check > 1 ? 'copy' : 'normal'); - } - // Clear target, otherwise drag end will trigger move into last valid droptarget - else - this.env.last_folder_target = null; - } - else if (pos.on) { - $(this.get_folder_li(k)).removeClass('droptarget'); - this.env.folder_coords[k].on = 0; - } + else { + // Clear target, otherwise drag end will trigger move into last valid droptarget + this.env.last_folder_target = null; } if (layerclass != oldclass && this.contact_list && this.contact_list.draglayer) @@ -1440,40 +1375,33 @@ function rcube_webmail() this.collapse_folder = function(name) { - var li = this.get_folder_li(name, '', true), - div = $('div:first', li), - ul = $('ul:first', li); + if (this.treelist) + this.treelist.toggle(name); + }; + + this.folder_collapsed = function(node) + { + var prefname = this.env.task == 'addressbook' ? 'collapsed_abooks' : 'collapsed_folders'; - if (div.hasClass('collapsed')) { - ul.show(); - div.removeClass('collapsed').addClass('expanded'); - var reg = new RegExp('&'+urlencode(name)+'&'); - this.env.collapsed_folders = this.env.collapsed_folders.replace(reg, ''); - } - else if (div.hasClass('expanded')) { - ul.hide(); - div.removeClass('expanded').addClass('collapsed'); - this.env.collapsed_folders = this.env.collapsed_folders+'&'+urlencode(name)+'&'; + if (node.collapsed) { + this.env[prefname] = this.env[prefname] + '&'+urlencode(node.id)+'&'; // select the folder if one of its childs is currently selected // don't select if it's virtual (#1488346) - if (this.env.mailbox.indexOf(name + this.env.delimiter) == 0 && !$(li).hasClass('virtual')) + if (this.env.mailbox && this.env.mailbox.indexOf(name + this.env.delimiter) == 0 && !node.virtual) this.command('list', name); } - else - return; - - // Work around a bug in IE6 and IE7, see #1485309 - if (bw.ie6 || bw.ie7) { - var siblings = li.nextSibling ? li.nextSibling.getElementsByTagName('ul') : null; - if (siblings && siblings.length && (li = siblings[0]) && li.style && li.style.display != 'none') { - li.style.display = 'none'; - li.style.display = ''; - } + else { + var reg = new RegExp('&'+urlencode(node.id)+'&'); + this.env[prefname] = this.env[prefname].replace(reg, ''); } - this.command('save-pref', { name: 'collapsed_folders', value: this.env.collapsed_folders }); - this.set_unread_count_display(name, false); + if (!this.drag_active) { + this.command('save-pref', { name: prefname, value: this.env[prefname] }); + + if (this.env.unread_counts) + this.set_unread_count_display(node.id, false); + } }; this.doc_mouse_up = function(e) @@ -1498,9 +1426,9 @@ function rcube_webmail() if (this.drag_active && model && this.env.last_folder_target) { var target = model[this.env.last_folder_target]; - $(this.get_folder_li(this.env.last_folder_target)).removeClass('droptarget'); this.env.last_folder_target = null; list.draglayer.hide(); + this.drag_end(e); if (!this.drag_menu(e, target)) this.command('moveto', target); @@ -4164,7 +4092,7 @@ function rcube_webmail() else if (!this.env.search_request) folder = group ? 'G'+src+group : src; - this.select_folder(folder); + this.select_folder(folder, '', true); this.env.source = src; this.env.group = group; @@ -4468,7 +4396,7 @@ function rcube_webmail() this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); this.env.group_renaming = true; - var link, li = this.get_folder_li(this.env.source+this.env.group, 'rcmliG'); + var link, li = this.get_folder_li('G'+this.env.source+this.env.group,'',true); if (li && (link = li.firstChild)) { $(link).hide().before(this.name_input); } @@ -4489,7 +4417,7 @@ function rcube_webmail() this.remove_group_item = function(prop) { var li, key = 'G'+prop.source+prop.id; - if ((li = this.get_folder_li(key))) { + if ((li = this.get_folder_li(key,'',true))) { this.triggerEvent('group_delete', { source:prop.source, id:prop.id, li:li }); li.parentNode.removeChild(li); @@ -4511,7 +4439,7 @@ function rcube_webmail() this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); this.name_input_li = $('
  • ').addClass(type).append(this.name_input); - var li = type == 'contactsearch' ? $('li:last', this.gui_objects.folderlist) : this.get_folder_li(this.env.source); + var li = type == 'contactsearch' ? $('li:last', this.gui_objects.folderlist) : $('ul.groups li:last', this.get_folder_li(this.env.source,'',true)); this.name_input_li.insertAfter(li); } @@ -4612,7 +4540,7 @@ function rcube_webmail() this.reset_add_input(); var key = 'G'+prop.source+prop.id, - li = this.get_folder_li(key), + li = this.get_folder_li(key,'',true), link; // group ID has changed, replace link node and identifiers @@ -4651,8 +4579,8 @@ function rcube_webmail() this.add_contact_group_row = function(prop, li, reloc) { var row, name = prop.name.toUpperCase(), - sibling = this.get_folder_li(prop.source), - prefix = 'rcmliG' + this.html_identifier(prop.source); + sibling = this.get_folder_li(prop.source,'',true), + prefix = 'rcmli' + this.html_identifier('G'+prop.source, true); // When renaming groups, we need to remove it from DOM and insert it in the proper place if (reloc) { @@ -4901,12 +4829,12 @@ function rcube_webmail() .attr('rel', id) .click(function() { return rcmail.command('listsearch', id, this); }) .html(name), - li = $('
  • ').attr({id: 'rcmli' + this.html_identifier(key), 'class': 'contactsearch'}) + li = $('
  • ').attr({ id:'rcmli' + this.html_identifier(key,true), 'class':'contactsearch' }) .append(link), prop = {name:name, id:id, li:li[0]}; this.add_saved_search_row(prop, li); - this.select_folder('S'+id); + this.select_folder(key,'',true); this.enable_command('search-delete', true); this.env.search_id = id; @@ -4960,7 +4888,7 @@ function rcube_webmail() this.remove_search_item = function(id) { var li, key = 'S'+id; - if ((li = this.get_folder_li(key))) { + if ((li = this.get_folder_li(key,'',true))) { this.triggerEvent('search_delete', { id:id, li:li }); li.parentNode.removeChild(li); @@ -4982,7 +4910,7 @@ function rcube_webmail() } this.reset_qsearch(); - this.select_folder('S'+id); + this.select_folder('S'+id, '', true); // reset vars this.env.current_page = 1; -- cgit v1.2.3