diff options
| author | Nathan Kinkade <nkinkade@nkinka.de> | 2009-03-05 20:48:46 +0000 |
|---|---|---|
| committer | Nathan Kinkade <nkinkade@nkinka.de> | 2009-03-11 12:51:51 +0000 |
| commit | 32ea464bdfe5f8a22f46bfac50dcdc26c36fc497 (patch) | |
| tree | 50b13882e43a778a9e9dcec19f18d18bc8bb1aa9 /roundcubemail/program/js | |
| parent | 8013cb2424ca6912419dc7df73a9b8b77da7bdb0 (diff) | |
Applied message threading patch from Chris January: http://www.atomice.com/blog/?p=33
Diffstat (limited to 'roundcubemail/program/js')
| -rw-r--r-- | roundcubemail/program/js/app.js | 131 | ||||
| -rw-r--r-- | roundcubemail/program/js/list.js | 161 |
2 files changed, 269 insertions, 23 deletions
diff --git a/roundcubemail/program/js/app.js b/roundcubemail/program/js/app.js index 7f1e7241b..03984427d 100644 --- a/roundcubemail/program/js/app.js +++ b/roundcubemail/program/js/app.js @@ -207,7 +207,7 @@ function rcube_webmail() } if (this.env.messagecount) - this.enable_command('select-all', 'select-none', 'expunge', true); + this.enable_command('select-all', 'select-none', 'expunge', 'expand-all', 'collapse-all', true); if (this.purge_mailbox_test()) this.enable_command('purge', true); @@ -298,7 +298,7 @@ function rcube_webmail() this.enable_command('save', true); if (this.env.action=='folders') - this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true); + this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', 'enable-threading', 'disable-threading', true); if (this.gui_objects.identitieslist) { @@ -381,6 +381,10 @@ function rcube_webmail() row.replied = this.env.messages[uid].replied ? true : false; row.flagged = this.env.messages[uid].flagged ? true : false; row.forwarded = this.env.messages[uid].forwarded ? true : false; + row.has_children = this.env.messages[uid].has_children ? true : false; + row.expanded = this.env.messages[uid].expanded ? true : false; + row.depth = this.env.messages[uid].depth; + row.unread_children = this.env.messages[uid].unread_children; } // set eventhandler to message icon @@ -397,7 +401,7 @@ function rcube_webmail() { var found; if((found = find_in_array('flag', this.env.coltypes)) >= 0) - this.set_env('flagged_col', found+1); + this.set_env('flagged_col', found); } // set eventhandler to flag icon, if icon found @@ -409,6 +413,15 @@ function rcube_webmail() row.flagged_icon._row = row.obj; row.flagged_icon.onmousedown = function(e) { p.command('toggle_flag', this); }; } + + // expando is handled here rather than in rcube_list_widget so that the + // expanded state may be persisted in this.env.messages + var expando = document.getElementById('rcmexpando' + uid); + if (expando != null) + { + var p = this; + expando.onmousedown = function(e) { return p.expando_clicked(e, uid); }; + } }; // init message compose form: set focus and eventhandlers @@ -794,6 +807,14 @@ function rcube_webmail() this.message_list.clear_selection(); break; + case 'expand-all': + this.message_list.expand_all(); + break; + + case 'collapse-all': + this.message_list.collapse_all(); + break; + case 'nextmessage': if (this.env.next_uid) this.show_message(this.env.next_uid, false, this.env.action=='preview'); @@ -1049,7 +1070,15 @@ function rcube_webmail() case 'unsubscribe': this.unsubscribe_folder(props); break; - + + case 'enable-threading': + this.enable_threading(props); + break; + + case 'disable-threading': + this.disable_threading(props); + break; + case 'create-folder': this.create_folder(props); break; @@ -1976,6 +2005,13 @@ function rcube_webmail() }; + this.expando_clicked = function(e, id) + { + this.message_list.expando_clicked(e, id); + this.env.messages[id].expanded = this.message_list.rows[id].expanded; + }, + + /*********************************************************/ /********* login form methods *********/ /*********************************************************/ @@ -3195,7 +3231,20 @@ function rcube_webmail() if (folder) this.http_post('unsubscribe', '_mbox='+urlencode(folder)); }; + + this.enable_threading = function(folder) + { + if (folder) + this.http_post('enable-threading', '_mbox='+urlencode(folder)); + }; + + this.disable_threading = function(folder) + { + if (folder) + this.http_post('disable-threading', '_mbox='+urlencode(folder)); + }; + // helper method to find a specific mailbox row ID this.get_folder_row_id = function(folder) { @@ -3479,7 +3528,7 @@ function rcube_webmail() for (var n=0; thead && n<this.coltypes.length; n++) { col = this.coltypes[n]; - if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to')) + if ((cell = thead.rows[0].cells[n]) && (col=='from' || col=='to')) { // if we have links for sorting, it's a bit more complicated... if (cell.firstChild && cell.firstChild.tagName=='A') @@ -3494,7 +3543,7 @@ function rcube_webmail() cell.id = 'rcm'+col; } else if (col == 'subject' && this.message_list) - this.message_list.subject_col = n+1; + this.message_list.subject_col = n; } }; @@ -3507,12 +3556,21 @@ function rcube_webmail() var tbody = this.gui_objects.messagelist.tBodies[0]; var rowcount = tbody.rows.length; var even = rowcount%2; + var depth = cols['depth']; + var unread_children = cols['unread_children']; - this.env.messages[uid] = {deleted:flags.deleted?1:0, - replied:flags.replied?1:0, - unread:flags.unread?1:0, - forwarded:flags.forwarded?1:0, - flagged:flags.flagged?1:0}; + // don't overwrite the existing values - we want to preserve expanded + var message = this.env.messages[uid]; + if (!message) + message = this.env.messages[uid] = Array(); + message.deleted = flags.deleted?1:0; + message.replied = flags.replied?1:0; + message.unread = flags.unread?1:0; + message.forwarded = flags.forwarded?1:0; + message.flagged = flags.flagged?1:0; + message.has_children = flags.has_children?1:0; + message.unread_children = unread_children; + message.depth = depth; var row = document.createElement('TR'); row.id = 'rcmrow'+uid; @@ -3526,7 +3584,9 @@ function rcube_webmail() row.className += ' selected'; var icon = this.env.messageicon; - if (flags.deleted && this.env.deletedicon) + if (!flags.unread && unread_children > 0 && this.env.unreadchildrenicon) + icon = this.env.unreadchildrenicon; + else if (flags.deleted && this.env.deletedicon) icon = this.env.deletedicon; else if (flags.replied && this.env.repliedicon) { @@ -3540,10 +3600,17 @@ function rcube_webmail() else if(flags.unread && this.env.unreadicon) icon = this.env.unreadicon; - var col = document.createElement('TD'); - col.className = 'icon'; - col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : ''; - row.appendChild(col); + var tree = ''; + if (depth > 0) + { + for (var i=1;i<depth;i++) + tree += '<div class="branch"> </div>'; + tree += flags.has_children?'<div id="rcmexpando' + uid + '" class="' + (message.expanded?'expanded':'collapsed') + '"> </div>':'<div class="leaf"> </div>'; + if (depth > 1) + row.style.display = 'none'; + } + + tree += icon ? '<img src="'+icon+'" alt="" />' : ''; // add each submitted col for (var n = 0; n < this.coltypes.length; n++) @@ -3552,17 +3619,21 @@ function rcube_webmail() col = document.createElement('TD'); col.className = String(c).toLowerCase(); + var html; if (c=='flag') { if (flags.flagged && this.env.flaggedicon) - col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />'; + html = '<img src="'+this.env.flaggedicon+'" alt="" />'; else if(!flags.flagged && this.env.unflaggedicon) - col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />'; + html = '<img src="'+this.env.unflaggedicon+'" alt="" />'; } else if (c=='attachment') - col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' '; + html = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' '; else - col.innerHTML = cols[c]; + html = cols[c]; + if (n == 0) + html = tree + html; + col.innerHTML = html; row.appendChild(col); } @@ -3578,6 +3649,12 @@ function rcube_webmail() } }; + // expand any threads that were open + this.expand_threads = function() + { + this.message_list.expand(null); + } + // replace content of row count display this.set_rowcount = function(text) { @@ -3667,6 +3744,14 @@ function rcube_webmail() } }; + // replace content of row count display + this.set_threaded = function(threaded) + { + var controls = document.getElementById("threadcontrols"); + if (controls) + controls.style.display = threaded?'inline':'none'; + }; + // notifies that a new message(s) has hit the mailbox this.new_message_focus = function() { @@ -3925,7 +4010,8 @@ function rcube_webmail() this.show_contentframe(false); // disable commands useless when mailbox is empty this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource', - 'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', false); + 'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', 'expand-all', + 'collapse-all', false); } break; @@ -3935,7 +4021,7 @@ function rcube_webmail() if (this.task == 'mail') { if (this.message_list && request_obj.__action == 'list') this.msglist_select(this.message_list); - this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0)); + this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', 'expand-all', 'collapse-all', (this.env.messagecount > 0)); this.enable_command('purge', this.purge_mailbox_test()); } else if (this.task == 'addressbook') @@ -4187,6 +4273,7 @@ function rcube_http_request() } catch(err) { + alert(err.stack); this.onerror(this); this.busy = false; } diff --git a/roundcubemail/program/js/list.js b/roundcubemail/program/js/list.js index 8d8f9e965..bca1ac9bb 100644 --- a/roundcubemail/program/js/list.js +++ b/roundcubemail/program/js/list.js @@ -77,7 +77,7 @@ init: function() 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')) + while (row && row.nodeType != 1) { row = row.nextSibling; r++; @@ -118,6 +118,10 @@ init_row: function(row) if (document.all) row.onselectstart = function() { return false; }; + var expando = document.getElementById('rcmexpando' + uid); + if (expando != null) + expando.onmousedown = function(e) { return p.expando_clicked(e, uid); }; + this.row_init(this.rows[uid]); } }, @@ -319,6 +323,130 @@ click_row: function(e, id) }, +expando_clicked: function(e, id) +{ + var row = this.rows[id]; + var evtarget = rcube_event.get_target(e); + // Don't select this message + this.dont_select = true; + // Don't treat double click on the expando as double click on the message. + row.clicked = 0; + if (row.expanded) { + evtarget.className = "collapsed"; + this.collapse(row); + } else { + evtarget.className = "expanded"; + this.expand(row); + } +}, + + +collapse: function(row) +{ + row.expanded = false; + var depth = row.depth; + var new_row = row ? row.obj.nextSibling : null; + var r; + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r &&r.depth <= depth) + break; + new_row.style.display = 'none'; + } + new_row = new_row.nextSibling; + } + return false; +}, + + +expand: function(row) +{ + var depth, new_row; + if (row) { + row.expanded = true; + depth = row.depth; + new_row = row.obj.nextSibling; + } else { + var tbody = this.list.tBodies[0]; + new_row = tbody.firstChild; + depth = 0; + } + var current_expand = true; + var current_depth = depth; + var expand_stack = new Array(); + var depth_stack = new Array(); + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r) { + if (r.depth <= depth) + break; + while (r.depth <= current_depth) { + current_expand = expand_stack.pop(); + current_depth = depth_stack.pop(); + } + if (current_expand) + new_row.style.display = 'table-row'; + if (r.has_children) { + expand_stack.push(current_expand); + depth_stack.push(current_depth); + current_expand = r.expanded; + current_depth = r.depth; + } + } + } + new_row = new_row.nextSibling; + } + return false; +}, + + +collapse_all: function() +{ + var tbody = this.list.tBodies[0]; + new_row = tbody.firstChild; + var r; + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r.depth > 1) + new_row.style.display = 'none'; + if (r.has_children) { + r.expanded = false; + var expando = document.getElementById('rcmexpando' + r.uid); + if (expando) + expando.className = 'collapsed'; + } + } + new_row = new_row.nextSibling; + } + return false; +}, + + +expand_all: function() +{ + var tbody = this.list.tBodies[0]; + new_row = tbody.firstChild; + var r; + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + new_row.style.display = 'table-row'; + if (r.has_children) { + r.expanded = true; + var expando = document.getElementById('rcmexpando' + r.uid); + if (expando) + expando.className = 'expanded'; + } + } + new_row = new_row.nextSibling; + } + return false; +}, + + /** * get next/previous/last rows that are not hidden */ @@ -641,6 +769,12 @@ key_press: function(e) // Stop propagation so that the browser doesn't scroll rcube_event.cancel(e); return this.use_arrow_key(keyCode, mod_key); + case 61: + case 109: + case 32: + // Stop propagation + rcube_event.cancel(e); + return this.use_plusminus_key(keyCode, mod_key); default: this.shiftkey = e.shiftKey; this.key_pressed = keyCode; @@ -664,6 +798,9 @@ key_down: function(e) case 38: case 63233: case 63232: + case 61: + case 109: + case 32: if (!rcube_event.get_modifier(e) && this.focused) return rcube_event.cancel(e); @@ -698,6 +835,28 @@ use_arrow_key: function(keyCode, mod_key) /** + * Special handling method for +/- keys + */ +use_plusminus_key: function(keyCode, mod_key) +{ + var last_selected_row = this.rows[this.last_selected]; + if (!last_selected_row) + return; + if (keyCode == 32) + keyCode = last_selected_row.expanded ? 109 : 61; + if (keyCode == 61) + this.expand(last_selected_row); + else + this.collapse(last_selected_row); + var expando = document.getElementById('rcmexpando' + last_selected_row.uid); + if (expando) + last_selected_row.className = last_selected_row.expanded?'expanded':'collapsed'; + + return false; +}, + + +/** * Try to scroll the list to make the specified row visible */ scrollto: function(id) |
