From 8eefbb2158c43b51a8c33e6c480cbe61539b9535 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 27 Aug 2012 04:16:04 -0400
Subject: [PATCH] Add option to enable HTML editor on forwarding (#1488517)

---
 program/js/app.js |  243 +++++++++++++++++++++++-------------------------
 1 files changed, 118 insertions(+), 125 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index cc4b8c2..838f240 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -48,7 +48,7 @@
   this.env.request_timeout = 180;  // seconds
   this.env.draft_autosave = 0;     // seconds
   this.env.comm_path = './';
-  this.env.blankpage = 'program/blank.gif';
+  this.env.blankpage = 'program/resources/blank.gif';
 
   // set jQuery ajax options
   $.ajaxSetup({
@@ -57,6 +57,8 @@
     error: function(request, status, err){ ref.http_error(request, status, err); },
     beforeSend: function(xmlhttp){ xmlhttp.setRequestHeader('X-Roundcube-Request', ref.env.request_token); }
   });
+
+  $(window).bind('beforeunload', function() { rcmail.unload = true; });
 
   // set environment variable(s)
   this.set_env = function(p, value)
@@ -459,7 +461,7 @@
       this.gui_containers.foldertray = $(this.gui_objects.folderlist);
 
     // activate html5 file drop feature (if browser supports it and if configured)
-    if (this.gui_objects.filedrop && this.env.filedrop && ((XMLHttpRequest && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) {
+    if (this.gui_objects.filedrop && this.env.filedrop && ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData)) {
       $(document.body).bind('dragover dragleave drop', function(e){ return ref.document_drag_hover(e, e.type == 'dragover'); });
       $(this.gui_objects.filedrop).addClass('droptarget')
         .bind('dragover dragleave', function(e){ return ref.file_drag_hover(e, e.type == 'dragover'); })
@@ -493,7 +495,7 @@
   /*********************************************************/
 
   // execute a specific command on the web client
-  this.command = function(command, props, obj)
+  this.command = function(command, props, obj, event)
   {
     var ret, uid, cid, url, flag;
 
@@ -713,7 +715,7 @@
       case 'delete':
         // mail task
         if (this.task == 'mail')
-          this.delete_messages();
+          this.delete_messages(event);
         // addressbook task
         else if (this.task == 'addressbook')
           this.delete_contacts();
@@ -920,14 +922,8 @@
         break;
 
       case 'savedraft':
-        var form = this.gui_objects.messageform, msgid;
-
         // Reset the auto-save timer
         clearTimeout(this.save_timer);
-
-        // saving Drafts is disabled
-        if (!form)
-          break;
 
         // compose form did not change
         if (this.cmp_hash == this.compose_field_hash()) {
@@ -938,35 +934,17 @@
         // re-set keep-alive timeout
         this.start_keepalive();
 
-        msgid = this.set_busy(true, 'savingmessage');
-
-        form.target = "savetarget";
-        form._draft.value = '1';
-        form.action = this.add_url(form.action, '_unlock', msgid);
-        form.submit();
+        this.submit_messageform(true);
         break;
 
       case 'send':
-        if (!this.gui_objects.messageform)
-          break;
-
         if (!props.nocheck && !this.check_compose_input(command))
           break;
 
         // Reset the auto-save timer
         clearTimeout(this.save_timer);
 
-        // all checks passed, send message
-        var lang = this.spellcheck_lang(),
-          form = this.gui_objects.messageform,
-          msgid = this.set_busy(true, 'sendingmessage');
-
-        form.target = 'savetarget';
-        form._draft.value = '';
-        form.action = this.add_url(form.action, '_unlock', msgid);
-        form.action = this.add_url(form.action, '_lang', lang);
-        form.submit();
-
+        this.submit_messageform();
         break;
 
       case 'send-attachment':
@@ -998,7 +976,7 @@
             // do reply-list, when list is detected and popup menu wasn't used 
             url._all = (!props && this.commands['reply-list'] ? 'list' : 'all');
           else if (command == 'reply-list')
-            url._all = list;
+            url._all = 'list';
 
           this.goto_url('compose', url, true);
         }
@@ -1469,29 +1447,21 @@
 
   this.doc_mouse_up = function(e)
   {
-    var model, list, li, id;
+    var model, list, 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();
-      else
-        list.focus();
+    if (list = this.message_list)
       model = this.env.mailboxes;
-    }
-    else if (list = this.contact_list) {
-      if (!rcube_mouse_is_over(e, list.list.parentNode))
-        list.blur();
-      else
-        list.focus();
+    else if (list = this.contact_list)
       model = this.env.contactfolders;
-    }
-    else if (this.ksearch_value) {
+    else if (this.ksearch_value)
       this.ksearch_blur();
-    }
+
+    if (list && !rcube_mouse_is_over(e, list.list.parentNode))
+      list.blur();
 
     // handle mouse release when dragging
     if (this.drag_active && model && this.env.last_folder_target) {
@@ -1634,7 +1604,7 @@
     for (i=0; i<cols.length; i++)
       if (cols[i].id && cols[i].id.match(/^rcm/)) {
         name = cols[i].id.replace(/^rcm/, '');
-        this.env.coltypes.push(name == 'to' ? 'from' : name);
+        this.env.coltypes.push(name);
       }
 
     if ((found = $.inArray('flag', this.env.coltypes)) >= 0)
@@ -1827,7 +1797,7 @@
         html = '<span id="flagicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
       }
       else if (c == 'attachment') {
-        if (/application\/|multipart\/m/.test(flags.ctype))
+        if (/application\/|multipart\/(m|signed)/.test(flags.ctype))
           html = '<span class="attachment">&nbsp;</span>';
         else if (/multipart\/report/.test(flags.ctype))
           html = '<span class="report">&nbsp;</span>';
@@ -1913,7 +1883,7 @@
       // make sure new columns are added at the end of the list
       var i, idx, name, newcols = [], oldcols = this.env.coltypes;
       for (i=0; i<oldcols.length; i++) {
-        name = oldcols[i] == 'to' ? 'from' : oldcols[i];
+        name = oldcols[i];
         idx = $.inArray(name, cols);
         if (idx != -1) {
           newcols.push(name);
@@ -2555,7 +2525,7 @@
   };
 
   // delete selected messages from the current mailbox
-  this.delete_messages = function()
+  this.delete_messages = function(event)
   {
     var uid, i, len, trash = this.env.trash_mailbox,
       list = this.message_list,
@@ -2587,7 +2557,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.modkey == SHIFT_KEY) {
+      if ((list && list.modkey == SHIFT_KEY) || (event && rcube_event.get_modifier(event) == SHIFT_KEY)) {
         if (confirm(this.get_label('deletemessagesconfirm')))
           this.permanently_remove_messages();
       }
@@ -2674,34 +2644,37 @@
   // set a specific flag to one or more messages
   this.mark_message = function(flag, uid)
   {
-    var a_uids = [], r_uids = [], len, n, id,
-      selection = this.message_list ? this.message_list.get_selection() : [];
+    var a_uids = [], r_uids = [], len, n, id, selection,
+      list = this.message_list;
 
     if (uid)
       a_uids[0] = uid;
     else if (this.env.uid)
       a_uids[0] = this.env.uid;
-    else if (this.message_list) {
+    else if (list) {
+      selection = list.get_selection();
       for (n=0, len=selection.length; n<len; n++) {
           a_uids.push(selection[n]);
       }
     }
 
-    if (!this.message_list)
+    if (!list)
       r_uids = a_uids;
-    else
+    else {
+      list.focus();
       for (n=0, len=a_uids.length; n<len; n++) {
         id = a_uids[n];
-        if ((flag=='read' && this.message_list.rows[id].unread) 
-            || (flag=='unread' && !this.message_list.rows[id].unread)
-            || (flag=='delete' && !this.message_list.rows[id].deleted)
-            || (flag=='undelete' && this.message_list.rows[id].deleted)
-            || (flag=='flagged' && !this.message_list.rows[id].flagged)
-            || (flag=='unflagged' && this.message_list.rows[id].flagged))
+        if ((flag=='read' && list.rows[id].unread) 
+            || (flag=='unread' && !list.rows[id].unread)
+            || (flag=='delete' && !list.rows[id].deleted)
+            || (flag=='undelete' && list.rows[id].deleted)
+            || (flag=='flagged' && !list.rows[id].flagged)
+            || (flag=='unflagged' && list.rows[id].flagged))
         {
           r_uids.push(id);
         }
       }
+    }
 
     // nothing to do
     if (!r_uids.length && !this.select_all_mode)
@@ -3031,6 +3004,29 @@
       .attr('autocomplete', 'off');
   };
 
+  this.submit_messageform = function(draft)
+  {
+    var form = this.gui_objects.messageform;
+
+    if (!form)
+      return;
+
+    // all checks passed, send message
+    var msgid = this.set_busy(true, draft ? 'savingmessage' : 'sendingmessage'),
+      lang = this.spellcheck_lang(),
+      files = [];
+
+    // send files list
+    $('li', this.gui_objects.attachmentlist).each(function() { files.push(this.id.replace(/^rcmfile/, '')); });
+    $('input[name="_attachments"]', form).val(files.join());
+
+    form.target = 'savetarget';
+    form._draft.value = draft ? '1' : '';
+    form.action = this.add_url(form.action, '_unlock', msgid);
+    form.action = this.add_url(form.action, '_lang', lang);
+    form.submit();
+  };
+
   this.compose_recipient_select = function(list)
   {
     this.enable_command('add-recipient', list.selection.length > 0);
@@ -3297,8 +3293,7 @@
       input_message = $("[name='_message']"),
       message = input_message.val(),
       is_html = ($("input[name='_is_html']").val() == '1'),
-      sig = this.env.identity,
-      sig_separator = this.env.sig_above && (this.env.compose_mode == 'reply' || this.env.compose_mode == 'forward') ? '---' : '-- ';
+      sig = this.env.identity;
 
     // enable manual signature insert
     if (this.env.signatures && this.env.signatures[id]) {
@@ -3311,12 +3306,8 @@
     if (!is_html) {
       // remove the 'old' signature
       if (show_sig && sig && this.env.signatures && this.env.signatures[sig]) {
-
-        sig = this.env.signatures[sig].is_html ? this.env.signatures[sig].plain_text : this.env.signatures[sig].text;
+        sig = this.env.signatures[sig].text;
         sig = sig.replace(/\r\n/g, '\n');
-
-        if (!sig.match(/^--[ -]\n/m))
-          sig = sig_separator + '\n' + sig;
 
         p = this.env.sig_above ? message.indexOf(sig) : message.lastIndexOf(sig);
         if (p >= 0)
@@ -3324,11 +3315,8 @@
       }
       // add the new signature string
       if (show_sig && this.env.signatures && this.env.signatures[id]) {
-        sig = this.env.signatures[id]['is_html'] ? this.env.signatures[id]['plain_text'] : this.env.signatures[id]['text'];
+        sig = this.env.signatures[id].text;
         sig = sig.replace(/\r\n/g, '\n');
-
-        if (!sig.match(/^--[ -]\n/m))
-          sig = sig_separator + '\n' + sig;
 
         if (this.env.sig_above) {
           if (p >= 0) { // in place of removed signature
@@ -3393,21 +3381,8 @@
         }
       }
 
-      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/m))
-            sig = sig_separator + '<br />' + sig;
-        }
-        else {
-          sig = this.env.signatures[id].text;
-          if (!sig.match(/^--[ -]\r?\n/m))
-            sig = sig_separator + '\n' + sig;
-          sig = '<pre>' + sig + '</pre>';
-        }
-
-        sigElem.innerHTML = sig;
-      }
+      if (this.env.signatures[id])
+        sigElem.innerHTML = this.env.signatures[id].html;
     }
 
     this.env.identity = id;
@@ -4030,8 +4005,7 @@
 
     // if a group is currently selected, and there is at least one contact selected
     // thend we can enable the group-remove-selected command
-    this.enable_command('group-remove-selected', typeof this.env.group != 'undefined' && list.selection.length > 0);
-
+    this.enable_command('group-remove-selected', this.env.group && 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);
@@ -4275,7 +4249,7 @@
     if (!this.gui_objects.contactslist)
       return false;
 
-    var c, list = this.contact_list,
+    var c, col, list = this.contact_list,
       row = document.createElement('tr');
 
     row.id = 'rcmrow'+this.html_identifier(cid);
@@ -4715,11 +4689,11 @@
   {
     if (form && form.elements._photo.value) {
       this.async_upload_form(form, 'upload-photo', function(e) {
-        rcmail.set_busy(false, null, rcmail.photo_upload_id);
+        rcmail.set_busy(false, null, rcmail.file_upload_id);
       });
 
       // display upload indicator
-      this.photo_upload_id = this.set_busy(true, 'uploading');
+      this.file_upload_id = this.set_busy(true, 'uploading');
     }
   };
 
@@ -4734,8 +4708,8 @@
 
   this.photo_upload_end = function()
   {
-    this.set_busy(false, null, this.photo_upload_id);
-    delete this.photo_upload_id;
+    this.set_busy(false, null, this.file_upload_id);
+    delete this.file_upload_id;
   };
 
   this.set_photo_actions = function(id)
@@ -5358,13 +5332,6 @@
     }
   };
 
-  // enable/disable buttons for page shifting
-  this.set_page_buttons = function()
-  {
-    this.enable_command('nextpage', 'lastpage', (this.env.pagecount > this.env.current_page));
-    this.enable_command('previouspage', 'firstpage', (this.env.current_page > 1));
-  };
-
   // set event handlers on registered buttons
   this.init_buttons = function()
   {
@@ -5372,7 +5339,7 @@
       if (typeof cmd !== 'string')
         continue;
 
-      for (var i=0; i< this.buttons[cmd].length; i++) {
+      for (var i=0; i<this.buttons[cmd].length; i++) {
         init_button(cmd, this.buttons[cmd][i]);
       }
     }
@@ -5391,28 +5358,31 @@
       button = a_buttons[n];
       obj = document.getElementById(button.id);
 
+      if (!obj)
+        continue;
+
       // get default/passive setting of the button
-      if (obj && button.type == 'image' && !button.status) {
+      if (button.type == 'image' && !button.status) {
         button.pas = obj._original_src ? obj._original_src : obj.src;
         // respect PNG fix on IE browsers
         if (obj.runtimeStyle && obj.runtimeStyle.filter && obj.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))
           button.pas = RegExp.$1;
       }
-      else if (obj && !button.status)
+      else if (!button.status)
         button.pas = String(obj.className);
 
       // set image according to button state
-      if (obj && button.type == 'image' && button[state]) {
+      if (button.type == 'image' && button[state]) {
         button.status = state;
         obj.src = button[state];
       }
       // set class name according to button state
-      else if (obj && button[state] !== undefined) {
+      else if (button[state] !== undefined) {
         button.status = state;
         obj.className = button[state];
       }
       // disable/enable input buttons
-      if (obj && button.type=='input') {
+      if (button.type == 'input') {
         button.status = state;
         obj.disabled = !state;
       }
@@ -5635,6 +5605,13 @@
     this.messages = {};
   };
 
+  // enable/disable buttons for page shifting
+  this.set_page_buttons = function()
+  {
+    this.enable_command('nextpage', 'lastpage', (this.env.pagecount > this.env.current_page));
+    this.enable_command('previouspage', 'firstpage', (this.env.current_page > 1));
+  };
+
   // mark a mailbox as selected and set environment variable
   this.select_folder = function(name, prefix, encode)
   {
@@ -5681,7 +5658,7 @@
 
   // for reordering column array (Konqueror workaround)
   // and for setting some message list global variables
-  this.set_message_coltypes = function(coltypes, repl)
+  this.set_message_coltypes = function(coltypes, repl, smart_col)
   {
     var list = this.message_list,
       thead = list ? list.list.tHead : null,
@@ -5709,7 +5686,7 @@
 
       for (n=0, len=this.env.coltypes.length; n<len; n++) {
         col = this.env.coltypes[n];
-        if ((cell = thead.rows[0].cells[n]) && (col=='from' || col=='to')) {
+        if ((cell = thead.rows[0].cells[n]) && (col == 'from' || col == 'to' || col == 'fromto')) {
           cell.id = 'rcm'+col;
           // if we have links for sorting, it's a bit more complicated...
           if (cell.firstChild && cell.firstChild.tagName.toLowerCase()=='a') {
@@ -5717,7 +5694,7 @@
             cell.onclick = function(){ return rcmail.command('sort', this.__col, this); };
             cell.__col = col;
           }
-          cell.innerHTML = this.get_label(col);
+          cell.innerHTML = this.get_label(col == 'fromto' ? smart_col : col);
         }
       }
     }
@@ -6168,6 +6145,10 @@
     this.set_busy(false, null, lock);
     request.abort();
 
+    // don't display error message on page unload (#1488547)
+    if (this.unload)
+      return;
+
     if (request.status && errmsg)
       this.display_message(this.get_label('servererror') + ' (' + errmsg + ')', 'error');
     else if (status == 'timeout')
@@ -6204,7 +6185,7 @@
     // 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>';
+      var html = '<iframe name="'+frame_name+'" src="program/resources/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>';
       document.body.insertAdjacentHTML('BeforeEnd', html);
     }
     else { // for standards-compilant browsers
@@ -6254,7 +6235,7 @@
     // prepare multipart form data composition
     var files = e.target.files || e.dataTransfer.files,
       formdata = window.FormData ? new FormData() : null,
-      fieldname = this.env.filedrop.fieldname || '_file',
+      fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
       boundary = '------multipartformboundary' + (new Date).getTime(),
       dashdash = '--', crlf = '\r\n',
       multipart = dashdash + boundary + crlf;
@@ -6269,7 +6250,8 @@
         content = '<span>' + (multiple ? ref.get_label('uploadingmany') : files[0].name) + '</span>';
 
       // add to attachments list
-      ref.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false });
+      if (!ref.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false }))
+        ref.file_upload_id = ref.set_busy(true, 'uploading');
 
       // complete multipart content and post request
       multipart += dashdash + boundary + dashdash + crlf;
@@ -6277,10 +6259,11 @@
       $.ajax({
         type: 'POST',
         dataType: 'json',
-        url: ref.url(ref.env.filedrop.action||'upload', { _id:ref.env.compose_id||'', _uploadid:ts, _remote:1 }),
+        url: ref.url(ref.env.filedrop.action||'upload', { _id:ref.env.compose_id||ref.env.cid||'', _uploadid:ts, _remote:1 }),
         contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
         processData: false,
         data: formdata || multipart,
+        headers: {'X-Roundcube-Request': ref.env.request_token},
         beforeSend: function(xhr, s) { if (!formdata && xhr.sendAsBinary) xhr.send = xhr.sendAsBinary; },
         success: function(data){ ref.http_response(data); },
         error: function(o, status, err) { ref.http_error(o, status, err, null, 'attachment'); }
@@ -6289,7 +6272,7 @@
 
     // get contents of all dropped files
     var last = this.env.filedrop.single ? 0 : files.length - 1;
-    for (var i=0, f; i <= last && (f = files[i]); i++) {
+    for (var j=0, i=0, f; j <= last && (f = files[i]); i++) {
       if (!f.name) f.name = f.fileName;
       if (!f.size) f.size = f.fileSize;
       if (!f.type) f.type = 'application/octet-stream';
@@ -6306,8 +6289,8 @@
 
       // do it the easy way with FormData (FF 4+, Chrome 5+, Safari 5+)
       if (formdata) {
-        formdata.append(fieldname + '[]', f);
-        if (i == last)
+        formdata.append(fieldname, f);
+        if (j == last)
           return submit_data();
       }
       // use FileReader supporetd by Firefox 3.6
@@ -6315,33 +6298,35 @@
         var reader = new FileReader();
 
         // closure to pass file properties to async callback function
-        reader.onload = (function(file, i) {
+        reader.onload = (function(file, j) {
           return function(e) {
-            multipart += 'Content-Disposition: form-data; name="' + fieldname + '[]"';
+            multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
             multipart += '; filename="' + (f.name_bin || file.name) + '"' + crlf;
             multipart += 'Content-Length: ' + file.size + crlf;
             multipart += 'Content-Type: ' + file.type + crlf + crlf;
             multipart += e.target.result + crlf;
             multipart += dashdash + boundary + crlf;
 
-            if (i == last)  // we're done, submit the data
+            if (j == last)  // we're done, submit the data
               return submit_data();
           }
-        })(f,i);
+        })(f,j);
         reader.readAsBinaryString(f);
       }
       // Firefox 3
       else if (f.getAsBinary) {
-        multipart += 'Content-Disposition: form-data; name="' + fieldname + '[]"';
+        multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
         multipart += '; filename="' + (f.name_bin || f.name) + '"' + crlf;
         multipart += 'Content-Length: ' + f.size + crlf;
         multipart += 'Content-Type: ' + f.type + crlf + crlf;
         multipart += f.getAsBinary() + crlf;
         multipart += dashdash + boundary +crlf;
 
-        if (i == last)
+        if (j == last)
           return submit_data();
       }
+
+      j++;
     }
   };
 
@@ -6538,7 +6523,7 @@
 
     img.onload = function() { rcmail.env.browser_capabilities.tif = 1; };
     img.onerror = function() { rcmail.env.browser_capabilities.tif = 0; };
-    img.src = 'program/blank.tif';
+    img.src = 'program/resources/blank.tif';
   };
 
   this.pdf_support_check = function()
@@ -6595,6 +6580,12 @@
     return 0;
   };
 
+  // Cookie setter
+  this.set_cookie = function(name, value, expires)
+  {
+    setCookie(name, value, expires, this.env.cookie_path, this.env.cookie_domain, this.env.cookie_secure);
+  }
+
 }  // end object rcube_webmail
 
 
@@ -6625,6 +6616,8 @@
   }
 };
 
+rcube_webmail.prototype.get_cookie = getCookie;
+
 // 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