Consider event duration and calendar_timeslots setting in availability finder (#5469, T1381)
Summary: 1. "Previous Slot" action moves the selection box to the beginning of the time slot not the end. It looked to me as an obvious mistake. 2. Both "previous slot" and "next slot" move the selection box in intervals defined in calendar_timeslots option instead of one hour. I.e. it will be one hour, 30 minutes, 20 minutes, 15 minutes or 10 minutes, depending what's configured in Preferences > Calendar > Time slots per hour. Reviewers: #roundcube_kolab_plugins_developers Differential Revision: https://git.kolab.org/D207
This commit is contained in:
parent
96cdf7c93e
commit
d575d09fd5
3 changed files with 159 additions and 106 deletions
|
@ -2185,6 +2185,7 @@ class calendar extends rcube_plugin
|
||||||
|
|
||||||
// if the backend has free-busy information
|
// if the backend has free-busy information
|
||||||
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
|
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
|
||||||
|
|
||||||
if (is_array($fblist)) {
|
if (is_array($fblist)) {
|
||||||
$status = 'FREE';
|
$status = 'FREE';
|
||||||
|
|
||||||
|
@ -2236,11 +2237,24 @@ class calendar extends rcube_plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
|
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
|
||||||
$slots = array();
|
$slots = '';
|
||||||
|
|
||||||
|
// prepare freebusy list before use (for better performance)
|
||||||
|
if (is_array($fblist)) {
|
||||||
|
foreach ($fblist as $idx => $slot) {
|
||||||
|
list($from, $to, ) = $slot;
|
||||||
|
|
||||||
|
// check for possible all-day times
|
||||||
|
if (gmdate('His', $from) == '000000' && gmdate('His', $to) == '235959') {
|
||||||
|
// shift into the user's timezone for sane matching
|
||||||
|
$fblist[$idx][0] -= $this->gmt_offset;
|
||||||
|
$fblist[$idx][1] -= $this->gmt_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// build a list from $start till $end with blocks representing the fb-status
|
// build a list from $start till $end with blocks representing the fb-status
|
||||||
for ($s = 0, $t = $start; $t <= $end; $s++) {
|
for ($s = 0, $t = $start; $t <= $end; $s++) {
|
||||||
$status = self::FREEBUSY_UNKNOWN;
|
|
||||||
$t_end = $t + $interval * 60;
|
$t_end = $t + $interval * 60;
|
||||||
$dt = new DateTime('@'.$t);
|
$dt = new DateTime('@'.$t);
|
||||||
$dt->setTimezone($this->timezone);
|
$dt->setTimezone($this->timezone);
|
||||||
|
@ -2248,16 +2262,10 @@ class calendar extends rcube_plugin
|
||||||
// determine attendee's status
|
// determine attendee's status
|
||||||
if (is_array($fblist)) {
|
if (is_array($fblist)) {
|
||||||
$status = self::FREEBUSY_FREE;
|
$status = self::FREEBUSY_FREE;
|
||||||
|
|
||||||
foreach ($fblist as $slot) {
|
foreach ($fblist as $slot) {
|
||||||
list($from, $to, $type) = $slot;
|
list($from, $to, $type) = $slot;
|
||||||
|
|
||||||
// check for possible all-day times
|
|
||||||
if (gmdate('His', $from) == '000000' && gmdate('His', $to) == '235959') {
|
|
||||||
// shift into the user's timezone for sane matching
|
|
||||||
$from -= $this->gmt_offset;
|
|
||||||
$to -= $this->gmt_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($from < $t_end && $to > $t) {
|
if ($from < $t_end && $to > $t) {
|
||||||
$status = isset($type) ? $type : self::FREEBUSY_BUSY;
|
$status = isset($type) ? $type : self::FREEBUSY_BUSY;
|
||||||
if ($status == self::FREEBUSY_BUSY) // can't get any worse :-)
|
if ($status == self::FREEBUSY_BUSY) // can't get any worse :-)
|
||||||
|
@ -2265,9 +2273,12 @@ class calendar extends rcube_plugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
$status = self::FREEBUSY_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
$slots[$s] = $status;
|
// use most compact format, assume $status is one digit/character
|
||||||
$times[$s] = intval($dt->format($strformat));
|
$slots .= $status;
|
||||||
$t = $t_end;
|
$t = $t_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2283,7 +2294,6 @@ class calendar extends rcube_plugin
|
||||||
'end' => $dte->format('c'),
|
'end' => $dte->format('c'),
|
||||||
'interval' => $interval,
|
'interval' => $interval,
|
||||||
'slots' => $slots,
|
'slots' => $slots,
|
||||||
'times' => $times,
|
|
||||||
));
|
));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1238,7 +1238,7 @@ function rcube_calendar_ui(settings)
|
||||||
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);
|
||||||
|
@ -1345,7 +1345,7 @@ function rcube_calendar_ui(settings)
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -1375,10 +1375,12 @@ function rcube_calendar_ui(settings)
|
||||||
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);
|
||||||
|
@ -1391,9 +1393,9 @@ function rcube_calendar_ui(settings)
|
||||||
// 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>';
|
||||||
|
@ -1407,7 +1409,7 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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');
|
||||||
|
@ -1429,7 +1431,7 @@ function rcube_calendar_ui(settings)
|
||||||
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
|
||||||
|
@ -1460,38 +1462,40 @@ function rcube_calendar_ui(settings)
|
||||||
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++) {
|
||||||
slotend = slotstart + slotsize - 1;
|
for (n=0; n < slotnum; n++) {
|
||||||
// event starts in this slot: compute left
|
slotend = slotstart + slotsize - 1;
|
||||||
if (eventstart >= slotstart && eventstart <= slotend) {
|
// event starts in this slot: compute left
|
||||||
fraction = 1 - (slotend - eventstart) / slotsize;
|
if (eventstart >= slotstart && eventstart <= slotend) {
|
||||||
pos.left = Math.round(cell.offsetLeft + cell.offsetWidth * fraction);
|
pos.left = Math.round(i * cell_width + (cell_width / slotnum) * n);
|
||||||
|
}
|
||||||
|
// event ends in this slot: compute width
|
||||||
|
if (eventend >= slotstart && eventend <= slotend) {
|
||||||
|
width = Math.round(i * cell_width + (cell_width / slotnum) * (n + 1)) - pos.left;
|
||||||
|
}
|
||||||
|
slotstart += slotsize;
|
||||||
}
|
}
|
||||||
// event ends in this slot: compute width
|
}
|
||||||
if (eventend >= slotstart && eventend <= slotend) {
|
|
||||||
fraction = 1 - (slotend - eventend) / slotsize;
|
|
||||||
width = Math.round(cell.offsetLeft + cell.offsetWidth * fraction) - pos.left;
|
|
||||||
}
|
|
||||||
|
|
||||||
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')) {
|
||||||
|
@ -1514,6 +1518,7 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
@ -1531,10 +1536,8 @@ function rcube_calendar_ui(settings)
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -1562,8 +1565,8 @@ function rcube_calendar_ui(settings)
|
||||||
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;
|
||||||
|
@ -1571,25 +1574,31 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, '');
|
||||||
|
@ -1652,27 +1661,51 @@ function rcube_calendar_ui(settings)
|
||||||
|
|
||||||
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) {
|
||||||
curdate.setTime(t);
|
var j, n, attr, last, all_slots = [], slots = [],
|
||||||
ts = date2timestring(curdate, dateonly);
|
all_cell = rowall.get(i),
|
||||||
cell.className = cell.className.replace('unknown', fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown');
|
cnt = dateonly ? 1 : (60 / freebusy_ui.interval),
|
||||||
|
percent = (100 / cnt);
|
||||||
|
|
||||||
// also update total row if all data was loaded
|
for (n=0; n < cnt; n++) {
|
||||||
if (freebusy_ui.loading == 0 && freebusy_data.all[ts] && (cell = rowall.get(i))) {
|
curdate.setTime(t);
|
||||||
var workinghours = cell.className.indexOf('workinghours') >= 0;
|
ts = date2timestring(curdate, dateonly);
|
||||||
var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown';
|
attr = {
|
||||||
req_status = freebusy_data.required[ts][2] ? 'busy' : 'free';
|
'style': 'float:left; width:' + percent.toFixed(2) + '%',
|
||||||
for (var j=1; j < status_classes.length; j++) {
|
'class': fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown'
|
||||||
if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired)
|
};
|
||||||
req_status = status_classes[j];
|
|
||||||
if (freebusy_data.all[ts][j] == event_attendees.length)
|
slots.push($('<div>').attr(attr));
|
||||||
all_status = status_classes[j];
|
|
||||||
|
// also update total row if all data was loaded
|
||||||
|
if (!freebusy_ui.loading && freebusy_data.all[ts] && all_cell) {
|
||||||
|
var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown',
|
||||||
|
req_status = freebusy_data.required[ts][2] ? 'busy' : 'free';
|
||||||
|
|
||||||
|
for (j=1; j < status_classes.length; j++) {
|
||||||
|
if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired)
|
||||||
|
req_status = status_classes[j];
|
||||||
|
if (freebusy_data.all[ts][j] == event_attendees.length)
|
||||||
|
all_status = status_classes[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.className = (workinghours ? 'workinghours ' : 'offhours ') + req_status + ' all-' + all_status;
|
t += freebusy_ui.interval * 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
t += freebusy_ui.interval * 60000;
|
$(cell).html('').append(slots);
|
||||||
|
if (all_slots.length)
|
||||||
|
$(all_cell).html('').append(all_slots);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1712,17 +1745,20 @@ function rcube_calendar_ui(settings)
|
||||||
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) {
|
||||||
|
@ -1734,10 +1770,10 @@ function rcube_calendar_ui(settings)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -1746,11 +1782,12 @@ function rcube_calendar_ui(settings)
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1761,22 +1798,15 @@ function rcube_calendar_ui(settings)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -1806,7 +1836,6 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 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()
|
||||||
{
|
{
|
||||||
|
@ -1821,7 +1850,6 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// add the given list of participants
|
// add the given list of participants
|
||||||
var add_attendees = function(names, params)
|
var add_attendees = function(names, params)
|
||||||
{
|
{
|
||||||
|
|
|
@ -115,6 +115,7 @@ body.calendarmain #mainscreen {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
top: 40px !important;
|
top: 40px !important;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
height: auto;
|
||||||
background: url(images/toggle.gif) -1px 48% no-repeat transparent;
|
background: url(images/toggle.gif) -1px 48% no-repeat transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,44 +1246,44 @@ td.topalign {
|
||||||
background: url(images/loading_blue.gif) center no-repeat;
|
background: url(images/loading_blue.gif) center no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.unknown,
|
#schedule-freebusy-times td div.unknown,
|
||||||
.availability img.availabilityicon.unknown {
|
.availability img.availabilityicon.unknown {
|
||||||
background: #ddd;
|
background: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.free,
|
#schedule-freebusy-times td div.free,
|
||||||
.availability img.availabilityicon.free {
|
.availability img.availabilityicon.free {
|
||||||
background: #abd640;
|
background: #abd640;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.busy,
|
#schedule-freebusy-times td div.busy,
|
||||||
.availability img.availabilityicon.busy {
|
.availability img.availabilityicon.busy {
|
||||||
background: #e26569;
|
background: #e26569;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.tentative,
|
#schedule-freebusy-times td div.tentative,
|
||||||
.availability img.availabilityicon.tentative {
|
.availability img.availabilityicon.tentative {
|
||||||
background: #8383fc;
|
background: #8383fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.out-of-office,
|
#schedule-freebusy-times td div.out-of-office,
|
||||||
.availability img.availabilityicon.out-of-office {
|
.availability img.availabilityicon.out-of-office {
|
||||||
background: #fbaa68;
|
background: #fbaa68;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.all-busy,
|
#schedule-freebusy-times td div.all-busy,
|
||||||
#schedule-freebusy-times td.all-tentative,
|
#schedule-freebusy-times td div.all-tentative,
|
||||||
#schedule-freebusy-times td.all-out-of-office {
|
#schedule-freebusy-times td div.all-out-of-office {
|
||||||
background-image: url(images/freebusy-colors.png);
|
background-image: url(images/freebusy-colors.png);
|
||||||
background-position: top right;
|
background-position: top right;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.all-tentative {
|
#schedule-freebusy-times td div.all-tentative {
|
||||||
background-position: right -40px;
|
background-position: right -40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times td.all-out-of-office {
|
#schedule-freebusy-times td div.all-out-of-office {
|
||||||
background-position: right -80px;
|
background-position: right -80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1409,7 +1410,8 @@ td.topalign {
|
||||||
.attendees-list .spacer,
|
.attendees-list .spacer,
|
||||||
#schedule-freebusy-times tr.spacer td {
|
#schedule-freebusy-times tr.spacer td {
|
||||||
background: 0;
|
background: 0;
|
||||||
font-size: 50%;
|
padding: 0;
|
||||||
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#schedule-freebusy-times {
|
#schedule-freebusy-times {
|
||||||
|
@ -1422,6 +1424,15 @@ td.topalign {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#schedule-freebusy-times tbody td {
|
||||||
|
padding: 0;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#schedule-freebusy-times tbody td div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
#attendees-freebusy-table div.timesheader,
|
#attendees-freebusy-table div.timesheader,
|
||||||
#schedule-freebusy-times tr.times td {
|
#schedule-freebusy-times tr.times td {
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
|
@ -1439,6 +1450,10 @@ td.topalign {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#schedule-freebusy-times #fbrowall td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
#schedule-event-time {
|
#schedule-event-time {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 2px solid #333;
|
border: 2px solid #333;
|
||||||
|
|
Loading…
Add table
Reference in a new issue