From b6cd452bd31bfd4b6b94b23fe54b424fdf901e61 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Thu, 18 Aug 2011 14:34:56 -0400
Subject: [PATCH] Backport changes from r5084 to r5090 to release branch

---
 program/js/app.js |  413 +++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 265 insertions(+), 148 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 3143236..d15950d 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -138,14 +138,6 @@
       return;
     }
 
-    // Enable debug console
-    if (!window.console || !window.console.log) {
-      window.console = new rcube_console();
-    }
-    else {
-      $('#console').hide();
-    }
-
     // find all registered gui containers
     for (var n in this.gui_containers)
       this.gui_containers[n] = $('#'+this.gui_containers[n]);
@@ -260,7 +252,10 @@
         }
         // show printing dialog
         else if (this.env.action == 'print' && this.env.uid)
-          window.print();
+          if (bw.safari)
+            window.setTimeout('window.print()', 10);
+          else
+            window.print();
 
         // get unread count for each mailbox
         if (this.gui_objects.mailboxlist) {
@@ -314,11 +309,8 @@
           this.enable_command('show', 'edit', true);
           // register handlers for group assignment via checkboxes
           if (this.gui_objects.editform) {
-            $('input.groupmember').change(function(){
-              var cmd = this.checked ? 'group-addmembers' : 'group-delmembers';
-              ref.http_post(cmd, '_cid='+urlencode(ref.env.cid)
-                + '&_source='+urlencode(ref.env.source)
-                + '&_gid='+urlencode(this.value));
+            $('input.groupmember').change(function() {
+              ref.group_member_change(this.checked ? 'add' : 'del', ref.env.cid, ref.env.source, this.value);
             });
           }
         }
@@ -338,6 +330,10 @@
 
         this.enable_command('add', 'import', this.env.writable_source);
         this.enable_command('list', 'listgroup', 'advanced-search', true);
+
+        // load contacts of selected source
+        if (!this.env.action)
+          this.command('list', this.env.source);
         break;
 
 
@@ -433,6 +429,11 @@
     this.start_keepalive();
   };
 
+  this.log = function(msg)
+  {
+    if (window.console && console.log)
+      console.log(msg);
+  };
 
   /*********************************************************/
   /*********       client command interface        *********/
@@ -640,11 +641,6 @@
             // reload form
             if (props == 'reload') {
               form.action += '?_reload=1';
-            }
-            else if ((input = $("input[name='_name']", form)) &&input.length && input.val() == '') {
-              alert(this.get_label('nonamewarning'));
-              input.focus();
-              break;
             }
             else if (this.task == 'settings' && (this.env.identities_level % 2) == 0  &&
               (input = $("input[name='_email']", form)) && input.length && !rcube_check_email(input.val())
@@ -991,14 +987,14 @@
 
       // reset quicksearch
       case 'reset-search':
-        var s = this.env.search_request;
+        var n, s = this.env.search_request || this.env.qsearch;
         this.reset_qsearch();
 
         if (s && this.env.mailbox)
           this.list_mailbox(this.env.mailbox);
         else if (s && this.task == 'addressbook') {
           if (this.env.source == '') {
-            for (var n in this.env.address_sources) break;
+            for (n in this.env.address_sources) break;
             this.env.source = n;
             this.env.group = '';
           }
@@ -1377,12 +1373,12 @@
       ul.show();
       div.removeClass('collapsed').addClass('expanded');
       var reg = new RegExp('&'+urlencode(id)+'&');
-      this.set_env('collapsed_folders', this.env.collapsed_folders.replace(reg, ''));
+      this.env.collapsed_folders = this.env.collapsed_folders.replace(reg, '');
     }
     else {
       ul.hide();
       div.removeClass('expanded').addClass('collapsed');
-      this.set_env('collapsed_folders', this.env.collapsed_folders+'&'+urlencode(id)+'&');
+      this.env.collapsed_folders = this.env.collapsed_folders+'&'+urlencode(id)+'&';
 
       // select parent folder if one of its childs is currently selected
       if (this.env.mailbox.indexOf(id + this.env.delimiter) == 0)
@@ -1567,10 +1563,10 @@
       }
 
     if ((found = $.inArray('flag', this.env.coltypes)) >= 0)
-      this.set_env('flagged_col', found);
+      this.env.flagged_col = found;
 
     if ((found = $.inArray('subject', this.env.coltypes)) >= 0)
-      this.set_env('subject_col', found);
+      this.env.subject_col = found;
 
     this.command('save-pref', { name: 'list_cols', value: this.env.coltypes, session: 'list_attrib/columns' });
   };
@@ -1876,10 +1872,7 @@
     if (action == 'preview' && String(target.location.href).indexOf(url) >= 0)
       this.show_contentframe(true);
     else {
-      if (!this.env.frame_lock) {
-        (this.is_framed() ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
-      }
-      this.location_href(this.env.comm_path+url, target);
+      this.location_href(this.env.comm_path+url, target, true);
 
       // mark as read and change mbox unread counter
       if (action == 'preview' && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread && this.env.preview_pane_mark_read >= 0) {
@@ -1911,6 +1904,12 @@
 
     if (!show && this.busy)
       this.set_busy(false, null, this.env.frame_lock);
+  };
+
+  this.lock_frame = function()
+  {
+    if (!this.env.frame_lock)
+      (this.is_framed() ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
   };
 
   // list a specific page
@@ -2867,12 +2866,21 @@
       input_subject = $("input[name='_subject']"),
       input_message = $("[name='_message']").get(0),
       html_mode = $("input[name='_is_html']").val() == '1',
-      ac_fields = ['cc', 'bcc', 'replyto', 'followupto'];
+      ac_fields = ['cc', 'bcc', 'replyto', 'followupto'],
+      ac_props;
+
+    // configure parallel autocompletion
+    if (this.env.autocomplete_threads > 0) {
+      ac_props = {
+        threads: this.env.autocomplete_threads,
+        sources: this.env.autocomplete_sources
+      };
+    }
 
     // init live search events
-    this.init_address_input_events(input_to);
+    this.init_address_input_events(input_to, ac_props);
     for (var i in ac_fields) {
-      this.init_address_input_events($("[name='_"+ac_fields[i]+"']"));
+      this.init_address_input_events($("[name='_"+ac_fields[i]+"']"), ac_props);
     }
 
     if (!html_mode) {
@@ -2900,9 +2908,9 @@
     this.auto_save_start();
   };
 
-  this.init_address_input_events = function(obj)
+  this.init_address_input_events = function(obj, props)
   {
-    obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e){ return ref.ksearch_keydown(e, this); })
+    obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e) { return ref.ksearch_keydown(e, this, props); })
       .attr('autocomplete', 'off');
   };
 
@@ -3245,11 +3253,21 @@
       return false;
 
     // get file input field, count files on capable browser
-    var field = $('input[type=file]', form).get(0),
+    var i, size = 0, field = $('input[type=file]', form).get(0),
       files = field.files ? field.files.length : field.value ? 1 : 0;
 
     // create hidden iframe and post upload form
     if (files) {
+      // check file size
+      if (field.files && this.env.max_filesize && this.env.filesizeerror) {
+        for (i=0; i<files; i++)
+          size += field.files[i].size;
+        if (size && size > this.env.max_filesize) {
+          this.display_message(this.env.filesizeerror, 'error');
+          return;
+        }
+      }
+
       var frame_name = this.async_upload_form(form, 'upload', function(e) {
         var d, content = '';
         try {
@@ -3381,7 +3399,7 @@
   this.qsearch = function(value)
   {
     if (value != '') {
-      var n, addurl = '', mods_arr = [],
+      var n, r, addurl = '', mods_arr = [],
         mods = this.env.search_mods,
         mbox = this.env.mailbox,
         lock = this.set_busy(true, 'searching');
@@ -3405,13 +3423,14 @@
 
       // reset vars
       this.env.current_page = 1;
-      this.http_request('search', '_q='+urlencode(value)
+      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};
     }
-    return true;
   };
 
   // reset quick-search form
@@ -3420,8 +3439,11 @@
     if (this.gui_objects.qsearchbox)
       this.gui_objects.qsearchbox.value = '';
 
+    if (this.env.qsearch)
+      this.abort_request(this.env.qsearch);
+
+    this.env.qsearch = null;
     this.env.search_request = null;
-    return true;
   };
 
   this.sent_successfully = function(type, msg)
@@ -3437,14 +3459,14 @@
   /*********************************************************/
 
   // handler for keyboard events on address-fields
-  this.ksearch_keydown = function(e, obj)
+  this.ksearch_keydown = function(e, obj, props)
   {
     if (this.ksearch_timer)
       clearTimeout(this.ksearch_timer);
 
-    var highlight;
-    var key = rcube_event.get_keycode(e);
-    var mod = rcube_event.get_modifier(e);
+    var highlight,
+      key = rcube_event.get_keycode(e),
+      mod = rcube_event.get_modifier(e);
 
     switch (key) {
       case 38:  // key up
@@ -3467,8 +3489,8 @@
         if (mod == SHIFT_KEY)
           break;
 
-     case 13:  // enter
-        if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value)
+      case 13:  // enter
+        if (this.ksearch_selected === null || !this.ksearch_value)
           break;
 
         // insert selected address and hide ksearch pane
@@ -3488,7 +3510,7 @@
     }
 
     // start timer
-    this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(); }, 200);
+    this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(props); }, 200);
     this.ksearch_input = obj;
 
     return true;
@@ -3518,10 +3540,11 @@
       p = inp_value.lastIndexOf(this.ksearch_value, cpos),
       trigger = false,
       insert = '',
-
       // replace search string with full address
       pre = inp_value.substring(0, p),
       end = inp_value.substring(p+this.ksearch_value.length, inp_value.length);
+
+    this.ksearch_destroy();
 
     // insert all members of a group
     if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) {
@@ -3556,7 +3579,7 @@
   };
 
   // address search processor
-  this.ksearch_get_results = function()
+  this.ksearch_get_results = function(props)
   {
     var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
 
@@ -3593,6 +3616,8 @@
     var old_value = this.ksearch_value;
     this.ksearch_value = q;
 
+    this.ksearch_destroy();
+
     // ...string is empty
     if (!q.length)
       return;
@@ -3601,59 +3626,104 @@
     if (old_value && old_value.length && this.env.contacts && !this.env.contacts.length && q.indexOf(old_value) == 0)
       return;
 
-    var lock = this.display_message(this.get_label('searching'), 'loading');
-    this.http_post('mail/autocomplete', '_search='+urlencode(q), lock);
+    var i, lock, source, xhr, reqid = new Date().getTime(),
+      threads = props && props.threads ? props.threads : 1,
+      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: []};
+
+    for (i=0; i<threads; i++) {
+      source = this.ksearch_data.sources.shift();
+      if (threads > 1 && source === null)
+        break;
+
+      lock = this.display_message(this.get_label('searching'), 'loading');
+      xhr = this.http_post(action, '_search='+urlencode(q)+'&_id='+reqid
+        + (source ? '&_source='+urlencode(source) : ''), lock);
+
+      this.ksearch_data.locks.push(lock);
+      this.ksearch_data.requests.push(xhr);
+    }
   };
 
-  this.ksearch_query_results = function(results, search)
+  this.ksearch_query_results = function(results, search, reqid)
   {
-    // ignore this outdated search response
-    if (this.ksearch_value && search != this.ksearch_value)
+    // search stopped in meantime?
+    if (!this.ksearch_value)
       return;
 
-    this.env.contacts = results ? results : [];
-    this.ksearch_display_results(this.env.contacts);
-  };
+    // ignore this outdated search response
+    if (this.ksearch_input && search != this.ksearch_value)
+      return;
 
-  this.ksearch_display_results = function (a_results)
-  {
     // display search results
-    if (a_results.length && this.ksearch_input && this.ksearch_value) {
-      var p, ul, li, text, s_val = this.ksearch_value;
+    var p, ul, li, text, init, s_val = this.ksearch_value,
+      maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
 
-      // create results pane if not present
-      if (!this.ksearch_pane) {
-        ul = $('<ul>');
-        this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane').css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
-        this.ksearch_pane.__ul = ul[0];
-      }
+    // create results pane if not present
+    if (!this.ksearch_pane) {
+      ul = $('<ul>');
+      this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane')
+        .css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
+      this.ksearch_pane.__ul = ul[0];
+    }
 
-      // remove all search results
-      ul = this.ksearch_pane.__ul;
+    ul = this.ksearch_pane.__ul;
+
+    // remove all search results or add to existing list if parallel search
+    if (reqid && this.ksearch_pane.data('reqid') == reqid) {
+      maxlen -= ul.childNodes.length;
+    }
+    else {
+      this.ksearch_pane.data('reqid', reqid);
+      init = 1;
+      // reset content
       ul.innerHTML = '';
+      this.env.contacts = [];
+      // move the results pane right under the input box
+      var pos = $(this.ksearch_input).offset();
+      this.ksearch_pane.css({ left:pos.left+'px', top:(pos.top + this.ksearch_input.offsetHeight)+'px', display: 'none'});
+    }
 
-      // add each result line to list
-      for (i=0; i < a_results.length; i++) {
-        text = typeof a_results[i] === 'object' ? a_results[i].name : a_results[i];
+    // add each result line to list
+    if (results && results.length) {
+      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.onmouseover = function(){ ref.ksearch_select(this); };
         li.onmouseup = function(){ ref.ksearch_click(this) };
-        li._rcm_id = i;
+        li._rcm_id = this.env.contacts.length + i;
         ul.appendChild(li);
+        maxlen -= 1;
       }
-
-      // select the first
-      $(ul.firstChild).attr('id', 'rcmksearchSelected').addClass('selected');
-      this.ksearch_selected = 0;
-
-      // move the results pane right under the input box and make it visible
-      var pos = $(this.ksearch_input).offset();
-      this.ksearch_pane.css({ left:pos.left+'px', top:(pos.top + this.ksearch_input.offsetHeight)+'px' }).show();
     }
-    // hide results pane
-    else
-      this.ksearch_hide();
+
+    if (ul.childNodes.length) {
+      this.ksearch_pane.show();
+      // select the first
+      if (!this.env.contacts.length) {
+        $('li:first', ul).attr('id', 'rcmksearchSelected').addClass('selected');
+        this.ksearch_selected = 0;
+      }
+    }
+
+    if (results && results.length)
+      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);
+
+        this.ksearch_data.locks.push(lock);
+        this.ksearch_data.requests.push(xhr);
+      }
+    }
   };
 
   this.ksearch_click = function(node)
@@ -3670,20 +3740,34 @@
     if (this.ksearch_timer)
       clearTimeout(this.ksearch_timer);
 
-    this.ksearch_value = '';
     this.ksearch_input = null;
     this.ksearch_hide();
   };
 
-
   this.ksearch_hide = function()
   {
     this.ksearch_selected = null;
+    this.ksearch_value = '';
 
     if (this.ksearch_pane)
       this.ksearch_pane.hide();
-   };
 
+    this.ksearch_destroy();
+  };
+
+  // Aborts pending autocomplete requests
+  this.ksearch_destroy = function()
+  {
+    var i, len, ac = this.ksearch_data;
+
+    if (!ac)
+      return;
+
+    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          *********/
@@ -3833,10 +3917,22 @@
       if (this.env.group)
         add_url += '&_gid='+urlencode(this.env.group);
 
-      this.set_busy(true);
-      this.location_href(this.env.comm_path+'&_action='+action+'&_source='+urlencode(this.env.source)+'&_cid='+urlencode(cid) + add_url, target);
+      this.location_href(this.env.comm_path+'&_action='+action
+        +'&_source='+urlencode(this.env.source)
+        +'&_cid='+urlencode(cid) + add_url, target, true);
     }
     return true;
+  };
+
+  // add/delete member to/from the group
+  this.group_member_change = function(what, cid, source, gid)
+  {
+    what = what == 'add' ? 'add' : 'del';
+    var lock = this.display_message(this.get_label(what == 'add' ? 'addingmember' : 'removingmember'), 'loading');
+
+    this.http_post('group-'+what+'members', '_cid='+urlencode(cid)
+      + '&_source='+urlencode(source)
+      + '&_gid='+urlencode(gid), lock);
   };
 
   // copy a contact to the specified target (group or directory)
@@ -3845,23 +3941,22 @@
     if (!cid)
       cid = this.contact_list.get_selection().join(',');
 
-    if (to.type == 'group' && to.source == this.env.source) {
-      this.http_post('group-addmembers', '_cid='+urlencode(cid)
-        + '&_source='+urlencode(this.env.source)
-        + '&_gid='+urlencode(to.id));
-    }
+    if (to.type == 'group' && to.source == this.env.source)
+      this.group_member_change('add', cid, to.source, to.id);
     else if (to.type == 'group' && !this.env.address_sources[to.source].readonly) {
+      var lock = this.display_message(this.get_label('copyingcontact'), 'loading');
       this.http_post('copy', '_cid='+urlencode(cid)
         + '&_source='+urlencode(this.env.source)
         + '&_to='+urlencode(to.source)
         + '&_togid='+urlencode(to.id)
-        + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''));
+        + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock);
     }
     else if (to.id != this.env.source && cid && this.env.address_sources[to.id] && !this.env.address_sources[to.id].readonly) {
+      var lock = this.display_message(this.get_label('copyingcontact'), 'loading');
       this.http_post('copy', '_cid='+urlencode(cid)
         + '&_source='+urlencode(this.env.source)
         + '&_to='+urlencode(to.id)
-        + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''));
+        + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock);
     }
   };
 
@@ -4016,8 +4111,10 @@
 
   this.group_delete = function()
   {
-    if (this.env.group)
-      this.http_post('group-delete', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(this.env.group), true);
+    if (this.env.group && confirm(this.get_label('deletegroupconfirm'))) {
+      var lock = this.set_busy(true, 'groupdeleting');
+      this.http_post('group-delete', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(this.env.group), lock);
+    }
   };
 
   // callback from server upon group-delete command
@@ -4335,7 +4432,7 @@
   this.set_photo_actions = function(id)
   {
     var n, buttons = this.buttons['upload-photo'];
-    for (n=0; n < buttons.length; n++)
+    for (n=0; buttons && n < buttons.length; n++)
       $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto'));
 
     $('#ff_photo').val(id);
@@ -4354,7 +4451,7 @@
       this.contact_list.clear_selection();
     }
 
-    this.location_href(this.env.comm_path+'&_action=search'+add_url, target);
+    this.location_href(this.env.comm_path+'&_action=search'+add_url, target, true);
 
     return true;
   };
@@ -4377,17 +4474,14 @@
   // preferences section select and load options frame
   this.section_select = function(list)
   {
-    var id = list.get_single_selection();
+    var id = list.get_single_selection(), add_url = '', target = window;
 
     if (id) {
-      var add_url = '', target = window;
-      this.set_busy(true);
-
       if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
         add_url = '&_framed=1';
         target = window.frames[this.env.contentframe];
       }
-      this.location_href(this.env.comm_path+'&_action=edit-prefs&_section='+id+add_url, target);
+      this.location_href(this.env.comm_path+'&_action=edit-prefs&_section='+id+add_url, target, true);
     }
 
     return true;
@@ -4456,6 +4550,9 @@
       row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
     };
     this.subscription_list.init();
+    $('#mailboxroot')
+      .mouseover(function(){ p.focus_subscription(this.id); })
+      .mouseout(function(){ p.unfocus_subscription(this.id); })
   };
 
   this.focus_subscription = function(id)
@@ -4466,18 +4563,16 @@
 
     if (this.drag_active && this.env.mailbox && (row = document.getElementById(id)))
       if (this.env.subscriptionrows[id] &&
-          (folder = this.env.subscriptionrows[id][0])) {
+          (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(reg, '')) &&
-            (!folder.match(new RegExp('^'+RegExp.escape(this.env.mailbox+this.env.delimiter))))) {
-          this.set_env('dstfolder', folder);
+            (!folder.match(new RegExp('^'+RegExp.escape(this.env.mailbox+this.env.delimiter))))
+        ) {
+          this.env.dstfolder = folder;
           $(row).addClass('droptarget');
         }
-      }
-      else if (this.env.mailbox.match(new RegExp(delim))) {
-        this.set_env('dstfolder', this.env.delimiter);
-        $(this.subscription_list.frame).addClass('droptarget');
       }
   };
 
@@ -4485,7 +4580,7 @@
   {
     var row = $('#'+id);
 
-    this.set_env('dstfolder', null);
+    this.env.dstfolder = null;
     if (this.env.subscriptionrows[id] && row[0])
       row.removeClass('droptarget');
     else
@@ -4499,7 +4594,7 @@
     if (list && (id = list.get_single_selection()) &&
         (folder = this.env.subscriptionrows['rcmrow'+id])
     ) {
-      this.set_env('mailbox', folder[0]);
+      this.env.mailbox = folder[0];
       this.show_folder(folder[0]);
       this.enable_command('delete-folder', !folder[2]);
     }
@@ -4515,15 +4610,17 @@
     var delim = RegExp.escape(this.env.delimiter),
       reg = RegExp('['+delim+']?[^'+delim+']+$');
 
-    if (this.env.mailbox && this.env.dstfolder && (this.env.dstfolder != this.env.mailbox) &&
+    if (this.env.mailbox && this.env.dstfolder !== null && (this.env.dstfolder != this.env.mailbox) &&
         (this.env.dstfolder != this.env.mailbox.replace(reg, ''))
     ) {
       reg = new RegExp('[^'+delim+']*['+delim+']', 'g');
-      var lock = this.set_busy(true, 'foldermoving'),
-        basename = this.env.mailbox.replace(reg, ''),
-        newname = this.env.dstfolder==this.env.delimiter ? basename : this.env.dstfolder+this.env.delimiter+basename;
+      var basename = this.env.mailbox.replace(reg, ''),
+        newname = this.env.dstfolder === '' ? basename : this.env.dstfolder+this.env.delimiter+basename;
 
-      this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.mailbox)+'&_folder_newname='+urlencode(newname), lock);
+      if (newname != this.env.mailbox) {
+        this.http_post('rename-folder', '_folder_oldname='+urlencode(this.env.mailbox)+'&_folder_newname='+urlencode(newname), this.set_busy(true, 'foldermoving'));
+        this.subscription_list.draglayer.hide();
+      }
     }
     this.drag_active = false;
     this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
@@ -4553,9 +4650,9 @@
     if (!this.gui_objects.subscriptionlist)
       return false;
 
-    var row, n, i, tmp, folders, len, list = [], slist = [],
+    var row, n, i, tmp, folders, rowid, list = [], slist = [],
       tbody = this.gui_objects.subscriptionlist.tBodies[0],
-      refrow = $('tr', tbody).get(0),
+      refrow = $('tr', tbody).get(1),
       id = 'rcmrow'+((new Date).getTime());
 
     if (!refrow) {
@@ -4582,7 +4679,10 @@
     this.env.subscriptionrows[id] = [name, display_name, 0];
 
     // sort folders, to find a place where to insert the row
-    folders = this.env.subscriptionrows;
+    folders = [];
+    $.each(this.env.subscriptionrows, function(k,v){ folders.push(v) });
+    folders.sort(function(a,b){ return a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0) });
+
     for (n in folders) {
       // protected folder
       if (folders[n][2]) {
@@ -4598,19 +4698,22 @@
         tmp = null;
       }
     }
-    list.sort();
-    // make sure protected folders (and their subs) are on top
-    list = slist.concat(list);
+
+    // check if subfolder of a protected folder
+    for (n=0; n<slist.length; n++) {
+      if (name.indexOf(slist[n]+this.env.delimiter) == 0)
+        rowid = this.get_folder_row_id(slist[n]);
+    }
 
     // find folder position after sorting
-    for (n=0, len=list.length; n<len; n++) {
-      if (list[n] == name)
-        break;
+    for (n=0; !rowid && n<list.length; n++) {
+      if (n && list[n] == name)
+        rowid = this.get_folder_row_id(list[n-1]);
     }
 
     // add row to the table
-    if (n && n < len)
-      $('#'+this.get_folder_row_id(list[n-1])).after(row);
+    if (rowid)
+      $('#'+rowid).after(row);
     else
       row.appendTo(tbody);
 
@@ -4772,10 +4875,7 @@
       this.show_contentframe(true);
     }
     else {
-      if (!this.env.frame_lock) {
-        (parent.rcmail ? parent.rcmail : this).env.frame_lock = this.set_busy(true, 'loading');
-      }
-      this.location_href(this.env.comm_path+url, target);
+      this.location_href(this.env.comm_path+url, target, true);
     }
   };
 
@@ -5058,7 +5158,8 @@
       obj.click(function() { return ref.hide_message(obj); });
     }
 
-    window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
+    if (timeout > 0)
+      window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
     return id;
   };
 
@@ -5187,14 +5288,14 @@
     this.env.status_col = null;
 
     if ((n = $.inArray('subject', this.env.coltypes)) >= 0) {
-      this.set_env('subject_col', n);
+      this.env.subject_col = n;
       if (list)
         list.subject_col = n;
     }
     if ((n = $.inArray('flag', this.env.coltypes)) >= 0)
-      this.set_env('flagged_col', n);
+      this.env.flagged_col = n;
     if ((n = $.inArray('status', this.env.coltypes)) >= 0)
-      this.set_env('status_col', n);
+      this.env.status_col = n;
 
     if (list)
       list.init_header();
@@ -5416,11 +5517,11 @@
       url = '?_task=utils&_action=html2text',
       lock = this.set_busy(true, 'converting');
 
-    console.log('HTTP POST: ' + url);
+    this.log('HTTP POST: ' + url);
 
     $.ajax({ type: 'POST', url: url, data: htmlText, contentType: 'application/octet-stream',
       error: function(o, status, err) { rcmail.http_error(o, status, err, lock); },
-      success: function(data) { rcmail.set_busy(false, null, lock); $(document.getElementById(id)).val(data); console.log(data); }
+      success: function(data) { rcmail.set_busy(false, null, lock); $(document.getElementById(id)).val(data); rcmail.log(data); }
     });
   };
 
@@ -5435,7 +5536,7 @@
   /********************************************************/
   /*********        remote request methods        *********/
   /********************************************************/
-  
+
   // compose a valid url with the given parameters
   this.url = function(action, query)
   {
@@ -5485,8 +5586,11 @@
     this.redirect(this.url(action, query));
   };
 
-  this.location_href = function(url, target)
+  this.location_href = function(url, target, frame)
   {
+    if (frame)
+      this.lock_frame();
+
     // simulate real link click to force IE to send referer header
     if (bw.ie && target == window)
       $('<a>').attr('href', url).appendTo(document.body).get(0).click();
@@ -5513,8 +5617,9 @@
     url += '&_remote=1';
 
     // send request
-    console.log('HTTP GET: ' + url);
-    $.ajax({
+    this.log('HTTP GET: ' + url);
+
+    return $.ajax({
       type: 'GET', url: url, data: { _unlock:(lock?lock:0) }, dataType: 'json',
       success: function(data){ ref.http_response(data); },
       error: function(o, status, err) { rcmail.http_error(o, status, err, lock); }
@@ -5544,12 +5649,22 @@
     }
 
     // send request
-    console.log('HTTP POST: ' + url);
-    $.ajax({
+    this.log('HTTP POST: ' + url);
+
+    return $.ajax({
       type: 'POST', url: url, data: postdata, dataType: 'json',
       success: function(data){ ref.http_response(data); },
       error: function(o, status, err) { rcmail.http_error(o, status, err, lock); }
     });
+  };
+
+  // aborts ajax request
+  this.abort_request = function(r)
+  {
+    if (r.request)
+      r.request.abort();
+    if (r.lock)
+      this.set_busy(false, null, r.lock);
   };
 
   // handle HTTP response
@@ -5577,7 +5692,7 @@
 
     // if we get javascript code from server -> execute it
     if (response.exec) {
-      console.log(response.exec);
+      this.log(response.exec);
       eval(response.exec);
     }
 
@@ -5638,6 +5753,7 @@
       case 'check-recent':
       case 'getunread':
       case 'search':
+        this.env.qsearch = null;
       case 'list':
         if (this.task == 'mail') {
           this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
@@ -5843,11 +5959,12 @@
 
       if (elm.type == 'hidden')
         continue;
-
       // remember which elem was disabled before lock
       if (lock && elm.disabled)
         this.disabled_form_elements.push(elm);
-      else if (lock || $.inArray(elm, this.disabled_form_elements)<0)
+      // check this.disabled_form_elements before inArray() as a workaround for FF5 bug
+      // http://bugs.jquery.com/ticket/9873
+      else if (lock || (this.disabled_form_elements && $.inArray(elm, this.disabled_form_elements)<0))
         elm.disabled = lock;
     }
   };

--
Gitblit v1.9.1