Changeset View
Changeset View
Standalone View
Standalone View
plugins/calendar/calendar_ui.js
Show First 20 Lines • Show All 1,232 Lines • ▼ Show 20 Lines | var event_freebusy_dialog = function() | ||||
var now = new Date(), fb_start = new Date(), fb_end = new Date(); | var now = new Date(), fb_start = new Date(), fb_end = new Date(); | ||||
fb_start.setTime(event.start); | fb_start.setTime(event.start); | ||||
fb_start.setHours(0); fb_start.setMinutes(0); fb_start.setSeconds(0); fb_start.setMilliseconds(0); | fb_start.setHours(0); fb_start.setMinutes(0); fb_start.setSeconds(0); fb_start.setMilliseconds(0); | ||||
fb_end.setTime(fb_start.getTime() + DAY_MS); | fb_end.setTime(fb_start.getTime() + DAY_MS); | ||||
freebusy_data = { required:{}, all:{} }; | freebusy_data = { required:{}, all:{} }; | ||||
freebusy_ui.loading = 1; // prevent render_freebusy_grid() to load data yet | freebusy_ui.loading = 1; // prevent render_freebusy_grid() to load data yet | ||||
freebusy_ui.numdays = Math.max(allday.checked ? 14 : 1, Math.ceil(duration * 2 / 86400)); | freebusy_ui.numdays = Math.max(allday.checked ? 14 : 1, Math.ceil(duration * 2 / 86400)); | ||||
freebusy_ui.interval = allday.checked ? 1440 : 60; | freebusy_ui.interval = allday.checked ? 1440 : (60 / (settings.timeslots || 1)); | ||||
freebusy_ui.start = fb_start; | freebusy_ui.start = fb_start; | ||||
freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays); | freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays); | ||||
render_freebusy_grid(0); | render_freebusy_grid(0); | ||||
// render list of attendees | // render list of attendees | ||||
freebusy_ui.attendees = {}; | freebusy_ui.attendees = {}; | ||||
var domid, dispname, data, role_html, list_html = ''; | var domid, dispname, data, role_html, list_html = ''; | ||||
for (var i=0; i < event_attendees.length; i++) { | for (var i=0; i < event_attendees.length; i++) { | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | var event_freebusy_dialog = function() | ||||
}).show(); | }).show(); | ||||
// hide edit dialog on IE6 because of drop-down elements | // hide edit dialog on IE6 because of drop-down elements | ||||
if (bw.ie6) | if (bw.ie6) | ||||
$("#edit-attendees-table").css('visibility','hidden'); | $("#edit-attendees-table").css('visibility','hidden'); | ||||
// adjust dialog size to fit grid without scrolling | // adjust dialog size to fit grid without scrolling | ||||
var gridw = $('#schedule-freebusy-times').width(); | var gridw = $('#schedule-freebusy-times').width(); | ||||
var overflow = gridw - $('#attendees-freebusy-table td.times').width() + 1; | var overflow = gridw - $('#attendees-freebusy-table td.times').width(); | ||||
me.dialog_resize($dialog.get(0), $dialog.height() + (bw.ie ? 20 : 0), 800 + Math.max(0, overflow)); | me.dialog_resize($dialog.get(0), $dialog.height() + (bw.ie ? 20 : 0), 800 + Math.max(0, overflow)); | ||||
// fetch data from server | // fetch data from server | ||||
freebusy_ui.loading = 0; | freebusy_ui.loading = 0; | ||||
load_freebusy_data(freebusy_ui.start, freebusy_ui.interval); | load_freebusy_data(freebusy_ui.start, freebusy_ui.interval); | ||||
}; | }; | ||||
// render an HTML table showing free-busy status for all the event attendees | // render an HTML table showing free-busy status for all the event attendees | ||||
Show All 13 Lines | var render_freebusy_grid = function(delta) | ||||
freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays); | freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays); | ||||
} | } | ||||
var dayslots = Math.floor(1440 / freebusy_ui.interval); | var dayslots = Math.floor(1440 / freebusy_ui.interval); | ||||
var date_format = 'ddd '+ (dayslots <= 2 ? settings.date_short : settings.date_format); | var date_format = 'ddd '+ (dayslots <= 2 ? settings.date_short : settings.date_format); | ||||
var lastdate, datestr, css, | var lastdate, datestr, css, | ||||
curdate = new Date(), | curdate = new Date(), | ||||
allday = (freebusy_ui.interval == 1440), | allday = (freebusy_ui.interval == 1440), | ||||
interval = allday ? 1440 : (freebusy_ui.interval * (settings.timeslots || 1)); | |||||
times_css = (allday ? 'allday ' : ''), | times_css = (allday ? 'allday ' : ''), | ||||
dates_row = '<tr class="dates">', | dates_row = '<tr class="dates">', | ||||
times_row = '<tr class="times">', | times_row = '<tr class="times">', | ||||
slots_row = ''; | slots_row = ''; | ||||
for (var s = 0, t = freebusy_ui.start.getTime(); t < freebusy_ui.end.getTime(); s++) { | for (var s = 0, t = freebusy_ui.start.getTime(); t < freebusy_ui.end.getTime(); s++) { | ||||
curdate.setTime(t); | curdate.setTime(t); | ||||
datestr = fc.fullCalendar('formatDate', curdate, date_format); | datestr = fc.fullCalendar('formatDate', curdate, date_format); | ||||
if (datestr != lastdate) { | if (datestr != lastdate) { | ||||
if (lastdate && !allday) break; | if (lastdate && !allday) break; | ||||
dates_row += '<th colspan="' + dayslots + '" class="boxtitle date' + $.fullCalendar.formatDate(curdate, 'ddMMyyyy') + '">' + Q(datestr) + '</th>'; | dates_row += '<th colspan="' + dayslots + '" class="boxtitle date' + $.fullCalendar.formatDate(curdate, 'ddMMyyyy') + '">' + Q(datestr) + '</th>'; | ||||
lastdate = datestr; | lastdate = datestr; | ||||
} | } | ||||
// set css class according to working hours | // set css class according to working hours | ||||
css = is_weekend(curdate) || (freebusy_ui.interval <= 60 && !is_workinghour(curdate)) ? 'offhours' : 'workinghours'; | css = is_weekend(curdate) || (freebusy_ui.interval <= 60 && !is_workinghour(curdate)) ? 'offhours' : 'workinghours'; | ||||
times_row += '<td class="' + times_css + css + '" id="t-' + Math.floor(t/1000) + '">' + Q(allday ? rcmail.gettext('all-day','calendar') : $.fullCalendar.formatDate(curdate, settings['time_format'])) + '</td>'; | times_row += '<td class="' + times_css + css + '" id="t-' + Math.floor(t/1000) + '">' + Q(allday ? rcmail.gettext('all-day','calendar') : $.fullCalendar.formatDate(curdate, settings['time_format'])) + '</td>'; | ||||
slots_row += '<td class="' + css + ' unknown"> </td>'; | slots_row += '<td class="' + css + '"> </td>'; | ||||
t += freebusy_ui.interval * 60000; | t += interval * 60000; | ||||
} | } | ||||
dates_row += '</tr>'; | dates_row += '</tr>'; | ||||
times_row += '</tr>'; | times_row += '</tr>'; | ||||
// render list of attendees | // render list of attendees | ||||
var domid, data, list_html = '', times_html = ''; | var domid, data, list_html = '', times_html = ''; | ||||
for (var i=0; i < event_attendees.length; i++) { | for (var i=0; i < event_attendees.length; i++) { | ||||
data = event_attendees[i]; | data = event_attendees[i]; | ||||
domid = String(data.email).replace(rcmail.identifier_expr, ''); | domid = String(data.email).replace(rcmail.identifier_expr, ''); | ||||
times_html += '<tr id="fbrow' + domid + '">' + slots_row + '</tr>'; | times_html += '<tr id="fbrow' + domid + '">' + slots_row + '</tr>'; | ||||
} | } | ||||
// add line for all/required attendees | // add line for all/required attendees | ||||
times_html += '<tr class="spacer"><td colspan="' + (dayslots * freebusy_ui.numdays) + '"> </td>'; | times_html += '<tr class="spacer"><td colspan="' + (dayslots * freebusy_ui.numdays) + '"></td>'; | ||||
times_html += '<tr id="fbrowall">' + slots_row + '</tr>'; | times_html += '<tr id="fbrowall">' + slots_row + '</tr>'; | ||||
var table = $('#schedule-freebusy-times'); | var table = $('#schedule-freebusy-times'); | ||||
table.children('thead').html(dates_row + times_row); | table.children('thead').html(dates_row + times_row); | ||||
table.children('tbody').html(times_html); | table.children('tbody').html(times_html); | ||||
// initialize event handlers on grid | // initialize event handlers on grid | ||||
if (!freebusy_ui.grid_events) { | if (!freebusy_ui.grid_events) { | ||||
freebusy_ui.grid_events = true; | freebusy_ui.grid_events = true; | ||||
table.children('thead').click(function(e){ | table.children('thead').click(function(e){ | ||||
// move event to the clicked date/time | // move event to the clicked date/time | ||||
if (e.target.id && e.target.id.match(/t-(\d+)/)) { | if (e.target.id && e.target.id.match(/t-(\d+)/)) { | ||||
var newstart = new Date(RegExp.$1 * 1000); | var newstart = new Date(RegExp.$1 * 1000); | ||||
// set time to 00:00 | // set time to 00:00 | ||||
if (me.selected_event.allDay) { | if (me.selected_event.allDay) { | ||||
newstart.setMinutes(0); | newstart.setMinutes(0); | ||||
newstart.setHours(0); | newstart.setHours(0); | ||||
} | } | ||||
update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000)); | update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000)); | ||||
render_freebusy_overlay(); | render_freebusy_overlay(); | ||||
} | } | ||||
}) | }); | ||||
} | } | ||||
// if we have loaded free-busy data, show it | // if we have loaded free-busy data, show it | ||||
if (!freebusy_ui.loading) { | if (!freebusy_ui.loading) { | ||||
if (freebusy_ui.start < freebusy_data.start || freebusy_ui.end > freebusy_data.end || freebusy_ui.interval != freebusy_data.interval) { | if (freebusy_ui.start < freebusy_data.start || freebusy_ui.end > freebusy_data.end || freebusy_ui.interval != freebusy_data.interval) { | ||||
load_freebusy_data(freebusy_ui.start, freebusy_ui.interval); | load_freebusy_data(freebusy_ui.start, freebusy_ui.interval); | ||||
} | } | ||||
else { | else { | ||||
Show All 14 Lines | |||||
{ | { | ||||
var overlay = $('#schedule-event-time'); | var overlay = $('#schedule-event-time'); | ||||
if (me.selected_event.end.getTime() <= freebusy_ui.start.getTime() || me.selected_event.start.getTime() >= freebusy_ui.end.getTime()) { | if (me.selected_event.end.getTime() <= freebusy_ui.start.getTime() || me.selected_event.start.getTime() >= freebusy_ui.end.getTime()) { | ||||
overlay.hide(); | overlay.hide(); | ||||
if (overlay.data('isdraggable')) | if (overlay.data('isdraggable')) | ||||
overlay.draggable('disable'); | overlay.draggable('disable'); | ||||
} | } | ||||
else { | else { | ||||
var table = $('#schedule-freebusy-times'), | var i, n, table = $('#schedule-freebusy-times'), | ||||
width = 0, | width = 0, | ||||
pos = { top:table.children('thead').height(), left:0 }, | pos = { top:table.children('thead').height(), left:0 }, | ||||
eventstart = date2unixtime(clone_date(me.selected_event.start, me.selected_event.allDay?1:0)), | eventstart = date2unixtime(clone_date(me.selected_event.start, me.selected_event.allDay?1:0)), | ||||
eventend = date2unixtime(clone_date(me.selected_event.end, me.selected_event.allDay?2:0)) - 60, | eventend = date2unixtime(clone_date(me.selected_event.end, me.selected_event.allDay?2:0)) - 60, | ||||
slotstart = date2unixtime(freebusy_ui.start), | slotstart = date2unixtime(freebusy_ui.start), | ||||
slotsize = freebusy_ui.interval * 60, | slotsize = freebusy_ui.interval * 60, | ||||
slotend, fraction, $cell; | slotnum = freebusy_ui.interval > 60 ? 1 : (60 / freebusy_ui.interval), | ||||
cells = table.children('thead').find('td'), | |||||
cell_width = cells.first().get(0).offsetWidth, | |||||
slotend; | |||||
// iterate through slots to determine position and size of the overlay | // iterate through slots to determine position and size of the overlay | ||||
table.children('thead').find('td').each(function(i, cell){ | for (i=0; i < cells.length; i++) { | ||||
for (n=0; n < slotnum; n++) { | |||||
slotend = slotstart + slotsize - 1; | slotend = slotstart + slotsize - 1; | ||||
// event starts in this slot: compute left | // event starts in this slot: compute left | ||||
if (eventstart >= slotstart && eventstart <= slotend) { | if (eventstart >= slotstart && eventstart <= slotend) { | ||||
fraction = 1 - (slotend - eventstart) / slotsize; | pos.left = Math.round(i * cell_width + (cell_width / slotnum) * n); | ||||
pos.left = Math.round(cell.offsetLeft + cell.offsetWidth * fraction); | |||||
} | } | ||||
// event ends in this slot: compute width | // event ends in this slot: compute width | ||||
if (eventend >= slotstart && eventend <= slotend) { | if (eventend >= slotstart && eventend <= slotend) { | ||||
fraction = 1 - (slotend - eventend) / slotsize; | width = Math.round(i * cell_width + (cell_width / slotnum) * (n + 1)) - pos.left; | ||||
width = Math.round(cell.offsetLeft + cell.offsetWidth * fraction) - pos.left; | } | ||||
slotstart += slotsize; | |||||
} | |||||
} | } | ||||
slotstart = slotstart + slotsize; | |||||
}); | |||||
if (!width) | if (!width) | ||||
width = table.width() - pos.left; | width = table.width() - pos.left; | ||||
// overlay is visible | // overlay is visible | ||||
if (width > 0) { | if (width > 0) { | ||||
overlay.css({ width: (width-5)+'px', height:(table.children('tbody').height() - 4)+'px', left:pos.left+'px', top:pos.top+'px' }).show(); | overlay.css({ width: (width-4)+'px', height:(table.children('tbody').height() - 4)+'px', left:pos.left+'px', top:pos.top+'px' }).show(); | ||||
// configure draggable | // configure draggable | ||||
if (!overlay.data('isdraggable')) { | if (!overlay.data('isdraggable')) { | ||||
overlay.draggable({ | overlay.draggable({ | ||||
axis: 'x', | axis: 'x', | ||||
scroll: true, | scroll: true, | ||||
stop: function(e, ui){ | stop: function(e, ui){ | ||||
// convert pixels to time | // convert pixels to time | ||||
var px = ui.position.left; | var px = ui.position.left; | ||||
var range_p = $('#schedule-freebusy-times').width(); | var range_p = $('#schedule-freebusy-times').width(); | ||||
var range_t = freebusy_ui.end.getTime() - freebusy_ui.start.getTime(); | var range_t = freebusy_ui.end.getTime() - freebusy_ui.start.getTime(); | ||||
var newstart = new Date(freebusy_ui.start.getTime() + px * (range_t / range_p)); | var newstart = new Date(freebusy_ui.start.getTime() + px * (range_t / range_p)); | ||||
newstart.setSeconds(0); newstart.setMilliseconds(0); | newstart.setSeconds(0); newstart.setMilliseconds(0); | ||||
// snap to day boundaries | // snap to day boundaries | ||||
if (me.selected_event.allDay) { | if (me.selected_event.allDay) { | ||||
if (newstart.getHours() >= 12) // snap to next day | if (newstart.getHours() >= 12) // snap to next day | ||||
newstart.setTime(newstart.getTime() + DAY_MS); | newstart.setTime(newstart.getTime() + DAY_MS); | ||||
newstart.setMinutes(0); | newstart.setMinutes(0); | ||||
newstart.setHours(0); | newstart.setHours(0); | ||||
} | } | ||||
else { | else { | ||||
// round to 5 minutes | // round to 5 minutes | ||||
// @TODO: round to timeslots? | |||||
var round = newstart.getMinutes() % 5; | var round = newstart.getMinutes() % 5; | ||||
if (round > 2.5) newstart.setTime(newstart.getTime() + (5 - round) * 60000); | if (round > 2.5) newstart.setTime(newstart.getTime() + (5 - round) * 60000); | ||||
else if (round > 0) newstart.setTime(newstart.getTime() - round * 60000); | else if (round > 0) newstart.setTime(newstart.getTime() - round * 60000); | ||||
} | } | ||||
// update event times and display | // update event times and display | ||||
update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000)); | update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000)); | ||||
if (me.selected_event.allDay) | if (me.selected_event.allDay) | ||||
render_freebusy_overlay(); | render_freebusy_overlay(); | ||||
} | } | ||||
}).data('isdraggable', true); | }).data('isdraggable', true); | ||||
} | } | ||||
else | else | ||||
overlay.draggable('enable'); | overlay.draggable('enable'); | ||||
} | } | ||||
else | else | ||||
overlay.draggable('disable').hide(); | overlay.draggable('disable').hide(); | ||||
} | } | ||||
}; | }; | ||||
// fetch free-busy information for each attendee from server | // fetch free-busy information for each attendee from server | ||||
var load_freebusy_data = function(from, interval) | var load_freebusy_data = function(from, interval) | ||||
{ | { | ||||
var start = new Date(from.getTime() - DAY_MS * 2); // start 2 days before event | var start = new Date(from.getTime() - DAY_MS * 2); // start 2 days before event | ||||
fix_date(start); | fix_date(start); | ||||
var end = new Date(start.getTime() + DAY_MS * Math.max(14, freebusy_ui.numdays + 7)); // load min. 14 days | var end = new Date(start.getTime() + DAY_MS * Math.max(14, freebusy_ui.numdays + 7)); // load min. 14 days | ||||
freebusy_ui.numrequired = 0; | freebusy_ui.numrequired = 0; | ||||
freebusy_data.all = []; | freebusy_data.all = []; | ||||
Show All 10 Lines | var load_freebusy_data = function(from, interval) | ||||
$.ajax({ | $.ajax({ | ||||
type: 'GET', | type: 'GET', | ||||
dataType: 'json', | dataType: 'json', | ||||
url: rcmail.url('freebusy-times'), | url: rcmail.url('freebusy-times'), | ||||
data: { email:email, start:date2servertime(clone_date(start, 1)), end:date2servertime(clone_date(end, 2)), interval:interval, _remote:1 }, | data: { email:email, start:date2servertime(clone_date(start, 1)), end:date2servertime(clone_date(end, 2)), interval:interval, _remote:1 }, | ||||
success: function(data) { | success: function(data) { | ||||
freebusy_ui.loading--; | freebusy_ui.loading--; | ||||
// find attendee | // find attendee | ||||
var attendee = null; | var i, attendee = null; | ||||
for (var i=0; i < event_attendees.length; i++) { | for (i=0; i < event_attendees.length; i++) { | ||||
if (freebusy_ui.attendees[i].email == data.email) { | if (freebusy_ui.attendees[i].email == data.email) { | ||||
attendee = freebusy_ui.attendees[i]; | attendee = freebusy_ui.attendees[i]; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// copy data to member var | // copy data to member var | ||||
var ts, req = attendee.role != 'OPT-PARTICIPANT'; | var ts, status, | ||||
freebusy_data.start = parseISO8601(data.start); | req = attendee.role != 'OPT-PARTICIPANT', | ||||
start = parseISO8601(data.start); | |||||
freebusy_data.start = new Date(start); | |||||
freebusy_data.end = parseISO8601(data.end); | |||||
freebusy_data.interval = data.interval; | |||||
freebusy_data[data.email] = {}; | freebusy_data[data.email] = {}; | ||||
for (var i=0; i < data.slots.length; i++) { | |||||
ts = data.times[i] + ''; | for (i=0; i < data.slots.length; i++) { | ||||
freebusy_data[data.email][ts] = data.slots[i]; | ts = date2timestring(start, data.interval > 60); | ||||
status = data.slots.charAt(i); | |||||
freebusy_data[data.email][ts] = status | |||||
start = new Date(start.getTime() + data.interval * 60000); | |||||
// set totals | // set totals | ||||
if (!freebusy_data.required[ts]) | if (!freebusy_data.required[ts]) | ||||
freebusy_data.required[ts] = [0,0,0,0]; | freebusy_data.required[ts] = [0,0,0,0]; | ||||
if (req) | if (req) | ||||
freebusy_data.required[ts][data.slots[i]]++; | freebusy_data.required[ts][status]++; | ||||
if (!freebusy_data.all[ts]) | if (!freebusy_data.all[ts]) | ||||
freebusy_data.all[ts] = [0,0,0,0]; | freebusy_data.all[ts] = [0,0,0,0]; | ||||
freebusy_data.all[ts][data.slots[i]]++; | freebusy_data.all[ts][status]++; | ||||
} | } | ||||
freebusy_data.end = parseISO8601(data.end); | |||||
freebusy_data.interval = data.interval; | |||||
// hide loading indicator | // hide loading indicator | ||||
var domid = String(data.email).replace(rcmail.identifier_expr, ''); | var domid = String(data.email).replace(rcmail.identifier_expr, ''); | ||||
$('#rcmli' + domid).removeClass('loading'); | $('#rcmli' + domid).removeClass('loading'); | ||||
// update display | // update display | ||||
update_freebusy_display(data.email); | update_freebusy_display(data.email); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | var update_freebusy_display = function(email) | ||||
var rowall = $('#fbrowall').children(); | var rowall = $('#fbrowall').children(); | ||||
var dateonly = freebusy_ui.interval > 60, | var dateonly = freebusy_ui.interval > 60, | ||||
t, ts = date2timestring(freebusy_ui.start, dateonly), | t, ts = date2timestring(freebusy_ui.start, dateonly), | ||||
curdate = new Date(), | curdate = new Date(), | ||||
fbdata = freebusy_data[email]; | fbdata = freebusy_data[email]; | ||||
if (fbdata && fbdata[ts] !== undefined && row.length) { | if (fbdata && fbdata[ts] !== undefined && row.length) { | ||||
t = freebusy_ui.start.getTime(); | t = freebusy_ui.start.getTime(); | ||||
row.children().each(function(i, cell){ | row.children().each(function(i, cell) { | ||||
var j, n, attr, last, all_slots = [], slots = [], | |||||
all_cell = rowall.get(i), | |||||
cnt = dateonly ? 1 : (60 / freebusy_ui.interval), | |||||
percent = (100 / cnt); | |||||
for (n=0; n < cnt; n++) { | |||||
curdate.setTime(t); | curdate.setTime(t); | ||||
ts = date2timestring(curdate, dateonly); | ts = date2timestring(curdate, dateonly); | ||||
cell.className = cell.className.replace('unknown', fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown'); | attr = { | ||||
'style': 'float:left; width:' + percent.toFixed(2) + '%', | |||||
'class': fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown' | |||||
}; | |||||
slots.push($('<div>').attr(attr)); | |||||
// also update total row if all data was loaded | // also update total row if all data was loaded | ||||
if (freebusy_ui.loading == 0 && freebusy_data.all[ts] && (cell = rowall.get(i))) { | if (!freebusy_ui.loading && freebusy_data.all[ts] && all_cell) { | ||||
var workinghours = cell.className.indexOf('workinghours') >= 0; | var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown', | ||||
var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown'; | |||||
req_status = freebusy_data.required[ts][2] ? 'busy' : 'free'; | req_status = freebusy_data.required[ts][2] ? 'busy' : 'free'; | ||||
for (var j=1; j < status_classes.length; j++) { | |||||
for (j=1; j < status_classes.length; j++) { | |||||
if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired) | if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired) | ||||
req_status = status_classes[j]; | req_status = status_classes[j]; | ||||
if (freebusy_data.all[ts][j] == event_attendees.length) | if (freebusy_data.all[ts][j] == event_attendees.length) | ||||
all_status = status_classes[j]; | all_status = status_classes[j]; | ||||
} | } | ||||
cell.className = (workinghours ? 'workinghours ' : 'offhours ') + req_status + ' all-' + all_status; | attr['class'] = req_status + ' all-' + all_status; | ||||
// these elements use some specific styling, so we want to minimize their number | |||||
if (last && last.attr('class') == attr['class']) | |||||
last.css('width', (percent + parseFloat(last.css('width').replace('%', ''))).toFixed(2) + '%'); | |||||
else { | |||||
last = $('<div>').attr(attr); | |||||
all_slots.push(last); | |||||
} | |||||
} | } | ||||
t += freebusy_ui.interval * 60000; | t += freebusy_ui.interval * 60000; | ||||
} | |||||
$(cell).html('').append(slots); | |||||
if (all_slots.length) | |||||
$(all_cell).html('').append(all_slots); | |||||
}); | }); | ||||
} | } | ||||
}; | }; | ||||
// write changed event date/times back to form fields | // write changed event date/times back to form fields | ||||
var update_freebusy_dates = function(start, end) | var update_freebusy_dates = function(start, end) | ||||
{ | { | ||||
// fix all-day evebt times | // fix all-day evebt times | ||||
Show All 23 Lines | var freebusy_find_slot = function(dir) | ||||
var event = me.selected_event, | var event = me.selected_event, | ||||
eventstart = clone_date(event.start, event.allDay ? 1 : 0).getTime(), // calculate with integers | eventstart = clone_date(event.start, event.allDay ? 1 : 0).getTime(), // calculate with integers | ||||
eventend = clone_date(event.end, event.allDay ? 2 : 0).getTime(), | eventend = clone_date(event.end, event.allDay ? 2 : 0).getTime(), | ||||
duration = eventend - eventstart - (event.allDay ? HOUR_MS : 0), /* make sure we don't cross day borders on DST change */ | duration = eventend - eventstart - (event.allDay ? HOUR_MS : 0), /* make sure we don't cross day borders on DST change */ | ||||
sinterval = freebusy_data.interval * 60000, | sinterval = freebusy_data.interval * 60000, | ||||
intvlslots = 1, | intvlslots = 1, | ||||
numslots = Math.ceil(duration / sinterval), | numslots = Math.ceil(duration / sinterval), | ||||
checkdate, slotend, email, ts, slot, slotdate = new Date(); | fb_start = freebusy_data.start.getTime(), | ||||
fb_end = freebusy_data.end.getTime(), | |||||
checkdate, slotend, email, ts, slot, slotdate = new Date(), | |||||
candidatecount = 0, candidatestart = false, success = false; | |||||
// shift event times to next possible slot | // shift event times to next possible slot | ||||
eventstart += sinterval * intvlslots * dir; | eventstart += sinterval * intvlslots * dir; | ||||
eventend += sinterval * intvlslots * dir; | eventend += sinterval * intvlslots * dir; | ||||
// iterate through free-busy slots and find candidates | // iterate through free-busy slots and find candidates | ||||
var candidatecount = 0, candidatestart = candidateend = success = false; | for (slot = dir > 0 ? fb_start : fb_end - sinterval; | ||||
for (slot = dir > 0 ? freebusy_data.start.getTime() : freebusy_data.end.getTime() - sinterval; | (dir > 0 && slot < fb_end) || (dir < 0 && slot >= fb_start); | ||||
(dir > 0 && slot < freebusy_data.end.getTime()) || (dir < 0 && slot >= freebusy_data.start.getTime()); | slot += sinterval * dir | ||||
slot += sinterval * dir) { | ) { | ||||
slotdate.setTime(slot); | slotdate.setTime(slot); | ||||
// fix slot if just crossed a DST change | // fix slot if just crossed a DST change | ||||
if (event.allDay) { | if (event.allDay) { | ||||
fix_date(slotdate); | fix_date(slotdate); | ||||
slot = slotdate.getTime(); | slot = slotdate.getTime(); | ||||
} | } | ||||
slotend = slot + sinterval; | slotend = slot + sinterval; | ||||
if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend)) // skip | if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend)) // skip | ||||
continue; | continue; | ||||
// respect workingours setting | // respect workinghours setting | ||||
if (freebusy_ui.workinhoursonly) { | if (freebusy_ui.workinhoursonly) { | ||||
if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) { // skip off-hours | if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) { // skip off-hours | ||||
candidatestart = candidateend = false; | candidatestart = false; | ||||
candidatecount = 0; | candidatecount = 0; | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
if (!candidatestart) | if (!candidatestart) | ||||
candidatestart = slot; | candidatestart = slot; | ||||
// check freebusy data for all attendees | |||||
ts = date2timestring(slotdate, freebusy_data.interval > 60); | ts = date2timestring(slotdate, freebusy_data.interval > 60); | ||||
// check freebusy data for all attendees | |||||
for (var i=0; i < event_attendees.length; i++) { | for (var i=0; i < event_attendees.length; i++) { | ||||
if (freebusy_ui.attendees[i].role != 'OPT-PARTICIPANT' && (email = freebusy_ui.attendees[i].email) && freebusy_data[email] && freebusy_data[email][ts] > 1) { | if (freebusy_ui.attendees[i].role != 'OPT-PARTICIPANT' && (email = freebusy_ui.attendees[i].email) && freebusy_data[email] && freebusy_data[email][ts] > 1) { | ||||
candidatestart = candidateend = false; | candidatestart = false; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// occupied slot | // occupied slot | ||||
if (!candidatestart) { | if (!candidatestart) { | ||||
slot += Math.max(0, intvlslots - candidatecount - 1) * sinterval * dir; | slot += Math.max(0, intvlslots - candidatecount - 1) * sinterval * dir; | ||||
candidatecount = 0; | candidatecount = 0; | ||||
continue; | continue; | ||||
} | } | ||||
else if (dir < 0) | |||||
candidatestart = slot; | |||||
// set candidate end to slot end time | |||||
candidatecount++; | candidatecount++; | ||||
if (dir < 0 && !candidateend) | |||||
candidateend = slotend; | |||||
// if candidate is big enough, this is it! | // if candidate is big enough, this is it! | ||||
if (candidatecount == numslots) { | if (candidatecount == numslots) { | ||||
if (dir > 0) { | |||||
event.start.setTime(candidatestart); | event.start.setTime(candidatestart); | ||||
event.end.setTime(candidatestart + duration); | event.end.setTime(candidatestart + duration); | ||||
} | |||||
else { | |||||
event.end.setTime(candidateend); | |||||
event.start.setTime(candidateend - duration); | |||||
} | |||||
success = true; | success = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// update event date/time display | // update event date/time display | ||||
if (success) { | if (success) { | ||||
update_freebusy_dates(event.start, event.end); | update_freebusy_dates(event.start, event.end); | ||||
Show All 13 Lines | var freebusy_find_slot = function(dir) | ||||
// speak new selection | // speak new selection | ||||
rcmail.display_message(rcmail.gettext('suggestedslot', 'calendar') + ': ' + me.event_date_text(event, true), 'voice'); | rcmail.display_message(rcmail.gettext('suggestedslot', 'calendar') + ': ' + me.event_date_text(event, true), 'voice'); | ||||
} | } | ||||
else { | else { | ||||
alert(rcmail.gettext('noslotfound','calendar')); | alert(rcmail.gettext('noslotfound','calendar')); | ||||
} | } | ||||
}; | }; | ||||
// update event properties and attendees availability if event times have changed | // update event properties and attendees availability if event times have changed | ||||
var event_times_changed = function() | var event_times_changed = function() | ||||
{ | { | ||||
if (me.selected_event) { | if (me.selected_event) { | ||||
var allday = $('#edit-allday').get(0); | var allday = $('#edit-allday').get(0); | ||||
me.selected_event.allDay = allday.checked; | me.selected_event.allDay = allday.checked; | ||||
me.selected_event.start = parse_datetime(allday.checked ? '12:00' : $('#edit-starttime').val(), $('#edit-startdate').val()); | me.selected_event.start = parse_datetime(allday.checked ? '12:00' : $('#edit-starttime').val(), $('#edit-startdate').val()); | ||||
me.selected_event.end = parse_datetime(allday.checked ? '13:00' : $('#edit-endtime').val(), $('#edit-enddate').val()); | me.selected_event.end = parse_datetime(allday.checked ? '13:00' : $('#edit-endtime').val(), $('#edit-enddate').val()); | ||||
if (event_attendees) | if (event_attendees) | ||||
freebusy_ui.needsupdate = true; | freebusy_ui.needsupdate = true; | ||||
$('#edit-startdate').data('duration', Math.round((me.selected_event.end.getTime() - me.selected_event.start.getTime()) / 1000)); | $('#edit-startdate').data('duration', Math.round((me.selected_event.end.getTime() - me.selected_event.start.getTime()) / 1000)); | ||||
} | } | ||||
}; | }; | ||||
// add the given list of participants | // add the given list of participants | ||||
var add_attendees = function(names, params) | var add_attendees = function(names, params) | ||||
{ | { | ||||
names = explode_quoted_string(names.replace(/,\s*$/, ''), ','); | names = explode_quoted_string(names.replace(/,\s*$/, ''), ','); | ||||
// parse name/email pairs | // parse name/email pairs | ||||
var item, email, name, success = false; | var item, email, name, success = false; | ||||
for (var i=0; i < names.length; i++) { | for (var i=0; i < names.length; i++) { | ||||
▲ Show 20 Lines • Show All 2,377 Lines • Show Last 20 Lines |