/** * This class contains Stud.IP specific code for the fullcalendar package. */ import { Calendar } from '@fullcalendar/core'; import deLocale from '@fullcalendar/core/locales/de'; import enLocale from '@fullcalendar/core/locales/en-gb'; import interactionPlugin from '@fullcalendar/interaction'; import { Draggable } from '@fullcalendar/interaction'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import resourceCommonPlugin from '@fullcalendar/resource-common'; import resourceTimeGridPlugin from '@fullcalendar/resource-timegrid'; import resourceTimelinePlugin from '@fullcalendar/resource-timeline'; import { jsPDF } from 'jspdf'; import html2canvas from 'html2canvas'; import Responsive from "./responsive"; Date.prototype.getWeekNumber = function () { var d = new Date(Date.UTC(this.getFullYear(), this.getMonth(), this.getDate())); var dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1)); return Math.ceil((((d - yearStart) / 86400000) + 1)/7); }; function pad(what, length = 2, char = '0') { let padding = new Array(length + 1).join(char); return `${padding}${what}`.substr(-length); } class Fullcalendar { /** * The initialisation method. It loads the JS files for fullcalendar * in case they are not loaded and sets up a fullcalendar instance * for the nodes specified in the parameter node. * * @param DOMElement|string node The node which shall have a full calendar. * This must either be a DOMElement or a string * containing a CSS selector. */ static init(node, fullcalendar_options = null) { // Convert css selector to actual dom element node = $(node)[0]; if (!node) { //We need a CSS selector or a node! return; } if (document.getElementById('external-events')) { new Draggable(document.getElementById('external-events'), { itemSelector: '.fc-event', eventData (eventEl) { return { title: eventEl.dataset.eventTitle, duration: eventEl.dataset.eventDuration, course_id: eventEl.dataset.eventCourse, tooltip: eventEl.dataset.eventTooltip, studip_api_urls: {drop: eventEl.dataset.eventDropUrl}, studip_view_urls: {edit: eventEl.dataset.eventDetailsUrl} }; } }); } var calendar = new Calendar(node, fullcalendar_options); node.calendar = calendar; calendar.render(); return calendar; } /** * Converts semester events to the default fullcalendar event format. * The begin and end date are converted to fit into the current week. */ static convertSemesterEvents(event_data, fake_week_start = Date()) { if (!event_data) { return {}; } var start = String(event_data.start).split('T'); var end = String(event_data.end).split('T'); //start and end must be transformed to the current week. //Therefore, we need the ISO weekdays for begin and end. var fake_start = new Date(fake_week_start); fake_start.setHours(12); fake_start.setMinutes(0); fake_start.setSeconds(0); var fake_end = new Date(fake_week_start); fake_end.setHours(12); fake_end.setMinutes(0); fake_end.setSeconds(0); //Calculcate the week day of the current week for the event //from the current day and convert sunday to ISO format var start_day_diff = fake_start.getDay() || 7; var end_day_diff = fake_end.getDay() || 7; start_day_diff = start_day_diff - event_data.studip_weekday_begin; end_day_diff = end_day_diff - event_data.studip_weekday_end; fake_start = new Date( fake_start.getTime() - start_day_diff * 24 * 60 * 60 * 1000 ); fake_end = new Date( fake_end.getTime() - end_day_diff * 24 * 60 * 60 * 1000 ); //Output the modified begin and end date in the correct ISO format: event_data.start =`${fake_start.getFullYear()}-${pad(fake_start.getMonth() + 1)}-${pad(fake_start.getDate())}T${start[1]}`; event_data.end = `${fake_end.getFullYear()}-${pad(fake_end.getMonth() + 1)}-${pad(fake_end.getDate())}T${end[1]}`; return event_data; } static createSemesterCalendarFromNode(node, additional_config = {}) { if (!node) { //Ain't no fullcalendar when the node's gone! return; } var config = $.extend( {}, $(node).data('config') || {}, additional_config ); if (Array.isArray(config.eventSources)) { config.eventSources = config.eventSources.map((s) => { if (s.url !== undefined) { return s; } }); } return this.createFromNode(node, config); } static defaultResizeEventHandler(info) { if (!info.event.durationEditable || !info.view.viewSpec.options.editable) { //Read-only events cannot be resized! info.revert(); return; } if (info.event.extendedProps.studip_api_urls.resize) { $.post({ url: info.event.extendedProps.studip_api_urls.resize, async: false, data: { begin: this.toRFC3339String(info.event.start), end: this.toRFC3339String(info.event.end) } }).fail(info.revert); } else if (info.event.extendedProps.studip_api_urls.resize_dialog) { STUDIP.Dialog.fromURL( info.event.extendedProps.studip_api_urls.resize_dialog, { data: { begin: this.toRFC3339String(info.event.start), end: this.toRFC3339String(info.event.end) } } ); } } static downloadPDF(format = 'landscape', withWeekend = false) { $('*[data-fullcalendar="1"]').each(function () { if (this.calendar != undefined) { $(this).addClass('print-view').toggleClass('without-weekend', !withWeekend); var title = $(this).data('title'); let print_title = $('