From c6447e2ce289188493590ec0d5449fa3692eed08 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Wed, 02 Jul 2014 07:03:22 -0400 Subject: [PATCH] Use treelist widget for folders list in Settings/Folders (#1489648) --- program/js/app.js | 200 ++++++++++++++++++++++++++------------------------ 1 files changed, 104 insertions(+), 96 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index 3fd1823..2b9c3f0 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -346,10 +346,10 @@ this.contact_list .addEventListener('initrow', function(o) { ref.triggerEvent('insertrow', { cid:o.uid, row:o }); }) .addEventListener('select', function(o) { ref.compose_recipient_select(o); }) - .addEventListener('dblclick', function(o) { ref.compose_add_recipient('to'); }) + .addEventListener('dblclick', function(o) { ref.compose_add_recipient(); }) .addEventListener('keypress', function(o) { if (o.key_pressed == o.ENTER_KEY) { - if (!ref.compose_add_recipient('to')) { + if (!ref.compose_add_recipient()) { // execute link action on <enter> if not a recipient entry if (o.last_selected && String(o.last_selected).charAt(0) == 'G') { $(o.rows[o.last_selected].obj).find('a').first().click(); @@ -358,6 +358,9 @@ } }) .init(); + + // remember last focused address field + $('#_to,#_cc,#_bcc').focus(function() { ref.env.focused_field = this; }); } if (this.gui_objects.addressbookslist) { @@ -409,6 +412,17 @@ this.update_group_commands(); this.command('list'); + } + + if (this.gui_objects.savedsearchlist) { + this.savedsearchlist = new rcube_treelist_widget(this.gui_objects.savedsearchlist, { + id_prefix: 'rcmli', + id_encode: this.html_identifier_encode, + id_decode: this.html_identifier_decode + }); + + this.savedsearchlist.addEventListener('select', function(node) { + ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }); }); } this.set_page_buttons(); @@ -551,6 +565,7 @@ // init treelist widget if (this.gui_objects.folderlist && window.rcube_treelist_widget) { this.treelist = new rcube_treelist_widget(this.gui_objects.folderlist, { + selectable: true, id_prefix: 'rcmli', id_encode: this.html_identifier_encode, id_decode: this.html_identifier_decode, @@ -615,7 +630,7 @@ { var ret, uid, cid, url, flag, aborted = false; - if (obj && obj.blur && !(event || rcube_event.is_keyboard(event))) + if (obj && obj.blur && !(event && rcube_event.is_keyboard(event))) obj.blur(); // do nothing if interface is locked by other command (with exception for searching reset) @@ -1840,9 +1855,6 @@ return (this.env.mailboxes[id] && !this.env.mailboxes[id].virtual && (this.env.mailboxes[id].id != this.env.mailbox || this.is_multifolder_listing())) ? 1 : 0; - - case 'settings': - return id != this.env.mailbox ? 1 : 0; case 'addressbook': var target; @@ -3485,6 +3497,12 @@ this.compose_add_recipient = function(field) { + // find last focused field name + if (!field) { + field = $(this.env.focused_field).filter(':visible'); + field = field.length ? field.attr('id').replace('_', '') : 'to'; + } + var recipients = [], input = $('#_'+field), delim = this.env.recipients_delimiter; if (this.contact_list && this.contact_list.selection.length) { @@ -4062,6 +4080,14 @@ if (upload_id) this.triggerEvent('fileuploaded', {name: name, attachment: att, id: upload_id}); + if (!this.env.attachments) + this.env.attachments = {}; + + if (upload_id && this.env.attachments[upload_id]) + delete this.env.attachments[upload_id]; + + this.env.attachments[name] = att; + if (!this.gui_objects.attachmentlist) return false; @@ -4070,7 +4096,7 @@ if (!att.complete && att.frame) att.html = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+name+'\', \''+att.frame+'\');" href="#cancelupload" class="cancelupload">' - + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="" />' : this.get_label('cancel')) + '</a>' + att.html; + + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="'+this.get_label('cancel')+'" />' : this.get_label('cancel')) + '</a>' + att.html; var indicator, li = $('<li>'); @@ -4087,10 +4113,9 @@ li.appendTo(this.gui_objects.attachmentlist); } - if (upload_id && this.env.attachments[upload_id]) - delete this.env.attachments[upload_id]; - - this.env.attachments[name] = att; + // set tabindex attribute + var tabindex = $(this.gui_objects.attachmentlist).attr('data-tabindex') || '0'; + li.find('a').attr('tabindex', tabindex); return true; }; @@ -4747,7 +4772,8 @@ $(this.gui_objects.addresslist_title).html(this.get_label('contacts')); } - this.select_folder(folder, '', true); + if (!this.env.search_id) + this.select_folder(folder, '', true); // load contacts remotely if (this.gui_objects.contactslist) { @@ -5171,7 +5197,7 @@ // find list (UL) element if (type == 'contactsearch') - ul = this.gui_objects.folderlist; + ul = this.gui_objects.savedsearchlist; else ul = $('ul.groups', this.get_folder_li(this.env.source,'',true)); @@ -5272,7 +5298,7 @@ .html(prop.name); this.env.contactfolders[key] = this.env.contactgroups[key] = prop; - this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, true); + this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup'); this.triggerEvent('group_insert', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key) }); }; @@ -5554,7 +5580,7 @@ .html(name), prop = { name:name, id:id }; - this.treelist.insert({ id:key, html:link, classes:['contactsearch'] }, null, 'contactsearch'); + this.savedsearchlist.insert({ id:key, html:link, classes:['contactsearch'] }, null, 'contactsearch'); this.select_folder(key,'',true); this.enable_command('search-delete', true); this.env.search_id = id; @@ -5580,7 +5606,7 @@ this.remove_search_item = function(id) { var li, key = 'S'+id; - if (this.treelist.remove(key)) { + if (this.savedsearchlist.remove(key)) { this.triggerEvent('search_delete', { id:id, li:li }); } @@ -5600,7 +5626,13 @@ } this.reset_qsearch(); - this.select_folder('S'+id, '', true); + + if (this.savedsearchlist) { + this.treelist.select(''); + this.savedsearchlist.select('S'+id); + } + else + this.select_folder('S'+id, '', true); // reset vars this.env.current_page = 1; @@ -5730,62 +5762,38 @@ this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$'); - this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, - {multiselect:false, draggable:true, keyboard:true, toggleselect:true}); + this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, { + selectable: true + }); + this.subscription_list - .addEventListener('select', function(o){ ref.subscription_select(o); }) - .addEventListener('dragstart', function(o){ ref.drag_active = true; }) - .addEventListener('dragend', function(o){ ref.subscription_move_folder(o); }) - .addEventListener('initrow', function (row) { - row.obj.onmouseover = function() { ref.focus_subscription(row.id); }; - row.obj.onmouseout = function() { ref.unfocus_subscription(row.id); }; - }) - .init() - .focus(); + .addEventListener('select', function(node) { ref.subscription_select(node.id); }) + .draggable({cancel: '#mailboxroot'}) + .droppable({ + // @todo: find better way, accept callback is executed for every folder + // on the list when dragging starts (and stops), this is slow, but + // I didn't find a method to check droptarget on over event + accept: function(node) { + var source = ref.env.subscriptionrows[$(node).attr('id')], + dest = ref.env.subscriptionrows[this.id], + source_name = source[0], + dest_name = dest[0]; - $('#mailboxroot') - .mouseover(function(){ ref.focus_subscription(this.id); }) - .mouseout(function(){ ref.unfocus_subscription(this.id); }) - }; - - this.focus_subscription = function(id) - { - var row, folder; - - if (this.drag_active && this.env.mailbox && (row = document.getElementById(id))) - if (this.env.subscriptionrows[id] && - (folder = this.env.subscriptionrows[id][0]) !== null - ) { - if (this.check_droptarget(folder) && - !this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2] && - folder != this.env.mailbox.replace(this.last_sub_rx, '') && - !folder.startsWith(this.env.mailbox + this.env.delimiter) - ) { - this.env.dstfolder = folder; - $(row).addClass('droptarget'); + return !source[2] + && dest_name != source_name.replace(ref.last_sub_rx, '') + && !dest_name.startsWith(source_name + ref.env.delimiter); + }, + drop: function(e, ui) { + ref.subscription_move_folder(ui.draggable.attr('id'), this.id); } - } + }); }; - this.unfocus_subscription = function(id) + this.subscription_select = function(id) { - var row = $('#'+id); + var folder; - this.env.dstfolder = null; - - if (row.length && this.env.subscriptionrows[id]) - row.removeClass('droptarget'); - else - $(this.subscription_list.frame).removeClass('droptarget'); - }; - - this.subscription_select = function(list) - { - var id, folder; - - if (list && (id = list.get_single_selection()) && - (folder = this.env.subscriptionrows['rcmrow'+id]) - ) { + if (id && id != 'mailboxroot' && (folder = this.env.subscriptionrows[id])) { this.env.mailbox = folder[0]; this.show_folder(folder[0]); this.enable_command('delete-folder', !folder[2]); @@ -5797,24 +5805,21 @@ } }; - this.subscription_move_folder = function(list) + this.subscription_move_folder = function(from, to) { - if (this.env.mailbox && this.env.dstfolder !== null && - this.env.dstfolder != this.env.mailbox && - this.env.dstfolder != this.env.mailbox.replace(this.last_sub_rx, '') - ) { - var path = this.env.mailbox.split(this.env.delimiter), - basename = path.pop(), - newname = this.env.dstfolder === '' ? basename : this.env.dstfolder + this.env.delimiter + basename; + var source = this.env.subscriptionrows[from][0]; + dest = this.env.subscriptionrows[to][0]; - if (newname != this.env.mailbox) { - this.http_post('rename-folder', {_folder_oldname: this.env.mailbox, _folder_newname: newname}, this.set_busy(true, 'foldermoving')); - this.subscription_list.draglayer.hide(); + if (source && dest !== null && source != dest && dest != source.replace(this.last_sub_rx, '')) { + var path = source.split(this.env.delimiter), + basename = path.pop(), + newname = dest === '' ? basename : dest + this.env.delimiter + basename; + + if (newname != source) { + this.http_post('rename-folder', {_folder_oldname: source, _folder_newname: newname}, + this.set_busy(true, 'foldermoving')); } } - - this.drag_active = false; - this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder)); }; // tell server to create and subscribe a new mailbox @@ -5830,8 +5835,7 @@ folder = this.env.subscriptionrows[id][0]; if (folder && confirm(this.get_label('deletefolderconfirm'))) { - var lock = this.set_busy(true, 'folderdeleting'); - this.http_post('delete-folder', {_mbox: folder}, lock); + this.http_post('delete-folder', {_mbox: folder}, this.set_busy(true, 'folderdeleting')); } }; @@ -5843,9 +5847,9 @@ var row, n, tmp, tmp_name, rowid, collator, folders = [], list = [], slist = [], - tbody = this.gui_objects.subscriptionlist.tBodies[0], - refrow = $('tr', tbody).get(1), - id = 'rcmrow'+((new Date).getTime()); + list_element = $(this.gui_objects.subscriptionlist), + refrow = $('li', list_element).get(1), + id = 'rcmli'+((new Date).getTime()); if (!refrow) { // Refresh page if we don't have a table row to clone @@ -5860,7 +5864,7 @@ row.attr({id: id, 'class': class_name}); // set folder name - row.find('td:first').html(display_name); + $('.name', row).html(display_name); // update subscription checkbox $('input[name="_subscribed[]"]', row).val(name) @@ -5931,12 +5935,13 @@ // add row to the table if (rowid) - $('#'+rowid).after(row); + $('#' + rowid).after(row); else - row.appendTo(tbody); + list_element.append(row); // update list widget - this.subscription_list.clear_selection(); + this.subscription_list.select(); + if (!skip_init) this.init_subscription_list(); @@ -5953,11 +5958,11 @@ if (!this.gui_objects.subscriptionlist) { if (this.is_framed) return parent.rcmail.replace_folder_row(oldfolder, newfolder, display_name, is_protected, class_name); + return false; } var i, n, len, name, dispname, oldrow, tmprow, row, level, - tbody = this.gui_objects.subscriptionlist.tBodies[0], folders = this.env.subscriptionrows, id = this.get_folder_row_id(oldfolder), prefix_len = oldfolder.length, @@ -5968,7 +5973,6 @@ // no renaming, only update class_name if (oldfolder == newfolder) { $('#'+id).attr('class', class_name || ''); - this.subscription_list.focus(); return; } @@ -6005,7 +6009,7 @@ for (i=level; i<0; i++) dispname = ' ' + dispname; } - row.find('td:first').html(dispname); + $('.name', row).html(dispname); this.env.subscriptionrows[id][1] = dispname; } } @@ -6033,8 +6037,8 @@ this._remove_folder_row = function(id) { - this.subscription_list.remove_row(id.replace(/^rcmrow/, '')); - $('#'+id).remove(); + this.subscription_list.remove(id.replace(/^rcmli/, '')); + $('#' + id).remove(); delete this.env.subscriptionrows[id]; }; @@ -6465,6 +6469,10 @@ // mark a mailbox as selected and set environment variable this.select_folder = function(name, prefix, encode) { + if (this.savedsearchlist) { + this.savedsearchlist.select(''); + } + if (this.treelist) { this.treelist.select(name); } @@ -7527,7 +7535,7 @@ $(form).attr({ target: frame_name, - action: this.url(action, { _id:this.env.compose_id||'', _uploadid:ts }), + action: this.url(action, {_id: this.env.compose_id || '', _uploadid: ts, _from: this.env.action}), method: 'POST'}) .attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data') .submit(); @@ -7590,7 +7598,7 @@ $.ajax({ type: 'POST', dataType: 'json', - url: ref.url(ref.env.filedrop.action||'upload', { _id:ref.env.compose_id||ref.env.cid||'', _uploadid:ts, _remote:1 }), + url: ref.url(ref.env.filedrop.action || 'upload', {_id: ref.env.compose_id||ref.env.cid||'', _uploadid: ts, _remote: 1, _from: ref.env.action}), contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary, processData: false, timeout: 0, // disable default timeout set in ajaxSetup() -- Gitblit v1.9.1