diff --git a/UI/WebServerResources/js/Scheduler/sgCalendarDayBlockGhost.directive.js b/UI/WebServerResources/js/Scheduler/sgCalendarDayBlockGhost.directive.js
new file mode 100644
index 000000000..e166e0413
--- /dev/null
+++ b/UI/WebServerResources/js/Scheduler/sgCalendarDayBlockGhost.directive.js
@@ -0,0 +1,205 @@
+/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+(function() {
+ 'use strict';
+
+ /*
+ * sgCalendarDayBlockGhost - An event ghost block to be displayed while dragging an event block. Each day of the
+ * calendar's view is associated to a ghost block.
+ * @memberof SOGo.SchedulerUI
+ * @restrict element
+ *
+ * @example:
+
+
+ */
+ sgCalendarDayBlockGhost.$inject = ['$rootScope', '$timeout', 'CalendarSettings', 'Calendar', 'Component'];
+ function sgCalendarDayBlockGhost($rootScope, $timeout, CalendarSettings, Calendar, Component) {
+ return {
+ restrict: 'E',
+ require: ['^sgCalendarDay', '^sgCalendarScrollView'],
+ replace: true,
+ template: [
+ '
',
+ '
',
+ '
{{ block.component.summary }}',
+ ' ',
+ // Component has an alarm
+ ' ',
+ // Component is confidential
+ ' ',
+ // Component is private
+ ' ',
+ ' ',
+ '
',
+ '
',
+ '
{{ startHour }}
',
+ '
{{ endHour }}
',
+ '
'
+ ].join(''),
+ link: link
+ };
+
+ function link(scope, iElement, attrs, ctrls) {
+ var domElement, calendarDayCtrl, scrollViewCtrl;
+
+ domElement = iElement[0];
+ calendarDayCtrl = ctrls[0];
+ scrollViewCtrl = ctrls[1];
+
+ // Listen on drag gestures
+ var deregisterDragStart = $rootScope.$on('calendar:dragstart', initGhost);
+ var deregisterDrag = $rootScope.$on('calendar:drag', updateGhost);
+ var deregisterDragEnd = $rootScope.$on('calendar:dragend', hideGhost);
+
+ // Deregister listeners on destroy
+ scope.$on('$destroy', function() {
+ deregisterDragStart();
+ deregisterDrag();
+ deregisterDragEnd();
+ });
+
+ function initGhost() {
+ // Expose ghost block to the scope
+ scope.block = Component.$ghost;
+ // Set background color
+ iElement.addClass('bg-folder' + scope.block.component.pid);
+ }
+
+ function hideGhost() {
+ // Remove background color
+ _.forEachRight(domElement.classList, function(c) {
+ if (/^bg-folder/.test(c))
+ iElement.removeClass(c);
+ });
+ // Hide ghost
+ iElement.addClass('ng-hide');
+ }
+
+ function updateGhost() {
+ // From SOGoEventDragGhostController._updateGhosts
+ var showGhost, isAllDay, originalDay, currentDay, wasOtherBlock,
+ start, duration, durationLeft, maxDuration, enableTransition;
+
+ showGhost = false;
+ enableTransition = function() {
+ iElement.removeClass('sg-event--notransition');
+ };
+
+ if (Calendar.$view && Calendar.$view.type == scrollViewCtrl.type) {
+ // The view of the dragging block is the scrolling view of this ghost block
+
+ isAllDay = scope.block.component.c_isallday;
+ originalDay = scope.block.pointerHandler.originalEventCoordinates.dayNumber;
+ currentDay = scope.block.pointerHandler.currentEventCoordinates.dayNumber;
+ start = scope.block.pointerHandler.currentEventCoordinates.start;
+ durationLeft = scope.block.pointerHandler.currentEventCoordinates.duration;
+ maxDuration = CalendarSettings.EventDragDayLength - start;
+
+ if (angular.isUndefined(durationLeft))
+ return;
+
+ duration = durationLeft;
+ if (duration > maxDuration)
+ duration = maxDuration;
+
+ delete scope.startHour;
+ delete scope.endHour;
+
+ if (currentDay > -1 && currentDay == calendarDayCtrl.dayNumber) {
+ // This ghost block (day) is the first of the dragging event
+ showGhost = true;
+ if (!isAllDay) {
+ // Show start hour and set the vertical position
+ scope.startHour = getStartTime(start);
+ wasOtherBlock = parseInt(iElement.css('top')) === 0;
+ if (wasOtherBlock)
+ iElement.addClass('sg-event--notransition');
+ iElement.css('top', (start * Calendar.$view.quarterHeight) + 'px');
+ iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px');
+ if (wasOtherBlock)
+ $timeout(enableTransition);
+ }
+ iElement.removeClass('fg-folder' + scope.block.component.pid);
+ iElement.removeClass('sg-event--ghost--last');
+ iElement.addClass('sg-event--ghost--first');
+ }
+
+ durationLeft -= duration;
+ currentDay++;
+
+ // Search a subsequent block that matches the current ghost's day
+ while (!showGhost && durationLeft && currentDay <= calendarDayCtrl.dayNumber) {
+ duration = durationLeft;
+ if (duration > CalendarSettings.EventDragDayLength)
+ duration = CalendarSettings.EventDragDayLength;
+ if (currentDay > -1 && currentDay == calendarDayCtrl.dayNumber) {
+ // The dragging event overlaps this current ghost's day
+ showGhost = true;
+ if (!isAllDay) {
+ wasOtherBlock = parseInt(iElement.css('top')) !== 0;
+ if (wasOtherBlock)
+ iElement.addClass('sg-event--notransition');
+ // Set the height
+ iElement.css('top', '0px');
+ iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px');
+ if (wasOtherBlock)
+ $timeout(enableTransition);
+ }
+ iElement.removeClass('sg-event--ghost--first');
+ iElement.removeClass('sg-event--ghost--last');
+ // Trick for all-day events: set the foreground color to the background color so the event's title
+ // is not visible but the div size remains identical.
+ iElement.addClass('fg-folder' + scope.block.component.pid);
+ }
+ durationLeft -= duration;
+ currentDay++;
+ start = 0;
+ }
+ if (!durationLeft) {
+ // Reached last ghost block
+ if (isAllDay) {
+ iElement.addClass('sg-event--ghost--last');
+ }
+ else {
+ // Set the end date
+ scope.endHour = getEndTime(start, duration);
+ }
+ }
+ }
+
+ if (showGhost)
+ iElement.removeClass('ng-hide');
+ else
+ iElement.addClass('ng-hide');
+ }
+
+ function quartersToHM(quarters) {
+ var minutes, hours, mins;
+
+ minutes = quarters * 15;
+ hours = Math.floor(minutes / 60);
+ if (hours < 10)
+ hours = "0" + hours;
+ mins = minutes % 60;
+ if (mins < 10)
+ mins = "0" + mins;
+
+ return "" + hours + ":" + mins;
+ }
+
+ function getStartTime(start) {
+ return quartersToHM(start);
+ }
+
+ function getEndTime(start, duration) {
+ var end = (start + duration) % CalendarSettings.EventDragDayLength;
+ return quartersToHM(end);
+ }
+ }
+ }
+
+ angular
+ .module('SOGo.SchedulerUI')
+ .directive('sgCalendarDayBlockGhost', sgCalendarDayBlockGhost);
+})();