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 | 492 +++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 355 insertions(+), 137 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index 3518712..838f240 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -17,8 +17,6 @@ +-----------------------------------------------------------------------+ | Requires: jquery.js, common.js, list.js | +-----------------------------------------------------------------------+ - - $Id$ */ function rcube_webmail() @@ -50,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({ @@ -59,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) @@ -309,6 +309,10 @@ this.http_post(postact, postdata); } + // detect browser capabilities + if (!this.is_framed()) + this.browser_capabilities_check(); + break; case 'addressbook': @@ -456,6 +460,14 @@ if (this.gui_objects.folderlist) 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 && ((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'); }) + .get(0).addEventListener('drop', function(e){ return ref.file_dropped(e); }, false); + } + // trigger init event hook this.triggerEvent('init', { task:this.task, action:this.env.action }); @@ -483,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; @@ -629,7 +641,7 @@ uid = this.get_single_uid(); if (uid && (!this.env.uid || uid != this.env.uid)) { if (this.env.mailbox == this.env.drafts_mailbox) - this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); + this.goto_url('compose', { _draft_uid: uid, _mbox: this.env.mailbox }, true); else this.show_message(uid); } @@ -651,13 +663,14 @@ break; case 'edit': - if (this.task=='addressbook' && (cid = this.get_single_cid())) + if (this.task == 'addressbook' && (cid = this.get_single_cid())) this.load_contact(cid, 'edit'); - else if (this.task=='settings' && props) + else if (this.task == 'settings' && props) this.load_identity(props, 'edit-identity'); - else if (this.task=='mail' && (cid = this.get_single_uid())) { - url = (this.env.mailbox == this.env.drafts_mailbox) ? '_draft_uid=' : '_uid='; - this.goto_url('compose', url+cid+'&_mbox='+urlencode(this.env.mailbox), true); + else if (this.task == 'mail' && (cid = this.get_single_uid())) { + url = { _mbox: this.env.mailbox }; + url[this.env.mailbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'] = cid; + this.goto_url('compose', url, true); } break; @@ -702,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(); @@ -767,7 +780,7 @@ case 'always-load': if (this.env.uid && this.env.sender) { - this.add_contact(urlencode(this.env.sender)); + this.add_contact(this.env.sender); setTimeout(function(){ ref.command('load-images'); }, 300); break; } @@ -909,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()) { @@ -927,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': @@ -982,12 +971,12 @@ case 'reply-list': case 'reply': if (uid = this.get_single_uid()) { - url = '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); + url = {_reply_uid: uid, _mbox: this.env.mailbox}; if (command == 'reply-all') // do reply-list, when list is detected and popup menu wasn't used - url += '&_all=' + (!props && this.commands['reply-list'] ? 'list' : 'all'); + 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); } @@ -996,9 +985,9 @@ case 'forward-attachment': case 'forward': if (uid = this.get_single_uid()) { - url = '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); + url = { _forward_uid: uid, _mbox: this.env.mailbox }; if (command == 'forward-attachment' || (!props && this.env.forward_attachment)) - url += '&_attachment=1'; + url._attachment = 1; this.goto_url('compose', url, true); } break; @@ -1024,7 +1013,7 @@ case 'download': if (uid = this.get_single_uid()) - this.goto_url('viewsource', '&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+'&_save=1'); + this.goto_url('viewsource', { _uid: uid, _mbox: this.env.mailbox, _save: 1 }); break; // quicksearch @@ -1077,7 +1066,7 @@ case 'export': if (this.contact_list.rowcount > 0) { - this.goto_url('export', { _source:this.env.source, _gid:this.env.group, _search:this.env.search_request }); + this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request }); } break; @@ -1458,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) { @@ -1578,7 +1559,7 @@ var uid = list.get_single_selection(); if (uid && this.env.mailbox == this.env.drafts_mailbox) - this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); + this.goto_url('compose', { _draft_uid: uid, _mbox: this.env.mailbox }, true); else if (uid) this.show_message(uid, false, false); }; @@ -1623,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) @@ -1816,7 +1797,7 @@ html = '<span id="flagicn'+uid+'" class="'+css_class+'"> </span>'; } else if (c == 'attachment') { - if (/application\/|multipart\/m/.test(flags.ctype)) + if (/application\/|multipart\/(m|signed)/.test(flags.ctype)) html = '<span class="attachment"> </span>'; else if (/multipart\/report/.test(flags.ctype)) html = '<span class="report"> </span>'; @@ -1902,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); @@ -1945,13 +1926,16 @@ if (this.env.search_request) url += '&_search='+this.env.search_request; - if (action == 'preview' && String(target.location.href).indexOf(url) >= 0) + // add browser capabilities, so we can properly handle attachments + url += '&_caps='+urlencode(this.browser_capabilities()); + + if (preview && String(target.location.href).indexOf(url) >= 0) this.show_contentframe(true); else { 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) { + if (preview && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread && this.env.preview_pane_mark_read >= 0) { this.preview_read_timer = setTimeout(function() { ref.set_message(id, 'unread', false); ref.update_thread_root(id, 'read'); @@ -2541,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, @@ -2573,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(); } @@ -2660,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) @@ -3017,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); @@ -3283,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]) { @@ -3297,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) @@ -3310,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 @@ -3379,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; @@ -3447,12 +3436,7 @@ var content = '<span>' + this.get_label('uploading' + (files > 1 ? 'many' : '')) + '</span>', ts = frame_name.replace(/^rcmupload/, ''); - if (this.env.loadingicon) - content = '<img src="'+this.env.loadingicon+'" alt="" class="uploading" />'+content; - content = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+ts+'\', \''+frame_name+'\');" href="#cancelupload" class="cancelupload">' - + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="" />' : this.get_label('cancel')) + '</a>' + content; - - this.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false }); + this.add2attachment_list(ts, { name:'', html:content, classname:'uploading', frame:frame_name, complete:false }); // upload progress support if (this.env.upload_progress_time) { @@ -3471,6 +3455,13 @@ { if (!this.gui_objects.attachmentlist) return false; + + if (!att.complete && ref.env.loadingicon) + att.html = '<img src="'+ref.env.loadingicon+'" alt="" class="uploading" />' + att.html; + + if (!att.complete && att.frame) + att.html = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+name+'\', \''+att.frame+'\');" href="#cancelupload" class="cancelupload">' + + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="" />' : this.get_label('cancel')) + '</a>' + att.html; var indicator, li = $('<li>').attr('id', name).addClass(att.classname).html(att.html); @@ -3798,7 +3789,7 @@ return; // ...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) + if (old_value && old_value.length && q.indexOf(old_value) == 0 && (!ac || ac.num <= 0) && this.env.contacts && !this.env.contacts.length) return; var i, lock, source, xhr, reqid = new Date().getTime(), @@ -4014,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); @@ -4259,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); @@ -4699,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'); } }; @@ -4718,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) @@ -4920,7 +4910,7 @@ // submit request with appended token if (confirm(this.get_label('deleteidentityconfirm'))) - this.goto_url('delete-identity', '_iid='+id+'&_token='+this.env.request_token, true); + this.goto_url('delete-identity', { _iid: id, _token: this.env.request_token }, true); return true; }; @@ -5342,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() { @@ -5356,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]); } } @@ -5375,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; } @@ -5619,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) { @@ -5665,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, @@ -5693,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') { @@ -5701,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); } } } @@ -6152,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') @@ -6188,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 @@ -6213,6 +6210,126 @@ return frame_name; }; + + // html5 file-drop API + this.document_drag_hover = function(e, over) + { + e.preventDefault(); + $(ref.gui_objects.filedrop)[(over?'addClass':'removeClass')]('active'); + }; + + this.file_drag_hover = function(e, over) + { + e.preventDefault(); + e.stopPropagation(); + $(ref.gui_objects.filedrop)[(over?'addClass':'removeClass')]('hover'); + }; + + // handler when files are dropped to a designated area. + // compose a multipart form data and submit it to the server + this.file_dropped = function(e) + { + // abort event and reset UI + this.file_drag_hover(e, false); + + // 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') + (this.env.filedrop.single ? '' : '[]'), + boundary = '------multipartformboundary' + (new Date).getTime(), + dashdash = '--', crlf = '\r\n', + multipart = dashdash + boundary + crlf; + + if (!files || !files.length) + return; + + // inline function to submit the files to the server + var submit_data = function() { + var multiple = files.length > 1, + ts = new Date().getTime(), + content = '<span>' + (multiple ? ref.get_label('uploadingmany') : files[0].name) + '</span>'; + + // add to attachments list + 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; + + $.ajax({ + type: 'POST', + dataType: 'json', + 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'); } + }); + }; + + // get contents of all dropped files + var last = this.env.filedrop.single ? 0 : files.length - 1; + 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'; + + // file name contains non-ASCII characters, do UTF8-binary string conversion. + if (!formdata && /[^\x20-\x7E]/.test(f.name)) + f.name_bin = unescape(encodeURIComponent(f.name)); + + // filter by file type if requested + if (this.env.filedrop.filter && !f.type.match(new RegExp(this.env.filedrop.filter))) { + // TODO: show message to user + continue; + } + + // do it the easy way with FormData (FF 4+, Chrome 5+, Safari 5+) + if (formdata) { + formdata.append(fieldname, f); + if (j == last) + return submit_data(); + } + // use FileReader supporetd by Firefox 3.6 + else if (window.FileReader) { + var reader = new FileReader(); + + // closure to pass file properties to async callback function + reader.onload = (function(file, j) { + return function(e) { + 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 (j == last) // we're done, submit the data + return submit_data(); + } + })(f,j); + reader.readAsBinaryString(f); + } + // Firefox 3 + else if (f.getAsBinary) { + 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 (j == last) + return submit_data(); + } + + j++; + } + }; + // starts interval for keep-alive/check-recent signal this.start_keepalive = function() @@ -6370,6 +6487,105 @@ $(elem).click(function() { rcmail.register_protocol_handler(name); return false; }); }; + // Checks browser capabilities eg. PDF support, TIF support + this.browser_capabilities_check = function() + { + if (!this.env.browser_capabilities) + this.env.browser_capabilities = {}; + + if (this.env.browser_capabilities.pdf === undefined) + this.env.browser_capabilities.pdf = this.pdf_support_check(); + + if (this.env.browser_capabilities.flash === undefined) + this.env.browser_capabilities.flash = this.flash_support_check(); + + if (this.env.browser_capabilities.tif === undefined) + this.tif_support_check(); + }; + + // Returns browser capabilities string + this.browser_capabilities = function() + { + if (!this.env.browser_capabilities) + return ''; + + var n, ret = []; + + for (n in this.env.browser_capabilities) + ret.push(n + '=' + this.env.browser_capabilities[n]); + + return ret.join(); + }; + + this.tif_support_check = function() + { + var img = new Image(); + + img.onload = function() { rcmail.env.browser_capabilities.tif = 1; }; + img.onerror = function() { rcmail.env.browser_capabilities.tif = 0; }; + img.src = 'program/resources/blank.tif'; + }; + + this.pdf_support_check = function() + { + var plugin = navigator.mimeTypes ? navigator.mimeTypes["application/pdf"] : {}, + plugins = navigator.plugins, + len = plugins.length, + regex = /Adobe Reader|PDF|Acrobat/i; + + if (plugin && plugin.enabledPlugin) + return 1; + + if (window.ActiveXObject) { + try { + if (axObj = new ActiveXObject("AcroPDF.PDF")) + return 1; + } + catch (e) {} + try { + if (axObj = new ActiveXObject("PDF.PdfCtrl")) + return 1; + } + catch (e) {} + } + + for (i=0; i<len; i++) { + plugin = plugins[i]; + if (typeof plugin === 'String') { + if (regex.test(plugin)) + return 1; + } + else if (plugin.name && regex.test(plugin.name)) + return 1; + } + + return 0; + }; + + this.flash_support_check = function() + { + var plugin = navigator.mimeTypes ? navigator.mimeTypes["application/x-shockwave-flash"] : {}; + + if (plugin && plugin.enabledPlugin) + return 1; + + if (window.ActiveXObject) { + try { + if (axObj = new ActiveXObject("ShockwaveFlash.ShockwaveFlash")) + return 1; + } + catch (e) {} + } + + 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 @@ -6400,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