From 8458c7ca0e787db4a04fb1a6264e15153fd173b3 Mon Sep 17 00:00:00 2001 From: thomascube <thomas@roundcube.net> Date: Sun, 23 Jan 2011 13:10:40 -0500 Subject: [PATCH] When creating a new contact, add it to the selected group; consider the selected group when deleting a contact --- program/js/app.js | 374 ++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 298 insertions(+), 76 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index b21435e..1c8c4c3 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -319,7 +319,20 @@ if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) { this.enable_command('save', true); - $("input[type='text']").first().select(); + this.enable_command('upload-photo', this.env.coltypes.photo ? true : false); + this.enable_command('delete-photo', this.env.coltypes.photo && this.env.action == 'edit'); + + for (var 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; + }); + + $("input[type='text']").first().focus(); } else if (this.gui_objects.qsearchbox) { this.enable_command('search', 'reset-search', 'moveto', true); @@ -639,6 +652,9 @@ input_email.focus(); break; } + + // clear empty input fields + $('input.placeholder').each(function(){ if (this.value == this._placeholder) this.value = ''; }); } this.gui_objects.editform.submit(); @@ -996,25 +1012,23 @@ case 'export': if (this.contact_list.rowcount > 0) { - var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : ''); - if (this.env.search_request) - add_url += '_search='+this.env.search_request; - - this.goto_url('export', add_url); + this.goto_url('export', { _source:this.env.source, _gid:this.env.group, _search:this.env.search_request }); } + break; + + case 'upload-photo': + this.upload_contact_photo(props); + break; + + case 'delete-photo': + this.replace_contact_photo('-del-'); break; // user settings commands case 'preferences': - this.goto_url(''); - break; - case 'identities': - this.goto_url('settings/identities'); - break; - case 'folders': - this.goto_url('settings/folders'); + this.goto_url('settings/' + command); break; // unified command call (command name == function name) @@ -1158,7 +1172,7 @@ this.is_framed = function() { - return (this.env.framed && parent.rcmail); + return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command); }; @@ -1701,7 +1715,7 @@ var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show'; var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'; cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+uid+'"'+ - ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>'; + ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')">'+cols.subject+'</a>'; } // add each submitted col @@ -3177,27 +3191,7 @@ // create hidden iframe and post upload form if (send) { - var ts = new Date().getTime(); - var frame_name = 'rcmupload'+ts; - - // have to do it this way for IE - // otherwise the form will be posted to a new window - if (document.all) { - var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; - document.body.insertAdjacentHTML('BeforeEnd',html); - } - else { // for standards-compilant browsers - var frame = document.createElement('iframe'); - frame.name = frame_name; - frame.style.border = 'none'; - frame.style.width = 0; - frame.style.height = 0; - frame.style.visibility = 'hidden'; - document.body.appendChild(frame); - } - - // handle upload errors, parsing iframe content in onload - $(frame_name).bind('load', {ts:ts}, function(e) { + this.async_upload_form(form, 'upload', function(e) { var d, content = ''; try { if (this.contentDocument) { @@ -3217,11 +3211,6 @@ if (bw.opera) rcmail.env.uploadframe = e.data.ts; }); - - form.target = frame_name; - form.action = this.env.comm_path+'&_action=upload&_uploadid='+ts; - form.setAttribute('enctype', 'multipart/form-data'); - form.submit(); // display upload indicator and cancel button var content = this.get_label('uploading'); @@ -3777,6 +3766,9 @@ 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; @@ -3976,6 +3968,165 @@ this.env.contactfolders[key].name = this.env.contactgroups[key].name = prop.name; this.triggerEvent('group_update', { id:prop.id, source:prop.source, name:prop.name, li:li[0] }); + }; + + + this.init_edit_field = function(col, elem) + { + if (!elem) + elem = $('.ff_' + col); + + elem.focus(function(){ ref.focus_textfield(this); }) + .blur(function(){ ref.blur_textfield(this); }) + .each(function(){ this._placeholder = ref.env.coltypes[col].label; ref.blur_textfield(this); }); + }; + + this.insert_edit_field = function(col, section, menu) + { + // just make pre-defined input field visible + var elem = $('#ff_'+col); + if (elem.length) { + elem.show().focus(); + $(menu).children('option[value="'+col+'"]').attr('disabled', true); + } + else { + var lastelem = $('.ff_'+col), + appendcontainer = $('#contactsection'+section+' .contactcontroller'+col); + + if (!appendcontainer.length) + appendcontainer = $('<fieldset>').addClass('contactfieldgroup contactcontroller'+col).insertAfter($('#contactsection'+section+' .contactfieldgroup').last()); + + if (appendcontainer.length && appendcontainer.get(0).nodeName == 'FIELDSET') { + var input, colprop = this.env.coltypes[col], + row = $('<div>').addClass('row'), + cell = $('<div>').addClass('contactfieldcontent data'), + label = $('<div>').addClass('contactfieldlabel label'); + + if (colprop.subtypes_select) + label.html(colprop.subtypes_select); + else + label.html(colprop.label); + + var name_suffix = colprop.limit != 1 ? '[]' : ''; + if (colprop.type == 'text' || colprop.type == 'date') { + input = $('<input>') + .addClass('ff_'+col) + .attr('type', 'text') + .attr('name', '_'+col+name_suffix) + .attr('size', colprop.size) + .appendTo(cell); + + this.init_edit_field(col, input); + } + else if (colprop.type == 'composite') { + var childcol, cp, first; + for (var childcol in colprop.childs) { + cp = colprop.childs[childcol]; + input = $('<input>') + .addClass('ff_'+childcol) + .attr('type', 'text') + .attr('name', '_'+childcol+name_suffix) + .attr('size', cp.size) + .appendTo(cell); + cell.append(" "); + this.init_edit_field(childcol, input); + if (!first) first = input; + } + input = first; // set focus to the first of this composite fields + } + else if (colprop.type == 'select') { + input = $('<select>') + .addClass('ff_'+col) + .attr('name', '_'+col+name_suffix) + .appendTo(cell); + + var options = input.attr('options'); + options[options.length] = new Option('---', ''); + if (colprop.options) + $.each(colprop.options, function(i, val){ options[options.length] = new Option(val, i); }); + } + + if (input) { + var delbutton = $('<a href="#del"></a>') + .addClass('contactfieldbutton deletebutton') + .attr('title', this.get_label('delete')) + .attr('rel', col) + .html(this.env.delbutton) + .click(function(){ ref.delete_edit_field(this); return false }) + .appendTo(cell); + + row.append(label).append(cell).appendTo(appendcontainer.show()); + input.first().focus(); + + // disable option if limit reached + if (!colprop.count) colprop.count = 0; + if (++colprop.count == colprop.limit && colprop.limit) + $(menu).children('option[value="'+col+'"]').attr('disabled', true); + } + } + } + }; + + this.delete_edit_field = function(elem) + { + var col = $(elem).attr('rel'), + colprop = this.env.coltypes[col], + fieldset = $(elem).parents('fieldset.contactfieldgroup'), + addmenu = fieldset.parent().find('select.addfieldmenu'); + + // just clear input but don't hide the last field + if (--colprop.count <= 0 && colprop.visible) + $(elem).parent().children('input').val('').blur(); + else { + $(elem).parents('div.row').remove(); + // hide entire fieldset if no more rows + if (!fieldset.children('div.row').length) + fieldset.hide(); + } + + // enable option in add-field selector or insert it if necessary + if (addmenu.length) { + var option = addmenu.children('option[value="'+col+'"]'); + if (option.length) + option.attr('disabled', false); + else + option = $('<option>').attr('value', col).html(colprop.label).appendTo(addmenu); + addmenu.show(); + } + }; + + + this.upload_contact_photo = function(form) + { + if (form && form.elements._photo.value) { + this.async_upload_form(form, 'upload-photo', function(e) { + rcmail.set_busy(false, null, rcmail.photo_upload_id); + }); + + // display upload indicator + this.photo_upload_id = this.set_busy(true, 'uploading'); + } + }; + + this.replace_contact_photo = function(id) + { + $('#ff_photo').val(id); + + var buttons = this.buttons['upload-photo']; + for (var n=0; n < buttons.length; n++) + $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto')); + + 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.gui_objects.contactphoto).children('img').attr('src', img_src); + + this.enable_command('delete-photo', id != '-del-'); + }; + + this.photo_upload_end = function() + { + this.set_busy(false, null, this.photo_upload_id); + delete this.photo_upload_id; }; @@ -4505,6 +4656,23 @@ } }; + + this.focus_textfield = function(elem) + { + elem._hasfocus = true; + var $elem = $(elem); + if ($elem.hasClass('placeholder') || $elem.val() == elem._placeholder) + $elem.val('').removeClass('placeholder').attr('spellcheck', true); + }; + + this.blur_textfield = function(elem) + { + elem._hasfocus = false; + var $elem = $(elem); + if (elem._placeholder && (!$elem.val() || $elem.val() == elem._placeholder)) + $elem.addClass('placeholder').attr('spellcheck', false).val(elem._placeholder); + }; + // write to the document/window title this.set_pagetitle = function(title) { @@ -4674,6 +4842,7 @@ } th.appendChild(tr); thead.parentNode.replaceChild(th, thead); + thead = th; } for (n=0, len=this.env.coltypes.length; n<len; n++) { @@ -4748,11 +4917,14 @@ // update the mailbox count display this.set_unread_count_display = function(mbox, set_title) { - var reg, text_obj, item, mycount, childcount, div; + var reg, link, text_obj, item, mycount, childcount, div; if (item = this.get_folder_li(mbox)) { mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0; - text_obj = item.getElementsByTagName('a')[0]; + link = $(item).children('a').eq(0); + text_obj = link.children('span.unreadcount'); + if (!text_obj.length && mycount) + text_obj = $('<span>').addClass('unreadcount').appendTo(link); reg = /\s+\([0-9]+\)$/i; childcount = 0; @@ -4764,12 +4936,10 @@ childcount += this.env.unread_counts[k]; } - if (mycount && text_obj.innerHTML.match(reg)) - text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+mycount+')'); - else if (mycount) - text_obj.innerHTML += ' ('+mycount+')'; - else - text_obj.innerHTML = text_obj.innerHTML.replace(reg, ''); + if (mycount && text_obj.length) + text_obj.html(' ('+mycount+')'); + else if (text_obj.length) + text_obj.remove(); // set parent's display reg = new RegExp(RegExp.escape(this.env.delimiter) + '[^' + RegExp.escape(this.env.delimiter) + ']+$'); @@ -4951,6 +5121,39 @@ /********************************************************/ /********* remote request methods *********/ /********************************************************/ + + // compose a valid url with the given parameters + this.url = function(action, query) + { + var querystring = typeof(query) == 'string' ? '&' + query : ''; + + if (typeof action != 'string') + query = action; + else if (!query || typeof(query) != 'object') + query = {}; + + if (action) + query._action = action; + else + query._action = this.env.action; + + var base = this.env.comm_path; + + // overwrite task name + if (query._action.match(/([a-z]+)\/([a-z-_]+)/)) { + query._action = RegExp.$2; + base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); + } + + // remove undefined values + var param = {}; + for (var k in query) { + if (typeof(query[k]) != 'undefined' && query[k] !== null) + param[k] = query[k]; + } + + return base + '&' + $.param(param) + querystring; + }; this.redirect = function(url, lock) { @@ -4965,28 +5168,13 @@ this.goto_url = function(action, query, lock) { - var url = this.env.comm_path, - querystring = query ? '&'+query : ''; - - // overwrite task name - if (action.match(/([a-z]+)\/([a-z-_]+)/)) { - action = RegExp.$2; - url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); - } - - this.redirect(url+'&_action='+action+querystring, lock); + this.redirect(this.url(action, query)); }; // send a http request to the server this.http_request = function(action, query, lock) { - var url = this.env.comm_path; - - // overwrite task name - if (action.match(/([a-z]+)\/([a-z-_]+)/)) { - action = RegExp.$2; - url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); - } + var url = this.url(action, query); // trigger plugin hook var result = this.triggerEvent('request'+action, query); @@ -4999,7 +5187,7 @@ query = result; } - url += '&_remote=1&_action=' + action + (query ? '&' : '') + query; + url += '&_remote=1'; // send request console.log('HTTP GET: ' + url); @@ -5013,15 +5201,7 @@ // send a http POST request to the server this.http_post = function(action, postdata, lock) { - var url = this.env.comm_path; - - // overwrite task name - if (action.match(/([a-z]+)\/([a-z-_]+)/)) { - action = RegExp.$2; - url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); - } - - url += '&_action=' + action; + var url = this.url(action); if (postdata && typeof(postdata) == 'object') { postdata._remote = 1; @@ -5168,6 +5348,37 @@ this.display_message(this.get_label('servererror') + ' (' + errmsg + ')', 'error'); }; + // post the given form to a hidden iframe + this.async_upload_form = function(form, action, onload) + { + var ts = new Date().getTime(); + var frame_name = 'rcmupload'+ts; + + // have to do it this way for IE + // otherwise the form will be posted to a new window + if (document.all) { + var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; + document.body.insertAdjacentHTML('BeforeEnd', html); + } + else { // for standards-compilant browsers + var frame = document.createElement('iframe'); + frame.name = frame_name; + frame.style.border = 'none'; + frame.style.width = 0; + frame.style.height = 0; + frame.style.visibility = 'hidden'; + document.body.appendChild(frame); + } + + // handle upload errors, parsing iframe content in onload + $(frame_name).bind('load', {ts:ts}, onload); + + form.target = frame_name; + form.action = this.url(action, { _uploadid:ts }); + form.setAttribute('enctype', 'multipart/form-data'); + form.submit(); + }; + // starts interval for keep-alive/check-recent signal this.start_keepalive = function() { @@ -5296,6 +5507,17 @@ } // end object rcube_webmail + +// some static methods +rcube_webmail.long_subject_title = function(elem, indent) +{ + if (!elem.title) { + var $elem = $(elem); + if ($elem.width() + indent * 15 > $elem.parent().width()) + elem.title = $elem.html(); + } +}; + // copy event engine prototype rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener; -- Gitblit v1.9.1