+ }
+ 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_click = function(node)
+ {
+ if (this.ksearch_input)
+ this.ksearch_input.focus();
+
+ this.insert_recipient(node._rcm_id);
+ this.ksearch_hide();
+ };
+
+ this.ksearch_blur = function()
+ {
+ if (this.ksearch_timer)
+ clearTimeout(this.ksearch_timer);
+
+ this.ksearch_input = null;
+ this.ksearch_hide();
+ };
+
+ this.ksearch_hide = function()
+ {
+ this.ksearch_selected = null;
+ this.ksearch_value = '';
+
+ if (this.ksearch_pane)
+ this.ksearch_pane.hide();
+
+ this.ksearch_destroy();
+ };
+
+ // Clears autocomplete data/requests
+ this.ksearch_destroy = function()
+ {
+ this.ksearch_abort();
+
+ if (this.ksearch_info)
+ this.hide_message(this.ksearch_info);
+
+ if (this.ksearch_msg)
+ this.hide_message(this.ksearch_msg);
+
+ 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]});
+ };
+
+
+ /*********************************************************/
+ /********* address book methods *********/
+ /*********************************************************/
+
+ this.contactlist_keypress = function(list)
+ {
+ if (list.key_pressed == list.DELETE_KEY)
+ this.command('delete');
+ };
+
+ this.contactlist_select = function(list)
+ {
+ if (this.preview_timer)
+ clearTimeout(this.preview_timer);
+
+ var n, id, sid, ref = this, writable = false,
+ 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);
+ else if (this.env.contentframe)
+ this.show_contentframe(false);
+
+ // no source = search result, we'll need to detect if any of
+ // selected contacts are in writable addressbook to enable edit/delete
+ if (list.selection.length) {
+ if (!source) {
+ for (n in list.selection) {
+ sid = String(list.selection[n]).replace(/^[^-]+-/, '');
+ if (sid && this.env.address_sources[sid] && !this.env.address_sources[sid].readonly) {
+ writable = true;
+ break;
+ }
+ }
+ }
+ else {
+ writable = !source.readonly;
+ }
+ }
+
+ 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);
+
+ return false;
+ };
+
+ this.list_contacts = function(src, group, page)
+ {
+ var folder, add_url = '',
+ target = window;
+
+ if (!src)
+ src = this.env.source;
+
+ if (page && this.current_page == page && src == this.env.source && group == this.env.group)
+ return false;
+
+ if (src != this.env.source) {
+ page = this.env.current_page = 1;
+ this.reset_qsearch();
+ }
+ else if (group != this.env.group)
+ page = this.env.current_page = 1;
+
+ if (this.env.search_id)
+ folder = 'S'+this.env.search_id;
+ else
+ folder = group ? 'G'+src+group : src;
+
+ this.select_folder(folder);
+
+ this.env.source = src;
+ this.env.group = group;
+
+ // load contacts remotely
+ if (this.gui_objects.contactslist) {
+ this.list_contacts_remote(src, group, page);
+ return;
+ }
+
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+ target = window.frames[this.env.contentframe];
+ add_url = '&_framed=1';
+ }
+
+ if (group)
+ add_url += '&_gid='+group;
+ if (page)
+ add_url += '&_page='+page;
+
+ // also send search request to get the correct listing
+ if (this.env.search_request)
+ add_url += '&_search='+this.env.search_request;
+
+ this.set_busy(true, 'loading');
+ this.location_href(this.env.comm_path + (src ? '&_source='+urlencode(src) : '') + add_url, target);
+ };
+
+ // send remote request to load contacts list
+ this.list_contacts_remote = function(src, group, page)
+ {
+ // clear message list first
+ this.list_contacts_clear();
+
+ // send request to server
+ var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : ''),
+ lock = this.set_busy(true, 'loading');
+
+ this.env.source = src;
+ this.env.group = group;
+
+ if (group)
+ url += '&_gid='+group;
+
+ // also send search request to get the right messages
+ if (this.env.search_request)
+ url += '&_search='+this.env.search_request;
+
+ this.http_request('list', url, lock);
+ };
+
+ this.list_contacts_clear = function()
+ {
+ this.contact_list.clear(true);
+ this.show_contentframe(false);
+ this.enable_command('delete', false);
+ this.enable_command('compose', this.env.group ? true : false);
+ };
+
+ // load contact record
+ this.load_contact = function(cid, action, framed)
+ {
+ var add_url = '', target = window;
+
+ if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+ add_url = '&_framed=1';
+ target = window.frames[this.env.contentframe];
+ this.show_contentframe(true);
+
+ // load dummy content
+ if (!cid) {
+ // unselect selected row(s)
+ this.contact_list.clear_selection();
+ this.enable_command('delete', 'compose', false);
+ }
+ }
+ else if (framed)
+ return false;
+
+ if (action && (cid || action=='add') && !this.drag_active) {
+ if (this.env.group)
+ add_url += '&_gid='+urlencode(this.env.group);
+
+ this.location_href(this.env.comm_path+'&_action='+action
+ +'&_source='+urlencode(this.env.source)
+ +'&_cid='+urlencode(cid) + add_url, target, true);
+ }
+ return true;
+ };
+
+ // add/delete member to/from the group
+ this.group_member_change = function(what, cid, source, gid)
+ {
+ what = what == 'add' ? 'add' : 'del';
+ var lock = this.display_message(this.get_label(what == 'add' ? 'addingmember' : 'removingmember'), 'loading');
+
+ this.http_post('group-'+what+'members', '_cid='+urlencode(cid)
+ + '&_source='+urlencode(source)
+ + '&_gid='+urlencode(gid), lock);
+ };
+
+ // copy a contact to the specified target (group or directory)
+ this.copy_contact = function(cid, to)
+ {
+ if (!cid)
+ cid = this.contact_list.get_selection().join(',');
+
+ if (to.type == 'group' && to.source == this.env.source)
+ this.group_member_change('add', cid, to.source, to.id);
+ else if (to.type == 'group' && !this.env.address_sources[to.source].readonly) {
+ var lock = this.display_message(this.get_label('copyingcontact'), 'loading');
+ this.http_post('copy', '_cid='+urlencode(cid)
+ + '&_source='+urlencode(this.env.source)
+ + '&_to='+urlencode(to.source)
+ + '&_togid='+urlencode(to.id)
+ + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock);
+ }
+ else if (to.id != this.env.source && cid && this.env.address_sources[to.id] && !this.env.address_sources[to.id].readonly) {
+ var lock = this.display_message(this.get_label('copyingcontact'), 'loading');
+ this.http_post('copy', '_cid='+urlencode(cid)
+ + '&_source='+urlencode(this.env.source)
+ + '&_to='+urlencode(to.id)
+ + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock);
+ }
+ };
+
+ this.delete_contacts = function()
+ {
+ var selection = this.contact_list.get_selection(),
+ undelete = this.env.source && this.env.address_sources[this.env.source].undelete;
+
+ // exit if no mailbox specified or if selection is empty
+ if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm'))))
+ return;
+
+ var id, n, a_cids = [], qs = '';
+
+ if (this.env.cid)
+ a_cids.push(this.env.cid);
+ else {
+ for (n=0; n<selection.length; n++) {
+ id = selection[n];
+ a_cids.push(id);
+ this.contact_list.remove_row(id, (n == selection.length-1));
+ }
+
+ // hide content frame if we delete the currently displayed contact
+ if (selection.length == 1)
+ this.show_contentframe(false);
+ }
+
+ if (this.env.group)
+ qs += '&_gid='+urlencode(this.env.group);
+
+ // also send search request to get the right records from the next page
+ if (this.env.search_request)
+ 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.display_message(this.get_label('contactdeleting'), 'loading'));
+
+ return true;
+ };
+
+ // update a contact record in the list
+ this.update_contact_row = function(cid, cols_arr, newcid, source)
+ {
+ var c, row, list = this.contact_list;
+
+ cid = this.html_identifier(cid);
+
+ // when in searching mode, concat cid with the source name
+ if (!list.rows[cid]) {
+ cid = cid+'-'+source;
+ if (newcid)
+ newcid = newcid+'-'+source;
+ }
+
+ if (list.rows[cid] && (row = list.rows[cid].obj)) {
+ for (c=0; c<cols_arr.length; c++)
+ if (row.cells[c])
+ $(row.cells[c]).html(cols_arr[c]);
+
+ // cid change
+ if (newcid) {
+ newcid = this.html_identifier(newcid);
+ row.id = 'rcmrow' + newcid;
+ list.remove_row(cid);
+ list.init_row(row);
+ list.selection[0] = newcid;
+ row.style.display = '';
+ }
+ }
+ };
+
+ // add row to contacts list
+ this.add_contact_row = function(cid, cols, select)
+ {
+ if (!this.gui_objects.contactslist)
+ return false;
+
+ var c, list = this.contact_list,
+ row = document.createElement('tr');
+
+ row.id = 'rcmrow'+this.html_identifier(cid);
+ row.className = 'contact';
+
+ if (list.in_selection(cid))
+ row.className += ' selected';
+
+ // add each submitted col
+ for (c in cols) {
+ col = document.createElement('td');
+ col.className = String(c).toLowerCase();
+ col.innerHTML = cols[c];
+ row.appendChild(col);
+ }
+
+ list.insert_row(row);
+
+ this.enable_command('export', list.rowcount > 0);
+ };
+
+ this.init_contact_form = function()
+ {
+ var ref = this, col;
+
+ this.set_photo_actions($('#ff_photo').val());
+
+ for (col in this.env.coltypes)
+ this.init_edit_field(col, null);
+
+ $('.contactfieldgroup .row a.deletebutton').click(function() {
+ ref.delete_edit_field(this);
+ return false;
+ });
+
+ $('select.addfieldmenu').change(function(e) {
+ 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();
+ };
+
+ this.group_create = function()
+ {
+ this.add_input_row('contactgroup');
+ };
+
+ this.group_rename = function()
+ {
+ if (!this.env.group || !this.gui_objects.folderlist)
+ return;
+
+ if (!this.name_input) {
+ this.enable_command('list', 'listgroup', false);
+ this.name_input = $('<input>').attr('type', 'text').val(this.env.contactgroups['G'+this.env.source+this.env.group].name);
+ this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); });
+ this.env.group_renaming = true;
+
+ var link, li = this.get_folder_li(this.env.source+this.env.group, 'rcmliG');
+ if (li && (link = li.firstChild)) {
+ $(link).hide().before(this.name_input);
+ }
+ }
+
+ this.name_input.select().focus();
+ };
+
+ this.group_delete = function()
+ {
+ if (this.env.group && confirm(this.get_label('deletegroupconfirm'))) {
+ var lock = this.set_busy(true, 'groupdeleting');
+ this.http_post('group-delete', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(this.env.group), lock);
+ }
+ };
+
+ // callback from server upon group-delete command
+ this.remove_group_item = function(prop)
+ {
+ var li, key = 'G'+prop.source+prop.id;
+ if ((li = this.get_folder_li(key))) {
+ this.triggerEvent('group_delete', { source:prop.source, id:prop.id, li:li });
+
+ li.parentNode.removeChild(li);
+ delete this.env.contactfolders[key];
+ delete this.env.contactgroups[key];
+ }
+
+ this.list_contacts(prop.source, 0);
+ };
+
+ // @TODO: maybe it would be better to use popup instead of inserting input to the list?
+ this.add_input_row = function(type)
+ {
+ if (!this.gui_objects.folderlist)
+ return;
+
+ if (!this.name_input) {
+ this.name_input = $('<input>').attr('type', 'text').data('tt', type);
+ this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); });
+ this.name_input_li = $('<li>').addClass(type).append(this.name_input);
+
+ var li = type == 'contactsearch' ? $('li:last', this.gui_objects.folderlist) : this.get_folder_li(this.env.source);
+ this.name_input_li.insertAfter(li);
+ }
+
+ this.name_input.select().focus();
+ };
+
+ // handler for keyboard events on the input field
+ this.add_input_keydown = function(e)
+ {
+ var key = rcube_event.get_keycode(e),
+ input = $(e.target), itype = input.data('tt');
+
+ // enter
+ if (key == 13) {
+ var newname = input.val();
+
+ if (newname) {
+ var lock = this.set_busy(true, 'loading');
+
+ if (itype == 'contactsearch')
+ this.http_post('search-create', '_search='+urlencode(this.env.search_request)+'&_name='+urlencode(newname), lock);
+ else if (this.env.group_renaming)
+ this.http_post('group-rename', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(this.env.group)+'&_name='+urlencode(newname), lock);