| | |
| | | this.onloads = []; |
| | | this.messages = {}; |
| | | this.group2expand = {}; |
| | | this.http_request_jobs = {}; |
| | | |
| | | // webmail client settings |
| | | this.dblclick_time = 500; |
| | |
| | | } |
| | | 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(); |
| | | } |
| | |
| | | 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 }); |
| | |
| | | + (!flags.seen ? ' unread' : '') |
| | | + (flags.deleted ? ' deleted' : '') |
| | | + (flags.flagged ? ' flagged' : '') |
| | | + (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '') |
| | | + (message.selected ? ' selected' : ''), |
| | | row = { cols:[], style:{}, id:'rcmrow'+uid }; |
| | | |
| | |
| | | expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '"> </div>'; |
| | | row_class += ' thread' + (message.expanded? ' expanded' : ''); |
| | | } |
| | | |
| | | if (flags.unread_children && flags.seen && !message.expanded) |
| | | row_class += ' unroot'; |
| | | } |
| | | |
| | | tree += '<span id="msgicn'+uid+'" class="'+css_class+'"> </span>'; |
| | |
| | | html = expando; |
| | | else if (c == 'subject') { |
| | | if (bw.ie) { |
| | | col.onmouseover = function() { rcube_webmail.long_subject_title_ie(this, message.depth+1); }; |
| | | col.onmouseover = function() { rcube_webmail.long_subject_title_ex(this, message.depth+1); }; |
| | | if (bw.ie8) |
| | | tree = '<span></span>' + tree; // #1487821 |
| | | } |
| | |
| | | { |
| | | var len = a_uids.length, |
| | | i, uid, all_deleted = true, |
| | | rows = this.message_list ? this.message_list.rows : []; |
| | | rows = this.message_list ? this.message_list.rows : {}; |
| | | |
| | | if (len == 1) { |
| | | if (!rows.length || (rows[a_uids[0]] && !rows[a_uids[0]].deleted)) |
| | | if (!this.message_list || (rows[a_uids[0]] && !rows[a_uids[0]].deleted)) |
| | | this.flag_as_deleted(a_uids); |
| | | else |
| | | this.flag_as_undeleted(a_uids); |
| | |
| | | var r_uids = [], |
| | | post_data = this.selection_post_data({_uid: this.uids_to_list(a_uids), _flag: 'delete'}), |
| | | lock = this.display_message(this.get_label('markingmessage'), 'loading'), |
| | | rows = this.message_list ? this.message_list.rows : [], |
| | | rows = this.message_list ? this.message_list.rows : {}, |
| | | count = 0; |
| | | |
| | | for (var i=0, len=a_uids.length; i<len; i++) { |
| | |
| | | |
| | | // make sure there are no selected rows |
| | | if (this.env.skip_deleted && this.message_list) { |
| | | if(!this.env.display_next) |
| | | if (!this.env.display_next) |
| | | this.message_list.clear_selection(); |
| | | if (count < 0) |
| | | post_data._count = (count*-1); |
| | |
| | | this.flag_deleted_as_read = function(uids) |
| | | { |
| | | var icn_src, uid, i, len, |
| | | rows = this.message_list ? this.message_list.rows : []; |
| | | rows = this.message_list ? this.message_list.rows : {}; |
| | | |
| | | uids = String(uids).split(','); |
| | | |
| | |
| | | message = input_message.val(), |
| | | is_html = ($("input[name='_is_html']").val() == '1'), |
| | | sig = this.env.identity, |
| | | delim = this.env.recipients_delimiter, |
| | | delim = this.env.recipients_separator, |
| | | rx_delim = RegExp.escape(delim), |
| | | headers = ['replyto', 'bcc']; |
| | | |
| | | // update reply-to/bcc fields with addresses defined in identities |
| | |
| | | } |
| | | |
| | | // cleanup |
| | | rx = new RegExp(RegExp.escape(delim) + '\\s*' + RegExp(delim), 'g'); |
| | | input_val = input_val.replace(rx, delim) |
| | | rx = new RegExp('^\\s*' + RegExp.escape(delim) + '\\s*$'); |
| | | input_val = input_val.replace(rx, '') |
| | | rx = new RegExp(rx_delim + '\\s*' + rx_delim, 'g'); |
| | | input_val = input_val.replace(rx, delim); |
| | | rx = new RegExp('^[\\s' + rx_delim + ']+'); |
| | | input_val = input_val.replace(rx, ''); |
| | | |
| | | // add new address(es) |
| | | if (new_val) { |
| | | rx = new RegExp(RegExp.escape(delim) + '\\s*$'); |
| | | if (input_val && !rx.test(input_val)) |
| | | input_val += delim + ' '; |
| | | if (new_val && input_val.indexOf(new_val) == -1 && input_val.indexOf(new_val.replace(/"/g, '')) == -1) { |
| | | if (input_val) { |
| | | rx = new RegExp('[' + rx_delim + '\\s]+$') |
| | | input_val = input_val.replace(rx, '') + delim + ' '; |
| | | } |
| | | |
| | | input_val += new_val + delim + ' '; |
| | | } |
| | | |
| | |
| | | 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); |
| | | var indicator, li = $('<li>'); |
| | | |
| | | li.attr('id', name) |
| | | .addClass(att.classname) |
| | | .html(att.html) |
| | | .on('mouseover', function() { rcube_webmail.long_subject_title_ex(this, 0); }); |
| | | |
| | | // replace indicator's li |
| | | if (upload_id && (indicator = document.getElementById(upload_id))) { |
| | |
| | | this.env.search_id = null; |
| | | }; |
| | | |
| | | this.sent_successfully = function(type, msg, target) |
| | | this.sent_successfully = function(type, msg, folders) |
| | | { |
| | | this.display_message(msg, type); |
| | | |
| | |
| | | this.lock_form(this.gui_objects.messageform); |
| | | if (rc) { |
| | | rc.display_message(msg, type); |
| | | // refresh the folder where sent message was saved |
| | | if (target && rc.env.task == 'mail' && rc.env.action == '' && rc.env.mailbox == target) |
| | | rc.command('checkmail'); |
| | | // refresh the folder where sent message was saved or replied message comes from |
| | | if (folders && rc.env.task == 'mail' && rc.env.action == '' && $.inArray(rc.env.mailbox, folders) >= 0) { |
| | | // @TODO: try with 'checkmail' here when #1485186 is fixed. See also #1489249. |
| | | rc.command('list'); |
| | | } |
| | | } |
| | | setTimeout(function(){ window.close() }, 1000); |
| | | } |
| | |
| | | p = inp_value.lastIndexOf(this.env.recipients_separator, cpos-1), |
| | | q = inp_value.substring(p+1, cpos), |
| | | min = this.env.autocomplete_min_length, |
| | | ac = this.ksearch_data; |
| | | data = this.ksearch_data; |
| | | |
| | | // trim query string |
| | | q = $.trim(q); |
| | |
| | | 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 <= 0) && this.env.contacts && !this.env.contacts.length) |
| | | if (old_value && old_value.length && q.indexOf(old_value) == 0 && (!data || data.num <= 0) && this.env.contacts && !this.env.contacts.length) |
| | | return; |
| | | |
| | | var i, lock, source, xhr, reqid = new Date().getTime(), |
| | | post_data = {_search: q, _id: reqid}, |
| | | threads = props && props.threads ? props.threads : 1, |
| | | sources = props && props.sources ? props.sources : [], |
| | | action = props && props.action ? props.action : 'mail/autocomplete'; |
| | | var sources = props && props.sources ? props.sources : ['']; |
| | | var reqid = this.multi_thread_http_request({ |
| | | items: sources, |
| | | threads: props && props.threads ? props.threads : 1, |
| | | action: props && props.action ? props.action : 'mail/autocomplete', |
| | | postdata: { _search:q, _source:'%s' }, |
| | | lock: this.display_message(this.get_label('searching'), 'loading') |
| | | }); |
| | | |
| | | this.ksearch_data = {id: reqid, sources: sources.slice(), action: action, |
| | | locks: [], requests: [], num: sources.length}; |
| | | |
| | | for (i=0; i<threads; i++) { |
| | | source = this.ksearch_data.sources.shift(); |
| | | if (threads > 1 && source === undefined) |
| | | break; |
| | | |
| | | post_data._source = source ? source : ''; |
| | | lock = this.display_message(this.get_label('searching'), 'loading'); |
| | | xhr = this.http_post(action, post_data, lock); |
| | | |
| | | this.ksearch_data.locks.push(lock); |
| | | this.ksearch_data.requests.push(xhr); |
| | | } |
| | | this.ksearch_data = { id:reqid, sources:sources.slice(), num:sources.length }; |
| | | }; |
| | | |
| | | this.ksearch_query_results = function(results, search, reqid) |
| | | { |
| | | // trigger multi-thread http response callback |
| | | this.multi_thread_http_response(results, reqid); |
| | | |
| | | // search stopped in meantime? |
| | | if (!this.ksearch_value) |
| | | return; |
| | |
| | | // display search results |
| | | var i, len, ul, li, text, init, |
| | | value = this.ksearch_value, |
| | | data = this.ksearch_data, |
| | | maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15; |
| | | |
| | | // create results pane if not present |
| | |
| | | if (len) |
| | | this.env.contacts = this.env.contacts.concat(results); |
| | | |
| | | // run next parallel search |
| | | if (data.id == reqid) { |
| | | data.num--; |
| | | if (maxlen > 0 && data.sources.length) { |
| | | var lock, xhr, source = data.sources.shift(), post_data; |
| | | if (source) { |
| | | post_data = {_search: value, _id: reqid, _source: source}; |
| | | lock = this.display_message(this.get_label('searching'), 'loading'); |
| | | xhr = this.http_post(data.action, post_data, lock); |
| | | |
| | | this.ksearch_data.locks.push(lock); |
| | | this.ksearch_data.requests.push(xhr); |
| | | } |
| | | } |
| | | else if (!maxlen) { |
| | | if (!this.ksearch_msg) |
| | | this.ksearch_msg = this.display_message(this.get_label('autocompletemore')); |
| | | // abort pending searches |
| | | this.ksearch_abort(); |
| | | } |
| | | } |
| | | if (this.ksearch_data.id == reqid) |
| | | this.ksearch_data.num--; |
| | | }; |
| | | |
| | | this.ksearch_click = function(node) |
| | |
| | | // Clears autocomplete data/requests |
| | | this.ksearch_destroy = function() |
| | | { |
| | | this.ksearch_abort(); |
| | | if (this.ksearch_data) |
| | | this.multi_thread_request_abort(this.ksearch_data.id); |
| | | |
| | | if (this.ksearch_info) |
| | | this.hide_message(this.ksearch_info); |
| | |
| | | this.ksearch_data = null; |
| | | this.ksearch_info = null; |
| | | this.ksearch_msg = null; |
| | | } |
| | | |
| | | // Aborts pending autocomplete requests |
| | | this.ksearch_abort = function() |
| | | { |
| | | var i, len, ac = this.ksearch_data; |
| | | |
| | | if (!ac) |
| | | return; |
| | | |
| | | for (i=0, len=ac.locks.length; i<len; i++) |
| | | this.abort_request({request: ac.requests[i], lock: ac.locks[i]}); |
| | | }; |
| | | |
| | | |
| | |
| | | boxtitle.append(' » '); |
| | | } |
| | | |
| | | boxtitle.append($('<span>'+prop.name+'</span>')); |
| | | boxtitle.append($('<span>').text(prop.name)); |
| | | } |
| | | |
| | | this.triggerEvent('groupupdate', prop); |
| | |
| | | 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); |
| | |
| | | clearTimeout(this.submit_timer); |
| | | }; |
| | | |
| | | /** |
| | | Send multi-threaded parallel HTTP requests to the server for a list if items. |
| | | The string '%' in either a GET query or POST parameters will be replaced with the respective item value. |
| | | This is the argument object expected: { |
| | | items: ['foo','bar','gna'], // list of items to send requests for |
| | | action: 'task/some-action', // Roudncube action to call |
| | | query: { q:'%s' }, // GET query parameters |
| | | postdata: { source:'%s' }, // POST data (sends a POST request if present) |
| | | threads: 3, // max. number of concurrent requests |
| | | onresponse: function(data){ }, // Callback function called for every response received from server |
| | | whendone: function(alldata){ } // Callback function called when all requests have been sent |
| | | } |
| | | */ |
| | | this.multi_thread_http_request = function(prop) |
| | | { |
| | | var reqid = new Date().getTime(); |
| | | |
| | | prop.reqid = reqid; |
| | | prop.running = 0; |
| | | prop.requests = []; |
| | | prop.result = []; |
| | | prop._items = $.extend([], prop.items); // copy items |
| | | |
| | | if (!prop.lock) |
| | | prop.lock = this.display_message(this.get_label('loading'), 'loading'); |
| | | |
| | | // add the request arguments to the jobs pool |
| | | this.http_request_jobs[reqid] = prop; |
| | | |
| | | // start n threads |
| | | var item, threads = prop.threads || 1; |
| | | for (var i=0; i < threads; i++) { |
| | | item = prop._items.shift(); |
| | | if (item === undefined) |
| | | break; |
| | | |
| | | prop.running++; |
| | | prop.requests.push(this.multi_thread_send_request(prop, item)); |
| | | } |
| | | |
| | | return reqid; |
| | | }; |
| | | |
| | | // helper method to send an HTTP request with the given iterator value |
| | | this.multi_thread_send_request = function(prop, item) |
| | | { |
| | | var postdata, query; |
| | | |
| | | // replace %s in post data |
| | | if (prop.postdata) { |
| | | postdata = {}; |
| | | for (var k in prop.postdata) { |
| | | postdata[k] = String(prop.postdata[k]).replace('%s', item); |
| | | } |
| | | postdata._reqid = prop.reqid; |
| | | } |
| | | // replace %s in query |
| | | else if (typeof prop.query == 'string') { |
| | | query = prop.query.replace('%s', item); |
| | | query += '&_reqid=' + prop.reqid; |
| | | } |
| | | else if (typeof prop.query == 'object' && prop.query) { |
| | | query = {}; |
| | | for (var k in prop.query) { |
| | | query[k] = String(prop.query[k]).replace('%s', item); |
| | | } |
| | | query._reqid = prop.reqid; |
| | | } |
| | | |
| | | // send HTTP GET or POST request |
| | | return postdata ? this.http_post(prop.action, postdata) : this.http_request(prop.action, query); |
| | | }; |
| | | |
| | | // callback function for multi-threaded http responses |
| | | this.multi_thread_http_response = function(data, reqid) |
| | | { |
| | | var prop = this.http_request_jobs[reqid]; |
| | | if (!prop || prop.running <= 0 || prop.cancelled) |
| | | return; |
| | | |
| | | prop.running--; |
| | | |
| | | // trigger response callback |
| | | if (prop.onresponse && typeof prop.onresponse == 'function') { |
| | | prop.onresponse(data); |
| | | } |
| | | |
| | | prop.result = $.extend(prop.result, data); |
| | | |
| | | // send next request if prop.items is not yet empty |
| | | var item = prop._items.shift(); |
| | | if (item !== undefined) { |
| | | prop.running++; |
| | | prop.requests.push(this.multi_thread_send_request(prop, item)); |
| | | } |
| | | // trigger whendone callback and mark this request as done |
| | | else if (prop.running == 0) { |
| | | if (prop.whendone && typeof prop.whendone == 'function') { |
| | | prop.whendone(prop.result); |
| | | } |
| | | |
| | | this.set_busy(false, '', prop.lock); |
| | | |
| | | // remove from this.http_request_jobs pool |
| | | delete this.http_request_jobs[reqid]; |
| | | } |
| | | }; |
| | | |
| | | // abort a running multi-thread request with the given identifier |
| | | this.multi_thread_request_abort = function(reqid) |
| | | { |
| | | var prop = this.http_request_jobs[reqid]; |
| | | if (prop) { |
| | | for (var i=0; prop.running > 0 && i < prop.requests.length; i++) { |
| | | if (prop.requests[i].abort) |
| | | prop.requests[i].abort(); |
| | | } |
| | | |
| | | prop.running = 0; |
| | | prop.cancelled = true; |
| | | this.set_busy(false, '', prop.lock); |
| | | } |
| | | }; |
| | | |
| | | // post the given form to a hidden iframe |
| | | this.async_upload_form = function(form, action, onload) |
| | | { |
| | |
| | | if (!elem.title) { |
| | | var $elem = $(elem); |
| | | if ($elem.width() + indent * 15 > $elem.parent().width()) |
| | | elem.title = $elem.html(); |
| | | elem.title = $elem.text(); |
| | | } |
| | | }; |
| | | |
| | | rcube_webmail.long_subject_title_ie = function(elem, indent) |
| | | rcube_webmail.long_subject_title_ex = function(elem, indent) |
| | | { |
| | | if (!elem.title) { |
| | | var $elem = $(elem), |