"use strict"; (function () { 'use strict'; angular.module('changeModule') .directive('calendarBookView', ['$timeout', '$log', 'events', '$rootScope', 'dateFilter', '$locale', function ($timeout, $log, events, $rootScope, dateFilter, $locale) { return { restrict: 'E', templateUrl: 'views/change/calendar-book-view.html', require: '^calendar', scope: {}, link: function (scope, element, attrs, calendarCtrl) { scope.model = calendarCtrl.model; var timelineDays = 5, zoomLevel = 3, maxTimelineDays = 20, halfTimelineDays = Math.floor(timelineDays / 2), timelineStart = scope.model.context.scheduledStartDate ? new DayPilot.Date(scope.model.context.scheduledStartDate, true).addDays(-halfTimelineDays) : new DayPilot.Date(); var zoomLevels = [ { cellDuration: 15, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Hour" }, { groupBy: "Cell" } ], minTimelineDays: 5, formats: ['dayAndDate', 'shortestTime'] }, { cellDuration: 30, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Hour" }, { groupBy: "Cell" } ], minTimelineDays: 5, formats: ['dayAndDate', 'shortestTime'] }, { cellDuration: 60, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Cell" } ], minTimelineDays: 5, formats: ['dayAndDate', 'shortestTime'] }, { cellDuration: 120, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Cell" } ], minTimelineDays: 5, formats: ['dayAndDate', 'shortestTime'] }, { cellDuration: 240, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Cell" } ], minTimelineDays: 10, formats: ['dayAndDate', 'shortestTime'] }, { cellDuration: 360, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Cell" } ], minTimelineDays: 14, formats: ['dayAndDate', 'shortestTime'] }, { cellDuration: 480, timeHeaders: [ { groupBy: "Day" }, { groupBy: "Cell" } ], minTimelineDays: 18, formats: ['dayAndDate', 'shortestTime'] } ]; scope.config = { heightSpec: "Parent100Pct", days: timelineDays, startDate: timelineStart, scale: "CellDuration", cellDuration: zoomLevels[zoomLevel].cellDuration, timeHeaders: zoomLevels[zoomLevel].timeHeaders, theme: 'bookview', cssClassPrefix: 'bookview', resources: generateResourceArray(25), onIncludeTimeCell: function (args) { var Sunday = 0; var Saturday = 6; if (!scope.model.showWeekends && (args.cell.start.getDayOfWeek() === Sunday || args.cell.start.getDayOfWeek() === Saturday)) { args.cell.visible = false; } }, onBeforeTimeHeaderRender: function (args) { var format = zoomLevels[zoomLevel].formats[args.header.level]; if (args.header.level === 0 && window.myitsmLocale === 'en' && $locale.id.toLowerCase() !== 'en-us') { format = 'shortDate'; } if (format) { var htmlText = dateFilter(args.header.start.toDateLocal(), format); if (args.header.level === 0 && window.myitsmLocale === 'en' && $locale.id.toLowerCase() !== 'en-us') { if ($locale.id.toLowerCase() === 'en-ca') { //remove yy- at the beginning htmlText = htmlText.substring(htmlText.indexOf('-') + 1); } else if ($locale.id.toLowerCase() === 'en-za') { //remove yy/ at the beginning htmlText = htmlText.substring(htmlText.indexOf('/') + 1); } else { //remove /yy at the end htmlText = htmlText.substring(0, htmlText.lastIndexOf('/')); } } args.header.html = htmlText; args.header.toolTip = htmlText; } }, onTimeRangeSelected: function (args) { scope.changeBookView.clearSelection(); if (args.resource === scope.model.currentRequestResourceId) { scope.model.context.scheduledStartDate = args.start.toDateLocal(); scope.model.context.scheduledEndDate = args.end.toDateLocal(); scope.$apply(); } }, onEventMove: function (args) { if (args.newResource !== scope.model.currentRequestResourceId) { args.preventDefault(); } }, onEventMoved: function (args) { if (args.newResource === scope.model.currentRequestResourceId) { scope.model.context.scheduledStartDate = args.newStart.toDateLocal(); scope.model.context.scheduledEndDate = args.newEnd.toDateLocal(); scope.$apply(); } }, onEventResize: function (args) { if (args.e.resource() !== scope.model.currentRequestResourceId) { args.preventDefault(); } }, onEventResized: function (args) { if (args.e.resource() === scope.model.currentRequestResourceId) { scope.model.context.scheduledStartDate = args.newStart.toDateLocal(); scope.model.context.scheduledEndDate = args.newEnd.toDateLocal(); scope.$apply(); } }, onEventClicked: calendarCtrl.onEventClicked }; scope.$watch('model.showWeekends', function () { updateBookView(); }); scope.$watch('model.expanded', function () { if (scope.model.expanded) { $timeout(function () { updateBookView(); }, 500); } }); scope.$watch('model.context.scheduledStartDate', function () { enqueueRescheduleChangeRequest(); }); scope.$watch('model.context.scheduledEndDate', function () { enqueueRescheduleChangeRequest(); }); scope.$watch('model.selectedCalendarView', function () { if (scope.model.selectedCalendarView === scope.model.calendarViews[0]) { $timeout(function () { updateBookView(); positionTimeline(); }); } }); var off = $rootScope.$on(events.CHANGE_COLLISION_STATUS_CHANGED, function (event, addressedCollisions) { calendarCtrl.applyAddressedCollisions(addressedCollisions); displayCollisions(); }); scope.$on('$destroy', function () { off(); }); scope.$watch('model.context', function () { calendarCtrl.updateCalendarHandling(scope.changeBookView); }, true); scope.$watch('model.collisions', function () { displayCollisions(); }, true); scope.$watch('model.calendarTypes', function () { displayCollisions(); }, true); scope.$on(events.ZOOM_LEVEL_CHANGED, function (event, increment) { applyZoomLevel(increment); }); function displayCollisions() { calendarCtrl.displayCollisions({ calendar: scope.changeBookView, resources: scope.config.resources, outagesIndex: computeOutagesIndex(), businessEventsIndex: computeBusinessEventsIndex(), changeCollisionCss: 'bookview_event_collision', changeNonCollisionCss: 'bookview_event_secondary', outageCollisionCss: 'bookview_event_outage_collision', outageNonCollisionCss: 'bookview_event_outage_secondary', businessEventCollisionCss: 'bookview_event_business_event_collision', businessEventNonCollisionCss: 'bookview_event_business_event_secondary', updateCalendar: function () { updateBookView(true); } }); } function computeOutagesIndex() { return (calendarCtrl.changeRequestFilterSelected() ? calendarCtrl.visibleChangeRequests(scope.changeBookView).length : 0) + 1; } function computeBusinessEventsIndex() { var outageIndex = computeOutagesIndex(); return calendarCtrl.outageFilterSelected() ? outageIndex + 1 : outageIndex; } function updateBookView(forceUpdate) { var start = scope.model.context.scheduledStartDate, end = scope.model.context.scheduledEndDate, update = !!(start && end); if (update) { $log.log("update book view: start=" + start + "(" + start.getTime() + "), end=" + end + "(" + end.getTime() + ")"); scope.changeBookView.collisionStart = new DayPilot.Date(start, true); scope.changeBookView.collisionEnd = new DayPilot.Date(end, true); } else { scope.changeBookView.collisionStart = null; scope.changeBookView.collisionEnd = null; } if (update || forceUpdate) { scope.changeBookView.update(); } } function positionTimeline() { var start = scope.model.context.scheduledStartDate ? scope.model.context.scheduledStartDate : new Date(); startTimelineOn(start); } function startTimelineOn(date) { if (date instanceof Date) { date = new DayPilot.Date(date, true); } date = date.addHours(-date.getHours()).addMinutes(-date.getMinutes()); scope.changeBookView.scrollTo(date); } function enqueueRescheduleChangeRequest() { $timeout(function () { rescheduleChangeRequest(scope.model.context.scheduledStartDate, scope.model.context.scheduledEndDate); }); } function rescheduleChangeRequest(start, end) { if (calendarCtrl.rescheduleChangeRequest(scope.changeBookView, start, end) && !extendTimeline(start, end)) { updateBookView(); } } function extendTimeline(start, end) { if (!start || !end) { return false; } if (start instanceof Date) { start = new DayPilot.Date(start, true); } if (end instanceof Date) { end = new DayPilot.Date(end, true); } var edgeDays = 1, bufferDays = 2, timelineStartMoment = moment(timelineStart.toDateLocal()).startOf("day"), timelineEnd = timelineStart.addDays(timelineDays), timelineEndMoment = moment(timelineEnd.toDateLocal()).startOf("day"), startMoment = moment(start.toDateLocal()).startOf("day"), endMoment = moment(end.toDateLocal()).startOf("day"), timelineModified = false; var startDiff = startMoment.diff(timelineStartMoment, "days"); var endDiff = timelineEndMoment.diff(endMoment, "days"); if (startDiff < edgeDays || endDiff <= edgeDays) { timelineStartMoment = startMoment.subtract(bufferDays, "days"); timelineEndMoment = endMoment.add(bufferDays + 1, "days"); // +1 is required to have full bufferDays number after end date, otherwise the actual number would be 1 day less timelineDays = timelineEndMoment.diff(timelineStartMoment, "days"); timelineDays = Math.min(timelineDays, maxTimelineDays); timelineDays = Math.max(timelineDays, zoomLevels[zoomLevel].minTimelineDays); timelineStart = new DayPilot.Date(timelineStartMoment.toDate()); timelineModified = true; } if (timelineModified) { $log.log("book view timeline extended: start=" + timelineStart + " days=" + timelineDays); scope.changeBookView.days = scope.config.days = timelineDays; scope.changeBookView.startDate = scope.config.startDate = timelineStart; updateBookView(); positionTimeline(); } return timelineModified; } function generateResourceArray(count) { count = count || 10; var resources = [ { name: "", id: scope.model.currentRequestResourceId } ]; for (var i = 1; i < count; i++) { resources.push({ name: "", id: i.toString() }); } return resources; } function applyZoomLevel(increment) { zoomLevel += increment; if (zoomLevel < 0 || zoomLevel >= zoomLevels.length) { zoomLevel -= increment; return; } scope.model.zoomInDisabled = zoomLevel === 0; scope.model.zoomOutDisabled = zoomLevel === zoomLevels.length - 1; $log.log("book view timeline zoomed to " + zoomLevel + " level."); scope.changeBookView.cellDuration = scope.config.cellDuration = zoomLevels[zoomLevel].cellDuration; scope.changeBookView.timeHeaders = scope.config.timeHeaders = zoomLevels[zoomLevel].timeHeaders; scope.changeBookView.days = scope.config.days = Math.max(timelineDays, zoomLevels[zoomLevel].minTimelineDays); updateBookView(); } positionTimeline(); } }; } ]); })();