mirror of
https://github.com/YunoHost-Apps/agendav_ynh.git
synced 2024-09-03 20:36:12 +02:00
2044 lines
54 KiB
JavaScript
2044 lines
54 KiB
JavaScript
/*
|
|
* Copyright 2011-2012 Jorge López Pérez <jorge@adobo.org>
|
|
*
|
|
* This file is part of AgenDAV.
|
|
*
|
|
* AgenDAV is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* AgenDAV 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with AgenDAV. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// Useful names
|
|
var ved = 'div.view_event_details';
|
|
var ced = '#com_event_dialog';
|
|
var dustbase = {};
|
|
|
|
|
|
$(document).ready(function() {
|
|
// Load i18n strings
|
|
// TODO: language
|
|
load_i18n_strings();
|
|
|
|
// Dust.js i18n helper
|
|
dust.helpers.i18n = function i18n(chunk, context, bodies, params) {
|
|
var i18n_params = {};
|
|
var i18n_name = params.name;
|
|
var i18n_type = params.type;
|
|
|
|
delete params.name;
|
|
delete params.type;
|
|
|
|
for (var key in params) {
|
|
if (params.hasOwnProperty(key)) {
|
|
var param_name = '%' + key;
|
|
i18n_params[param_name] = dust.helpers.tap(params[key],
|
|
chunk, context);
|
|
}
|
|
}
|
|
return chunk.write(t(i18n_type, i18n_name, i18n_params));
|
|
};
|
|
|
|
|
|
|
|
// Login page: focus first input field
|
|
if ($('body').hasClass('loginpage')) {
|
|
$('input:submit').button();
|
|
$('input[name="user"]').focus();
|
|
} else if ($('body').hasClass('prefspage')) {
|
|
$('#prefs_tabs').tabs();
|
|
$('#prefs_buttons button').button();
|
|
$('#return_button').on('click', function() {
|
|
window.location = base_app_url;
|
|
return false;
|
|
});
|
|
$('#save_button').on('click', function() {
|
|
var thisform = $('#prefs_form');
|
|
proceed_send_ajax_form(thisform,
|
|
function(data) {
|
|
show_success(
|
|
t('messages', 'info_prefssaved'),
|
|
'');
|
|
},
|
|
function(data) {
|
|
show_error(t('messages', 'error_invalidinput'), data);
|
|
},
|
|
function(data) { });
|
|
});
|
|
} else if ($('body').hasClass('calendarpage')) {
|
|
// Dust.js base context
|
|
dustbase = dust.makeBase({
|
|
default_calendar_color: default_calendar_color,
|
|
base_url: base_url,
|
|
base_app_url: base_app_url,
|
|
csrf_token_name: AgenDAVConf.prefs_csrf_token_name,
|
|
enable_calendar_sharing: enable_calendar_sharing
|
|
});
|
|
|
|
// Default colorpicker options
|
|
set_default_colorpicker_options();
|
|
|
|
// Enable full calendar
|
|
// TODO: configurable!
|
|
$('#calendar_view').fullCalendar({
|
|
selectable: true,
|
|
editable: true,
|
|
firstDay: AgenDAVConf.prefs_firstday,
|
|
timeFormat: {
|
|
agenda: AgenDAVConf.prefs_timeformat + '{ - '
|
|
+ AgenDAVConf.prefs_timeformat + '}',
|
|
'': AgenDAVConf.prefs_timeformat
|
|
},
|
|
columnFormat: {
|
|
month: AgenDAVConf.prefs_format_column_month,
|
|
week: AgenDAVConf.prefs_format_column_week,
|
|
day: AgenDAVConf.prefs_format_column_day,
|
|
table: AgenDAVConf.prefs_format_column_table
|
|
},
|
|
titleFormat: {
|
|
month: AgenDAVConf.prefs_format_title_month,
|
|
week: AgenDAVConf.prefs_format_title_week,
|
|
day: AgenDAVConf.prefs_format_title_day,
|
|
table: AgenDAVConf.prefs_format_title_table
|
|
},
|
|
currentTimeIndicator: true,
|
|
weekMode: 'liquid',
|
|
height: calendar_height(),
|
|
windowResize: function(view) {
|
|
$(this).fullCalendar('option', 'height', calendar_height());
|
|
},
|
|
header: {
|
|
left: 'month,agendaWeek,agendaDay table',
|
|
center: 'title',
|
|
right: 'today prev,next'
|
|
},
|
|
|
|
listTexts: {
|
|
until: t('labels', 'repeatuntil'),
|
|
past: t('labels', 'pastevents'),
|
|
today: t('labels', 'today'),
|
|
tomorrow: t('labels', 'tomorrow'),
|
|
thisWeek: t('labels', 'thisweek'),
|
|
nextWeek: t('labels', 'nextweek'),
|
|
thisMonth: t('labels', 'thismonth'),
|
|
nextMonth: t('labels', 'nextmonth'),
|
|
future: t('labels', 'future'),
|
|
week: 'W'
|
|
},
|
|
// list/table options
|
|
listSections: 'smart',
|
|
listRange: 30,
|
|
listPage: 7,
|
|
|
|
monthNames: month_names_long(),
|
|
monthNamesShort: month_names_short(),
|
|
dayNames: day_names_long(),
|
|
dayNamesShort: day_names_short(),
|
|
buttonText: {
|
|
today: t('labels', 'today'),
|
|
month: t('labels', 'month'),
|
|
week: t('labels', 'week'),
|
|
day: t('labels', 'day'),
|
|
table: t('labels', 'tableview')
|
|
},
|
|
theme: true, // use jQuery UI themeing
|
|
allDayText: t('labels', 'allday'),
|
|
axisFormat: AgenDAVConf.prefs_timeformat,
|
|
slotMinutes: 30,
|
|
firstHour: 8,
|
|
|
|
allDayDefault: false,
|
|
|
|
loading: function(bool) {
|
|
loading(bool);
|
|
},
|
|
|
|
eventRender: event_render_callback,
|
|
eventClick: event_click_callback,
|
|
|
|
// Add new event by dragging. Click also triggers this event,
|
|
// if you define dayClick and select there is some kind of
|
|
// collision between them.
|
|
select: slots_drag_callback,
|
|
|
|
// Useful for creating events in agenda view
|
|
selectHelper: select_helper,
|
|
|
|
eventResize: event_resize_callback,
|
|
eventDrop: event_drop_callback
|
|
});
|
|
|
|
|
|
// Refresh link
|
|
$('<span class="fc-button-refresh">'
|
|
+'<i class="icon-refresh"></i> '
|
|
+t('labels', 'refresh') + '</span>')
|
|
.appendTo('#calendar_view td.fc-header-right')
|
|
.button()
|
|
.on('click', function() {
|
|
update_calendar_list(true);
|
|
})
|
|
.before('<span class="fc-header-space">');
|
|
|
|
// Date picker above calendar
|
|
dust.render('datepicker_button', dustbase, function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
$('#calendar_view span.fc-button-next')
|
|
.after(out);
|
|
$('#datepicker_fullcalendar')
|
|
.datepicker({
|
|
changeYear: true,
|
|
closeText: t('labels', 'cancel'),
|
|
onSelect: function(date, text) {
|
|
var d = $('#datepicker_fullcalendar').datepicker('getDate');
|
|
$('#calendar_view').fullCalendar('gotoDate', d);
|
|
}
|
|
})
|
|
.prev()
|
|
.button()
|
|
.on('click', function() {
|
|
$('#datepicker_fullcalendar').datepicker('setDate', $('#calendar_view').fullCalendar('getDate'));
|
|
$('#datepicker_fullcalendar').datepicker('show');
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
$('#calendar_view').fullCalendar('renderEvent',
|
|
{
|
|
title: 'Little portal',
|
|
start: '1985-02-15T00:00:00Z',
|
|
end: '1985-02-15T23:59:59Z',
|
|
allDay: true,
|
|
editable: false,
|
|
color: '#E78AEF'
|
|
},
|
|
true);
|
|
|
|
|
|
/*************************************************************
|
|
* Calendar list events
|
|
*************************************************************/
|
|
|
|
// Editing a calendar
|
|
$('div.calendar_list').on('click', 'i.cfg', function(e) {
|
|
e.stopPropagation();
|
|
var calentry = $(this).parent();
|
|
calendar_modify_dialog($(calentry[0]).data());
|
|
})
|
|
.on('click', 'li.available_calendar', function(e) {
|
|
// Make calendar transparent
|
|
toggle_calendar($(this));
|
|
});
|
|
|
|
// First time load: create calendar list
|
|
update_calendar_list(true);
|
|
|
|
$('#sidebar').on('click', '#toggle_all_shared_calendars', function(e) {
|
|
var shared_cals = $('#shared_calendar_list').find('ul').children();
|
|
if ($(this).hasClass('hide_all')) {
|
|
$.map(shared_cals, function(e, i) {
|
|
hide_calendar($(e));
|
|
});
|
|
$(this)
|
|
.removeClass('hide_all')
|
|
.addClass('show_all')
|
|
.find('i')
|
|
.removeClass('icon-eye-close')
|
|
.addClass('icon-eye-open');
|
|
} else {
|
|
$.map(shared_cals, function(e, i) {
|
|
show_calendar($(e));
|
|
});
|
|
$(this)
|
|
.removeClass('show_all')
|
|
.addClass('hide_all')
|
|
.find('i')
|
|
.removeClass('icon-eye-open')
|
|
.addClass('icon-eye-close');
|
|
}
|
|
});
|
|
|
|
// Help tooltips
|
|
$('#sidebar div.buttons').find('img[title],span[title],a[title]').qtip({
|
|
position: {
|
|
my: 'top left',
|
|
at: 'bottom left'
|
|
},
|
|
show: {
|
|
delay: 600
|
|
},
|
|
style: {
|
|
classes: 'ui-tooltip-bootstrap',
|
|
tip: true
|
|
}
|
|
});
|
|
|
|
|
|
// Create calendar
|
|
$('#calendar_add')
|
|
.on('click', calendar_create_dialog);
|
|
|
|
/*************************************************************
|
|
* End of calendar list events
|
|
*************************************************************/
|
|
|
|
/*************************************************************
|
|
* Shortcuts
|
|
*************************************************************/
|
|
|
|
$('#shortcut_add_event')
|
|
.button({
|
|
icons: {
|
|
primary: 'ui-icon-plusthick'
|
|
}
|
|
})
|
|
.on('click', function() {
|
|
var start = fulldatetimestring($('#calendar_view').fullCalendar('getDate'));
|
|
var data = {
|
|
start: start,
|
|
allday: false,
|
|
view: 'month'
|
|
};
|
|
|
|
// Unselect every single day/slot
|
|
$('#calendar_view').fullCalendar('unselect');
|
|
event_field_form('new', data);
|
|
});
|
|
}
|
|
|
|
// Printing
|
|
|
|
setup_print_tweaks();
|
|
|
|
|
|
|
|
|
|
// User menu
|
|
$('#usermenu').qtip({
|
|
content: $('#usermenu_content'),
|
|
position: { my: 'top center', at: 'bottom center' },
|
|
style: {
|
|
tip: true,
|
|
classes: 'ui-tooltip-bootstrap agendav-menu'
|
|
},
|
|
show: {
|
|
event: 'click',
|
|
effect: false,
|
|
delay: 0
|
|
},
|
|
hide: {
|
|
event: 'unfocus'
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
/**
|
|
* Used to calculate calendar view height
|
|
*/
|
|
var calendar_height = function calendar_height() {
|
|
var offset = $('#calendar_view').offset();
|
|
return $(window).height() - Math.ceil(offset.top) - 30;
|
|
};
|
|
|
|
/**
|
|
* Used to show error messages
|
|
*/
|
|
|
|
var show_error = function show_error(title, message) {
|
|
// Hide loading indicator
|
|
loading(false);
|
|
|
|
$('#popup').freeow(title, message,
|
|
{
|
|
classes: ['popup_error'],
|
|
autoHide: false,
|
|
showStyle: {
|
|
opacity: 1,
|
|
left: 0
|
|
},
|
|
hideStyle: {
|
|
opacity: 0,
|
|
left: '400px'
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Used to show success messages
|
|
*/
|
|
|
|
|
|
var show_success = function show_success(title, message) {
|
|
$('#popup').freeow(title, message,
|
|
{
|
|
classes: ['popup_success'],
|
|
autoHide: true,
|
|
autoHideDelay: 2000,
|
|
showStyle: {
|
|
opacity: 1,
|
|
left: 0
|
|
},
|
|
hideStyle: {
|
|
opacity: 0,
|
|
left: '400px'
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets data from body
|
|
*/
|
|
var get_data = function get_data(name) {
|
|
return $.data($('body')[0], name);
|
|
};
|
|
|
|
/**
|
|
* Sets data on body
|
|
*/
|
|
var set_data = function set_data(name, value) {
|
|
$.data($('body')[0], name, value);
|
|
};
|
|
|
|
/**
|
|
* Removes data from body
|
|
*/
|
|
var remove_data = function remove_data(name) {
|
|
$.removeData($('body')[0], name);
|
|
};
|
|
|
|
|
|
/**
|
|
* Loads a form (via AJAX) to a specified div
|
|
*/
|
|
var load_generated_dialog = function load_generated_dialog(url, data, preDialogFunc, title, buttons, divname, width) {
|
|
|
|
divname = '#' + divname;
|
|
|
|
// Avoid double dialog opening
|
|
if ($(divname).length != 0) {
|
|
return false;
|
|
}
|
|
|
|
// Do it via POST
|
|
var newid = generate_on_the_fly_form(
|
|
base_app_url + 'event/modify', data);
|
|
|
|
if (get_data('formcreation') == 'ok') {
|
|
var thisform = $('#' + newid);
|
|
var action = $(thisform).attr('action');
|
|
var formdata = $(thisform).serialize();
|
|
|
|
var dialog_ajax_req = $.ajax({
|
|
url: base_app_url + url,
|
|
cache: false,
|
|
type: 'POST',
|
|
data: formdata,
|
|
dataType: 'html'
|
|
});
|
|
|
|
dialog_ajax_req.then(function() {
|
|
loading(false);
|
|
});
|
|
|
|
dialog_ajax_req.fail(function(jqXHR, textStatus, errorThrown) {
|
|
show_error(t('messages', 'error_loading_dialog'),
|
|
t('messages', 'error_oops') + ': ' + textStatus);
|
|
});
|
|
|
|
dialog_ajax_req.done(function(data, textStatus, jqxHR) {
|
|
$('body').append(data);
|
|
$(divname).dialog({
|
|
autoOpen: true,
|
|
buttons: buttons,
|
|
title: title,
|
|
minWidth: width,
|
|
modal: true,
|
|
open: function(event, ui) {
|
|
preDialogFunc();
|
|
$(divname).dialog('option', 'position', 'center');
|
|
var buttons = $(event.target).parent().find('.ui-dialog-buttonset').children();
|
|
add_button_icons(buttons);
|
|
},
|
|
close: function(ev, ui) { $(this).remove(); }
|
|
})
|
|
});
|
|
|
|
// Remove generated form
|
|
$(thisform).remove();
|
|
} else {
|
|
// Error generating dialog on the fly?
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
t('messages', 'error_oops'));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sends a form via AJAX.
|
|
*
|
|
* This way we respect CodeIgniter CSRF tokens
|
|
*/
|
|
var proceed_send_ajax_form = function proceed_send_ajax_form(formObj, successFunc, exceptionFunc,
|
|
errorFunc) {
|
|
var url = $(formObj).attr('action');
|
|
var data = $(formObj).serialize();
|
|
|
|
// Mask body
|
|
loading(true);
|
|
|
|
var sendform_ajax_req = $.ajax({
|
|
url: url,
|
|
cache: false,
|
|
type: 'POST',
|
|
data: data,
|
|
dataType: 'json'
|
|
});
|
|
|
|
sendform_ajax_req.then(function() {
|
|
loading(false);
|
|
});
|
|
|
|
sendform_ajax_req.fail(function(jqXHR, textStatus, errorThrown) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
t('messages', 'error_oops') + ':' + textStatus);
|
|
set_data('lastoperation', 'failed');
|
|
errorFunc();
|
|
});
|
|
|
|
sendform_ajax_req.done(function(data, textStatus, jqXHR) {
|
|
// "ERROR", "EXCEPTION" or "SUCCESS"
|
|
var result = data.result;
|
|
var message = data.message;
|
|
if (result == 'ERROR') {
|
|
set_data('lastoperation', 'failed');
|
|
show_error(
|
|
t('messages', 'error_internal'),
|
|
message);
|
|
errorFunc();
|
|
} else if (result == 'EXCEPTION') {
|
|
set_data('lastoperation', 'failed');
|
|
exceptionFunc(message);
|
|
} else if (result == 'SUCCESS') {
|
|
set_data('lastoperation', 'success');
|
|
successFunc(message);
|
|
} else {
|
|
show_error(t('messages', 'error_internal'),
|
|
t('messages', 'error_oops') + ':' + result);
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Generates a dialog
|
|
*/
|
|
|
|
var show_dialog = function show_dialog(template, data, title, buttons,
|
|
divname, width, pre_func) {
|
|
|
|
dust.render(template, dustbase.push(data), function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
$('body').append(out);
|
|
$('#' + divname).dialog({
|
|
autoOpen: true,
|
|
buttons: buttons,
|
|
title: title,
|
|
minWidth: width,
|
|
modal: true,
|
|
open: function(event, ui) {
|
|
pre_func();
|
|
$(divname).dialog('option', 'position', 'center');
|
|
var buttons = $(event.target).parent().find('.ui-dialog-buttonset').children();
|
|
add_button_icons(buttons);
|
|
},
|
|
close: function(ev, ui) { $(this).remove(); }
|
|
})
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Creates a form with a random id in the document, and returns it.
|
|
* Defines each element in the second parameter as hidden fields
|
|
*/
|
|
var generate_on_the_fly_form = function generate_on_the_fly_form(action, data) {
|
|
var random_id = '';
|
|
var possible =
|
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
for( var i=0; i < 10; i++ )
|
|
random_id += possible.charAt(Math.floor(Math.random() *
|
|
possible.length));
|
|
|
|
// Now we have our random id
|
|
var form_gen = base_app_url + 'dialog_generator/on_the_fly_form/' +
|
|
random_id;
|
|
var csrf_ajax_gen = $.ajax({
|
|
url: form_gen,
|
|
cache: false,
|
|
type: 'POST',
|
|
contentType: 'text',
|
|
dataType: 'text',
|
|
async: false // Let's wait
|
|
});
|
|
|
|
csrf_ajax_gen.fail(function(jqXHR, textStatus, errorThrown) {
|
|
// This is generally caused by expired session
|
|
session_expired();
|
|
set_data('formcreation', 'failed');
|
|
});
|
|
|
|
csrf_ajax_gen.done(function(formdata, textStatus, jqXHR) {
|
|
var hidden_fields = '';
|
|
|
|
$.each(data, function (i, v) {
|
|
hidden_fields += '<input type="hidden" name="'+i
|
|
+'" value="'+v+'" />';
|
|
});
|
|
|
|
$(formdata)
|
|
.append(hidden_fields)
|
|
.attr('action' , action)
|
|
.appendTo(document.body);
|
|
|
|
set_data('formcreation', 'ok');
|
|
});
|
|
|
|
return random_id;
|
|
};
|
|
|
|
/**
|
|
* Destroys a dialog
|
|
*/
|
|
var destroy_dialog = function destroy_dialog(name) {
|
|
$(name).dialog('close');
|
|
$(name).dialog('destroy');
|
|
$(name).remove();
|
|
};
|
|
|
|
/**
|
|
* Sets datepicker options
|
|
*/
|
|
var set_default_datepicker_options = function set_default_datepicker_options() {
|
|
// Localization (TODO: make this configurable!)
|
|
$.datepicker.regional['custom'] = {
|
|
closeText: t('labels', 'close'),
|
|
prevText: t('labels', 'previous'),
|
|
nextText: t('labels', 'next'),
|
|
currentText: t('labels', 'today'),
|
|
monthNames: month_names_long(),
|
|
monthNamesShort: month_names_short(),
|
|
dayNames: day_names_long(),
|
|
dayNamesShort: day_names_short(),
|
|
dayNamesMin: day_names_short(),
|
|
weekHeader: 'Sm',
|
|
firstDay: AgenDAVConf.prefs_firstday,
|
|
isRTL: false,
|
|
showMonthAfterYear: false,
|
|
yearSuffix: ''};
|
|
|
|
$.datepicker.setDefaults($.datepicker.regional['custom']);
|
|
$.datepicker.setDefaults({constrainInput: true});
|
|
$.datepicker.setDefaults({dateFormat: AgenDAVConf.prefs_dateformat});
|
|
};
|
|
|
|
/**
|
|
* Sets a minDate on end_date
|
|
*/
|
|
var set_end_minDate = function set_end_minDate() {
|
|
var elems = ced + ' input.start_date';
|
|
var eleme = ced + ' input.end_date';
|
|
var elemru = ced + ' input.recurrence_until';
|
|
|
|
var selected = $(elems).datepicker('getDate');
|
|
|
|
selected.setTime(selected.getTime());
|
|
|
|
$(eleme).datepicker('option', 'minDate', selected);
|
|
$(elemru).datepicker('option', 'minDate', selected);
|
|
|
|
};
|
|
|
|
/**
|
|
* Sets recurrence options to be enabled or disabled
|
|
*/
|
|
var update_recurrence_options = function update_recurrence_options(newval) {
|
|
if (newval == 'none') {
|
|
$(ced + ' input.recurrence_count').val('');
|
|
$(ced + ' input.recurrence_until').val('');
|
|
|
|
$(ced + ' input.recurrence_count').attr('disabled', 'disabled');
|
|
$(ced + ' input.recurrence_count').addClass('ui-state-disabled');
|
|
$(ced + ' label[for="recurrence_count"]').addClass('ui-state-disabled');
|
|
|
|
$(ced + ' input.recurrence_until').attr('disabled', 'disabled');
|
|
$(ced + ' input.recurrence_until').datepicker('disable');
|
|
$(ced + ' input.recurrence_until').addClass('ui-state-disabled');
|
|
$(ced + ' label[for="recurrence_until"]').addClass('ui-state-disabled');
|
|
} else {
|
|
enforce_exclusive_recurrence_field('recurrence_count', 'recurrence_until');
|
|
enforce_exclusive_recurrence_field('recurrence_until', 'recurrence_count');
|
|
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/***************************
|
|
* Event handling functions
|
|
*/
|
|
|
|
// Triggers a dialog for editing/creating events
|
|
var event_field_form = function event_field_form(type, data) {
|
|
|
|
var url_dialog = 'dialog_generator/';
|
|
var title;
|
|
var action_verb;
|
|
|
|
if (type == 'new') {
|
|
url_dialog += 'create_event';
|
|
title = t('labels', 'createevent');
|
|
} else {
|
|
url_dialog += 'edit_event';
|
|
title = t('labels', 'editevent');
|
|
}
|
|
|
|
load_generated_dialog(url_dialog,
|
|
data,
|
|
function() {
|
|
var start_datepicker_opts = {
|
|
onSelect: function(dateText, inst) {
|
|
// End date can't be previous to start date
|
|
set_end_minDate();
|
|
}
|
|
};
|
|
|
|
// Tabs
|
|
$(ced + '_tabs').tabs();
|
|
|
|
|
|
$(ced + ' input.start_time').timePicker(AgenDAVConf.timepicker_base);
|
|
$(ced + ' input.end_time').timePicker(AgenDAVConf.timepicker_base);
|
|
$(ced + ' input.start_date').datepicker(start_datepicker_opts);
|
|
$(ced + ' input.end_date').datepicker();
|
|
$(ced + ' input.recurrence_until').datepicker();
|
|
|
|
// Untouched value
|
|
$(ced + ' input.end_time').data('untouched', true);
|
|
|
|
// First time datepicker is run we need to set minDate on end date
|
|
set_end_minDate();
|
|
|
|
// And recurrence options have to be enabled/disabled
|
|
update_recurrence_options($(ced + ' select.recurrence_type').val());
|
|
|
|
// All day checkbox
|
|
$(ced).on('change', 'input.allday', function() {
|
|
// TODO: timepickers should update their values
|
|
var current = $(ced + " input.start_date").datepicker('getDate');
|
|
set_end_minDate();
|
|
|
|
if ($(this).is(':checked')) {
|
|
$(ced + ' input.start_time').hide();
|
|
$(ced + ' input.end_time').hide();
|
|
} else {
|
|
$(ced + ' input.end_date').removeAttr('disabled');
|
|
$(ced + ' input.end_date').removeClass('ui-state-disabled');
|
|
$(ced + ' input.end_date').datepicker('setDate', current);
|
|
|
|
$(ced + ' input.start_time').show();
|
|
$(ced + ' input.end_time').show();
|
|
}
|
|
});
|
|
|
|
// Recurrence type
|
|
$(ced).on('change', 'select.recurrence_type', function() {
|
|
var newval = $(this).val();
|
|
|
|
update_recurrence_options($(this).val());
|
|
});
|
|
|
|
// Avoid having a value in both recurrence options (count / until)
|
|
$(ced)
|
|
.on('keyup', 'input.recurrence_count', function() {
|
|
enforce_exclusive_recurrence_field('recurrence_count', 'recurrence_until');
|
|
})
|
|
.on('keyup change', 'input.recurrence_until', function() {
|
|
enforce_exclusive_recurrence_field('recurrence_until', 'recurrence_count');
|
|
});
|
|
|
|
// Timepicker: keep 1h between start-end if on the same day
|
|
// and end_time hasn't been changed by hand
|
|
var origStart = $.timePicker(ced + ' input.start_time').getTime();
|
|
var origDur = $.timePicker(ced + ' input.end_time').getTime() - origStart.getTime();
|
|
|
|
|
|
$(ced).on('change', 'input.start_time', function() {
|
|
if ($(ced + ' input.end_time').data('untouched')) {
|
|
|
|
var start = $.timePicker(ced + ' input.start_time').getTime();
|
|
|
|
var dur = $.timePicker(ced + ' input.end_time').getTime()
|
|
- origStart.getTime();
|
|
$.timePicker(ced + ' input.end_time').setTime(new Date(start.getTime() + dur));
|
|
origStart = start;
|
|
}
|
|
});
|
|
|
|
$(ced).on('change', 'input.end_time', function() {
|
|
var durn = $.timePicker(this).getTime()
|
|
- $.timePicker(ced + ' input.start_time').getTime();
|
|
if (durn != origDur) {
|
|
$(this).data('untouched', false);
|
|
}
|
|
});
|
|
|
|
// Focus first field on creation
|
|
if (type == 'new') {
|
|
$('input[name="summary"]').focus();
|
|
}
|
|
|
|
// Show 'Reminders' tab contents
|
|
dust.render('reminders_table', dustbase.push(data), function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
$('#tabs-reminders').html(out);
|
|
reminders_manager();
|
|
}
|
|
});
|
|
|
|
|
|
},
|
|
title,
|
|
[
|
|
{
|
|
'text': t('labels', 'save'),
|
|
'class': 'addicon btn-icon-event-edit',
|
|
'click': function() {
|
|
var thisform = $('#com_form');
|
|
proceed_send_ajax_form(thisform,
|
|
function(data) {
|
|
// Reload only affected calendars
|
|
$.each(data, function(k, cal) {
|
|
reload_event_source(cal);
|
|
});
|
|
|
|
destroy_dialog(ced);
|
|
},
|
|
function(data) {
|
|
// Problem with form data
|
|
show_error(t('messages', 'error_invalidinput'), data);
|
|
},
|
|
function(data) {
|
|
// Do nothing
|
|
});
|
|
|
|
}
|
|
},
|
|
{
|
|
'text': t('labels', 'cancel'),
|
|
'class': 'addicon btn-icon-cancel',
|
|
'click': function() { destroy_dialog(ced); }
|
|
}
|
|
],
|
|
'com_event_dialog', 550);
|
|
};
|
|
|
|
/*
|
|
* Updates a single event fetching it from server
|
|
*/
|
|
var update_single_event = function update_single_event(event, new_data) {
|
|
$.each(new_data, function (i, v) {
|
|
event[i] = v;
|
|
});
|
|
|
|
$('#calendar_view').fullCalendar('updateEvent', event);
|
|
};
|
|
|
|
// Triggers a dialog for creating calendars
|
|
var calendar_create_dialog = function calendar_create_dialog() {
|
|
|
|
var form_url = base_app_url + 'calendar/create';
|
|
var title = t('labels', 'newcalendar');
|
|
|
|
var data = {
|
|
applyid: 'calendar_create_form',
|
|
frm: {
|
|
action: form_url,
|
|
method: 'post',
|
|
csrf: get_csrf_token()
|
|
}
|
|
};
|
|
|
|
show_dialog('calendar_create_dialog',
|
|
data,
|
|
title,
|
|
[
|
|
{
|
|
'text': t('labels', 'create'),
|
|
'class': 'addicon btn-icon-calendar-add',
|
|
'click': function() {
|
|
var thisform = $('#calendar_create_form');
|
|
proceed_send_ajax_form(thisform,
|
|
function(data) {
|
|
destroy_dialog('#calendar_create_dialog');
|
|
update_calendar_list(false);
|
|
},
|
|
function(data) {
|
|
// Problem with form data
|
|
show_error(t('messages', 'error_invalidinput'), data);
|
|
},
|
|
function(data) {
|
|
// Do nothing
|
|
});
|
|
}
|
|
},
|
|
{
|
|
'text': t('labels', 'cancel'),
|
|
'class': 'addicon btn-icon-cancel',
|
|
'click': function() { destroy_dialog('#calendar_create_dialog'); }
|
|
}
|
|
],
|
|
'calendar_create_dialog',
|
|
400,
|
|
function() {
|
|
$('input.pick_color').colorPicker();
|
|
});
|
|
};
|
|
|
|
// Triggers a dialog for editing calendars
|
|
var calendar_modify_dialog = function calendar_modify_dialog(calendar_obj) {
|
|
|
|
var form_url = base_app_url + 'calendar/modify';
|
|
var title = t('labels', 'modifycalendar');
|
|
|
|
var data = calendar_obj;
|
|
$.extend(data, {
|
|
applyid: 'calendar_modify_form',
|
|
frm: {
|
|
action: form_url,
|
|
method: 'post',
|
|
csrf: get_csrf_token()
|
|
}
|
|
});
|
|
|
|
// Buttons for modification dialog
|
|
var buttons_and_actions =
|
|
[
|
|
{
|
|
'text': t('labels', 'deletecalendar'),
|
|
'class': 'addicon btn-icon-calendar-delete',
|
|
'click': function() {
|
|
calendar_delete_dialog(calendar_obj);
|
|
}
|
|
},
|
|
{
|
|
'text': t('labels', 'save'),
|
|
'class': 'addicon btn-icon-calendar-edit',
|
|
'click': function() {
|
|
var thisform = $('#calendar_modify_form');
|
|
|
|
proceed_send_ajax_form(thisform,
|
|
function(data) {
|
|
destroy_dialog('#calendar_modify_dialog');
|
|
// TODO remove specific calendar and update only its events
|
|
update_calendar_list(false);
|
|
},
|
|
function(data) {
|
|
// Problem with form data
|
|
show_error(t('messages', 'error_invalidinput'), data);
|
|
},
|
|
function(data) {
|
|
// Do nothing
|
|
});
|
|
}
|
|
},
|
|
{
|
|
'text': t('labels', 'cancel'),
|
|
'class': 'addicon btn-icon-cancel',
|
|
'click': function() { destroy_dialog('#calendar_modify_dialog'); }
|
|
}
|
|
];
|
|
|
|
// On shared calendars, don't show 'Remove calendar'
|
|
if (data.shared === true) {
|
|
buttons_and_actions.splice(0, 1);
|
|
}
|
|
|
|
|
|
show_dialog('calendar_modify_dialog',
|
|
data,
|
|
title,
|
|
buttons_and_actions,
|
|
'calendar_modify_dialog',
|
|
500,
|
|
function() {
|
|
$('input.pick_color').colorPicker();
|
|
$('#calendar_modify_dialog_tabs').tabs();
|
|
|
|
if (enable_calendar_sharing === true && data.shared !== true) {
|
|
share_manager();
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Shows the 'Delete calendar' dialog
|
|
*/
|
|
var calendar_delete_dialog = function calendar_delete_dialog(calendar_obj) {
|
|
destroy_dialog('#calendar_modify_dialog');
|
|
var form_url = base_app_url + 'calendar/delete';
|
|
var title = t('labels', 'deletecalendar');
|
|
|
|
var data = calendar_obj;
|
|
$.extend(data, {
|
|
applyid: 'calendar_delete_form',
|
|
frm: {
|
|
action: form_url,
|
|
method: 'post',
|
|
csrf: get_csrf_token()
|
|
}
|
|
});
|
|
|
|
show_dialog('calendar_delete_dialog',
|
|
data,
|
|
title,
|
|
[
|
|
{
|
|
'text': t('labels', 'yes'),
|
|
'class': 'addicon btn-icon-calendar-delete',
|
|
'click': function() {
|
|
var thisform = $('#calendar_delete_form');
|
|
proceed_send_ajax_form(thisform,
|
|
function(removed_calendar) {
|
|
// Just remove deleted calendar
|
|
$('.calendar_list li.available_calendar').each(function(index) {
|
|
var thiscal = $(this).data();
|
|
if (thiscal.calendar == removed_calendar) {
|
|
$('#calendar_view').fullCalendar('removeEventSource', thiscal.eventsource);
|
|
$(this).remove();
|
|
return false; // stop looking for calendar
|
|
}
|
|
});
|
|
},
|
|
function(data) {
|
|
show_error(t('messages', 'error_caldelete'), data);
|
|
},
|
|
function() {});
|
|
|
|
// Destroy dialog
|
|
destroy_dialog('#calendar_delete_dialog');
|
|
}
|
|
},
|
|
{
|
|
'text': t('labels', 'cancel'),
|
|
'class': 'addicon btn-icon-cancel',
|
|
'click': function() { destroy_dialog('#calendar_delete_dialog'); }
|
|
}
|
|
],
|
|
'calendar_delete_dialog',
|
|
500,
|
|
function() { });
|
|
};
|
|
|
|
/*
|
|
* Updates the calendar list and generates eventSources for fullcalendar
|
|
*/
|
|
|
|
var update_calendar_list = function update_calendar_list(maskbody) {
|
|
if (maskbody) {
|
|
loading(true);
|
|
}
|
|
|
|
var updcalendar_ajax_req = $.ajax({
|
|
url: base_app_url + 'calendar/all',
|
|
cache: false,
|
|
dataType: 'json',
|
|
async: false // Let's wait
|
|
});
|
|
|
|
updcalendar_ajax_req.then(function() {
|
|
if (maskbody) {
|
|
loading(false);
|
|
}
|
|
});
|
|
|
|
updcalendar_ajax_req.fail(function(jqXHR, textStatus, errorThrown) {
|
|
show_error(t('messages', 'error_loading_calendar_list'),
|
|
t('messages', 'error_oops') + textStatus);
|
|
});
|
|
|
|
updcalendar_ajax_req.done(function(data, textStatus, jqXHR) {
|
|
var was_transparent = {};
|
|
|
|
// Remove old eventSources and remove every list item
|
|
$('.calendar_list li.available_calendar').each(function(index) {
|
|
var data = $(this).data();
|
|
$('#calendar_view').fullCalendar('removeEventSource',
|
|
data.eventsource);
|
|
|
|
if ($(this).hasClass('transparent')) {
|
|
was_transparent[data.calendar] = true;
|
|
}
|
|
|
|
$(this).remove();
|
|
});
|
|
|
|
var count = 0,
|
|
count_shared = 0,
|
|
own_calendars = document.createDocumentFragment(),
|
|
shared_calendars = document.createDocumentFragment(),
|
|
collected_event_sources = [];
|
|
|
|
$.each(data, function(key, calendar) {
|
|
count++;
|
|
|
|
// Some values need to be generated
|
|
if (calendar.color === undefined || calendar.color === false || calendar.color == null) {
|
|
calendar.color = default_calendar_color;
|
|
} else {
|
|
calendar.color = calendar.color.substr(0,7);
|
|
}
|
|
calendar.fg = fg_for_bg(calendar.color);
|
|
calendar.bordercolor = $.color.parse(calendar.color).scale('rgb',
|
|
(calendar.fg == '#000000' ? 0.8 : 1.8)).toString();
|
|
|
|
var li = generate_calendar_entry(calendar);
|
|
|
|
if (was_transparent[calendar.calendar]) {
|
|
li.addClass('transparent');
|
|
} else {
|
|
collected_event_sources.push($(li).data().eventsource);
|
|
}
|
|
|
|
if (calendar.shared == true) {
|
|
count_shared++;
|
|
shared_calendars.appendChild(li[0]);
|
|
} else {
|
|
own_calendars.appendChild(li[0]);
|
|
}
|
|
|
|
});
|
|
|
|
// No calendars?
|
|
if (count == 0) {
|
|
// Some CalDAV servers (e.g. DAViCal) create first calendar on first
|
|
// login. Let's reload calendar list again
|
|
var last_calendar_count = get_data('last_calendar_count');
|
|
if (last_calendar_count === undefined ||
|
|
last_calendar_count != '0') {
|
|
set_data('last_calendar_count', 0);
|
|
setTimeout(function() {
|
|
update_calendar_list(false);
|
|
}, 1);
|
|
} else {
|
|
// Calendar list received empty twice
|
|
show_error(t('messages','notice_no_calendars'), '');
|
|
$('#shortcut_add_event').button('disable');
|
|
}
|
|
} else {
|
|
set_data('last_calendar_count', count);
|
|
|
|
$('#own_calendar_list ul')[0]
|
|
.appendChild(own_calendars);
|
|
|
|
// Hide unused block
|
|
if (count_shared == 0) {
|
|
$('#shared_calendar_list').hide();
|
|
} else {
|
|
$('#shared_calendar_list ul')[0]
|
|
.appendChild(shared_calendars);
|
|
$('#shared_calendar_list').show();
|
|
}
|
|
|
|
// Add event sources
|
|
while (count--) {
|
|
$('#calendar_view').fullCalendar('addEventSource',
|
|
collected_event_sources[count]);
|
|
}
|
|
|
|
$('#shortcut_add_event').button('enable');
|
|
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Function used to query the server for events
|
|
*/
|
|
var generate_event_source = function generate_event_source(calendar) {
|
|
var ajax_options = {
|
|
// If #calendar is not used, Fullcalendar will be confused when
|
|
// calling removeEventSource, and will remove all calendars
|
|
url: base_app_url + 'event/all#' + calendar,
|
|
cache: false,
|
|
// TODO make timezone configurable
|
|
data: {
|
|
calendar: calendar
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
if (jqXHR.status !== undefined && jqXHR.status == 401) {
|
|
session_expired();
|
|
} else {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
t('messages',
|
|
'error_loadevents', { '%cal' : calendar }));
|
|
}
|
|
},
|
|
|
|
startParamUTC: true,
|
|
endParamUTC: true
|
|
};
|
|
|
|
return ajax_options;
|
|
};
|
|
|
|
/**
|
|
* Keeps session alive
|
|
*
|
|
* n = refresh interval in miliseconds
|
|
*/
|
|
var session_refresh = function session_refresh(n) {
|
|
var sessrefresh_ajax_req = $.ajax({
|
|
url: base_app_url + 'js_generator/keepalive',
|
|
cache: false,
|
|
method: 'GET',
|
|
dataType: 'html'
|
|
});
|
|
|
|
sessrefresh_ajax_req.done(function(data, textStatus, jqXHR) {
|
|
if (data !== '') {
|
|
// When data is not empty, it's usually JavaScript code
|
|
// TODO think about using dataType: script here
|
|
$('body').append(data);
|
|
} else {
|
|
setTimeout(function() {
|
|
session_refresh(n);
|
|
}, n);
|
|
}
|
|
});
|
|
|
|
sessrefresh_ajax_req.fail(function(jqXHR, textStatus, errorThrown) {
|
|
session_expired();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Adds button icons
|
|
*/
|
|
var add_button_icons = function add_button_icons(buttons) {
|
|
buttons.filter('button.addicon')
|
|
.removeClass('addicon')
|
|
.removeClass('ui-button-text-only')
|
|
.addClass('ui-button-text-icon-primary')
|
|
.each(function(k, v) {
|
|
var classes = $(v).attr('class').split(' ');
|
|
$.each(classes, function(i, j) {
|
|
if (j.match(/^btn-icon-/)) {
|
|
$(v).prepend('<span class="ui-button-icon-primary ui-icon '+ j +'"></span>');
|
|
$(v).removeClass(j);
|
|
return false;
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Generates a new calendar entry
|
|
*/
|
|
var generate_calendar_entry = function generate_calendar_entry(data) {
|
|
var eventsource = generate_event_source(data.calendar);
|
|
eventsource.ignoreTimezone = true; // Ignore UTC offsets
|
|
eventsource.color = data.color;
|
|
eventsource.textColor = data.fg;
|
|
eventsource.borderColor = data.bordercolor;
|
|
|
|
// Shared calendars
|
|
if (data.shared !== undefined && data.shared == true && data.write_access == '0') {
|
|
eventsource.editable = false;
|
|
}
|
|
|
|
data.eventsource = eventsource;
|
|
|
|
var $out;
|
|
|
|
dust.render('calendar_list_entry', dustbase.push(data), function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
$out = $(out);
|
|
|
|
// Associate data + eventsource to new list item
|
|
$out.data(data);
|
|
|
|
// Disable text selection on this (useful for dblclick)
|
|
$out.disableSelection();
|
|
|
|
$out.find('span[title],i[title]').qtip({
|
|
position: {
|
|
my: 'top left',
|
|
at: 'bottom left'
|
|
},
|
|
show: {
|
|
delay: 600
|
|
},
|
|
style: {
|
|
classes: 'ui-tooltip-bootstrap',
|
|
tip: true
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
return $out;
|
|
};
|
|
|
|
/**
|
|
* Gets calendar data from its internal name
|
|
*/
|
|
var get_calendar_data = function get_calendar_data(c) {
|
|
var data = undefined;
|
|
|
|
$('.calendar_list li.available_calendar').each(function(index) {
|
|
var thiscal = $(this).data();
|
|
if (thiscal.calendar == c) {
|
|
data = thiscal;
|
|
return false; // stop looking for calendar
|
|
}
|
|
});
|
|
|
|
return data;
|
|
};
|
|
|
|
/**
|
|
* Gets calendar display name from its internal name
|
|
*/
|
|
var get_calendar_displayname = function get_calendar_displayname(c) {
|
|
var data = get_calendar_data(c);
|
|
|
|
if (data === undefined || data.displayname === undefined) {
|
|
return '(?)';
|
|
} else {
|
|
return data.displayname;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Reloads an event source
|
|
*/
|
|
var reload_event_source = function reload_event_source(cal) {
|
|
var eventsource = undefined;
|
|
|
|
$('.calendar_list li.available_calendar').each(function(index) {
|
|
var thiscal = $(this).data();
|
|
if (thiscal.calendar == cal) {
|
|
eventsource = thiscal.eventsource;
|
|
return false; // stop looking for calendar
|
|
}
|
|
});
|
|
|
|
if (eventsource !== undefined) {
|
|
$('#calendar_view').fullCalendar('removeEventSource', eventsource);
|
|
$('#calendar_view').fullCalendar('addEventSource', eventsource);
|
|
} else {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
t('messages', 'error_calendarnotfound', {'%calendar' : cal }));
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
* Enforces the use of only one recurrence fields
|
|
*/
|
|
var enforce_exclusive_recurrence_field = function enforce_exclusive_recurrence_field(current, other) {
|
|
if ($(ced + ' input.' + current).val() == '') {
|
|
$(ced + ' input.' + other).removeAttr('disabled');
|
|
$(ced + ' input.' + other).removeClass('ui-state-disabled');
|
|
$(ced + ' label[for="' + other + '"]').removeClass('ui-state-disabled');
|
|
if (other == 'recurrence_until') {
|
|
$(ced + ' input.' + other).datepicker('enable');
|
|
}
|
|
} else {
|
|
$(ced + ' input.' + other).attr('disabled', 'disabled');
|
|
$(ced + ' input.' + other).addClass('ui-state-disabled');
|
|
$(ced + ' input.' + other).val('');
|
|
$(ced + ' label[for="' + other + '"]').addClass('ui-state-disabled');
|
|
if (other == 'recurrence_until') {
|
|
$(ced + ' input.' + other).datepicker('disable');
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Round a Date timestamp
|
|
*/
|
|
var timestamp = function timestamp(d) {
|
|
return Math.round(d.getTime()/1000);
|
|
};
|
|
|
|
/*
|
|
* Returns a full date+time string which is easily parseable
|
|
*/
|
|
var fulldatetimestring = function fulldatetimestring(d) {
|
|
if (d != undefined) {
|
|
return $.fullCalendar.formatDate(d, 'yyyyMMddHHmmss');
|
|
} else {
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a foreground color for a given background
|
|
*/
|
|
var fg_for_bg = function fg_for_bg(color) {
|
|
var colr = parseInt(color.substr(1), 16);
|
|
|
|
var is_dark = (colr >>> 16) // R
|
|
+ ((colr >>> 8) & 0x00ff) // G
|
|
+ (colr & 0x0000ff) // B
|
|
< 500;
|
|
|
|
return (is_dark) ? '#ffffff' : '#000000';
|
|
};
|
|
|
|
|
|
/**
|
|
* This method is called when a session has expired
|
|
*/
|
|
var session_expired = function session_expired() {
|
|
$('.ui-dialog-content').dialog('close');
|
|
|
|
show_error(t('messages', 'error_sessexpired'),
|
|
t('messages', 'error_loginagain'));
|
|
setTimeout(function() {
|
|
window.location = base_url;
|
|
}, 2000);
|
|
};
|
|
|
|
/**
|
|
* Handles events on share calendar dialog
|
|
*/
|
|
var share_manager = function share_manager() {
|
|
var manager = $('#calendar_share_table');
|
|
var new_entry_form = $('#calendar_share_add');
|
|
|
|
share_manager_no_entries_placeholder();
|
|
|
|
manager.on('click',
|
|
'.calendar_share_delete', function(event) {
|
|
$(this).parent().parent()
|
|
.fadeOut('fast', function() {
|
|
$(this).remove();
|
|
share_manager_no_entries_placeholder();
|
|
});
|
|
});
|
|
|
|
// Autocomplete caching
|
|
var user_autocomplete_cache = {}, lastXhr;
|
|
|
|
new_entry_form.find('#calendar_share_add_username')
|
|
.autocomplete({
|
|
minLength: 3,
|
|
source: function(request, response) {
|
|
var term = request.term;
|
|
|
|
if (term in user_autocomplete_cache) {
|
|
response(user_autocomplete_cache[term]);
|
|
return;
|
|
}
|
|
|
|
lastXhr = $.getJSON(base_app_url + 'caldav2json/principal_search',
|
|
request, function(data, status, xhr) {
|
|
user_autocomplete_cache[term] = data;
|
|
if (xhr === lastXhr) {
|
|
response(data);
|
|
}
|
|
});
|
|
},
|
|
focus: function( event, ui ) {
|
|
$(this).val(ui.item.username);
|
|
return false;
|
|
},
|
|
select: function( event, ui ) {
|
|
$(this).val(ui.item.username);
|
|
return false;
|
|
}
|
|
})
|
|
.data('autocomplete')._renderItem = function(ul, item) {
|
|
return $('<li></li>')
|
|
.data('item.autocomplete', item)
|
|
.append('<a><i class="icon-user"></i> ' + item.displayname
|
|
+ '<span style="font-style: italic">'
|
|
+ ' <' + item.email + '></span></a>')
|
|
.appendTo(ul);
|
|
};
|
|
|
|
new_entry_form.on('click',
|
|
'#calendar_share_add_button', function(event) {
|
|
var new_user = $('#calendar_share_add_username').val();
|
|
var access = $('#calendar_share_add_write_access').val();
|
|
if (new_user != '') {
|
|
// Check if new_user is already on list
|
|
var already_added = false;
|
|
manager.find('span.username')
|
|
.each(function(index) {
|
|
if (!already_added && $(this).text() == new_user) {
|
|
already_added = true;
|
|
$(this).parent().parent().effect('highlight', {}, 'slow');
|
|
}
|
|
});
|
|
|
|
if (!already_added) {
|
|
var new_row_data = {
|
|
username: new_user,
|
|
write_access: access
|
|
};
|
|
|
|
dust.render('calendar_share_row',
|
|
dustbase.push(new_row_data),
|
|
function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
manager.find('tbody').append(out);
|
|
|
|
// Reset form
|
|
$('#calendar_share_add_username').val('');
|
|
$('#calendar_share_add_write_access').val('0');
|
|
|
|
share_manager_no_entries_placeholder();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Shows the placeholder for empty share lists
|
|
*/
|
|
var share_manager_no_entries_placeholder = function share_manager_no_entries_placeholder() {
|
|
var manager = $('#calendar_share_table');
|
|
if (manager.find('tbody tr').length == 1) {
|
|
$('#calendar_share_no_rows').show();
|
|
} else {
|
|
$('#calendar_share_no_rows').hide();
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Reminders manager
|
|
*/
|
|
|
|
var reminders_manager = function reminders_manager() {
|
|
|
|
var tab_reminders = $('#tabs-reminders');
|
|
var manager = $('#reminders_table');
|
|
|
|
initialize_date_and_time_pickers(tab_reminders);
|
|
|
|
reminders_manager_no_entries_placeholder();
|
|
|
|
manager.on('click',
|
|
'.reminder_delete', function(event) {
|
|
$(this).parent().parent()
|
|
.fadeOut('fast', function() {
|
|
$(this).remove();
|
|
reminders_manager_no_entries_placeholder();
|
|
});
|
|
});
|
|
|
|
manager.parent().on('click', 'img.reminder_add_button', function(event) {
|
|
var formdata = $(this).closest('tbody').serializeObject();
|
|
// Basic validations
|
|
var proceed = false;
|
|
var regexp_num = /^[0-9]+$/;
|
|
|
|
if (formdata.is_absolute === false) {
|
|
if (formdata.qty !== '' && regexp_num.test(formdata.qty) &&
|
|
formdata.interval !== '' && formdata.before !== '') {
|
|
|
|
proceed = true;
|
|
}
|
|
} else {
|
|
if (formdata.tdate !== '' && formdata.ttime !== '') {
|
|
proceed = true;
|
|
}
|
|
}
|
|
|
|
if (proceed === true) {
|
|
var $new_reminder_row = $(this).closest('tr');
|
|
|
|
dust.render('reminder_row',
|
|
dustbase.push(formdata), function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
manager.find('tbody').append(out);
|
|
|
|
$new_reminder_row.find('input').val('');
|
|
$new_reminder_row.find('select').val('');
|
|
|
|
initialize_date_and_time_pickers(tab_reminders);
|
|
reminders_manager_no_entries_placeholder();
|
|
}
|
|
});
|
|
|
|
}
|
|
});
|
|
};
|
|
|
|
/*
|
|
* Shows/hides reminders placeholder when no reminders are set up
|
|
*/
|
|
|
|
var reminders_manager_no_entries_placeholder = function reminders_manager_no_entries_placeholder() {
|
|
var manager = $('#reminders_table');
|
|
if (manager.find('tbody tr').length == 1) {
|
|
$('#reminders_no_rows').show();
|
|
} else {
|
|
$('#reminders_no_rows').hide();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Event render
|
|
*/
|
|
var event_render_callback = function event_render_callback(event, element) {
|
|
var caldata = get_calendar_data(event.calendar);
|
|
var data = $.extend({},
|
|
event,
|
|
{ caldata: caldata });
|
|
|
|
if (caldata !== undefined && caldata.shared === true &&
|
|
caldata.write_access == '0') {
|
|
$.extend(data, { disable_actions: true });
|
|
}
|
|
|
|
// Icons
|
|
var icons = [];
|
|
|
|
if (event.rrule != undefined) {
|
|
icons.push('icon-repeat');
|
|
}
|
|
if (event.reminders.length > 0) {
|
|
icons.push('icon-bell');
|
|
}
|
|
|
|
// Prepend icons
|
|
if (icons.length != 0) {
|
|
var icon_html = $('<span class="fc-event-icons"></span>');
|
|
$.each(icons, function(n, i) {
|
|
icon_html.append('<i class="' + i + '"></i>');
|
|
});
|
|
|
|
if (!element.hasClass('fc-event-row')) {
|
|
element.find('.fc-event-title').after(icon_html);
|
|
}
|
|
}
|
|
|
|
dust.render('event_details_popup', dustbase.push(data), function(err, out) {
|
|
if (err != null) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
err.message);
|
|
} else {
|
|
element.qtip({
|
|
content: {
|
|
text: out,
|
|
title: {
|
|
text: event.title,
|
|
button: true
|
|
}
|
|
},
|
|
position: {
|
|
my: 'bottom center',
|
|
at: 'top center',
|
|
viewport: $('#calendar_view')
|
|
},
|
|
style: {
|
|
classes: 'view_event_details ui-tooltip-bootstrap',
|
|
tip: true
|
|
},
|
|
show: {
|
|
target: $('#calendar_view'),
|
|
event: false,
|
|
solo: $('#calendar_view'),
|
|
effect: false
|
|
},
|
|
hide: {
|
|
fixed: true,
|
|
event: 'unfocus',
|
|
effect: false
|
|
},
|
|
|
|
events: {
|
|
show: function (event, api) {
|
|
// Attach modify and delete events
|
|
$(this)
|
|
.find('button.link_delete_event')
|
|
.off('click')
|
|
.on('click', function() {
|
|
event_delete_dialog();
|
|
})
|
|
.end()
|
|
.find('button.link_modify_event')
|
|
.off('click')
|
|
.on('click', function() {
|
|
modify_event_handler();
|
|
});
|
|
|
|
$(window).on('keydown.tooltipevents', function(e) {
|
|
if(e.keyCode === $.ui.keyCode.ESCAPE) {
|
|
api.hide(e);
|
|
}
|
|
})
|
|
|
|
// Icons
|
|
var links = api.elements.tooltip.find('div.actions').find('button.addicon').button();
|
|
add_button_icons(links);
|
|
},
|
|
|
|
hide: function (event, api) {
|
|
remove_data('current_event');
|
|
$(window).off('keydown.tooltipevents');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Event click
|
|
*/
|
|
var event_click_callback = function event_click_callback(event,
|
|
jsEvent, view) {
|
|
var current_event = get_data('current_event');
|
|
|
|
if (current_event == event) {
|
|
$(ved).qtip('hide');
|
|
remove_data('current_event');
|
|
} else {
|
|
set_data('current_event', event);
|
|
$(this).qtip('show', jsEvent);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Calendar slots dragging
|
|
*/
|
|
var slots_drag_callback = function slots_drag_callback(startDate, endDate, allDay, jsEvent, view) {
|
|
var pass_allday = (view.name == 'month') ? false : allDay;
|
|
var data = {
|
|
start: fulldatetimestring(startDate),
|
|
end: fulldatetimestring(endDate),
|
|
allday: pass_allday,
|
|
view: view.name
|
|
};
|
|
|
|
// Unselect every single day/slot
|
|
$('#calendar_view').fullCalendar('unselect');
|
|
event_field_form('new', data);
|
|
};
|
|
|
|
/**
|
|
* Select helper
|
|
*/
|
|
|
|
var select_helper = function select_helper(start,end) {
|
|
return $('<div style="border: 1px solid black; background-color: #f0f0f0;" class="selecthelper"/>')
|
|
.text(
|
|
$.fullCalendar.formatDates(start, end,
|
|
AgenDAVConf.prefs_timeformat + '{ - ' + AgenDAVConf.prefs_timeformat + '}'));
|
|
};
|
|
|
|
/**
|
|
* Event resizing
|
|
*/
|
|
|
|
var event_resize_callback = function event_resize_callback(event, dayDelta, minuteDelta, revertFunc,
|
|
jsEvent, ui, view ) {
|
|
|
|
// Generate on-the-fly form
|
|
var formid = generate_on_the_fly_form(
|
|
base_app_url + 'event/alter',
|
|
{
|
|
uid: event.uid,
|
|
calendar: event.calendar,
|
|
etag: event.etag,
|
|
view: view.name,
|
|
dayDelta: dayDelta,
|
|
minuteDelta: minuteDelta,
|
|
allday: event.allDay,
|
|
was_allday: event.was_allday,
|
|
timezone: event.timezone,
|
|
type: 'resize'
|
|
});
|
|
|
|
if (get_data('formcreation') == 'ok') {
|
|
var thisform = $('#' + formid);
|
|
|
|
proceed_send_ajax_form(thisform,
|
|
function(data) {
|
|
// Users just want to know if something fails
|
|
update_single_event(event, data);
|
|
},
|
|
function(data) {
|
|
show_error(t('messages', 'error_modfailed'), data);
|
|
revertFunc();
|
|
},
|
|
function() {
|
|
revertFunc();
|
|
});
|
|
}
|
|
|
|
// Remove generated form
|
|
$(thisform).remove();
|
|
};
|
|
|
|
/**
|
|
* Event drag and drop
|
|
*/
|
|
|
|
var event_drop_callback = function event_drop_callback(event, dayDelta, minuteDelta, allDay,
|
|
revertFunc, jsEvent, ui, view) {
|
|
|
|
// Generate on-the-fly form
|
|
var formid = generate_on_the_fly_form(
|
|
base_app_url + 'event/alter',
|
|
{
|
|
uid: event.uid,
|
|
calendar: event.calendar,
|
|
etag: event.etag,
|
|
view: view.name,
|
|
dayDelta: dayDelta,
|
|
minuteDelta: minuteDelta,
|
|
allday: event.allDay,
|
|
was_allday: event.orig_allday,
|
|
timezone: event.timezone,
|
|
type: 'drag'
|
|
});
|
|
|
|
if (get_data('formcreation') == 'ok') {
|
|
var thisform = $('#' + formid);
|
|
|
|
proceed_send_ajax_form(thisform,
|
|
function(data) {
|
|
// Users just want to know if something fails
|
|
update_single_event(event, data);
|
|
},
|
|
function(data) {
|
|
show_error(t('messages', 'error_modfailed'), data);
|
|
revertFunc();
|
|
},
|
|
function() {
|
|
revertFunc();
|
|
});
|
|
}
|
|
|
|
// Remove generated form
|
|
$(thisform).remove();
|
|
};
|
|
|
|
// Delete link
|
|
// TODO: check for rrule/recurrence-id (EXDATE, etc)
|
|
var event_delete_dialog = function event_delete_dialog() {
|
|
var form_url = base_app_url + 'event/delete';
|
|
var title = t('labels', 'deleteevent');
|
|
|
|
var data = get_data('current_event');
|
|
|
|
if (data === undefined) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
t('messages', 'error_current_event_not_loaded'));
|
|
return;
|
|
}
|
|
|
|
$.extend(data, {
|
|
applyid: 'event_delete_form',
|
|
frm: {
|
|
action: form_url,
|
|
method: 'post',
|
|
csrf: get_csrf_token()
|
|
}
|
|
});
|
|
|
|
show_dialog('event_delete_dialog',
|
|
data,
|
|
title,
|
|
[
|
|
{
|
|
'text': t('labels', 'yes'),
|
|
'class': 'addicon btn-icon-event-delete',
|
|
'click': function() {
|
|
var thisform = $('#event_delete_form');
|
|
proceed_send_ajax_form(thisform,
|
|
function(rdata) {
|
|
$('#calendar_view').fullCalendar('removeEvents', data.id);
|
|
},
|
|
function(rdata) {
|
|
show_error(t('messages', 'error_event_not_deleted'), data);
|
|
},
|
|
function() {});
|
|
|
|
// Destroy dialog
|
|
destroy_dialog('#event_delete_dialog');
|
|
}
|
|
},
|
|
{
|
|
'text': t('labels', 'cancel'),
|
|
'class': 'addicon btn-icon-cancel',
|
|
'click': function() { destroy_dialog('#event_delete_dialog'); }
|
|
}
|
|
],
|
|
'event_delete_dialog',
|
|
400,
|
|
function() {});
|
|
|
|
// Close tooltip
|
|
$(ved).qtip('hide');
|
|
return false;
|
|
};
|
|
|
|
// Edit/Modify link
|
|
var modify_event_handler = function modify_event_handler() {
|
|
// TODO: check for rrule/recurrence-id
|
|
// Data about this event
|
|
var event_data = get_data('current_event');
|
|
if (event_data === undefined) {
|
|
show_error(t('messages', 'error_interfacefailure'),
|
|
t('messages', 'error_current_event_not_loaded'));
|
|
return;
|
|
}
|
|
|
|
var data = {
|
|
uid: event_data.uid,
|
|
calendar: event_data.calendar,
|
|
href: event_data.href,
|
|
etag: event_data.etag,
|
|
start: fulldatetimestring(event_data.start),
|
|
end: fulldatetimestring(event_data.end),
|
|
summary: event_data.title,
|
|
location: event_data.location,
|
|
allday: event_data.allDay,
|
|
description: event_data.description,
|
|
rrule: event_data.rrule,
|
|
rrule_serialized: event_data.rrule_serialized,
|
|
rrule_explained: event_data.rrule_explained,
|
|
icalendar_class: event_data.icalendar_class,
|
|
transp: event_data.transp,
|
|
recurrence_id: event_data.recurrence_id,
|
|
reminders: event_data.reminders,
|
|
visible_reminders: event_data.visible_reminders,
|
|
orig_start: fulldatetimestring($.fullCalendar.parseDate(event_data.orig_start)),
|
|
orig_end: fulldatetimestring($.fullCalendar.parseDate(event_data.orig_end))
|
|
};
|
|
// Close tooltip
|
|
$(ved).qtip('hide');
|
|
|
|
event_field_form('modify', data);
|
|
|
|
return false;
|
|
};
|
|
|
|
// Shows a calendar
|
|
var show_calendar = function show_calendar(calendar_obj) {
|
|
$('#calendar_view').fullCalendar('addEventSource', calendar_obj.data().eventsource);
|
|
calendar_obj.removeClass('transparent');
|
|
};
|
|
|
|
// Hides a calendar
|
|
var hide_calendar = function hide_calendar(calendar_obj) {
|
|
$('#calendar_view').fullCalendar('removeEventSource', calendar_obj.data().eventsource);
|
|
calendar_obj.addClass('transparent');
|
|
};
|
|
|
|
// Toggles calendar visibility
|
|
var toggle_calendar = function toggle_calendar(calendar_obj) {
|
|
if (calendar_obj.hasClass('transparent')) {
|
|
show_calendar(calendar_obj);
|
|
} else {
|
|
hide_calendar(calendar_obj);
|
|
}
|
|
};
|
|
|
|
// Initializes datepickers and timepickers
|
|
var initialize_date_and_time_pickers = function initialize_date_and_time_pickers(obj) {
|
|
obj.find('.needs-datepicker').datepicker();
|
|
obj.find('.needs-timepicker').timePicker(AgenDAVConf.timepicker_base);
|
|
};
|
|
|
|
|
|
// Gets csrf token value
|
|
var get_csrf_token = function get_csrf_token() {
|
|
return $.cookie(AgenDAVConf.prefs_csrf_cookie_name);
|
|
}
|
|
|
|
// Loading indicator
|
|
var loading = function loading(status) {
|
|
if (status === false) {
|
|
$('#loading').hide();
|
|
} else {
|
|
$('#loading').show();
|
|
}
|
|
}
|
|
|
|
// Printing helpers
|
|
|
|
var beforePrint = function beforePrint() {
|
|
// Prepare calendar for printing
|
|
$('#calendar_view').addClass('printing');
|
|
$('#calendar_view').fullCalendar('render');
|
|
};
|
|
|
|
var afterPrint = function afterPrint() {
|
|
$('#calendar_view').removeClass('printing');
|
|
$('#calendar_view').fullCalendar('render');
|
|
};
|
|
|
|
|
|
// Apply printing helpers to document
|
|
var setup_print_tweaks = function setup_print_tweaks() {
|
|
if (window.matchMedia) {
|
|
var mediaQueryList = window.matchMedia('print');
|
|
mediaQueryList.addListener(function(mql) {
|
|
if (mql.matches) {
|
|
beforePrint();
|
|
} else {
|
|
afterPrint();
|
|
}
|
|
});
|
|
}
|
|
|
|
window.onbeforeprint = beforePrint;
|
|
window.onafterprint = afterPrint;
|
|
};
|
|
|
|
|
|
// vim: sw=2 tabstop=2
|