summaryrefslogtreecommitdiff
path: root/roundcubemail/program/js
diff options
context:
space:
mode:
authorNathan Kinkade <nkinkade@nkinka.de>2009-03-05 20:48:46 +0000
committerNathan Kinkade <nkinkade@nkinka.de>2009-03-11 12:51:51 +0000
commit32ea464bdfe5f8a22f46bfac50dcdc26c36fc497 (patch)
tree50b13882e43a778a9e9dcec19f18d18bc8bb1aa9 /roundcubemail/program/js
parent8013cb2424ca6912419dc7df73a9b8b77da7bdb0 (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.js131
-rw-r--r--roundcubemail/program/js/list.js161
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">&nbsp</div>';
+ tree += flags.has_children?'<div id="rcmexpando' + uid + '" class="' + (message.expanded?'expanded':'collapsed') + '">&nbsp;</div>':'<div class="leaf">&nbsp;</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="" />' : '&nbsp;';
+ html = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : '&nbsp;';
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)