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" ;
/ * e g w : u s e s
/ e t e m p l a t e / j s / e t 2 _ c o r e _ v a l u e W i d g e t ;
* /
/ * *
* 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 ) ;
}
2016-01-05 21:43:19 +01:00
if ( this . options . value && this . options . value . row _id )
{
egw . dataRegisterUID (
'calendar::' + this . options . value . row _id ,
this . _UID _callback ,
this ,
this . getInstanceManager ( ) . execId ,
this . id
) ;
}
2015-05-06 21:03:45 +02:00
} ,
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-25 18:20:30 +01:00
var old _app _id = this . options . value . app _id ;
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 )
{
2016-01-05 21:43:19 +01:00
var old _id = this . options . value . row _id ;
if ( ! _value || ! _value . row _id || old _id !== _value . row _id )
{
egw . dataUnregisterUID ( 'calendar::' + old _id , false , this ) ;
}
2015-06-10 23:51:28 +02:00
}
2015-05-06 21:03:45 +02:00
this . options . value = _value ;
2015-06-10 23:51:28 +02:00
// Register for updates
2016-01-05 21:43:19 +01:00
var id = this . options . value . row _id ;
if ( ! old _id || old _id !== id )
{
egw . dataRegisterUID ( 'calendar::' + id , this . _UID _callback , this , this . getInstanceManager ( ) . execId , this . id ) ;
}
if ( _value && ! egw . dataHasUID ( 'calendar::' + id ) )
{
egw . dataStoreUID ( 'calendar::' + id , _value ) ;
}
} ,
_UID _callback : function _UID _callback ( event ) {
// Make sure id is a string
this . _values _check ( event ) ;
2015-07-03 19:56:36 +02:00
2016-01-05 21:43:19 +01:00
// 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 ;
}
2015-07-22 01:45:38 +02:00
2016-01-05 21:43:19 +01:00
// Copy to avoid changes, which may cause nm problems
this . options . value = jQuery . extend ( { } , event ) ;
if ( this . _parent . options . date )
{
this . options . value . date = this . _parent . options . date ;
}
2015-06-10 23:51:28 +02:00
2016-01-05 21:43:19 +01:00
// Let parent position
this . _parent . position _event ( this ) ;
2015-06-10 23:51:28 +02:00
2016-01-05 21:43:19 +01:00
// Parent may remove this if the date isn't the same
if ( this . _parent )
2015-06-10 23:51:28 +02:00
{
2016-01-05 21:43:19 +01:00
// This gives some slight speed enhancements over doing it immediately,
// but it looks weird
/ *
window . setTimeout ( jQuery . proxy ( function ( ) {
if ( this . options ) this . _update ( this . options . value ) ;
} , this ) , 100 ) ;
* /
this . _update ( this . options . 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 ;
2016-01-05 21:43:19 +01:00
var id = event . row _id ? event . row _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
2016-01-05 21:43:19 +01:00
this . set _id ( 'event_' + id ) ;
2015-12-07 19:32:59 +01:00
if ( this . _actionObject )
{
2016-01-05 21:43:19 +01:00
this . _actionObject . id = 'calendar::' + id ;
2015-12-07 19:32:59 +01:00
}
2016-01-05 21:43:19 +01:00
// Copy actions set in parent
this . _link _actions ( this . _parent . _parent . _parent . options . actions || { } ) ;
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
2015-11-30 18:21:40 +01:00
. attr ( 'data-id' , event . id )
. attr ( 'data-app' , event . app || 'calendar' )
2016-01-05 21:43:19 +01:00
. attr ( 'data-app_id' , event . 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 = [ ] ;
2015-11-24 00:32:45 +01:00
2015-06-10 23:51:28 +02:00
if ( this . options . value . is _private )
{
2015-11-24 00:32:45 +01:00
// Hide everything
2015-06-10 23:51:28 +02:00
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-11-24 00:32:45 +01:00
if ( this . options . value . app !== 'calendar' )
{
icons . push ( '<img src="' + this . egw ( ) . image ( 'navbar' , this . options . value . app ) + '" title="' + this . egw ( ) . lang ( this . options . value . app ) + '"/>' ) ;
}
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' ) + '"/>' ) ;
}
2015-11-24 00:32:45 +01:00
if ( this . options . value . public == '0' )
{
// Show private flag
icons . push ( '<img src="' + this . egw ( ) . image ( 'private' , 'calendar' ) + '"/>' ) ;
}
2015-07-01 18:02:20 +02:00
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
{
2015-12-29 23:12:30 +01:00
var duration = event . multiday ?
( event . end - event . start ) / 60000 :
( event . end _m - event . start _m ) ;
2015-05-06 21:03:45 +02:00
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
2015-12-29 23:12:30 +01:00
/ * *
* Make sure event data has all proper values , and format them as expected
* @ param { Object } event
* /
_values _check : function _values _check ( event )
{
// Make sure ID is a string
if ( event . id )
{
event . id = '' + event . id ;
}
// Use dates as objects
if ( typeof event . start !== 'object' )
{
this . _parent . date _helper . set _value ( event . start ) ;
event . start = new Date ( this . _parent . date _helper . getValue ( ) ) ;
}
if ( typeof event . end !== 'object' )
{
this . _parent . date _helper . set _value ( event . end ) ;
event . end = new Date ( this . _parent . date _helper . getValue ( ) ) ;
}
// We need minutes for durations
if ( typeof event . start _m === 'undefined' )
{
event . start _m = event . start . getUTCHours ( ) * 60 + event . start . getUTCMinutes ( ) ;
event . end _m = event . end . getUTCHours ( ) * 60 + event . end . getUTCMinutes ( ) ;
}
if ( typeof event . multiday === 'undefined' )
{
event . multiday = ( event . start . getUTCFullYear ( ) !== event . end . getUTCFullYear ( ) ||
event . start . getUTCMonth ( ) !== event . end . getUTCMonth ( ) ||
event . start . getUTCDate ( ) != event . end . getUTCDate ( ) ) ;
}
if ( ! event . start . getUTCHours ( ) && ! event . start . getUTCMinutes ( ) && event . end . getUTCHours ( ) == 23 && event . end . getUTCMinutes ( ) == 59 )
{
event . whole _day _on _top = ( event . non _blocking && event . non _blocking != '0' ) ;
}
} ,
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 new _daywise = egw . dataGetUIDdata ( new _cache _id ) ;
new _daywise = new _daywise ? new _daywise . data : [ ] ;
2015-12-29 23:12:30 +01:00
var old _cache _id = false ;
if ( this . options . value && this . options . value . date )
{
old _cache _id = app . classes . calendar . _daywise _cache _id ( this . options . value . date , this . _parent . options . owner ) ;
var old _daywise = egw . dataGetUIDdata ( old _cache _id ) ;
old _daywise = old _daywise ? old _daywise . data : [ ] ;
old _daywise . splice ( old _daywise . indexOf ( this . options . value . id ) , 1 ) ;
egw . dataStoreUID ( old _cache _id , old _daywise ) ;
}
2015-11-17 17:57:34 +01:00
if ( new _daywise . indexOf ( event . id ) < 0 )
{
new _daywise . push ( event . id ) ;
}
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
2015-12-07 19:32:59 +01:00
* @ param { Object } [ extra _data ]
2015-06-10 23:51:28 +02:00
* /
2015-12-07 19:32:59 +01:00
recur _prompt : function ( callback , extra _data )
2015-05-06 21:03:45 +02:00
{
2015-12-07 19:32:59 +01:00
et2 _calendar _event . recur _prompt ( this . options . value , callback , extra _data ) ;
2015-06-10 23:51:28 +02:00
} ,
2015-12-04 18:37:26 +01:00
/ * *
* Show the series split prompt for this event
*
* @ param { function } callback
* /
series _split _prompt : function ( callback )
{
et2 _calendar _event . series _split _prompt ( this . options . value , this . options . value . recur _date , callback ) ;
} ,
2015-06-10 23:51:28 +02:00
/ * *
* 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 .
2015-12-07 19:32:59 +01:00
* @ param { Object } [ extra _data ] - Additional data passed to the callback , used as extra parameters for default callback
2015-06-10 23:51:28 +02:00
*
* @ augments { et2 _calendar _event }
* /
2015-12-07 19:32:59 +01:00
et2 _calendar _event . recur _prompt = function ( event _data , callback , extra _data )
2015-06-10 23:51:28 +02:00
{
2015-12-01 23:02:47 +01:00
var edit _id = event _data . app _id ;
2015-06-10 23:51:28 +02:00
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 ;
2015-12-07 19:32:59 +01:00
var extra _params = extra _data && typeof extra _data == 'object' ? extra _data : { } ;
2015-12-11 20:52:52 +01:00
extra _params . date = edit _date . toJSON ? edit _date . toJSON ( ) : edit _date ;
2015-06-10 23:51:28 +02:00
if ( typeof callback != 'function' )
{
callback = function ( _button _id )
{
switch ( _button _id )
{
case 'exception' :
2015-12-07 19:32:59 +01:00
extra _params . exception = '1' ;
egw . open ( edit _id , event _data . app || 'calendar' , 'edit' , extra _params ) ;
2015-06-10 23:51:28 +02:00
break ;
case 'series' :
case 'single' :
2015-12-07 19:32:59 +01:00
egw . open ( edit _id , event _data . app || 'calendar' , 'edit' , extra _params ) ;
2015-06-10 23:51:28 +02:00
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 ) ;
}
} ;
2015-12-04 18:37:26 +01:00
/ * *
* Split series prompt
*
* If the event is recurring and the user adjusts the time or duration , we may need
* to split the series , ending the current one and creating a new one with the changes .
* This prompts the user if they really want to do that .
*
* @ param { Object } event _data - Event information
* @ param { string } event _data . id - Unique ID for the event , possibly with a timestamp
* @ param { string | Date } instance _date - The date of the edited instance of the event
* @ param { Function } [ callback ] - Callback is called with the button ( ok or cancel ) and the event data .
*
* @ augments { et2 _calendar _event }
* /
et2 _calendar _event . series _split _prompt = function ( event _data , instance _date , callback )
{
var egw = this . egw ? ( typeof this . egw == 'function' ? this . egw ( ) : this . egw ) : ( window . opener || window ) . egw ;
var that = this ;
if ( typeof instance _date == 'string' )
{
instance _date = new Date ( instance _date ) ;
}
// Check for modifying a series that started before today
var tempDate = new Date ( ) ;
var today = new Date ( tempDate . getFullYear ( ) , tempDate . getMonth ( ) , tempDate . getDate ( ) , tempDate . getHours ( ) , - tempDate . getTimezoneOffset ( ) , tempDate . getSeconds ( ) ) ;
var termination _date = instance _date < today ? egw . lang ( 'today' ) : date ( egw . preference ( 'dateformat' ) , instance _date ) ;
if ( parseInt ( event _data . recur _type ) )
{
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 really want to change the start of this series? If you do, the original series will be terminated as of %1 and a new series for the future reflecting your changes will be created." , termination _date ) ,
egw . lang ( "This event is part of a series" ) , { } , et2 _dialog . BUTTONS _OK _CANCEL , et2 _dialog . WARNING _MESSAGE
) ;
}
} ;
2015-06-10 23:51:28 +02:00
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 ;
} ;