| | |
| | | if (this.gui_objects.folderlist) |
| | | this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups); |
| | | |
| | | this.enable_command('add', 'import', this.env.writable_source); |
| | | this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true); |
| | | |
| | | if (this.gui_objects.contactslist) { |
| | | |
| | | this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, |
| | |
| | | } |
| | | |
| | | this.update_group_commands(); |
| | | this.command('list'); |
| | | } |
| | | |
| | | this.set_page_buttons(); |
| | |
| | | if (this.env.action == 'add' || this.env.action == 'edit') |
| | | this.init_contact_form(); |
| | | } |
| | | |
| | | if (this.gui_objects.qsearchbox) { |
| | | this.enable_command('search', 'reset-search', 'moveto', true); |
| | | } |
| | | |
| | | if (this.contact_list && this.contact_list.rowcount > 0) |
| | | this.enable_command('export', true); |
| | | |
| | | this.enable_command('add', 'import', this.env.writable_source); |
| | | this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true); |
| | | |
| | | // load contacts of selected source |
| | | if (!this.env.action) |
| | | this.command('list', this.env.source); |
| | | break; |
| | | |
| | | |
| | | case 'settings': |
| | | this.enable_command('preferences', 'identities', 'save', 'folders', true); |
| | |
| | | $('#rcmloginpwd').focus(); |
| | | |
| | | // detect client timezone |
| | | $('#rcmlogintz').val(new Date().getTimezoneOffset() / -60); |
| | | var tz = new Date().getTimezoneOffset() / -60; |
| | | var stdtz = new Date().getStdTimezoneOffset() / -60; |
| | | $('#rcmlogintz').val(stdtz); |
| | | $('#rcmlogindst').val(tz > stdtz ? 1 : 0); |
| | | |
| | | // display 'loading' message on form submit, lock submit button |
| | | $('form').submit(function () { |
| | |
| | | // execute a specific command on the web client |
| | | this.command = function(command, props, obj) |
| | | { |
| | | var ret; |
| | | |
| | | if (obj && obj.blur) |
| | | obj.blur(); |
| | | |
| | |
| | | |
| | | // process external commands |
| | | if (typeof this.command_handlers[command] === 'function') { |
| | | var ret = this.command_handlers[command](props, obj); |
| | | ret = this.command_handlers[command](props, obj); |
| | | return ret !== undefined ? ret : (obj ? false : true); |
| | | } |
| | | else if (typeof this.command_handlers[command] === 'string') { |
| | | var ret = window[this.command_handlers[command]](props, obj); |
| | | ret = window[this.command_handlers[command]](props, obj); |
| | | return ret !== undefined ? ret : (obj ? false : true); |
| | | } |
| | | |
| | | // trigger plugin hooks |
| | | this.triggerEvent('actionbefore', {props:props, action:command}); |
| | | var ret = this.triggerEvent('before'+command, props); |
| | | ret = this.triggerEvent('before'+command, props); |
| | | if (ret !== undefined) { |
| | | // abort if one the handlers returned false |
| | | // abort if one of the handlers returned false |
| | | if (ret === false) |
| | | return false; |
| | | else |
| | | props = ret; |
| | | } |
| | | |
| | | ret = undefined; |
| | | |
| | | // process internal command |
| | | switch (command) { |
| | |
| | | } |
| | | |
| | | if (a_cids.length) |
| | | this.http_post('mailto', {_cid: a_cids.join(','), _source: this.env.source}, true); |
| | | this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source}, true); |
| | | else if (this.env.group) |
| | | this.http_post('mailto', { _gid: this.env.group, _source: this.env.source}, true); |
| | | |
| | | break; |
| | | } |
| | | else if (props) |
| | | url += '&_to='+urlencode(props); |
| | | |
| | | this.redirect(url); |
| | | break; |
| | |
| | | // unified command call (command name == function name) |
| | | default: |
| | | var func = command.replace(/-/g, '_'); |
| | | if (this[func] && typeof this[func] === 'function') |
| | | this[func](props); |
| | | if (this[func] && typeof this[func] === 'function') { |
| | | ret = this[func](props); |
| | | } |
| | | break; |
| | | } |
| | | |
| | | this.triggerEvent('after'+command, props); |
| | | if (this.triggerEvent('after'+command, props) === false) |
| | | ret = false; |
| | | this.triggerEvent('actionafter', {props:props, action:command}); |
| | | |
| | | return obj ? false : true; |
| | | return ret === false ? false : obj ? false : true; |
| | | }; |
| | | |
| | | // set command(s) enabled or disabled |
| | |
| | | { |
| | | var model, list, li, 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(); |
| | |
| | | if (!this.gui_objects.messagelist || !this.message_list) |
| | | return false; |
| | | |
| | | // Prevent from adding messages from different folder (#1487752) |
| | | if (flags.mbox != this.env.mailbox && !flags.skip_mbox_check) |
| | | return false; |
| | | |
| | | if (!this.env.messages[uid]) |
| | | this.env.messages[uid] = {}; |
| | | |
| | |
| | | flags: flags.extra_flags |
| | | }); |
| | | |
| | | var c, html, tree = expando = '', |
| | | var c, n, col, html, tree = '', expando = '', |
| | | list = this.message_list, |
| | | rows = list.rows, |
| | | tbody = this.gui_objects.messagelist.tBodies[0], |
| | | rowcount = tbody.rows.length, |
| | | even = rowcount%2, |
| | | message = this.env.messages[uid], |
| | | css_class = 'message' |
| | | + (even ? ' even' : ' odd') |
| | | + (!flags.seen ? ' unread' : '') |
| | | + (flags.deleted ? ' deleted' : '') |
| | | + (flags.flagged ? ' flagged' : '') |
| | | + (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '') |
| | | + (message.selected ? ' selected' : ''), |
| | | // for performance use DOM instead of jQuery here |
| | | row = document.createElement('tr'), |
| | | col = document.createElement('td'); |
| | | row = document.createElement('tr'); |
| | | |
| | | row.id = 'rcmrow'+uid; |
| | | row.className = css_class; |
| | |
| | | |
| | | // threads |
| | | if (this.env.threading) { |
| | | // This assumes that div width is hardcoded to 15px, |
| | | var width = message.depth * 15; |
| | | if (message.depth) { |
| | | // This assumes that div width is hardcoded to 15px, |
| | | tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + (message.depth * 15) + 'px;"> </span>'; |
| | | |
| | | if ((rows[message.parent_uid] && rows[message.parent_uid].expanded === false) |
| | | || ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) && |
| | | (!rows[message.parent_uid] || !rows[message.parent_uid].expanded)) |
| | |
| | | if (message.expanded === undefined && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) { |
| | | message.expanded = true; |
| | | } |
| | | } |
| | | |
| | | if (width) |
| | | tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + width + 'px;"> </span>'; |
| | | |
| | | if (message.has_children && !message.depth) |
| | | expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '"> </div>'; |
| | | } |
| | | } |
| | | |
| | | tree += '<span id="msgicn'+uid+'" class="'+css_class+'"> </span>'; |
| | |
| | | } |
| | | |
| | | // add each submitted col |
| | | for (var n in this.env.coltypes) { |
| | | for (n in this.env.coltypes) { |
| | | c = this.env.coltypes[n]; |
| | | col = document.createElement('td'); |
| | | col.className = String(c).toLowerCase(); |
| | |
| | | // list messages of a specific mailbox using filter |
| | | this.filter_mailbox = function(filter) |
| | | { |
| | | var search, lock = this.set_busy(true, 'searching'); |
| | | |
| | | if (this.gui_objects.qsearchbox) |
| | | search = this.gui_objects.qsearchbox.value; |
| | | var lock = this.set_busy(true, 'searching'); |
| | | |
| | | this.clear_message_list(); |
| | | |
| | | // reset vars |
| | | this.env.current_page = 1; |
| | | this.http_request('search', '_filter='+filter |
| | | + (search ? '&_q='+urlencode(search) : '') |
| | | + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), lock); |
| | | this.http_request('search', this.search_params(false, filter), lock); |
| | | }; |
| | | |
| | | // list messages of a specific mailbox |
| | |
| | | }; |
| | | |
| | | // Initializes threads indicators/expanders after list update |
| | | this.init_threads = function(roots) |
| | | this.init_threads = function(roots, mbox) |
| | | { |
| | | // #1487752 |
| | | if (mbox && mbox != this.env.mailbox) |
| | | return false; |
| | | |
| | | for (var n=0, len=roots.length; n<len; n++) |
| | | this.add_tree_icons(roots[n]); |
| | | this.expand_threads(); |
| | |
| | | this.qsearch = function(value) |
| | | { |
| | | if (value != '') { |
| | | var n, r, addurl = '', mods_arr = [], |
| | | mods = this.env.search_mods, |
| | | mbox = this.env.mailbox, |
| | | lock = this.set_busy(true, 'searching'); |
| | | var n, lock = this.set_busy(true, 'searching'); |
| | | |
| | | if (this.message_list) { |
| | | if (this.message_list) |
| | | this.clear_message_list(); |
| | | if (mods) |
| | | mods = mods[mbox] ? mods[mbox] : mods['*']; |
| | | } else if (this.contact_list) { |
| | | else if (this.contact_list) |
| | | this.list_contacts_clear(); |
| | | } |
| | | |
| | | // reset vars |
| | | this.env.current_page = 1; |
| | | r = this.http_request('search', this.search_params(value) |
| | | + (this.env.source ? '&_source='+urlencode(this.env.source) : '') |
| | | + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock); |
| | | |
| | | this.env.qsearch = {lock: lock, request: r}; |
| | | } |
| | | }; |
| | | |
| | | // build URL params for search |
| | | this.search_params = function(search, filter) |
| | | { |
| | | var n, url = [], mods_arr = [], |
| | | mods = this.env.search_mods, |
| | | mbox = this.env.mailbox; |
| | | |
| | | if (!filter && this.gui_objects.search_filter) |
| | | filter = this.gui_objects.search_filter.value; |
| | | |
| | | if (!search && this.gui_objects.qsearchbox) |
| | | search = this.gui_objects.qsearchbox.value; |
| | | |
| | | if (filter) |
| | | url.push('_filter=' + urlencode(filter)); |
| | | |
| | | if (search) { |
| | | url.push('_q='+urlencode(search)); |
| | | |
| | | if (mods && this.message_list) |
| | | mods = mods[mbox] ? mods[mbox] : mods['*']; |
| | | |
| | | if (mods) { |
| | | for (n in mods) |
| | | mods_arr.push(n); |
| | | addurl += '&_headers='+mods_arr.join(','); |
| | | url.push('_headers='+mods_arr.join(',')); |
| | | } |
| | | |
| | | if (this.gui_objects.search_filter) |
| | | addurl += '&_filter=' + this.gui_objects.search_filter.value; |
| | | |
| | | // reset vars |
| | | this.env.current_page = 1; |
| | | r = this.http_request('search', '_q='+urlencode(value) |
| | | + (mbox ? '&_mbox='+urlencode(mbox) : '') |
| | | + (this.env.source ? '&_source='+urlencode(this.env.source) : '') |
| | | + (this.env.group ? '&_gid='+urlencode(this.env.group) : '') |
| | | + (addurl ? addurl : ''), lock); |
| | | |
| | | this.env.qsearch = {lock: lock, request: r}; |
| | | } |
| | | |
| | | if (mbox) |
| | | url.push('_mbox='+urlencode(mbox)); |
| | | |
| | | return url.join('&'); |
| | | }; |
| | | |
| | | // reset quick-search form |
| | |
| | | var cpos = this.get_caret_pos(this.ksearch_input), |
| | | p = inp_value.lastIndexOf(',', cpos-1), |
| | | q = inp_value.substring(p+1, cpos), |
| | | min = this.env.autocomplete_min_length; |
| | | min = this.env.autocomplete_min_length, |
| | | ac = this.ksearch_data; |
| | | |
| | | // trim query string |
| | | q = $.trim(q); |
| | |
| | | // Don't (re-)search if the last results are still active |
| | | if (q == this.ksearch_value) |
| | | return; |
| | | |
| | | this.ksearch_destroy(); |
| | | |
| | | if (q.length && q.length < min) { |
| | | if (!this.ksearch_info) { |
| | |
| | | |
| | | var old_value = this.ksearch_value; |
| | | this.ksearch_value = q; |
| | | this.ksearch_destroy(); |
| | | |
| | | // ...string is empty |
| | | if (!q.length) |
| | | return; |
| | | |
| | | // ...new search value contains old one and previous search result was empty |
| | | if (old_value && old_value.length && this.env.contacts && !this.env.contacts.length && q.indexOf(old_value) == 0) |
| | | // ...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) |
| | | return; |
| | | |
| | | var i, lock, source, xhr, reqid = new Date().getTime(), |
| | |
| | | sources = props && props.sources ? props.sources : [], |
| | | action = props && props.action ? props.action : 'mail/autocomplete'; |
| | | |
| | | this.ksearch_data = {id: reqid, sources: sources.slice(), action: action, locks: [], requests: []}; |
| | | 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(); |
| | |
| | | return; |
| | | |
| | | // display search results |
| | | var p, ul, li, text, init, s_val = this.ksearch_value, |
| | | var 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 |
| | |
| | | for (i=0; i < results.length && maxlen > 0; i++) { |
| | | text = typeof results[i] === 'object' ? results[i].name : results[i]; |
| | | li = document.createElement('LI'); |
| | | li.innerHTML = text.replace(new RegExp('('+RegExp.escape(s_val)+')', 'ig'), '##$1%%').replace(/</g, '<').replace(/>/g, '>').replace(/##([^%]+)%%/g, '<b>$1</b>'); |
| | | li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(/</g, '<').replace(/>/g, '>').replace(/##([^%]+)%%/g, '<b>$1</b>'); |
| | | li.onmouseover = function(){ ref.ksearch_select(this); }; |
| | | li.onmouseup = function(){ ref.ksearch_click(this) }; |
| | | li._rcm_id = this.env.contacts.length + i; |
| | |
| | | this.env.contacts = this.env.contacts.concat(results); |
| | | |
| | | // run next parallel search |
| | | if (this.ksearch_data.id == reqid) { |
| | | if (maxlen > 0 && this.ksearch_data.sources.length) { |
| | | var lock, xhr, props = this.ksearch_data, source = props.sources.shift(); |
| | | if (data.id == reqid) { |
| | | data.num--; |
| | | if (maxlen > 0 && data.sources.length) { |
| | | var lock, xhr, source = data.sources.shift(); |
| | | if (source) { |
| | | lock = this.display_message(this.get_label('searching'), 'loading'); |
| | | xhr = this.http_post(props.action, '_search='+urlencode(s_val)+'&_id='+reqid |
| | | xhr = this.http_post(data.action, '_search='+urlencode(value)+'&_id='+reqid |
| | | +'&_source='+urlencode(source), lock); |
| | | |
| | | this.ksearch_data.locks.push(lock); |
| | | this.ksearch_data.requests.push(xhr); |
| | | } |
| | | } |
| | | else if (!maxlen && !this.ksearch_msg) |
| | | this.ksearch_msg = this.display_message(this.get_label('autocompletemore')); |
| | | else if (!maxlen) { |
| | | if (!this.ksearch_msg) |
| | | this.ksearch_msg = this.display_message(this.get_label('autocompletemore')); |
| | | // abort pending searches |
| | | this.ksearch_abort(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | |
| | | this.ksearch_destroy(); |
| | | }; |
| | | |
| | | // Aborts pending autocomplete requests |
| | | // Clears autocomplete data/requests |
| | | this.ksearch_destroy = 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]}); |
| | | this.ksearch_abort(); |
| | | |
| | | if (this.ksearch_info) |
| | | this.hide_message(this.ksearch_info); |
| | |
| | | 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]}); |
| | | }; |
| | | |
| | | |
| | | /*********************************************************/ |
| | | /********* address book methods *********/ |
| | |
| | | } |
| | | } |
| | | |
| | | this.enable_command('compose', 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); |
| | | |
| | |
| | | { |
| | | this.contact_list.clear(true); |
| | | this.show_contentframe(false); |
| | | this.enable_command('delete', 'compose', false); |
| | | this.enable_command('delete', false); |
| | | this.enable_command('compose', this.env.group ? true : false); |
| | | }; |
| | | |
| | | // load contact record |
| | |
| | | this.delete_contacts = function() |
| | | { |
| | | // exit if no mailbox specified or if selection is empty |
| | | var selection = this.contact_list.get_selection(); |
| | | var undelete = this.env.address_sources[this.env.source].undelete; |
| | | var selection = this.contact_list.get_selection(), |
| | | undelete = this.env.address_sources[this.env.source].undelete; |
| | | |
| | | if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm')))) |
| | | return; |
| | | |
| | |
| | | qs += '&_search='+this.env.search_request; |
| | | |
| | | // send request to server |
| | | this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs); |
| | | this.http_post('delete', '_cid='+urlencode(a_cids.join(',')) |
| | | +'&_source='+urlencode(this.env.source) |
| | | +'&_from='+(this.env.action ? this.env.action : '')+qs, |
| | | this.display_message(this.get_label('contactdeleting'), 'loading')); |
| | | |
| | | return true; |
| | | }; |
| | |
| | | // add row to contacts list |
| | | this.add_contact_row = function(cid, cols, select) |
| | | { |
| | | if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0]) |
| | | if (!this.gui_objects.contactslist) |
| | | return false; |
| | | |
| | | var tbody = this.gui_objects.contactslist.tBodies[0], |
| | | rowcount = tbody.rows.length, |
| | | even = rowcount%2, |
| | | var c, list = this.contact_list, |
| | | row = document.createElement('tr'); |
| | | |
| | | row.id = 'rcmrow'+String(cid).replace(this.identifier_expr, '_'); |
| | | row.className = 'contact '+(even ? 'even' : 'odd'); |
| | | row.className = 'contact'; |
| | | |
| | | if (this.contact_list.in_selection(cid)) |
| | | if (list.in_selection(cid)) |
| | | row.className += ' selected'; |
| | | |
| | | // add each submitted col |
| | | for (var c in cols) { |
| | | for (c in cols) { |
| | | col = document.createElement('td'); |
| | | col.className = String(c).toLowerCase(); |
| | | col.innerHTML = cols[c]; |
| | | row.appendChild(col); |
| | | } |
| | | |
| | | this.contact_list.insert_row(row); |
| | | list.insert_row(row); |
| | | |
| | | this.enable_command('export', (this.contact_list.rowcount > 0)); |
| | | this.enable_command('export', list.rowcount > 0); |
| | | }; |
| | | |
| | | this.init_contact_form = function() |
| | |
| | | ref.insert_edit_field($(this).val(), $(this).attr('rel'), this); |
| | | this.selectedIndex = 0; |
| | | }); |
| | | |
| | | // enable date pickers on date fields |
| | | if ($.datepicker && this.env.date_format) { |
| | | $.datepicker.setDefaults({ |
| | | dateFormat: this.env.date_format, |
| | | changeMonth: true, |
| | | changeYear: true, |
| | | yearRange: '-100:+10', |
| | | showOtherMonths: true, |
| | | selectOtherMonths: true, |
| | | monthNamesShort: this.env.month_names, |
| | | onSelect: function(dateText) { $(this).focus().val(dateText) } |
| | | }); |
| | | $('input.datepicker').datepicker(); |
| | | } |
| | | |
| | | $("input[type='text']:visible").first().focus(); |
| | | }; |
| | |
| | | |
| | | elem.focus(function(){ ref.focus_textfield(this); }) |
| | | .blur(function(){ ref.blur_textfield(this); }) |
| | | .each(function(){ this._placeholder = this.title = ref.env.coltypes[col].label; ref.blur_textfield(this); }); |
| | | .each(function(){ this._placeholder = this.title = (ref.env.coltypes[col].label || ''); ref.blur_textfield(this); }); |
| | | }; |
| | | |
| | | this.insert_edit_field = function(col, section, menu) |
| | |
| | | .appendTo(cell); |
| | | |
| | | this.init_edit_field(col, input); |
| | | |
| | | if (colprop.type == 'date' && $.datepicker) |
| | | input.datepicker(); |
| | | } |
| | | else if (colprop.type == 'composite') { |
| | | var childcol, cp, first, templ, cols = [], suffices = []; |
| | |
| | | }; |
| | | |
| | | // replace content of row count display |
| | | this.set_rowcount = function(text) |
| | | this.set_rowcount = function(text, mbox) |
| | | { |
| | | // #1487752 |
| | | if (mbox && mbox != this.env.mailbox) |
| | | return false; |
| | | |
| | | $(this.gui_objects.countdisplay).html(text); |
| | | |
| | | // update page navigation buttons |