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$
* /
/ * 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 ;
* /
/ * *
2016-01-13 23:07:09 +01:00
* Class for a single event , displayed in either the timegrid or planner view
*
* It is possible to directly provide all information directly , but calendar
* uses egw . data for caching , so ID is all that is needed .
*
* Note that there are several pieces of information that have 'ID' in them :
* - row _id - used by both et2 _calendar _event and the nextmatch to uniquely
* identify a particular entry or entry ocurrence
* - id - Recurring events may have their recurrence as a timestamp after their ID ,
* such as '194:1453318200' , or not . It ' s usually ( always ? ) the same as row ID .
* - app _id - the ID according to the source application . For calendar , this
* is the same as ID ( but always with the recurrence ) , for other apps this is
* usually just an integer . With app _id and app , you should be able to call
* egw . open ( ) and get the specific entry .
* - Events from other apps will have their app name prepended to their ID , such
* as 'infolog123' , so app _id and id will be different for these events
* - Cache ID is the same as other apps , and looks like 'calendar::<row_id>'
* - The DOM ID for the containing div is event _ < row _id >
*
* Events are expected to be added to either et2 _calendar _daycol or
* et2 _calendar _planner _row rather than either et2 _calendar _timegrid or
* et2 _calendar _planner directly .
2015-05-06 21:03:45 +02:00
*
*
* @ augments et2 _valueWidget
* /
2016-03-01 17:27:45 +01:00
var et2 _calendar _event = ( function ( ) { "use strict" ; return et2 _valueWidget . extend ( [ et2 _IDetachedDOM ] ,
2015-05-06 21:03:45 +02:00
{
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 ;
2016-03-01 17:27:45 +01:00
2015-05-06 21:03:45 +02:00
// Main container
2016-06-02 16:51:15 +02:00
this . div = jQuery ( document . createElement ( "div" ) )
2015-05-06 21:03:45 +02:00
. 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 ( ) {
2017-02-24 16:31:39 +01:00
// Bind actions on first mouseover for faster creation
if ( event . _need _actions _linked )
{
event . _copy _parent _actions ( ) ;
}
2016-01-16 01:38:04 +01:00
// Tooltip
if ( ! event . _tooltipElem )
{
2017-09-05 19:04:45 +02:00
event . options . statustext _html = true ;
2016-01-16 01:38:04 +01:00
event . set _statustext ( event . _tooltip ( ) ) ;
return event . div . trigger ( 'mouseenter' ) ;
}
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 ( ) {
2016-06-02 16:51:15 +02:00
jQuery ( 'body .egw_tooltip' )
2015-11-13 01:53:23 +01:00
. css ( 'border' , 'none' )
. on ( 'mouseenter' , function ( ) {
event . div . off ( 'mouseleave.tooltip' ) ;
2016-06-02 16:51:15 +02:00
jQuery ( 'body.egw_tooltip' ) . remove ( ) ;
jQuery ( 'body' ) . append ( this ) ;
jQuery ( this ) . stop ( true ) . fadeTo ( 400 , 1 )
2015-11-13 01:53:23 +01:00
. on ( 'mouseleave' , function ( ) {
2016-06-02 16:51:15 +02:00
jQuery ( this ) . fadeOut ( '400' , function ( ) {
jQuery ( this ) . remove ( ) ;
2015-11-13 01:53:23 +01:00
// Set up to work again
event . set _statustext ( event . _tooltip ( ) ) ;
} ) ;
} ) ;
} ) ;
2015-08-19 02:08:22 +02:00
} , 105 ) ;
} ) ;
2016-06-02 16:51:15 +02:00
this . title = jQuery ( document . createElement ( 'div' ) )
2015-05-06 21:03:45 +02:00
. addClass ( "calendar_calEventHeader" )
. appendTo ( this . div ) ;
2016-06-02 16:51:15 +02:00
this . body = jQuery ( document . createElement ( 'div' ) )
2015-05-06 21:03:45 +02:00
. addClass ( "calendar_calEventBody" )
. appendTo ( this . div ) ;
2016-06-02 16:51:15 +02:00
this . icons = jQuery ( document . createElement ( 'div' ) )
2015-06-10 23:51:28 +02:00
. addClass ( "calendar_calEventIcons" )
. appendTo ( this . title ) ;
2015-05-06 21:03:45 +02:00
this . setDOMNode ( this . div [ 0 ] ) ;
2017-02-24 16:31:39 +01:00
this . _need _actions _linked = false ;
2015-05-06 21:03:45 +02:00
} ,
doLoadingFinished : function ( ) {
this . _super . apply ( this , arguments ) ;
2016-01-13 23:07:09 +01:00
// Already know what is needed to hook to cache
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 ,
2016-01-13 23:07:09 +01:00
this . _UID _callback ,
2016-01-05 21:43:19 +01:00
this ,
this . getInstanceManager ( ) . execId ,
this . id
) ;
}
2016-01-15 21:22:55 +01:00
return true ;
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 ;
}
2016-03-01 17:27:45 +01:00
2015-08-26 01:30:32 +02:00
this . div . off ( ) ;
this . title . remove ( ) ;
this . title = null ;
this . body . remove ( ) ;
this . body = null ;
this . icons = null ;
this . div . remove ( ) ;
this . div = null ;
2016-06-02 16:51:15 +02:00
jQuery ( 'body.egw_tooltip' ) . remove ( ) ;
2016-03-01 17:27:45 +01: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 )
{
2016-01-13 23:07:09 +01:00
var old _app _id = this . options . value . row _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 ) ;
}
} ,
2016-01-13 23:07:09 +01:00
/ * *
* Callback for changes in cached data
* /
2016-01-05 21:43:19 +01:00
_UID _callback : function _UID _callback ( event ) {
2016-05-02 22:24:04 +02:00
// Copy to avoid changes, which may cause nm problems
2016-06-21 19:42:44 +02:00
var value = event === null ? null : jQuery . extend ( { } , event ) ;
2018-02-26 19:31:11 +01:00
var parent = this . _parent ;
2016-05-02 22:24:04 +02:00
2016-01-13 23:07:09 +01:00
// Make sure id is a string, check values
2016-05-02 22:24:04 +02:00
if ( value )
2016-01-13 19:55:23 +01:00
{
2016-05-02 22:24:04 +02:00
this . _values _check ( value ) ;
2016-01-13 19:55:23 +01:00
}
2015-07-03 19:56:36 +02:00
2016-01-05 21:43:19 +01:00
// Check for changing days in the grid view
2016-05-02 22:24:04 +02:00
if ( ! this . _sameday _check ( value ) )
2016-01-05 21:43:19 +01:00
{
2016-08-30 16:30:48 +02:00
// May need to update parent to remove out-of-view events
2018-02-26 19:31:11 +01:00
parent . removeChild ( this ) ;
2016-08-30 16:30:48 +02:00
if ( event === null && parent && parent . _out _of _view )
{
parent . _out _of _view ( ) ;
}
2017-09-05 19:04:45 +02:00
2016-01-05 21:43:19 +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
2016-01-05 21:43:19 +01:00
// Copy to avoid changes, which may cause nm problems
2016-05-02 22:24:04 +02:00
this . options . value = jQuery . extend ( { } , value ) ;
2016-01-05 21:43:19 +01:00
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-15 17:58:59 +01:00
this . _update ( ) ;
2015-06-10 23:51:28 +02:00
}
2015-05-06 21:03:45 +02:00
} ,
2016-01-13 23:07:09 +01:00
/ * *
* Draw the event
* /
2016-01-15 00:03:53 +01:00
_update : function ( ) {
2015-06-10 23:51:28 +02:00
2016-01-15 00:03:53 +01:00
// Update to reflect new information
var event = this . options . value ;
2016-03-01 17:27:45 +01:00
2016-01-05 21:43:19 +01:00
var id = event . row _id ? event . row _id : event . id + ( event . recur _type ? ':' + event . recur _date : '' ) ;
2016-01-16 01:38:04 +01:00
var formatted _start = event . start . toJSON ( ) ;
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
2018-03-05 23:07:37 +01:00
this . _need _actions _linked = ! this . options . readonly ;
2016-01-05 21:43:19 +01: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' } ) ;
2016-01-19 19:03:42 +01:00
// Need cleaning? (DnD helper removes content)
if ( ! this . div . has ( this . title ) . length )
{
this . div
. empty ( )
. append ( this . title )
. append ( this . body ) ;
}
2016-05-16 21:53:35 +02:00
if ( ! this . _parent . options . readonly && ! this . options . readonly && this . div . droppable ( 'instance' ) )
2016-03-18 16:49:11 +01:00
{
this . div
// Let timegrid always get the drag
. droppable ( 'option' , 'greedy' , false ) ;
}
2015-11-10 00:06:17 +01:00
// DOM nodes
2015-05-06 21:03:45 +02:00
this . div
2016-01-13 00:55:59 +01:00
// Set full day flag
2016-01-20 23:27:41 +01:00
. attr ( 'data-full_day' , event . whole _day )
2016-03-01 17:27:45 +01:00
2015-05-06 21:03:45 +02:00
// 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' : '' ) )
2017-03-09 21:28:09 +01:00
. attr ( 'data-priority' , event . priority )
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 ( ' ' ) ;
} )
2016-01-28 22:35:22 +01:00
. removeClass ( 'calendar_calEventSmall' )
2015-07-01 18:02:20 +02:00
. 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 . body . toggleClass ( 'calendar_calEventBodySmall' , event . whole _day _on _top || false ) ;
2016-03-01 17:27:45 +01:00
2015-05-06 21:03:45 +02:00
// Header
2017-03-01 18:52:38 +01:00
var title = ! event . is _private ? egw . htmlspecialchars ( event [ 'title' ] ) : egw . lang ( 'private' ) ;
2015-05-06 21:03:45 +02:00
2016-01-28 22:35:22 +01:00
this . title
. html ( '<span class="calendar_calTimespan">' + this . _get _timespan ( event ) + '<br /></span>' )
. append ( '<span class="calendar_calEventTitle">' + title + '</span>' )
2016-03-01 17:27:45 +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
2017-11-22 19:54:31 +01:00
this . div . css ( 'border-color' , this . div . css ( 'background-color' ) ) ;
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 ( ) ) ;
2016-03-01 17:27:45 +01:00
2015-05-06 21:03:45 +02:00
// Body
if ( event . whole _day _on _top )
{
this . body . html ( title ) ;
}
else
{
2016-01-14 22:24:01 +01:00
var start _time = jQuery . datepicker . formatTime (
egw . preference ( "timeformat" ) === "12" ? "h:mmtt" : "HH:mm" ,
{
hour : event . start _m / 60 ,
minute : event . start _m % 60 ,
seconds : 0 ,
timezone : 0
} ,
{ "ampm" : ( egw . preference ( "timeformat" ) === "12" ) }
) . trim ( ) ;
2015-11-11 19:48:41 +01:00
this . body
. html ( '<span class="calendar_calEventTitle">' + title + '</span>' )
2016-02-02 22:12:44 +01:00
. append ( '<span class="calendar_calTimespan">' + start _time + '</span>' ) ;
if ( this . options . value . description . trim ( ) )
{
this . body
2017-03-01 18:52:38 +01:00
. append ( '<p>' + egw . htmlspecialchars ( this . options . value . description ) + '</p>' ) ;
2016-02-02 22:12:44 +01:00
}
2015-05-06 21:03:45 +02:00
}
2016-02-03 17:52:28 +01:00
2016-02-04 20:30:32 +01:00
// Clear tooltip for regeneration
this . set _statustext ( '' ) ;
2016-02-03 17:52:28 +01:00
// Height specific section
2016-02-23 22:32:43 +01:00
// This can take an unreasonable amount of time if parent is hidden
if ( this . _parent . div . is ( ':visible' ) )
{
this . _small _size ( ) ;
}
2015-06-10 23:51:28 +02:00
} ,
2016-01-28 22:35:22 +01:00
/ * *
* Calculate display variants for when event is too short for full display
2016-03-01 17:27:45 +01:00
*
2016-01-28 22:35:22 +01:00
* Display is based on the number of visible lines , calculated off the header
* height :
* 1 - show just the event title , with ellipsis
* 2 - Show timespan and title , with ellipsis
* > 4 - Show description as well , truncated to fit
* /
_small _size : function ( ) {
2016-02-03 18:06:26 +01:00
if ( this . options . value . whole _day _on _top ) return ;
2017-02-24 16:31:39 +01:00
// Skip for planner view, it's always small
if ( this . _parent && this . _parent . instanceOf ( et2 _calendar _planner _row ) ) return ;
2016-02-02 22:12:44 +01:00
// Pre-calculation reset
2016-01-28 22:35:22 +01:00
this . div . removeClass ( 'calendar_calEventSmall' ) ;
2016-02-02 22:12:44 +01:00
this . body . css ( 'height' , 'auto' ) ;
var line _height = parseFloat ( this . div . css ( 'line-height' ) ) ;
var visible _lines = Math . floor ( this . div . innerHeight ( ) / line _height ) ;
2016-01-28 22:35:22 +01:00
if ( ! this . title . height ( ) )
{
// Handle sizing while hidden, such as when calendar is not the active tab
visible _lines = Math . floor ( egw . getHiddenDimensions ( this . div ) . h / egw . getHiddenDimensions ( this . title ) . h ) ;
}
2016-02-02 22:12:44 +01:00
visible _lines = Math . max ( 1 , visible _lines ) ;
2016-01-28 22:35:22 +01:00
2016-04-18 21:03:35 +02:00
if ( this . getParent ( ) && this . getParent ( ) . instanceOf ( et2 _calendar _daycol ) )
{
this . div . toggleClass ( 'calendar_calEventSmall' , visible _lines < 4 ) ;
this . div
. attr ( 'data-visible_lines' , visible _lines ) ;
}
else if ( this . getParent ( ) && this . getParent ( ) . instanceOf ( et2 _calendar _planner _row ) )
{
// Less than 8 hours is small
2016-04-19 19:39:57 +02:00
this . div . toggleClass ( 'calendar_calEventSmall' , this . options . value . end . valueOf ( ) - this . options . value . start . valueOf ( ) < 28800000 ) ;
2016-04-18 21:03:35 +02:00
}
2016-02-02 22:12:44 +01:00
if ( this . body . height ( ) > this . div . height ( ) - this . title . height ( ) && visible _lines >= 4 )
{
this . body . css ( 'height' , Math . floor ( ( visible _lines - 1 ) * line _height - this . title . height ( ) ) + 'px' ) ;
}
else
{
this . body . css ( 'height' , '' ) ;
}
2016-01-28 22:35:22 +01:00
} ,
2015-07-01 18:02:20 +02:00
/ * *
* Examines the participants & returns CSS classname for status
2016-03-01 17:27:45 +01:00
*
2015-07-01 18:02:20 +02:00
* @ 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 ;
} ,
2016-01-13 23:07:09 +01:00
/ * *
* Create tooltip shown on hover
*
* @ return { String }
* /
2015-07-01 18:02:20 +02:00
_tooltip : function ( ) {
2017-03-21 16:17:31 +01:00
if ( ! this . div || ! this . options . value || ! this . options . value . app _id ) return '' ;
2016-03-01 17:27:45 +01: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' ) ;
2016-01-13 17:30:41 +01:00
var timespan = this . _get _timespan ( this . options . value ) ;
2015-06-10 23:51:28 +02:00
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 ?
2016-01-13 17:30:41 +01:00
'<span class="calendar_calEventLabel">' + this . egw ( ) . lang ( 'Time' ) + '</span>:' + timespan :
2016-08-03 17:34:08 +02:00
'<span class="calendar_calEventLabel">' + this . egw ( ) . lang ( 'Start' ) + '</span>:' + start + ' ' +
2016-01-14 22:24:01 +01:00
'<span class="calendar_calEventLabel">' + this . egw ( ) . lang ( 'End' ) + '</span>:' + end ;
var cat _label = '' ;
if ( this . options . value . category )
2015-11-17 21:56:47 +01:00
{
2016-01-14 22:24:01 +01:00
var cat = et2 _createWidget ( 'select-cat' , { 'readonly' : true } , this ) ;
cat . set _value ( this . options . value . category ) ;
cat _label = this . options . value . category . indexOf ( ',' ) <= 0 ? cat . span . text ( ) : [ ] ;
if ( typeof cat _label != 'string' )
{
cat . span . children ( ) . each ( function ( ) {
2016-06-02 16:51:15 +02:00
cat _label . push ( jQuery ( this ) . text ( ) ) ;
2016-01-14 22:24:01 +01:00
} ) ;
cat _label = cat _label . join ( ', ' ) ;
}
cat . destroy ( ) ;
2015-11-17 21:56:47 +01:00
}
2016-07-28 01:04:02 +02:00
var participants = '' ;
2016-09-02 16:21:57 +02:00
if ( this . options . value . participant _types [ '' ] )
{
participants += this . options . value . participant _types [ '' ] . join ( "<br />" ) ;
}
2016-07-28 01:04:02 +02:00
for ( var type _name in this . options . value . participant _types )
{
if ( type _name )
{
participants += '</p><p><span class="calendar_calEventLabel">' + type _name + '</span>:<br />' ;
2016-09-02 16:21:57 +02:00
participants += this . options . value . participant _types [ type _name ] . join ( "<br />" ) ;
2016-07-28 01:04:02 +02:00
}
}
2016-03-01 17:27:45 +01:00
2016-06-15 17:17:12 +02:00
return '<div class="calendar_calEventTooltip ' + this . _status _class ( ) + ' ' + this . options . class +
'" style="border-color: ' + border + '; background-color: ' + bg _color + ';">' +
2016-01-18 21:02:35 +01:00
'<div class="calendar_calEventHeaderSmall">' +
2016-01-13 17:30:41 +01:00
'<font style="color:' + header _color + '">' + timespan + '</font>' +
2015-06-10 23:51:28 +02:00
this . icons [ 0 ] . outerHTML +
'</div>' +
2016-01-18 21:02:35 +01:00
'<div class="calendar_calEventBody">' +
2015-06-10 23:51:28 +02:00
'<p style="margin: 0px;">' +
2017-03-01 18:52:38 +01:00
'<span class="calendar_calEventTitle">' + egw . htmlspecialchars ( this . options . value . title ) + '</span><br>' +
egw . htmlspecialchars ( this . options . value . description ) + '</p>' +
2015-06-10 23:51:28 +02:00
'<p style="margin: 2px 0px;">' + times + '</p>' +
2017-09-05 19:04:45 +02:00
( this . options . value . location ? '<p><span class="calendar_calEventLabel">' + this . egw ( ) . lang ( 'Location' ) + '</span>:' +
2017-03-01 18:52:38 +01:00
egw . htmlspecialchars ( 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>' : '' ) +
2016-05-04 17:09:37 +02:00
'<p><span class="calendar_calEventLabel">' + this . egw ( ) . lang ( 'Participants' ) + '</span>:<br />' +
2017-02-27 17:59:58 +01:00
participants + '</p>' + this . _participant _summary ( this . options . value . participants ) +
2015-06-10 23:51:28 +02:00
'</div>' +
'</div>' ;
} ,
2017-02-27 17:59:58 +01:00
/ * *
* Generate participant summary line
*
* @ returns { String }
* /
_participant _summary : function ( participants )
{
if ( Object . keys ( this . options . value . participants ) . length < 2 )
{
return '' ;
}
2017-09-05 19:04:45 +02:00
2017-02-27 17:59:58 +01:00
var participant _status = { A : 0 , R : 0 , T : 0 , U : 0 , D : 0 } ;
2017-03-21 15:10:11 +01:00
var status _label = { A : 'accepted' , R : 'rejected' , T : 'tentative' , U : 'unknown' , D : 'delegated' } ;
2017-02-27 17:59:58 +01:00
var participant _summary = Object . keys ( this . options . value . participants ) . length + ' ' + this . egw ( ) . lang ( 'Participants' ) + ': ' ;
var status _totals = [ ] ;
for ( var id in this . options . value . participants )
{
var status = this . options . value . participants [ id ] . substr ( 0 , 1 ) ;
participant _status [ status ] ++ ;
}
for ( var status in participant _status )
{
if ( participant _status [ status ] > 0 )
{
status _totals . push ( participant _status [ status ] + ' ' + this . egw ( ) . lang ( status _label [ status ] ) ) ;
}
}
return participant _summary + status _totals . join ( ', ' ) ;
} ,
2015-06-10 23:51:28 +02:00
/ * *
* 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 . 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
}
2018-03-02 18:55:06 +01:00
// Always include non-blocking, regardless of privacy
if ( this . options . value . non _blocking )
{
icons . push ( '<img src="' + this . egw ( ) . image ( 'nonblocking' , 'calendar' ) + '" title="' + this . egw ( ) . lang ( 'non blocking' ) + '"/>' ) ;
}
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
{
2016-06-24 19:19:03 +02:00
timespan = this . egw ( ) . lang ( 'Whole day' ) ;
2015-05-06 21:03:45 +02:00
}
}
else
{
2016-03-01 17:27:45 +01:00
var duration = event . multiday ?
2015-12-29 23:12:30 +01:00
( event . end - event . start ) / 60000 :
( event . end _m - event . start _m ) ;
2015-05-06 21:03:45 +02:00
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 ( ) ;
2016-07-29 00:39:26 +02:00
timespan += ' - ' + jQuery . datepicker . formatTime (
egw . preference ( "timeformat" ) === "12" ? "h:mmtt" : "HH:mm" ,
{
hour : event . end _m / 60 ,
minute : event . end _m % 60 ,
seconds : 0 ,
timezone : 0
} ,
{ "ampm" : ( egw . preference ( "timeformat" ) === "12" ) }
) . trim ( ) ;
timespan += ': ' + duration ;
2015-05-06 21:03:45 +02:00
}
return timespan ;
} ,
2016-03-01 17:27:45 +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 ( ) ) ;
}
2016-03-01 17:27:45 +01:00
2015-12-29 23:12:30 +01:00
// 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' ) ;
}
} ,
2016-01-13 23:07:09 +01:00
/ * *
* Check to see if the provided event information is for the same date as
* what we ' re currently expecting , and that it has not been changed .
*
* If the date has changed , we adjust the associated daywise caches to move
2016-01-15 17:58:59 +01:00
* the event ' s ID to where it should be . This check allows us to be more
* directly reliant on the data cache , and less on any other control logic
* elsewhere first .
2016-01-13 23:07:09 +01:00
*
* @ param { Object } event Map of event data from cache
* @ param { string } event . date For non - recurring , single day events , this is
* the date the event is on .
* @ param { string } event . start Start of the event ( used for multi - day events )
* @ param { string } event . end End of the event ( used for multi - day events )
*
* @ return { Boolean } Provided event data is for the same date
* /
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 ;
}
2016-01-14 16:46:55 +01:00
// Also check participants against owner
2016-04-05 01:58:23 +02:00
var owner _match = et2 _calendar _event . owner _check ( event , this . _parent ) ;
2016-01-14 16:46:55 +01:00
2015-11-17 17:57:34 +01:00
// Simple, same day
2016-01-15 23:43:59 +01:00
if ( owner _match && this . options . value . date && event . date == this . options . value . date )
2015-11-17 17:57:34 +01:00
{
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 ) ;
2016-01-15 23:43:59 +01:00
if ( owner _match && this . _parent . date >= event _start && this . _parent . date <= event _end )
2015-11-17 17:57:34 +01:00
{
return true ;
}
// Delete all old actions
2016-01-15 23:43:59 +01:00
if ( this . _actionObject )
{
this . _actionObject . clear ( ) ;
this . _actionObject . unregisterActions ( ) ;
this . _actionObject = null ;
}
2015-11-17 17:57:34 +01:00
// 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 ) ;
2016-01-18 22:38:45 +01:00
new _daywise = new _daywise && new _daywise . data ? 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 ) ;
2016-01-26 00:47:58 +01:00
}
2016-03-01 17:27:45 +01:00
2016-01-26 00:47:58 +01:00
if ( new _cache _id != old _cache _id )
{
2015-12-29 23:12:30 +01:00
var old _daywise = egw . dataGetUIDdata ( old _cache _id ) ;
2016-01-18 22:38:45 +01:00
old _daywise = old _daywise && old _daywise . data ? old _daywise . data : [ ] ;
2016-06-21 19:42:44 +02:00
old _daywise . splice ( old _daywise . indexOf ( this . options . value . row _id ) , 1 ) ;
2015-12-29 23:12:30 +01:00
egw . dataStoreUID ( old _cache _id , old _daywise ) ;
2016-03-01 17:27:45 +01:00
2016-06-21 19:42:44 +02:00
if ( new _daywise . indexOf ( event . row _id ) < 0 )
2016-01-19 17:21:44 +01:00
{
2016-06-21 19:42:44 +02:00
new _daywise . push ( event . row _id ) ;
2016-01-19 17:21:44 +01:00
}
2018-02-26 19:31:11 +01:00
if ( egw . dataHasUID ( new _cache _id ) )
2016-01-19 17:21:44 +01:00
{
egw . dataStoreUID ( new _cache _id , new _daywise ) ;
}
2016-01-18 22:38:45 +01:00
}
2015-11-17 17:57:34 +01:00
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 )
{
2016-06-02 16:51:15 +02:00
jQuery ( this . node ) . off ( "click" ) ;
2015-05-06 21:03:45 +02:00
}
} ,
/ * *
* 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
*
2016-01-13 23:07:09 +01:00
* Calls et2 _calendar _event . recur _prompt with this event ' s value .
*
* @ param { et2 _calendar _event ~ prompt _callback } 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
*
2016-01-13 23:07:09 +01:00
* Calls et2 _calendar _event . series _split _prompt with this event ' s value .
*
* @ param { et2 _calendar _event ~ prompt _callback } callback
2015-12-04 18:37:26 +01:00
* /
series _split _prompt : function ( callback )
{
et2 _calendar _event . series _split _prompt ( this . options . value , this . options . value . recur _date , callback ) ;
} ,
2017-02-24 16:31:39 +01:00
/ * *
* Copy the actions set on the parent , apply them to self
*
* This can take a while to do , so we try to do it only when needed - on mouseover
* /
_copy _parent _actions : function ( )
{
// Copy actions set in parent
if ( ! this . options . readonly && ! this . _parent . options . readonly )
{
var action _parent = this ;
while ( action _parent != null && ! action _parent . options . actions &&
! action _parent . instanceOf ( et2 _container )
)
{
action _parent = action _parent . getParent ( ) ;
}
try {
this . _link _actions ( action _parent . options . actions || { } ) ;
this . _need _actions _linked = false ;
} catch ( e ) {
// something went wrong, but keep quiet about it
}
}
} ,
2017-09-05 19:04:45 +02:00
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
2016-07-14 18:58:54 +02:00
var objectManager = this . getParent ( ) . _actionObject || this . getParent ( ) . getParent ( ) . _actionObject ||
2015-08-26 01:30:32 +02:00
egw _getAppObjectManager ( true ) . getObjectById ( this . _parent . _parent . _parent . id ) || egw _getAppObjectManager ( true ) ;
2016-01-20 21:58:14 +01:00
this . _actionObject = objectManager . getObjectById ( 'calendar::' + this . options . value . row _id ) ;
2015-08-26 01:30:32 +02:00
}
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 (
2016-01-20 21:58:14 +01:00
'calendar::' + this . options . value . row _id , objectManager , new et2 _event _action _object _impl ( this , this . getDOMNode ( ) ) ,
2016-02-23 22:29:13 +01:00
this . _actionManager || objectManager . manager . getActionById ( 'calendar::' + this . options . value . row _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' ) ;
2016-05-16 21:53:35 +02:00
if ( this . _actionObject . parent . getActionLink ( 'invite' ) )
{
action _links . push ( 'invite' ) ;
}
2015-08-26 01:30:32 +02:00
this . _actionObject . updateActionLinks ( action _links ) ;
2015-05-06 21:03:45 +02:00
} ,
2016-03-01 17:27:45 +01:00
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 ) {
} ,
2016-03-01 17:27:45 +01:00
} ) ; } ) . call ( this ) ;
2015-06-10 23:51:28 +02:00
et2 _register _widget ( et2 _calendar _event , [ "calendar-event" ] ) ;
// Static class stuff
2016-04-05 01:58:23 +02:00
/ * *
* Check event owner against a parent object
2016-04-26 16:35:58 +02:00
*
2016-04-05 01:58:23 +02:00
* As an event is edited , its participants may change . Also , as the state
* changes we may change which events are displayed and show the same event
* in several places for different users . Here we check the event participants
* against an owner value ( which may be an array ) to see if the event should be
* displayed or included .
2016-04-26 16:35:58 +02:00
*
2016-04-05 01:58:23 +02:00
* @ param { Object } event - Event information
* @ param { et2 _widget _daycol | et2 _widget _planner _row } parent - potential parent object
* that has an owner option
2016-05-16 18:55:02 +02:00
* @ param { boolean } owner _too - Include the event owner in consideration , or only
* event participants
2016-04-26 16:35:58 +02:00
*
2016-04-05 01:58:23 +02:00
* @ return { boolean } Should the event be displayed
* /
2016-05-16 18:55:02 +02:00
et2 _calendar _event . owner _check = function owner _check ( event , parent , owner _too )
2016-04-05 01:58:23 +02:00
{
var owner _match = true ;
2016-05-16 18:55:02 +02:00
if ( typeof owner _too === 'undefined' && app . calendar . state . status _filter )
{
owner _too = app . calendar . state . status _filter === 'owner' ;
}
2016-07-21 18:21:04 +02:00
var options = false ;
2016-07-19 22:09:58 +02:00
if ( app . calendar && app . calendar . sidebox _et2 && app . calendar . sidebox _et2 . getWidgetById ( 'owner' ) )
{
options = app . calendar . sidebox _et2 . getWidgetById ( 'owner' ) . taglist . getSelection ( ) ;
}
else
{
2016-07-21 18:42:47 +02:00
options = parent . getArrayMgr ( "sel_options" ) . getRoot ( ) . getEntry ( 'owner' ) ;
2016-07-19 22:09:58 +02:00
}
2016-10-18 18:40:24 +02:00
if ( event . participants && typeof parent . options . owner != 'undefined' && parent . options . owner . length > 0 )
2016-04-05 01:58:23 +02:00
{
2016-07-19 22:09:58 +02:00
var parent _owner = jQuery . extend ( [ ] , typeof parent . options . owner !== 'object' ?
2016-04-05 01:58:23 +02:00
[ parent . options . owner ] :
2016-07-19 22:09:58 +02:00
parent . options . owner ) ;
2016-04-05 01:58:23 +02:00
owner _match = false ;
var length = parent _owner . length ;
for ( var i = 0 ; i < length ; i ++ )
{
2016-07-19 22:09:58 +02:00
// Handle grouped resources like mailing lists, they won't match so
// we need the list - pull it from sidebox owner
if ( isNaN ( parent _owner [ i ] ) && options && options . find )
{
var resource = options . find ( function ( element ) { return element . id == parent _owner [ i ] ; } ) || { } ;
if ( resource && resource . resources )
{
parent _owner . splice ( i , 1 ) ;
parent _owner = parent _owner . concat ( resource . resources ) ;
continue ;
}
}
2016-07-22 12:03:46 +02:00
2016-04-05 01:58:23 +02:00
if ( parseInt ( parent _owner [ i ] ) < 0 )
{
// Add in groups, if we can get them (this is syncronous)
egw . accountData ( parent _owner [ i ] , 'account_id' , true , function ( members ) {
parent _owner = parent _owner . concat ( Object . keys ( members ) ) ;
} ) ;
}
}
2016-05-16 18:55:02 +02:00
var participants = jQuery . extend ( [ ] , Object . keys ( event . participants ) ) ;
for ( var i = 0 ; i < participants . length ; i ++ )
2016-04-05 01:58:23 +02:00
{
2016-05-16 18:55:02 +02:00
var id = participants [ i ] ;
// Expand group invitations
if ( parseInt ( id ) < 0 )
{
// Add in groups, if we can get them (this is syncronous)
egw . accountData ( id , 'account_id' , true , function ( members ) {
participants = participants . concat ( Object . keys ( members ) ) ;
} ) ;
}
2016-04-05 01:58:23 +02:00
if ( parent . options . owner == id ||
parent _owner . indexOf &&
parent _owner . indexOf ( id ) >= 0 )
{
owner _match = true ;
break ;
}
}
}
2016-05-16 18:55:02 +02:00
if ( owner _too && ! owner _match )
{
owner _match = ( parent . options . owner == event . owner ||
parent _owner . indexOf &&
parent _owner . indexOf ( event . owner ) >= 0 ) ;
}
2016-04-05 01:58:23 +02:00
return owner _match ;
} ;
2016-01-13 23:07:09 +01:00
/ * *
* @ callback et2 _calendar _event ~ prompt _callback
* @ param { string } button _id - One of ok , exception , series , single or cancel
* depending on which buttons are on the prompt
* @ param { Object } event _data - Event information - whatever you passed in to
* the prompt .
* /
2015-06-10 23:51:28 +02:00
/ * *
* Recur prompt
2016-03-01 17:27:45 +01:00
* If the event is recurring , asks the user if they want to edit the event as
2015-06-10 23:51:28 +02:00
* an exception , or change the whole series . Then the callback is called .
*
2016-01-13 23:07:09 +01:00
* If callback is not provided , egw . open ( ) will be used to open an edit dialog .
*
* If you call this on a single ( non - recurring ) event , the callback will be
* executed immediately , with the passed button _id as 'single' .
*
2015-06-10 23:51:28 +02:00
* @ param { Object } event _data - Event information
2016-01-13 23:07:09 +01:00
* @ param { string } event _data . id - Unique ID for the event , possibly with a
* timestamp
2015-06-10 23:51:28 +02:00
* @ 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
2016-01-13 23:07:09 +01:00
* @ param { et2 _calendar _event ~ prompt _callback } [ callback ] - Callback is
* called with the button ( exception , series , single or cancel ) and the event
* data .
* @ param { Object } [ extra _data ] - Additional data passed to the callback , used
* as extra parameters for default callback
2016-03-01 17:27:45 +01:00
*
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 ;
2016-07-22 12:03:46 +02:00
// seems window.opener somehow in certian conditions could be from different origin
// we try to catch the exception and in this case retrive the egw object from current window.
try {
var egw = this . egw ? ( typeof this . egw == 'function' ? this . egw ( ) : this . egw ) : window . opener && typeof window . opener . egw != 'undefined' ? window . opener . egw ( 'calendar' ) : window . egw ( 'calendar' ) ;
}
catch ( e ) {
var egw = window . egw ( 'calendar' ) ;
}
2015-06-10 23:51:28 +02:00
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 .
*
2016-01-13 23:07:09 +01:00
* There is no default callback , and nothing happens if you call this on a
* single ( non - recurring ) event
*
2015-12-04 18:37:26 +01:00
* @ 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
2016-01-13 23:07:09 +01:00
* @ param { et2 _calendar _event ~ prompt _callback } callback - Callback is
* called with the button ( ok or cancel ) and the event data .
2015-12-04 18:37:26 +01:00
* @ augments { et2 _calendar _event }
* /
et2 _calendar _event . series _split _prompt = function ( event _data , instance _date , callback )
{
2016-07-22 12:03:46 +02:00
// seems window.opener somehow in certian conditions could be from different origin
// we try to catch the exception and in this case retrive the egw object from current window.
try {
var egw = this . egw ? ( typeof this . egw == 'function' ? this . egw ( ) : this . egw ) : window . opener && typeof window . opener . egw != 'undefined' ? window . opener . egw ( 'calendar' ) : window . egw ( 'calendar' ) ;
}
catch ( e ) {
var egw = window . egw ( 'calendar' ) ;
}
2015-12-04 18:37:26 +01:00
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
2016-03-01 17:27:45 +01:00
* @ param { string } [ role ]
2015-06-10 23:51:28 +02:00
* @ 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
2016-01-13 23:07:09 +01:00
* to tie actions to DOM nodes . I ' m not sure if we need this .
2015-06-10 23:51:28 +02:00
*
* 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 ;
} ;