/* * 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: { "value": { type: "any", default: et2_no_init }, "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); var event = this; // Main container this.div = $j(document.createElement("div")) .addClass("calendar_calEvent") .addClass(this.options.class) .css('width',this.options.width) .on('mouseenter', function() { // Hacky to remove egw's tooltip border and let the mouse in window.setTimeout(function() { $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()); }); }); }); },105); }); this.title = $j(document.createElement('div')) .addClass("calendar_calEventHeader") .appendTo(this.div); this.body = $j(document.createElement('div')) .addClass("calendar_calEventBody") .appendTo(this.div); this.icons = $j(document.createElement('div')) .addClass("calendar_calEventIcons") .appendTo(this.title); 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); 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; $j('body.egw_tooltip').remove(); // Unregister, or we'll continue to be notified... if(this.options.value) { var old_app_id = this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : ''); egw.dataUnregisterUID('calendar::'+old_app_id,false,this); } }, set_value: function(_value) { // Un-register for updates if(this.options.value) { var old_app_id = this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : ''); egw.dataUnregisterUID('calendar::'+old_app_id,false,this); } this.options.value = _value; // Register for updates var app_id = this.options.value.id + (this.options.value.recur_type ? ':'+this.options.value.recur_date : ''); egw.dataRegisterUID('calendar::'+app_id, function(event) { // Check for changing days in the grid view if(!this._sameday_check(event)) { // This should now cease to exist, as new events have been created this.free(); return; } // Copy to avoid changes, which may cause nm problems this.options.value = jQuery.extend({},event); this.options.value.date = this._parent.options.date; // Let parent position this._parent.position_event(this); // Parent may remove this if the date isn't the same if(this._parent) { this._update(this.options.value); } },this,this.getInstanceManager().execId,this.id); if(_value && !egw.dataHasUID('calendar::'+app_id)) { egw.dataStoreUID('calendar::'+app_id, _value); } }, _update: function(event) { // Copy new information this.options.value = event; var eventId = event.id.match(/-?\d+\.?\d*/g)[0]; var appName = event.id.replace(/-?\d+\.?\d*/g,''); var app_id = event.app_id ? event.app_id : event.id + (event.recur_type ? ':'+event.recur_date : ''); this._parent.date_helper.set_value(event.start.valueOf ? new Date(event.start) : event.start); var formatted_start = this._parent.date_helper.getValue(); this.set_id('event_' + (eventId || event.id)); // 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 this.div // Empty & re-append to make sure dnd helpers are gone .empty() .append(this.title) .append(this.body) // Let timegrid always get the drag .droppable('option','greedy',false) // ? .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) .attr('data-start', formatted_start) .attr('data-owner', event.owner) .attr('data-recur_type', event.recur_type) .attr('data-resize', event.whole_day ? 'WD' : '' + (event.recur_type ? 'S':'')) // Remove any category classes .removeClass(function(index, css) { return (css.match (/(^|\s)cat_\S+/g) || []).join(' '); }) // Remove any status classes .removeClass(function(index, css) { return (css.match(/calendar_calEvent\S+/g) || []).join(' '); }) // Remove any resize classes, the handles are gone due to empty() .removeClass('ui-resizable') .addClass(event.class) .toggleClass('calendar_calEventPrivate', typeof event.private !== 'undefined' && event.private); this.options.class = event.class; var status_class = this._status_class(); // Add category classes, if real categories are set if(event.category && event.category != '0') { var cats = event.category.split(','); for(var i = 0; i < cats.length; i++) { this.div.addClass('cat_' + cats[i]); } } this.div.toggleClass('calendar_calEventUnknown', event.participants[egw.user('account_id')] ? event.participants[egw.user('account_id')][0] === 'U' : false); this.div.addClass(status_class); 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'); // 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; this.div.attr('data-title', title); this.title.text(small_height ? title : this._get_timespan(event)); // 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()) { // Most statuses use colored borders this.div.css('border-color',status_class === 'calendar_calEventAllAccepted' ? this.div.css('background-color') : ''); // Set title color based on background brightness this.title .css('background-color', this.div.css('background-color')) .css('color', jQuery.Color(this.div.css('background-color')).lightness() > 0.45 ? 'black':'white'); } this.icons.appendTo(this.title) .html(this._icons()); // Body if(event.whole_day_on_top) { this.body.html(title); } else { this.body .html(''+title+'') .append('
'+this.options.value.description+'
'); } this.body // Set background color to a lighter version of the header color .css('background-color',jQuery.Color(this.title.css('background-color')).lightness( Math.max(0.8, parseFloat(jQuery.Color(this.title.css('background-color')).lightness())) )); this.set_statustext(this._tooltip()); }, /** * Examines the participants & returns CSS classname for status * * @returns {String} */ _status_class: function() { 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'; return status_class; // break for default: status_class = 'calendar_calEventAllAnswered'; break; } } return status_class; }, _tooltip: function() { if(!this.div) return ''; var border = this.div.css('borderTopColor'); var bg_color = this.div.css('background-color'); var header_color = this.title.css('color'); this._parent.date_helper.set_value(this.options.value.start.valueOf ? new Date(this.options.value.start) : this.options.value.start); var start = this._parent.date_helper.input_date.val(); this._parent.date_helper.set_value(this.options.value.end.valueOf ? new Date(this.options.value.end) : this.options.value.end); var end = this._parent.date_helper.input_date.val(); var times = !this.options.value.multiday ? ''+this.egw().lang('Time')+':' + this._get_timespan(this.options.value) : ''+this.egw().lang('Start') + ':' +start+ ''+this.egw().lang('End') + ':' + end var cat = et2_createWidget('select-cat',{'readonly':true},this); cat.set_value(this.options.value.category); 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(', '); } cat.destroy(); return ''+
''+this.div.attr('data-title')+'
'+
this.options.value.description+'
'+times+'
'+ (this.options.value.location ? ''+this.egw().lang('Location') + ':' + this.options.value.location+'
' : '')+ (cat_label ? ''+this.egw().lang('Category') + ':' + cat_label +'
' : '')+ ''+this.egw().lang('Participants')+':
'+
(this.options.value.parts ? this.options.value.parts.replace("\n","
"):'')+'