/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ (function() { 'use strict'; /* * sgCalendarBlock - Applied to an event ghost block to be displayed while dragging an event block. Each day of the * calendar's view must have a ghost block. * @memberof SOGo.SchedulerUI * @restrict attribute * * @example: / */ sgCalendarGhost.$inject = ['$rootScope', '$timeout', 'CalendarSettings', 'Calendar', 'Component']; function sgCalendarGhost($rootScope, $timeout, CalendarSettings, Calendar, Component) { return { restrict: 'A', require: ['^sgCalendarDay', '^sgCalendarScrollView'], link: link }; function link(scope, iElement, attrs, ctrls) { var domElement, calendarDayCtrl, scrollViewCtrl, calendarNumber, originalCalendarNumber; domElement = iElement[0]; calendarDayCtrl = ctrls[0]; scrollViewCtrl = ctrls[1]; calendarNumber = -1; iElement.addClass('sg-event--ghost md-whiteframe-3dp ng-hide'); // 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() { var pid, calendarData, userState; // Expose ghost block to the scope scope.block = Component.$ghost; calendarData = calendarDayCtrl.calendarData(); if (calendarData) { // A calendar is associated to the day; this is a special multicolumn day view calendarNumber = calendarData.index; pid = calendarData.pid; originalCalendarNumber = scope.block.pointerHandler.originalCalendar.index; } if (!pid) pid = scope.block.component.pid; // Add class for user's participation state userState = scope.block.component.blocks[0].userState; if (userState) iElement.addClass('sg-event--' + userState); // Set background color iElement.addClass('bg-folder' + 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, isRelative, isAllDay, currentDay, start, duration, durationLeft, maxDuration; showGhost = false; if (Calendar.$view && Calendar.$view.type == scrollViewCtrl.type) { // The view of the dragging block is the scrolling view of this ghost block isRelative = scrollViewCtrl.type === 'multiday-allday'; isAllDay = scope.block.component.c_isallday; 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; if (currentDay > -1 && // pointer is inside viewport ((calendarNumber < 0 && // day is not associated to a calendar currentDay == calendarDayCtrl.dayNumber) || // pointer is inside ghost's day currentDay == calendarNumber && // pointer is inside ghost's calendar (originalCalendarNumber == calendarNumber || // still inside original calendar !scope.block.component.isException) // not an exception, event can be moved to a // different calendar )) { // This ghost block (day) is the first of the dragging event showGhost = true; if (!isRelative) { if (!isAllDay) // Show start hour and set the vertical position scope.block.startHour = getStartTime(start); // Set the height if (Calendar.$view.quarterHeight) { iElement.css('top', (start * Calendar.$view.quarterHeight) + 'px'); iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px'); } else iElement.css('top', Calendar.$view.topOffset + 'px'); } iElement.removeClass('fg-folder' + scope.block.component.pid); iElement.removeClass('sg-event--ghost--last'); iElement.addClass('sg-event--ghost--first'); scope.block.isFirst = true; } 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 (!isRelative) { iElement.css('top', Calendar.$view.topOffset + 'px'); // Set the height if (Calendar.$view.quarterHeight) iElement.css('height', (duration * Calendar.$view.quarterHeight) + 'px'); } 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 (isRelative) { iElement.addClass('sg-event--ghost--last'); } else if (!isAllDay) { // Set the end date scope.block.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('sgCalendarGhost', sgCalendarGhost); })();