2015-05-06 21:03:45 +02:00
/ *
* Egroupware Calendar timegrid
* @ 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 ;
/ c a l e n d a r / j s / e t 2 _ w i d g e t _ d a y c o l . j s ;
/ c a l e n d a r / j s / e t 2 _ w i d g e t _ e v e n t . j s ;
* /
/ * *
* Class which implements the "calendar-timegrid" XET - Tag for displaying a span of days
*
* This widget is responsible for the times on the side
*
* @ augments et2 _DOMWidget
* /
var et2 _calendar _timegrid = et2 _valueWidget . extend ( [ et2 _IDetachedDOM , et2 _IResizeable ] ,
{
createNamespace : true ,
attributes : {
start _date : {
name : "Start date" ,
type : "any"
} ,
end _date : {
name : "End date" ,
type : "any"
} ,
value : {
type : "any" ,
description : "An array of events, indexed by date (Ymd format)."
} ,
day _start : {
name : "Day start time" ,
type : "string" ,
default : parseInt ( egw . preference ( 'workdaystarts' , 'calendar' ) ) || 9 ,
description : "Work day start time. If unset, this will default to the current user's preference"
} ,
day _end : {
name : "Day end time" ,
type : "string" ,
default : parseInt ( egw . preference ( 'workdayends' , 'calendar' ) ) || 17 ,
description : "Work day end time. If unset, this will default to the current user's preference"
} ,
show _weekend : {
name : "Weekends" ,
type : "boolean" ,
default : egw . preference ( 'days_in_weekview' , 'calendar' ) != 5 ,
description : "Display weekends. The date range should still include them for proper scrolling, but they just won't be shown."
} ,
granularity : {
name : "Granularity" ,
type : "integer" ,
default : parseInt ( egw . preference ( 'interval' , 'calendar' ) ) || 30 ,
description : "How many minutes per row"
} ,
owner : {
name : "Owner" ,
type : "any" , // Integer, or array of integers
default : 0 ,
description : "Account ID number of the calendar owner, if not the current user"
} ,
2015-06-10 23:51:28 +02:00
"onchange" : {
"name" : "onchange" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "JS code which is executed when the date range changes."
} ,
"onevent_change" : {
"name" : "onevent_change" ,
"type" : "js" ,
"default" : et2 _no _init ,
"description" : "JS code which is executed when an event changes."
} ,
2015-05-06 21:03:45 +02:00
height : {
"default" : '100%'
}
} ,
/ * *
* Constructor
*
* @ memberOf et2 _calendar _timegrid
* /
init : function ( ) {
this . _super . apply ( this , arguments ) ;
// Main container
this . div = $j ( document . createElement ( "div" ) )
2015-08-25 02:00:45 +02:00
. addClass ( "calendar_calTimeGrid" )
. addClass ( "calendar_TimeGridNoLabel" ) ;
2015-05-06 21:03:45 +02:00
2015-10-06 01:45:51 +02:00
// Headers
2015-05-06 21:03:45 +02:00
this . gridHeader = $j ( document . createElement ( "div" ) )
2015-11-12 02:01:21 +01:00
. addClass ( "calendar_calGridHeader" )
2015-05-06 21:03:45 +02:00
. appendTo ( this . div ) ;
2015-10-06 01:45:51 +02:00
this . dayHeader = $j ( document . createElement ( "div" ) )
. appendTo ( this . gridHeader ) ;
2015-05-06 21:03:45 +02:00
2015-10-06 01:45:51 +02:00
// Contains times / rows
this . scrolling = $j ( document . createElement ( 'div' ) )
. addClass ( "calendar_calTimeGridScroll" )
. appendTo ( this . div ) ;
2015-05-06 21:03:45 +02:00
// Contains days / columns
this . days = $j ( document . createElement ( "div" ) )
. addClass ( "calendar_calDayCols" )
2015-10-06 01:45:51 +02:00
. appendTo ( this . scrolling ) ;
2015-05-06 21:03:45 +02:00
// Used for its date calculations
this . date _helper = et2 _createWidget ( 'date' , { } , null ) ;
this . date _helper . loadingFinished ( ) ;
// Used for owners
this . owner = et2 _createWidget ( 'select-account_ro' , { } , this ) ;
2015-08-19 02:08:22 +02:00
this . _labelContainer = $j ( document . createElement ( "label" ) )
. addClass ( "et2_label" )
. appendTo ( this . gridHeader ) ;
2015-05-06 21:03:45 +02:00
// List of dates in Ymd
// The first one should be start_date, last should be end_date
this . day _list = [ ] ;
this . day _widgets = [ ] ;
// Update timer, to avoid redrawing twice when changing start & end date
this . update _timer = null ;
this . setDOMNode ( this . div [ 0 ] ) ;
} ,
destroy : function ( ) {
2015-08-26 01:30:32 +02:00
// Stop the invalidate timer
if ( this . update _timer )
{
window . clearTimeout ( this . update _timer ) ;
}
2015-05-06 21:03:45 +02:00
this . _super . apply ( this , arguments ) ;
2015-08-26 01:30:32 +02:00
// Delete all old objects
this . _actionObject . clear ( ) ;
this . _actionObject . unregisterActions ( ) ;
this . _actionObject . remove ( ) ;
this . _actionObject = null ;
2015-05-06 21:03:45 +02:00
this . div . off ( ) ;
2015-08-26 01:30:32 +02:00
this . div = null ;
this . gridHeader = null ;
2015-10-06 01:45:51 +02:00
this . dayHeader = null ;
2015-08-26 01:30:32 +02:00
this . days = null ;
2015-10-06 01:45:51 +02:00
this . scrolling = null ;
2015-08-26 01:30:32 +02:00
this . _labelContainer = null ;
2015-05-06 21:03:45 +02:00
// date_helper has no parent, so we must explicitly remove it
this . date _helper . destroy ( ) ;
this . date _helper = null ;
2015-06-10 23:51:28 +02:00
// Stop the invalidate timer
if ( this . update _timer )
{
window . clearTimeout ( this . update _timer ) ;
}
2015-05-06 21:03:45 +02:00
} ,
doLoadingFinished : function ( ) {
this . _super . apply ( this , arguments ) ;
2015-10-06 01:45:51 +02:00
// Need to get the correct internal sizing
this . resize ( ) ;
2015-05-06 21:03:45 +02:00
this . _drawGrid ( ) ;
2015-06-10 23:51:28 +02:00
// Actions may be set on a parent, so we need to explicitly get in here
// and get ours
this . _link _actions ( this . options . actions || this . _parent . options . actions || [ ] ) ;
// Automatically bind drag and resize for every event using jQuery directly
// - no action system -
var timegrid = this ;
2015-07-02 21:31:52 +02:00
/ * *
* If user puts the mouse over an event , then we ' ll set up resizing so
* they can adjust the length . Should be a little better on resources
* than binding it for every calendar event , and we won ' t need exceptions
* for planner view to resize horizontally .
* /
this . div . on ( 'mouseover' , '.calendar_calEvent:not(.ui-resizable):not(.rowNoEdit)' , function ( ) {
// Load the event
timegrid . _get _event _info ( this ) ;
var that = this ;
//Resizable event handler
$j ( this ) . resizable
( {
distance : 10 ,
2015-10-06 01:45:51 +02:00
// Grid matching preference
grid : [ 10000 , timegrid . rowHeight ] ,
2015-07-02 21:31:52 +02:00
autoHide : false ,
handles : 's,se' ,
containment : 'parent' ,
/ * *
* Triggered when the resizable is created .
*
* @ param { event } event
* @ param { Object } ui
* /
create : function ( event , ui )
{
var resizeHelper = event . target . getAttribute ( 'data-resize' ) ;
if ( resizeHelper == 'WD' || resizeHelper == 'WDS' )
{
jQuery ( this ) . resizable ( 'destroy' ) ;
}
} ,
/ * *
* Triggered at the end of resizing the calEvent .
*
* @ param { event } event
* @ param { Object } ui
* /
stop : function ( event , ui )
{
var e = new jQuery . Event ( 'change' ) ;
e . originalEvent = event ;
e . data = { duration : 0 } ;
var event _data = timegrid . _get _event _info ( this ) ;
2015-11-30 18:21:40 +01:00
var event _widget = timegrid . getWidgetById ( 'event_' + event _data . app _id ) ;
2015-07-02 21:31:52 +02:00
var sT = event _widget . options . value . start _m ;
if ( typeof this . dropEnd != 'undefined' && this . dropEnd . length == 1 )
{
var eT = parseInt ( this . dropEnd . attr ( 'data-hour' ) * 60 ) + parseInt ( this . dropEnd . attr ( 'data-minute' ) ) ;
e . data . duration = ( ( eT - sT ) / 60 ) * 3600 ;
if ( event _widget )
{
2015-07-03 19:56:36 +02:00
event _widget . options . value . end _m = eT ;
event _widget . options . value . duration = e . data . duration ;
2015-07-02 21:31:52 +02:00
}
$j ( this ) . trigger ( e ) ;
// That cleared the resize handles, so remove for re-creation...
2015-11-05 21:56:13 +01:00
if ( $j ( this ) . resizable ( 'instance' ) )
{
$j ( this ) . resizable ( 'destroy' ) ;
}
2015-07-02 21:31:52 +02:00
}
// Clear the helper, re-draw
2015-11-05 21:56:13 +01:00
if ( event _widget && event _widget . _parent )
2015-07-03 19:56:36 +02:00
{
event _widget . _parent . position _event ( event _widget ) ;
}
2015-10-06 01:45:51 +02:00
timegrid . div . children ( '.drop-hover' ) . removeClass ( '.drop-hover' ) ;
2015-07-02 21:31:52 +02:00
} ,
/ * *
* Triggered during the resize , on the drag of the resize handler
*
* @ param { event } event
* @ param { Object } ui
* /
resize : function ( event , ui )
{
// Add 5px to make sure it doesn't land right on the edge of a div
2015-10-06 01:45:51 +02:00
var drop = timegrid . _drag _helper ( this , ui . element [ 0 ] , ui . helper . outerHeight ( ) + 5 ) ;
if ( drop && ! drop . is ( ':visible' ) )
{
drop . get ( 0 ) . scrollIntoView ( false ) ;
}
2015-07-02 21:31:52 +02:00
}
} ) ;
} ) ;
2015-06-10 23:51:28 +02:00
// Customize and override some draggable settings
2015-11-11 17:54:00 +01:00
this . div
. on ( 'dragcreate' , '.calendar_calEvent' , function ( event , ui ) {
$j ( this ) . draggable ( 'option' , 'cancel' , '.rowNoEdit' ) ;
2015-11-10 19:35:24 +01:00
// Act like you clicked the header, makes it easier to position
$j ( this ) . draggable ( 'option' , 'cursorAt' , { top : 5 , left : 5 } ) ;
2015-06-10 23:51:28 +02:00
} )
2015-07-22 01:45:38 +02:00
. on ( 'dragstart' , '.calendar_calEvent' , function ( event , ui ) {
2015-06-10 23:51:28 +02:00
$j ( '.calendar_calEvent' , ui . helper ) . width ( $j ( this ) . width ( ) )
. height ( $j ( this ) . outerHeight ( ) )
2015-07-22 01:45:38 +02:00
. css ( 'top' , '' ) . css ( 'left' , '' )
2015-06-10 23:51:28 +02:00
. appendTo ( ui . helper ) ;
2015-07-22 01:45:38 +02:00
ui . helper . width ( $j ( this ) . width ( ) ) ;
} ) ;
return true ;
} ,
/ * *
* Show the current time while dragging
* Used for resizing as well as drag & drop
* /
_drag _helper : function ( element , helper , height )
{
2015-11-10 19:35:24 +01:00
if ( ! element ) return ;
2015-07-22 01:45:38 +02:00
element . dropEnd = this . _get _time _from _position ( helper . getBoundingClientRect ( ) . left ,
helper . getBoundingClientRect ( ) . top + parseInt ( height ) ) ;
2015-11-02 21:04:31 +01:00
if ( element . dropEnd . length )
{
this . _drop _data = element . dropEnd [ 0 ] . dataset || { } ;
}
2015-07-22 01:45:38 +02:00
if ( typeof element . dropEnd != 'undefined' && element . dropEnd . length )
{
element . dropEnd . addClass ( "drop-hover" ) ;
2015-10-06 01:45:51 +02:00
// Make sure the target is visible in the scrollable day
var scrollto = element . dropEnd . next ( ) ? element . dropEnd . next ( ) : element . dropEnd ;
2015-11-04 00:41:55 +01:00
if ( scrollto . length && this . scrolling . height ( ) + this . scrolling . scrollTop ( ) < scrollto . position ( ) . top + scrollto . height ( ) )
2015-10-06 01:45:51 +02:00
{
scrollto . get ( 0 ) . scrollIntoView ( false ) ;
}
2015-11-02 21:04:31 +01:00
else if ( element . dropEnd . position ( ) . top < this . scrolling [ 0 ] . scrollTop )
2015-10-06 01:45:51 +02:00
{
2015-11-02 21:04:31 +01:00
this . scrolling . scrollTop ( element . dropEnd . position ( ) . top ) ;
2015-10-06 01:45:51 +02:00
}
2015-11-10 19:35:24 +01:00
var time = '' ;
if ( this . _drop _data . whole _day )
{
time = this . egw ( ) . lang ( 'Whole day' ) ;
}
else
{
time = jQuery . datepicker . formatTime (
2015-11-17 21:19:47 +01:00
egw . preference ( "timeformat" ) === "12" ? "h:mmtt" : "HH:mm" ,
2015-11-10 19:35:24 +01:00
{
hour : element . dropEnd . attr ( 'data-hour' ) ,
minute : element . dropEnd . attr ( 'data-minute' ) ,
seconds : 0 ,
timezone : 0
} ,
{ "ampm" : ( egw . preference ( "timeformat" ) == "12" ) }
) ;
}
2015-07-22 01:45:38 +02:00
element . innerHTML = '<div style="font-size: 1.1em; text-align:center; font-weight: bold; height:100%;"><span class="calendar_timeDemo" >' + time + '</span></div>' ;
}
else
{
element . innerHTML = '<div class="calendar_d-n-d_forbiden" style="height:100%"></div>' ;
}
$j ( element ) . width ( $j ( helper ) . width ( ) ) ;
return element . dropEnd ;
} ,
2015-06-10 23:51:28 +02:00
2015-07-22 01:45:38 +02:00
/ * *
* Handler for dropping an event on the timegrid
* /
2015-11-02 21:04:31 +01:00
_event _drop : function ( timegrid , event , ui , dropEnd ) {
2015-07-22 01:45:38 +02:00
var e = new jQuery . Event ( 'change' ) ;
e . originalEvent = event ;
e . data = { start : 0 } ;
2015-11-10 19:35:24 +01:00
2015-11-02 21:04:31 +01:00
if ( typeof dropEnd != 'undefined' && dropEnd )
{
var drop _date = dropEnd . date || false ;
2015-06-10 23:51:28 +02:00
2015-07-22 01:45:38 +02:00
var event _data = timegrid . _get _event _info ( ui . draggable ) ;
2015-11-27 17:57:25 +01:00
var event _widget = timegrid . getWidgetById ( 'event_' + event _data . app _id ) ;
2015-07-22 01:45:38 +02:00
if ( ! event _widget )
{
// Widget was moved across weeks / owners
2015-11-30 18:21:40 +01:00
event _widget = timegrid . getParent ( ) . getWidgetById ( 'event_' + event _data . app _id ) ;
2015-07-22 01:45:38 +02:00
}
if ( event _widget )
{
event _widget . _parent . date _helper . set _year ( drop _date . substring ( 0 , 4 ) ) ;
event _widget . _parent . date _helper . set _month ( drop _date . substring ( 4 , 6 ) ) ;
event _widget . _parent . date _helper . set _date ( drop _date . substring ( 6 , 8 ) ) ;
2015-11-10 19:35:24 +01:00
// Make sure whole day events stay as whole day events by ignoring drop time
if ( event _data . app == 'calendar' && event _widget . options . value . whole _day )
{
event _widget . _parent . date _helper . set _hours ( 0 ) ;
event _widget . _parent . date _helper . set _minutes ( 0 )
}
else
{
// Non-whole day events, and integrated apps, can change
event _widget . _parent . date _helper . set _hours ( dropEnd . whole _day ? 0 : dropEnd . hour || 0 ) ;
event _widget . _parent . date _helper . set _minutes ( dropEnd . whole _day ? 0 : dropEnd . minute || 0 ) ;
}
2015-07-22 01:45:38 +02:00
// Leave the helper there until the update is done
2015-11-10 20:57:05 +01:00
var loading = ui . helper . clone ( true ) . appendTo ( $j ( 'body' ) ) ;
2015-08-06 19:14:20 +02:00
// and add a loading icon so user knows something is happening
2015-11-10 20:57:05 +01:00
if ( $j ( '.calendar_timeDemo' , loading ) . length == 0 )
{
$j ( '.calendar_calEventHeader' , loading ) . addClass ( 'loading' ) ;
}
else
{
$j ( '.calendar_timeDemo' , loading ) . after ( '<div class="loading"></div>' ) ;
}
2015-08-06 19:14:20 +02:00
2015-07-22 01:45:38 +02:00
event _widget . recur _prompt ( function ( button _id ) {
2015-11-02 21:04:31 +01:00
if ( button _id === 'cancel' || ! button _id )
{
// Need to refresh the event with original info to clean up
var app _id = event _widget . options . value . app _id ? event _widget . options . value . app _id : event _widget . options . value . id + ( event _widget . options . value . recur _type ? ':' + event _widget . options . value . recur _date : '' ) ;
2015-11-13 21:22:58 +01:00
egw ( ) . dataStoreUID ( 'calendar::' + app _id , egw . dataGetUIDdata ( 'calendar::' + app _id ) . data ) ;
loading . remove ( ) ;
2015-11-02 21:04:31 +01:00
return ;
}
2015-07-22 01:45:38 +02:00
//Get infologID if in case if it's an integrated infolog event
if ( event _data . app === 'infolog' )
2015-06-10 23:51:28 +02:00
{
2015-11-10 19:35:24 +01:00
// Duration - infologs are always non-blocking
var duration = dropEnd . whole _day ? 86400 - 1 : (
event _widget . options . value . whole _day ? ( egw ( ) . preference ( 'defaultlength' , 'calendar' ) * 60 ) : false ) ;
2015-07-22 01:45:38 +02:00
// If it is an integrated infolog event we need to edit infolog entry
egw ( ) . json ( 'stylite_infolog_calendar_integration::ajax_moveInfologEvent' ,
2015-11-27 17:57:25 +01:00
[ event _data . id , event _widget . _parent . date _helper . getValue ( ) || false , duration ] ,
2015-07-22 01:45:38 +02:00
function ( ) { loading . remove ( ) ; }
) . sendRequest ( true ) ;
2015-06-10 23:51:28 +02:00
}
2015-07-22 01:45:38 +02:00
else
{
//Edit calendar event
2015-11-10 19:35:24 +01:00
// Duration - check for whole day dropped on a time, change it to full day
var duration = event _widget . options . value . whole _day && dropEnd . hour ? 86400 - 1 : false ;
// Event (whole day or not) dropped on whole day section, change to whole day non blocking
if ( dropEnd . whole _day ) duration = 'whole_day' ;
2015-11-27 17:57:25 +01:00
// Send the update
var _send = function ( series _instance )
{
var start = new Date ( event _widget . _parent . date _helper . getValue ( ) ) ;
egw ( ) . json ( 'calendar.calendar_uiforms.ajax_moveEvent' , [
button _id === 'series' ? event _data . id : event _data . app _id , event _data . owner ,
start ,
timegrid . options . owner || egw . user ( 'account_id' ) ,
duration ,
series _instance
] ,
function ( ) { loading . remove ( ) ; }
) . sendRequest ( true ) ;
} ;
// Check for modifying a series that started before today
var tempDate = new Date ( ) ;
var today = new Date ( tempDate . getFullYear ( ) , tempDate . getMonth ( ) , tempDate . getDate ( ) , 0 , - tempDate . getTimezoneOffset ( ) , 0 ) ;
if ( today >= new Date ( event _widget . options . value . start ) )
{
et2 _dialog . show _dialog ( function ( _button _id )
{
if ( _button _id == et2 _dialog . OK _BUTTON )
{
_send ( event _widget . options . value . recur _date ) ;
}
else
{
return false ;
}
} ,
egw . lang ( "Do you really want to change the start of this series? If you do, the original series will be terminated as of today and a new series for the future reflecting your changes will be created." ) ,
egw . lang ( "This event is part of a series" ) , { } , et2 _dialog . BUTTONS _OK _CANCEL , et2 _dialog . WARNING _MESSAGE ) ;
}
else
{
_send ( event _widget . options . value . recur _date ) ;
}
2015-07-22 01:45:38 +02:00
}
} ) ;
}
}
2015-05-06 21:03:45 +02:00
} ,
/ * *
* Something changed , and the days need to be re - drawn . We wait a bit to
* avoid re - drawing twice if start and end date both changed , then recreate
* the days .
* The whole grid is not regenerated because times aren ' t expected to change ,
* just the days .
2015-06-10 23:51:28 +02:00
*
* @ param { boolean } trigger = false Trigger an event once things are done .
* Waiting until invalidate completes prevents 2 updates when changing the date range .
2015-05-06 21:03:45 +02:00
* @ returns { undefined }
* /
2015-06-10 23:51:28 +02:00
invalidate : function ( trigger ) {
2015-05-06 21:03:45 +02:00
// Reset the list of days
this . day _list = [ ] ;
// Wait a bit to see if anything else changes, then re-draw the days
if ( this . update _timer === null )
{
this . update _timer = window . setTimeout ( jQuery . proxy ( function ( ) {
2015-06-10 23:51:28 +02:00
this . widget . update _timer = null ;
// Update actions
2015-08-05 23:24:07 +02:00
if ( this . widget . _actionManager )
2015-06-10 23:51:28 +02:00
{
2015-08-05 23:24:07 +02:00
this . widget . _link _actions ( this . widget . _actionManager . children ) ;
2015-06-10 23:51:28 +02:00
}
this . widget . _drawDays ( ) ;
2015-11-12 02:01:21 +01:00
this . widget . _resizeTimes ( ) ;
2015-06-10 23:51:28 +02:00
if ( this . trigger )
{
this . widget . change ( ) ;
}
} , { widget : this , "trigger" : trigger } ) , ET2 _GRID _INVALIDATE _TIMEOUT ) ;
2015-05-06 21:03:45 +02:00
}
} ,
2015-06-10 23:51:28 +02:00
detachFromDOM : function ( ) {
// Remove the binding to the change handler
2015-07-22 01:45:38 +02:00
$j ( this . div ) . off ( ".et2_calendar_timegrid" ) ;
2015-06-10 23:51:28 +02:00
this . _super . apply ( this , arguments ) ;
} ,
attachToDOM : function ( ) {
this . _super . apply ( this , arguments ) ;
// Add the binding for the event change handler
$j ( this . div ) . on ( "change.et2_calendar_timegrid" , '.calendar_calEvent' , this , function ( e ) {
// Make sure function gets a reference to the widget
var args = Array . prototype . slice . call ( arguments ) ;
if ( args . indexOf ( this ) == - 1 ) args . push ( this ) ;
return e . data . event _change . apply ( e . data , args ) ;
} ) ;
// Add the binding for the change handler
$j ( this . div ) . on ( "change.et2_calendar_timegrid" , '*:not(.calendar_calEvent)' , this , function ( e ) {
return e . data . change . call ( e . data , e , this ) ;
} ) ;
} ,
2015-05-06 21:03:45 +02:00
getDOMNode : function ( _sender ) {
if ( _sender === this || ! _sender )
{
2015-10-27 17:45:37 +01:00
return this . div ? this . div [ 0 ] : null ;
2015-05-06 21:03:45 +02:00
}
else if ( _sender . instanceOf ( et2 _calendar _daycol ) )
{
2015-10-27 17:45:37 +01:00
return this . days ? this . days [ 0 ] : null ;
2015-05-06 21:03:45 +02:00
}
else if ( _sender )
{
2015-10-27 17:45:37 +01:00
return this . gridHeader ? this . gridHeader [ 0 ] : null ;
2015-05-06 21:03:45 +02:00
}
} ,
2015-07-02 21:31:52 +02:00
/ * *
* Clear everything , and redraw the whole grid
* /
2015-05-06 21:03:45 +02:00
_drawGrid : function ( ) {
this . div . css ( 'height' , this . options . height )
. empty ( ) ;
// Draw in the horizontal - the times
this . _drawTimes ( ) ;
// Draw in the vertical - the days
2015-06-10 23:51:28 +02:00
this . invalidate ( ) ;
2015-05-06 21:03:45 +02:00
} ,
/ * *
* Creates the DOM nodes for the times in the left column , and the horizontal
* lines ( mostly via CSS ) that span the whole time span .
* /
_drawTimes : function ( ) {
2015-08-19 02:08:22 +02:00
$j ( '.calendar_calTimeRow' , this . div ) . remove ( ) ;
2015-10-27 20:08:33 +01:00
2015-05-06 21:03:45 +02:00
var wd _start = 60 * this . options . day _start ;
var wd _end = 60 * this . options . day _end ;
var granularity = this . options . granularity ;
var totalDisplayMinutes = wd _end - wd _start ;
2015-11-12 02:01:21 +01:00
var rowsToDisplay = Math . ceil ( ( totalDisplayMinutes + 60 ) / granularity ) ;
2015-05-06 21:03:45 +02:00
this . gridHeader
2015-10-06 01:45:51 +02:00
. empty ( )
2015-09-03 00:40:38 +02:00
. attr ( 'data-date' , this . options . start _date )
. attr ( 'data-owner' , this . options . owner )
2015-10-28 16:55:10 +01:00
. append ( this . _labelContainer )
2015-09-03 00:40:38 +02:00
. append ( this . owner . getDOMNode ( ) )
2015-10-06 01:45:51 +02:00
. append ( this . dayHeader )
2015-05-06 21:03:45 +02:00
. appendTo ( this . div ) ;
2015-11-04 00:41:55 +01:00
// Max with 45 avoids problems when it's not shown
2015-11-12 02:01:21 +01:00
var header _height = Math . max ( this . gridHeader . outerHeight ( true ) , 45 ) ;
2015-11-04 00:41:55 +01:00
2015-10-06 01:45:51 +02:00
this . scrolling
2015-11-12 02:01:21 +01:00
. css ( 'height' , ( this . div . innerHeight ( ) - header _height ) + 'px' )
2015-10-06 01:45:51 +02:00
. appendTo ( this . div )
2015-11-04 22:47:52 +01:00
. empty ( )
. off ( ) . on ( 'scroll' , jQuery . proxy ( this . _scroll , this ) ) ;
2015-10-06 01:45:51 +02:00
2015-10-29 22:53:47 +01:00
// Percent
var rowHeight = ( 100 / rowsToDisplay ) . toFixed ( 1 ) ;
// Pixels
2015-11-12 02:01:21 +01:00
this . rowHeight = this . scrolling . height ( ) / rowsToDisplay ;
2015-10-29 22:53:47 +01:00
2015-11-16 19:31:09 +01:00
// We need a reasonable bottom limit here...
2015-11-17 17:57:34 +01:00
if ( this . rowHeight < 5 && this . div . is ( ':visible' ) )
2015-11-16 19:31:09 +01:00
{
this . options . granularity *= 2 ;
return this . _drawTimes ( ) ;
}
2015-05-06 21:03:45 +02:00
// the hour rows
var show = {
5 : [ 0 , 15 , 30 , 45 ] ,
10 : [ 0 , 30 ] ,
15 : [ 0 , 30 ] ,
45 : [ 0 , 15 , 30 , 45 ]
} ;
var html = '' ;
2015-11-16 19:31:09 +01:00
var line _height = parseInt ( this . div . css ( 'line-height' ) ) ;
2015-10-06 01:45:51 +02:00
this . _top _time = 0
for ( var t = 0 , i = 0 ; t < 1440 ; t += granularity , ++ i )
2015-05-06 21:03:45 +02:00
{
2015-10-06 01:45:51 +02:00
html += '<div class="calendar_calTimeRow" style="height: ' + this . rowHeight + 'px;">' ;
2015-05-06 21:03:45 +02:00
// show time for full hours, always for 45min interval and at least on every 3 row
var time = 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 : t / 60 ,
minute : t % 60 ,
seconds : 0 ,
timezone : 0
} ,
2015-11-17 21:19:47 +01:00
{ "ampm" : ( egw . preference ( "timeformat" ) === "12" ) }
2015-05-06 21:03:45 +02:00
) ;
2015-10-06 01:45:51 +02:00
if ( t <= wd _start && t + granularity > wd _start )
{
this . _top _time = this . rowHeight * ( i + 1 + ( wd _start - ( t + granularity ) ) / granularity )
}
2015-05-06 21:03:45 +02:00
var time _label = ( typeof show [ granularity ] === 'undefined' ? t % 60 === 0 : show [ granularity ] . indexOf ( t % 60 ) !== - 1 ) ? time : '' ;
2015-11-16 19:31:09 +01:00
if ( this . rowHeight < line _height )
{
// Rows too small for regular label frequency, use automatic calculation
time _label = ( i % Math . ceil ( line _height / this . rowHeight ) ) === 0 ? time : '' ;
}
2015-06-10 23:51:28 +02:00
html += '<div class="calendar_calTimeRowTime et2_clickable" data-time="' + time . trim ( ) + '" data-hour="' + Math . floor ( t / 60 ) + '" data-minute="' + ( t % 60 ) + '">' + time _label + "</div></div>\n" ;
2015-05-06 21:03:45 +02:00
}
2015-10-06 01:45:51 +02:00
// Set heights in pixels for scrolling
this . scrolling
2015-11-12 02:01:21 +01:00
. append ( '<div class="calendar_calTimeLabels">' + html + '</div>' ) ;
2015-10-06 01:45:51 +02:00
this . days . css ( 'height' , ( this . rowHeight * i ) + 'px' ) ;
// Scroll to start of day
this . scrolling . scrollTop ( this . _top _time ) ;
2015-05-06 21:03:45 +02:00
} ,
2015-11-12 02:01:21 +01:00
_resizeTimes : function ( ) {
var wd _start = 60 * this . options . day _start ;
var wd _end = 60 * this . options . day _end ;
var totalDisplayMinutes = wd _end - wd _start ;
var rowsToDisplay = Math . ceil ( ( totalDisplayMinutes + 60 ) / this . options . granularity ) ;
this . scrolling
. css ( 'height' , ( this . options . height - this . gridHeader . outerHeight ( true ) ) + 'px' ) ;
var new _height = this . scrolling . height ( ) / rowsToDisplay ;
2015-11-13 01:53:23 +01:00
this . rowHeight = new _height ;
var rows = $j ( '.calendar_calTimeRow' , this . scrolling ) . height ( this . rowHeight ) ;
this . days . css ( 'height' , ( this . rowHeight * rows . length ) + 'px' ) ;
$j ( '.calendar_calAddEvent' , this . scrolling ) . height ( this . rowHeight ) ;
2015-11-13 00:10:16 +01:00
// Scroll to start of day
this . _top _time = ( wd _start * this . rowHeight ) / this . options . granularity ;
this . scrolling . scrollTop ( this . _top _time ) ;
2015-11-12 02:01:21 +01:00
} ,
2015-05-06 21:03:45 +02:00
/ * *
* Set up the needed day widgets to correctly display the selected date
* range . First we calculate the needed dates , then we create any needed
* widgets . Existing widgets are recycled rather than discarded .
* /
_drawDays : function ( ) {
2015-10-06 01:45:51 +02:00
this . scrolling . append ( this . days ) ;
2015-05-06 21:03:45 +02:00
// If day list is still empty, recalculate it from start & end date
if ( this . day _list . length === 0 )
{
this . day _list = this . _calculate _day _list ( this . options . start _date , this . options . end _date , this . options . show _weekend ) ;
}
2015-10-28 16:55:10 +01:00
var day _width = ( this . days . width ( ) / this . day _list . length ) ;
2015-11-06 16:52:06 +01:00
if ( ! day _width || ! this . day _list )
{
// Hidden on another tab, or no days for some reason
var dim = egw . getHiddenDimensions ( this . days , false ) ;
day _width = ( dim . w / Math . max ( this . day _list . length , 1 ) ) ;
}
2015-10-21 21:53:19 +02:00
2015-05-06 21:03:45 +02:00
// Create any needed widgets - otherwise, we'll just recycle
// Add any needed day widgets (now showing more days)
2015-10-21 21:53:19 +02:00
var add _index = 0 ;
var before = true ;
2015-05-06 21:03:45 +02:00
while ( this . day _list . length > this . day _widgets . length )
{
2015-10-21 21:53:19 +02:00
var existing _index = this . day _widgets [ add _index ] ? this . day _list . indexOf ( this . day _widgets [ add _index ] . options . date ) : - 1 ;
before = existing _index > add _index ;
2015-05-06 21:03:45 +02:00
var day = et2 _createWidget ( 'calendar-daycol' , {
2015-10-21 21:53:19 +02:00
owner : this . options . owner ,
2015-10-28 16:55:10 +01:00
width : ( before ? 0 : day _width ) + "px"
2015-05-06 21:03:45 +02:00
} , this ) ;
if ( this . isInTree ( ) )
{
day . doLoadingFinished ( ) ;
}
2015-10-21 21:53:19 +02:00
if ( existing _index != - 1 && parseInt ( this . day _list [ add _index ] ) < parseInt ( this . day _list [ existing _index ] ) )
{
this . day _widgets . unshift ( day ) ;
$j ( this . getDOMNode ( day ) ) . prepend ( day . getDOMNode ( day ) ) ;
}
else
{
this . day _widgets . push ( day ) ;
}
add _index ++ ;
2015-05-06 21:03:45 +02:00
}
// Remove any extra day widgets (now showing less)
var delete _index = this . day _widgets . length - 1 ;
2015-10-21 21:53:19 +02:00
before = false ;
2015-05-06 21:03:45 +02:00
while ( this . day _widgets . length > this . day _list . length )
{
// If we're going down to an existing one, just keep it for cool CSS animation
while ( this . day _list . indexOf ( this . day _widgets [ delete _index ] . options . date ) > - 1 )
{
delete _index -- ;
2015-10-21 21:53:19 +02:00
before = true ;
}
// Wait until any animations or other timeouts are done
window . setTimeout ( jQuery . proxy ( function ( ) {
this . free ( ) ;
} , this . day _widgets [ delete _index ] ) , 1000 ) ;
// Widgets that are before our date shrink, after just get pushed out
if ( before )
{
this . day _widgets [ delete _index ] . set _width ( '0px' ) ;
2015-05-06 21:03:45 +02:00
}
this . day _widgets . splice ( delete _index -- , 1 ) ;
}
2015-07-15 18:29:10 +02:00
// Create / update day widgets with dates and data
2015-05-06 21:03:45 +02:00
for ( var i = 0 ; i < this . day _list . length ; i ++ )
{
day = this . day _widgets [ i ] ;
2015-10-06 01:45:51 +02:00
// Position
2015-10-28 16:55:10 +01:00
day . set _left ( ( day _width * i ) + 'px' ) ;
2015-10-06 01:45:51 +02:00
2015-05-06 21:03:45 +02:00
day . set _date ( this . day _list [ i ] , this . value [ this . day _list [ i ] ] || false ) ;
2015-06-10 23:51:28 +02:00
day . set _owner ( this . options . owner ) ;
2015-05-06 21:03:45 +02:00
day . set _id ( this . day _list [ i ] ) ;
2015-10-28 16:55:10 +01:00
day . set _width ( day _width + 'px' ) ;
2015-05-06 21:03:45 +02:00
}
2015-10-21 21:53:19 +02:00
2015-08-06 19:14:20 +02:00
// Don't hold on to value any longer, use the data cache for best info
this . value = { } ;
2015-06-10 23:51:28 +02:00
2015-11-12 02:01:21 +01:00
// Adjust and scroll to start of day
this . _resizeTimes ( ) ;
2015-11-04 22:47:52 +01:00
// Handle not fully visible elements
this . _scroll ( ) ;
2015-10-06 01:45:51 +02:00
2015-05-06 21:03:45 +02:00
// TODO: Figure out how to do this with detached nodes
/ *
var nodes = this . day _col . getDetachedNodes ( ) ;
var supportedAttrs = [ ] ;
this . day _col . getDetachedAttributes ( supportedAttrs ) ;
supportedAttrs . push ( "id" ) ;
for ( var i = 0 ; i < day _count ; i ++ )
{
this . day _col . setDetachedAttributes ( nodes . clone ( ) , )
}
* /
} ,
2015-11-04 22:47:52 +01:00
/ * *
* Update UI while scrolling within the selected time
*
* Toggles out of view indicators and adjusts not visible headers
* @ param { Event } event Scroll event
* /
_scroll : function ( event )
{
// Loop through days, let them deal with it
for ( var day = 0 ; day < this . day _widgets . length ; day ++ )
{
this . day _widgets [ day ] . _out _of _view ( ) ;
}
} ,
2015-05-06 21:03:45 +02:00
/ * *
* Calculate a list of days between start and end date , skipping weekends if
* desired .
*
* @ param { Date | string } start _date Date that et2 _date widget can understand
* @ param { Date | string } end _date Date that et2 _date widget can understand
* @ param { boolean } show _weekend If not showing weekend , Saturday and Sunday
* will not be in the returned list .
*
* @ returns { string [ ] } List of days in Ymd format
* /
_calculate _day _list : function ( start _date , end _date , show _weekend ) {
var day _list = [ ] ;
this . date _helper . set _value ( end _date ) ;
var end = this . date _helper . date . getTime ( ) ;
var i = 1 ;
this . date _helper . set _value ( start _date ) ;
do
{
2015-06-10 23:51:28 +02:00
if ( show _weekend || ! show _weekend && [ 0 , 6 ] . indexOf ( this . date _helper . date . getUTCDay ( ) ) === - 1 || end _date == start _date )
2015-05-06 21:03:45 +02:00
{
day _list . push ( '' + this . date _helper . get _year ( ) + sprintf ( '%02d' , this . date _helper . get _month ( ) ) + sprintf ( '%02d' , this . date _helper . get _date ( ) ) ) ;
}
this . date _helper . set _date ( this . date _helper . get _date ( ) + 1 ) ;
}
// Limit it to 14 days to avoid infinite loops in case something is mis-set,
// though the limit is more based on how wide the screen is
while ( end >= this . date _helper . date . getTime ( ) && i <= 14 )
return day _list ;
} ,
/ * *
* Link the actions to the DOM nodes / widget bits .
*
* @ param { object } actions { ID : { attributes . . } + } map of egw action information
* /
_link _actions : function ( actions )
{
2015-06-10 23:51:28 +02:00
// Get the parent? Might be a grid row, might not. Either way, it is
// just a container with no valid actions
2015-08-05 23:24:07 +02:00
var objectManager = egw _getObjectManager ( this . getInstanceManager ( ) . app , true , 1 ) ;
objectManager = objectManager . getObjectById ( this . getInstanceManager ( ) . uniqueId , 2 ) || objectManager ;
var parent = objectManager . getObjectById ( this . id , 3 ) || objectManager . getObjectById ( this . _parent . id , 3 ) || objectManager ;
if ( ! parent )
{
debugger ;
egw . debug ( 'error' , 'No parent objectManager found' )
return ;
}
2015-06-10 23:51:28 +02:00
for ( var i = 0 ; i < parent . children . length ; i ++ )
{
var parent _finder = jQuery ( this . div , parent . children [ i ] . iface . doGetDOMNode ( ) ) ;
if ( parent _finder . length > 0 )
{
parent = parent . children [ i ] ;
break ;
}
}
2015-07-02 21:31:52 +02:00
2015-06-10 23:51:28 +02:00
// This binds into the egw action system. Most user interactions (drag to move, resize)
// are handled internally using jQuery directly.
2015-08-12 00:30:50 +02:00
var widget _object = this . _actionObject || parent . getObjectById ( this . id ) ;
2015-06-10 23:51:28 +02:00
var aoi = new et2 _action _object _impl ( this , this . getDOMNode ( ) ) ;
2015-07-02 21:31:52 +02:00
2015-06-10 23:51:28 +02:00
aoi . doTriggerEvent = function ( _event , _data ) {
// Determine target node
var event = _data . event || false ;
if ( ! event ) return ;
2015-07-22 01:45:38 +02:00
if ( _data . ui . draggable . hasClass ( 'rowNoEdit' ) ) return ;
/ *
We have to handle the drop in the normal event stream instead of waiting
for the egwAction system so we can get the helper , and destination
* /
if ( event . type === 'drop' )
{
2015-11-04 00:41:55 +01:00
var dropEnd = false ;
var helper = $j ( '.calendar_d-n-d_timeCounter' , _data . ui . helper ) [ 0 ] ;
if ( helper && helper . dropEnd && helper . dropEnd . length >= 1 )
if ( typeof this . dropEnd != 'undefined' && this . dropEnd . length >= 1 )
{
dropEnd = helper . dropEnd [ 0 ] . dataset || false ;
}
this . getWidget ( ) . _event _drop . call ( $j ( '.calendar_d-n-d_timeCounter' , _data . ui . helper ) [ 0 ] , this . getWidget ( ) , event , _data . ui , dropEnd ) ;
2015-07-22 01:45:38 +02:00
}
var drag _listener = function ( event , ui ) {
aoi . getWidget ( ) . _drag _helper ( $j ( '.calendar_d-n-d_timeCounter' , ui . helper ) [ 0 ] , ui . helper [ 0 ] , 0 ) ;
} ;
var time = $j ( '.calendar_d-n-d_timeCounter' , _data . ui . helper ) ;
2015-06-10 23:51:28 +02:00
switch ( _event )
{
2015-07-02 21:31:52 +02:00
// Triggered once, when something is dragged into the timegrid's div
2015-06-10 23:51:28 +02:00
case EGW _AI _DRAG _OVER :
2015-07-22 01:45:38 +02:00
// Listen to the drag and update the helper with the time
// This part lets us drag between different timegrids
_data . ui . draggable . on ( 'drag.et2_timegrid' + widget _object . id , drag _listener ) ;
_data . ui . draggable . on ( 'dragend.et2_timegrid' + widget _object . id , function ( ) {
_data . ui . draggable . off ( 'drag.et2_timegrid' + widget _object . id ) ;
} ) ;
if ( time . length )
{
// The out will trigger after the over, so we count
time . data ( 'count' , time . data ( 'count' ) + 1 ) ;
}
else
{
_data . ui . helper . prepend ( '<div class="calendar_d-n-d_timeCounter" data-count="1"><span></span></div>' ) ;
}
2015-06-10 23:51:28 +02:00
break ;
2015-07-02 21:31:52 +02:00
// Triggered once, when something is dragged out of the timegrid
2015-06-10 23:51:28 +02:00
case EGW _AI _DRAG _OUT :
2015-07-22 01:45:38 +02:00
// Stop listening
_data . ui . draggable . off ( 'drag.et2_timegrid' + widget _object . id ) ;
// Remove any highlighted time squares
2015-06-10 23:51:28 +02:00
$j ( '[data-date]' , this . doGetDOMNode ( ) ) . removeClass ( "ui-state-active" ) ;
2015-07-22 01:45:38 +02:00
// Out triggers after the over, count to not accidentally remove
time . data ( 'count' , time . data ( 'count' ) - 1 ) ;
if ( time . length && time . data ( 'count' ) <= 0 )
{
time . remove ( ) ;
}
2015-06-10 23:51:28 +02:00
break ;
}
} ;
2015-07-02 21:31:52 +02:00
2015-06-10 23:51:28 +02:00
if ( widget _object == null ) {
// Add a new container to the object manager which will hold the widget
// objects
widget _object = parent . insertObject ( false , new egwActionObject (
this . id , parent , aoi ,
2015-08-11 17:35:54 +02:00
this . _actionManager || parent . manager . getActionById ( this . id ) || parent . manager
2015-06-10 23:51:28 +02:00
) ) ;
}
else
{
widget _object . setAOI ( aoi ) ;
}
2015-08-12 00:30:50 +02:00
this . _actionObject = widget _object ;
2015-06-10 23:51:28 +02:00
// Delete all old objects
widget _object . clear ( ) ;
widget _object . unregisterActions ( ) ;
2015-05-06 21:03:45 +02:00
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 ) ;
this . _init _links _dnd ( widget _object . manager , action _links ) ;
widget _object . updateActionLinks ( action _links ) ;
2015-05-06 21:03:45 +02:00
} ,
/ * *
2015-06-10 23:51:28 +02:00
* Automatically add dnd support for linking
2015-05-06 21:03:45 +02:00
* /
2015-06-10 23:51:28 +02:00
_init _links _dnd : function ( mgr , actionLinks ) {
var self = this ;
2015-05-06 21:03:45 +02:00
2015-06-10 23:51:28 +02:00
var drop _action = mgr . getActionById ( 'egw_link_drop' ) ;
var drag _action = mgr . getActionById ( 'egw_link_drag' ) ;
// Check if this app supports linking
2015-08-05 23:24:07 +02:00
if ( ! egw . link _get _registry ( this . dataStorePrefix || 'calendar' , 'query' ) ||
egw . link _get _registry ( this . dataStorePrefix || 'calendar' , 'title' ) )
2015-05-06 21:03:45 +02:00
{
2015-06-10 23:51:28 +02:00
if ( drop _action )
{
drop _action . remove ( ) ;
if ( actionLinks . indexOf ( drop _action . id ) >= 0 )
{
actionLinks . splice ( actionLinks . indexOf ( drop _action . id ) , 1 ) ;
}
}
if ( drag _action )
{
drag _action . remove ( ) ;
if ( actionLinks . indexOf ( drag _action . id ) >= 0 )
{
actionLinks . splice ( actionLinks . indexOf ( drag _action . id ) , 1 ) ;
}
}
2015-05-06 21:03:45 +02:00
return ;
}
2015-06-10 23:51:28 +02:00
// Don't re-add
if ( drop _action == null )
2015-05-06 21:03:45 +02:00
{
2015-06-10 23:51:28 +02:00
// Create the drop action that links entries
2015-07-02 21:31:52 +02:00
drop _action = mgr . addAction ( 'drop' , 'egw_link_drop' , egw . lang ( 'Create link' ) , egw . image ( 'link' ) , function ( action , source , target ) {
2015-06-10 23:51:28 +02:00
// Extract link IDs
var links = [ ] ;
var id = '' ;
for ( var i = 0 ; i < source . length ; i ++ )
{
2015-07-02 21:31:52 +02:00
// Check for no ID (invalid) or same manager (dragging an event)
2015-11-02 21:04:31 +01:00
if ( ! source [ i ] . id ) continue ;
if ( source [ i ] . manager === target . manager )
{
2015-11-04 00:41:55 +01:00
// Find the timegrid, could have dropped on an event
var timegrid = target . iface . getWidget ( ) ;
while ( target . parent && timegrid . instanceOf && ! timegrid . instanceOf ( et2 _calendar _timegrid ) )
{
target = target . parent ;
timegrid = target . iface . getWidget ( ) ;
}
if ( timegrid && timegrid . _drop _data )
2015-11-02 21:04:31 +01:00
{
2015-11-04 00:41:55 +01:00
timegrid . _event _drop . call ( source [ i ] . iface . getDOMNode ( ) , timegrid , null , action . ui , timegrid . _drop _data ) ;
2015-11-02 21:04:31 +01:00
}
2015-11-04 00:41:55 +01:00
timegrid . _drop _data = false ;
2015-11-02 21:04:31 +01:00
// Ok, stop.
return false ;
}
2015-07-02 21:31:52 +02:00
2015-06-10 23:51:28 +02:00
id = source [ i ] . id . split ( '::' ) ;
links . push ( { app : id [ 0 ] == 'filemanager' ? 'link' : id [ 0 ] , id : id [ 1 ] } ) ;
}
2015-07-02 21:31:52 +02:00
if ( links . length && target && target . iface . getWidget ( ) && target . iface . getWidget ( ) . instanceOf ( et2 _calendar _event ) )
2015-06-10 23:51:28 +02:00
{
2015-07-02 21:31:52 +02:00
// Link the entries
egw . json ( self . egw ( ) . getAppName ( ) + ".etemplate_widget_link.ajax_link.etemplate" ,
target . id . split ( '::' ) . concat ( [ links ] ) ,
function ( result ) {
if ( result )
{
this . egw ( ) . message ( 'Linked' ) ;
}
} ,
self ,
true ,
self
) . sendRequest ( ) ;
}
else if ( links . length )
{
// Get date and time
var params = jQuery . extend ( { } , $j ( '.drop-hover[data-date]' , target . iface . getDOMNode ( ) ) [ 0 ] . dataset || { } ) ;
// Add link IDs
var app _registry = egw . link _get _registry ( 'calendar' ) ;
params [ app _registry . add _app ] = [ ] ;
params [ app _registry . add _id ] = [ ] ;
for ( var n in links )
{
params [ app _registry . add _app ] . push ( links [ n ] . app ) ;
params [ app _registry . add _id ] . push ( links [ n ] . id ) ;
}
egw . open ( '' , 'calendar' , 'add' , params ) ;
2015-06-10 23:51:28 +02:00
}
} , true ) ;
2015-05-06 21:03:45 +02:00
}
2015-06-10 23:51:28 +02:00
if ( actionLinks . indexOf ( drop _action . id ) < 0 )
{
actionLinks . push ( drop _action . id ) ;
}
// Accept other links, and files dragged from the filemanager
// This does not handle files dragged from the desktop. They are
// handled by et2_nextmatch, since it needs DOM stuff
if ( drop _action . acceptedTypes . indexOf ( 'link' ) == - 1 )
{
drop _action . acceptedTypes . push ( 'link' ) ;
}
// Don't re-add
if ( drag _action == null )
{
// Create drag action that allows linking
drag _action = mgr . addAction ( 'drag' , 'egw_link_drag' , egw . lang ( 'link' ) , 'link' , function ( action , selected ) {
2015-07-22 01:45:38 +02:00
// Drag helper - list titles.
2015-06-10 23:51:28 +02:00
// As we wanted to have a general defaul helper interface, we return null here and not using customize helper for links
// TODO: Need to decide if we need to create a customized helper interface for links anyway
//return helper;
return null ;
} , true ) ;
}
2015-08-12 00:30:50 +02:00
// The timegrid itself is not draggable, so don't add a link.
// The action is there for the children (events) to use
if ( false && actionLinks . indexOf ( drag _action . id ) < 0 )
2015-06-10 23:51:28 +02:00
{
2015-07-22 01:45:38 +02:00
actionLinks . push ( drag _action . id ) ;
2015-06-10 23:51:28 +02:00
}
drag _action . set _dragType ( 'link' ) ;
} ,
/ * *
* Get all action - links / id ' s of 1. - level actions from a given action object
*
* Here we are only interested in drop events .
*
* @ param actions
* @ returns { Array }
* /
_get _action _links : function ( actions )
{
var action _links = [ ] ;
// TODO: determine which actions are allowed without an action (empty actions)
for ( var i in actions )
{
var action = actions [ i ] ;
if ( action . type == 'drop' )
{
action _links . push ( typeof action . id != 'undefined' ? action . id : i ) ;
}
}
return action _links ;
} ,
2015-05-06 21:03:45 +02:00
/ * *
* Provide specific data to be displayed .
* This is a way to set start and end dates , owner and event data in once call .
*
* @ param { Object [ ] } events Array of events , indexed by date in Ymd format :
* {
* 20150501 : [ ... ] ,
* 20150502 : [ ... ]
* }
* Days should be in order .
*
* /
set _value : function ( events )
{
if ( typeof events !== 'object' ) return false ;
2015-06-10 23:51:28 +02:00
var use _days _sent = true ;
2015-05-06 21:03:45 +02:00
if ( events . owner )
{
this . set _owner ( events . owner ) ;
delete events . owner ;
}
2015-06-10 23:51:28 +02:00
if ( events . start _date )
{
this . set _start _date ( events . start _date ) ;
delete events . start _date ;
use _days _sent = false ;
}
if ( events . end _date )
{
this . set _end _date ( events . end _date ) ;
delete events . end _date ;
use _days _sent = false ;
}
this . value = events || { } ;
if ( use _days _sent )
{
var day _list = Object . keys ( events ) ;
if ( day _list . length )
{
this . set _start _date ( day _list [ 0 ] ) ;
this . set _end _date ( day _list [ day _list . length - 1 ] ) ;
}
}
2015-05-06 21:03:45 +02:00
// Reset and calculate instead of just use the keys so we can get the weekend preference
this . day _list = [ ] ;
} ,
/ * *
* Change the start date
*
* @ param { string | number | Date } new _date New starting date
* @ returns { undefined }
* /
set _start _date : function ( new _date )
{
2015-06-10 23:51:28 +02:00
if ( ! new _date || new _date === null )
{
throw exception ( 'Invalid start date. ' + new _date . toString ( ) ) ;
}
2015-05-06 21:03:45 +02:00
// Use date widget's existing functions to deal
if ( typeof new _date === "object" || typeof new _date === "string" && new _date . length > 8 )
{
this . date _helper . set _value ( new _date ) ;
}
else if ( typeof new _date === "string" )
{
this . date _helper . set _year ( new _date . substring ( 0 , 4 ) ) ;
this . date _helper . set _month ( new _date . substring ( 4 , 6 ) ) ;
this . date _helper . set _date ( new _date . substring ( 6 , 8 ) ) ;
}
var old _date = this . options . start _date ;
this . options . start _date = this . date _helper . getValue ( ) ;
if ( old _date !== this . options . start _date && this . isAttached ( ) )
{
2015-06-10 23:51:28 +02:00
this . invalidate ( true ) ;
2015-05-06 21:03:45 +02:00
}
} ,
/ * *
* Change the end date
*
* @ param { string | number | Date } new _date New end date
* @ returns { undefined }
* /
set _end _date : function ( new _date )
{
2015-06-10 23:51:28 +02:00
if ( ! new _date || new _date === null )
{
throw exception ( 'Invalid end date. ' + new _date . toString ( ) ) ;
}
2015-05-06 21:03:45 +02:00
// Use date widget's existing functions to deal
if ( typeof new _date === "object" || typeof new _date === "string" && new _date . length > 8 )
{
this . date _helper . set _value ( new _date ) ;
}
else if ( typeof new _date === "string" )
{
this . date _helper . set _year ( new _date . substring ( 0 , 4 ) ) ;
this . date _helper . set _month ( new _date . substring ( 4 , 6 ) ) ;
this . date _helper . set _date ( new _date . substring ( 6 , 8 ) ) ;
}
var old _date = this . options . end _date ;
this . options . end _date = this . date _helper . getValue ( ) ;
if ( old _date !== this . options . end _date && this . isAttached ( ) )
{
2015-06-10 23:51:28 +02:00
this . invalidate ( true ) ;
}
} ,
2015-07-15 18:29:10 +02:00
/ * *
* Set which user owns this . Owner is passed along to the individual
* days .
*
* @ param { number | number [ ] } _owner Account ID
* @ returns { undefined }
* /
set _owner : function ( _owner )
{
var old = this . options . owner || 0 ;
2015-08-19 02:08:22 +02:00
this . owner . set _label ( '' ) ;
2015-08-25 02:00:45 +02:00
this . div . removeClass ( 'calendar_TimeGridNoLabel' ) ;
2015-09-03 00:40:38 +02:00
2015-08-12 01:20:24 +02:00
if ( typeof _owner == 'string' && isNaN ( _owner ) )
{
switch ( _owner [ 0 ] )
{
case 'r' :
this . owner . options . application = 'resources' ;
this . owner . set _value ( _owner . substr ( 1 ) ) ;
break ;
}
2015-08-25 02:00:45 +02:00
// Label is empty, but give extra space for the owner name
this . div . removeClass ( 'calendar_TimeGridNoLabel' ) ;
2015-08-12 01:20:24 +02:00
}
2015-08-12 18:37:02 +02:00
else if ( typeof _owner == 'object' && _owner . length )
{
2015-08-19 02:08:22 +02:00
// Don't show owners if more than one, show week number
this . owner . set _value ( '' ) ;
if ( this . options . start _date )
2015-08-12 18:37:02 +02:00
{
2015-08-19 02:08:22 +02:00
this . set _label ( egw . lang ( 'wk' ) + ' ' + app . calendar . date . week _number ( this . options . start _date ) ) ;
2015-08-12 18:37:02 +02:00
}
}
2015-08-12 01:20:24 +02:00
else
{
this . owner . options . application = 'home-accounts'
this . owner . set _value ( typeof _owner == "string" || typeof _owner == "number" ? _owner : jQuery . extend ( [ ] , _owner ) ) ;
2015-09-03 00:40:38 +02:00
$j ( this . getDOMNode ( this . owner ) ) . prepend ( this . owner . getDOMNode ( ) ) ;
2015-08-12 01:20:24 +02:00
}
2015-07-15 18:29:10 +02:00
this . options . owner = _owner ; //this.owner.getValue();
if ( this . isAttached ( ) && (
typeof old == "number" && typeof _owner == "number" && old !== this . options . owner ||
// Array of ids will not compare as equal
( ( typeof old === 'object' || typeof _owner === 'object' ) && old . toString ( ) !== _owner . toString ( ) )
) )
{
this . invalidate ( true ) ;
}
} ,
2015-08-19 02:08:22 +02:00
/ * *
* Set a label for this week
*
* May conflict with owner , which is displayed when there ' s only one owner .
*
* @ param { string } label
* /
set _label : function ( label )
{
this . options . label = label ;
2015-10-28 16:55:10 +01:00
this . _labelContainer . html ( label ) ;
this . gridHeader . prepend ( this . _labelContainer ) ;
2015-08-25 02:00:45 +02:00
// If it's a short label (eg week number), don't give it an extra line
// but is empty, but give extra space for a single owner name
this . div . removeClass ( 'calendar_TimeGridNoLabel' ) ;
this . div . toggleClass ( 'calendar_TimeGridNoLabel' , label . trim ( ) . length < 6 && typeof this . options . owner === 'object' ) ;
2015-08-19 02:08:22 +02:00
} ,
2015-07-15 18:29:10 +02:00
2015-08-19 02:08:22 +02:00
/ * *
* Set how big the time divisions are
*
* @ param { number } minutes
* /
set _granularity : function ( minutes )
{
if ( this . options . granularity != minutes )
{
this . options . granularity = minutes ;
this . _drawTimes ( ) ;
}
} ,
2015-07-15 18:29:10 +02:00
/ * *
* Turn on or off the visibility of weekends
*
* @ param { boolean } weekends
* /
set _show _weekend : function ( weekends )
{
if ( this . options . show _weekend !== weekends )
{
this . options . show _weekend = weekends ? true : false ;
if ( this . isAttached ( ) )
{
this . invalidate ( ) ;
}
}
} ,
2015-06-10 23:51:28 +02:00
/ * *
* Call change handler , if set
* /
change : function ( ) {
if ( this . onchange )
{
if ( typeof this . onchange == 'function' )
{
// Make sure function gets a reference to the widget
var args = Array . prototype . slice . call ( arguments ) ;
if ( args . indexOf ( this ) == - 1 ) args . push ( this ) ;
return this . onchange . apply ( this , args ) ;
} else {
return ( et2 _compileLegacyJS ( this . options . onchange , this , _node ) ) ( ) ;
}
}
} ,
/ * *
* Call event change handler , if set
* /
event _change : function ( event , dom _node ) {
if ( this . onevent _change )
{
var event _data = this . _get _event _info ( dom _node ) ;
2015-11-30 18:21:40 +01:00
var event _widget = this . getWidgetById ( 'event_' + event _data . app _id ) ;
2015-06-10 23:51:28 +02:00
et2 _calendar _event . recur _prompt ( event _data , jQuery . proxy ( function ( button _id , event _data ) {
// No need to continue
if ( button _id === 'cancel' ) return false ;
if ( typeof this . onevent _change == 'function' )
{
// Make sure function gets a reference to the widget
var args = Array . prototype . slice . call ( arguments ) ;
if ( args . indexOf ( event _widget ) == - 1 ) args . push ( event _widget ) ;
// Put button ID in event
event . button _id = button _id ;
return this . onevent _change . apply ( this , [ event , event _widget , button _id ] ) ;
} else {
return ( et2 _compileLegacyJS ( this . options . onevent _change , event _widget , dom _node ) ) ( ) ;
}
} , this ) ) ;
}
return false ;
} ,
2015-05-06 21:03:45 +02:00
get _granularity : function ( )
{
// get option, or user's preference
if ( typeof this . options . granularity === 'undefined' )
{
this . options . granularity = egw . preference ( 'interval' , 'calendar' ) || 30 ;
}
return parseInt ( this . options . granularity ) ;
} ,
/ * *
* Click handler calling custom handler set via onclick attribute to this . onclick
*
* This also handles all its own actions , including navigation . If there is
* an event associated with the click , it will be found and passed to the
* onclick function .
*
* @ param { Event } _ev
* @ returns { boolean }
* /
click : function ( _ev )
{
var result = true ;
// Is this click in the event stuff, or in the header?
2015-10-06 01:45:51 +02:00
if ( $j ( _ev . target ) . hasClass ( '.calendar_calEvent' ) || $j ( _ev . target ) . parents ( '.calendar_calEvent' ) . length )
2015-05-06 21:03:45 +02:00
{
// Event came from inside, maybe a calendar event
var event = this . _get _event _info ( _ev . originalEvent . target ) ;
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 ) ;
}
if ( event . id && result && ! this . options . disabled && ! this . options . readonly )
{
2015-06-10 23:51:28 +02:00
et2 _calendar _event . recur _prompt ( event ) ;
2015-05-06 21:03:45 +02:00
return false ;
}
return result ;
}
2015-09-03 00:40:38 +02:00
else if ( this . gridHeader . is ( _ev . target ) && _ev . target . dataset )
{
app . calendar . update _state ( jQuery . extend ( { view : 'week' } , _ev . target . dataset ) ) ;
}
2015-10-06 01:45:51 +02:00
else if ( this . dayHeader . has ( _ev . target ) . length )
{
// Click on a day header - let day deal with it
// First child is a selectAccount
for ( var i = 1 ; i < this . _children . length ; i ++ )
{
2015-10-29 22:53:47 +01:00
if ( this . _children [ i ] . header && (
this . _children [ i ] . header . has ( _ev . target ) . length || this . _children [ i ] . header . is ( _ev . target ) )
)
2015-10-06 01:45:51 +02:00
{
return this . _children [ i ] . click ( _ev ) ;
}
}
}
2015-06-10 23:51:28 +02:00
else if ( _ev . target . dataset . date )
2015-05-06 21:03:45 +02:00
{
// Default handler to open a new event at the selected time
2015-11-13 18:07:48 +01:00
var options = {
2015-05-06 21:03:45 +02:00
date : _ev . target . dataset . date || this . day _list [ 0 ] ,
hour : _ev . target . dataset . hour || this . options . day _start ,
minute : _ev . target . dataset . minute || 0
2015-11-13 18:07:48 +01:00
} ;
if ( this . options . owner != app . calendar . state . owner )
{
options . owner = this . options . owner ;
}
this . egw ( ) . open ( null , 'calendar' , 'add' , options , '_blank' ) ;
2015-05-06 21:03:45 +02:00
return false ;
}
} ,
_get _event _info : function ( dom _node )
{
// Determine as much relevant info as can be found
var event _node = $j ( dom _node ) . closest ( '[data-id]' , this . div ) [ 0 ] ;
var day _node = $j ( event _node ) . closest ( '[data-date]' , this . div ) [ 0 ] ;
return jQuery . extend ( {
event _node : event _node ,
day _node : day _node ,
} ,
event _node ? event _node . dataset : { } ,
day _node ? day _node . dataset : { }
) ;
} ,
2015-06-10 23:51:28 +02:00
/ * *
* Get time from position
*
* @ param { number } x
* @ param { number } y
* @ returns { DOMNode [ ] } time node ( s ) for the given position
* /
_get _time _from _position : function ( x , y ) {
x = Math . round ( x ) ;
y = Math . round ( y ) ;
2015-11-10 19:35:24 +01:00
var nodes = $j ( '.calendar_calAddEvent[data-hour],.calendar_calDayColHeader' , this . div ) . removeClass ( 'drop-hover' ) . filter ( function ( ) {
2015-06-10 23:51:28 +02:00
var offset = $j ( this ) . offset ( ) ;
var range = { x : [ offset . left , offset . left + $j ( this ) . outerWidth ( ) ] , y : [ offset . top , offset . top + $j ( this ) . outerHeight ( ) ] } ;
var i = ( x >= range . x [ 0 ] && x <= range . x [ 1 ] ) && ( y >= range . y [ 0 ] && y <= range . y [ 1 ] ) ;
return i ;
} ) . addClass ( "drop-hover" ) ;
return nodes ;
2015-05-06 21:03:45 +02:00
} ,
2015-06-10 23:51:28 +02: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 ) {
_attrs . push ( 'start_date' , 'end_date' ) ;
} ,
getDetachedNodes : function ( ) {
return [ this . getDOMNode ( ) ] ;
} ,
setDetachedAttributes : function ( _nodes , _values ) {
this . div = $j ( _nodes [ 0 ] ) ;
if ( _values . start _date )
{
this . set _start _date ( _values . start _date ) ;
}
if ( _values . end _date )
{
this . set _end _date ( _values . end _date ) ;
}
} ,
// Resizable interface
2015-10-06 01:45:51 +02:00
resize : function ( )
2015-05-06 21:03:45 +02:00
{
2015-11-05 21:56:13 +01:00
if ( ! this . div . is ( ':visible' ) )
{
return ;
}
2015-10-06 01:45:51 +02:00
// We expect the timegrid to be in a table with 0 or more other timegrids,
// 1 per row. We want each timegrid to be as large as possible, but space
// shared equally. Height can't be set to a percentage on the rows, because
// that doesn't work.
// Find the table
var table = this . div . parentsUntil ( 'table' ) . parent ( ) ;
// How many rows?
var rowCount = table . children ( 'tr' ) . length ;
// Take the whole tab height
this . options . height = Math . floor ( Math . min ( $j ( this . getInstanceManager ( ) . DOMContainer ) . height ( ) , $j ( this . getInstanceManager ( ) . DOMContainer ) . parent ( ) . innerHeight ( ) ) / rowCount ) ;
this . options . height = Math . floor ( ( egw . getHiddenDimensions ( this . getInstanceManager ( ) . DOMContainer ) . h ) / rowCount ) ;
this . options . height -= 2 * ( ( this . div . outerWidth ( true ) - this . div . innerWidth ( ) ) + parseInt ( this . div . parent ( ) . css ( 'padding-top' ) ) ) ;
if ( this . options . height + "px" != this . div . css ( 'height' ) )
{
this . div . css ( 'height' , this . options . height ) ;
// Re-do time grid
2015-11-12 02:01:21 +01:00
this . _drawGrid ( ) ;
2015-11-13 00:10:16 +01:00
// Just re-did everything, no need to do more
return ;
}
// Try to resize width, though animations cause problems
var day _width = ( $j ( this . getInstanceManager ( ) . DOMContainer ) . width ( ) - ( this . div . innerWidth ( ) - this . days . innerWidth ( ) ) ) / this . day _list . length ;
// update day widgets
for ( var i = 0 ; i < this . day _list . length ; i ++ )
{
var day = this . day _widgets [ i ] ;
// Position
day . set _left ( ( day _width * i ) + 'px' ) ;
day . set _width ( day _width + 'px' ) ;
2015-10-06 01:45:51 +02:00
}
2015-05-06 21:03:45 +02:00
}
} ) ;
et2 _register _widget ( et2 _calendar _timegrid , [ "calendar-timegrid" ] ) ;