From 2429cfde7887d1c92369cba972305a7f9ddddb18 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Sun, 30 Oct 2011 11:32:42 -0400
Subject: [PATCH] Avoid titles like 'undefined' or 'false'

---
 program/js/app.js |  353 ++++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 218 insertions(+), 135 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 0f17633..b2f3b08 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -86,7 +86,7 @@
     if (over) button_prop.over = over;
 
     this.buttons[command].push(button_prop);
-    
+
     if (this.loaded)
       init_button(command, button_prop);
   };
@@ -155,7 +155,7 @@
     }
 
     // enable general commands
-    this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'undo', true);
+    this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', true);
 
     if (this.env.permaurl)
       this.enable_command('permaurl', true);
@@ -164,7 +164,7 @@
 
       case 'mail':
         // enable mail commands
-        this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
+        this.enable_command('list', 'checkmail', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
 
         if (this.gui_objects.messagelist) {
 
@@ -278,6 +278,9 @@
         if (this.gui_objects.folderlist)
           this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups);
 
+        this.enable_command('add', 'import', this.env.writable_source);
+        this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true);
+
         if (this.gui_objects.contactslist) {
 
           this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
@@ -300,6 +303,7 @@
           }
 
           this.update_group_commands();
+          this.command('list');
         }
 
         this.set_page_buttons();
@@ -319,21 +323,12 @@
           if (this.env.action == 'add' || this.env.action == 'edit')
               this.init_contact_form();
         }
+
         if (this.gui_objects.qsearchbox) {
           this.enable_command('search', 'reset-search', 'moveto', true);
         }
 
-        if (this.contact_list && this.contact_list.rowcount > 0)
-          this.enable_command('export', true);
-
-        this.enable_command('add', 'import', this.env.writable_source);
-        this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true);
-
-        // load contacts of selected source
-        if (!this.env.action)
-          this.command('list', this.env.source);
         break;
-
 
       case 'settings':
         this.enable_command('preferences', 'identities', 'save', 'folders', true);
@@ -385,7 +380,10 @@
           $('#rcmloginpwd').focus();
 
         // detect client timezone
-        $('#rcmlogintz').val(new Date().getTimezoneOffset() / -60);
+        var tz = new Date().getTimezoneOffset() / -60;
+        var stdtz = new Date().getStdTimezoneOffset() / -60;
+        $('#rcmlogintz').val(stdtz);
+        $('#rcmlogindst').val(tz > stdtz ? 1 : 0);
 
         // display 'loading' message on form submit, lock submit button
         $('form').submit(function () {
@@ -444,6 +442,8 @@
   // execute a specific command on the web client
   this.command = function(command, props, obj)
   {
+    var ret;
+
     if (obj && obj.blur)
       obj.blur();
 
@@ -467,24 +467,26 @@
 
     // process external commands
     if (typeof this.command_handlers[command] === 'function') {
-      var ret = this.command_handlers[command](props, obj);
+      ret = this.command_handlers[command](props, obj);
       return ret !== undefined ? ret : (obj ? false : true);
     }
     else if (typeof this.command_handlers[command] === 'string') {
-      var ret = window[this.command_handlers[command]](props, obj);
+      ret = window[this.command_handlers[command]](props, obj);
       return ret !== undefined ? ret : (obj ? false : true);
     }
 
     // trigger plugin hooks
     this.triggerEvent('actionbefore', {props:props, action:command});
-    var ret = this.triggerEvent('before'+command, props);
+    ret = this.triggerEvent('before'+command, props);
     if (ret !== undefined) {
-      // abort if one the handlers returned false
+      // abort if one of the handlers returned false
       if (ret === false)
         return false;
       else
         props = ret;
     }
+
+    ret = undefined;
 
     // process internal command
     switch (command) {
@@ -809,7 +811,7 @@
         break;
 
       case 'compose':
-        var url = this.env.comm_path+'&_action=compose';
+        var url = this.url('mail/compose');
 
         if (this.task == 'mail') {
           url += '&_mbox='+urlencode(this.env.mailbox);
@@ -843,10 +845,14 @@
           }
 
           if (a_cids.length)
-            this.http_post('mailto', {_cid: a_cids.join(','), _source: this.env.source}, true);
+            this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source}, true);
+          else if (this.env.group)
+            this.http_post('mailto', { _gid: this.env.group, _source: this.env.source}, true);
 
           break;
         }
+        else if (props)
+          url += '&_to='+urlencode(props);
 
         this.redirect(url);
         break;
@@ -1048,15 +1054,17 @@
       // unified command call (command name == function name)
       default:
         var func = command.replace(/-/g, '_');
-        if (this[func] && typeof this[func] === 'function')
-          this[func](props);
+        if (this[func] && typeof this[func] === 'function') {
+          ret = this[func](props);
+        }
         break;
     }
 
-    this.triggerEvent('after'+command, props);
+    if (this.triggerEvent('after'+command, props) === false)
+      ret = false;
     this.triggerEvent('actionafter', {props:props, action:command});
 
-    return obj ? false : true;
+    return ret === false ? false : obj ? false : true;
   };
 
   // set command(s) enabled or disabled
@@ -1401,6 +1409,10 @@
   {
     var model, list, li, id;
 
+    // ignore event if jquery UI dialog is open
+    if ($(rcube_event.get_target(e)).closest('.ui-dialog, .ui-widget-overlay').length)
+      return;
+
     if (list = this.message_list) {
       if (!rcube_mouse_is_over(e, list.list.parentNode))
         list.blur();
@@ -1522,11 +1534,12 @@
 
   this.msglist_keypress = function(list)
   {
+    if (list.modkey == CONTROL_KEY)
+      return;
+
     if (list.key_pressed == list.ENTER_KEY)
       this.command('show');
-    else if (list.key_pressed == list.DELETE_KEY)
-      this.command('delete');
-    else if (list.key_pressed == list.BACKSPACE_KEY)
+    else if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
       this.command('delete');
     else if (list.key_pressed == 33)
       this.command('previouspage');
@@ -1640,14 +1653,18 @@
     if (!this.gui_objects.messagelist || !this.message_list)
       return false;
 
+    // Prevent from adding messages from different folder (#1487752)
+    if (flags.mbox != this.env.mailbox && !flags.skip_mbox_check)
+      return false;
+
     if (!this.env.messages[uid])
       this.env.messages[uid] = {};
 
     // merge flags over local message object
     $.extend(this.env.messages[uid], {
       deleted: flags.deleted?1:0,
-      replied: flags.replied?1:0,
-      unread: flags.unread?1:0,
+      replied: flags.answered?1:0,
+      unread: !flags.seen?1:0,
       forwarded: flags.forwarded?1:0,
       flagged: flags.flagged?1:0,
       has_children: flags.has_children?1:0,
@@ -1661,23 +1678,18 @@
       flags: flags.extra_flags
     });
 
-    var c, html, tree = expando = '',
+    var c, n, col, html, tree = '', expando = '',
       list = this.message_list,
       rows = list.rows,
-      tbody = this.gui_objects.messagelist.tBodies[0],
-      rowcount = tbody.rows.length,
-      even = rowcount%2,
       message = this.env.messages[uid],
       css_class = 'message'
-        + (even ? ' even' : ' odd')
-        + (flags.unread ? ' unread' : '')
+        + (!flags.seen ? ' unread' : '')
         + (flags.deleted ? ' deleted' : '')
         + (flags.flagged ? ' flagged' : '')
-        + (flags.unread_children && !flags.unread && !this.env.autoexpand_threads ? ' unroot' : '')
+        + (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '')
         + (message.selected ? ' selected' : ''),
       // for performance use DOM instead of jQuery here
-      row = document.createElement('tr'),
-      col = document.createElement('td');
+      row = document.createElement('tr');
 
     row.id = 'rcmrow'+uid;
     row.className = css_class;
@@ -1688,12 +1700,12 @@
       css_class += ' status';
       if (flags.deleted)
         css_class += ' deleted';
-      else if (flags.unread)
+      else if (!flags.seen)
         css_class += ' unread';
       else if (flags.unread_children > 0)
         css_class += ' unreadchildren';
     }
-    if (flags.replied)
+    if (flags.answered)
       css_class += ' replied';
     if (flags.forwarded)
       css_class += ' forwarded';
@@ -1704,9 +1716,10 @@
 
     // threads
     if (this.env.threading) {
-      // This assumes that div width is hardcoded to 15px,
-      var width = message.depth * 15;
       if (message.depth) {
+        // This assumes that div width is hardcoded to 15px,
+        tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + (message.depth * 15) + 'px;">&nbsp;&nbsp;</span>';
+
         if ((rows[message.parent_uid] && rows[message.parent_uid].expanded === false)
           || ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) &&
             (!rows[message.parent_uid] || !rows[message.parent_uid].expanded))
@@ -1721,13 +1734,9 @@
         if (message.expanded === undefined && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) {
           message.expanded = true;
         }
-      }
 
-      if (width)
-        tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + width + 'px;">&nbsp;&nbsp;</span>';
-
-      if (message.has_children && !message.depth)
         expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '">&nbsp;&nbsp;</div>';
+      }
     }
 
     tree += '<span id="msgicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
@@ -1741,7 +1750,7 @@
     }
 
     // add each submitted col
-    for (var n in this.env.coltypes) {
+    for (n in this.env.coltypes) {
       c = this.env.coltypes[n];
       col = document.createElement('td');
       col.className = String(c).toLowerCase();
@@ -1761,7 +1770,7 @@
       else if (c == 'status') {
         if (flags.deleted)
           css_class = 'deleted';
-        else if (flags.unread)
+        else if (!flags.seen)
           css_class = 'unread';
         else if (flags.unread_children > 0)
           css_class = 'unreadchildren';
@@ -1945,18 +1954,13 @@
   // list messages of a specific mailbox using filter
   this.filter_mailbox = function(filter)
   {
-    var search, lock = this.set_busy(true, 'searching');
-
-    if (this.gui_objects.qsearchbox)
-      search = this.gui_objects.qsearchbox.value;
+    var lock = this.set_busy(true, 'searching');
 
     this.clear_message_list();
 
     // reset vars
     this.env.current_page = 1;
-    this.http_request('search', '_filter='+filter
-        + (search ? '&_q='+urlencode(search) : '')
-        + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), lock);
+    this.http_request('search', this.search_params(false, filter), lock);
   };
 
   // list messages of a specific mailbox
@@ -2055,8 +2059,7 @@
       new_row = tbody.firstChild;
 
     while (new_row) {
-      if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid])
-	    && r.unread_children) {
+      if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) && r.unread_children) {
 	    this.message_list.expand_all(r);
 	    this.set_unread_children(r.uid);
       }
@@ -2091,8 +2094,12 @@
   };
 
   // Initializes threads indicators/expanders after list update
-  this.init_threads = function(roots)
+  this.init_threads = function(roots, mbox)
   {
+    // #1487752
+    if (mbox && mbox != this.env.mailbox)
+      return false;
+
     for (var n=0, len=roots.length; n<len; n++)
       this.add_tree_icons(roots[n]);
     this.expand_threads();
@@ -2496,7 +2503,7 @@
     // if there is a trash mailbox defined and we're not currently in it
     else {
       // if shift was pressed delete it immediately
-      if (list && list.shiftkey) {
+      if (list && list.modkey == SHIFT_KEY) {
         if (confirm(this.get_label('deletemessagesconfirm')))
           this.permanently_remove_messages();
       }
@@ -3156,7 +3163,7 @@
         sig = this.env.signatures[sig].is_html ? this.env.signatures[sig].plain_text : this.env.signatures[sig].text;
         sig = sig.replace(/\r\n/g, '\n');
 
-        if (!sig.match(/^--[ -]\n/))
+        if (!sig.match(/^--[ -]\n/m))
           sig = sig_separator + '\n' + sig;
 
         p = this.env.sig_above ? message.indexOf(sig) : message.lastIndexOf(sig);
@@ -3168,7 +3175,7 @@
         sig = this.env.signatures[id]['is_html'] ? this.env.signatures[id]['plain_text'] : this.env.signatures[id]['text'];
         sig = sig.replace(/\r\n/g, '\n');
 
-        if (!sig.match(/^--[ -]\n/))
+        if (!sig.match(/^--[ -]\n/m))
           sig = sig_separator + '\n' + sig;
 
         if (this.env.sig_above) {
@@ -3237,12 +3244,12 @@
       if (this.env.signatures[id]) {
         if (this.env.signatures[id].is_html) {
           sig = this.env.signatures[id].text;
-          if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/))
+          if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/m))
             sig = sig_separator + '<br />' + sig;
         }
         else {
           sig = this.env.signatures[id].text;
-          if (!sig.match(/^--[ -]\r?\n/))
+          if (!sig.match(/^--[ -]\r?\n/m))
             sig = sig_separator + '\n' + sig;
           sig = '<pre>' + sig + '</pre>';
         }
@@ -3408,38 +3415,56 @@
   this.qsearch = function(value)
   {
     if (value != '') {
-      var n, r, addurl = '', mods_arr = [],
-        mods = this.env.search_mods,
-        mbox = this.env.mailbox,
-        lock = this.set_busy(true, 'searching');
+      var n, lock = this.set_busy(true, 'searching');
 
-      if (this.message_list) {
+      if (this.message_list)
         this.clear_message_list();
-        if (mods)
-          mods = mods[mbox] ? mods[mbox] : mods['*'];
-      } else if (this.contact_list) {
+      else if (this.contact_list)
         this.list_contacts_clear();
-      }
+
+      // reset vars
+      this.env.current_page = 1;
+      r = this.http_request('search', this.search_params(value)
+        + (this.env.source ? '&_source='+urlencode(this.env.source) : '')
+        + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock);
+
+      this.env.qsearch = {lock: lock, request: r};
+    }
+  };
+
+  // build URL params for search
+  this.search_params = function(search, filter)
+  {
+    var n, url = [], mods_arr = [],
+      mods = this.env.search_mods,
+      mbox = this.env.mailbox;
+
+    if (!filter && this.gui_objects.search_filter)
+      filter = this.gui_objects.search_filter.value;
+
+    if (!search && this.gui_objects.qsearchbox)
+      search = this.gui_objects.qsearchbox.value;
+
+    if (filter)
+      url.push('_filter=' + urlencode(filter));
+
+    if (search) {
+      url.push('_q='+urlencode(search));
+
+      if (mods && this.message_list)
+        mods = mods[mbox] ? mods[mbox] : mods['*'];
 
       if (mods) {
         for (n in mods)
           mods_arr.push(n);
-        addurl += '&_headers='+mods_arr.join(',');
+        url.push('_headers='+mods_arr.join(','));
       }
-
-      if (this.gui_objects.search_filter)
-        addurl += '&_filter=' + this.gui_objects.search_filter.value;
-
-      // reset vars
-      this.env.current_page = 1;
-      r = this.http_request('search', '_q='+urlencode(value)
-        + (mbox ? '&_mbox='+urlencode(mbox) : '')
-        + (this.env.source ? '&_source='+urlencode(this.env.source) : '')
-        + (this.env.group ? '&_gid='+urlencode(this.env.group) : '')
-        + (addurl ? addurl : ''), lock);
-
-      this.env.qsearch = {lock: lock, request: r};
     }
+
+    if (mbox)
+      url.push('_mbox='+urlencode(mbox));
+
+    return url.join('&');
   };
 
   // reset quick-search form
@@ -3495,13 +3520,15 @@
 
         return rcube_event.cancel(e);
 
-      case 9:  // tab
-        if (mod == SHIFT_KEY)
-          break;
+      case 9:   // tab
+        if (mod == SHIFT_KEY || !this.ksearch_visible()) {
+          this.ksearch_hide();
+          return;
+        }
 
       case 13:  // enter
-        if (this.ksearch_selected === null || !this.ksearch_value)
-          break;
+        if (!this.ksearch_visible())
+          return false;
 
         // insert selected address and hide ksearch pane
         this.insert_recipient(this.ksearch_selected);
@@ -3526,6 +3553,11 @@
     return true;
   };
 
+  this.ksearch_visible = function()
+  {
+    return (this.ksearch_selected !== null && this.ksearch_selected !== undefined && this.ksearch_value);
+  };
+
   this.ksearch_select = function(node)
   {
     var current = $('#rcmksearchSelected');
@@ -3541,7 +3573,7 @@
 
   this.insert_recipient = function(id)
   {
-    if (!this.env.contacts[id] || !this.ksearch_input)
+    if (id === null || !this.env.contacts[id] || !this.ksearch_input)
       return;
 
     // get cursor pos
@@ -3603,7 +3635,8 @@
     var cpos = this.get_caret_pos(this.ksearch_input),
       p = inp_value.lastIndexOf(',', cpos-1),
       q = inp_value.substring(p+1, cpos),
-      min = this.env.autocomplete_min_length;
+      min = this.env.autocomplete_min_length,
+      ac = this.ksearch_data;
 
     // trim query string
     q = $.trim(q);
@@ -3612,28 +3645,25 @@
     if (q == this.ksearch_value)
       return;
 
+    this.ksearch_destroy();
+
     if (q.length && q.length < min) {
-      if (!this.env.acinfo) {
-        this.env.acinfo = this.display_message(
+      if (!this.ksearch_info) {
+        this.ksearch_info = this.display_message(
           this.get_label('autocompletechars').replace('$min', min));
       }
       return;
-    }
-    else if (this.env.acinfo) {
-      this.hide_message(this.env.acinfo);
     }
 
     var old_value = this.ksearch_value;
     this.ksearch_value = q;
 
-    this.ksearch_destroy();
-
     // ...string is empty
     if (!q.length)
       return;
 
-    // ...new search value contains old one and previous search result was empty
-    if (old_value && old_value.length && this.env.contacts && !this.env.contacts.length && q.indexOf(old_value) == 0)
+    // ...new search value contains old one and previous search was not finished or its result was empty
+    if (old_value && old_value.length && q.indexOf(old_value) == 0 && (!ac || !ac.num) && this.env.contacts && !this.env.contacts.length)
       return;
 
     var i, lock, source, xhr, reqid = new Date().getTime(),
@@ -3641,7 +3671,8 @@
       sources = props && props.sources ? props.sources : [],
       action = props && props.action ? props.action : 'mail/autocomplete';
 
-    this.ksearch_data = {id: reqid, sources: sources.slice(), action: action, locks: [], requests: []};
+    this.ksearch_data = {id: reqid, sources: sources.slice(), action: action,
+      locks: [], requests: [], num: sources.length};
 
     for (i=0; i<threads; i++) {
       source = this.ksearch_data.sources.shift();
@@ -3668,7 +3699,9 @@
       return;
 
     // display search results
-    var p, ul, li, text, init, s_val = this.ksearch_value,
+    var ul, li, text, init,
+      value = this.ksearch_value,
+      data = this.ksearch_data,
       maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
 
     // create results pane if not present
@@ -3701,7 +3734,7 @@
       for (i=0; i < results.length && maxlen > 0; i++) {
         text = typeof results[i] === 'object' ? results[i].name : results[i];
         li = document.createElement('LI');
-        li.innerHTML = text.replace(new RegExp('('+RegExp.escape(s_val)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
+        li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
         li.onmouseover = function(){ ref.ksearch_select(this); };
         li.onmouseup = function(){ ref.ksearch_click(this) };
         li._rcm_id = this.env.contacts.length + i;
@@ -3723,15 +3756,24 @@
       this.env.contacts = this.env.contacts.concat(results);
 
     // run next parallel search
-    if (maxlen > 0 && this.ksearch_data.id == reqid && this.ksearch_data.sources.length) {
-      var lock, xhr, props = this.ksearch_data, source = props.sources.shift();
-      if (source) {
-        lock = this.display_message(this.get_label('searching'), 'loading');
-        xhr = this.http_post(props.action, '_search='+urlencode(s_val)+'&_id='+reqid
-          +'&_source='+urlencode(source), lock);
+    if (data.id == reqid) {
+      data.num--;
+      if (maxlen > 0 && data.sources.length) {
+        var lock, xhr, source = data.sources.shift();
+        if (source) {
+          lock = this.display_message(this.get_label('searching'), 'loading');
+          xhr = this.http_post(data.action, '_search='+urlencode(value)+'&_id='+reqid
+            +'&_source='+urlencode(source), lock);
 
-        this.ksearch_data.locks.push(lock);
-        this.ksearch_data.requests.push(xhr);
+          this.ksearch_data.locks.push(lock);
+          this.ksearch_data.requests.push(xhr);
+        }
+      }
+      else if (!maxlen) {
+        if (!this.ksearch_msg)
+          this.ksearch_msg = this.display_message(this.get_label('autocompletemore'));
+        // abort pending searches
+        this.ksearch_abort();
       }
     }
   };
@@ -3765,8 +3807,24 @@
     this.ksearch_destroy();
   };
 
-  // Aborts pending autocomplete requests
+  // Clears autocomplete data/requests
   this.ksearch_destroy = function()
+  {
+    this.ksearch_abort();
+
+    if (this.ksearch_info)
+      this.hide_message(this.ksearch_info);
+
+    if (this.ksearch_msg)
+      this.hide_message(this.ksearch_msg);
+
+    this.ksearch_data = null;
+    this.ksearch_info = null;
+    this.ksearch_msg = null;
+  }
+
+  // Aborts pending autocomplete requests
+  this.ksearch_abort = function()
   {
     var i, len, ac = this.ksearch_data;
 
@@ -3775,9 +3833,8 @@
 
     for (i=0, len=ac.locks.length; i<len; i++)
       this.abort_request({request: ac.requests[i], lock: ac.locks[i]});
+  };
 
-    this.ksearch_data = null;
-  }
 
   /*********************************************************/
   /*********         address book methods          *********/
@@ -3819,7 +3876,7 @@
       }
     }
 
-    this.enable_command('compose', list.selection.length > 0);
+    this.enable_command('compose', this.env.group || list.selection.length > 0);
     this.enable_command('edit', id && writable);
     this.enable_command('delete', list.selection.length && writable);
 
@@ -3905,7 +3962,8 @@
   {
     this.contact_list.clear(true);
     this.show_contentframe(false);
-    this.enable_command('delete', 'compose', false);
+    this.enable_command('delete', false);
+    this.enable_command('compose', this.env.group ? true : false);
   };
 
   // load contact record
@@ -3978,8 +4036,10 @@
   this.delete_contacts = function()
   {
     // exit if no mailbox specified or if selection is empty
-    var selection = this.contact_list.get_selection();
-    if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
+    var selection = this.contact_list.get_selection(),
+      undelete = this.env.address_sources[this.env.source].undelete;
+
+    if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm'))))
       return;
 
     var id, n, a_cids = [], qs = '';
@@ -4006,7 +4066,10 @@
       qs += '&_search='+this.env.search_request;
 
     // send request to server
-    this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs);
+    this.http_post('delete', '_cid='+urlencode(a_cids.join(','))
+      +'&_source='+urlencode(this.env.source)
+      +'&_from='+(this.env.action ? this.env.action : '')+qs,
+      this.display_message(this.get_label('contactdeleting'), 'loading'));
 
     return true;
   };
@@ -4045,31 +4108,29 @@
   // add row to contacts list
   this.add_contact_row = function(cid, cols, select)
   {
-    if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
+    if (!this.gui_objects.contactslist)
       return false;
 
-    var tbody = this.gui_objects.contactslist.tBodies[0],
-      rowcount = tbody.rows.length,
-      even = rowcount%2,
+    var c, list = this.contact_list,
       row = document.createElement('tr');
 
     row.id = 'rcmrow'+String(cid).replace(this.identifier_expr, '_');
-    row.className = 'contact '+(even ? 'even' : 'odd');
+    row.className = 'contact';
 
-    if (this.contact_list.in_selection(cid))
+    if (list.in_selection(cid))
       row.className += ' selected';
 
     // add each submitted col
-    for (var c in cols) {
+    for (c in cols) {
       col = document.createElement('td');
       col.className = String(c).toLowerCase();
       col.innerHTML = cols[c];
       row.appendChild(col);
     }
 
-    this.contact_list.insert_row(row);
+    list.insert_row(row);
 
-    this.enable_command('export', (this.contact_list.rowcount > 0));
+    this.enable_command('export', list.rowcount > 0);
   };
 
   this.init_contact_form = function()
@@ -4090,6 +4151,21 @@
       ref.insert_edit_field($(this).val(), $(this).attr('rel'), this);
       this.selectedIndex = 0;
     });
+
+    // enable date pickers on date fields
+    if ($.datepicker && this.env.date_format) {
+      $.datepicker.setDefaults({
+        dateFormat: this.env.date_format,
+        changeMonth: true,
+        changeYear: true,
+        yearRange: '-100:+10',
+        showOtherMonths: true,
+        selectOtherMonths: true,
+        monthNamesShort: this.env.month_names,
+        onSelect: function(dateText) { $(this).focus().val(dateText) }
+      });
+      $('input.datepicker').datepicker();
+    }
 
     $("input[type='text']:visible").first().focus();
   };
@@ -4309,7 +4385,7 @@
 
     elem.focus(function(){ ref.focus_textfield(this); })
       .blur(function(){ ref.blur_textfield(this); })
-      .each(function(){ this._placeholder = this.title = ref.env.coltypes[col].label; ref.blur_textfield(this); });
+      .each(function(){ this._placeholder = this.title = (ref.env.coltypes[col].label || ''); ref.blur_textfield(this); });
   };
 
   this.insert_edit_field = function(col, section, menu)
@@ -4346,6 +4422,9 @@
             .appendTo(cell);
 
           this.init_edit_field(col, input);
+          
+          if (colprop.type == 'date' && $.datepicker)
+            input.datepicker();
         }
         else if (colprop.type == 'composite') {
           var childcol, cp, first, templ, cols = [], suffices = [];
@@ -5433,8 +5512,12 @@
   };
 
   // replace content of row count display
-  this.set_rowcount = function(text)
+  this.set_rowcount = function(text, mbox)
   {
+    // #1487752
+    if (mbox && mbox != this.env.mailbox)
+      return false;
+
     $(this.gui_objects.countdisplay).html(text);
 
     // update page navigation buttons
@@ -5686,7 +5769,7 @@
     var base = this.env.comm_path;
 
     // overwrite task name
-    if (query._action.match(/([a-z]+)\/([a-z-_.]+)/)) {
+    if (query._action.match(/([a-z]+)\/([a-z0-9-_.]+)/)) {
       query._action = RegExp.$2;
       base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
     }
@@ -6120,7 +6203,7 @@
 {
   if (!elem.title) {
     var $elem = $(elem),
-      txt = $elem.text(),
+      txt = $.trim($elem.text()),
       tmp = $('<span>').text(txt)
         .css({'position': 'absolute', 'float': 'left', 'visibility': 'hidden',
           'font-size': $elem.css('font-size'), 'font-weight': $elem.css('font-weight')})

--
Gitblit v1.9.1