2015-05-06 21:03:45 +02:00
|
|
|
/*
|
|
|
|
* Egroupware Calendar event widget
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package etemplate
|
|
|
|
* @subpackage api
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @author Nathan Gray
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*egw:uses
|
|
|
|
/etemplate/js/et2_core_valueWidget;
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class for a single event, displayed in a timegrid
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @augments et2_valueWidget
|
|
|
|
*/
|
|
|
|
var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
|
|
|
|
{
|
|
|
|
|
|
|
|
attributes: {
|
2015-07-03 19:56:36 +02:00
|
|
|
"value": {
|
|
|
|
type: "any",
|
|
|
|
default: et2_no_init
|
|
|
|
},
|
2015-05-06 21:03:45 +02:00
|
|
|
"onclick": {
|
|
|
|
"description": "JS code which is executed when the element is clicked. " +
|
|
|
|
"If no handler is provided, or the handler returns true and the event is not read-only, the " +
|
|
|
|
"event will be opened according to calendar settings."
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @memberOf et2_calendar_daycol
|
|
|
|
*/
|
|
|
|
init: function() {
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
2015-11-13 01:53:23 +01:00
|
|
|
var event = this;
|
|
|
|
|
2015-05-06 21:03:45 +02:00
|
|
|
// Main container
|
|
|
|
this.div = $j(document.createElement("div"))
|
|
|
|
.addClass("calendar_calEvent")
|
2015-06-25 19:44:28 +02:00
|
|
|
.addClass(this.options.class)
|
2015-08-19 02:08:22 +02:00
|
|
|
.css('width',this.options.width)
|
|
|
|
.on('mouseenter', function() {
|
2015-11-13 01:53:23 +01:00
|
|
|
// Hacky to remove egw's tooltip border and let the mouse in
|
2015-08-19 02:08:22 +02:00
|
|
|
window.setTimeout(function() {
|
2015-11-13 01:53:23 +01:00
|
|
|
$j('body .egw_tooltip')
|
|
|
|
.css('border','none')
|
|
|
|
.on('mouseenter', function() {
|
|
|
|
event.div.off('mouseleave.tooltip');
|
|
|
|
$j('body.egw_tooltip').remove();
|
|
|
|
$j('body').append(this);
|
|
|
|
$j(this).stop(true).fadeTo(400, 1)
|
|
|
|
.on('mouseleave', function() {
|
|
|
|
$j(this).fadeOut('400', function() {
|
|
|
|
$j(this).remove();
|
|
|
|
// Set up to work again
|
|
|
|
event.set_statustext(event._tooltip());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-08-19 02:08:22 +02:00
|
|
|
},105);
|
|
|
|
});
|
2015-05-06 21:03:45 +02:00
|
|
|
this.title = $j(document.createElement('div'))
|
|
|
|
.addClass("calendar_calEventHeader")
|
|
|
|
.appendTo(this.div);
|
|
|
|
this.body = $j(document.createElement('div'))
|
|
|
|
.addClass("calendar_calEventBody")
|
|
|
|
.appendTo(this.div);
|
2015-06-10 23:51:28 +02:00
|
|
|
this.icons = $j(document.createElement('div'))
|
|
|
|
.addClass("calendar_calEventIcons")
|
|
|
|
.appendTo(this.title);
|
2015-05-06 21:03:45 +02:00
|
|
|
|
|
|
|
this.setDOMNode(this.div[0]);
|
|
|
|
},
|
|
|
|
|
|
|
|
doLoadingFinished: function() {
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
|
|
|
// Parent will have everything we need, just load it from there
|
|
|
|
if(this.title.text() == '' && this.options.date &&
|
|
|
|
this._parent && this._parent.instanceOf(et2_calendar_timegrid))
|
|
|
|
{
|
|
|
|
// Forces an update
|
|
|
|
var date = this.options.date;
|
|
|
|
this.options.date = '';
|
|
|
|
this.set_date(date);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function() {
|
|
|
|
this._super.apply(this, arguments);
|
2015-08-26 01:30:32 +02:00
|
|
|
|
|
|
|
if(this._actionObject)
|
|
|
|
{
|
|
|
|
this._actionObject.remove();
|
|
|
|
this._actionObject = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.div.off();
|
|
|
|
this.title.remove();
|
|
|
|
this.title = null;
|
|
|
|
this.body.remove();
|
|
|
|
this.body = null;
|
|
|
|
this.icons = null;
|
|
|
|
this.div.remove();
|
|
|
|
this.div = null;
|
|
|
|
|
2015-11-18 18:44:22 +01:00
|
|
|
$j('body.egw_tooltip').remove();
|
2015-08-26 01:30:32 +02:00
|
|
|
|
2015-06-10 23:51:28 +02:00
|
|
|
// Unregister, or we'll continue to be notified...
|
2015-07-03 19:56:36 +02:00
|
|
|
if(this.options.value)
|
|
|
|
{
|
2015-11-09 19:55:48 +01:00
|
|
|
var old_app_id = this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : '');
|
2015-07-03 19:56:36 +02:00
|
|
|
egw.dataUnregisterUID('calendar::'+old_app_id,false,this);
|
|
|
|
}
|
2015-05-06 21:03:45 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
set_value: function(_value) {
|
2015-06-10 23:51:28 +02:00
|
|
|
// Un-register for updates
|
|
|
|
if(this.options.value)
|
|
|
|
{
|
2015-11-09 19:55:48 +01:00
|
|
|
var old_app_id = this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : '');
|
2015-06-10 23:51:28 +02:00
|
|
|
egw.dataUnregisterUID('calendar::'+old_app_id,false,this);
|
|
|
|
}
|
2015-05-06 21:03:45 +02:00
|
|
|
this.options.value = _value;
|
2015-06-10 23:51:28 +02:00
|
|
|
|
|
|
|
// Register for updates
|
2015-11-09 19:55:48 +01:00
|
|
|
var app_id = this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : '');
|
2015-06-10 23:51:28 +02:00
|
|
|
egw.dataRegisterUID('calendar::'+app_id, function(event) {
|
2015-08-06 19:14:20 +02:00
|
|
|
// Check for changing days in the grid view
|
2015-11-17 17:57:34 +01:00
|
|
|
if(!this._sameday_check(event))
|
2015-07-22 01:45:38 +02:00
|
|
|
{
|
2015-11-13 21:22:58 +01:00
|
|
|
// This should now cease to exist, as new events have been created
|
|
|
|
this.free();
|
|
|
|
return;
|
2015-07-22 01:45:38 +02:00
|
|
|
}
|
2015-11-17 17:57:34 +01:00
|
|
|
|
2015-06-10 23:51:28 +02:00
|
|
|
// Copy to avoid changes, which may cause nm problems
|
2015-07-03 19:56:36 +02:00
|
|
|
this.options.value = jQuery.extend({},event);
|
2015-11-17 17:57:34 +01:00
|
|
|
this.options.value.date = this._parent.options.date;
|
2015-07-03 19:56:36 +02:00
|
|
|
|
|
|
|
// Let parent position
|
|
|
|
this._parent.position_event(this);
|
2015-07-22 01:45:38 +02:00
|
|
|
|
|
|
|
// Parent may remove this if the date isn't the same
|
|
|
|
if(this._parent)
|
|
|
|
{
|
|
|
|
this._update(this.options.value);
|
|
|
|
}
|
2015-06-10 23:51:28 +02:00
|
|
|
|
|
|
|
},this,this.getInstanceManager().execId,this.id);
|
|
|
|
|
2015-07-15 18:29:10 +02:00
|
|
|
if(_value && !egw.dataHasUID('calendar::'+app_id))
|
2015-06-10 23:51:28 +02:00
|
|
|
{
|
2015-07-15 18:29:10 +02:00
|
|
|
egw.dataStoreUID('calendar::'+app_id, _value);
|
2015-06-10 23:51:28 +02:00
|
|
|
}
|
2015-05-06 21:03:45 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
_update: function(event) {
|
|
|
|
|
2015-06-10 23:51:28 +02:00
|
|
|
// Copy new information
|
|
|
|
this.options.value = event;
|
|
|
|
|
2015-05-06 21:03:45 +02:00
|
|
|
var eventId = event.id.match(/-?\d+\.?\d*/g)[0];
|
|
|
|
var appName = event.id.replace(/-?\d+\.?\d*/g,'');
|
2015-06-10 23:51:28 +02:00
|
|
|
var app_id = event.app_id ? event.app_id : event.id + (event.recur_type ? ':'+event.recur_date : '');
|
2015-07-22 01:45:38 +02:00
|
|
|
this._parent.date_helper.set_value(event.start.valueOf ? new Date(event.start) : event.start);
|
2015-06-10 23:51:28 +02:00
|
|
|
var formatted_start = this._parent.date_helper.getValue();
|
2015-05-06 21:03:45 +02:00
|
|
|
|
2015-07-22 01:45:38 +02:00
|
|
|
this.set_id('event_' + (eventId || event.id));
|
2015-06-10 23:51:28 +02:00
|
|
|
|
2015-11-10 00:06:17 +01:00
|
|
|
// Make sure category stuff is there
|
|
|
|
// Fake it to use the cache / call - if already there, these will return
|
|
|
|
// immediately.
|
|
|
|
var im = this.getInstanceManager();
|
|
|
|
et2_selectbox.cat_options({
|
|
|
|
_type:'select-cat',
|
|
|
|
getInstanceManager: function() {return im}
|
|
|
|
}, {application:event.app||'calendar'});
|
|
|
|
|
|
|
|
// Get CSS too
|
|
|
|
egw.includeCSS('/phpgwapi/categories.php?app='+event.app);
|
|
|
|
|
|
|
|
// DOM nodes
|
2015-05-06 21:03:45 +02:00
|
|
|
this.div
|
2015-06-10 23:51:28 +02:00
|
|
|
// Empty & re-append to make sure dnd helpers are gone
|
|
|
|
.empty()
|
|
|
|
.append(this.title)
|
|
|
|
.append(this.body)
|
2015-11-18 19:40:52 +01:00
|
|
|
|
|
|
|
// Let timegrid always get the drag
|
|
|
|
.droppable('option','greedy',false)
|
2015-06-10 23:51:28 +02:00
|
|
|
|
|
|
|
// ?
|
2015-05-06 21:03:45 +02:00
|
|
|
.attr('data-draggable-id',event['id']+'_O'+event.owner+'_C'+(event.owner<0?'group'+Math.abs(event.owner):event.owner))
|
|
|
|
|
|
|
|
// Put everything we need for basic interaction here, so it's available immediately
|
|
|
|
.attr('data-id', eventId || event.id)
|
|
|
|
.attr('data-app', appName || 'calendar')
|
|
|
|
.attr('data-app_id', app_id)
|
2015-06-10 23:51:28 +02:00
|
|
|
.attr('data-start', formatted_start)
|
|
|
|
.attr('data-owner', event.owner)
|
2015-05-06 21:03:45 +02:00
|
|
|
.attr('data-recur_type', event.recur_type)
|
2015-06-10 23:51:28 +02:00
|
|
|
.attr('data-resize', event.whole_day ? 'WD' : '' + (event.recur_type ? 'S':''))
|
2015-05-06 21:03:45 +02:00
|
|
|
// Remove any category classes
|
|
|
|
.removeClass(function(index, css) {
|
|
|
|
return (css.match (/(^|\s)cat_\S+/g) || []).join(' ');
|
2015-06-10 23:51:28 +02:00
|
|
|
})
|
2015-07-01 18:02:20 +02:00
|
|
|
// Remove any status classes
|
|
|
|
.removeClass(function(index, css) {
|
|
|
|
return (css.match(/calendar_calEvent\S+/g) || []).join(' ');
|
|
|
|
})
|
2015-06-10 23:51:28 +02:00
|
|
|
// Remove any resize classes, the handles are gone due to empty()
|
2015-07-01 18:02:20 +02:00
|
|
|
.removeClass('ui-resizable')
|
|
|
|
.addClass(event.class)
|
2015-11-12 19:22:48 +01:00
|
|
|
.toggleClass('calendar_calEventPrivate', typeof event.private !== 'undefined' && event.private);
|
2015-07-22 01:45:38 +02:00
|
|
|
this.options.class = event.class;
|
2015-11-12 19:22:48 +01:00
|
|
|
var status_class = this._status_class();
|
|
|
|
|
|
|
|
// Add category classes, if real categories are set
|
|
|
|
if(event.category && event.category != '0')
|
2015-05-06 21:03:45 +02:00
|
|
|
{
|
2015-10-14 17:25:29 +02:00
|
|
|
var cats = event.category.split(',');
|
|
|
|
for(var i = 0; i < cats.length; i++)
|
|
|
|
{
|
|
|
|
this.div.addClass('cat_' + cats[i]);
|
|
|
|
}
|
2015-05-06 21:03:45 +02:00
|
|
|
}
|
|
|
|
|
2015-07-01 18:02:20 +02:00
|
|
|
this.div.toggleClass('calendar_calEventUnknown', event.participants[egw.user('account_id')] ? event.participants[egw.user('account_id')][0] === 'U' : false);
|
2015-11-12 19:22:48 +01:00
|
|
|
this.div.addClass(status_class);
|
2015-05-06 21:03:45 +02:00
|
|
|
|
|
|
|
this.title.toggle(!event.whole_day_on_top);
|
|
|
|
this.body.toggleClass('calendar_calEventBodySmall', event.whole_day_on_top || false);
|
|
|
|
|
|
|
|
// Header
|
|
|
|
var title = !event.is_private ? event['title'] : egw.lang('private');
|
2015-11-11 19:48:41 +01:00
|
|
|
// If there isn't enough height for header + 1 line in the body, it's small
|
|
|
|
var small_height = this.div.innerHeight() <= this.title.height() * 2;
|
2015-05-06 21:03:45 +02:00
|
|
|
|
2015-06-10 23:51:28 +02:00
|
|
|
this.div.attr('data-title', title);
|
2015-10-09 18:33:34 +02:00
|
|
|
this.title.text(small_height ? title : this._get_timespan(event));
|
2015-11-11 19:48:41 +01:00
|
|
|
|
2015-10-09 18:33:34 +02:00
|
|
|
// Colors - don't make them transparent if there is no color
|
|
|
|
if(jQuery.Color("rgba(0,0,0,0)").toRgbaString() != jQuery.Color(this.div,'background-color').toRgbaString())
|
|
|
|
{
|
2015-11-12 19:22:48 +01:00
|
|
|
// Most statuses use colored borders
|
2015-11-13 00:34:04 +01:00
|
|
|
this.div.css('border-color',status_class === 'calendar_calEventAllAccepted' ? this.div.css('background-color') : '');
|
2015-11-12 19:22:48 +01:00
|
|
|
|
|
|
|
// Set title color based on background brightness
|
2015-10-09 18:33:34 +02:00
|
|
|
this.title
|
|
|
|
.css('background-color', this.div.css('background-color'))
|
2015-11-11 19:01:35 +01:00
|
|
|
.css('color', jQuery.Color(this.div.css('background-color')).lightness() > 0.45 ? 'black':'white');
|
2015-10-09 18:33:34 +02:00
|
|
|
}
|
2015-05-06 21:03:45 +02:00
|
|
|
|
2015-06-10 23:51:28 +02:00
|
|
|
this.icons.appendTo(this.title)
|
|
|
|
.html(this._icons());
|
|
|
|
|
2015-05-06 21:03:45 +02:00
|
|
|
// Body
|
|
|
|
if(event.whole_day_on_top)
|
|
|
|
{
|
|
|
|
this.body.html(title);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-11 19:48:41 +01:00
|
|
|
this.body
|
|
|
|
.html('<span class="calendar_calEventTitle">'+title+'</span>')
|
|
|
|
.append('<p>'+this.options.value.description+'</p>');
|
2015-05-06 21:03:45 +02:00
|
|
|
}
|
|
|
|
this.body
|
|
|
|
// Set background color to a lighter version of the header color
|
2015-11-12 19:22:48 +01:00
|
|
|
.css('background-color',jQuery.Color(this.title.css('background-color')).lightness(
|
|
|
|
Math.max(0.8, parseFloat(jQuery.Color(this.title.css('background-color')).lightness()))
|
|
|
|
));
|
2015-05-06 21:03:45 +02:00
|
|
|
|
2015-06-10 23:51:28 +02:00
|
|
|
this.set_statustext(this._tooltip());
|
|
|
|
},
|
|
|
|
|
2015-07-01 18:02:20 +02:00
|
|
|
/**
|
|
|
|
* Examines the participants & returns CSS classname for status
|
|
|
|
*
|
|
|
|
* @returns {String}
|
|
|
|
*/
|
|
|
|
_status_class: function() {
|
2015-06-10 23:51:28 +02:00
|
|
|
var status_class = 'calendar_calEventAllAccepted';
|
|
|
|
for(var id in this.options.value.participants)
|
|
|
|
{
|
|
|
|
var status = this.options.value.participants[id];
|
|
|
|
|
|
|
|
status = et2_calendar_event.split_status(status);
|
|
|
|
|
|
|
|
switch (status)
|
|
|
|
{
|
|
|
|
case 'A':
|
|
|
|
case '': // app without status
|
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
status_class = 'calendar_calEventSomeUnknown';
|
2015-07-01 18:02:20 +02:00
|
|
|
return status_class; // break for
|
2015-06-10 23:51:28 +02:00
|
|
|
default:
|
|
|
|
status_class = 'calendar_calEventAllAnswered';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-07-01 18:02:20 +02:00
|
|
|
return status_class;
|
|
|
|
},
|
|
|
|
|
|
|
|
_tooltip: function() {
|
2015-11-17 17:57:34 +01:00
|
|
|
if(!this.div) return '';
|
2015-07-01 18:02:20 +02:00
|
|
|
|
2015-10-15 23:34:07 +02:00
|
|
|
var border = this.div.css('borderTopColor');
|
2015-06-10 23:51:28 +02:00
|
|
|
var bg_color = this.div.css('background-color');
|
|
|
|
var header_color = this.title.css('color');
|
|
|
|
|
2015-07-22 01:45:38 +02:00
|
|
|
this._parent.date_helper.set_value(this.options.value.start.valueOf ? new Date(this.options.value.start) : this.options.value.start);
|
2015-06-10 23:51:28 +02:00
|
|
|
var start = this._parent.date_helper.input_date.val();
|
2015-07-22 01:45:38 +02:00
|
|
|
this._parent.date_helper.set_value(this.options.value.end.valueOf ? new Date(this.options.value.end) : this.options.value.end);
|
2015-06-10 23:51:28 +02:00
|
|
|
var end = this._parent.date_helper.input_date.val();
|
|
|
|
|
|
|
|
var times = !this.options.value.multiday ?
|
|
|
|
'<span class="calendar_calEventLabel">'+this.egw().lang('Time')+'</span>:' + this._get_timespan(this.options.value) :
|
|
|
|
'<span class="calendar_calEventLabel">'+this.egw().lang('Start') + '</span>:' +start+
|
|
|
|
'<span class="calendar_calEventLabel">'+this.egw().lang('End') + '</span>:' + end
|
|
|
|
var cat = et2_createWidget('select-cat',{'readonly':true},this);
|
|
|
|
cat.set_value(this.options.value.category);
|
2015-11-17 21:56:47 +01:00
|
|
|
var cat_label = this.options.value.category.indexOf(',') <= 0 ? cat.span.text() : [];
|
|
|
|
if(typeof cat_label != 'string')
|
|
|
|
{
|
|
|
|
cat.span.children().each(function() {
|
|
|
|
cat_label.push($j(this).text());
|
|
|
|
});
|
|
|
|
cat_label = cat_label.join(', ');
|
|
|
|
}
|
2015-06-10 23:51:28 +02:00
|
|
|
cat.destroy();
|
|
|
|
|
2015-07-01 18:02:20 +02:00
|
|
|
return '<div class="calendar_calEventTooltip ' + this._status_class() + '" style="border-color: '+border+'; background: '+bg_color+';">'+
|
2015-11-12 19:22:48 +01:00
|
|
|
'<div class="calendar_calEventHeaderSmall" style="background-color: '+this.title.css('background-color')+';">'+
|
2015-06-10 23:51:28 +02:00
|
|
|
'<font style="color:'+header_color+'">'+this._get_timespan(this.options.value)+'</font>'+
|
|
|
|
this.icons[0].outerHTML+
|
|
|
|
'</div>'+
|
2015-07-15 18:29:10 +02:00
|
|
|
'<div class="calendar_calEventBodySmall" style="background-color: '+
|
2015-11-12 19:22:48 +01:00
|
|
|
jQuery.Color(this.title.css('background-color')).lightness("0.9") + '">'+
|
2015-06-10 23:51:28 +02:00
|
|
|
'<p style="margin: 0px;">'+
|
|
|
|
'<span class="calendar_calEventTitle">'+this.div.attr('data-title')+'</span><br>'+
|
|
|
|
this.options.value.description+'</p>'+
|
|
|
|
'<p style="margin: 2px 0px;">'+times+'</p>'+
|
|
|
|
(this.options.value.location ? '<p><span class="calendar_calEventLabel">'+this.egw().lang('Location') + '</span>:' + this.options.value.location+'</p>' : '')+
|
2015-11-17 21:56:47 +01:00
|
|
|
(cat_label ? '<p><span class="calendar_calEventLabel">'+this.egw().lang('Category') + '</span>:' + cat_label +'</p>' : '')+
|
2015-06-10 23:51:28 +02:00
|
|
|
'<p><span class="calendar_calEventLabel">'+this.egw().lang('Participants')+'</span>:<br />'+
|
|
|
|
(this.options.value.parts ? this.options.value.parts.replace("\n","<br />"):'')+'</p>'+
|
|
|
|
'</div>'+
|
|
|
|
'</div>';
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get actual icons from list
|
|
|
|
* @returns {undefined}
|
|
|
|
*/
|
|
|
|
_icons: function() {
|
|
|
|
var icons = [];
|
|
|
|
|
|
|
|
if(this.options.value.is_private)
|
|
|
|
{
|
|
|
|
icons.push('<img src="'+this.egw().image('private','calendar')+'"/>');
|
|
|
|
}
|
2015-07-01 18:02:20 +02:00
|
|
|
else
|
2015-06-10 23:51:28 +02:00
|
|
|
{
|
2015-07-01 18:02:20 +02:00
|
|
|
if(this.options.value.priority == 3)
|
|
|
|
{
|
|
|
|
icons.push('<img src="'+this.egw().image('high','calendar')+'" title="'+this.egw().lang('high priority')+'"/>');
|
|
|
|
}
|
|
|
|
if(this.options.value['recur_type'])
|
|
|
|
{
|
|
|
|
icons.push('<img src="'+this.egw().image('recur','calendar')+'" title="'+this.egw().lang('recurring event')+'"/>');
|
|
|
|
}
|
|
|
|
// icons for single user, multiple users or group(s) and resources
|
2015-07-03 19:56:36 +02:00
|
|
|
var single = '<img src="'+this.egw().image('single','calendar')+'" title="'+'"/>';
|
|
|
|
var multiple = '<img src="'+this.egw().image('users','calendar')+'" title="'+'"/>';
|
2015-07-01 18:02:20 +02:00
|
|
|
for(var uid in this.options.value['participants'])
|
|
|
|
{
|
|
|
|
if(Object.keys(this.options.value.participants).length == 1 && !isNaN(uid))
|
|
|
|
{
|
2015-07-03 19:56:36 +02:00
|
|
|
icons.push(single);
|
2015-07-01 18:02:20 +02:00
|
|
|
break;
|
|
|
|
}
|
2015-07-03 19:56:36 +02:00
|
|
|
if(!isNaN(uid) && icons.indexOf(multiple) === -1)
|
2015-07-01 18:02:20 +02:00
|
|
|
{
|
2015-07-03 19:56:36 +02:00
|
|
|
icons.push(multiple);
|
2015-07-01 18:02:20 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* TODO: resource icons
|
|
|
|
elseif(!isset($icons[$uid[0]]) && isset($this->bo->resources[$uid[0]]) && isset($this->bo->resources[$uid[0]]['icon']))
|
|
|
|
{
|
|
|
|
$icons[$uid[0]] = html::image($this->bo->resources[$uid[0]]['app'],
|
|
|
|
($this->bo->resources[$uid[0]]['icon'] ? $this->bo->resources[$uid[0]]['icon'] : 'navbar'),
|
|
|
|
lang($this->bo->resources[$uid[0]]['app']),
|
|
|
|
'width="16px" height="16px"');
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.options.value.non_blocking)
|
|
|
|
{
|
|
|
|
icons.push('<img src="'+this.egw().image('nonblocking','calendar')+'" title="'+this.egw().lang('non blocking')+'"/>');
|
|
|
|
}
|
|
|
|
if(this.options.value.alarm && !jQuery.isEmptyObject(this.options.value.alarm) && !this.options.value.is_private)
|
|
|
|
{
|
|
|
|
icons.push('<img src="'+this.egw().image('alarm','calendar')+'" title="'+this.egw().lang('alarm')+'"/>');
|
|
|
|
}
|
|
|
|
if(this.options.value.participants[egw.user('account_id')] && this.options.value.participants[egw.user('account_id')][0] == 'U')
|
|
|
|
{
|
2015-10-19 19:24:21 +02:00
|
|
|
icons.push('<img src="'+this.egw().image('needs-action','calendar')+'" title="'+this.egw().lang('Needs action')+'"/>');
|
2015-07-01 18:02:20 +02:00
|
|
|
}
|
2015-06-10 23:51:28 +02:00
|
|
|
}
|
|
|
|
return icons;
|
2015-05-06 21:03:45 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a text representation of the timespan of the event. Either start
|
|
|
|
* - end, or 'all day'
|
|
|
|
*
|
|
|
|
* @param {Object} event Event to get the timespan for
|
|
|
|
* @param {number} event.start_m Event start, in minutes from midnight
|
|
|
|
* @param {number} event.end_m Event end, in minutes from midnight
|
|
|
|
*
|
|
|
|
* @return {string} Timespan
|
|
|
|
*/
|
|
|
|
_get_timespan: function(event) {
|
|
|
|
var timespan = '';
|
|
|
|
if (event['start_m'] === 0 && event['end_m'] >= 24*60-1)
|
|
|
|
{
|
|
|
|
if (event['end_m'] > 24*60)
|
|
|
|
{
|
|
|
|
timespan = jQuery.datepicker.formatTime(
|
2015-11-17 21:19:47 +01:00
|
|
|
egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm",
|
2015-05-06 21:03:45 +02:00
|
|
|
{
|
|
|
|
hour: event.start_m / 60,
|
|
|
|
minute: event.start_m % 60,
|
|
|
|
seconds: 0,
|
|
|
|
timezone: 0
|
|
|
|
},
|
|
|
|
{"ampm": (egw.preference("timeformat") === "12")}
|
|
|
|
).trim()+' - '+jQuery.datepicker.formatTime(
|
2015-11-17 21:19:47 +01:00
|
|
|
egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm",
|
2015-05-06 21:03:45 +02:00
|
|
|
{
|
|
|
|
hour: event.end_m / 60,
|
|
|
|
minute: event.end_m % 60,
|
|
|
|
seconds: 0,
|
|
|
|
timezone: 0
|
|
|
|
},
|
|
|
|
{"ampm": (egw.preference("timeformat") === "12")}
|
|
|
|
).trim();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-10 19:35:24 +01:00
|
|
|
timespan = egw.lang('Whole day');
|
2015-05-06 21:03:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var duration = event.end_m - event.start_m;
|
|
|
|
if (event.end_m === 24*60-1) ++duration;
|
|
|
|
duration = Math.floor(duration/60) + this.egw().lang('h')+(duration%60 ? duration%60 : '');
|
|
|
|
|
|
|
|
timespan = jQuery.datepicker.formatTime(
|
2015-11-17 21:19:47 +01:00
|
|
|
egw.preference("timeformat") === "12" ? "h:mmtt" : "HH:mm",
|
2015-05-06 21:03:45 +02:00
|
|
|
{
|
|
|
|
hour: event.start_m / 60,
|
|
|
|
minute: event.start_m % 60,
|
|
|
|
seconds: 0,
|
|
|
|
timezone: 0
|
|
|
|
},
|
|
|
|
{"ampm": (egw.preference("timeformat") === "12")}
|
|
|
|
).trim();
|
|
|
|
|
|
|
|
timespan += ' ' + duration;
|
|
|
|
}
|
|
|
|
return timespan;
|
|
|
|
},
|
2015-11-17 17:57:34 +01:00
|
|
|
|
|
|
|
_sameday_check: function(event)
|
|
|
|
{
|
2015-11-17 22:32:46 +01:00
|
|
|
// Event somehow got orphaned, or deleted
|
|
|
|
if(!this._parent || event === null)
|
2015-11-17 17:57:34 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simple, same day
|
|
|
|
if(this.options.value.date && event.date == this.options.value.date)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Multi-day non-recurring event spans days - date does not match
|
|
|
|
var event_start = new Date(event.start);
|
|
|
|
var event_end = new Date(event.end);
|
|
|
|
if(this._parent.date >= event_start && this._parent.date <= event_end)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete all old actions
|
|
|
|
this._actionObject.clear();
|
|
|
|
this._actionObject.unregisterActions();
|
|
|
|
this._actionObject = null;
|
|
|
|
|
|
|
|
// Update daywise caches
|
|
|
|
var new_cache_id = app.classes.calendar._daywise_cache_id(event.date,this._parent.options.owner);
|
|
|
|
var old_cache_id = app.classes.calendar._daywise_cache_id(this.options.value.date,this._parent.options.owner);
|
|
|
|
var new_daywise = egw.dataGetUIDdata(new_cache_id);
|
|
|
|
var old_daywise = egw.dataGetUIDdata(old_cache_id);
|
|
|
|
new_daywise = new_daywise ? new_daywise.data : [];
|
|
|
|
old_daywise = old_daywise ? old_daywise.data : [];
|
|
|
|
if (new_daywise.indexOf(event.id) < 0)
|
|
|
|
{
|
|
|
|
new_daywise.push(event.id);
|
|
|
|
}
|
|
|
|
old_daywise.splice(old_daywise.indexOf(this.options.value.id),1);
|
|
|
|
egw.dataStoreUID(old_cache_id,old_daywise);
|
|
|
|
egw.dataStoreUID(new_cache_id,new_daywise);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
2015-05-06 21:03:45 +02:00
|
|
|
|
|
|
|
attachToDOM: function()
|
|
|
|
{
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
|
|
|
// Remove the binding for the click handler, unless there's something
|
|
|
|
// custom here.
|
|
|
|
if (!this.onclick)
|
|
|
|
{
|
|
|
|
$j(this.node).off("click");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Click handler calling custom handler set via onclick attribute to this.onclick.
|
|
|
|
* All other handling is done by the timegrid widget.
|
|
|
|
*
|
|
|
|
* @param {Event} _ev
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
click: function(_ev) {
|
|
|
|
var result = true;
|
|
|
|
if(typeof this.onclick == 'function')
|
|
|
|
{
|
|
|
|
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
|
|
|
|
var args = Array.prototype.slice.call(arguments);
|
|
|
|
if(args.indexOf(this) == -1) args.splice(1, 0, this);
|
|
|
|
|
|
|
|
result = this.onclick.apply(this, args);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
2015-06-10 23:51:28 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Show the recur prompt for this event
|
|
|
|
*
|
|
|
|
* @param {function} callback
|
|
|
|
*/
|
|
|
|
recur_prompt: function(callback)
|
2015-05-06 21:03:45 +02:00
|
|
|
{
|
2015-06-10 23:51:28 +02:00
|
|
|
et2_calendar_event.recur_prompt(this.options.value,callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Link the actions to the DOM nodes / widget bits.
|
|
|
|
*
|
|
|
|
* @param {object} actions {ID: {attributes..}+} map of egw action information
|
|
|
|
*/
|
|
|
|
_link_actions: function(actions)
|
|
|
|
{
|
2015-08-26 01:30:32 +02:00
|
|
|
if(!this._actionObject)
|
|
|
|
{
|
|
|
|
// Get the top level element - timegrid or so
|
|
|
|
var objectManager = this.getParent().getParent()._actionObject ||
|
|
|
|
egw_getAppObjectManager(true).getObjectById(this._parent._parent._parent.id) || egw_getAppObjectManager(true);
|
|
|
|
this._actionObject = objectManager.getObjectById('calendar::'+this.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._actionObject == null) {
|
2015-06-10 23:51:28 +02:00
|
|
|
// Add a new container to the object manager which will hold the widget
|
|
|
|
// objects
|
2015-08-26 01:30:32 +02:00
|
|
|
this._actionObject = objectManager.insertObject(false, new egwActionObject(
|
2015-06-10 23:51:28 +02:00
|
|
|
'calendar::'+this.id, objectManager, new et2_event_action_object_impl(this,this.getDOMNode()),
|
2015-08-11 17:35:54 +02:00
|
|
|
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager
|
2015-06-10 23:51:28 +02:00
|
|
|
));
|
2015-05-06 21:03:45 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-26 01:30:32 +02:00
|
|
|
this._actionObject.setAOI(new et2_event_action_object_impl(this, this.getDOMNode()));
|
2015-05-06 21:03:45 +02:00
|
|
|
}
|
2015-06-10 23:51:28 +02:00
|
|
|
|
|
|
|
// Delete all old objects
|
2015-08-26 01:30:32 +02:00
|
|
|
this._actionObject.clear();
|
|
|
|
this._actionObject.unregisterActions();
|
2015-06-10 23:51:28 +02:00
|
|
|
|
|
|
|
// Go over the widget & add links - this is where we decide which actions are
|
|
|
|
// 'allowed' for this widget at this time
|
|
|
|
var action_links = this._get_action_links(actions);
|
2015-08-12 00:30:50 +02:00
|
|
|
action_links.push('egw_link_drag');
|
|
|
|
action_links.push('egw_link_drop');
|
2015-08-26 01:30:32 +02:00
|
|
|
this._actionObject.updateActionLinks(action_links);
|
2015-05-06 21:03:45 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code for implementing et2_IDetachedDOM
|
|
|
|
*
|
|
|
|
* @param {array} _attrs array to add further attributes to
|
|
|
|
*/
|
|
|
|
getDetachedAttributes: function(_attrs) {
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
getDetachedNodes: function() {
|
|
|
|
return [this.getDOMNode()];
|
|
|
|
},
|
|
|
|
|
|
|
|
setDetachedAttributes: function(_nodes, _values) {
|
|
|
|
|
|
|
|
},
|
|
|
|
});
|
2015-06-10 23:51:28 +02:00
|
|
|
et2_register_widget(et2_calendar_event, ["calendar-event"]);
|
|
|
|
|
|
|
|
// Static class stuff
|
|
|
|
/**
|
|
|
|
* Recur prompt
|
|
|
|
* If the event is recurring, asks the user if they want to edit the event as
|
|
|
|
* an exception, or change the whole series. Then the callback is called.
|
|
|
|
*
|
|
|
|
* @param {Object} event_data - Event information
|
|
|
|
* @param {string} event_data.id - Unique ID for the event, possibly with a timestamp
|
|
|
|
* @param {string|Date} event_data.start - Start date/time for the event
|
|
|
|
* @param {number} event_data.recur_type - Recur type, or 0 for a non-recurring event
|
|
|
|
* @param {Function} [callback] - Callback is called with the button (exception, series, single or cancel) and the event data.
|
|
|
|
*
|
|
|
|
* @augments {et2_calendar_event}
|
|
|
|
*/
|
|
|
|
et2_calendar_event.recur_prompt = function(event_data, callback)
|
|
|
|
{
|
|
|
|
var edit_id = event_data.id;
|
|
|
|
var edit_date = event_data.start;
|
|
|
|
var egw = this.egw ? (typeof this.egw == 'function' ? this.egw() : this.egw) : (window.opener || window).egw;
|
|
|
|
var that = this;
|
|
|
|
|
|
|
|
if(typeof callback != 'function')
|
|
|
|
{
|
|
|
|
callback = function(_button_id)
|
|
|
|
{
|
|
|
|
switch(_button_id)
|
|
|
|
{
|
|
|
|
case 'exception':
|
|
|
|
egw.open(edit_id, event_data.app||'calendar', 'edit', {date:edit_date,exception: '1'});
|
|
|
|
break;
|
|
|
|
case 'series':
|
|
|
|
case 'single':
|
|
|
|
egw.open(edit_id, event_data.app||'calendar', 'edit', {date:edit_date});
|
|
|
|
break;
|
|
|
|
case 'cancel':
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2015-07-01 01:34:38 +02:00
|
|
|
if(parseInt(event_data.recur_type))
|
2015-06-10 23:51:28 +02:00
|
|
|
{
|
|
|
|
var buttons = [
|
|
|
|
{text: egw.lang("Edit exception"), id: "exception", class: "ui-priority-primary", "default": true},
|
|
|
|
{text: egw.lang("Edit series"), id:"series"},
|
|
|
|
{text: egw.lang("Cancel"), id:"cancel"}
|
|
|
|
];
|
|
|
|
et2_dialog.show_dialog(
|
|
|
|
function(button_id) {callback.call(that, button_id, event_data);},
|
|
|
|
(!event_data.is_private ? event_data['title'] : egw.lang('private')) + "\n" +
|
|
|
|
egw.lang("Do you want to edit this event as an exception or the whole series?"),
|
|
|
|
egw.lang("This event is part of a series"), {}, buttons, et2_dialog.QUESTION_MESSAGE
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
callback.call(this,'single',event_data);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
et2_calendar_event.drag_helper = function(event,ui) {
|
|
|
|
ui.helper.width(ui.width());
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* splits the combined status, quantity and role
|
|
|
|
*
|
|
|
|
* @param {string} status - combined value, O: status letter: U, T, A, R
|
|
|
|
* @param {int} [quantity] - quantity
|
|
|
|
* @param {string} [role]
|
|
|
|
* @return string status U, T, A or R, same as $status parameter on return
|
|
|
|
*/
|
|
|
|
et2_calendar_event.split_status = function(status,quantity,role)
|
|
|
|
{
|
|
|
|
quantity = 1;
|
|
|
|
role = 'REQ-PARTICIPANT';
|
|
|
|
//error_log(__METHOD__.__LINE__.array2string($status));
|
|
|
|
var matches = null;
|
|
|
|
if (typeof status === 'string' && status.length > 1)
|
|
|
|
{
|
|
|
|
matches = status.match(/^.([0-9]*)(.*)$/gi);
|
|
|
|
}
|
|
|
|
if(matches)
|
|
|
|
{
|
|
|
|
if (parseInt(matches[1]) > 0) quantity = parseInt(matches[1]);
|
|
|
|
if (matches[2]) role = matches[2];
|
|
|
|
status = status[0];
|
|
|
|
}
|
|
|
|
else if (status === true)
|
|
|
|
{
|
|
|
|
status = 'U';
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The egw_action system requires an egwActionObjectInterface Interface implementation
|
|
|
|
* to tie actions to DOM nodes. This one can be used by any widget.
|
|
|
|
*
|
|
|
|
* The class extension is different than the widgets
|
|
|
|
*
|
|
|
|
* @param {et2_DOMWidget} widget
|
|
|
|
* @param {Object} node
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function et2_event_action_object_impl(widget, node)
|
|
|
|
{
|
|
|
|
var aoi = new et2_action_object_impl(widget, node);
|
|
|
|
|
|
|
|
// _outerCall may be used to determine, whether the state change has been
|
|
|
|
// evoked from the outside and the stateChangeCallback has to be called
|
|
|
|
// or not.
|
|
|
|
aoi.doSetState = function(_state, _outerCall) {
|
|
|
|
};
|
|
|
|
|
|
|
|
return aoi;
|
|
|
|
};
|