From 4910b0666edeeebb18ac9682cb898dba8f22962a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli <thomas@roundcube.net> Date: Thu, 15 Aug 2013 02:57:11 -0400 Subject: [PATCH] Distinguish mobile/tablet/touch devices --- program/js/app.js | 244 ++++++++++++++++++++++++++++++++++-------------- 1 files changed, 174 insertions(+), 70 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index 41be99c..dedad37 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -229,7 +229,7 @@ this.set_button_titles(); this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', - 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', + 'move', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'print', 'load-attachment', 'download-attachment', 'show-headers', 'hide-headers', 'download', 'forward', 'forward-inline', 'forward-attachment', 'change-format']; @@ -277,6 +277,8 @@ // init message compose form this.init_messageform(); } + else if (this.env.action == 'get') + this.enable_command('download', 'print', true); // show printing dialog else if (this.env.action == 'print' && this.env.uid) { if (bw.safari) @@ -374,7 +376,7 @@ } if (this.gui_objects.qsearchbox) - this.enable_command('search', 'reset-search', 'moveto', true); + this.enable_command('search', 'reset-search', true); break; @@ -396,7 +398,7 @@ } else if (this.env.action == 'edit-folder' && this.gui_objects.editform) { this.enable_command('save', 'folder-size', true); - parent.rcmail.env.messagecount = this.env.messagecount; + parent.rcmail.env.exists = this.env.messagecount; parent.rcmail.enable_command('purge', this.env.messagecount); $("input[type='text']").first().select(); } @@ -793,16 +795,18 @@ // mail task commands case 'move': - case 'moveto': + case 'moveto': // deprecated if (this.task == 'mail') this.move_messages(props); else if (this.task == 'addressbook') - this.copy_contact(null, props); + this.move_contacts(props); break; case 'copy': if (this.task == 'mail') this.copy_messages(props); + else if (this.task == 'addressbook') + this.copy_contacts(props); break; case 'mark': @@ -864,7 +868,7 @@ // open attachment in frame if it's of a supported mimetype if (command != 'download-attachment' && mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0) { - if (this.open_window(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', true, true)) + if (this.open_window(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1')) break; } @@ -1048,7 +1052,10 @@ break; case 'print': - if (uid = this.get_single_uid()) { + if (this.env.action == 'get') { + this.gui_objects.messagepartframe.contentWindow.print(); + } + else if (uid = this.get_single_uid()) { ref.printwin = this.open_window(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''), true, true); if (this.printwin) { if (this.env.action != 'show') @@ -1063,7 +1070,10 @@ break; case 'download': - if (uid = this.get_single_uid()) + if (this.env.action == 'get') { + location.href = location.href.replace(/_frame=/, '_download='); + } + else if (uid = this.get_single_uid()) this.goto_url('viewsource', { _uid: uid, _mbox: this.env.mailbox, _save: 1 }); break; @@ -1342,7 +1352,7 @@ this.drag_menu = function(e, target) { var modkey = rcube_event.get_modifier(e), - menu = this.gui_objects.message_dragmenu; + menu = this.gui_objects.dragmenu; if (menu && modkey == SHIFT_KEY && this.commands['copy']) { var pos = rcube_event.get_mouse_pos(e); @@ -1356,7 +1366,7 @@ this.drag_menu_action = function(action) { - var menu = this.gui_objects.message_dragmenu; + var menu = this.gui_objects.dragmenu; if (menu) { $(menu).hide(); } @@ -1471,8 +1481,12 @@ list.draglayer.hide(); this.drag_end(e); - if (!this.drag_menu(e, target)) - this.command('moveto', target); + if (this.contact_list) { + if (!this.contacts_drag_menu(e, target)) + this.command('move', target); + } + else if (!this.drag_menu(e, target)) + this.command('move', target); } // reset 'pressed' buttons @@ -1519,7 +1533,7 @@ } } // Multi-message commands - this.enable_command('delete', 'moveto', 'copy', 'mark', 'forward', 'forward-attachment', list.selection.length > 0); + this.enable_command('delete', 'move', 'copy', 'mark', 'forward', 'forward-attachment', list.selection.length > 0); // reset all-pages-selection if (selected || (list.selection.length && list.selection.length != list.rowcount)) @@ -1622,28 +1636,28 @@ this.check_droptarget = function(id) { - if (this.task == 'mail') - return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual) ? 1 : 0; + switch (this.task) { + case 'mail': + return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual) ? 1 : 0; - if (this.task == 'settings') - return id != this.env.mailbox ? 1 : 0; + case 'settings': + return id != this.env.mailbox ? 1 : 0; - if (this.task == 'addressbook') { - if (id != this.env.source && this.env.contactfolders[id]) { - // droptarget is a group - contact add to group action - if (this.env.contactfolders[id].type == 'group') { - var target_abook = this.env.contactfolders[id].source; - if (this.env.contactfolders[id].id != this.env.group && !this.env.contactfolders[target_abook].readonly) { - // search result may contain contacts from many sources - return (this.env.selection_sources.length > 1 || $.inArray(target_abook, this.env.selection_sources) == -1) ? 2 : 1; + case 'addressbook': + var target; + if (id != this.env.source && (target = this.env.contactfolders[id])) { + // droptarget is a group + if (target.type == 'group') { + if (target.id != this.env.group && !this.env.contactfolders[target.source].readonly) { + var is_other = this.env.selection_sources.length > 1 || $.inArray(target.source, this.env.selection_sources) == -1; + return !is_other || this.commands.move ? 1 : 2; + } + } + // droptarget is a (writable) addressbook and it's not the source + else if (!target.readonly && (this.env.selection_sources.length > 1 || $.inArray(id, this.env.selection_sources) == -1)) { + return this.commands.move ? 1 : 2; } } - // droptarget is a (writable) addressbook - contact copy action - else if (!this.env.contactfolders[id].readonly) { - // search result may contain contacts from many sources - return (this.env.selection_sources.length > 1 || $.inArray(id, this.env.selection_sources) == -1) ? 2 : 0; - } - } } return 0; @@ -1717,6 +1731,14 @@ if (!row.depth && row.has_children && (expando = document.getElementById('rcmexpando'+row.uid))) { row.expando = expando; expando.onmousedown = function(e) { return self.expand_message_row(e, uid); }; + if (bw.touch) { + expando.addEventListener('touchend', function(e) { + if (e.changedTouches.length == 1) { + self.expand_message_row(e, uid); + return rcube_event.cancel(e); + } + }, false); + } } this.triggerEvent('insertrow', { uid:uid, row:row }); @@ -2000,14 +2022,18 @@ if (name && (frame = this.get_frame_element(name))) { if (!show && (win = this.get_frame_window(name))) { - if (win.location && win.location.href.indexOf(this.env.blankpage)<0) - win.location.href = this.env.blankpage; + if (win.stop) + win.stop(); + else // IE + win.document.execCommand('Stop'); + + win.location.href = this.env.blankpage; } else if (!bw.safari && !bw.konq) $(frame)[show ? 'show' : 'hide'](); } - if (!show && this.busy) + if (!show && this.env.frame_lock) this.set_busy(false, null, this.env.frame_lock); }; @@ -2135,12 +2161,12 @@ this.clear_message_list = function() { - this.env.messages = {}; - this.last_selected = 0; + this.env.messages = {}; + this.last_selected = 0; - this.show_contentframe(false); - if (this.message_list) - this.message_list.clear(true); + this.show_contentframe(false); + if (this.message_list) + this.message_list.clear(true); }; // send remote request to load message list @@ -2585,7 +2611,7 @@ // Hide message command buttons until a message is selected this.enable_command(this.env.message_commands, false); - this._with_selected_messages('moveto', post_data, lock); + this._with_selected_messages('move', post_data, lock); }; // delete selected messages from the current mailbox @@ -2644,7 +2670,7 @@ this._with_selected_messages('delete', post_data); }; - // Send a specifc moveto/delete request with UIDs of all selected messages + // Send a specifc move/delete request with UIDs of all selected messages // @private this._with_selected_messages = function(action, post_data, lock) { @@ -2686,7 +2712,7 @@ this.delete_excessive_thread_rows(); if (!lock) { - msg = action == 'moveto' ? 'movingmessage' : 'deletingmessage'; + msg = action == 'move' ? 'movingmessage' : 'deletingmessage'; lock = this.display_message(this.get_label(msg), 'loading'); } @@ -3047,7 +3073,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' && !this.env.opened_extwin) { + if (input_from.prop('type') == 'select-one') { this.change_identity(input_from[0]); } } @@ -3383,6 +3409,15 @@ if (!show_sig) show_sig = this.env.show_sig; + + // first function execution + if (!this.env.identities_initialized) { + this.env.identities_initialized = true; + if (this.env.show_sig_later) + this.env.show_sig = true; + if (this.env.opened_extwin) + return; + } var i, rx, cursor_pos, p = -1, id = obj.options[obj.selectedIndex].value, @@ -4142,6 +4177,8 @@ this.show_contentframe(false); if (list.selection.length) { + list.draggable = false; + // no source = search result, we'll need to detect if any of // selected contacts are in writable addressbook to enable edit/delete // we'll also need to know sources used in selection for copy @@ -4164,6 +4201,9 @@ else { writable = writable || (!source.readonly && !contact.readonly); } + + if (contact._type != 'group') + list.draggable = true; } this.env.selection_sources = $.unique(this.env.selection_sources); @@ -4173,9 +4213,9 @@ // thend we can enable the group-remove-selected command this.enable_command('group-remove-selected', this.env.group && list.selection.length > 0 && writable); this.enable_command('compose', this.env.group || list.selection.length > 0); - this.enable_command('export-selected', list.selection.length > 0); + this.enable_command('export-selected', 'copy', list.selection.length > 0); this.enable_command('edit', id && writable); - this.enable_command('delete', list.selection.length > 0 && writable); + this.enable_command('delete', 'move', list.selection.length > 0 && writable); return false; }; @@ -4283,7 +4323,7 @@ this.contact_list.data = {}; this.contact_list.clear(true); this.show_contentframe(false); - this.enable_command('delete', false); + this.enable_command('delete', 'move', 'copy', false); this.enable_command('compose', this.env.group ? true : false); }; @@ -4353,14 +4393,38 @@ this.http_post('group-'+what+'members', post_data, lock); }; - // copy a contact to the specified target (group or directory) - this.copy_contact = function(cid, to) + this.contacts_drag_menu = function(e, to) + { + var dest = to.type == 'group' ? to.source : to.id, + source = this.env.source; + + if (!this.env.address_sources[dest] || this.env.address_sources[dest].readonly) + return true; + + // search result may contain contacts from many sources, but if there is only one... + if (source == '' && this.env.selection_sources.length == 1) + source = this.env.selection_sources[0]; + + if (to.type == 'group' && dest == source) { + var cid = this.contact_list.get_selection().join(','); + this.group_member_change('add', cid, dest, to.id); + return true; + } + // move action is not possible, "redirect" to copy if menu wasn't requested + else if (!this.commands.move && rcube_event.get_modifier(e) != SHIFT_KEY) { + this.copy_contacts(to); + return true; + } + + return this.drag_menu(e, to); + }; + + // copy contact(s) to the specified target (group or directory) + this.copy_contacts = function(to) { var n, dest = to.type == 'group' ? to.source : to.id, source = this.env.source, - group = this.env.group ? this.env.group : ''; - - if (!cid) + group = this.env.group ? this.env.group : '', cid = this.contact_list.get_selection().join(','); if (!cid || !this.env.address_sources[dest] || this.env.address_sources[dest].readonly) @@ -4373,13 +4437,12 @@ // tagret is a group if (to.type == 'group') { if (dest == source) - this.group_member_change('add', cid, dest, to.id); - else { - var lock = this.display_message(this.get_label('copyingcontact'), 'loading'), - post_data = {_cid: cid, _source: this.env.source, _to: dest, _togid: to.id, _gid: group}; + return; - this.http_post('copy', post_data, lock); - } + var lock = this.display_message(this.get_label('copyingcontact'), 'loading'), + post_data = {_cid: cid, _source: this.env.source, _to: dest, _togid: to.id, _gid: group}; + + this.http_post('copy', post_data, lock); } // target is an addressbook else if (to.id != source) { @@ -4390,19 +4453,53 @@ } }; - this.delete_contacts = function() + // move contact(s) to the specified target (group or directory) + this.move_contacts = function(to) { - var selection = this.contact_list.get_selection(), - undelete = this.env.source && this.env.address_sources[this.env.source].undelete; + var dest = to.type == 'group' ? to.source : to.id, + source = this.env.source, + group = this.env.group ? this.env.group : ''; - // exit if no mailbox specified or if selection is empty - if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm')))) + if (!this.env.address_sources[dest] || this.env.address_sources[dest].readonly) return; - var id, n, a_cids = [], - post_data = {_source: this.env.source, _from: (this.env.action ? this.env.action : '')}, - lock = this.display_message(this.get_label('contactdeleting'), 'loading'); + // search result may contain contacts from many sources, but if there is only one... + if (source == '' && this.env.selection_sources.length == 1) + source = this.env.selection_sources[0]; + if (to.type == 'group') { + if (dest == source) + return; + + this._with_selected_contacts('move', {_to: dest, _togid: to.id}); + } + // target is an addressbook + else if (to.id != source) + this._with_selected_contacts('move', {_to: to.id}); + }; + + // delete contact(s) + this.delete_contacts = function() + { + var undelete = this.env.source && this.env.address_sources[this.env.source].undelete; + + if (!undelete && !confirm(this.get_label('deletecontactconfirm'))) + return; + + return this._with_selected_contacts('delete'); + }; + + this._with_selected_contacts = function(action, post_data) + { + var selection = this.contact_list ? this.contact_list.get_selection() : []; + + // exit if no mailbox specified or if selection is empty + if (!selection.length && !this.env.cid) + return; + + var n, a_cids = [], + label = action == 'delete' ? 'contactdeleting' : 'movingcontact', + lock = this.display_message(this.get_label(label), 'loading'); if (this.env.cid) a_cids.push(this.env.cid); else { @@ -4417,6 +4514,11 @@ this.show_contentframe(false); } + if (!post_data) + post_data = {}; + + post_data._source = this.env.source; + post_data._from = this.env.action; post_data._cid = a_cids.join(','); if (this.env.group) @@ -4427,7 +4529,7 @@ post_data._search = this.env.search_request; // send request to server - this.http_post('delete', post_data, lock) + this.http_post(action, post_data, lock) return true; }; @@ -4901,7 +5003,7 @@ this.replace_contact_photo = function(id) { 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.env.comm_path + '&_action=photo&_source=' + this.env.source + '&_cid=' + (this.env.cid || 0) + '&_photo=' + id; this.set_photo_actions(id); $(this.gui_objects.contactphoto).children('img').attr('src', img_src); @@ -6302,7 +6404,7 @@ this.enable_command('export-selected', false); } - case 'moveto': + case 'move': if (this.env.action == 'show') { // re-enable commands on move/delete error this.enable_command(this.env.message_commands, true); @@ -6343,6 +6445,7 @@ if ((response.action == 'list' || response.action == 'search') && this.message_list) { this.msglist_select(this.message_list); + this.message_list.resize(); this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount }); } } @@ -6353,6 +6456,7 @@ this.enable_command('search-create', this.env.source == ''); this.enable_command('search-delete', this.env.search_id); this.update_group_commands(); + this.contact_list.resize(); this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount }); } } @@ -6517,7 +6621,7 @@ timeout: 0, // disable default timeout set in ajaxSetup() data: formdata || multipart, headers: {'X-Roundcube-Request': ref.env.request_token}, - beforeSend: function(xhr, s) { if (!formdata && xhr.sendAsBinary) xhr.send = xhr.sendAsBinary; }, + xhr: function() { var xhr = jQuery.ajaxSettings.xhr(); if (!formdata && xhr.sendAsBinary) xhr.send = xhr.sendAsBinary; return xhr; }, success: function(data){ ref.http_response(data); }, error: function(o, status, err) { ref.http_error(o, status, err, null, 'attachment'); } }); @@ -6557,7 +6661,7 @@ 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 += reader.result + crlf; multipart += dashdash + boundary + crlf; if (j == last) // we're done, submit the data -- Gitblit v1.9.1