From 50569114acdc64e7c7cae1498635d3f821517c30 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Mon, 7 Mar 2016 15:53:16 +0100 Subject: Initial commit of the Faster IT roundcube_calendar plugin distribution This includes: * Kolab plugins 3.2.9 (calendar and libcalendaring) * CalDAV driver 3.2.8 * .htaccess files for at least some security * SabreDAV updated to 1.8.12 (Jan 2015 release) * Support for CURLOPT_SSL_* settings to allow self-signed certificates * Small fixes & improved documentation --- calendar/calendar_ui.js | 4273 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4273 insertions(+) create mode 100644 calendar/calendar_ui.js (limited to 'calendar/calendar_ui.js') diff --git a/calendar/calendar_ui.js b/calendar/calendar_ui.js new file mode 100644 index 0000000..452ea38 --- /dev/null +++ b/calendar/calendar_ui.js @@ -0,0 +1,4273 @@ +/** + * Client UI Javascript for the Calendar plugin + * + * @author Lazlo Westerhof + * @author Thomas Bruederli + * + * @licstart The following is the entire license notice for the + * JavaScript code in this file. + * + * Copyright (C) 2010, Lazlo Westerhof + * Copyright (C) 2014-2015, Kolab Systems AG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @licend The above is the entire license notice + * for the JavaScript code in this file. + */ + +// Roundcube calendar UI client class +function rcube_calendar_ui(settings) +{ + // extend base class + rcube_calendar.call(this, settings); + + /*** member vars ***/ + this.is_loading = false; + this.selected_event = null; + this.selected_calendar = null; + this.search_request = null; + this.saving_lock; + this.calendars = {}; + this.quickview_sources = []; + + + /*** private vars ***/ + var DAY_MS = 86400000; + var HOUR_MS = 3600000; + var me = this; + var gmt_offset = (new Date().getTimezoneOffset() / -60) - (settings.timezone || 0) - (settings.dst || 0); + var client_timezone = new Date().getTimezoneOffset(); + var day_clicked = day_clicked_ts = 0; + var ignore_click = false; + var event_defaults = { free_busy:'busy', alarms:'' }; + var event_attendees = []; + var calendars_list; + var calenders_search_list; + var calenders_search_container; + var search_calendars = {}; + var attendees_list; + var resources_list; + var resources_treelist; + var resources_data = {}; + var resources_index = []; + var resource_owners = {}; + var resources_events_source = { url:null, editable:false }; + var freebusy_ui = { workinhoursonly:false, needsupdate:false }; + var freebusy_data = {}; + var current_view = null; + var count_sources = []; + var exec_deferred = bw.ie6 ? 5 : 1; + var sensitivitylabels = { 'public':rcmail.gettext('public','calendar'), 'private':rcmail.gettext('private','calendar'), 'confidential':rcmail.gettext('confidential','calendar') }; + var ui_loading = rcmail.set_busy(true, 'loading'); + + // general datepicker settings + var datepicker_settings = { + // translate from fullcalendar format to datepicker format + dateFormat: settings['date_format'].replace(/M/g, 'm').replace(/mmmmm/, 'MM').replace(/mmm/, 'M').replace(/dddd/, 'DD').replace(/ddd/, 'D').replace(/yy/g, 'y'), + firstDay : settings['first_day'], + dayNamesMin: settings['days_short'], + monthNames: settings['months'], + monthNamesShort: settings['months'], + changeMonth: false, + showOtherMonths: true, + selectOtherMonths: true + }; + + // global fullcalendar settings + var fullcalendar_defaults = { + aspectRatio: 1, + ignoreTimezone: true, // will treat the given date strings as in local (browser's) timezone + monthNames : settings.months, + monthNamesShort : settings.months_short, + dayNames : settings.days, + dayNamesShort : settings.days_short, + firstDay : settings.first_day, + firstHour : settings.first_hour, + slotMinutes : 60/settings.timeslots, + timeFormat: { + '': settings.time_format, + agenda: settings.time_format + '{ - ' + settings.time_format + '}', + list: settings.time_format + '{ - ' + settings.time_format + '}', + table: settings.time_format + '{ - ' + settings.time_format + '}' + }, + axisFormat : settings.time_format, + columnFormat: { + month: 'ddd', // Mon + week: 'ddd ' + settings.date_short, // Mon 9/7 + day: 'dddd ' + settings.date_short, // Monday 9/7 + table: settings.date_agenda + }, + titleFormat: { + month: 'MMMM yyyy', + week: settings.dates_long, + day: 'dddd ' + settings['date_long'], + table: settings.dates_long + }, + listPage: 7, // advance one week in agenda view + listRange: settings.agenda_range, + listSections: settings.agenda_sections, + tableCols: ['handle', 'date', 'time', 'title', 'location'], + defaultView: rcmail.env.view || settings.default_view, + allDayText: rcmail.gettext('all-day', 'calendar'), + buttonText: { + prev: ' ◄ ', + next: ' ► ', + today: settings['today'], + day: rcmail.gettext('day', 'calendar'), + week: rcmail.gettext('week', 'calendar'), + month: rcmail.gettext('month', 'calendar'), + table: rcmail.gettext('agenda', 'calendar') + }, + listTexts: { + until: rcmail.gettext('until', 'calendar'), + past: rcmail.gettext('pastevents', 'calendar'), + today: rcmail.gettext('today', 'calendar'), + tomorrow: rcmail.gettext('tomorrow', 'calendar'), + thisWeek: rcmail.gettext('thisweek', 'calendar'), + nextWeek: rcmail.gettext('nextweek', 'calendar'), + thisMonth: rcmail.gettext('thismonth', 'calendar'), + nextMonth: rcmail.gettext('nextmonth', 'calendar'), + future: rcmail.gettext('futureevents', 'calendar'), + week: rcmail.gettext('weekofyear', 'calendar') + }, + currentTimeIndicator: settings.time_indicator, + // event rendering + eventRender: function(event, element, view) { + if (view.name != 'list' && view.name != 'table') { + var prefix = event.sensitivity && event.sensitivity != 'public' ? String(sensitivitylabels[event.sensitivity]).toUpperCase()+': ' : ''; + element.attr('title', prefix + event.title); + } + if (view.name != 'month') { + if (event.location) { + element.find('div.fc-event-title').after('
@ ' + Q(event.location) + '
'); + } + if (event.sensitivity && event.sensitivity != 'public') + element.find('div.fc-event-time').append(''); + if (event.recurrence) + element.find('div.fc-event-time').append(''); + if (event.alarms || (event.valarms && event.valarms.length)) + element.find('div.fc-event-time').append(''); + } + if (event.status) { + element.addClass('cal-event-status-' + String(event.status).toLowerCase()); + } + + element.attr('aria-label', event.title + ', ' + me.event_date_text(event, true)); + }, + // render element indicating more (invisible) events + overflowRender: function(data, element) { + element.html(rcmail.gettext('andnmore', 'calendar').replace('$nr', data.count)) + .click(function(e){ me.fisheye_view(data.date); }); + }, + // callback when a specific event is clicked + eventClick: function(event, ev, view) { + if (!event.temp && String(event.className).indexOf('fc-type-freebusy') < 0) + event_show_dialog(event, ev); + } + }; + + /*** imports ***/ + var Q = this.quote_html; + var text2html = this.text2html; + var event_date_text = this.event_date_text; + var parse_datetime = this.parse_datetime; + var date2unixtime = this.date2unixtime; + var fromunixtime = this.fromunixtime; + var parseISO8601 = this.parseISO8601; + var date2servertime = this.date2ISO8601; + var render_message_links = this.render_message_links; + + + /*** private methods ***/ + + // same as str.split(delimiter) but it ignores delimiters within quoted strings + var explode_quoted_string = function(str, delimiter) + { + var result = [], + strlen = str.length, + q, p, i, char, last; + + for (q = p = i = 0; i < strlen; i++) { + char = str.charAt(i); + if (char == '"' && last != '\\') { + q = !q; + } + else if (!q && char == delimiter) { + result.push(str.substring(p, i)); + p = i + 1; + } + last = char; + } + + result.push(str.substr(p)); + return result; + }; + + // Change the first charcter to uppercase + var ucfirst = function(str) + { + return str.charAt(0).toUpperCase() + str.substr(1); + }; + + // clone the given date object and optionally adjust time + var clone_date = function(date, adjust) + { + var d = new Date(date.getTime()); + + // set time to 00:00 + if (adjust == 1) { + d.setHours(0); + d.setMinutes(0); + } + // set time to 23:59 + else if (adjust == 2) { + d.setHours(23); + d.setMinutes(59); + } + + return d; + }; + + // fix date if jumped over a DST change + var fix_date = function(date) + { + if (date.getHours() == 23) + date.setTime(date.getTime() + HOUR_MS); + else if (date.getHours() > 0) + date.setHours(0); + }; + + var date2timestring = function(date, dateonly) + { + return date2servertime(date).replace(/[^0-9]/g, '').substr(0, (dateonly ? 8 : 14)); + } + + var format_datetime = function(date, mode, voice) + { + return me.format_datetime(date, mode, voice); + } + + var render_link = function(url) + { + var islink = false, href = url; + if (url.match(/^[fhtpsmailo]+?:\/\//i)) { + islink = true; + } + else if (url.match(/^[a-z0-9.-:]+(\/|$)/i)) { + islink = true; + href = 'http://' + url; + } + return islink ? '' + Q(url) + '' : Q(url); + } + + // determine whether the given date is on a weekend + var is_weekend = function(date) + { + return date.getDay() == 0 || date.getDay() == 6; + }; + + var is_workinghour = function(date) + { + if (settings['work_start'] > settings['work_end']) + return date.getHours() >= settings['work_start'] || date.getHours() < settings['work_end']; + else + return date.getHours() >= settings['work_start'] && date.getHours() < settings['work_end']; + }; + + // check if the event has 'real' attendees, excluding the current user + var has_attendees = function(event) + { + return (event.attendees && event.attendees.length && (event.attendees.length > 1 || String(event.attendees[0].email).toLowerCase() != settings.identity.email)); + }; + + // check if the current user is an attendee of this event + var is_attendee = function(event, role, email) + { + var emails = email ? ';'+email.toLowerCase() : settings.identity.emails; + for (var i=0; event.attendees && i < event.attendees.length; i++) { + if ((!role || event.attendees[i].role == role) && event.attendees[i].email && emails.indexOf(';'+event.attendees[i].email.toLowerCase()) >= 0) + return event.attendees[i]; + } + return false; + }; + + // check if the current user is the organizer + var is_organizer = function(event, email) + { + return is_attendee(event, 'ORGANIZER', email) || !event.id; + }; + + /** + * Check permissions on the given calendar object + */ + var has_permission = function(cal, perm) + { + // multiple chars means "either of" + if (String(perm).length > 1) { + for (var i=0; i < perm.length; i++) { + if (has_permission(cal, perm[i])) + return true; + } + } + + if (cal.rights && String(cal.rights).indexOf(perm) >= 0) { + return true; + } + + return (perm == 'i' && cal.editable) || (perm == 'v' && cal.editable); + } + + var load_attachment = function(event, att) + { + var query = { _id: att.id, _event: event.recurrence_id || event.id, _cal:event.calendar, _frame: 1 }; + if (event.rev) + query._rev = event.rev; + + // open attachment in frame if it's of a supported mimetype + if (id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) { + if (rcmail.open_window(rcmail.url('get-attachment', query), true, true)) { + return; + } + } + + query._frame = null; + query._download = 1; + rcmail.goto_url('get-attachment', query, false); + }; + + // build event attachments list + var event_show_attachments = function(list, container, event, edit) + { + var i, id, len, img, content, li, elem, + ul = document.createElement('UL'); + ul.className = 'attachmentslist'; + + for (i=0, len=list.length; i') + .attr('title', rcmail.gettext('delete')) + .attr('aria-label', rcmail.gettext('delete') + ' ' + Q(elem.name)) + .addClass('delete') + .click({id: elem.id}, function(e) { remove_attachment(this, e.data.id); return false; }); + + if (!rcmail.env.deleteicon) + content.html(rcmail.gettext('delete')); + else { + img = document.createElement('IMG'); + img.src = rcmail.env.deleteicon; + img.alt = rcmail.gettext('delete'); + content.append(img); + } + + content.appendTo(li); + } + + // name/link + content = $('') + .html(Q(elem.name)) + .addClass('file') + .click({event: event, att: elem}, function(e) { + load_attachment(e.data.event, e.data.att); + return false; + }) + .appendTo(li); + + ul.appendChild(li); + } + + if (edit && rcmail.gui_objects.attachmentlist) { + ul.id = rcmail.gui_objects.attachmentlist.id; + rcmail.gui_objects.attachmentlist = ul; + } + + container.empty().append(ul); + }; + + var remove_attachment = function(elem, id) + { + $(elem.parentNode).hide(); + rcmail.env.deleted_attachments.push(id); + delete rcmail.env.attachments[id]; + }; + + // event details dialog (show only) + var event_show_dialog = function(event, ev, temp) + { + var $dialog = $("#eventshow"); + var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:false, rights:'lrs' }; + + if (!temp) + me.selected_event = event; + + if ($dialog.is(':ui-dialog')) + $dialog.dialog('close'); + + // remove status-* classes + $dialog.removeClass(function(i, oldclass) { + var oldies = String(oldclass).split(' '); + return $.grep(oldies, function(cls) { return cls.indexOf('status-') === 0 || cls.indexOf('sensitivity-') === 0 }).join(' '); + }); + + // convert start/end dates if not done yet by fullcalendar + if (typeof event.start == 'string') + event.start = parseISO8601(event.start); + if (typeof event.end == 'string') + event.end = parseISO8601(event.end); + + // allow other plugins to do actions when event form is opened + rcmail.triggerEvent('calendar-event-init', {o: event}); + + $dialog.find('div.event-section, div.event-line').hide(); + $('#event-title').html(Q(event.title)).show(); + + if (event.location) + $('#event-location').html('@ ' + text2html(event.location)).show(); + if (event.description) + $('#event-description').show().children('.event-text').html(text2html(event.description, 300, 6)); + if (event.vurl) + $('#event-url').show().children('.event-text').html(render_link(event.vurl)); + + // render from-to in a nice human-readable way + // -> now shown in dialog title + // $('#event-date').html(Q(me.event_date_text(event))).show(); + + if (event.recurrence && event.recurrence_text) + $('#event-repeat').show().children('.event-text').html(Q(event.recurrence_text)); + + if (event.valarms && event.alarms_text) + $('#event-alarm').show().children('.event-text').html(Q(event.alarms_text)); + + if (calendar.name) + $('#event-calendar').show().children('.event-text').html(Q(calendar.name)).attr('class', 'event-text cal-'+calendar.id).css('color', calendar.textColor || calendar.color || ''); + if (event.categories) + $('#event-category').show().children('.event-text').html(Q(event.categories)).attr('class', 'event-text cat-'+String(event.categories).toLowerCase().replace(rcmail.identifier_expr, '')); + if (event.free_busy) + $('#event-free-busy').show().children('.event-text').html(Q(rcmail.gettext(event.free_busy, 'calendar'))); + if (event.priority > 0) { + var priolabels = [ '', rcmail.gettext('highest'), rcmail.gettext('high'), '', '', rcmail.gettext('normal'), '', '', rcmail.gettext('low'), rcmail.gettext('lowest') ]; + $('#event-priority').show().children('.event-text').html(Q(event.priority+' '+priolabels[event.priority])); + } + + if (event.status) { + var status_lc = String(event.status).toLowerCase(); + $('#event-status').show().children('.event-text').html(Q(rcmail.gettext('status-'+status_lc,'calendar'))); + $dialog.addClass('status-'+status_lc); + } + if (event.sensitivity && event.sensitivity != 'public') { + $('#event-sensitivity').show().children('.event-text').html(Q(sensitivitylabels[event.sensitivity])); + $dialog.addClass('sensitivity-'+event.sensitivity); + } + if (event.created || event.changed) { + var created = parseISO8601(event.created), + changed = parseISO8601(event.changed) + $('#event-created-changed .event-created').html(Q(created ? format_datetime(created) : rcmail.gettext('unknown','calendar'))) + $('#event-created-changed .event-changed').html(Q(changed ? format_datetime(changed) : rcmail.gettext('unknown','calendar'))) + $('#event-created-changed').show() + } + + // create attachments list + if ($.isArray(event.attachments)) { + event_show_attachments(event.attachments, $('#event-attachments').children('.event-text'), event); + if (event.attachments.length > 0) { + $('#event-attachments').show(); + } + } + else if (calendar.attachments) { + // fetch attachments, some drivers doesn't set 'attachments' prop of the event? + } + + // build attachments list + $('#event-links').hide(); + if ($.isArray(event.links) && event.links.length) { + render_message_links(event.links || [], $('#event-links').children('.event-text'), false, 'calendar'); + $('#event-links').show(); + } + + // list event attendees + if (calendar.attendees && event.attendees) { + // sort resources to the end + event.attendees.sort(function(a,b) { + var j = a.cutype == 'RESOURCE' ? 1 : 0, + k = b.cutype == 'RESOURCE' ? 1 : 0; + return (j - k); + }); + + var data, mystatus = null, rsvp, line, morelink, html = '', overflow = ''; + for (var j=0; j < event.attendees.length; j++) { + data = event.attendees[j]; + if (data.email) { + if (data.role == 'ORGANIZER') + organizer = true; + else if (settings.identity.emails.indexOf(';'+data.email) >= 0) { + mystatus = data.status.toLowerCase(); + if (data.status == 'NEEDS-ACTION' || data.status == 'TENTATIVE' || data.rsvp) + rsvp = mystatus; + } + } + + line = event_attendee_html(data); + + if (morelink) + overflow += line; + else + html += line; + + // stop listing attendees + if (j == 7 && event.attendees.length >= 7) { + morelink = $('').html(rcmail.gettext('andnmore', 'calendar').replace('$nr', event.attendees.length - j - 1)); + } + } + + if (html && (event.attendees.length > 1 || !organizer)) { + $('#event-attendees').show() + .children('.event-text') + .html(html) + .find('a.mailtolink').click(event_attendee_click); + + // display all attendees in a popup when clicking the "more" link + if (morelink) { + $('#event-attendees .event-text').append(morelink); + morelink.click(function(e){ + rcmail.show_popup_dialog( + '
' + html + overflow + '
', + rcmail.gettext('tabattendees','calendar'), + null, + { width:450, modal:false }); + $('#all-event-attendees a.mailtolink').click(event_attendee_click); + return false; + }) + } + } + + if (mystatus && !rsvp) { + $('#event-partstat').show().children('.changersvp') + .removeClass('accepted tentative declined delegated needs-action') + .addClass(mystatus) + .children('.event-text') + .html(Q(rcmail.gettext('itip' + mystatus, 'libcalendaring'))); + } + + var show_rsvp = rsvp && !is_organizer(event) && event.status != 'CANCELLED' && has_permission(calendar, 'v'); + $('#event-rsvp')[(show_rsvp ? 'show' : 'hide')](); + $('#event-rsvp .rsvp-buttons input').prop('disabled', false).filter('input[rel='+mystatus+']').prop('disabled', true); + + if (show_rsvp && event.comment) + $('#event-rsvp-comment').show().children('.event-text').html(Q(event.comment)); + + $('#event-rsvp a.reply-comment-toggle').show(); + $('#event-rsvp .itip-reply-comment textarea').hide().val(''); + + if (event.recurrence && event.id) { + var sel = event._savemode || (event.thisandfuture ? 'future' : (event.isexception ? 'current' : 'all')); + $('#event-rsvp .rsvp-buttons').addClass('recurring'); + } + else { + $('#event-rsvp .rsvp-buttons').removeClass('recurring'); + } + } + + var buttons = []; + if (!temp && calendar.editable && event.editable !== false) { + buttons.push({ + text: rcmail.gettext('edit', 'calendar'), + click: function() { + event_edit_dialog('edit', event); + } + }); + } + if (!temp && has_permission(calendar, 'td') && event.editable !== false) { + buttons.push({ + text: rcmail.gettext('delete', 'calendar'), + 'class': 'delete', + click: function() { + me.delete_event(event); + $dialog.dialog('close'); + } + }); + } + + if (!buttons.length) { + buttons.push({ + text: rcmail.gettext('close', 'calendar'), + click: function(){ + $dialog.dialog('close'); + } + }); + } + + // open jquery UI dialog + $dialog.dialog({ + modal: false, + resizable: !bw.ie6, + closeOnEscape: (!bw.ie6 && !bw.ie7), // disable for performance reasons + title: me.event_date_text(event), + open: function() { + $dialog.attr('aria-hidden', 'false'); + setTimeout(function(){ + $dialog.parent().find('.ui-button:not(.ui-dialog-titlebar-close)').first().focus(); + }, 5); + }, + close: function() { + $dialog.dialog('destroy').attr('aria-hidden', 'true').hide(); + rcmail.command('menu-close','eventoptionsmenu'); + $('.libcal-rsvp-replymode').hide(); + }, + dragStart: function() { + rcmail.command('menu-close','eventoptionsmenu'); + $('.libcal-rsvp-replymode').hide(); + }, + resizeStart: function() { + rcmail.command('menu-close','eventoptionsmenu'); + $('.libcal-rsvp-replymode').hide(); + }, + buttons: buttons, + minWidth: 320, + width: 420 + }).show(); + + // remember opener element (to be focused on close) + $dialog.data('opener', ev && rcube_event.is_keyboard(ev) ? ev.target : null); + + // set voice title on dialog widget + $dialog.dialog('widget').removeAttr('aria-labelledby') + .attr('aria-label', me.event_date_text(event, true) + ', ', event.title); + + // set dialog size according to content + me.dialog_resize($dialog.get(0), $dialog.height(), 420); + + // add link for "more options" drop-down + if (!temp && !event.temporary && event.calendar != '_resource') { + $('') + .attr('href', '#') + .html(rcmail.gettext('eventoptions','calendar')) + .addClass('dropdown-link') + .click(function(e) { + return rcmail.command('menu-open','eventoptionsmenu', this, e) + }) + .appendTo($dialog.parent().find('.ui-dialog-buttonset')); + } + + rcmail.enable_command('event-history', calendar.history) + }; + + // render HTML code for displaying an attendee record + var event_attendee_html = function(data) + { + var dispname = Q(data.name || data.email), tooltip = ''; + + if (data.email) { + tooltip = data.email + '; ' + data.status; + dispname = '' + dispname + ''; + } + + if (data['delegated-to']) + tooltip = rcmail.gettext('delegatedto', 'calendar') + data['delegated-to']; + else if (data['delegated-from']) + tooltip = rcmail.gettext('delegatedfrom', 'calendar') + data['delegated-from']; + + return '' + dispname + ' '; + }; + + // event handler for clicks on an attendee link + var event_attendee_click = function(e) + { + var cutype = $(this).attr('data-cutype'), + mailto = this.href.substr(7); + if (rcmail.env.calendar_resources && cutype == 'RESOURCE') { + event_resources_dialog(mailto); + } + else { + rcmail.command('compose', mailto, e ? e.target : null, e); + } + return false; + }; + + // bring up the event dialog (jquery-ui popup) + var event_edit_dialog = function(action, event) + { + // copy opener element from show dialog + var op_elem = $("#eventshow:ui-dialog").data('opener'); + + // close show dialog first + $("#eventshow:ui-dialog").data('opener', null).dialog('close'); + + var $dialog = $('
'); + var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:true, rights: action=='new' ? 'lrwitd' : 'lrs' }; + me.selected_event = $.extend($.extend({}, event_defaults), event); // clone event object (with defaults) + event = me.selected_event; // change reference to clone + freebusy_ui.needsupdate = false; + + // reset dialog first + $('#eventtabs').get(0).reset(); + $('#event-panel-recurrence input, #event-panel-recurrence select, #event-panel-attachments input').prop('disabled', false); + $('#event-panel-recurrence, #event-panel-attachments').removeClass('disabled'); + + // allow other plugins to do actions when event form is opened + rcmail.triggerEvent('calendar-event-init', {o: event}); + + // event details + var title = $('#edit-title').val(event.title || ''); + var location = $('#edit-location').val(event.location || ''); + var description = $('#edit-description').text(event.description || ''); + var vurl = $('#edit-url').val(event.vurl || ''); + var categories = $('#edit-categories').val(event.categories); + var calendars = $('#edit-calendar').val(event.calendar); + var eventstatus = $('#edit-event-status').val(event.status); + var freebusy = $('#edit-free-busy').val(event.free_busy); + var priority = $('#edit-priority').val(event.priority); + var sensitivity = $('#edit-sensitivity').val(event.sensitivity); + + var duration = Math.round((event.end.getTime() - event.start.getTime()) / 1000); + var startdate = $('#edit-startdate').val($.fullCalendar.formatDate(event.start, settings['date_format'])).data('duration', duration); + var starttime = $('#edit-starttime').val($.fullCalendar.formatDate(event.start, settings['time_format'])).show(); + var enddate = $('#edit-enddate').val($.fullCalendar.formatDate(event.end, settings['date_format'])); + var endtime = $('#edit-endtime').val($.fullCalendar.formatDate(event.end, settings['time_format'])).show(); + var allday = $('#edit-allday').get(0); + var notify = $('#edit-attendees-donotify').get(0); + var invite = $('#edit-attendees-invite').get(0); + var comment = $('#edit-attendees-comment'); + + invite.checked = settings.itip_notify & 1 > 0; + notify.checked = has_attendees(event) && invite.checked; + + if (event.allDay) { + starttime.val("12:00").hide(); + endtime.val("13:00").hide(); + allday.checked = true; + } + else { + allday.checked = false; + } + + // set calendar selection according to permissions + calendars.find('option').each(function(i, opt) { + var cal = me.calendars[opt.value] || {}; + $(opt).prop('disabled', !(cal.editable || (action == 'new' && has_permission(cal, 'i')))) + }); + + // set alarm(s) + me.set_alarms_edit('#edit-alarms', action != 'new' && event.valarms && calendar.alarms ? event.valarms : []); + + // enable/disable alarm property according to backend support + $('#edit-alarms')[(calendar.alarms ? 'show' : 'hide')](); + + // check categories drop-down: add value if not exists + if (event.categories && !categories.find("option[value='"+event.categories+"']").length) { + $('