From 669747a81c4b2ff823d1f20dc50899163c0a8a4a Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Thu, 28 Jun 2012 03:22:31 -0400 Subject: [PATCH] Merge branch 'master' of github.com:roundcube/roundcubemail --- program/js/app.js | 459 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 340 insertions(+), 119 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index f3c91b2..a2307fd 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() @@ -54,9 +52,10 @@ // set jQuery ajax options $.ajaxSetup({ - cache:false, - error:function(request, status, err){ ref.http_error(request, status, err); }, - beforeSend:function(xmlhttp){ xmlhttp.setRequestHeader('X-Roundcube-Request', ref.env.request_token); } + cache: false, + timeout: this.env.request_timeout * 1000, + error: function(request, status, err){ ref.http_error(request, status, err); }, + beforeSend: function(xmlhttp){ xmlhttp.setRequestHeader('X-Roundcube-Request', ref.env.request_token); } }); // set environment variable(s) @@ -224,7 +223,7 @@ this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', 'forward', 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'download', - 'print', 'load-attachment', 'load-headers', 'forward-attachment']; + 'print', 'load-attachment', 'show-headers', 'hide-headers', 'forward-attachment']; if (this.env.action == 'show' || this.env.action == 'preview') { this.enable_command(this.env.message_commands, this.env.uid); @@ -272,7 +271,7 @@ // show printing dialog else if (this.env.action == 'print' && this.env.uid) if (bw.safari) - window.setTimeout('window.print()', 10); + setTimeout('window.print()', 10); else window.print(); @@ -307,6 +306,10 @@ } this.http_post(postact, postdata); } + + // detect browser capabilities + if (!this.is_framed()) + this.browser_capabilities_check(); break; @@ -455,6 +458,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 && ((XMLHttpRequest && 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 }); @@ -482,7 +493,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; @@ -580,10 +591,6 @@ this.list_contacts(props); break; - case 'load-headers': - this.load_headers(obj); - break; - case 'sort': var sort_order, sort_col = props; @@ -632,7 +639,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); } @@ -654,13 +661,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; @@ -705,7 +713,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(); @@ -770,8 +778,8 @@ case 'always-load': if (this.env.uid && this.env.sender) { - this.add_contact(urlencode(this.env.sender)); - window.setTimeout(function(){ ref.command('load-images'); }, 300); + this.add_contact(this.env.sender); + setTimeout(function(){ ref.command('load-images'); }, 300); break; } @@ -789,7 +797,7 @@ qstring += '&_safe=1'; this.attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment'); if (this.attachment_win) { - window.setTimeout(function(){ ref.attachment_win.focus(); }, 10); + setTimeout(function(){ ref.attachment_win.focus(); }, 10); break; } } @@ -912,19 +920,25 @@ break; case 'savedraft': + var form = this.gui_objects.messageform, msgid; + // Reset the auto-save timer - self.clearTimeout(this.save_timer); + clearTimeout(this.save_timer); - if (!this.gui_objects.messageform) + // saving Drafts is disabled + if (!form) break; - // if saving Drafts is disabled in main.inc.php - // or if compose form did not change - if (!this.env.drafts_mailbox || this.cmp_hash == this.compose_field_hash()) + // compose form did not change + if (this.cmp_hash == this.compose_field_hash()) { + this.auto_save_start(); break; + } - var form = this.gui_objects.messageform, - msgid = this.set_busy(true, 'savingmessage'); + // re-set keep-alive timeout + this.start_keepalive(); + + msgid = this.set_busy(true, 'savingmessage'); form.target = "savetarget"; form._draft.value = '1'; @@ -940,7 +954,7 @@ break; // Reset the auto-save timer - self.clearTimeout(this.save_timer); + clearTimeout(this.save_timer); // all checks passed, send message var lang = this.spellcheck_lang(), @@ -953,13 +967,11 @@ form.action = this.add_url(form.action, '_lang', lang); form.submit(); - // clear timeout (sending could take longer) - clearTimeout(this.request_timer); break; case 'send-attachment': // Reset the auto-save timer - self.clearTimeout(this.save_timer); + clearTimeout(this.save_timer); this.upload_file(props || this.gui_objects.uploadform); break; @@ -981,12 +993,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); } @@ -995,9 +1007,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; @@ -1006,7 +1018,7 @@ if (uid = this.get_single_uid()) { ref.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : '')); if (this.printwin) { - window.setTimeout(function(){ ref.printwin.focus(); }, 20); + setTimeout(function(){ ref.printwin.focus(); }, 20); if (this.env.action != 'show') this.mark_message('read', uid); } @@ -1017,13 +1029,13 @@ if (uid = this.get_single_uid()) { ref.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)); if (this.sourcewin) - window.setTimeout(function(){ ref.sourcewin.focus(); }, 20); + setTimeout(function(){ ref.sourcewin.focus(); }, 20); } break; 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 @@ -1076,7 +1088,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; @@ -1103,7 +1115,7 @@ default: var func = command.replace(/-/g, '_'); if (this[func] && typeof this[func] === 'function') { - ret = this[func](props); + ret = this[func](props, obj); } break; } @@ -1156,14 +1168,6 @@ if (this.gui_objects.editform) this.lock_form(this.gui_objects.editform, a); - // clear pending timer - if (this.request_timer) - clearTimeout(this.request_timer); - - // set timer for requests - if (a && this.env.request_timeout) - this.request_timer = window.setTimeout(function(){ ref.request_timed_out(); }, this.env.request_timeout * 1000); - return id; }; @@ -1202,19 +1206,12 @@ return url.replace(/_task=[a-z]+/, '_task='+task); }; - // called when a request timed out - this.request_timed_out = function() - { - this.set_busy(false); - this.display_message('Request timed out!', 'error'); - }; - this.reload = function(delay) { if (this.is_framed()) parent.rcmail.reload(delay); else if (delay) - window.setTimeout(function(){ rcmail.reload(); }, delay); + setTimeout(function(){ rcmail.reload(); }, delay); else if (window.location) location.href = this.env.comm_path + (this.env.action ? '&_action='+this.env.action : ''); }; @@ -1347,7 +1344,7 @@ this.env.last_folder_target = null; if (this.folder_auto_timer) { - window.clearTimeout(this.folder_auto_timer); + clearTimeout(this.folder_auto_timer); this.folder_auto_timer = null; this.folder_auto_expand = null; } @@ -1400,15 +1397,15 @@ // if the folder is collapsed, expand it after 1sec and restart the drag & drop process. if (div.hasClass('collapsed')) { if (this.folder_auto_timer) - window.clearTimeout(this.folder_auto_timer); + clearTimeout(this.folder_auto_timer); this.folder_auto_expand = this.env.mailboxes[k].id; - this.folder_auto_timer = window.setTimeout(function() { + this.folder_auto_timer = setTimeout(function() { rcmail.command('collapse-folder', rcmail.folder_auto_expand); rcmail.drag_start(null); }, 1000); } else if (this.folder_auto_timer) { - window.clearTimeout(this.folder_auto_timer); + clearTimeout(this.folder_auto_timer); this.folder_auto_timer = null; this.folder_auto_expand = null; } @@ -1560,7 +1557,7 @@ // start timer for message preview (wait for double click) if (selected && this.env.contentframe && !list.multi_selecting && !this.dummy_select) - this.preview_timer = window.setTimeout(function(){ ref.msglist_get_preview(); }, 200); + this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200); else if (this.env.contentframe) this.show_contentframe(false); }; @@ -1577,7 +1574,7 @@ clearTimeout(this.preview_timer); if (this.preview_read_timer) clearTimeout(this.preview_read_timer); - this.preview_timer = window.setTimeout(function(){ ref.msglist_get_preview(); }, 200); + this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200); } } }; @@ -1592,7 +1589,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); }; @@ -1830,7 +1827,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>'; @@ -1851,8 +1848,11 @@ else if (c == 'threads') html = expando; else if (c == 'subject') { - if (bw.ie) + if (bw.ie) { col.onmouseover = function() { rcube_webmail.long_subject_title_ie(this, message.depth+1); }; + if (bw.ie8) + tree = '<span></span>' + tree; // #1487821 + } html = tree + cols[c]; } else if (c == 'priority') { @@ -1956,14 +1956,17 @@ 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) { - this.preview_read_timer = window.setTimeout(function() { + 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'); if (ref.env.unread_counts[ref.env.mailbox]) { @@ -2552,7 +2555,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, @@ -2584,7 +2587,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(); } @@ -2999,7 +3002,7 @@ this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); // add signature according to selected identity // if we have HTML editor, signature is added in callback - if (input_from.prop('type') == 'select-one' && $("input[name='_draft_saveid']").val() == '') { + if (input_from.prop('type') == 'select-one') { this.change_identity(input_from[0]); } } @@ -3152,7 +3155,7 @@ tinyMCE.execCommand('mceAddControl', false, props.id); if (this.env.default_font) - window.setTimeout(function() { + setTimeout(function() { $(tinyMCE.get(props.id).getBody()).css('font-family', rcmail.env.default_font); }, 500); } @@ -3251,7 +3254,7 @@ this.auto_save_start = function() { if (this.env.draft_autosave) - this.save_timer = self.setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000); + this.save_timer = setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000); // Unlock interface now that saving is complete this.busy = false; @@ -3260,20 +3263,11 @@ this.compose_field_hash = function(save) { // check input fields - var ed, str = '', - value_to = $("[name='_to']").val(), - value_cc = $("[name='_cc']").val(), - value_bcc = $("[name='_bcc']").val(), - value_subject = $("[name='_subject']").val(); + var ed, i, val, str = '', hash_fields = ['to', 'cc', 'bcc', 'subject']; - if (value_to) - str += value_to+':'; - if (value_cc) - str += value_cc+':'; - if (value_bcc) - str += value_bcc+':'; - if (value_subject) - str += value_subject+':'; + for (i=0; i<hash_fields.length; i++) + if (val = $('[name="_' + hash_fields[i] + '"]').val()) + str += val + ':'; if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody))) str += ed.getContent(); @@ -3467,12 +3461,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) { @@ -3491,6 +3480,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); @@ -3536,7 +3532,7 @@ this.upload_progress_start = function(action, name) { - window.setTimeout(function() { rcmail.http_request(action, {_progress: name}); }, + setTimeout(function() { rcmail.http_request(action, {_progress: name}); }, this.env.upload_progress_time * 1000); }; @@ -3641,7 +3637,7 @@ { this.display_message(msg, type); // before redirect we need to wait some time for Chrome (#1486177) - window.setTimeout(function(){ ref.list_mailbox(); }, 500); + setTimeout(function(){ ref.list_mailbox(); }, 500); }; @@ -3699,11 +3695,11 @@ case 37: // left case 39: // right if (mod != SHIFT_KEY) - return; + return; } // start timer - this.ksearch_timer = window.setTimeout(function(){ ref.ksearch_get_results(props); }, 200); + this.ksearch_timer = setTimeout(function(){ ref.ksearch_get_results(props); }, 200); this.ksearch_input = obj; return true; @@ -3818,7 +3814,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(), @@ -3832,7 +3828,7 @@ for (i=0; i<threads; i++) { source = this.ksearch_data.sources.shift(); - if (threads > 1 && source === null) + if (threads > 1 && source === undefined) break; post_data._source = source ? source : ''; @@ -4011,7 +4007,7 @@ source = this.env.source ? this.env.address_sources[this.env.source] : null; if (id = list.get_single_selection()) - this.preview_timer = window.setTimeout(function(){ ref.load_contact(id, 'show'); }, 200); + this.preview_timer = setTimeout(function(){ ref.load_contact(id, 'show'); }, 200); else if (this.env.contentframe) this.show_contentframe(false); @@ -4719,11 +4715,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'); } }; @@ -4738,8 +4734,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) @@ -4940,7 +4936,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; }; @@ -5553,7 +5549,7 @@ } // add element and set timeout this.messages[key].elements.push(id); - window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout); + setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout); return id; } @@ -5573,7 +5569,7 @@ this.triggerEvent('message', { message:msg, type:type, timeout:timeout, object:obj }); if (timeout > 0) - window.setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout); + setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout); return id; }; @@ -5853,14 +5849,14 @@ }; // display all-headers row and fetch raw message headers - this.load_headers = function(elem) + this.show_headers = function(props, elem) { if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box || !this.env.uid) return; $(elem).removeClass('show-headers').addClass('hide-headers'); $(this.gui_objects.all_headers_row).show(); - elem.onclick = function() { rcmail.hide_headers(elem); }; + elem.onclick = function() { rcmail.command('hide-headers', '', elem); }; // fetch headers only once if (!this.gui_objects.all_headers_box.innerHTML) { @@ -5870,14 +5866,14 @@ }; // hide all-headers row - this.hide_headers = function(elem) + this.hide_headers = function(props, elem) { if (!this.gui_objects.all_headers_row || !this.gui_objects.all_headers_box) return; $(elem).removeClass('hide-headers').addClass('show-headers'); $(this.gui_objects.all_headers_row).hide(); - elem.onclick = function() { rcmail.load_headers(elem); }; + elem.onclick = function() { rcmail.command('show-headers', '', elem); }; }; @@ -6001,7 +5997,7 @@ 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); } + error: function(o, status, err) { ref.http_error(o, status, err, lock, action); } }); }; @@ -6033,7 +6029,7 @@ 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); } + error: function(o, status, err) { ref.http_error(o, status, err, lock, action); } }); }; @@ -6165,7 +6161,7 @@ }; // handle HTTP request errors - this.http_error = function(request, status, err, lock) + this.http_error = function(request, status, err, lock, action) { var errmsg = request.statusText; @@ -6174,6 +6170,16 @@ if (request.status && errmsg) this.display_message(this.get_label('servererror') + ' (' + errmsg + ')', 'error'); + else if (status == 'timeout') + this.display_message(this.get_label('requesttimedout'), 'error'); + else if (request.status == 0 && status != 'abort') + this.display_message(this.get_label('servererror') + ' (No connection)', 'error'); + + // re-send keep-alive requests after 30 seconds + if (action == 'keep-alive') + setTimeout(function(){ ref.keep_alive(); }, 30000); + else if (action == 'check-recent') + setTimeout(function(){ ref.check_for_recent(false); }, 30000); }; // post the given form to a hidden iframe @@ -6224,15 +6230,137 @@ 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, + 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() { + if (!this.env.keep_alive || this.env.framed) + return; + if (this._int) clearInterval(this._int); - if (this.env.keep_alive && !this.env.framed && this.task == 'mail' && this.gui_objects.mailboxlist) + if (this.task == 'mail' && this.gui_objects.mailboxlist) this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000); - else if (this.env.keep_alive && !this.env.framed && this.task != 'login' && this.env.action != 'print') + else if (this.task != 'login' && this.env.action != 'print') this._int = setInterval(function(){ ref.keep_alive(); }, this.env.keep_alive * 1000); }; @@ -6293,7 +6421,7 @@ return obj.selectionEnd; else if (document.selection && document.selection.createRange) { var range = document.selection.createRange(); - if (range.parentElement()!=obj) + if (range.parentElement() != obj) return 0; var gm = range.duplicate(); @@ -6377,6 +6505,99 @@ $(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/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; + }; + } // end object rcube_webmail -- Gitblit v1.9.1