| | |
| | | return ref.command('sort', $(this).attr('rel'), this); |
| | | }); |
| | | |
| | | this.gui_objects.messagelist.parentNode.onclick = function(e){ return ref.click_on_list(e || window.event); }; |
| | | |
| | | this.enable_command('toggle_status', 'toggle_flag', 'sort', true); |
| | | this.enable_command('set-listmode', this.env.threads && !this.is_multifolder_listing()); |
| | | |
| | |
| | | .addEventListener('dragend', function(e) { ref.drag_end(e); }) |
| | | .init(); |
| | | |
| | | this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return ref.click_on_list(e); }; |
| | | |
| | | $(this.gui_objects.qsearchbox).focusin(function() { ref.contact_list.blur(); }); |
| | | |
| | | this.update_group_commands(); |
| | |
| | | // select first input field in an edit form |
| | | if (this.gui_objects.editform) |
| | | $("input,select,textarea", this.gui_objects.editform) |
| | | .not(':hidden').not(':disabled').first().select(); |
| | | .not(':hidden').not(':disabled').first().select().focus(); |
| | | |
| | | // unset contentframe variable if preview_pane is enabled |
| | | if (this.env.contentframe && !$('#' + this.env.contentframe).is(':visible')) |
| | |
| | | |
| | | // remove copy from local storage if compose screen is left intentionally |
| | | this.remove_compose_data(this.env.compose_id); |
| | | this.compose_skip_unsavedcheck = true; |
| | | } |
| | | |
| | | this.last_command = command; |
| | | |
| | | // process external commands |
| | | if (typeof this.command_handlers[command] === 'function') { |
| | | ret = this.command_handlers[command](props, obj); |
| | | ret = this.command_handlers[command](props, obj, event); |
| | | return ret !== undefined ? ret : (obj ? false : true); |
| | | } |
| | | else if (typeof this.command_handlers[command] === 'string') { |
| | | ret = window[this.command_handlers[command]](props, obj); |
| | | ret = window[this.command_handlers[command]](props, obj, event); |
| | | return ret !== undefined ? ret : (obj ? false : true); |
| | | } |
| | | |
| | |
| | | |
| | | if (win) { |
| | | this.save_compose_form_local(); |
| | | this.compose_skip_unsavedcheck = true; |
| | | $("input[name='_action']", form).val('compose'); |
| | | form.action = this.url('mail/compose', { _id: this.env.compose_id, _extwin: 1 }); |
| | | form.target = win.name; |
| | |
| | | if (this.task == 'mail') { |
| | | url._mbox = this.env.mailbox; |
| | | if (props) |
| | | url._to = props; |
| | | url._to = props; |
| | | // also send search request so we can go back to search result after message is sent |
| | | if (this.env.search_request) |
| | | url._search = this.env.search_request; |
| | |
| | | break; |
| | | } |
| | | } |
| | | else if (props) |
| | | else if (props && typeof props == 'string') { |
| | | url._to = props; |
| | | } |
| | | else if (props && typeof props == 'object') { |
| | | $.extend(url, props); |
| | | } |
| | | |
| | | this.open_compose_step(url); |
| | | break; |
| | |
| | | |
| | | if (task == 'mail') |
| | | url += '&_mbox=INBOX'; |
| | | else if (task == 'logout') |
| | | else if (task == 'logout' && !this.env.server_error) |
| | | this.clear_compose_data(); |
| | | |
| | | this.redirect(url); |
| | |
| | | |
| | | this.is_framed = function() |
| | | { |
| | | return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command); |
| | | return this.env.framed && parent.rcmail && parent.rcmail != this && typeof parent.rcmail.command == 'function'; |
| | | }; |
| | | |
| | | this.save_pref = function(prop) |
| | |
| | | |
| | | return true; |
| | | } |
| | | |
| | | this.click_on_list = function(e) |
| | | { |
| | | if (this.gui_objects.qsearchbox) |
| | | this.gui_objects.qsearchbox.blur(); |
| | | |
| | | if (this.message_list) |
| | | this.message_list.focus(e); |
| | | else if (this.contact_list) |
| | | this.contact_list.focus(e); |
| | | |
| | | return true; |
| | | }; |
| | | |
| | | this.msglist_select = function(list) |
| | | { |
| | |
| | | } |
| | | |
| | | // check for locally stored compose data |
| | | if (window.localStorage) { |
| | | var key, formdata, index = this.local_storage_get_item('compose.index', []); |
| | | |
| | | for (i = 0; i < index.length; i++) { |
| | | key = index[i]; |
| | | formdata = this.local_storage_get_item('compose.' + key, null, true); |
| | | if (!formdata) { |
| | | continue; |
| | | } |
| | | // restore saved copy of current compose_id |
| | | if (formdata.changed && key == this.env.compose_id) { |
| | | this.restore_compose_form(key, html_mode); |
| | | break; |
| | | } |
| | | // skip records from 'other' drafts |
| | | if (this.env.draft_id && formdata.draft_id && formdata.draft_id != this.env.draft_id) { |
| | | continue; |
| | | } |
| | | // skip records on reply |
| | | if (this.env.reply_msgid && formdata.reply_msgid != this.env.reply_msgid) { |
| | | continue; |
| | | } |
| | | // show dialog asking to restore the message |
| | | if (formdata.changed && formdata.session != this.env.session_id) { |
| | | this.show_popup_dialog( |
| | | this.get_label('restoresavedcomposedata') |
| | | .replace('$date', new Date(formdata.changed).toLocaleString()) |
| | | .replace('$subject', formdata._subject) |
| | | .replace(/\n/g, '<br/>'), |
| | | this.get_label('restoremessage'), |
| | | [{ |
| | | text: this.get_label('restore'), |
| | | click: function(){ |
| | | ref.restore_compose_form(key, html_mode); |
| | | ref.remove_compose_data(key); // remove old copy |
| | | ref.save_compose_form_local(); // save under current compose_id |
| | | $(this).dialog('close'); |
| | | } |
| | | }, |
| | | { |
| | | text: this.get_label('delete'), |
| | | click: function(){ |
| | | ref.remove_compose_data(key); |
| | | $(this).dialog('close'); |
| | | } |
| | | }, |
| | | { |
| | | text: this.get_label('ignore'), |
| | | click: function(){ |
| | | $(this).dialog('close'); |
| | | } |
| | | }] |
| | | ); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | this.compose_restore_dialog(0, html_mode) |
| | | |
| | | if (input_to.val() == '') |
| | | input_to.focus(); |
| | |
| | | // start the auto-save timer |
| | | this.auto_save_start(); |
| | | }; |
| | | |
| | | this.compose_restore_dialog = function(j, html_mode) |
| | | { |
| | | var i, key, formdata, index = this.local_storage_get_item('compose.index', []); |
| | | |
| | | var show_next = function(i) { |
| | | if (++i < index.length) |
| | | ref.compose_restore_dialog(i, html_mode) |
| | | } |
| | | |
| | | for (i = j || 0; i < index.length; i++) { |
| | | key = index[i]; |
| | | formdata = this.local_storage_get_item('compose.' + key, null, true); |
| | | if (!formdata) { |
| | | continue; |
| | | } |
| | | // restore saved copy of current compose_id |
| | | if (formdata.changed && key == this.env.compose_id) { |
| | | this.restore_compose_form(key, html_mode); |
| | | break; |
| | | } |
| | | // skip records from 'other' drafts |
| | | if (this.env.draft_id && formdata.draft_id && formdata.draft_id != this.env.draft_id) { |
| | | continue; |
| | | } |
| | | // skip records on reply |
| | | if (this.env.reply_msgid && formdata.reply_msgid != this.env.reply_msgid) { |
| | | continue; |
| | | } |
| | | // show dialog asking to restore the message |
| | | if (formdata.changed && formdata.session != this.env.session_id) { |
| | | this.show_popup_dialog( |
| | | this.get_label('restoresavedcomposedata') |
| | | .replace('$date', new Date(formdata.changed).toLocaleString()) |
| | | .replace('$subject', formdata._subject) |
| | | .replace(/\n/g, '<br/>'), |
| | | this.get_label('restoremessage'), |
| | | [{ |
| | | text: this.get_label('restore'), |
| | | click: function(){ |
| | | ref.restore_compose_form(key, html_mode); |
| | | ref.remove_compose_data(key); // remove old copy |
| | | ref.save_compose_form_local(); // save under current compose_id |
| | | $(this).dialog('close'); |
| | | } |
| | | }, |
| | | { |
| | | text: this.get_label('delete'), |
| | | click: function(){ |
| | | ref.remove_compose_data(key); |
| | | $(this).dialog('close'); |
| | | show_next(i); |
| | | } |
| | | }, |
| | | { |
| | | text: this.get_label('ignore'), |
| | | click: function(){ |
| | | $(this).dialog('close'); |
| | | show_next(i); |
| | | } |
| | | }] |
| | | ); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.init_address_input_events = function(obj, props) |
| | | { |
| | |
| | | form._draft.value = draft ? '1' : ''; |
| | | form.action = this.add_url(form.action, '_unlock', msgid); |
| | | form.action = this.add_url(form.action, '_lang', lang); |
| | | form.action = this.add_url(form.action, '_framed', 1); |
| | | |
| | | // register timer to notify about connection timeout |
| | | this.submit_timer = setTimeout(function(){ |
| | |
| | | |
| | | // always remove local copy upon saving as draft |
| | | this.remove_compose_data(this.env.compose_id); |
| | | this.compose_skip_unsavedcheck = false; |
| | | }; |
| | | |
| | | this.auto_save_start = function() |
| | |
| | | ref.compose_type_activity_last = ref.compose_type_activity; |
| | | } |
| | | }, 5000); |
| | | |
| | | $(window).unload(function() { |
| | | // remove copy from local storage if compose screen is left after warning |
| | | if (!ref.env.server_error) |
| | | ref.remove_compose_data(ref.env.compose_id); |
| | | }); |
| | | } |
| | | |
| | | // check for unsaved changes before leaving the compose page |
| | | if (!window.onbeforeunload) { |
| | | window.onbeforeunload = function() { |
| | | if (!ref.compose_skip_unsavedcheck && ref.cmp_hash != ref.compose_field_hash()) { |
| | | return ref.get_label('notsentwarning'); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | // Unlock interface now that saving is complete |
| | |
| | | } |
| | | }); |
| | | |
| | | if (window.localStorage && !empty) { |
| | | if (!empty) { |
| | | var index = this.local_storage_get_item('compose.index', []), |
| | | key = this.env.compose_id; |
| | | |
| | | if ($.inArray(key, index) < 0) { |
| | | index.push(key); |
| | | } |
| | | this.local_storage_set_item('compose.' + key, formdata, true); |
| | | this.local_storage_set_item('compose.index', index); |
| | | if ($.inArray(key, index) < 0) { |
| | | index.push(key); |
| | | } |
| | | |
| | | this.local_storage_set_item('compose.' + key, formdata, true); |
| | | this.local_storage_set_item('compose.index', index); |
| | | } |
| | | }; |
| | | |
| | |
| | | // remove stored compose data from localStorage |
| | | this.remove_compose_data = function(key) |
| | | { |
| | | if (window.localStorage) { |
| | | var index = this.local_storage_get_item('compose.index', []); |
| | | var index = this.local_storage_get_item('compose.index', []); |
| | | |
| | | if ($.inArray(key, index) >= 0) { |
| | | this.local_storage_remove_item('compose.' + key); |
| | | this.local_storage_set_item('compose.index', $.grep(index, function(val,i) { return val != key; })); |
| | | } |
| | | if ($.inArray(key, index) >= 0) { |
| | | this.local_storage_remove_item('compose.' + key); |
| | | this.local_storage_set_item('compose.index', $.grep(index, function(val,i) { return val != key; })); |
| | | } |
| | | }; |
| | | |
| | | // clear all stored compose data of this user |
| | | this.clear_compose_data = function() |
| | | { |
| | | if (window.localStorage) { |
| | | var i, index = this.local_storage_get_item('compose.index', []); |
| | | var i, index = this.local_storage_get_item('compose.index', []); |
| | | |
| | | for (i=0; i < index.length; i++) { |
| | | this.local_storage_remove_item('compose.' + index[i]); |
| | | } |
| | | this.local_storage_remove_item('compose.index'); |
| | | for (i=0; i < index.length; i++) { |
| | | this.local_storage_remove_item('compose.' + index[i]); |
| | | } |
| | | |
| | | this.local_storage_remove_item('compose.index'); |
| | | }; |
| | | |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | var i, rx, |
| | | id = obj.options[obj.selectedIndex].value, |
| | | var id = obj.options[obj.selectedIndex].value, |
| | | sig = this.env.identity, |
| | | delim = this.env.recipients_separator, |
| | | rx_delim = RegExp.escape(delim), |
| | | headers = ['replyto', 'bcc']; |
| | | rx_delim = RegExp.escape(delim); |
| | | |
| | | // update reply-to/bcc fields with addresses defined in identities |
| | | for (i in headers) { |
| | | var key = headers[i], |
| | | old_val = sig && this.env.identities[sig] ? this.env.identities[sig][key] : '', |
| | | new_val = id && this.env.identities[id] ? this.env.identities[id][key] : '', |
| | | $.each(['replyto', 'bcc'], function() { |
| | | var rx, key = this, |
| | | old_val = sig && ref.env.identities[sig] ? ref.env.identities[sig][key] : '', |
| | | new_val = id && ref.env.identities[id] ? ref.env.identities[id][key] : '', |
| | | input = $('[name="_'+key+'"]'), input_val = input.val(); |
| | | |
| | | // remove old address(es) |
| | |
| | | |
| | | if (old_val || new_val) |
| | | input.val(input_val).change(); |
| | | } |
| | | }); |
| | | |
| | | // enable manual signature insert |
| | | if (this.env.signatures && this.env.signatures[id]) { |
| | |
| | | this.sent_successfully = function(type, msg, folders) |
| | | { |
| | | this.display_message(msg, type); |
| | | this.compose_skip_unsavedcheck = true; |
| | | |
| | | if (this.env.extwin) { |
| | | this.lock_form(this.gui_objects.messageform); |
| | |
| | | this.list_contacts = function(src, group, page) |
| | | { |
| | | var win, folder, url = {}, |
| | | refresh = src === undefined && group === undefined && page === undefined, |
| | | target = window; |
| | | |
| | | if (!src) |
| | |
| | | page = this.env.current_page = 1; |
| | | this.reset_qsearch(); |
| | | } |
| | | else if (group != this.env.group) |
| | | else if (!refresh && group != this.env.group) |
| | | page = this.env.current_page = 1; |
| | | |
| | | if (this.env.search_id) |
| | |
| | | if (action && (cid || action == 'add') && !this.drag_active) { |
| | | if (this.env.group) |
| | | url._gid = this.env.group; |
| | | |
| | | if (this.env.search_request) |
| | | url._search = this.env.search_request; |
| | | |
| | | url._action = action; |
| | | url._source = this.env.source; |
| | |
| | | .submit(function() { $('input.mainaction').click(); return false; }); |
| | | }; |
| | | |
| | | // group creation dialog |
| | | this.group_create = function() |
| | | { |
| | | this.add_input_row('contactgroup'); |
| | | var input = $('<input>').attr('type', 'text'), |
| | | content = $('<label>').text(this.get_label('namex')).append(input); |
| | | |
| | | this.show_popup_dialog(content, this.get_label('newgroup'), |
| | | [{ |
| | | text: this.get_label('save'), |
| | | click: function() { |
| | | var name; |
| | | |
| | | if (name = input.val()) { |
| | | ref.http_post('group-create', {_source: ref.env.source, _name: name}, |
| | | ref.set_busy(true, 'loading')); |
| | | } |
| | | |
| | | $(this).dialog('close'); |
| | | } |
| | | }] |
| | | ); |
| | | }; |
| | | |
| | | // group rename dialog |
| | | this.group_rename = function() |
| | | { |
| | | if (!this.env.group || !this.gui_objects.folderlist) |
| | | if (!this.env.group) |
| | | 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 ref.add_input_keydown(e); }); |
| | | this.env.group_renaming = true; |
| | | var group_name = this.env.contactgroups['G' + this.env.source + this.env.group].name, |
| | | input = $('<input>').attr('type', 'text').val(group_name), |
| | | content = $('<label>').text(this.get_label('namex')).append(input); |
| | | |
| | | var link, li = this.get_folder_li('G'+this.env.source+this.env.group,'',true); |
| | | if (li && (link = li.firstChild)) { |
| | | $(link).hide().before(this.name_input); |
| | | } |
| | | } |
| | | this.show_popup_dialog(content, this.get_label('grouprename'), |
| | | [{ |
| | | text: this.get_label('save'), |
| | | click: function() { |
| | | var name; |
| | | |
| | | this.name_input.select().focus(); |
| | | if ((name = input.val()) && name != group_name) { |
| | | ref.http_post('group-rename', {_source: ref.env.source, _gid: ref.env.group, _name: name}, |
| | | ref.set_busy(true, 'loading')); |
| | | } |
| | | |
| | | $(this).dialog('close'); |
| | | } |
| | | }], |
| | | {open: function() { input.select(); }} |
| | | ); |
| | | }; |
| | | |
| | | this.group_delete = function() |
| | |
| | | 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 ref.add_input_keydown(e); }); |
| | | this.name_input_li = $('<li>').addClass(type).append(this.name_input); |
| | | |
| | | var ul, li; |
| | | |
| | | // find list (UL) element |
| | | if (type == 'contactsearch') |
| | | ul = this.gui_objects.savedsearchlist; |
| | | else |
| | | ul = $('ul.groups', this.get_folder_li(this.env.source,'',true)); |
| | | |
| | | // append to the list |
| | | li = $('li:last', ul); |
| | | if (li.length) |
| | | this.name_input_li.insertAfter(li); |
| | | else { |
| | | this.name_input_li.appendTo(ul); |
| | | ul.show(); // make sure the list is visible |
| | | } |
| | | } |
| | | |
| | | this.name_input.select().focus(); |
| | | }; |
| | | |
| | | //remove selected contacts from current active group |
| | | this.group_remove_selected = function() |
| | | { |
| | |
| | | } |
| | | }; |
| | | |
| | | // 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: this.env.search_request, _name: newname}, lock); |
| | | else if (this.env.group_renaming) |
| | | this.http_post('group-rename', {_source: this.env.source, _gid: this.env.group, _name: newname}, lock); |
| | | else |
| | | this.http_post('group-create', {_source: this.env.source, _name: newname}, lock); |
| | | } |
| | | return false; |
| | | } |
| | | // escape |
| | | else if (key == 27) |
| | | this.reset_add_input(); |
| | | |
| | | return true; |
| | | }; |
| | | |
| | | this.reset_add_input = function() |
| | | { |
| | | if (this.name_input) { |
| | | var li = this.name_input.parent(); |
| | | if (this.env.group_renaming) { |
| | | li.children().last().show(); |
| | | this.env.group_renaming = false; |
| | | } |
| | | else if ($('li', li.parent()).length == 1) |
| | | li.parent().hide(); |
| | | |
| | | this.name_input.remove(); |
| | | |
| | | if (this.name_input_li) |
| | | this.name_input_li.remove(); |
| | | |
| | | this.name_input = this.name_input_li = null; |
| | | } |
| | | |
| | | this.enable_command('list', 'listgroup', true); |
| | | }; |
| | | |
| | | // callback for creating a new contact group |
| | | this.insert_contact_group = function(prop) |
| | | { |
| | | this.reset_add_input(); |
| | | |
| | | prop.type = 'group'; |
| | | |
| | | var key = 'G'+prop.source+prop.id, |
| | |
| | | // callback for renaming a contact group |
| | | this.update_contact_group = function(prop) |
| | | { |
| | | this.reset_add_input(); |
| | | |
| | | var key = 'G'+prop.source+prop.id, |
| | | newnode = {}; |
| | | |
| | |
| | | // callback for creating a new saved search record |
| | | this.insert_saved_search = function(name, id) |
| | | { |
| | | this.reset_add_input(); |
| | | |
| | | var key = 'S'+id, |
| | | link = $('<a>').attr('href', '#') |
| | | .attr('rel', id) |
| | |
| | | this.triggerEvent('abook_search_insert', prop); |
| | | }; |
| | | |
| | | // creates an input for saved search name |
| | | // creates a dialog for saved search |
| | | this.search_create = function() |
| | | { |
| | | this.add_input_row('contactsearch'); |
| | | var input = $('<input>').attr('type', 'text'), |
| | | content = $('<label>').text(this.get_label('namex')).append(input); |
| | | |
| | | this.show_popup_dialog(content, this.get_label('searchsave'), |
| | | [{ |
| | | text: this.get_label('save'), |
| | | click: function() { |
| | | var name; |
| | | |
| | | if (name = input.val()) { |
| | | ref.http_post('search-create', {_search: ref.env.search_request, _name: name}, |
| | | ref.set_busy(true, 'loading')); |
| | | } |
| | | |
| | | $(this).dialog('close'); |
| | | } |
| | | }] |
| | | ); |
| | | }; |
| | | |
| | | this.search_delete = function() |
| | |
| | | frame.location.href = this.env.blankpage; |
| | | } |
| | | } |
| | | |
| | | this.enable_command('delete', false); |
| | | }; |
| | | |
| | | this.remove_identity = function(id) |
| | |
| | | frame.location.href = this.env.blankpage; |
| | | } |
| | | } |
| | | |
| | | this.enable_command('delete', false); |
| | | }; |
| | | |
| | | |
| | |
| | | this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$'); |
| | | |
| | | this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, { |
| | | selectable: true |
| | | selectable: true, |
| | | tabexit: false, |
| | | id_prefix: 'rcmli', |
| | | id_encode: this.html_identifier_encode, |
| | | id_decode: this.html_identifier_decode, |
| | | searchbox: '#foldersearch' |
| | | }); |
| | | |
| | | this.subscription_list |
| | | .addEventListener('select', function(node) { ref.subscription_select(node.id); }) |
| | | .draggable({cancel: '#mailboxroot'}) |
| | | .addEventListener('collapse', function(node) { ref.folder_collapsed(node) }) |
| | | .addEventListener('expand', function(node) { ref.folder_collapsed(node) }) |
| | | .addEventListener('search', function(p) { if (p.query) ref.subscription_select(); }) |
| | | .draggable({cancel: 'li.mailbox.root'}) |
| | | .droppable({ |
| | | // @todo: find better way, accept callback is executed for every folder |
| | | // on the list when dragging starts (and stops), this is slow, but |
| | | // I didn't find a method to check droptarget on over event |
| | | accept: function(node) { |
| | | var source = ref.env.subscriptionrows[$(node).attr('id')], |
| | | dest = ref.env.subscriptionrows[this.id], |
| | | source_name = source[0], |
| | | dest_name = dest[0]; |
| | | var source_folder = ref.folder_id2name($(node).attr('id')), |
| | | dest_folder = ref.folder_id2name(this.id), |
| | | source = ref.env.subscriptionrows[source_folder], |
| | | dest = ref.env.subscriptionrows[dest_folder]; |
| | | |
| | | return !source[2] |
| | | && dest_name != source_name.replace(ref.last_sub_rx, '') |
| | | && !dest_name.startsWith(source_name + ref.env.delimiter); |
| | | return source && !source[2] |
| | | && dest_folder != source_folder.replace(ref.last_sub_rx, '') |
| | | && !dest_folder.startsWith(source_folder + ref.env.delimiter); |
| | | }, |
| | | drop: function(e, ui) { |
| | | ref.subscription_move_folder(ui.draggable.attr('id'), this.id); |
| | | var source = ref.folder_id2name(ui.draggable.attr('id')), |
| | | dest = ref.folder_id2name(this.id); |
| | | |
| | | ref.subscription_move_folder(source, dest); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | this.folder_id2name = function(id) |
| | | { |
| | | return ref.html_identifier_decode(id.replace(/^rcmli/, '')); |
| | | }; |
| | | |
| | | this.subscription_select = function(id) |
| | | { |
| | | var folder; |
| | | |
| | | if (id && id != 'mailboxroot' && (folder = this.env.subscriptionrows[id])) { |
| | | this.env.mailbox = folder[0]; |
| | | this.show_folder(folder[0]); |
| | | if (id && id != '*' && (folder = this.env.subscriptionrows[id])) { |
| | | this.env.mailbox = id; |
| | | this.show_folder(id); |
| | | this.enable_command('delete-folder', !folder[2]); |
| | | } |
| | | else { |
| | |
| | | |
| | | this.subscription_move_folder = function(from, to) |
| | | { |
| | | var source = this.env.subscriptionrows[from][0]; |
| | | dest = this.env.subscriptionrows[to][0]; |
| | | |
| | | if (source && dest !== null && source != dest && dest != source.replace(this.last_sub_rx, '')) { |
| | | var path = source.split(this.env.delimiter), |
| | | if (from && to !== null && from != to && to != from.replace(this.last_sub_rx, '')) { |
| | | var path = from.split(this.env.delimiter), |
| | | basename = path.pop(), |
| | | newname = dest === '' ? basename : dest + this.env.delimiter + basename; |
| | | newname = to === '' || to === '*' ? basename : to + this.env.delimiter + basename; |
| | | |
| | | if (newname != source) { |
| | | this.http_post('rename-folder', {_folder_oldname: source, _folder_newname: newname}, |
| | | if (newname != from) { |
| | | this.http_post('rename-folder', {_folder_oldname: from, _folder_newname: newname}, |
| | | this.set_busy(true, 'foldermoving')); |
| | | } |
| | | } |
| | |
| | | // delete a specific mailbox with all its messages |
| | | this.delete_folder = function(name) |
| | | { |
| | | var id = this.get_folder_row_id(name ? name : this.env.mailbox), |
| | | folder = this.env.subscriptionrows[id][0]; |
| | | if (!name) |
| | | name = this.env.mailbox; |
| | | |
| | | if (folder && confirm(this.get_label('deletefolderconfirm'))) { |
| | | this.http_post('delete-folder', {_mbox: folder}, this.set_busy(true, 'folderdeleting')); |
| | | if (name && confirm(this.get_label('deletefolderconfirm'))) { |
| | | this.http_post('delete-folder', {_mbox: name}, this.set_busy(true, 'folderdeleting')); |
| | | } |
| | | }; |
| | | |
| | | // Add folder row to the table and initialize it |
| | | this.add_folder_row = function (name, display_name, is_protected, subscribed, skip_init, class_name) |
| | | this.add_folder_row = function (id, name, display_name, is_protected, subscribed, class_name, refrow, subfolders) |
| | | { |
| | | if (!this.gui_objects.subscriptionlist) |
| | | return false; |
| | | |
| | | var row, n, tmp, tmp_name, rowid, collator, |
| | | folders = [], list = [], slist = [], |
| | | list_element = $(this.gui_objects.subscriptionlist), |
| | | refrow = $('li', list_element).get(1), |
| | | id = 'rcmli'+((new Date).getTime()); |
| | | // reset searching |
| | | if (this.subscription_list.is_search()) { |
| | | this.subscription_select(); |
| | | this.subscription_list.reset_search(); |
| | | } |
| | | |
| | | if (!refrow) { |
| | | // disable drag-n-drop temporarily |
| | | this.subscription_list.draggable('destroy').droppable('destroy'); |
| | | |
| | | var row, n, tmp, tmp_name, rowid, collator, pos, p, parent = '', |
| | | folders = [], list = [], slist = [], |
| | | list_element = $(this.gui_objects.subscriptionlist); |
| | | row = refrow ? refrow : $($('li', list_element).get(1)).clone(true); |
| | | |
| | | if (!row.length) { |
| | | // Refresh page if we don't have a table row to clone |
| | | this.goto_url('folders'); |
| | | return false; |
| | | } |
| | | |
| | | // clone a table row if there are existing rows |
| | | row = $(refrow).clone(true); |
| | | |
| | | // set ID, reset css class |
| | | row.attr({id: id, 'class': class_name}); |
| | | row.attr({id: 'rcmli' + this.html_identifier_encode(id), 'class': class_name}); |
| | | |
| | | if (!refrow || !refrow.length) { |
| | | // remove old data, subfolders and toggle |
| | | $('ul,div.treetoggle', row).remove(); |
| | | row.removeData('filtered'); |
| | | } |
| | | |
| | | // set folder name |
| | | $('.name', row).html(display_name); |
| | | $('a:first', row).text(display_name); |
| | | |
| | | // update subscription checkbox |
| | | $('input[name="_subscribed[]"]', row).val(name) |
| | | $('input[name="_subscribed[]"]:first', row).val(id) |
| | | .prop({checked: subscribed ? true : false, disabled: is_protected ? true : false}); |
| | | |
| | | // add to folder/row-ID map |
| | | this.env.subscriptionrows[id] = [name, display_name, false]; |
| | | |
| | | // copy folders data to an array for sorting |
| | | $.each(this.env.subscriptionrows, function(k, v) { folders.push(v); }); |
| | | $.each(this.env.subscriptionrows, function(k, v) { v[3] = k; folders.push(v); }); |
| | | |
| | | try { |
| | | // use collator if supported (FF29, IE11, Opera15, Chrome24) |
| | |
| | | folders.sort(function(a, b) { |
| | | var i, f1, f2, |
| | | path1 = a[0].split(ref.env.delimiter), |
| | | path2 = b[0].split(ref.env.delimiter); |
| | | path2 = b[0].split(ref.env.delimiter), |
| | | len = path1.length; |
| | | |
| | | for (i=0; i<path1.length; i++) { |
| | | for (i=0; i<len; i++) { |
| | | f1 = path1[i]; |
| | | f2 = path2[i]; |
| | | |
| | | if (f1 !== f2) { |
| | | if (f2 === undefined) |
| | | return 1; |
| | | if (collator) |
| | | return collator.compare(f1, f2); |
| | | else |
| | | return f1 < f2 ? -1 : 1; |
| | | } |
| | | else if (i == len-1) { |
| | | return -1 |
| | | } |
| | | } |
| | | }); |
| | | |
| | | for (n in folders) { |
| | | p = folders[n][3]; |
| | | // protected folder |
| | | if (folders[n][2]) { |
| | | tmp_name = folders[n][0] + this.env.delimiter; |
| | | tmp_name = p + this.env.delimiter; |
| | | // prefix namespace cannot have subfolders (#1488349) |
| | | if (tmp_name == this.env.prefix_ns) |
| | | continue; |
| | | slist.push(folders[n][0]); |
| | | slist.push(p); |
| | | tmp = tmp_name; |
| | | } |
| | | // protected folder's child |
| | | else if (tmp && folders[n][0].startsWith(tmp)) |
| | | slist.push(folders[n][0]); |
| | | else if (tmp && p.startsWith(tmp)) |
| | | slist.push(p); |
| | | // other |
| | | else { |
| | | list.push(folders[n][0]); |
| | | list.push(p); |
| | | tmp = null; |
| | | } |
| | | } |
| | | |
| | | // check if subfolder of a protected folder |
| | | for (n=0; n<slist.length; n++) { |
| | | if (name.startsWith(slist[n] + this.env.delimiter)) |
| | | rowid = this.get_folder_row_id(slist[n]); |
| | | if (id.startsWith(slist[n] + this.env.delimiter)) |
| | | rowid = slist[n]; |
| | | } |
| | | |
| | | // find folder position after sorting |
| | | for (n=0; !rowid && n<list.length; n++) { |
| | | if (n && list[n] == name) |
| | | rowid = this.get_folder_row_id(list[n-1]); |
| | | if (n && list[n] == id) |
| | | rowid = list[n-1]; |
| | | } |
| | | |
| | | // add row to the table |
| | | if (rowid) |
| | | $('#' + rowid).after(row); |
| | | else |
| | | if (rowid && (n = this.subscription_list.get_item(rowid, true))) { |
| | | // find parent folder |
| | | if (pos = id.lastIndexOf(this.env.delimiter)) { |
| | | parent = id.substring(0, pos); |
| | | parent = this.subscription_list.get_item(parent, true); |
| | | |
| | | // add required tree elements to the parent if not already there |
| | | if (!$('div.treetoggle', parent).length) { |
| | | $('<div> </div>').addClass('treetoggle collapsed').appendTo(parent); |
| | | } |
| | | if (!$('ul', parent).length) { |
| | | $('<ul>').css('display', 'none').appendTo(parent); |
| | | } |
| | | } |
| | | |
| | | if (parent && n == parent) { |
| | | $('ul:first', parent).append(row); |
| | | } |
| | | else { |
| | | while (p = $(n).parent().parent().get(0)) { |
| | | if (parent && p == parent) |
| | | break; |
| | | if (!$(p).is('li.mailbox')) |
| | | break; |
| | | n = p; |
| | | } |
| | | |
| | | $(n).after(row); |
| | | } |
| | | } |
| | | else { |
| | | list_element.append(row); |
| | | } |
| | | |
| | | // add subfolders |
| | | $.extend(this.env.subscriptionrows, subfolders || {}); |
| | | |
| | | // update list widget |
| | | this.subscription_list.select(); |
| | | this.subscription_list.reset(true); |
| | | this.subscription_select(); |
| | | |
| | | if (!skip_init) |
| | | this.init_subscription_list(); |
| | | // expand parent |
| | | if (parent) { |
| | | this.subscription_list.expand(this.folder_id2name(parent.id)); |
| | | } |
| | | |
| | | row = row.get(0); |
| | | row = row.show().get(0); |
| | | if (row.scrollIntoView) |
| | | row.scrollIntoView(); |
| | | |
| | |
| | | }; |
| | | |
| | | // replace an existing table row with a new folder line (with subfolders) |
| | | this.replace_folder_row = function(oldfolder, newfolder, display_name, is_protected, class_name) |
| | | this.replace_folder_row = function(oldid, id, name, display_name, is_protected, class_name) |
| | | { |
| | | if (!this.gui_objects.subscriptionlist) { |
| | | if (this.is_framed) |
| | | return parent.rcmail.replace_folder_row(oldfolder, newfolder, display_name, is_protected, class_name); |
| | | if (this.is_framed()) { |
| | | // @FIXME: for some reason this 'parent' variable need to be prefixed with 'window.' |
| | | return window.parent.rcmail.replace_folder_row(oldid, id, name, display_name, is_protected, class_name); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | var i, n, len, name, dispname, oldrow, tmprow, row, level, |
| | | folders = this.env.subscriptionrows, |
| | | id = this.get_folder_row_id(oldfolder), |
| | | prefix_len = oldfolder.length, |
| | | subscribed = $('input[name="_subscribed[]"]', $('#'+id)).prop('checked'), |
| | | // find subfolders of renamed folder |
| | | list = this.get_subfolders(oldfolder); |
| | | // reset searching |
| | | if (this.subscription_list.is_search()) { |
| | | this.subscription_select(); |
| | | this.subscription_list.reset_search(); |
| | | } |
| | | |
| | | var subfolders = {}, |
| | | row = this.subscription_list.get_item(oldid, true), |
| | | parent = $(row).parent(), |
| | | old_folder = this.env.subscriptionrows[oldid], |
| | | prefix_len_id = oldid.length, |
| | | prefix_len_name = old_folder[0].length, |
| | | subscribed = $('input[name="_subscribed[]"]:first', row).prop('checked'); |
| | | |
| | | // no renaming, only update class_name |
| | | if (oldfolder == newfolder) { |
| | | $('#'+id).attr('class', class_name || ''); |
| | | if (oldid == id) { |
| | | $(row).attr('class', class_name || ''); |
| | | return; |
| | | } |
| | | |
| | | // replace an existing table row |
| | | this._remove_folder_row(id); |
| | | row = $(this.add_folder_row(newfolder, display_name, is_protected, subscribed, true, class_name)); |
| | | // update subfolders |
| | | $('li', row).each(function() { |
| | | var fname = ref.folder_id2name(this.id), |
| | | folder = ref.env.subscriptionrows[fname], |
| | | newid = id + fname.slice(prefix_len_id); |
| | | |
| | | // detect tree depth change |
| | | if (len = list.length) { |
| | | level = (oldfolder.split(this.env.delimiter)).length - (newfolder.split(this.env.delimiter)).length; |
| | | this.id = 'rcmli' + ref.html_identifier_encode(newid); |
| | | $('input[name="_subscribed[]"]:first', this).val(newid); |
| | | folder[0] = name + folder[0].slice(prefix_len_name); |
| | | |
| | | subfolders[newid] = folder; |
| | | delete ref.env.subscriptionrows[fname]; |
| | | }); |
| | | |
| | | // get row off the list |
| | | row = $(row).detach(); |
| | | |
| | | delete this.env.subscriptionrows[oldid]; |
| | | |
| | | // remove parent list/toggle elements if not needed |
| | | if (parent.get(0) != this.gui_objects.subscriptionlist && !$('li', parent).length) { |
| | | $('ul,div.treetoggle', parent.parent()).remove(); |
| | | } |
| | | |
| | | // move subfolders to the new branch |
| | | for (n=0; n<len; n++) { |
| | | id = list[n]; |
| | | name = this.env.subscriptionrows[id][0]; |
| | | dispname = this.env.subscriptionrows[id][1]; |
| | | oldrow = $('#'+id); |
| | | tmprow = oldrow.clone(true); |
| | | oldrow.remove(); |
| | | row.after(tmprow); |
| | | row = tmprow; |
| | | // update folder index |
| | | name = newfolder + name.slice(prefix_len); |
| | | $('input[name="_subscribed[]"]', row).val(name); |
| | | this.env.subscriptionrows[id][0] = name; |
| | | // update the name if level is changed |
| | | if (level != 0) { |
| | | if (level > 0) { |
| | | for (i=level; i>0; i--) |
| | | dispname = dispname.replace(/^ /, ''); |
| | | } |
| | | else { |
| | | for (i=level; i<0; i++) |
| | | dispname = ' ' + dispname; |
| | | } |
| | | $('.name', row).html(dispname); |
| | | this.env.subscriptionrows[id][1] = dispname; |
| | | } |
| | | } |
| | | |
| | | // update list widget |
| | | this.init_subscription_list(); |
| | | // move the existing table row |
| | | this.add_folder_row(id, name, display_name, is_protected, subscribed, class_name, row, subfolders); |
| | | }; |
| | | |
| | | // remove the table row of a specific mailbox from the table |
| | | this.remove_folder_row = function(folder, subs) |
| | | this.remove_folder_row = function(folder) |
| | | { |
| | | var n, len, list = [], id = this.get_folder_row_id(folder); |
| | | |
| | | // get subfolders if any |
| | | if (subs) |
| | | list = this.get_subfolders(folder); |
| | | |
| | | // remove old row |
| | | this._remove_folder_row(id); |
| | | |
| | | // remove subfolders |
| | | for (n=0, len=list.length; n<len; n++) |
| | | this._remove_folder_row(list[n]); |
| | | }; |
| | | |
| | | this._remove_folder_row = function(id) |
| | | { |
| | | this.subscription_list.remove(id.replace(/^rcmli/, '')); |
| | | $('#' + id).remove(); |
| | | delete this.env.subscriptionrows[id]; |
| | | }; |
| | | |
| | | this.get_subfolders = function(folder) |
| | | { |
| | | var name, list = [], |
| | | prefix = folder + this.env.delimiter, |
| | | row = $('#'+this.get_folder_row_id(folder)).get(0); |
| | | |
| | | while (row = row.nextSibling) { |
| | | if (row.id) { |
| | | name = this.env.subscriptionrows[row.id][0]; |
| | | if (name && name.startsWith(prefix)) { |
| | | list.push(row.id); |
| | | } |
| | | else |
| | | break; |
| | | } |
| | | // reset searching |
| | | if (this.subscription_list.is_search()) { |
| | | this.subscription_select(); |
| | | this.subscription_list.reset_search(); |
| | | } |
| | | |
| | | return list; |
| | | var list = [], row = this.subscription_list.get_item(folder, true); |
| | | |
| | | // get subfolders if any |
| | | $('li', row).each(function() { list.push(ref.folder_id2name(this.id)); }); |
| | | |
| | | // remove folder row (and subfolders) |
| | | this.subscription_list.remove(folder); |
| | | |
| | | // update local list variable |
| | | list.push(folder); |
| | | $.each(list, function(i, v) { delete ref.env.subscriptionrows[v]; }); |
| | | }; |
| | | |
| | | this.subscribe = function(folder) |
| | |
| | | var lock = this.display_message(this.get_label('folderunsubscribing'), 'loading'); |
| | | this.http_post('unsubscribe', {_mbox: folder}, lock); |
| | | } |
| | | }; |
| | | |
| | | // helper method to find a specific mailbox row ID |
| | | this.get_folder_row_id = function(folder) |
| | | { |
| | | var id, folders = this.env.subscriptionrows; |
| | | for (id in folders) |
| | | if (folders[id] && folders[id][0] == folder) |
| | | return id; |
| | | }; |
| | | |
| | | // when user select a folder in manager |
| | |
| | | // disables subscription checkbox (for protected folder) |
| | | this.disable_subscription = function(folder) |
| | | { |
| | | var id = this.get_folder_row_id(folder); |
| | | if (id) |
| | | $('input[name="_subscribed[]"]', $('#'+id)).prop('disabled', true); |
| | | var row = this.subscription_list.get_item(folder, true); |
| | | if (row) |
| | | $('input[name="_subscribed[]"]:first', row).prop('disabled', true); |
| | | }; |
| | | |
| | | this.folder_size = function(folder) |
| | |
| | | $('#folder-size').replaceWith(size); |
| | | }; |
| | | |
| | | // filter folders by namespace |
| | | this.folder_filter = function(prefix) |
| | | { |
| | | this.subscription_list.reset_search(); |
| | | |
| | | this.subscription_list.container.children('li').each(function() { |
| | | var i, folder = ref.folder_id2name(this.id); |
| | | // show all folders |
| | | if (prefix == '---') { |
| | | } |
| | | // got namespace prefix |
| | | else if (prefix) { |
| | | if (folder !== prefix) { |
| | | $(this).data('filtered', true).hide(); |
| | | return |
| | | } |
| | | } |
| | | // no namespace prefix, filter out all other namespaces |
| | | else { |
| | | // first get all namespace roots |
| | | for (i in ref.env.ns_roots) { |
| | | if (folder === ref.env.ns_roots[i]) { |
| | | $(this).data('filtered', true).hide(); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | $(this).removeData('filtered').show(); |
| | | }); |
| | | }; |
| | | |
| | | /*********************************************************/ |
| | | /********* GUI functionality *********/ |
| | |
| | | }; |
| | | |
| | | // open a jquery UI dialog with the given content |
| | | this.show_popup_dialog = function(html, title, buttons, options) |
| | | this.show_popup_dialog = function(content, title, buttons, options) |
| | | { |
| | | // forward call to parent window |
| | | if (this.is_framed()) { |
| | | return parent.rcmail.show_popup_dialog(html, title, buttons, options); |
| | | return parent.rcmail.show_popup_dialog(content, title, buttons, options); |
| | | } |
| | | |
| | | var popup = $('<div class="popup">') |
| | | .html(html) |
| | | .dialog($.extend({ |
| | | var popup = $('<div class="popup">'); |
| | | |
| | | if (typeof content == 'object') |
| | | popup.append(content); |
| | | else |
| | | popup.html(content); |
| | | |
| | | popup.dialog($.extend({ |
| | | title: title, |
| | | buttons: buttons, |
| | | modal: true, |
| | | resizable: true, |
| | | width: 500, |
| | | close: function(event, ui) { $(this).remove() } |
| | | close: function(event, ui) { $(this).remove(); } |
| | | }, options || {})); |
| | | |
| | | // resize and center popup |
| | |
| | | // save message in local storage and do not redirect |
| | | if (this.env.action == 'compose') { |
| | | this.save_compose_form_local(); |
| | | this.compose_skip_unsavedcheck = true; |
| | | } |
| | | else if (redirect_url) { |
| | | setTimeout(function(){ ref.redirect(redirect_url, true); }, 2000); |
| | |
| | | // wrapper for localStorage.getItem(key) |
| | | this.local_storage_get_item = function(key, deflt, encrypted) |
| | | { |
| | | var item; |
| | | |
| | | // TODO: add encryption |
| | | var item = localStorage.getItem(this.get_local_storage_prefix() + key); |
| | | try { |
| | | item = localStorage.getItem(this.get_local_storage_prefix() + key); |
| | | } |
| | | catch (e) { } |
| | | |
| | | return item !== null ? JSON.parse(item) : (deflt || null); |
| | | }; |
| | | |
| | | // wrapper for localStorage.setItem(key, data) |
| | | this.local_storage_set_item = function(key, data, encrypted) |
| | | { |
| | | // TODO: add encryption |
| | | return localStorage.setItem(this.get_local_storage_prefix() + key, JSON.stringify(data)); |
| | | // try/catch to handle no localStorage support, but also error |
| | | // in Safari-in-private-browsing-mode where localStorage exists |
| | | // but can't be used (#1489996) |
| | | try { |
| | | // TODO: add encryption |
| | | localStorage.setItem(this.get_local_storage_prefix() + key, JSON.stringify(data)); |
| | | return true; |
| | | } |
| | | catch (e) { |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | // wrapper for localStorage.removeItem(key) |
| | | this.local_storage_remove_item = function(key) |
| | | { |
| | | return localStorage.removeItem(this.get_local_storage_prefix() + key); |
| | | try { |
| | | localStorage.removeItem(this.get_local_storage_prefix() + key); |
| | | return true; |
| | | } |
| | | catch (e) { |
| | | return false; |
| | | } |
| | | }; |
| | | } // end object rcube_webmail |
| | | |