From ab0b51a1fef87bcc643c3aaf2e635c811b28ccd8 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Tue, 15 Feb 2011 06:10:59 -0500
Subject: [PATCH] - Use only one from IMAP authentication methods to prevent login delays (1487784)

---
 program/js/app.js |  521 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 389 insertions(+), 132 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 868919c..43be6ae 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -3,7 +3,7 @@
  | Roundcube Webmail Client Script                                       |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2010, Roundcube Dev, - Switzerland                 |
+ | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  +-----------------------------------------------------------------------+
@@ -16,7 +16,6 @@
 
   $Id$
 */
-
 
 function rcube_webmail()
 {
@@ -63,7 +62,7 @@
   // set environment variable(s)
   this.set_env = function(p, value)
   {
-    if (p != null && typeof(p) == 'object' && !value)
+    if (p != null && typeof p === 'object' && !value)
       for (var n in p)
         this.env[n] = p[n];
     else
@@ -319,7 +318,20 @@
 
         if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) {
           this.enable_command('save', true);
-          $("input[type='text']").first().select();
+          this.enable_command('upload-photo', this.env.coltypes.photo ? true : false);
+          this.enable_command('delete-photo', this.env.coltypes.photo && this.env.action == 'edit');
+
+          for (var col in this.env.coltypes)
+            this.init_edit_field(col, null);
+
+          $('.contactfieldgroup .row a.deletebutton').click(function(){ ref.delete_edit_field(this); return false });
+
+          $('select.addfieldmenu').change(function(e){
+            ref.insert_edit_field($(this).val(), $(this).attr('rel'), this);
+            this.selectedIndex = 0;
+          });
+
+          $("input[type='text']:visible").first().focus();
         }
         else if (this.gui_objects.qsearchbox) {
           this.enable_command('search', 'reset-search', 'moveto', true);
@@ -415,9 +427,9 @@
     // execute all foreign onload scripts
     // @deprecated
     for (var i in this.onloads) {
-      if (typeof(this.onloads[i]) == 'string')
+      if (typeof this.onloads[i] === 'string')
         eval(this.onloads[i]);
-      else if (typeof(this.onloads[i]) == 'function')
+      else if (typeof this.onloads[i] === 'function')
         this.onloads[i]();
       }
 
@@ -455,19 +467,19 @@
     }
 
     // process external commands
-    if (typeof this.command_handlers[command] == 'function') {
+    if (typeof this.command_handlers[command] === 'function') {
       var ret = this.command_handlers[command](props, obj);
-      return ret !== null ? ret : (obj ? false : true);
+      return ret !== undefined ? ret : (obj ? false : true);
     }
-    else if (typeof this.command_handlers[command] == 'string') {
+    else if (typeof this.command_handlers[command] === 'string') {
       var ret = window[this.command_handlers[command]](props, obj);
-      return ret !== null ? ret : (obj ? false : true);
+      return ret !== undefined ? ret : (obj ? false : true);
     }
 
     // trigger plugin hooks
     this.triggerEvent('actionbefore', {props:props, action:command});
     var event_ret = this.triggerEvent('before'+command, props);
-    if (typeof event_ret != 'undefined') {
+    if (event_ret !== undefined) {
       // abort if one the handlers returned false
       if (event_ret === false)
         return false;
@@ -634,11 +646,14 @@
               input_name.focus();
               break;
             }
-            else if (input_email.length && !rcube_check_email(input_email.val())) {
+            else if (this.task == 'settings' && input_email.length && !rcube_check_email(input_email.val())) {
               alert(this.get_label('noemailwarning'));
               input_email.focus();
               break;
             }
+
+            // clear empty input fields
+            $('input.placeholder').each(function(){ if (this.value == this._placeholder) this.value = ''; });
           }
 
           this.gui_objects.editform.submit();
@@ -996,31 +1011,29 @@
 
       case 'export':
         if (this.contact_list.rowcount > 0) {
-          var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : '');
-          if (this.env.search_request)
-            add_url += '_search='+this.env.search_request;
-
-          this.goto_url('export', add_url);
+          this.goto_url('export', { _source:this.env.source, _gid:this.env.group, _search:this.env.search_request });
         }
+        break;
+
+      case 'upload-photo':
+        this.upload_contact_photo(props);
+        break;
+
+      case 'delete-photo':
+        this.replace_contact_photo('-del-');
         break;
 
       // user settings commands
       case 'preferences':
-        this.goto_url('');
-        break;
-
       case 'identities':
-        this.goto_url('settings/identities');
-        break;
-
       case 'folders':
-        this.goto_url('settings/folders');
+        this.goto_url('settings/' + command);
         break;
 
       // unified command call (command name == function name)
       default:
         var func = command.replace(/-/g, '_');
-        if (this[func] && typeof this[func] == 'function')
+        if (this[func] && typeof this[func] === 'function')
           this[func](props);
         break;
     }
@@ -1158,7 +1171,7 @@
 
   this.is_framed = function()
   {
-    return (this.env.framed && parent.rcmail);
+    return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command);
   };
 
 
@@ -1396,7 +1409,7 @@
     // reset 'pressed' buttons
     if (this.buttons_sel) {
       for (var id in this.buttons_sel)
-        if (typeof id != 'function')
+        if (typeof id !== 'function')
           this.button_out(this.buttons_sel[id], id);
       this.buttons_sel = {};
     }
@@ -1682,7 +1695,7 @@
           message.expanded = true;
       }
       else if (message.has_children) {
-        if (typeof(message.expanded) == 'undefined' && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) {
+        if (message.expanded === undefined && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) {
           message.expanded = true;
         }
       }
@@ -1701,7 +1714,7 @@
       var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show';
       var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid';
       cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+uid+'"'+
-        ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>';
+        ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')">'+cols.subject+'</a>';
     }
 
     // add each submitted col
@@ -1770,7 +1783,7 @@
   {
     var update, add_url = '';
 
-    if (typeof sort_col == 'undefined')
+    if (sort_col === undefined)
       sort_col = this.env.sort_col;
     if (!sort_order)
       sort_order = this.env.sort_order;
@@ -2366,7 +2379,7 @@
   // copy selected messages to the specified mailbox
   this.copy_messages = function(mbox)
   {
-    if (mbox && typeof mbox == 'object')
+    if (mbox && typeof mbox === 'object')
       mbox = mbox.id;
 
     // exit if current or no mailbox specified or if selection is empty
@@ -2395,7 +2408,7 @@
   // move selected messages to the specified mailbox
   this.move_messages = function(mbox)
   {
-    if (mbox && typeof mbox == 'object')
+    if (mbox && typeof mbox === 'object')
       mbox = mbox.id;
 
     // exit if current or no mailbox specified or if selection is empty
@@ -2886,7 +2899,7 @@
 
     // check if all files has been uploaded
     for (var key in this.env.attachments) {
-      if (typeof this.env.attachments[key] == 'object' && !this.env.attachments[key].complete) {
+      if (typeof this.env.attachments[key] === 'object' && !this.env.attachments[key].complete) {
         alert(this.get_label('notuploadedwarning'));
         return false;
       }
@@ -3177,27 +3190,7 @@
 
     // create hidden iframe and post upload form
     if (send) {
-      var ts = new Date().getTime();
-      var frame_name = 'rcmupload'+ts;
-
-      // have to do it this way for IE
-      // otherwise the form will be posted to a new window
-      if (document.all) {
-        var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
-        document.body.insertAdjacentHTML('BeforeEnd',html);
-      }
-      else { // for standards-compilant browsers
-        var frame = document.createElement('iframe');
-        frame.name = frame_name;
-        frame.style.border = 'none';
-        frame.style.width = 0;
-        frame.style.height = 0;
-        frame.style.visibility = 'hidden';
-        document.body.appendChild(frame);
-      }
-
-      // handle upload errors, parsing iframe content in onload
-      $(frame_name).bind('load', {ts:ts}, function(e) {
+      this.async_upload_form(form, 'upload', function(e) {
         var d, content = '';
         try {
           if (this.contentDocument) {
@@ -3217,11 +3210,6 @@
         if (bw.opera)
           rcmail.env.uploadframe = e.data.ts;
       });
-
-      form.target = frame_name;
-      form.action = this.env.comm_path+'&_action=upload&_uploadid='+ts;
-      form.setAttribute('enctype', 'multipart/form-data');
-      form.submit();
 
       // display upload indicator and cancel button
       var content = this.get_label('uploading');
@@ -3449,13 +3437,13 @@
       end = inp_value.substring(p+this.ksearch_value.length, inp_value.length);
 
     // insert all members of a group
-    if (typeof this.env.contacts[id] == 'object' && this.env.contacts[id].id) {
+    if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) {
       insert += this.env.contacts[id].name + ', ';
       this.group2expand = $.extend({}, this.env.contacts[id]);
       this.group2expand.input = this.ksearch_input;
       this.http_request('group-expand', '_source='+urlencode(this.env.contacts[id].source)+'&_gid='+urlencode(this.env.contacts[id].id), false);
     }
-    else if (typeof this.env.contacts[id] == 'string')
+    else if (typeof this.env.contacts[id] === 'string')
       insert = this.env.contacts[id] + ', ';
 
     this.ksearch_input.value = pre + insert + end;
@@ -3554,7 +3542,7 @@
 
       // 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];
+        text = typeof a_results[i] === 'object' ? a_results[i].name : a_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); };
@@ -3777,6 +3765,9 @@
         this.show_contentframe(false);
     }
 
+    if (this.env.group)
+      qs += '&_gid='+urlencode(this.env.group);
+
     // also send search request to get the right records from the next page
     if (this.env.search_request) 
       qs += '&_search='+this.env.search_request;
@@ -3790,6 +3781,9 @@
   // update a contact record in the list
   this.update_contact_row = function(cid, cols_arr, newcid)
   {
+    cid = String(cid).replace(this.identifier_expr, '_');
+    newcid = String(newcid).replace(this.identifier_expr, '_');
+
     var row;
     if (this.contact_list.rows[cid] && (row = this.contact_list.rows[cid].obj)) {
       for (var c=0; c<cols_arr.length; c++)
@@ -3822,7 +3816,7 @@
       even = rowcount%2,
       row = document.createElement('tr');
 
-    row.id = 'rcmrow'+cid;
+    row.id = 'rcmrow'+String(cid).replace(this.identifier_expr, '_');
     row.className = 'contact '+(even ? 'even' : 'odd');
 
     if (this.contact_list.in_selection(cid))
@@ -3956,7 +3950,7 @@
     var link = $('<a>').attr('href', '#')
       .bind('click', function() { return rcmail.command('listgroup', prop, this);})
       .html(prop.name);
-    var li = $('<li>').attr('id', 'rcmli'+key)
+    var li = $('<li>').attr('id', 'rcmli'+key.replace(this.identifier_expr, '_'))
       .addClass('contactgroup')
       .append(link)
       .insertAfter(this.get_folder_li(prop.source));
@@ -3976,6 +3970,165 @@
 
     this.env.contactfolders[key].name = this.env.contactgroups[key].name = prop.name;
     this.triggerEvent('group_update', { id:prop.id, source:prop.source, name:prop.name, li:li[0] });
+  };
+
+
+  this.init_edit_field = function(col, elem)
+  {
+    if (!elem)
+      elem = $('.ff_' + col);
+    
+    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); });
+  };
+
+  this.insert_edit_field = function(col, section, menu)
+  {
+    // just make pre-defined input field visible
+    var elem = $('#ff_'+col);
+    if (elem.length) {
+      elem.show().focus();
+      $(menu).children('option[value="'+col+'"]').attr('disabled', true);
+    }
+    else {
+      var lastelem = $('.ff_'+col),
+        appendcontainer = $('#contactsection'+section+' .contactcontroller'+col);
+      
+      if (!appendcontainer.length)
+        appendcontainer = $('<fieldset>').addClass('contactfieldgroup contactcontroller'+col).insertAfter($('#contactsection'+section+' .contactfieldgroup').last());
+
+      if (appendcontainer.length && appendcontainer.get(0).nodeName == 'FIELDSET') {
+        var input, colprop = this.env.coltypes[col],
+          row = $('<div>').addClass('row'),
+          cell = $('<div>').addClass('contactfieldcontent data'),
+          label = $('<div>').addClass('contactfieldlabel label');
+          
+        if (colprop.subtypes_select)
+          label.html(colprop.subtypes_select);
+        else
+          label.html(colprop.label);
+
+        var name_suffix = colprop.limit != 1 ? '[]' : '';
+        if (colprop.type == 'text' || colprop.type == 'date') {
+          input = $('<input>')
+            .addClass('ff_'+col)
+            .attr('type', 'text')
+            .attr('name', '_'+col+name_suffix)
+            .attr('size', colprop.size)
+            .appendTo(cell);
+
+          this.init_edit_field(col, input);
+        }
+        else if (colprop.type == 'composite') {
+          var childcol, cp, first;
+          for (var childcol in colprop.childs) {
+            cp = colprop.childs[childcol];
+            input = $('<input>')
+              .addClass('ff_'+childcol)
+              .attr('type', 'text')
+              .attr('name', '_'+childcol+name_suffix)
+              .attr('size', cp.size)
+              .appendTo(cell);
+            cell.append(" ");
+            this.init_edit_field(childcol, input);
+            if (!first) first = input;
+          }
+          input = first;  // set focus to the first of this composite fields
+        }
+        else if (colprop.type == 'select') {
+          input = $('<select>')
+            .addClass('ff_'+col)
+            .attr('name', '_'+col+name_suffix)
+            .appendTo(cell);
+          
+          var options = input.attr('options');
+          options[options.length] = new Option('---', '');
+          if (colprop.options)
+            $.each(colprop.options, function(i, val){ options[options.length] = new Option(val, i); });
+        }
+
+        if (input) {
+          var delbutton = $('<a href="#del"></a>')
+            .addClass('contactfieldbutton deletebutton')
+            .attr('title', this.get_label('delete'))
+            .attr('rel', col)
+            .html(this.env.delbutton)
+            .click(function(){ ref.delete_edit_field(this); return false })
+            .appendTo(cell);
+          
+          row.append(label).append(cell).appendTo(appendcontainer.show());
+          input.first().focus();
+          
+          // disable option if limit reached
+          if (!colprop.count) colprop.count = 0;
+          if (++colprop.count == colprop.limit && colprop.limit)
+            $(menu).children('option[value="'+col+'"]').attr('disabled', true);
+        }
+      }
+    }
+  };
+
+  this.delete_edit_field = function(elem)
+  {
+    var col = $(elem).attr('rel'),
+      colprop = this.env.coltypes[col],
+      fieldset = $(elem).parents('fieldset.contactfieldgroup'),
+      addmenu = fieldset.parent().find('select.addfieldmenu');
+    
+    // just clear input but don't hide the last field
+    if (--colprop.count <= 0 && colprop.visible)
+      $(elem).parent().children('input').val('').blur();
+    else {
+      $(elem).parents('div.row').remove();
+      // hide entire fieldset if no more rows
+      if (!fieldset.children('div.row').length)
+        fieldset.hide();
+    }
+    
+    // enable option in add-field selector or insert it if necessary
+    if (addmenu.length) {
+      var option = addmenu.children('option[value="'+col+'"]');
+      if (option.length)
+        option.attr('disabled', false);
+      else
+        option = $('<option>').attr('value', col).html(colprop.label).appendTo(addmenu);
+      addmenu.show();
+    }
+  };
+
+
+  this.upload_contact_photo = function(form)
+  {
+    if (form && form.elements._photo.value) {
+      this.async_upload_form(form, 'upload-photo', function(e) {
+        rcmail.set_busy(false, null, rcmail.photo_upload_id);
+      });
+
+      // display upload indicator
+      this.photo_upload_id = this.set_busy(true, 'uploading');
+    }
+  };
+  
+  this.replace_contact_photo = function(id)
+  {
+    $('#ff_photo').val(id);
+    
+    var buttons = this.buttons['upload-photo'];
+    for (var n=0; n < buttons.length; n++)
+      $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto'));
+    
+    var img_src = id == '-del-' ? this.env.photo_placeholder :
+      this.env.comm_path + '&_action=photo&_source=' + this.env.source + '&_cid=' + this.env.cid + '&_photo=' + id;
+    $(this.gui_objects.contactphoto).children('img').attr('src', img_src);
+    
+    this.enable_command('delete-photo', id != '-del-');
+  };
+  
+  this.photo_upload_end = function()
+  {
+    this.set_busy(false, null, this.photo_upload_id);
+    delete this.photo_upload_id;
   };
 
 
@@ -4309,6 +4462,14 @@
     }
   };
 
+  // disables subscription checkbox (for protected folder)
+  this.disable_subscription = function(folder)
+  {
+    var id = this.get_folder_row_id(folder);
+    if (id)
+      $('input[name="_subscribed[]"]', $('#'+id)).attr('disabled', true);
+  };
+
   this.folder_size = function(folder)
   {
     var lock = this.set_busy(true, 'loading');
@@ -4336,7 +4497,7 @@
   this.init_buttons = function()
   {
     for (var cmd in this.buttons) {
-      if (typeof cmd != 'string')
+      if (typeof cmd !== 'string')
         continue;
 
       for (var i=0; i< this.buttons[cmd].length; i++) {
@@ -4397,7 +4558,7 @@
         obj.src = button[state];
       }
       // set class name according to button state
-      else if (obj && typeof(button[state])!='undefined') {
+      else if (obj && button[state] !== undefined) {
         button.status = state;
         obj.className = button[state];
       }
@@ -4497,6 +4658,23 @@
     }
   };
 
+
+  this.focus_textfield = function(elem)
+  {
+    elem._hasfocus = true;
+    var $elem = $(elem);
+    if ($elem.hasClass('placeholder') || $elem.val() == elem._placeholder)
+      $elem.val('').removeClass('placeholder').attr('spellcheck', true);
+  };
+
+  this.blur_textfield = function(elem)
+  {
+    elem._hasfocus = false;
+    var $elem = $(elem);
+    if (elem._placeholder && (!$elem.val() || $elem.val() == elem._placeholder))
+      $elem.addClass('placeholder').attr('spellcheck', false).val(elem._placeholder);
+  };
+  
   // write to the document/window title
   this.set_pagetitle = function(title)
   {
@@ -4521,35 +4699,48 @@
     type = type ? type : 'notice';
 
     var ref = this,
+      key = msg,
       date = new Date(),
       id = type + date.getTime(),
-      timeout = type == 'loading' ? this.env.request_timeout * 1000 : (this.message_time * (type == 'error' || type == 'warning' ? 2 : 1));
-
-    if (type == 'loading' && !msg)
-      msg = this.get_label('loading');
+      timeout = this.message_time * (type == 'error' || type == 'warning' ? 2 : 1);
+      
+    if (type == 'loading') {
+      key = 'loading';
+      timeout = this.env.request_timeout * 1000;
+      if (!msg)
+        msg = this.get_label('loading');
+    }
 
     // The same message is already displayed
-    if (this.messages[msg]) {
-      this.messages[msg].elements.push(id);
-      window.setTimeout(function() { ref.hide_message(id, true); }, timeout);
+    if (this.messages[key]) {
+      // replace label
+      if (this.messages[key].obj)
+        this.messages[key].obj.html(msg);
+      // store label in stack
+      if (type == 'loading') {
+        this.messages[key].labels.push({'id': id, 'msg': msg});
+      }
+      // add element and set timeout
+      this.messages[key].elements.push(id);
+      window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
       return id;
     }
 
-    var obj = $('<div>').addClass(type).html(msg).data('msg', msg),
-      cont = $(this.gui_objects.message).show();
+    // create DOM object and display it
+    var obj = $('<div>').addClass(type).html(msg).data('key', key),
+      cont = $(this.gui_objects.message).append(obj).show();
+
+    this.messages[key] = {'obj': obj, 'elements': [id]};
 
     if (type == 'loading') {
-      obj.appendTo(cont);
-      this.messages[msg] = {'obj': obj, 'elements': [id]};
-      window.setTimeout(function() { rcmail.hide_message(id); }, timeout);
-      return id;
+      this.messages[key].labels = [{'id': id, 'msg': msg}];
     }
     else {
-      obj.appendTo(cont).bind('mousedown', function() { return ref.hide_message(obj, true); });
-      window.setTimeout(function() { ref.hide_message(id, true); }, timeout);
-      this.messages[msg] = { 'obj': obj, 'elements': [id] };
-      return id;
+      obj.click(function() { return ref.hide_message(obj); });
     }
+
+    window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
+    return id;
   };
 
   // make a message to disapear
@@ -4559,25 +4750,37 @@
     if (this.is_framed())
       return parent.rcmail.hide_message(obj, fade);
 
-    if (typeof(obj) == 'object') {
-      // hide message object
+    var k, n, i, msg, m = this.messages;
+
+    // Hide message by object, don't use for 'loading'!
+    if (typeof obj === 'object') {
       $(obj)[fade?'fadeOut':'hide']();
-      
-      var msg = $(obj).data('msg');
+      msg = $(obj).data('key');
       if (this.messages[msg])
         delete this.messages[msg];
     }
+    // Hide message by id
     else {
-      // hide message by id
-      var k, n, m = this.messages;
       for (k in m) {
         for (n in m[k].elements) {
           if (m[k] && m[k].elements[n] == obj) {
             m[k].elements.splice(n, 1);
-            // hide dom element if last instance is removed
+            // hide DOM element if last instance is removed
             if (!m[k].elements.length) {
               m[k].obj[fade?'fadeOut':'hide']();
               delete m[k];
+            }
+            // set pending action label for 'loading' message
+            else if (k == 'loading') {
+              for (i in m[k].labels) {
+                if (m[k].labels[i].id == obj) {
+                  delete m[k].labels[i];
+                }
+                else {
+                  msg = m[k].labels[i].msg;
+                }
+                m[k].obj.html(msg);
+              }
             }
           }
         }
@@ -4641,6 +4844,7 @@
         }
         th.appendChild(tr);
         thead.parentNode.replaceChild(th, thead);
+        thead = th;
       }
 
       for (n=0, len=this.env.coltypes.length; n<len; n++) {
@@ -4695,7 +4899,7 @@
   this.set_quota = function(content)
   {
     if (content && this.gui_objects.quotadisplay) {
-      if (typeof(content) == 'object' && content.type == 'image')
+      if (typeof content === 'object' && content.type == 'image')
         this.percent_indicator(this.gui_objects.quotadisplay, content);
       else
         $(this.gui_objects.quotadisplay).html(content);
@@ -4715,11 +4919,14 @@
   // update the mailbox count display
   this.set_unread_count_display = function(mbox, set_title)
   {
-    var reg, text_obj, item, mycount, childcount, div;
+    var reg, link, text_obj, item, mycount, childcount, div;
 
     if (item = this.get_folder_li(mbox)) {
       mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0;
-      text_obj = item.getElementsByTagName('a')[0];
+      link = $(item).children('a').eq(0);
+      text_obj = link.children('span.unreadcount');
+      if (!text_obj.length && mycount)
+        text_obj = $('<span>').addClass('unreadcount').appendTo(link);
       reg = /\s+\([0-9]+\)$/i;
 
       childcount = 0;
@@ -4731,12 +4938,10 @@
             childcount += this.env.unread_counts[k];
       }
 
-      if (mycount && text_obj.innerHTML.match(reg))
-        text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+mycount+')');
-      else if (mycount)
-        text_obj.innerHTML += ' ('+mycount+')';
-      else
-        text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
+      if (mycount && text_obj.length)
+        text_obj.html(' ('+mycount+')');
+      else if (text_obj.length)
+        text_obj.remove();
 
       // set parent's display
       reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+$');
@@ -4918,6 +5123,39 @@
   /********************************************************/
   /*********        remote request methods        *********/
   /********************************************************/
+  
+  // compose a valid url with the given parameters
+  this.url = function(action, query)
+  {
+    var querystring = typeof query === 'string' ? '&' + query : '';
+
+    if (typeof action !== 'string')
+      query = action;
+    else if (!query || typeof query !== 'object')
+      query = {};
+
+    if (action)
+      query._action = action;
+    else
+      query._action = this.env.action;
+
+    var base = this.env.comm_path;
+
+    // overwrite task name
+    if (query._action.match(/([a-z]+)\/([a-z-_]+)/)) {
+      query._action = RegExp.$2;
+      base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
+    }
+
+    // remove undefined values
+    var param = {};
+    for (var k in query) {
+      if (query[k] !== undefined && query[k] !== null)
+        param[k] = query[k];
+    }
+
+    return base + '&' + $.param(param) + querystring;
+  };
 
   this.redirect = function(url, lock)
   {
@@ -4932,33 +5170,18 @@
 
   this.goto_url = function(action, query, lock)
   {
-    var url = this.env.comm_path,
-     querystring = query ? '&'+query : '';
-
-    // overwrite task name
-    if (action.match(/([a-z]+)\/([a-z-_]+)/)) {
-      action = RegExp.$2;
-      url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
-    }
-
-    this.redirect(url+'&_action='+action+querystring, lock);
+    this.redirect(this.url(action, query));
   };
 
   // send a http request to the server
   this.http_request = function(action, query, lock)
   {
-    var url = this.env.comm_path;
-
-    // overwrite task name
-    if (action.match(/([a-z]+)\/([a-z-_]+)/)) {
-      action = RegExp.$2;
-      url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
-    }
+    var url = this.url(action, query);
 
     // trigger plugin hook
     var result = this.triggerEvent('request'+action, query);
 
-    if (typeof result != 'undefined') {
+    if (result !== undefined) {
       // abort if one the handlers returned false
       if (result === false)
         return false;
@@ -4966,7 +5189,7 @@
         query = result;
     }
 
-    url += '&_remote=1&_action=' + action + (query ? '&' : '') + query;
+    url += '&_remote=1';
 
     // send request
     console.log('HTTP GET: ' + url);
@@ -4980,17 +5203,9 @@
   // send a http POST request to the server
   this.http_post = function(action, postdata, lock)
   {
-    var url = this.env.comm_path;
+    var url = this.url(action);
 
-    // overwrite task name
-    if (action.match(/([a-z]+)\/([a-z-_]+)/)) {
-      action = RegExp.$2;
-      url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
-    }
-
-    url += '&_action=' + action;
-
-    if (postdata && typeof(postdata) == 'object') {
+    if (postdata && typeof postdata === 'object') {
       postdata._remote = 1;
       postdata._unlock = (lock ? lock : 0);
     }
@@ -4999,7 +5214,7 @@
 
     // trigger plugin hook
     var result = this.triggerEvent('request'+action, postdata);
-    if (typeof result != 'undefined') {
+    if (result !== undefined) {
       // abort if one the handlers returned false
       if (result === false)
         return false;
@@ -5033,9 +5248,9 @@
       this.set_env(response.env);
 
     // we have labels to add
-    if (typeof response.texts == 'object') {
+    if (typeof response.texts === 'object') {
       for (var name in response.texts)
-        if (typeof response.texts[name] == 'string')
+        if (typeof response.texts[name] === 'string')
           this.add_label(name, response.texts[name]);
     }
 
@@ -5135,6 +5350,37 @@
       this.display_message(this.get_label('servererror') + ' (' + errmsg + ')', 'error');
   };
 
+  // post the given form to a hidden iframe
+  this.async_upload_form = function(form, action, onload)
+  {
+    var ts = new Date().getTime();
+    var frame_name = 'rcmupload'+ts;
+
+    // have to do it this way for IE
+    // otherwise the form will be posted to a new window
+    if (document.all) {
+      var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
+      document.body.insertAdjacentHTML('BeforeEnd', html);
+    }
+    else { // for standards-compilant browsers
+      var frame = document.createElement('iframe');
+      frame.name = frame_name;
+      frame.style.border = 'none';
+      frame.style.width = 0;
+      frame.style.height = 0;
+      frame.style.visibility = 'hidden';
+      document.body.appendChild(frame);
+    }
+
+    // handle upload errors, parsing iframe content in onload
+    $(frame_name).bind('load', {ts:ts}, onload);
+
+    form.target = frame_name;
+    form.action = this.url(action, { _uploadid:ts });
+    form.setAttribute('enctype', 'multipart/form-data');
+    form.submit();
+  };
+  
   // starts interval for keep-alive/check-recent signal
   this.start_keepalive = function()
   {
@@ -5200,7 +5446,7 @@
   // gets cursor position
   this.get_caret_pos = function(obj)
   {
-    if (typeof(obj.selectionEnd)!='undefined')
+    if (obj.selectionEnd !== undefined)
       return obj.selectionEnd;
     else if (document.selection && document.selection.createRange) {
       var range = document.selection.createRange();
@@ -5263,6 +5509,17 @@
 
 }  // end object rcube_webmail
 
+
+// some static methods
+rcube_webmail.long_subject_title = function(elem, indent)
+{
+  if (!elem.title) {
+    var $elem = $(elem);
+    if ($elem.width() + indent * 15 > $elem.parent().width())
+      elem.title = $elem.html();
+  }
+};
+
 // copy event engine prototype
 rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
 rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;

--
Gitblit v1.9.1