1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/agendav_ynh.git synced 2024-09-03 20:36:12 +02:00
agendav_ynh/sources/web/public/js/app.js
2014-01-07 17:53:08 +01:00

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">'
+ ' &lt;' + item.email + '&gt;</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