Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F1974875
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
127 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index cae76516..5f8aa844 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1,3458 +1,3463 @@
/**
* Client scripts for the Tasklist plugin
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2012-2015, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
function rcube_tasklist_ui(settings)
{
// extend base class
rcube_libcalendaring.call(this, settings);
/* constants */
var FILTER_MASK_ALL = 0;
var FILTER_MASK_TODAY = 1;
var FILTER_MASK_TOMORROW = 2;
var FILTER_MASK_WEEK = 4;
var FILTER_MASK_LATER = 8;
var FILTER_MASK_NODATE = 16;
var FILTER_MASK_OVERDUE = 32;
var FILTER_MASK_FLAGGED = 64;
var FILTER_MASK_COMPLETE = 128;
var FILTER_MASK_ASSIGNED = 256;
var FILTER_MASK_MYTASKS = 512;
var filter_masks = {
all: FILTER_MASK_ALL,
today: FILTER_MASK_TODAY,
tomorrow: FILTER_MASK_TOMORROW,
week: FILTER_MASK_WEEK,
later: FILTER_MASK_LATER,
nodate: FILTER_MASK_NODATE,
overdue: FILTER_MASK_OVERDUE,
flagged: FILTER_MASK_FLAGGED,
complete: FILTER_MASK_COMPLETE,
assigned: FILTER_MASK_ASSIGNED,
mytasks: FILTER_MASK_MYTASKS
};
/* private vars */
var filtermask = FILTER_MASK_ALL;
var loadstate = { filter:-1, lists:'', search:null };
var idcount = 0;
var focusview = false;
var focusview_lists = [];
var saving_lock;
var ui_loading;
var taskcounts = {};
var listindex = [];
var listdata = {};
var tagsfilter = [];
var draghelper;
var search_request;
var search_query;
var completeness_slider;
var task_draghelper;
var task_drag_active = false;
var list_scroll_top = 0;
var scroll_delay = 400;
var scroll_step = 5;
var scroll_speed = 20;
var scroll_sensitivity = 40;
var scroll_timer;
var tasklists_widget;
var focused_task;
var focused_subclass;
var task_attendees = [];
var attendees_list;
var me = this;
var extended_datepicker_settings;
/* public members */
this.tasklists = rcmail.env.tasklists;
this.selected_task = null;
this.selected_list = null;
/* public methods */
this.init = init;
this.edit_task = task_edit_dialog;
this.print_tasks = print_tasks;
this.delete_task = delete_task;
this.add_childtask = add_childtask;
this.quicksearch = quicksearch;
this.reset_search = reset_search;
this.expand_collapse = expand_collapse;
this.list_delete = list_delete;
this.list_remove = list_remove;
this.list_showurl = list_showurl;
this.list_edit_dialog = list_edit_dialog;
this.list_set_sort_and_order = list_set_sort_and_order;
this.get_setting = get_setting;
this.unlock_saving = unlock_saving;
/* imports */
var Q = this.quote_html;
var text2html = this.text2html;
var render_message_links = this.render_message_links;
/**
* initialize the tasks UI
*/
function init()
{
if (rcmail.env.action == 'print' && rcmail.task == 'tasks') {
filtermask = rcmail.env.filtermask;
data_ready({data: rcmail.env.tasks});
return;
}
if (rcmail.env.action == 'dialog-ui') {
task_edit_dialog(null, 'new', rcmail.env.task_prop);
rcmail.addEventListener('plugin.unlock_saving', function(status) {
unlock_saving();
if (status) {
var rc = window.parent.rcmail,
win = rc.env.contentframe ? rc.get_frame_window(rc.env.contentframe) : window.parent;
if (win) {
win.location.reload();
}
window.parent.kolab_task_dialog_element.dialog('destroy');
}
});
return;
}
// initialize task list selectors
for (var id in me.tasklists) {
if (settings.selected_list && me.tasklists[settings.selected_list] && !me.tasklists[settings.selected_list].active) {
me.tasklists[settings.selected_list].active = true;
me.selected_list = settings.selected_list;
$(rcmail.gui_objects.tasklistslist).find("input[value='"+settings.selected_list+"']").prop('checked', true);
}
if (me.tasklists[id].editable && (!me.selected_list || me.tasklists[id]['default'] || (me.tasklists[id].active && !me.tasklists[me.selected_list].active))) {
me.selected_list = id;
}
}
if (rcmail.env.source && me.tasklists[rcmail.env.source])
me.selected_list = rcmail.env.source;
// initialize treelist widget that controls the tasklists list
var widget_class = window.kolab_folderlist || rcube_treelist_widget;
tasklists_widget = new widget_class(rcmail.gui_objects.tasklistslist, {
id_prefix: 'rcmlitasklist',
selectable: true,
save_state: true,
keyboard: false,
searchbox: '#tasklistsearch',
search_action: 'tasks/tasklist',
search_sources: [ 'folders', 'users' ],
search_title: rcmail.gettext('listsearchresults','tasklist')
});
tasklists_widget.addEventListener('select', function(node) {
var id = $(this).data('id');
rcmail.enable_command('list-edit', me.has_permission(me.tasklists[node.id], 'wa'));
rcmail.enable_command('list-delete', me.has_permission(me.tasklists[node.id], 'xa'));
rcmail.enable_command('list-import', me.has_permission(me.tasklists[node.id], 'i'));
rcmail.enable_command('list-remove', me.tasklists[node.id] && me.tasklists[node.id].removable);
rcmail.enable_command('list-showurl', me.tasklists[node.id] && !!me.tasklists[node.id].caldavurl);
me.selected_list = node.id;
rcmail.update_state({source: node.id});
rcmail.triggerEvent('show-list', {title: me.tasklists[node.id].name});
});
tasklists_widget.addEventListener('subscribe', function(p) {
var list;
if ((list = me.tasklists[p.id])) {
list.subscribed = p.subscribed || false;
rcmail.http_post('tasklist', { action:'subscribe', l:{ id:p.id, active:list.active?1:0, permanent:list.subscribed?1:0 } });
}
});
tasklists_widget.addEventListener('remove', function(p) {
if (me.tasklists[p.id] && me.tasklists[p.id].removable) {
list_remove(p.id);
}
});
tasklists_widget.addEventListener('insert-item', function(p) {
var list = p.data;
if (list && list.id && !list.virtual) {
me.tasklists[list.id] = list;
var prop = { id:p.id, active:list.active?1:0 };
if (list.subscribed) prop.permanent = 1;
rcmail.http_post('tasklist', { action:'subscribe', l:prop });
list_tasks();
$(p.item).data('type', 'tasklist');
}
});
tasklists_widget.addEventListener('search-complete', function(data) {
if (data.length)
rcmail.display_message(rcmail.gettext('nrtasklistsfound','tasklist').replace('$nr', data.length), 'voice');
else
rcmail.display_message(rcmail.gettext('notasklistsfound','tasklist'), 'notice');
});
// Make Elastic checkboxes pretty
if (window.UI && UI.pretty_checkbox) {
$(rcmail.gui_objects.tasklistslist).find('input[type=checkbox]').each(function() {
UI.pretty_checkbox(this);
});
tasklists_widget.addEventListener('add-item', function(prop) {
UI.pretty_checkbox($(prop.li).find('input'));
});
}
// init (delegate) event handler on tasklist checkboxes
tasklists_widget.container.on('click', 'input[type=checkbox]', function(e) {
var list, id = this.value;
if ((list = me.tasklists[id])) {
list.active = this.checked;
fetch_counts();
if (!this.checked) remove_tasks(id);
else list_tasks(null);
rcmail.http_post('tasklist', { action:'subscribe', l:{ id:id, active:list.active?1:0 } });
// disable focusview
if (!this.checked && focusview && $.inArray(id, focusview_lists) >= 0) {
set_focusview(null);
}
// adjust checked state of original list item
if (tasklists_widget.is_search()) {
tasklists_widget.container.find('input[value="'+id+'"]').prop('checked', this.checked);
}
}
e.stopPropagation();
})
.on('keypress', 'input[type=checkbox]', function(e) {
// select tasklist on <Enter>
if (e.keyCode == 13) {
tasklists_widget.select(this.value);
return rcube_event.cancel(e);
}
})
.find('li:not(.virtual)').data('type', 'tasklist');
// handler for clicks on quickview buttons
tasklists_widget.container.on('click', '.quickview', function(e){
var id = $(this).closest('li').attr('id').replace(/^rcmlitasklist/, '');
if (tasklists_widget.is_search())
id = id.replace(/--xsR$/, '');
if (!rcube_event.is_keyboard(e) && this.blur)
this.blur();
set_focusview(id, e.shiftKey || e.metaKey || e.ctrlKey);
e.stopPropagation();
return false;
});
// register dbl-click handler to open calendar edit dialog
tasklists_widget.container.on('dblclick', ':not(.virtual) > .tasklist', function(e){
var id = $(this).closest('li').attr('id').replace(/^rcmlitasklist/, '');
if (tasklists_widget.is_search())
id = id.replace(/--xsR$/, '');
list_edit_dialog(id);
});
if (me.selected_list) {
rcmail.enable_command('addtask', true);
tasklists_widget.select(me.selected_list);
}
// register server callbacks
rcmail.addEventListener('plugin.data_ready', data_ready);
rcmail.addEventListener('plugin.update_task', update_taskitem);
rcmail.addEventListener('plugin.refresh_tasks', function(p) { update_taskitem(p, true); });
rcmail.addEventListener('plugin.update_counts', update_counts);
rcmail.addEventListener('plugin.insert_tasklist', insert_list);
rcmail.addEventListener('plugin.update_tasklist', update_list);
rcmail.addEventListener('plugin.destroy_tasklist', destroy_list);
rcmail.addEventListener('plugin.unlock_saving', unlock_saving);
rcmail.addEventListener('plugin.refresh_tagcloud', function() { update_tagcloud(); });
rcmail.addEventListener('requestrefresh', before_refresh);
rcmail.addEventListener('plugin.reload_data', function(){
list_tasks(null, true);
setTimeout(fetch_counts, 200);
});
rcmail.addEventListener('plugin.import_success', function(p){ rctasks.import_success(p); });
rcmail.addEventListener('plugin.import_error', function(p){ rctasks.import_error(p); });
rcmail.addEventListener('plugin.task_render_changelog', task_render_changelog);
rcmail.addEventListener('plugin.task_show_diff', task_show_diff);
rcmail.addEventListener('plugin.task_show_revision', function(data){ task_show_dialog(null, data, true); });
rcmail.addEventListener('plugin.close_history_dialog', close_history_dialog);
rcmail.register_command('list-sort', list_set_sort, true);
rcmail.register_command('list-order', list_set_order, (settings.sort_col || 'auto') != 'auto');
rcmail.register_command('task-history', task_history_dialog, false);
$('#taskviewsortmenu .by-' + (settings.sort_col || 'auto')).attr('aria-checked', 'true').addClass('selected');
$('#taskviewsortmenu .sortorder.' + (settings.sort_order || 'asc')).attr('aria-checked', 'true').addClass('selected');
// start loading tasks
fetch_counts();
list_tasks(settings.selected_filter);
// register event handlers for UI elements
$('#taskselector a').click(function(e) {
if (!$(this).parent().hasClass('inactive')) {
var selector = this.href.replace(/^.*#/, ''),
mask = filter_masks[selector],
shift = e.shiftKey || e.ctrlKey || e.metaKey;
if (!shift)
filtermask = mask; // reset selection on regular clicks
else if (filtermask & mask)
filtermask -= mask;
else
filtermask |= mask;
list_tasks();
}
return false;
});
// quick-add a task
$(rcmail.gui_objects.quickaddform).submit(function(e){
if (saving_lock)
return false;
var tasktext = this.elements.text.value,
rec = { id:-(++idcount), title:tasktext, readonly:true, mask:0, complete:0 };
if (tasktext && tasktext.length) {
save_task({ tempid:rec.id, raw:tasktext, list:me.selected_list }, 'new');
$('#listmessagebox').hide();
}
// clear form
this.reset();
return false;
}).find('input[type=text]').placeholder(rcmail.gettext('createnewtask','tasklist'));
// click-handler on task list items (delegate)
$(rcmail.gui_objects.resultlist).on('click', function(e) {
var item = $(e.target), className = e.target.className;
if (item.hasClass('childtoggle')) {
item = item.parent().find('.taskhead');
className = 'childtoggle';
}
else {
if (!item.hasClass('taskhead'))
item = item.closest('div.taskhead');
className = String(className).split(' ')[0];
}
// ignore
if (!item.length)
return false;
var id = item.data('id'),
li = item.parent(),
rec = listdata[id];
switch (className) {
case 'childtoggle':
rec.collapsed = !rec.collapsed;
li.children('.childtasks:first').toggle().attr('aria-hidden', rec.collapsed ? 'true' : 'false');
$(e.target).toggleClass('collapsed').html('').append($('<span class="inner">').text(rec.collapsed ? '▶' : '▼'));
rcmail.http_post('tasks/task', { action:'collapse', t:{ id:rec.id, list:rec.list }, collapsed:rec.collapsed?1:0 });
if (e.shiftKey) // expand/collapse all childs
li.children('.childtasks:first .childtoggle.'+(rec.collapsed?'expanded':'collapsed')).click();
break;
case 'complete':
if (rcmail.busy)
return false;
save_task_confirm(rec, 'edit', { _status_before:rec.status + '', status:e.target.checked ? 'COMPLETED' : (rec.complete > 0 ? 'IN-PROCESS' : 'NEEDS-ACTION') });
item.toggleClass('complete');
return true;
case 'flagged':
if (rcmail.busy)
return false;
rec.flagged = rec.flagged ? 0 : 1;
item.toggleClass('flagged').find('.flagged:first').attr('aria-checked', (rec.flagged ? 'true' : 'false'));
save_task(rec, 'edit');
break;
case 'date':
if (rcmail.busy)
return false;
var link = $(e.target).html(''),
input = $('<input type="text" size="10" />').appendTo(link).val(rec.date || '');
input.datepicker($.extend({
onClose: function(dateText, inst) {
if (dateText != (rec.date || '')) {
save_task_confirm(rec, 'edit', { date:dateText });
}
input.datepicker('destroy').remove();
link.html(dateText || rcmail.gettext('nodate','tasklist'));
}
}, extended_datepicker_settings)
)
.datepicker('setDate', rec.date)
.datepicker('show');
break;
case 'delete':
delete_task(id);
break;
case 'actions':
var pos, ref = $(e.target),
menu = $('#taskitemmenu');
if (menu.is(':visible') && menu.data('refid') == id) {
rcmail.command('menu-close', 'taskitemmenu');
}
else {
rcmail.enable_command('task-history', me.tasklists[rec.list] && !!me.tasklists[rec.list].history);
rcmail.command('menu-open', { menu: 'taskitemmenu', show: true }, e.target, e);
menu.data('refid', id);
me.selected_task = rec;
}
e.bubble = false;
break;
case 'extlink':
return true;
default:
if (e.target.nodeName != 'INPUT') {
task_show_dialog(id);
$(rcmail.gui_objects.resultlist).find('.selected').removeClass('selected');
$(item).addClass('selected');
}
break;
}
return false;
})
.on('dblclick', '.taskhead, .childtoggle', function(e){
var id, rec, item = $(e.target);
if (!item.hasClass('taskhead'))
item = item.closest('div.taskhead');
if (!rcmail.busy && item.length && (id = item.data('id')) && (rec = listdata[id])) {
var list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] : {};
if (rec.readonly || !list.editable)
task_show_dialog(id);
else
task_edit_dialog(id, 'edit');
clearSelection();
}
$(rcmail.gui_objects.resultlist).find('.selected').removeClass('selected');
$(item).addClass('selected');
})
.on('keydown', '.taskhead', function(e) {
if (e.target.nodeName == 'INPUT' && e.target.type == 'text')
return true;
var inc = 1;
switch (e.keyCode) {
case 13: // Enter
$(e.target).trigger('click', { pointerType:'keyboard' });
return rcube_event.cancel(e);
case 38: // Up arrow key
inc = -1;
case 40: // Down arrow key
if ($(e.target).hasClass('actions')) {
// unfold actions menu
$(e.target).trigger('click', { pointerType:'keyboard' });
return rcube_event.cancel(e);
}
// focus next/prev task item
var x = 0, target = this, items = $(rcmail.gui_objects.resultlist).find('.taskhead:visible');
items.each(function(i, item) {
if (item === target) {
x = i;
return false;
}
});
items.get(x + inc).focus();
return rcube_event.cancel(e);
case 37: // Left arrow key
case 39: // Right arrow key
$(this).parent().children('.childtoggle:visible').first().trigger('click', { pointerType:'keyboard' });
break;
}
})
.on('focusin', '.taskhead', function(e){
if (rcube_event.is_keyboard(e)) {
var item = $(e.target);
if (!item.hasClass('taskhead'))
item = item.closest('div.taskhead');
var id = item.data('id');
if (id && listdata[id]) {
focused_task = id;
focused_subclass = item.get(0) !== e.target ? e.target.className : null;
}
}
$(item).addClass('focused');
})
.on('focusout', '.taskhead', function(e){
var item = $(e.target);
if (focused_task && item.data('id') == focused_task) {
focused_task = focused_subclass = null;
}
$(item).removeClass('focused');
});
/**
*
*/
function task_rsvp(response, delegate)
{
if (me.selected_task && me.selected_task.attendees && response) {
// bring up delegation dialog
if (response == 'delegated' && !delegate) {
rcube_libcalendaring.itip_delegate_dialog(function(data) {
$('#reply-comment-task-rsvp').val(data.comment);
data.rsvp = data.rsvp ? 1 : '';
task_rsvp('delegated', data);
});
return;
}
// update attendee status
for (var data, i=0; i < me.selected_task.attendees.length; i++) {
data = me.selected_task.attendees[i];
if (settings.identity.emails.indexOf(';'+String(data.email).toLowerCase()) >= 0) {
data.status = response.toUpperCase();
if (data.status == 'DELEGATED') {
data['delegated-to'] = delegate.to;
}
else {
delete data.rsvp; // unset RSVP flag
if (data['delegated-to']) {
delete data['delegated-to'];
if (data.role == 'NON-PARTICIPANT' && data.status != 'DECLINED') {
data.role = 'REQ-PARTICIPANT';
}
}
}
}
}
// submit status change to server
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('tasks/task', {
action: 'rsvp',
t: $.extend({}, me.selected_task, (delegate || {})),
filter: filtermask,
status: response,
noreply: $('#noreply-task-rsvp:checked').length ? 1 : 0,
comment: $('#reply-comment-task-rsvp').val()
});
task_show_dialog(me.selected_task.id);
}
}
// init RSVP widget
$('#task-rsvp input.button').click(function(e) {
task_rsvp($(this).attr('rel'))
});
// register click handler for message links
$('#task-links, #taskedit-links').on('click', 'li a.messagelink', function(e) {
rcmail.open_window(this.href);
return false;
});
// register click handler for message delete buttons
$('#taskedit-links').on('click', 'li a.delete', function(e) {
remove_link(e.target);
return false;
});
// extended datepicker settings
var extended_datepicker_settings = $.extend({
showButtonPanel: true,
beforeShow: function(input, inst) {
setTimeout(function(){
$(input).datepicker('widget').find('button.ui-datepicker-close')
.html(rcmail.gettext('nodate','tasklist'))
.attr('onclick', '')
.unbind('click')
.bind('click', function(e){
$(input).datepicker('setDate', null).datepicker('hide');
});
}, 1);
}
}, me.datepicker_settings);
rcmail.addEventListener('kolab-tags-search', filter_tasks)
.addEventListener('kolab-tags-drop-data', function(e) { return listdata[e.id]; })
.addEventListener('kolab-tags-drop', function(e) {
var rec = listdata[e.id];
if (rec && rec.id && e.tag) {
if (!rec.tags)
rec.tags = [];
rec.tags.push(e.tag);
save_task(rec, 'edit');
}
});
// Create simple list widget replacement for Elastic skin,
// as we do not use list nor treelist widget for tasks list
rcmail.tasklist = {
_find_sibling: function(dir) {
if (me.selected_task && me.selected_task.id) {
var n = false,
target = $('li[rel="' + me.selected_task.id + '"] > .taskhead', rcmail.gui_objects.resultlist)[0],
items = $(rcmail.gui_objects.resultlist).find('.taskhead');
items.each(function(i, item) {
if (item === target) {
n = i;
return false;
}
});
if (n !== false) {
return items[n + dir];
}
}
},
get_single_selection: function() {
if (me.selected_task) {
return me.selected_task.id;
}
},
get_node: function(uid) {
if (me.selected_task && me.selected_task.id) {
return {collapsed: true};
}
},
expand: function() {
if (me.selected_task && me.selected_task.id) {
var parent = $('li[rel="' + me.selected_task.id + '"]', rcmail.gui_objects.resultlist).parent('.childtasks')[0];
if (parent) {
$(parent).parent().children('.childtoggle.collapsed').click();
}
}
},
get_next: function() {
return rcmail.tasklist._find_sibling(1);
},
get_prev: function() {
return rcmail.tasklist._find_sibling(-1);
},
select: function(node) {
$(node).click();
}
};
rcmail.triggerEvent('tasklist-init');
}
/**
* initialize task edit form elements
*/
function init_taskedit()
{
$('#taskedit:not([data-notabs])').tabs({
activate: function(event, ui) {
// reset autocompletion on tab change (#3389)
rcmail.ksearch_blur();
}
});
$('#taskedit li.nav-item a').on('click', function() {
// reset autocompletion on tab change (#3389)
rcmail.ksearch_blur();
});
var completeness_slider_change = function(e, ui) {
var v = completeness_slider.slider('value');
if (v >= 98) v = 100;
if (v <= 2) v = 0;
$('#taskedit-completeness').val(v);
};
completeness_slider = $('#taskedit-completeness-slider').slider({
range: 'min',
animate: 'fast',
slide: completeness_slider_change,
change: completeness_slider_change
});
$('#taskedit-completeness').change(function(e){
completeness_slider.slider('value', parseInt(this.value))
});
// register events on alarms and recurrence fields
me.init_alarms_edit('#taskedit-alarms');
me.init_recurrence_edit('#eventedit');
$('#taskedit-date, #taskedit-startdate').datepicker(me.datepicker_settings);
$('a.edit-nodate').click(function(){
var sel = $(this).attr('rel');
if (sel) $(sel).val('');
return false;
});
// init attendees autocompletion
var ac_props;
// parallel autocompletion
if (rcmail.env.autocomplete_threads > 0) {
ac_props = {
threads: rcmail.env.autocomplete_threads,
sources: rcmail.env.autocomplete_sources
};
}
rcmail.init_address_input_events($('#edit-attendee-name'), ac_props);
rcmail.addEventListener('autocomplete_insert', function(e) {
var cutype, success = false;
if (e.field.name == 'participant') {
cutype = e.data && e.data.type == 'group' && e.result_type == 'person' ? 'GROUP' : 'INDIVIDUAL';
success = add_attendees(e.insert, { role:'REQ-PARTICIPANT', status:'NEEDS-ACTION', cutype: cutype });
}
if (e.field && success) {
e.field.value = '';
}
});
$('#edit-attendee-add').click(function() {
var input = $('#edit-attendee-name');
rcmail.ksearch_blur();
if (add_attendees(input.val(), { role:'REQ-PARTICIPANT', status:'NEEDS-ACTION', cutype:'INDIVIDUAL' })) {
input.val('');
}
});
// handle change of "send invitations" checkbox
$('#edit-attendees-invite').change(function() {
$('#edit-attendees-donotify,input.edit-attendee-reply').prop('checked', this.checked);
// hide/show comment field
$('#taskeditform .attendees-commentbox')[this.checked ? 'show' : 'hide']();
});
// delegate change task to "send invitations" checkbox
$('#edit-attendees-donotify').change(function() {
$('#edit-attendees-invite').click();
return false;
});
// configure drop-down menu on time input fields based on jquery UI autocomplete
$('#taskedit-starttime, #taskedit-time').each(function() {
me.init_time_autocomplete(this, {container: '#taskedit'});
});
}
/**
* Request counts from the server
*/
function fetch_counts()
{
var active = active_lists();
if (active.length)
rcmail.http_request('counts', { lists:active.join(',') });
else
update_counts({});
}
/**
* List tasks matching the given selector
*/
function list_tasks(sel, force)
{
if (rcmail.busy)
return;
if (sel && filter_masks[sel] !== undefined) {
filtermask = filter_masks[sel];
}
var active = active_lists(),
basefilter = filtermask & FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL,
reload = force || active.join(',') != loadstate.lists || basefilter != loadstate.filter || loadstate.search != search_query;
if (active.length && reload) {
ui_loading = rcmail.set_busy(true, 'loading');
rcmail.http_request('fetch', { filter:basefilter, lists:active.join(','), q:search_query }, true);
}
else if (reload)
data_ready({ data:[], lists:'', filter:basefilter, search:search_query });
else
render_tasklist();
$('#taskselector li.selected').removeClass('selected').attr('aria-checked', 'false');
// select all active selectors
if (filtermask > 0) {
$.each(filter_masks, function(sel, mask) {
if (filtermask & mask)
$('#taskselector li.'+sel).addClass('selected').attr('aria-checked', 'true');
});
}
else
$('#taskselector li.all').addClass('selected').attr('aria-checked', 'true');
}
/**
* Filter tasks by tag
*/
function filter_tasks(tags)
{
tagsfilter = tags;
list_tasks();
}
/**
* Remove all tasks of the given list from the UI
*/
function remove_tasks(list_id)
{
// remove all tasks of the given list from index
var newindex = $.grep(listindex, function(id, i){
return listdata[id] && listdata[id].list != list_id;
});
listindex = newindex;
render_tasklist();
// avoid reloading
me.tasklists[list_id].active = false;
loadstate.lists = active_lists();
}
var init_cloned_form = function(form) {
// update element IDs after clone
$('select,input,label', form).each(function() {
if (this.htmlFor)
this.htmlFor += '-clone';
else if (this.id)
this.id += '-clone';
});
}
// open a dialog to upload an .ics file with tasks to be imported
this.import_tasks = function(tasklist)
{
// close show dialog first
var buttons = {},
$dialog = $("#tasksimport").clone(true).removeClass('uidialog'),
form = $dialog.find('form').get(0);
if ($dialog.is(':ui-dialog'))
$dialog.dialog('close');
if (tasklist)
$('#task-import-list').val(tasklist);
init_cloned_form(form);
buttons[rcmail.gettext('import', 'tasklist')] = function() {
if (form && form.elements._data.value) {
rcmail.async_upload_form(form, 'import', function(e) {
rcmail.set_busy(false, null, saving_lock);
saving_lock = null;
$('.ui-dialog-buttonpane button', $dialog.parent()).prop('disabled', false);
// display error message if no sophisticated response from server arrived (e.g. iframe load error)
if (me.import_succeeded === null)
rcmail.display_message(rcmail.get_label('importerror', 'tasklist'), 'error');
});
// display upload indicator (with extended timeout)
var timeout = rcmail.env.request_timeout;
rcmail.env.request_timeout = 600;
me.import_succeeded = null;
saving_lock = rcmail.set_busy(true, 'uploading');
$('.ui-dialog-buttonpane button', $dialog.parent()).prop('disabled', true);
// restore settings
rcmail.env.request_timeout = timeout;
}
};
buttons[rcmail.gettext('cancel', 'tasklist')] = function() {
$(this).dialog('close');
};
// open jquery UI dialog
this.import_dialog = rcmail.show_popup_dialog($dialog, rcmail.gettext('tasklist.importtasks'), buttons, {
closeOnEscape: false,
button_classes: ['import mainaction', 'cancel']
});
};
// callback from server if import succeeded
this.import_success = function(p)
{
this.import_succeeded = true;
this.import_dialog.dialog('close');
rcmail.set_busy(false, null, saving_lock);
saving_lock = null;
rcmail.gui_objects.importform.reset();
if (p.refetch) {
list_tasks(null, true);
setTimeout(fetch_counts, 200);
}
};
// callback from server to report errors on import
this.import_error = function(p)
{
this.import_succeeded = false;
rcmail.set_busy(false, null, saving_lock);
saving_lock = null;
rcmail.display_message(p.message || rcmail.get_label('importerror', 'tasklist'), 'error');
};
// open a tasks export dialog
this.export_tasks = function()
{
// close show dialog first
var list, $dialog = $("#tasksexport").clone(true).removeClass('uidialog'),
form = $dialog.find('form').get(0),
buttons = {};
if ($dialog.is(':ui-dialog'))
$dialog.dialog('close');
list = $("#task-export-list").val('');
init_cloned_form(form);
buttons[rcmail.gettext('export', 'tasklist')] = function() {
var data = {}, form_elements = $('select, input', form);
// "current view" export, use hidden form to POST task IDs
if (list.val() === '') {
var cache = {}, tasks = [], inputs = [],
postform = $('#tasks-export-form-post');
$.each(listindex || [], function() {
var rec = listdata[this];
if (match_filter(rec, cache)) {
tasks.push(rec.id);
}
});
// copy form inputs, there may be controls added by other plugins
form_elements.each(function() {
if (this.type != 'checkbox' || this.checked)
inputs.push($('<input>').attr({type: 'hidden', name: this.name, value: this.value}));
});
inputs.push($('<input>').attr({type: 'hidden', name: '_token', value: rcmail.env.request_token}));
inputs.push($('<input>').attr({type: 'hidden', name: 'id', value: tasks.join(',')}));
if (!postform.length)
postform = $('<form>')
.attr({style: 'display: none', method: 'POST', action: '?_task=tasks&_action=export'})
.appendTo('body');
postform.html('').append(inputs).submit();
}
// otherwise we can use simple GET
else {
form_elements.each(function() {
if (this.type != 'checkbox' || this.checked)
data[this.name] = $(this).val();
});
rcmail.goto_url('export', data, false);
}
$(this).dialog('close');
};
buttons[rcmail.gettext('cancel', 'tasklist')] = function() {
$(this).dialog('close');
};
// open jquery UI dialog
rcmail.show_popup_dialog($dialog, rcmail.gettext('exporttitle', 'tasklist'), buttons, {
button_classes: ['export mainaction', 'cancel']
});
};
/*
// download the selected task as iCal
this.task_download = function(task)
{
if (task && task.id) {
rcmail.goto_url('export', {source: task.list, id: task.id, attachments: 1});
}
};
*/
/**
* Modify query parameters for refresh requests
*/
function before_refresh(query)
{
query.filter = filtermask == FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL;
query.lists = active_lists().join(',');
if (search_query)
query.q = search_query;
return query;
}
/**
* Callback if task data from server is ready
*/
function data_ready(response)
{
listdata = {};
listindex = [];
loadstate.lists = response.lists;
loadstate.filter = response.filter;
loadstate.search = response.search;
for (var id, i=0; i < response.data.length; i++) {
id = response.data[i].id;
listindex.push(id);
listdata[id] = response.data[i];
listdata[id].children = [];
// register a forward-pointer to child tasks
if (listdata[id].parent_id && listdata[listdata[id].parent_id])
listdata[listdata[id].parent_id].children.push(id);
}
// sort index before rendering
listindex.sort(function(a, b) { return task_cmp(listdata[a], listdata[b]); });
update_taglist(response.tags || []);
render_tasklist();
// show selected task dialog
if (settings.selected_id) {
if (listdata[settings.selected_id]) {
task_show_dialog(settings.selected_id);
delete settings.selected_id;
}
// remove _id from window location
if (window.history.replaceState) {
window.history.replaceState({}, document.title, rcmail.url('', { _list: me.selected_list }));
}
}
rcmail.set_busy(false, 'loading', ui_loading);
}
/**
*
*/
function render_tasklist()
{
// clear display
var id, rec,
count = 0,
cache = {},
activetags = {},
msgbox = $('#listmessagebox').hide(),
list = $(rcmail.gui_objects.resultlist);
selected = $('.taskhead.selected', list).parent().attr('rel');
list.html('');
for (var i=0; i < listindex.length; i++) {
id = listindex[i];
rec = listdata[id];
if (match_filter(rec, cache)) {
if (rcmail.env.action == 'print') {
render_task_printmode(rec);
continue;
}
render_task(rec);
count++;
// keep a list of tags from all visible tasks
for (var t, j=0; rec.tags && j < rec.tags.length; j++) {
t = rec.tags[j];
if (typeof activetags[t] == 'undefined')
activetags[t] = 0;
activetags[t]++;
}
}
}
if (rcmail.env.action == 'print')
return;
fix_tree_toggles();
update_taglist();
if (!count) {
msgbox.html(rcmail.gettext('notasksfound','tasklist')).show();
rcmail.display_message(rcmail.gettext('notasksfound','tasklist'), 'voice');
}
else if (selected) {
// mark back the selected task
$('li[rel="' + selected + '"] > .taskhead').addClass('selected');
}
}
/**
* Show/hide child toggle buttons on all visible task items
*/
function fix_tree_toggles()
{
$('.taskitem', rcmail.gui_objects.resultlist).each(function(i,elem){
var li = $(elem),
rec = listdata[li.attr('rel')],
childs = $('.childtasks li', li);
$('.childtoggle', li)[(childs.length ? 'show' : 'hide')]();
});
}
/**
* Expand/collapse all task items with childs
*/
function expand_collapse(expand)
{
var collapsed = !expand;
$('.taskitem .childtasks')[(collapsed ? 'hide' : 'show')]();
$('.taskitem .childtoggle')
.removeClass(collapsed ? 'expanded' : 'collapsed')
.addClass(collapsed ? 'collapsed' : 'expanded')
.html('').append($('<span class="inner">').text(collapsed ? '▶' : '▼'));
// store new toggle collapse states
var ids = [];
for (var id in listdata) {
if (listdata[id].children && listdata[id].children.length)
ids.push(id);
}
if (ids.length) {
rcmail.http_post('tasks/task', { action:'collapse', t:{ id:ids.join(',') }, collapsed:collapsed?1:0 });
}
}
/**
* Display the given counts to each tag and set those inactive which don't
* have any matching tasks in the current view.
*/
function update_taglist(tags)
{
var counts = {};
$.each(listdata, function(id, rec) {
for (var t, j=0; rec && rec.tags && j < rec.tags.length; j++) {
t = rec.tags[j];
if (typeof counts[t] == 'undefined')
counts[t] = 0;
counts[t]++;
}
});
rcmail.triggerEvent('kolab-tags-counts', {counter: counts});
if (tags && tags.length) {
rcmail.triggerEvent('kolab-tags-refresh', {tags: tags});
}
}
function update_counts(counts)
{
// got new data
if (counts)
taskcounts = counts;
// iterate over all selector links and update counts
$('#taskselector a').each(function(i, elem){
var link = $(elem),
f = link.parent().attr('class').replace(/\s\w+/g, '');
if (f != 'all')
link.children('span').html('+' + (taskcounts[f] || ''))[(taskcounts[f] ? 'show' : 'hide')]();
});
// spacial case: overdue
$('#taskselector li.overdue')[(taskcounts.overdue ? 'removeClass' : 'addClass')]('inactive');
}
/**
* Callback from server to update a single task item
*/
function update_taskitem(rec, filter)
{
// handle a list of task records
if ($.isArray(rec)) {
$.each(rec, function(i,r){ update_taskitem(r, filter); });
return;
}
var id = rec.id,
oldid = rec.tempid || id,
oldrec = listdata[oldid],
oldindex = $.inArray(oldid, listindex),
oldparent = oldrec ? (oldrec._old_parent_id || oldrec.parent_id) : null,
list = me.tasklists[rec.list];
if (!id || !list)
return;
if (oldindex >= 0)
listindex[oldindex] = id;
else
listindex.push(id);
listdata[id] = rec;
// remove child-pointer from old parent
if (oldparent && listdata[oldparent] && oldparent != rec.parent_id) {
var oldchilds = listdata[oldparent].children,
i = $.inArray(oldid, oldchilds);
if (i >= 0) {
listdata[oldparent].children = oldchilds.slice(0,i).concat(oldchilds.slice(i+1));
}
}
// register a forward-pointer to child tasks
if (rec.parent_id && listdata[rec.parent_id] && listdata[rec.parent_id].children && $.inArray(id, listdata[rec.parent_id].children) < 0)
listdata[rec.parent_id].children.push(id);
// restore pointers to my children
if (!listdata[id].children) {
listdata[id].children = [];
for (var pid in listdata) {
if (listdata[pid].parent_id == id)
listdata[id].children.push(pid);
}
}
// copy _depth property from old rec or derive from parent
if (rec.parent_id && listdata[rec.parent_id]) {
rec._depth = (listdata[rec.parent_id]._depth || 0) + 1;
}
else if (oldrec) {
rec._depth = oldrec._depth || 0;
}
if (list.active || rec.tempid) {
if (!filter || match_filter(rec, {}))
render_task(rec, oldid);
}
else {
$('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).remove();
}
update_taglist(rec.tags || []);
fix_tree_toggles();
// refresh currently displayed task details dialog
if ($('#taskshow').is(':visible') && me.selected_task && me.selected_task.id == rec.id) {
task_show_dialog(rec.id);
}
}
/**
* Submit the given (changed) task record to the server
*/
function save_task(rec, action)
{
// show confirmation dialog when status of an assigned task has changed
if (rec._status_before !== undefined && me.is_attendee(rec))
return save_task_confirm(rec, action);
if (!rcmail.busy) {
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('tasks/task', { action:action, t:rec, filter:filtermask });
return true;
}
return false;
}
/**
* Display confirm dialog when modifying/deleting a task record
*/
var save_task_confirm = function(rec, action, updates)
{
var data = $.extend({}, rec, updates || {}),
notify = false, partstat = false, html = '',
do_confirm = settings.itip_notify & 2;
// task has attendees, ask whether to notify them
if (me.has_attendees(rec) && me.is_organizer(rec)) {
notify = true;
if (do_confirm) {
html = rcmail.gettext('changeconfirmnotifications', 'tasklist');
}
else {
data._notify = settings.itip_notify;
}
}
// ask whether to change my partstat and notify organizer
else if (data._status_before !== undefined && data.status && data._status_before != data.status && me.is_attendee(rec)) {
partstat = true;
if (do_confirm) {
html = rcmail.gettext('partstatupdatenotification', 'tasklist');
}
else if (settings.itip_notify & 1) {
data._reportpartstat = data.status == 'CANCELLED' ? 'DECLINED' : data.status;
}
}
// remove to avoid endless recursion
delete data._status_before;
// show dialog
if (html) {
var $dialog = $('<div>').html(html);
var buttons = [];
buttons.push({
text: rcmail.gettext('saveandnotify', 'tasklist'),
'class': 'mainaction save notify',
click: function() {
if (notify) data._notify = 1;
if (partstat) data._reportpartstat = data.status == 'CANCELLED' ? 'DECLINED' : data.status;
save_task(data, action);
$(this).dialog('close');
}
});
buttons.push({
text: rcmail.gettext('save', 'tasklist'),
'class': 'save',
click: function() {
save_task(data, action);
$(this).dialog('close');
}
});
buttons.push({
text: rcmail.gettext('cancel', 'tasklist'),
'class': 'cancel',
click: function() {
$(this).dialog('close');
if (updates)
render_task(rec, rec.id); // restore previous state
}
});
$dialog.dialog({
modal: true,
width: 460,
closeOnEscapeType: false,
dialogClass: 'warning no-close',
title: rcmail.gettext('changetaskconfirm', 'tasklist'),
buttons: buttons,
open: function() {
setTimeout(function(){
$dialog.parent().find('button:not(.ui-dialog-titlebar-close)').first().focus();
}, 5);
},
close: function(){
$dialog.dialog('destroy').remove();
}
}).addClass('task-update-confirm').show();
return true;
}
// do update
return save_task(data, action);
}
/**
* Remove saving lock and free the UI for new input
*/
function unlock_saving()
{
if (saving_lock) {
rcmail.set_busy(false, null, saving_lock);
saving_lock = null;
// Elastic
if (!$('.selected', rcmail.gui_objects.resultlist).length) {
$('#taskedit').parents('.watermark').removeClass('formcontainer');
rcmail.triggerEvent('show-list', {force: true});
}
}
}
/**
* Render the given task into the tasks list
*/
function render_task(rec, replace)
{
var label_id = rcmail.html_identifier(rec.id) + '-title';
var div = $('<div>').addClass('taskhead').html(
'<div class="progressbar"><div class="progressvalue" style="width:' + (rec.complete * 100) + '%"></div></div>' +
'<input type="checkbox" name="completed[]" value="1" class="complete" aria-label="' + rcmail.gettext('complete','tasklist') + '" ' + (is_complete(rec) ? 'checked="checked" ' : '') + '/>' +
'<span class="flagged" role="checkbox" tabindex="0" aria-checked="' + (rec.flagged ? 'true' : 'false') + '" aria-label="' + rcmail.gettext('flagged','tasklist') + '"></span>' +
'<span class="title" id="' + label_id + '">' + text2html(Q(rec.title)) + '</span>' +
'<span class="tags"></span>' +
'<span class="date">' + Q(rec.date || rcmail.gettext('nodate','tasklist')) + '</span>' +
'<a href="#" class="actions" aria-haspopup="true" aria-expanded="false">' + rcmail.gettext('taskactions','tasklist') + '</a>'
)
.attr('tabindex', '0')
.attr('aria-labelledby', label_id)
.data('id', rec.id)
.draggable({
revert: 'invalid',
addClasses: false,
cursorAt: { left:-10, top:12 },
helper: task_draggable_helper,
appendTo: 'body',
start: task_draggable_start,
stop: task_draggable_stop,
drag: task_draggable_move,
revertDuration: 300
});
if (window.kolab_tags_text_block) {
var tags = rec.tags || [];
if (tags.length) {
window.kolab_tags_text_block(tags, $('.tags', div));
}
}
if (is_complete(rec))
div.addClass('complete');
if (rec.flagged)
div.addClass('flagged');
if (!rec.date)
div.addClass('nodate');
if ((rec.mask & FILTER_MASK_OVERDUE))
div.addClass('overdue');
var li, inplace = false, parent = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > ul.childtasks', rcmail.gui_objects.resultlist) : null;
if (replace && (li = $('li[rel="'+replace+'"]', rcmail.gui_objects.resultlist)) && li.length) {
li.children('div.taskhead').first().replaceWith(div);
li.attr('rel', rec.id);
inplace = true;
}
else {
li = $('<li role="treeitem">')
.attr('rel', rec.id)
.addClass('taskitem')
.append((rec.collapsed ? '<span class="childtoggle collapsed"><span class="inner">▶' : '<span class="childtoggle expanded"><span class="inner">▼') + '</span></span>')
.append(div)
.append('<ul class="childtasks" role="group" style="' + (rec.collapsed ? 'display:none' : '') + '" aria-hidden="' + (rec.collapsed ? 'true' : 'false') +'"></ul>');
if (!parent || !parent.length)
li.appendTo(rcmail.gui_objects.resultlist);
}
if (!inplace && parent && parent.length)
li.appendTo(parent);
rcmail.triggerEvent('tasks-insertrow', {div: div});
if (replace) {
resort_task(rec, li, true);
// TODO: remove the item after a while if it doesn't match the current filter anymore
}
// re-set focus to taskhead element after DOM update
if (focused_task == rec.id) {
focus_task(li);
}
}
/**
* Render the given task into the tasks list (in print mode)
*/
function render_task_printmode(rec)
{
var label_id = rcmail.html_identifier(rec.id) + '-title',
div = $('<div>').addClass('taskhead')
.append($('<span class="title">').attr('id', label_id).text(rec.title)),
parent = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > ul.childtasks', rcmail.gui_objects.resultlist) : null,
li = $('<li role="treeitem">').attr('rel', rec.id).addClass('taskitem')
.append(div)
.append('<ul class="childtasks" role="group"></ul>');
if (rec.description)
div.append($('<span class="description">').text(rec.description));
/*
if (is_complete(rec))
div.addClass('complete');
if (rec.flagged)
div.addClass('flagged');
if (!rec.date)
div.addClass('nodate');
if (rec.mask & FILTER_MASK_OVERDUE)
div.addClass('overdue');
*/
if (!parent || !parent.length)
li.appendTo(rcmail.gui_objects.resultlist);
else
li.appendTo(parent);
}
/**
* Move the given task item to the right place in the list
*/
function resort_task(rec, li, animated)
{
var dir = 0, index, slice, cmp, next_li, next_id, next_rec, insert_after, past_myself;
// animated moving
var insert_animated = function(li, before, after) {
if (before && li.next().get(0) == before.get(0))
return; // nothing to do
else if (after && li.prev().get(0) == after.get(0))
return; // nothing to do
var speed = 300;
li.slideUp(speed, function(){
if (before) li.insertBefore(before);
else if (after) li.insertAfter(after);
li.slideDown(speed, function(){
if (focused_task == rec.id) {
focus_task(li);
}
});
});
}
// remove from list index
var oldlist = listindex.join('%%%');
var oldindex = $.inArray(rec.id, listindex);
if (oldindex >= 0) {
slice = listindex.slice(0,oldindex);
listindex = slice.concat(listindex.slice(oldindex+1));
}
// find the right place to insert the task item
li.parent().children('.taskitem').each(function(i, elem){
next_li = $(elem);
next_id = next_li.attr('rel');
next_rec = listdata[next_id];
if (next_id == rec.id) {
past_myself = true;
return 1; // continue
}
cmp = next_rec ? task_cmp(rec, next_rec) : 0;
if (cmp > 0 || (cmp == 0 && !past_myself)) {
insert_after = next_li;
return 1; // continue;
}
else if (next_li && cmp < 0) {
if (animated) insert_animated(li, next_li);
else li.insertBefore(next_li);
index = $.inArray(next_id, listindex);
return false; // break
}
});
if (insert_after) {
if (animated) insert_animated(li, null, insert_after);
else li.insertAfter(insert_after);
next_id = insert_after.attr('rel');
index = $.inArray(next_id, listindex);
}
// insert into list index
if (next_id && index >= 0) {
slice = listindex.slice(0,index);
slice.push(rec.id);
listindex = slice.concat(listindex.slice(index));
}
else { // restore old list index
listindex = oldlist.split('%%%');
}
}
/**
* Compare function of two task records.
* (used for sorting)
*/
function task_cmp(a, b)
{
// sort by hierarchy level first
if ((a._depth || 0) != (b._depth || 0))
return a._depth - b._depth;
var p, alt, inv = 1, c = is_complete(a) - is_complete(b), d = c;
// completed tasks always move to the end
if (c != 0)
return c;
// custom sorting
if (settings.sort_col && settings.sort_col != 'auto') {
alt = settings.sort_col == 'datetime' || settings.sort_col == 'startdatetime' ? 99999999999 : 0
d = (a[settings.sort_col]||alt) - (b[settings.sort_col]||alt);
inv = settings.sort_order == 'desc' ? -1 : 1;
}
// default sorting (auto)
else {
if (!d) d = (b._hasdate-0) - (a._hasdate-0);
if (!d) d = (a.datetime||99999999999) - (b.datetime||99999999999);
}
// fall-back to created/changed date
if (!d) d = (a.created||0) - (b.created||0);
if (!d) d = (a.changed||0) - (b.changed||0);
return d * inv;
}
/**
* Set focus on the given task item after DOM update
*/
function focus_task(li)
{
var selector = '.taskhead';
if (focused_subclass)
selector += ' .' + focused_subclass
li.find(selector).focus();
}
/**
* Determine whether the given task should be displayed as "complete"
*/
function is_complete(rec)
{
return ((rec.complete == 1.0 && !rec.status) || rec.status === 'COMPLETED') ? 1 : 0;
}
/**
*
*/
function get_all_childs(id)
{
var cid, childs = [];
for (var i=0; listdata[id].children && i < listdata[id].children.length; i++) {
cid = listdata[id].children[i];
childs.push(cid);
childs = childs.concat(get_all_childs(cid));
}
return childs;
}
/* Helper functions for drag & drop functionality */
function task_draggable_helper(e)
{
if (!task_draghelper)
task_draghelper = $('<div id="rcmdraglayer" class="taskitem-draghelper">');
var title = $(e.target).parents('li').first().find('.title:first').text();
task_draghelper.html(Q(title) || '✔');
return task_draghelper;
}
function task_draggable_start(event, ui)
{
var opts = {
hoverClass: 'droptarget',
accept: task_droppable_accept,
drop: task_draggable_dropped,
tolerance: 'pointer',
addClasses: false
};
$('.taskhead, #rootdroppable').droppable(opts);
tasklists_widget.droppable(opts);
$(this).parent().addClass('dragging');
$('#rootdroppable').show();
// enable auto-scrolling of list container
var container = $(rcmail.gui_objects.resultlist);
if (container.height() > container.parent().height()) {
task_drag_active = true;
list_scroll_top = container.parent().scrollTop();
}
}
function task_draggable_move(event, ui)
{
var scroll = 0,
mouse = rcube_event.get_mouse_pos(event),
container = $(rcmail.gui_objects.resultlist);
mouse.y -= container.parent().offset().top;
if (mouse.y < scroll_sensitivity && list_scroll_top > 0) {
scroll = -1; // up
}
else if (mouse.y > container.parent().height() - scroll_sensitivity) {
scroll = 1; // down
}
if (task_drag_active && scroll != 0) {
if (!scroll_timer)
scroll_timer = window.setTimeout(function(){ tasklist_drag_scroll(container, scroll); }, scroll_delay);
}
else if (scroll_timer) {
window.clearTimeout(scroll_timer);
scroll_timer = null;
}
}
function task_draggable_stop(event, ui)
{
$(this).parent().removeClass('dragging');
$('#rootdroppable').hide();
task_drag_active = false;
}
function task_droppable_accept(draggable)
{
if (rcmail.busy)
return false;
var drag_id = draggable.data('id'),
drop_id = $(this).data('id'),
drag_rec = listdata[drag_id] || {},
drop_rec = listdata[drop_id];
// drop target is another list
if (drag_rec && $(this).data('type') == 'tasklist') {
var drop_list = me.tasklists[drop_id],
from_list = me.tasklists[drag_rec.list];
return !drag_rec.parent_id && drop_id != drag_rec.list && drop_list && drop_list.editable && from_list && from_list.editable;
}
if (drop_rec && drop_rec.list != drag_rec.list)
return false;
if (drop_id == drag_rec.parent_id)
return false;
while (drop_rec && drop_rec.parent_id) {
if (drop_rec.parent_id == drag_id)
return false;
drop_rec = listdata[drop_rec.parent_id];
}
return true;
}
function task_draggable_dropped(event, ui)
{
var drop_id = $(this).data('id'),
task_id = ui.draggable.data('id'),
rec = listdata[task_id],
parent, li;
// dropped on another list -> move
if ($(this).data('type') == 'tasklist') {
if (rec) {
save_task({ id:rec.id, list:drop_id, _fromlist:rec.list }, 'move');
rec.list = drop_id;
}
}
// dropped on a new parent task or root
else {
parent = drop_id ? $('li[rel="'+drop_id+'"] > ul.childtasks', rcmail.gui_objects.resultlist) : $(rcmail.gui_objects.resultlist)
if (rec && parent.length) {
// submit changes to server
rec._old_parent_id = rec.parent_id;
rec.parent_id = drop_id || 0;
save_task(rec, 'edit');
li = ui.draggable.parent();
li.slideUp(300, function(){
li.appendTo(parent);
resort_task(rec, li);
li.slideDown(300);
fix_tree_toggles();
});
}
}
}
/**
* Scroll list container in the given direction
*/
function tasklist_drag_scroll(container, dir)
{
if (!task_drag_active)
return;
var old_top = list_scroll_top;
container.parent().get(0).scrollTop += scroll_step * dir;
list_scroll_top = container.parent().scrollTop();
scroll_timer = null;
if (list_scroll_top != old_top)
scroll_timer = window.setTimeout(function(){ tasklist_drag_scroll(container, dir); }, scroll_speed);
}
// check if the current user is the organizer
this.is_organizer = function(task, email)
{
if (!email) email = task.organizer ? task.organizer.email : null;
if (email)
return settings.identity.emails.indexOf(';'+email) >= 0;
return true;
};
// add the given list of participants
var add_attendees = function(names, params)
{
names = explode_quoted_string(names.replace(/,\s*$/, ''), ',');
// parse name/email pairs
var i, item, email, name, success = false;
for (i=0; i < names.length; i++) {
email = name = '';
item = $.trim(names[i]);
if (!item.length) {
continue;
}
// address in brackets without name (do nothing)
else if (item.match(/^<[^@]+@[^>]+>$/)) {
email = item.replace(/[<>]/g, '');
}
// address without brackets and without name (add brackets)
else if (rcube_check_email(item)) {
email = item;
}
// address with name
else if (item.match(/([^\s<@]+@[^>]+)>*$/)) {
email = RegExp.$1;
name = item.replace(email, '').replace(/^["\s<>]+/, '').replace(/["\s<>]+$/, '');
}
if (email) {
add_attendee($.extend({ email:email, name:name }, params));
success = true;
}
else {
rcmail.alert_dialog(rcmail.gettext('noemailwarning'));
}
}
return success;
};
// add the given attendee to the list
var add_attendee = function(data, readonly, before)
{
if (!me.selected_task)
return false;
// check for dupes...
var exists = false;
$.each(task_attendees, function(i, v) { exists |= (v.email == data.email); });
if (exists)
return false;
var dispname = Q(data.name || data.email);
dispname = '<span' + ((data.email && data.email != dispname) ? ' title="' + Q(data.email) + '"' : '') + '>' + dispname + '</span>';
// delete icon
var icon = rcmail.env.deleteicon ? '<img src="' + rcmail.env.deleteicon + '" alt="" />' : '<span class="inner">' + Q(rcmail.gettext('delete')) + '</span>';
var dellink = '<a href="#delete" class="iconlink icon button delete deletelink" title="' + Q(rcmail.gettext('delete')) + '">' + icon + '</a>';
var tooltip, status = (data.status || '').toLowerCase(),
status_label = rcmail.gettext('status' + status, 'libcalendaring');
// send invitation checkbox
var invbox = '<input type="checkbox" class="edit-attendee-reply" value="' + Q(data.email) +'" title="' + Q(rcmail.gettext('tasklist.sendinvitations')) + '" '
+ (!data.noreply && settings.itip_notify & 1 ? 'checked="checked" ' : '') + '/>';
if (data['delegated-to'])
tooltip = rcmail.gettext('libcalendaring.delegatedto') + ' ' + data['delegated-to'];
else if (data['delegated-from'])
tooltip = rcmail.gettext('libcalendaring.delegatedfrom') + ' ' + data['delegated-from'];
else if (status)
tooltip = status_label;
// add expand button for groups
if (data.cutype == 'GROUP') {
dispname += ' <a href="#expand" data-email="' + Q(data.email)
+ '" class="iconbutton add icon button expandlink" title="' + rcmail.gettext('expandattendeegroup','libcalendaring') + '">'
+ '<span class="inner">' + rcmail.gettext('expandattendeegroup','libcalendaring') + '</span></a>';
}
var elastic = $(attendees_list).parents('.no-img').length > 0;
var html = '<td class="name"><span class="attendee-name">' + dispname + '</span></td>' +
'<td class="confirmstate"><span class="attendee ' + status + '" title="' + Q(tooltip) + '">' + Q(status && !elastic ? status_label : '') + '</span></td>' +
(data.cutype != 'RESOURCE' ? '<td class="invite">' + (readonly || !invbox ? '' : invbox) + '</td>' : '') +
'<td class="options">' + (readonly ? '' : dellink) + '</td>';
var tr = $('<tr>')
.addClass(String(data.role).toLowerCase())
.html(html);
if (before)
tr.insertBefore(before)
else
tr.appendTo(attendees_list);
tr.find('a.deletelink').click({ id:(data.email || data.name) }, function(e) { remove_attendee(this, e.data.id); return false; });
tr.find('a.expandlink').click(data, function(e) { me.expand_attendee_group(e, add_attendee, remove_attendee); return false; });
tr.find('input.edit-attendee-reply').click(function() {
var enabled = $('#edit-attendees-invite:checked').length || $('input.edit-attendee-reply:checked').length;
$('#taskeditform .attendees-commentbox')[enabled ? 'show' : 'hide']();
});
// Make Elastic checkboxes pretty
if (window.UI && UI.pretty_checkbox)
$(tr).find('input[type=checkbox]').each(function() { UI.pretty_checkbox(this); });
task_attendees.push(data);
return true;
};
// event handler for clicks on an attendee link
var task_attendee_click = function(e)
{
var mailto = this.href.substr(7);
rcmail.command('compose', mailto);
return false;
};
// remove an attendee from the list
var remove_attendee = function(elem, id)
{
$(elem).closest('tr').remove();
task_attendees = $.grep(task_attendees, function(data) { return (data.name != id && data.email != id) });
};
/**
* Show task details in a dialog
*/
function task_show_dialog(id, data, temp)
{
var rec, list, elastic = false, $dialog = $('#taskshow');
if ($dialog.data('nodialog')) {
elastic = true;
$('#taskedit').addClass('hidden').parent().addClass('watermark'); // Elastic
}
else {
$dialog.filter(':ui-dialog').dialog('close');
}
rcmail.enable_command('edit-task', 'delete-task', 'save-task', 'task-history', false);
// remove status-* classes
$dialog.removeClass(function(i, oldclass) {
var oldies = String(oldclass).split(' ');
return $.grep(oldies, function(cls) { return cls.indexOf('status-') === 0 }).join(' ');
});
if (!(rec = (data || listdata[id])) || (rcmail.menu_stack && rcmail.menu_stack.length > 0))
return;
me.selected_task = rec;
list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] : {};
// hide nav buttons on mobile (elastic)
$('.content-frame-navigation > .buttons > .disabled').hide();
// for Elastic
rcmail.triggerEvent('show-content', {
mode: 'info',
title: rcmail.gettext('taskdetails', 'tasklist'),
obj: $('#taskshow').parent(),
scrollElement: $('#taskshow')
});
var status = rcmail.gettext('status-' + String(rec.status).toLowerCase(),'tasklist');
// fill dialog data
$('#task-parent-title').html(Q(rec.parent_title || '')+' »').css('display', rec.parent_title ? 'block' : 'none');
$('#task-title').html(text2html(Q(rec.title || '')));
$('#task-description').html(text2html(rec.description || '', 300, 6))[(rec.description ? 'show' : 'hide')]();
$('#task-date')[(rec.date ? 'show' : 'hide')]().find('.task-text').text(rec.date || rcmail.gettext('nodate','tasklist'));
$('#task-time').text(rec.time || '');
$('#task-start')[(rec.startdate ? 'show' : 'hide')]().find('.task-text').text(rec.startdate || '');
$('#task-starttime').text(rec.starttime || '');
$('#task-alarm')[(rec.alarms_text ? 'show' : 'hide')]().children('.task-text').html(Q(rec.alarms_text));
$('#task-completeness .task-text').html(((rec.complete || 0) * 100) + '%');
$('#task-status')[(rec.status ? 'show' : 'hide')]().children('.task-text').text(status);
$('#task-list .task-text').html(Q(me.tasklists[rec.list] ? me.tasklists[rec.list].name : ''));
$('#task-attendees, #task-organizer, #task-created-changed, #task-created, #task-changed, #task-rsvp, #task-rsvp-comment').hide();
$('#event-status-badge > span').text(status);
// tags
var taglist = $('#task-tags').hide().children('.task-text').empty();
if (window.kolab_tags_text_block) {
var tags = $.uniqueStrings((rec.tags || []).concat(get_inherited_tags(rec)));
if (tags.length) {
window.kolab_tags_text_block(tags, taglist);
$('#task-tags').show();
}
}
if (rec.status) {
$dialog.addClass('status-' + String(rec.status).toLowerCase());
}
if (rec.flagged) {
$dialog.addClass('status-flagged');
}
if (rec.recurrence && rec.recurrence_text) {
$('#task-recurrence').show().children('.task-text').html(Q(rec.recurrence_text));
}
else {
$('#task-recurrence').hide();
}
if (rec.created || rec.changed) {
$('.task-created', $dialog).text(rec.created_ || rcmail.gettext('unknown','tasklist'));
$('.task-changed', $dialog).text(rec.changed_ || rcmail.gettext('unknown','tasklist'));
$('#task-created-changed, #task-created, #task-changed').show();
}
// build attachments list
$('#task-attachments').hide();
if ($.isArray(rec.attachments)) {
task_show_attachments(rec.attachments || [], $('#task-attachments').children('.task-text'), rec);
if (rec.attachments.length > 0) {
$('#task-attachments').show();
}
}
// build attachments list
$('#task-links').hide();
if ($.isArray(rec.links) && rec.links.length) {
render_message_links(rec.links || [], $('#task-links').children('.task-text'), false, 'tasklist');
$('#task-links').show();
}
// list task attendees
if (list.attendees && rec.attendees) {
/*
// sort resources to the end
rec.attendees.sort(function(a,b) {
var j = a.cutype == 'RESOURCE' ? 1 : 0,
k = b.cutype == 'RESOURCE' ? 1 : 0;
return (j - k);
});
*/
var j, data, rsvp = false, mystatus = null, line, morelink, html = '', overflow = '',
organizer = me.is_organizer(rec);
for (j=0; j < rec.attendees.length; j++) {
data = rec.attendees[j];
if (data.email && settings.identity.emails.indexOf(';'+data.email) >= 0) {
mystatus = data.status.toLowerCase();
if (data.status == 'NEEDS-ACTION' || data.status == 'TENTATIVE' || data.rsvp)
rsvp = mystatus;
}
line = rcube_libcalendaring.attendee_html(data);
if (morelink)
overflow += line;
else
html += line;
// stop listing attendees
if (j == 7 && rec.attendees.length >= 7) {
morelink = $('<a href="#more" class="morelink"></a>').html(rcmail.gettext('andnmore', 'tasklist').replace('$nr', rec.attendees.length - j - 1));
}
}
if (html) {
$('#task-attendees').show()
.children('.task-text')
.html(html)
.find('a.mailtolink').click(task_attendee_click);
// display all attendees in a popup when clicking the "more" link
if (morelink) {
$('#task-attendees .task-text').append(morelink);
morelink.click(function(e) {
rcmail.show_popup_dialog(
'<div id="all-task-attendees" class="task-attendees">' + html + overflow + '</div>',
rcmail.gettext('tabattendees', 'tasklist'),
null,
{width: 450, modal: false}
);
$('#all-task-attendees a.mailtolink').click(task_attendee_click);
return false;
});
}
}
/*
if (mystatus && !rsvp) {
$('#task-partstat').show().children('.changersvp')
.removeClass('accepted tentative declined delegated needs-action')
.addClass(mystatus)
.children('.task-text')
.html(rcmail.gettext('status' + mystatus, 'libcalendaring'));
}
*/
var show_rsvp = !temp && rsvp && list.editable && !me.is_organizer(rec) && rec.status != 'CANCELLED';
$('#task-rsvp')[(show_rsvp ? 'show' : 'hide')]();
$('#task-rsvp .rsvp-buttons input').prop('disabled', false).filter('input[rel='+mystatus+']').prop('disabled', true);
if (show_rsvp && rec.comment) {
$('#task-rsvp-comment').show().children('.task-text').html(Q(rec.comment));
}
$('#task-rsvp a.reply-comment-toggle').show();
$('#task-rsvp .itip-reply-comment textarea').hide().val('');
if (rec.organizer && !organizer) {
$('#task-organizer').show().children('.task-text').html(rcube_libcalendaring.attendee_html($.extend(rec.organizer, { role:'ORGANIZER' })));
}
}
// define dialog buttons
var buttons = [];
if (list.editable && !rec.readonly) {
rcmail.enable_command('edit-task', 'add-child-task', true);
buttons.push({
text: rcmail.gettext('edit','tasklist'),
click: function() {
task_edit_dialog(me.selected_task.id, 'edit');
},
disabled: rcmail.busy
});
}
if (me.has_permission(list, 'td') && !rec.readonly) {
rcmail.enable_command('delete-task', true);
buttons.push({
text: rcmail.gettext('delete','tasklist'),
'class': 'delete',
click: function() {
if (delete_task(me.selected_task.id))
$dialog.dialog('close');
},
disabled: rcmail.busy
});
}
rcmail.enable_command('task-history', !!list.history);
if (!elastic) {
// open jquery UI dialog
$dialog.dialog({
modal: false,
resizable: true,
closeOnEscape: true,
title: rcmail.gettext('taskdetails', 'tasklist'),
open: function() {
$dialog.parent().find('button:not(.ui-dialog-titlebar-close)').first().focus();
},
close: function() {
$dialog.dialog('destroy').appendTo(document.body);
$('.libcal-rsvp-replymode').hide();
},
dragStart: function() {
$('.libcal-rsvp-replymode').hide();
},
resizeStart: function() {
$('.libcal-rsvp-replymode').hide();
},
buttons: buttons,
minWidth: 500,
width: 580
}).show();
// set dialog size according to content
me.dialog_resize($dialog.get(0), $dialog.height(), 580);
}
// Elastic
$dialog.removeClass('hidden').parents('.watermark').addClass('formcontainer');
$('#taskshow').parent().trigger('loaded');
// hide nav buttons on mobile (elastic)
$('.content-frame-navigation > .buttons > :not(.disabled)').show();
}
/**
*
*/
function task_history_dialog()
{
var dialog, rec = me.selected_task;
if (!rec || !rec.id || !window.libkolab_audittrail) {
return false;
}
// render dialog
$dialog = libkolab_audittrail.object_history_dialog({
module: 'tasklist',
container: '#taskhistory',
title: rcmail.gettext('objectchangelog','tasklist') + ' - ' + rec.title,
// callback function for list actions
listfunc: function(action, rev) {
var rec = $dialog.data('rec');
saving_lock = rcmail.set_busy(true, 'loading', saving_lock);
rcmail.http_post('task', { action: action, t: { id: rec.id, list:rec.list, rev: rev } }, saving_lock);
},
// callback function for comparing two object revisions
comparefunc: function(rev1, rev2) {
var rec = $dialog.data('rec');
saving_lock = rcmail.set_busy(true, 'loading', saving_lock);
rcmail.http_post('task', { action:'diff', t: { id: rec.id, list: rec.list, rev1: rev1, rev2: rev2 } }, saving_lock);
}
});
$dialog.data('rec', rec);
// fetch changelog data
saving_lock = rcmail.set_busy(true, 'loading', saving_lock);
rcmail.http_post('task', { action: 'changelog', t: { id: rec.id, list: rec.list } }, saving_lock);
}
/**
*
*/
function task_render_changelog(data)
{
var $dialog = $('#taskhistory'),
rec = $dialog.data('rec');
if (data === false || !data.length || !rec) {
// display 'unavailable' message
$('<div class="notfound-message dialog-message warning">' + rcmail.gettext('objectchangelognotavailable','tasklist') + '</div>')
.insertBefore($dialog.find('.changelog-table').hide());
return;
}
data.module = 'tasklist';
libkolab_audittrail.render_changelog(data, rec, me.tasklists[rec.list]);
// set dialog size according to content
me.dialog_resize($dialog.get(0), $dialog.height(), 600);
}
/**
*
*/
function task_show_diff(data)
{
var rec = me.selected_task,
$dialog = $("#taskdiff");
$dialog.find('div.form-section, h2.task-title-new').hide().data('set', false).find('.index').html('');
$dialog.find('div.form-section.clone').remove();
// always show event title and date
$('.task-title', $dialog).text(rec.title).removeClass('task-text-old').show();
// show each property change
$.each(data.changes, function(i, change) {
var prop = change.property, r2, html = false,
row = $('div.task-' + prop, $dialog).first();
// special case: title
if (prop == 'title') {
$('.task-title', $dialog).addClass('task-text-old').text(change['old'] || '--');
$('.task-title-new', $dialog).text(change['new'] || '--').show();
}
// no display container for this property
if (!row.length) {
return true;
}
// clone row if already exists
if (row.data('set')) {
r2 = row.clone().addClass('clone').insertAfter(row);
row = r2;
}
// render description text
if (prop == 'description') {
if (!change.diff_ && change['old']) change.old_ = text2html(change['old']);
if (!change.diff_ && change['new']) change.new_ = text2html(change['new']);
html = true;
}
// format attendees struct
else if (prop == 'attendees') {
if (change['old']) change.old_ = rcube_libcalendaring.attendee_html(change['old']);
if (change['new']) change.new_ = rcube_libcalendaring.attendee_html($.extend({}, change['old'] || {}, change['new']));
html = true;
}
// localize status
else if (prop == 'status') {
if (change['old']) change.old_ = rcmail.gettext('status-'+String(change['old']).toLowerCase(), 'tasklist');
if (change['new']) change.new_ = rcmail.gettext('status-'+String(change['new']).toLowerCase(), 'tasklist');
}
// format attachments struct
if (prop == 'attachments') {
if (change['old']) task_show_attachments([change['old']], row.children('.task-text-old'), rec, false);
else row.children('.task-text-old').text('--');
if (change['new']) task_show_attachments([$.extend({}, change['old'] || {}, change['new'])], row.children('.task-text-new'), rec, false);
else row.children('.task-text-new').text('--');
// remove click handler in diff view
$('.attachmentslist li a', row).unbind('click').removeAttr('href');
}
else if (change.diff_) {
row.children('.task-text-diff').html(change.diff_);
row.children('.task-text-old, .task-text-new').hide();
}
else {
if (!html) {
// escape HTML characters
change.old_ = Q(change.old_ || change['old'] || '--')
change.new_ = Q(change.new_ || change['new'] || '--')
}
row.children('.task-text-old').html(change.old_ || change['old'] || '--').show();
row.children('.task-text-new').html(change.new_ || change['new'] || '--').show();
}
// display index number
if (typeof change.index != 'undefined') {
row.find('.index').html('(' + change.index + ')');
}
row.show().data('set', true);
});
// open jquery UI dialog
$dialog.dialog({
modal: false,
resizable: true,
closeOnEscape: true,
title: rcmail.gettext('objectdiff','tasklist').replace('$rev1', data.rev1).replace('$rev2', data.rev2) + ' - ' + rec.title,
open: function() {
$dialog.attr('aria-hidden', 'false');
},
close: function() {
$dialog.dialog('destroy').attr('aria-hidden', 'true').hide();
},
buttons: [
{
text: rcmail.gettext('close'),
click: function() { $dialog.dialog('close'); },
autofocus: true
}
],
minWidth: 320,
width: 450
}).show();
// set dialog size according to content
me.dialog_resize($dialog.get(0), $dialog.height(), 400);
}
// close the event history dialog
function close_history_dialog()
{
$('#taskhistory, #taskdiff').each(function(i, elem) {
var $dialog = $(elem);
if ($dialog.is(':ui-dialog'))
$dialog.dialog('close');
});
};
/**
* Opens the dialog to edit a task
*/
function task_edit_dialog(id, action, presets)
{
var elastic = false, infodialog = $('#taskshow');
if (infodialog.data('nodialog') || $('#taskedit').data('nodialog')) {
elastic = true;
infodialog.addClass('hidden').parent().addClass('watermark'); // Elastic
}
else {
infodialog.filter(':ui-dialog').dialog('close');
}
rcmail.enable_command('edit-task', 'delete-task', 'add-child-task', 'save-task', 'task-history', false);
var selected_list, rec = listdata[id] || presets,
$dialog = $('<div>'),
editform = $('#taskedit'),
readonly = rec && rec.readonly,
list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] :
(me.selected_list ? me.tasklists[me.selected_list] : { editable: action == 'new', rights: action == 'new' ? 'rwitd' : 'r' });
if (rcmail.busy || !me.has_permission(list, 'i') || (action == 'edit' && (!rec || rec.readonly)))
return false;
// hide nav buttons on mobile (elastic)
$('.content-frame-navigation > .buttons > .disabled').hide();
if (action == 'new') {
$(rcmail.gui_objects.resultlist).find('.selected').removeClass('selected');
}
// for Elastic
rcmail.triggerEvent('show-content', {
mode: rec.uid ? 'edit' : 'add',
title: rcmail.gettext(rec.uid ? 'edittask' : 'newtask', 'tasklist'),
obj: $('#taskedit').parent(),
scrollElement: $('#taskedit')
});
me.selected_task = $.extend({ valarms:[] }, rec); // clone task object
rec = me.selected_task;
// assign temporary id
if (!me.selected_task.id)
me.selected_task.id = -(++idcount);
// reset dialog first
$('#taskeditform').get(0).reset();
// allow other plugins to do actions when task form is opened
rcmail.triggerEvent('tasklist-task-init', {o: rec});
// fill form data
var title = $('#taskedit-title').val(rec.title || '');
var description = $('#taskedit-description').val(rec.description || '');
var recdate = $('#taskedit-date').val(rec.date || '');
var rectime = $('#taskedit-time').val(rec.time || '');
var recstartdate = $('#taskedit-startdate').val(rec.startdate || '');
var recstarttime = $('#taskedit-starttime').val(rec.starttime || '');
var complete = $('#taskedit-completeness').val((rec.complete || 0) * 100);
completeness_slider.slider('value', complete.val());
var taskstatus = $('#taskedit-status').val(rec.status || '');
var tasklist = $('#taskedit-tasklist').prop('disabled', rec.parent_id ? true : false);
var notify = $('#edit-attendees-donotify').get(0);
var invite = $('#edit-attendees-invite').get(0);
var comment = $('#edit-attendees-comment');
var taglist;
invite.checked = settings.itip_notify & 1 > 0;
notify.checked = me.has_attendees(rec) && invite.checked;
// set tasklist selection according to permissions
tasklist.find('option').each(function(i, opt) {
var l = me.tasklists[opt.value] || {},
writable = l.editable || (action == 'new' && me.has_permission(l, 'i'));
$(opt).prop('disabled', !writable);
if (!selected_list && writable)
selected_list = opt.value;
});
tasklist.val(rec.list || me.selected_list || selected_list);
// tag-edit line
var tagline = $('#taskedit-tagline');
if (window.kolab_tags_input) {
tagline.parent().show();
taglist = kolab_tags_input(tagline, rec.tags, readonly);
}
else {
tagline.parent().hide();
}
// set alarm(s)
me.set_alarms_edit('#taskedit-alarms', action != 'new' && rec.valarms ? rec.valarms : []);
if ($.isArray(rec.links) && rec.links.length) {
render_message_links(rec.links, $('#taskedit-links .task-text'), true, 'tasklist');
$('#taskedit-links').show();
}
else {
$('#taskedit-links').hide();
}
// set recurrence
me.set_recurrence_edit(rec);
// init attendees tab
var organizer = !rec.attendees || me.is_organizer(rec),
allow_invitations = organizer || (rec.owner && rec.owner == 'anonymous') || settings.invite_shared;
task_attendees = [];
attendees_list = $('#edit-attendees-table > tbody').html('');
$('#edit-attendees-notify')[(allow_invitations && me.has_attendees(rec) && (settings.itip_notify & 2) ? 'show' : 'hide')]();
$('#edit-localchanges-warning')[(me.has_attendees(rec) && !(allow_invitations || (rec.owner && me.is_organizer(rec, rec.owner))) ? 'show' : 'hide')]();
// attendees (aka assignees)
if (list.attendees) {
var j, data, reply_selected = 0;
if (rec.attendees) {
for (j=0; j < rec.attendees.length; j++) {
data = rec.attendees[j];
add_attendee(data, !allow_invitations);
if (allow_invitations && !data.noreply) {
reply_selected++;
}
}
}
// make sure comment box is visible if at least one attendee has reply enabled
// or global "send invitations" checkbox is checked
$('#taskeditform .attendees-commentbox')[(reply_selected || invite.checked ? 'show' : 'hide')]();
// select the correct organizer identity
var identity_id = 0;
$.each(settings.identities, function(i,v) {
if (!rec.organizer || v == rec.organizer.email) {
identity_id = i;
return false;
}
});
$('#edit-tab-attendees').show(); // Larry
$('a[href="#taskedit-panel-attendees"]').parent().show(); // Elastic
$('#edit-attendees-form')[(allow_invitations?'show':'hide')]();
$('#edit-identities-list').val(identity_id);
$('#taskedit-organizer')[(organizer ? 'show' : 'hide')]();
}
else {
$('#edit-tab-attendees').hide(); // Larry
$('a[href="#taskedit-panel-attendees"]').parent().hide(); // Elastic
}
// attachments
rcmail.enable_command('remove-attachment', 'upload-file', list.editable);
me.selected_task.deleted_attachments = [];
// we're sharing some code for uploads handling with app.js
rcmail.env.attachments = [];
rcmail.env.compose_id = me.selected_task.id; // for rcmail.async_upload_form()
if ($.isArray(rec.attachments)) {
task_show_attachments(rec.attachments, $('#taskedit-attachments'), rec, true);
}
else {
$('#taskedit-attachments > ul').empty();
}
// show/hide tabs according to calendar's feature support
$('#taskedit-tab-attachments')[(list.attachments||rec.attachments?'show':'hide')](); // Larry
$('a[href="#taskedit-panel-attachments"]').parent()[(list.attachments||rec.attachments?'show':'hide')](); // Elastic
// activate the first tab
$('#taskedit:not([data-notabs])').tabs('option', 'active', 0); // Larry
if ($('#taskedit').data('notabs'))
$('#taskedit li.nav-item:first-child a').tab('show'); // Elastic
// define dialog buttons
var buttons = [], save_func = function() {
var data = me.selected_task;
data._status_before = me.selected_task.status + '';
// copy form field contents into task object to save
$.each({ title:title, description:description, date:recdate, time:rectime, startdate:recstartdate, starttime:recstarttime, status:taskstatus }, function(key,input){
data[key] = input.val();
});
data.list = tasklist.find('option:selected').val();
data.tags = taglist ? kolab_tags_input_value(taglist) : [];
data.attachments = [];
data.attendees = task_attendees;
data.valarms = me.serialize_alarms('#taskedit-alarms');
data.recurrence = me.serialize_recurrence(rectime.val());
// do some basic input validation
if (!data.title || !data.title.length) {
title.focus();
return false;
}
else if (data.startdate && data.date) {
var startdate = $.datepicker.parseDate(me.datepicker_settings.dateFormat, data.startdate, me.datepicker_settings);
var duedate = $.datepicker.parseDate(me.datepicker_settings.dateFormat, data.date, me.datepicker_settings);
if (startdate > duedate) {
rcmail.alert_dialog(rcmail.gettext('invalidstartduedates', 'tasklist'));
return false;
}
else if ((data.time == '') != (data.starttime == '')) {
rcmail.alert_dialog(rcmail.gettext('invalidstartduetimes', 'tasklist'));
return false;
}
}
else if (data.recurrence && !data.startdate && !data.date) {
rcmail.alert_dialog(rcmail.gettext('recurrencerequiresdate', 'tasklist'));
return false;
}
// uploaded attachments list
for (var i in rcmail.env.attachments) {
if (i.match(/^rcmfile(.+)/))
data.attachments.push(RegExp.$1);
}
// task assigned to a new list
if (data.list && listdata[id] && data.list != listdata[id].list) {
data._fromlist = list.id;
}
data.complete = complete.val() / 100;
if (isNaN(data.complete))
data.complete = null;
else if (data.complete == 1.0 && rec.status === '')
data.status = 'COMPLETED';
if (!data.list && list.id)
data.list = list.id;
if (!data.tags.length)
data.tags = '';
if (organizer) {
data._identity = $('#edit-identities-list option:selected').val();
delete data.organizer;
}
// per-attendee notification suppression
var need_invitation = false;
if (allow_invitations) {
$.each(data.attendees, function (i, v) {
if (v.role != 'ORGANIZER') {
if ($('input.edit-attendee-reply[value="' + v.email + '"]').prop('checked')) {
need_invitation = true;
delete data.attendees[i]['noreply'];
}
else if (settings.itip_notify > 0) {
data.attendees[i].noreply = 1;
}
}
});
}
// tell server to send notifications
if ((me.has_attendees(data) || (rec.id && me.has_attendees(rec))) && allow_invitations && (notify.checked || invite.checked || need_invitation)) {
data._notify = settings.itip_notify;
data._comment = comment.val();
}
else if (data._notify) {
delete data._notify;
}
if (save_task(data, action) && !elastic)
$dialog.dialog('close');
};
rcmail.enable_command('save-task', true);
rcmail.env.task_save_func = save_func;
buttons.push({
text: rcmail.gettext('save', 'tasklist'),
'class': 'mainaction',
click: save_func
});
if (action != 'new') {
rcmail.enable_command('delete-task', true);
buttons.push({
text: rcmail.gettext('delete', 'tasklist'),
'class': 'delete',
click: function() {
if (delete_task(rec.id))
$dialog.dialog('close');
}
});
}
buttons.push({
text: rcmail.gettext('cancel', 'tasklist'),
click: function() {
$dialog.dialog('close');
}
});
// open jquery UI dialog
if (!elastic) {
$dialog.dialog({
modal: true,
resizable: (!bw.ie6 && !bw.ie7), // disable for performance reasons
closeOnEscape: false,
title: rcmail.gettext((action == 'edit' ? 'edittask' : 'newtask'), 'tasklist'),
close: function() {
rcmail.ksearch_blur();
editform.hide().appendTo(document.body);
$dialog.dialog('destroy').remove();
},
buttons: buttons,
minHeight: 460,
minWidth: 500,
width: 580
}).append(editform.show()); // adding form content AFTERWARDS massively speeds up opening on IE
// set dialog size according to content
me.dialog_resize($dialog.get(0), $dialog.height(), 580);
}
// Elastic
editform.removeClass('hidden').parents('.watermark').addClass('formcontainer');
$('#taskedit').parent().trigger('loaded');
title.select();
// show nav buttons on mobile (elastic)
$('.content-frame-navigation > .buttons > :not(.disabled)').show();
}
/**
* Open a task attachment either in a browser window for inline view or download it
*/
function load_attachment(data)
{
var rec = data.record;
// can't open temp attachments
if (!rec.id || rec.id < 0)
return false;
var query = {_id: data.attachment.id, _t: rec.recurrence_id || rec.id, _list: rec.list};
if (rec.rev)
query._rev = rec.rev;
libkolab.load_attachment(query, data.attachment);
};
/**
* Build task attachments list
*/
function task_show_attachments(list, container, task, edit)
{
libkolab.list_attachments(list, container, edit, task,
function(id) { remove_attachment(id); },
function(data) { load_attachment(data); }
);
};
/**
*
*/
function remove_attachment(id)
{
me.selected_task.deleted_attachments.push(id);
}
/**
*
*/
function remove_link(elem)
{
var $elem = $(elem), uri = $elem.attr('data-uri');
// remove the link item matching the given uri
me.selected_task.links = $.grep(me.selected_task.links, function(link) { return link.uri != uri; });
// remove UI list item
$elem.hide().closest('li').addClass('deleted');
}
/**
*
*/
function add_childtask(id)
{
if (rcmail.busy)
return false;
var rec = listdata[id];
task_edit_dialog(null, 'new', { parent_id:id, list:rec.list });
}
/**
* Delete the given task
*/
function delete_task(id)
{
var rec = listdata[id];
if (!rec || rec.readonly || rcmail.busy)
return false;
var html, buttons = [], $dialog = $('<div>');
// Subfunction to submit the delete command after confirm
var _delete_task = function(id, mode) {
var rec = listdata[id],
li = $('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).hide(),
decline = $dialog.find('input.confirm-attendees-decline:checked').length,
notify = $dialog.find('input.confirm-attendees-notify:checked').length;
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('task', { action:'delete', t:{ id:rec.id, list:rec.list, _decline:decline, _notify:notify }, mode:mode, filter:filtermask });
// move childs to parent/root
if (mode != 1 && rec.children !== undefined) {
var parent_node = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > .childtasks', rcmail.gui_objects.resultlist) : null;
if (!parent_node || !parent_node.length)
parent_node = rcmail.gui_objects.resultlist;
$.each(rec.children, function(i,cid) {
var child = listdata[cid];
child.parent_id = rec.parent_id;
resort_task(child, $('li[rel="'+cid+'"]').appendTo(parent_node), true);
});
}
li.remove();
delete listdata[id];
}
if (rec.children && rec.children.length) {
html = rcmail.gettext('deleteparenttasktconfirm','tasklist');
buttons.push({
text: rcmail.gettext('deletethisonly','tasklist'),
'class': 'delete',
click: function() {
_delete_task(id, 0);
$(this).dialog('close');
}
});
buttons.push({
text: rcmail.gettext('deletewithchilds','tasklist'),
'class': 'delete',
click: function() {
_delete_task(id, 1);
$(this).dialog('close');
}
});
}
else {
html = rcmail.gettext('deletetasktconfirm','tasklist');
buttons.push({
text: rcmail.gettext('delete','tasklist'),
'class': 'delete',
click: function() {
_delete_task(id, 0);
$(this).dialog('close');
}
});
}
if (me.is_attendee(rec)) {
html += '<div class="dialog-message">' +
'<label><input class="confirm-attendees-decline pretty-checkbox" type="checkbox" checked="checked" value="1" name="_decline" /> ' +
rcmail.gettext('itipdeclinetask', 'tasklist') +
'</label></div>';
}
else if (me.has_attendees(rec) && me.is_organizer(rec)) {
html += '<div class="dialog-message">' +
'<label><input class="confirm-attendees-notify pretty-checkbox" type="checkbox" checked="checked" value="1" name="_notify" /> ' +
rcmail.gettext('sendcancellation', 'tasklist') +
'</label></div>';
}
buttons.push({
text: rcmail.gettext('cancel', 'tasklist'),
'class': 'cancel',
click: function() {
$(this).dialog('close');
}
});
$dialog.html(html);
$dialog.dialog({
modal: true,
width: 520,
dialogClass: 'warning no-close',
title: rcmail.gettext('deletetask', 'tasklist'),
buttons: buttons,
close: function(){
$dialog.dialog('destroy').hide();
}
}).addClass('tasklist-confirm').show();
return true;
}
/**
* Check if the given task matches the current filtermask and tag selection
*/
function match_filter(rec, cache, recursive)
{
if (!rec)
return false;
// return cached result
if (typeof cache[rec.id] != 'undefined' && recursive != 2) {
return cache[rec.id];
}
var match = !filtermask || (filtermask & rec.mask) == filtermask;
// in focusview mode, only tasks from the selected list are allowed
if (focusview)
match = $.inArray(rec.list, focusview_lists) >= 0 && match;
if (match && tagsfilter.length) {
match = rec.tags && rec.tags.length;
var alltags = get_inherited_tags(rec).concat(rec.tags || []);
for (var i=0; match && i < tagsfilter.length; i++) {
if ($.inArray(tagsfilter[i], alltags) < 0)
match = false;
}
}
// check if a child task matches the tags
if (!match && (recursive||0) < 2 && rec.children && rec.children.length) {
for (var j=0; !match && j < rec.children.length; j++) {
match = match_filter(listdata[rec.children[j]], cache, 1);
}
}
// walk up the task tree and check if a parent task matches
var parent_id;
if (!match && !recursive && (parent_id = rec.parent_id)) {
while (!match && parent_id && listdata[parent_id]) {
match = match_filter(listdata[parent_id], cache, 2);
parent_id = listdata[parent_id].parent_id;
}
}
if (recursive != 1) {
cache[rec.id] = match;
}
return match;
}
/**
*
*/
function get_inherited_tags(rec)
{
var parent_id, itags = [];
if ((parent_id = rec.parent_id)) {
while (parent_id && listdata[parent_id]) {
itags = itags.concat(listdata[parent_id].tags || []);
parent_id = listdata[parent_id].parent_id;
}
}
return $.uniqueStrings(itags);
}
/**
* Change tasks list sorting
*/
function list_set_sort(col)
{
list_set_sort_and_order(col, settings.sort_order);
}
/**
* Change tasks list sort order
*/
function list_set_order(order)
{
list_set_sort_and_order(settings.sort_col, order);
}
/**
* Change tasks list sorting and/or order
*/
function list_set_sort_and_order(col, order)
{
var col_change = settings.sort_col != col,
ord_change = settings.sort_order != order;
if (col_change || ord_change) {
settings.sort_col = col;
settings.sort_order = order;
// re-sort list index and re-render list
listindex.sort(function(a, b) { return task_cmp(listdata[a], listdata[b]); });
render_tasklist();
rcmail.enable_command('list-order', settings.sort_col != 'auto');
if (col_change)
rcmail.save_pref({ name: 'tasklist_sort_col', value: (col == 'auto' ? '' : col) });
if (ord_change)
rcmail.save_pref({ name: 'tasklist_sort_order', value: order });
$('#taskviewsortmenu .sortcol').attr('aria-checked', 'false').removeClass('selected')
.filter('.by-' + col).attr('aria-checked', 'true').addClass('selected');
$('#taskviewsortmenu .sortorder').removeClass('selected')
.filter('[aria-checked=true]').addClass('selected');
}
}
function get_setting(name)
{
return settings[name];
}
/**
* Dialog for tasks list creation/update
*/
function list_edit_dialog(id)
{
var list = me.tasklists[id] || {name: '', editable: true, rights: 'riwta', showalarms: true},
title = rcmail.gettext((list.id ? 'editlist' : 'createlist'), 'tasklist'),
params = {action: (list.id ? 'form-edit' : 'form-new'), l: { id:list.id }, _framed: 1},
$dialog = $('<iframe>').attr('src', rcmail.url('tasklist', params)).on('load', function() {
$(this).contents().find('#taskedit-showalarms')
.prop('checked', list.showalarms);
$(this).contents().find('#taskedit-tasklistname')
.prop('disabled', !me.has_permission(list, 'a') || list.norename)
.val(list.editname || list.name)
.select();
}),
save_func = function() {
var data,
form = $dialog.contents().find('#tasklisteditform'),
name = form.find('#taskedit-tasklistname');
// form is not loaded
if (!form || !form.length)
return false;
// do some input validation
if (!name.val() || name.val().length < 2) {
rcmail.alert_dialog(rcmail.gettext('invalidlistproperties', 'tasklist'), function() {
name.select();
});
return false;
}
// post data to server
data = form.serializeJSON();
if (list.id)
data.id = list.id;
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('tasklist', {action: (list.id ? 'edit' : 'new'), l: data});
return true;
};
rcmail.simple_dialog($dialog, title, save_func, {
width: 600,
height: 400
});
}
/**
*
*/
function list_delete(id)
{
var list = me.tasklists[id];
if (list && !list.norename) {
rcmail.confirm_dialog(rcmail.gettext(list.children ? 'deletelistconfirmrecursive' : 'deletelistconfirm', 'tasklist'), 'delete', function() {
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('tasklist', { action:'delete', l:{ id:list.id } });
});
}
return false;
}
/**
*
*/
function list_remove(id)
{
var list = me.tasklists[id];
if (list && list.removable) {
destroy_list(list);
rcmail.http_post('tasklist', { action:'subscribe', l:{ id:list.id, active:0, permanent:0, recursive:1 } });
}
}
/**
* Callback from server to finally remove the given list
*/
function destroy_list(prop)
{
var li, delete_ids = [],
list = me.tasklists[prop.id];
// find sub-lists
if (list && list.children) {
for (var child_id in me.tasklists) {
if (String(child_id).indexOf(prop.id) == 0)
delete_ids.push(child_id);
}
}
else {
delete_ids.push(prop.id);
}
// delete all subfolders in the list
for (var i=0; i < delete_ids.length; i++) {
id = delete_ids[i];
list = me.tasklists[id];
tasklists_widget.remove(id);
if (list) {
list.active = false;
// delete me.tasklists[prop.id];
unlock_saving();
remove_tasks(list.id);
}
$("#taskedit-tasklist option[value='"+id+"']").remove();
}
}
/**
*
*/
function insert_list(prop)
{
if (prop._reload) {
rcmail.redirect(rcmail.url(''));
return;
}
tasklists_widget.insert({
id: prop.id,
classes: [ prop.group || '' ],
virtual: prop.virtual,
html: prop.html
}, prop.parent || null, prop.group);
// flag as tasklist for drag & drop
- $(tasklists_widget.get_item(prop.id)).data('type', 'tasklist');
+ var li = $(tasklists_widget.get_item(prop.id)).data('type', 'tasklist');
delete prop.html;
me.tasklists[prop.id] = prop;
// append to list selector in task edit dialog, too (#2985)
$('<option>').attr('value', prop.id).html(Q(prop.name)).appendTo('#taskedit-tasklist');
+
+ // Elastic skin
+ if (window.UI && UI.pretty_checkbox) {
+ UI.pretty_checkbox($(li).find('input'));
+ }
}
/**
*
*/
function update_list(prop)
{
var id = prop.oldid || prop.id,
li = tasklists_widget.get_item(id);
if (prop._reload) {
rcmail.redirect(rcmail.url(''));
return;
}
if (me.tasklists[id] && li) {
delete me.tasklists[id];
me.tasklists[prop.id] = prop;
$(li).find('input').first().val(prop.id);
$(li).find('.listname').first().html(Q(prop.name));
tasklists_widget.update(id, {id: prop.id, html: $(li).children().first()});
}
}
/**
* Display tasklist CalDAV URL dialog
*/
function list_showurl(id)
{
var list = me.tasklists[id];
if (list && list.caldavurl) {
var dialog = $('<div>').addClass('showurldialog').append('<p>'+rcmail.gettext('caldavurldescription', 'tasklist')+'</p>'),
textbox = $('<textarea>').addClass('urlbox form-control').css('width', '100%')
.attr({rows: 3, readonly: true}).appendTo(dialog);
rcmail.simple_dialog(dialog, 'tasklist.showcaldavurl', null, {
open: function() { textbox.val(list.caldavurl).select(); },
cancel_button: 'close'
});
}
}
/**
* Execute search
*/
function quicksearch()
{
var q;
if (rcmail.gui_objects.qsearchbox && (q = rcmail.gui_objects.qsearchbox.value)) {
var id = 'search-'+q;
var resources = [];
for (var rid in me.tasklists) {
if (me.tasklists[rid].active) {
resources.push(rid);
}
}
id += '@'+resources.join(',');
// ignore if query didn't change
if (search_request == id)
return;
search_request = id;
search_query = q;
list_tasks();
}
else // empty search input equals reset
this.reset_search();
}
/**
* Reset search and get back to normal listing
*/
function reset_search()
{
$(rcmail.gui_objects.qsearchbox).val('');
if (search_request) {
search_request = search_query = null;
list_tasks();
}
}
/**
* Method to show the tasks print page.
*/
function print_tasks()
{
var param = {}, active = active_lists();
if (active.length) {
param = {filter: filtermask, lists: active.join(','), q: search_query};
rcmail.open_window(rcmail.url('print', param), true, true);
}
};
/**** Utility functions ****/
// same as str.split(delimiter) but it ignores delimiters within quoted strings
var explode_quoted_string = function(str, delimiter)
{
var result = [],
strlen = str.length,
q, p, i, chr, last;
for (q = p = i = 0; i < strlen; i++) {
chr = str.charAt(i);
if (chr == '"' && last != '\\') {
q = !q;
}
else if (!q && chr == delimiter) {
result.push(str.substring(p, i));
p = i + 1;
}
last = chr;
}
result.push(str.substr(p));
return result;
};
/**
* Clear any text selection
* (text is probably selected when double-clicking somewhere)
*/
function clearSelection()
{
if (document.selection && document.selection.empty) {
document.selection.empty() ;
}
else if (window.getSelection) {
var sel = window.getSelection();
if (sel && sel.removeAllRanges)
sel.removeAllRanges();
}
}
/**
* Check whether the event target is a descentand of the given element
*/
function target_overlaps(target, elem)
{
while (target.parentNode) {
if (target.parentNode == elem)
return true;
target = target.parentNode;
}
return false;
}
/**
*
*/
function active_lists()
{
var active = [];
for (var id in me.tasklists) {
if (me.tasklists[id].active)
active.push(id);
}
return active;
}
/**
* Enable/disable focusview mode for the given list
*/
function set_focusview(id, shift)
{
var in_focus = $.inArray(id, focusview_lists) >= 0,
li = $(tasklists_widget.get_item(id)).find('.tasklist').first();
// remove list from focusview
if (in_focus && shift && id !== null) {
focusview_lists = $.grep(focusview_lists, function(list_id) { return list_id != id; });
}
else {
if (!shift || id === null) {
focusview_lists = [];
// uncheck all active focusview icons
tasklists_widget.container.find('div.focusview')
.removeClass('focusview')
.find('a.quickview').attr('aria-checked', 'false');
}
if (!in_focus && id !== null) {
focusview_lists.push(id)
}
}
focusview = focusview_lists.length > 0;
// activate list if necessary
if (focusview && !me.tasklists[id].active) {
li.find('input[type=checkbox]').get(0).checked = true;
me.tasklists[id].active = true;
fetch_counts();
}
// update list
list_tasks(null);
if (focusview) {
li[in_focus ? 'removeClass' : 'addClass']('focusview')
.find('a.quickview').attr('aria-checked', in_focus ? 'false' : 'true');
$('body').addClass('quickview-active');
}
else {
$('body').removeClass('quickview-active');
}
}
// init dialog by default
init_taskedit();
}
// equivalent to $.unique() but working on arrays of strings
jQuery.uniqueStrings = (function() {
return function(arr) {
var hash = {}, out = [];
for (var i = 0; i < arr.length; i++) {
hash[arr[i]] = 0;
}
for (var val in hash) {
out.push(val);
}
return out;
};
})();
/**
* Tasks list options dialog
*/
function tasklist_options_menu(p)
{
var content = $('#listoptions-menu'),
width = content.width() + 25,
dialog = content.clone(true);
// set form values
$('#options-sortcol', dialog).val(rctasks.get_setting('sort_col') || 'auto');
$('#options-ord', dialog).val(rctasks.get_setting('sort_order') || 'asc');
// Fix id/for attributes
$('select', dialog).each(function() { this.id = this.id + '-clone'; });
$('label', dialog).each(function() { $(this).attr('for', $(this).attr('for') + '-clone'); });
var save_func = function(e) {
if (rcube_event.is_keyboard(e.originalEvent)) {
$('#listmenulink').focus();
}
var col = $('#options-sortcol-clone', dialog).val(),
ord = $('#options-ord-clone', dialog).val();
rctasks.list_set_sort_and_order(col, ord);
return true;
};
dialog = rcmail.simple_dialog(dialog, rcmail.gettext('listoptionstitle'), save_func, {
closeOnEscape: true,
minWidth: 400,
width: width
});
};
/* tasklist plugin UI initialization */
var rctasks;
window.rcmail && rcmail.addEventListener('init', function(evt) {
rctasks = new rcube_tasklist_ui($.extend(rcmail.env.tasklist_settings, rcmail.env.libcal_settings));
// register button commands
rcmail.register_command('newtask', function(){ rctasks.edit_task(null, 'new', {}); }, true);
rcmail.register_command('print', function(){ rctasks.print_tasks(); }, true);
rcmail.register_command('import', function(){ rctasks.import_tasks(rctasks.selected_list); }, true);
rcmail.register_command('edit-task', function(){ rctasks.edit_task(rctasks.selected_task.id, 'edit'); });
rcmail.register_command('delete-task', function(){ rctasks.delete_task(rctasks.selected_task.id); });
rcmail.register_command('add-child-task', function(){ rctasks.add_childtask(rctasks.selected_task.id); });
rcmail.register_command('save-task', function(){ rcmail.env.task_save_func(); });
rcmail.register_command('list-create', function(){ rctasks.list_edit_dialog(null); }, true);
rcmail.register_command('list-edit', function(){ rctasks.list_edit_dialog(rctasks.selected_list); }, false);
rcmail.register_command('list-delete', function(){ rctasks.list_delete(rctasks.selected_list); }, false);
rcmail.register_command('list-remove', function(){ rctasks.list_remove(rctasks.selected_list); }, false);
rcmail.register_command('list-showurl', function(){ rctasks.list_showurl(rctasks.selected_list); }, false);
rcmail.register_command('export', function(){ rctasks.export_tasks(); }, true);
rcmail.register_command('search', function(){ rctasks.quicksearch(); }, true);
rcmail.register_command('reset-search', function(){ rctasks.reset_search(); }, true);
rcmail.register_command('expand-all', function(){ rctasks.expand_collapse(true); }, true);
rcmail.register_command('collapse-all', function(){ rctasks.expand_collapse(false); }, true);
rctasks.init();
});
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Sep 15, 7:24 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
287498
Default Alt Text
(127 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment