aboutsummaryrefslogtreecommitdiff
path: root/resources/assets/javascripts/lib/fullcalendar.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/assets/javascripts/lib/fullcalendar.js')
-rw-r--r--resources/assets/javascripts/lib/fullcalendar.js977
1 files changed, 0 insertions, 977 deletions
diff --git a/resources/assets/javascripts/lib/fullcalendar.js b/resources/assets/javascripts/lib/fullcalendar.js
deleted file mode 100644
index 5882460..0000000
--- a/resources/assets/javascripts/lib/fullcalendar.js
+++ /dev/null
@@ -1,977 +0,0 @@
-/**
- * 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
-{
- static holidayCache = sessionStorage.getItem('fullcalendar_holidays') ? JSON.parse(sessionStorage.getItem('fullcalendar_holidays')) : {};
- static vacationCache = sessionStorage.getItem('fullcalendar_vacations') ? JSON.parse(sessionStorage.getItem('fullcalendar_vacations')) : {};
-
- static loadHolidays(year) {
- if (this.holidayCache[year]) {
- return Promise.resolve(this.holidayCache[year]);
- }
-
- return STUDIP.jsonapi.withPromises().get('holidays', {
- data: { 'filter[year]': year }
- }).then(response => {
- const events = [];
- if (!response) {
- return events;
- }
-
- for (const [date, data] of Object.entries(response)) {
- const classNames = ['holiday'];
- if (data.mandatory) {
- classNames.push('official');
- }
-
- const day = new Date(date);
- events.push({
- // Note: Since allDay is set to true, the start and end time is ignored.
- // See the documentation: https://fullcalendar.io/docs/v4/event-parsing
- start: day,
- end: day,
- allDay: true,
- title: data.holiday,
- editable: false,
-
- classNames,
-
- // Note: Colours are set via SCSS.
- textColor: '',
- color: '',
- borderColor: '',
-
- rendering: 'background'
- });
- }
-
- this.holidayCache[year] = events;
-
- sessionStorage.setItem('fullcalendar_holidays', JSON.stringify(this.holidayCache));
-
- return events;
- });
- }
-
- static loadVacations(year) {
- if (this.vacationCache[year]) {
- return Promise.resolve(this.vacationCache[year]);
- }
-
- return STUDIP.jsonapi.withPromises().get('vacations', {
- data: {'filter[year]': year}
- }).then(response => {
- if (!response) {
- return [];
- }
-
- const items = [];
-
- for (const vacation_data of Object.values(response)) {
- const start = new Date(parseInt(vacation_data.start) * 1000);
- const end = new Date(parseInt(vacation_data.end) * 1000);
- items.push({
- start,
- end,
- allDay: true,
- title: vacation_data.name,
- editable: false,
- classNames: ['holiday'],
-
- // Note: Colours are set via SCSS.
- textColor: '',
- color: '',
- borderColor: '',
-
- rendering: 'background'
- });
- }
-
- this.vacationCache[year] = items;
-
- sessionStorage.setItem('fullcalendar_vacations', JSON.stringify(this.vacationCache));
-
- return items;
- });
- }
-
- /**
- * 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 = $('<h1>').text(title).prependTo(this);
-
- window.scrollTo(0, 0);
-
- html2canvas(this).then(canvas => {
- var imgData = canvas.toDataURL('image/jpeg');
- var pdf = new jsPDF({
- orientation: format === 'landscape' ? 'landscape' : 'portrait'
- });
- if (format === 'landscape') {
- pdf.addImage(imgData, 'JPEG', 20, 20, 250, 250, 'i1', 'NONE', 0);
- } else {
- pdf.addImage(imgData, 'JPEG', 25, 20, 160, 190, 'i1', 'NONE', 0);
- }
- pdf.save(title + '.pdf');
- });
-
- print_title.remove();
- $(this).removeClass('print-view without-weekend');
- }
- });
- }
-
- static toRFC3339String(date)
- {
- var timezone_offset_min = date.getTimezoneOffset();
- var offset_hrs = parseInt(Math.abs(timezone_offset_min / 60), 10);
- var offset_min = Math.abs(timezone_offset_min%60);
- var timezone_standard;
-
- offset_hrs = pad(offset_hrs);
- offset_min = pad(offset_min);
-
- // Add an opposite sign to the offset
- // If offset is 0, it means timezone is UTC
- if (timezone_offset_min < 0) {
- timezone_standard = `+${offset_hrs}:${offset_min}`;
- } else if (timezone_offset_min > 0) {
- timezone_standard = `-${offset_hrs}:${offset_min}`;
- } else {
- timezone_standard = '+00:00';
- }
-
- var current_date = pad(date.getDate());
- var current_month = pad(date.getMonth() + 1);
- var current_year = date.getFullYear();
- var current_hrs = pad(date.getHours());
- var current_mins = pad(date.getMinutes());
- var current_secs = pad(date.getSeconds());
- var current_datetime;
-
- // Current datetime
- // String such as 2016-07-16T19:20:30
- current_datetime = `${current_year}-${current_month}-${current_date}T${current_hrs}:${current_mins}:${current_secs}`;
-
- return current_datetime + timezone_standard;
- }
-
- static defaultDropEventHandler(info)
- {
- // The logic from fullcalendar is inversed here:
- // If the calendar isn't editable, the event isn't either.
- if (!info.event.startEditable || !info.view.viewSpec.options.editable) {
- //Read-only events cannot be dragged and dropped!
- info.revert();
- return;
- }
-
- var drop_resource_id = info.newResource ? info.newResource.id : info.event.extendedProps.studip_range_id;
-
- if (info.event.extendedProps.studip_api_urls.move || info.event.extendedProps.studip_api_urls.move_dialog) {
- let move_dialog = info.event.extendedProps.studip_api_urls.move_dialog;
- if (info.event.allDay) {
- if (move_dialog) {
- STUDIP.Dialog.fromURL(
- move_dialog,
- {
- data: {
- resource_id: drop_resource_id,
- begin: this.toRFC3339String(info.event.start.setHours(0, 0, 0)),
- end: this.toRFC3339String(info.event.start.setHours(23, 59, 59))
- },
- size: 'auto'
- }
- );
- } else {
- jQuery.post({
- async: false,
- url: info.event.extendedProps.studip_api_urls.move,
- data: {
- resource_id: drop_resource_id,
- begin: this.toRFC3339String(info.event.start.setHours(0, 0, 0)),
- end: this.toRFC3339String(info.event.start.setHours(23, 59, 59))
- }
- }).fail(info.revert);
- }
- } else if (info.event.end === null) {
- let real_end = new Date();
- real_end.setTime(info.event.start.getTime());
- real_end.setHours(info.event.start.getHours()+2);
- if (move_dialog) {
- STUDIP.Dialog.fromURL(
- move_dialog,
- {
- data: {
- resource_id: drop_resource_id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(real_end)
- },
- size: 'auto'
- }
- );
- } else {
- jQuery.post({
- async: false,
- url: info.event.extendedProps.studip_api_urls.move,
- data: {
- resource_id: drop_resource_id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(real_end)
- }
- }).fail(info.revert);
- }
- } else {
- if (move_dialog) {
- STUDIP.Dialog.fromURL(
- move_dialog,
- {
- data: {
- resource_id: drop_resource_id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(info.event.end)
- },
- size: 'auto'
- }
- );
- } else {
- jQuery.post({
- async: false,
- url: info.event.extendedProps.studip_api_urls.move,
- data: {
- resource_id: drop_resource_id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(info.event.end)
- }
- }).fail(info.revert);
- }
- }
- }
- }
-
- static institutePlanDropEventHandler(info)
- {
- //The logic from fullcalendar is inversed here:
- if (info.newResource) {
- $.post({
- async: false,
- url: info.event.extendedProps.studip_api_urls.move,
- data: {
- cycle_id: info.event.id,
- resource_id: info.newResource.id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(info.event.end)
- }
- }).fail(info.revert);
- } else {
- //If the calendar isn't editable, the event isn't either.
- if (!info.event.startEditable || !info.view.viewSpec.options.editable) {
- //Read-only events cannot be dragged and dropped!
- info.revert();
- return;
- }
-
- $.post({
- async: false,
- url: info.event.extendedProps.studip_api_urls.move,
- data: {
- cycle_id: info.event.id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(info.event.end)
- }
- }).fail(info.revert);
- }
- }
-
- static institutePlanExternalDropEventHandler(info)
- {
- var resourceIds = info.event.getResources().map(resource => resource.id);
-
- $.post({
- async: false,
- url: info.event.extendedProps.studip_api_urls.drop,
- data: {
- course_id: info.event.extendedProps.course_id,
- begin: this.toRFC3339String(info.event.start),
- end: this.toRFC3339String(info.event.end),
- resource_id: resourceIds[0]
- }
- }).done(data => {
- if (data) {
- info.view.context.calendar.addEvent(JSON.parse(data));
- info.event.remove();
- }
- });
- }
-
- static createFromNode(node, additional_config = {})
- {
- if (!node) {
- //No node? No fullcalendar!
- return;
- }
-
- let config = $(node).data('config');
-
- let defaultView = 'timeGridWeek';
- if (Responsive.isResponsive() && config.responsiveDefaultView !== undefined) {
- defaultView = config.responsiveDefaultView;
- } else if (config.defaultView !== undefined) {
- defaultView = config.defaultView;
- }
-
- //Make sure the default values are set, if they are not found
- //in the additional_config object:
- config = $.extend({
- plugins: [ interactionPlugin, dayGridPlugin, timeGridPlugin, resourceCommonPlugin, resourceTimeGridPlugin, resourceTimelinePlugin ],
- schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
- header: {
- left: 'dayGridMonth,timeGridWeek,timeGridDay'
- },
- minTime: '08:00:00',
- maxTime: '20:00:00',
- validRange: {
- start: '1970-01-01'
- },
- height: 'auto',
- contentHeight: 'auto',
- firstDay: 1,
- weekNumberCalculation: 'ISO',
- locales: [enLocale, deLocale ],
- locale: String.locale === 'de-DE' ? 'de' : 'en-gb',
- timeFormat: 'H:mm',
- slotLabelFormat: {
- hour: 'numeric',
- minute: '2-digit',
- omitZeroMinute: false
- },
- columnHeaderHtml: STUDIP.Fullcalendar.renderDateForColumn,
- nowIndicator: true,
- timeZone: 'local',
- studip_functions: [],
- resourceAreaWidth: '20%',
- select (selectionInfo) {
- let calendar_config = JSON.parse(selectionInfo.view.context.calendar.el.dataset.config);
- let dialog_size = 'auto';
- if (calendar_config.dialog_size !== undefined) {
- dialog_size = calendar_config.dialog_size;
- }
-
- if (!selectionInfo.view.viewSpec.options.editable || !selectionInfo.view.viewSpec.options.studip_urls) {
- //The calendar isn't editable.
- return;
- }
- if (selectionInfo.view.viewSpec.options.studip_urls.add) {
- if (selectionInfo.resource) {
- STUDIP.Dialog.fromURL( selectionInfo.view.viewSpec.options.studip_urls.add, {
- data: {
- begin: selectionInfo.start.getTime()/1000,
- end: selectionInfo.end.getTime()/1000,
- ressource_id: selectionInfo.resource.id,
- all_day: selectionInfo.allDay ? '1' : '0'
- },
- size: dialog_size
- });
- } else {
- STUDIP.Dialog.fromURL(selectionInfo.view.viewSpec.options.studip_urls.add, {
- data: {
- begin: selectionInfo.start.getTime()/1000,
- end: selectionInfo.end.getTime()/1000,
- all_day: selectionInfo.allDay ? '1' : '0'
- },
- size: dialog_size
- });
- }
- }
- },
- eventClick (eventClickInfo) {
- var event = eventClickInfo.event;
- var extended_props = event.extendedProps;
- if ($(eventClickInfo.jsEvent.target).hasClass('event-colorpicker')) {
- STUDIP.Dialog.fromURL(
- STUDIP.URLHelper.getURL('dispatch.php/admin/courseplanning/pick_color/' + extended_props.metadate_id + '/' + config.actionCalled),
- {'size': '400x400'}
- );
- return false;
- }
-
-
- if ($(eventClickInfo.event._calendar.el).hasClass('request-plan')) {
- if (extended_props.request_id && extended_props.studip_view_urls.edit) {
- STUDIP.Dialog.fromURL(
- STUDIP.URLHelper.getURL(extended_props.studip_view_urls.edit)
- );
- } else if(extended_props.studip_parent_object_class == 'ResourceBooking' && $.inArray('for-course', event._def.ui.classNames) != -1) {
- STUDIP.Dialog.fromURL(
- STUDIP.URLHelper.getURL('dispatch.php/resources/room_request/rerequest_booking/' + extended_props.studip_parent_object_id)
- );
- }
- return false;
- }
-
- if (extended_props.studip_view_urls === undefined) {
- return;
- }
- let calendar_config = JSON.parse(eventClickInfo.view.context.calendar.el.dataset.config);
- let dialog_size = 'auto';
- if (calendar_config.dialog_size !== undefined) {
- //Use the configured default dialog size for the fullcalendar instance:
- dialog_size = calendar_config.dialog_size;
- }
- if (extended_props.dialog_size !== undefined) {
- //Use the dialog size of the event:
- dialog_size = extended_props.dialog_size;
- }
- if (!event.startEditable && extended_props.studip_view_urls.show) {
- STUDIP.Dialog.fromURL(
- STUDIP.URLHelper.getURL(extended_props.studip_view_urls.show),
- {size: dialog_size}
- );
- } else if (event.startEditable) {
- if (extended_props.studip_view_urls.edit) {
- STUDIP.Dialog.fromURL(
- STUDIP.URLHelper.getURL(extended_props.studip_view_urls.edit),
- {size: dialog_size}
- );
- } else if (extended_props.studip_view_urls.show) {
- STUDIP.Dialog.fromURL(
- STUDIP.URLHelper.getURL(extended_props.studip_view_urls.show),
- {size: dialog_size}
- );
- }
- }
- return false;
- },
- eventResize (info) {
- // The logic from fullcalendar is inversed here:
- // If the calendar isn't editable, the event isn't either.
- if (info.view.viewSpec.options.studip_functions.resize_event) {
- info.view.viewSpec.options.studip_functions.resize_event(info);
- } else {
- STUDIP.Fullcalendar.defaultResizeEventHandler(info);
- }
- info.event.source.refetch();
- },
- eventDrop (info) {
- let handle_drop = function() {
- if ($(info.view.context.calendar.el).hasClass('institute-plan')) {
- var start = info.event.start;
- var cal_start = info.view.activeStart;
- if ((start.getHours() - cal_start.getHours()) % 2 === 1) {
- info.event.moveDates('-01:00');
- }
- STUDIP.Fullcalendar.institutePlanDropEventHandler(info);
- } else {
- if (info.view.viewSpec.options.studip_functions.drop_event) {
- info.view.viewSpec.options.studip_functions.drop_event(info);
- } else {
- STUDIP.Fullcalendar.defaultDropEventHandler(info);
- }
- info.event.source.refetch();
- }
- };
-
- let calendar_config = JSON.parse(info.view.context.calendar.el.dataset.config);
- if (calendar_config.confirm) {
- if (calendar_config.confirm.drop) {
- STUDIP.Dialog.confirm(calendar_config.confirm.drop)
- .done(handle_drop)
- .fail(function() {
- //Revert the dropped element:
- info.revert();
- });
- } else {
- handle_drop();
- }
- } else {
- handle_drop();
- }
- },
- eventRender (info) {
- let event = info.event;
- let eventElement = info.el;
- let iconColor = event.textColor === '#000000' ? 'black' : 'white';
-
- if ($(info.view.context.calendar.el).hasClass('institute-plan')) {
- $(eventElement).attr('title', event.extendedProps.tooltip);
- if (event.extendedProps.content_fields) {
- for (const [css_class, field] of Object.entries(event.extendedProps.content_fields)) {
- $(eventElement).find('.fc-content').append(
- $('<div>').css({
- width: 'calc(100% - 21px)',
- height: '100%',
- wordBreak: 'break-word'
- }).text(field)
- .addClass(css_class + ' fc-title')
- );
- }
- }
- $(eventElement).find('.fc-content').append(
- $('<button class="event-colorpicker">').addClass(iconColor)
- );
- } else {
- $(eventElement).attr('title', event.title);
- }
-
- if (event.extendedProps.icon) {
- //Check if there is more than one icon:
- let event_icons = event.extendedProps.icon.split(',');
- let title = $(eventElement).find('.fc-title');
- for (let icon of event_icons) {
- //Check if the icon is already a URL or just the name of an icon.
- let iconUrl = '';
- if (icon.includes('://')) {
- //The icon is already a URL.
- iconUrl = icon;
- } else {
- //The icon is just referenced by its name. We do not need a specific color here, background-color is currentColor.
- iconUrl = `${STUDIP.ASSETS_URL}images/icons/black/${icon}.svg`
- }
- //Add the icons as spans in front of the content:
- let icon_element = $('<span class="icon"></span>');
- icon_element.css('--icon-url', `url('${iconUrl}')`);
- title.prepend(icon_element);
- }
- }
-
- //If a background event with title shall be rendered, we have to check
- //if an all-day slot is present or not.
- let generate_title = false;
- if (event.rendering === 'background' && event.title && event.allDay) {
- if (info.view.viewSpec.options.allDaySlot === true) {
- //An all-day slot is present in the calendar.
- if (info.isStart === true || info.isEnd === true) {
-
- generate_title = true;
- }
- } else {
- //No all-day slot in the calendar. Display a title
- //at the start of the day in the calendar.
- if (info.isStart === false || info.isEnd === false) {
- generate_title = true;
- }
- }
- }
- if (generate_title) {
- //Generate a visible title for the background element:
- let element = $('<div class="title"></div>');
- $(element).text(event.title);
- if (event.textColor) {
- element.css('color', event.textColor);
- }
- $(eventElement).append(element);
- }
- },
- eventSourceSuccess: function(content) {
- if ($(node).hasClass('semester-plan')) {
- $(content).each(function(i, event_data){
- STUDIP.Fullcalendar.convertSemesterEvents(event_data, config.defaultDate);
- });
- }
- return content;
- },
- loading (isLoading) {
- if (isLoading) {
- if (!$('#loading-spinner').length) {
- jQuery('#content').append(
- $('<div id="loading-spinner" style="position: absolute; top: calc(50% - 55px); left: calc(50% + 135px); z-index: 9001;">').html(
- $('<img>').attr('src', STUDIP.ASSETS_URL + 'images/loading-indicator.svg')
- .css({
- width: 32,
- height: 32
- })
- )
- );
- }
- } else {
- $('#loading-spinner').remove();
- this.updateSize();
- }
- },
- datesRender (info) {
- if ($(node).hasClass('schedule') && info.view.type !== 'timeGridDay') {
- document.querySelector('.fc-right').style.display = 'none';
- }
-
- if ($(node).hasClass('semester-plan')) {
- //Nothing to do in the semester plan, since it already displays
- //all the information on loading.
- return;
- }
- let activeRange = info.view.props.dateProfile.activeRange;
- let timestamp = activeRange.start.getTime() / 1000;
- if ($(info.el).hasClass('institute-plan')) {
- $('.fc-slats tr:odd .fc-widget-content:not(.fc-axis)').remove();
- }
-
- if (document.getElementById('booking-plan-header-semname') === null) {
- return;
- }
-
- $.getJSON(
- STUDIP.URLHelper.getURL(`dispatch.php/resources/ajax/semester_week/${timestamp}`)
- ).done((data) => {
- if (data) {
- $('#booking-plan-header-semname').text(data.semester_name);
- if (data.sem_week) {
- $('#booking-plan-header-semweek').text(data.sem_week);
- $('#booking-plan-header-semweek-part').show();
- } else {
- $('#booking-plan-header-semweek').text('');
- $('#booking-plan-header-semweek-part').hide();
- }
- $('#booking-plan-header-semrow').show();
- $('#booking-plan-header-calweek').text(data.week_number);
- $('#booking-plan-header-calbegin').text('(' + data.current_day + ')');
- } else {
- $('#booking-plan-header-semrow').hide();
- $('#booking-plan-header-semweek-part').hide();
- }
- });
- },
- resourceRender (renderInfo) {
- if ($(renderInfo.view.context.calendar.el).hasClass('room-group-booking-plan')) {
- let action = $(renderInfo.view.context.calendar.el).hasClass('semester-plan') ? 'semester' : 'booking';
- let url = STUDIP.URLHelper.getURL(`dispatch.php/resources/room_planning/${action}_plan/${renderInfo.resource.id}`);
- $(renderInfo.el).find('.fc-cell-text').html(
- $('<a>').attr('href', url).text(renderInfo.resource.title)
- );
- } else if ($("*[data-fullcalendar='1']").hasClass('institute-plan') && renderInfo.resource.id > 0) {
- const icon = '<span class="btn-icon btn-icon--edit icon-role-clickable" aria-label="edit"></span>';
- $(renderInfo.el).append(
- '<a href="'
- + STUDIP.URLHelper.getURL('dispatch.php/admin/courseplanning/rename_column/'
- + renderInfo.resource.id
- +'/'
- + renderInfo.view.activeStart.getDay())
- + '" data-dialog="size=auto"> '
- + icon
- + '</a>'
- );
- }
- },
- drop (dropInfo) {
- $(dropInfo.draggedEl).remove();
- },
- eventReceive (info) {
- if ($(info.view.context.calendar.el).hasClass('institute-plan')) {
- STUDIP.Fullcalendar.institutePlanExternalDropEventHandler(info);
- }
- }
- }, config);
-
- //Special treatment: If a general column header format is set,
- //in the configuration, it shall be used for all columns in all views
- //by using a special columnHeaderHtml function.
- if (config.columnHeaderFormat) {
- config.columnHeaderHtml = function (date) {
- if ($("*[data-fullcalendar='1']").hasClass('institute-plan')) {
- return '<a href="' + STUDIP.URLHelper.getURL('dispatch.php/admin/courseplanning/weekday/' + date.getDay()) + '">' + date.toLocaleDateString('de-DE', config.columnHeaderFormat) + '</a>';
- } else {
- return date.toLocaleDateString('de-DE', config.columnHeaderFormat);
- }
- };
- }
-
- config = $.extend({}, config, additional_config);
-
- if (!Array.isArray(config.eventSources)) {
- config.eventSources = [];
- }
- config.eventSources.push({
- events: (fetchInfo, successCallback, failureCallback) => {
- const startYear = fetchInfo.start.getFullYear();
- const endYear = fetchInfo.end.getFullYear();
- const requests = [];
- for (let year = startYear; year <= endYear; year++) {
- requests.push(this.loadHolidays(year));
- requests.push(this.loadVacations(year));
- }
- Promise.all(requests).then(results => {
- const events = [].concat(...results);
- successCallback(events);
- return results;
- }).catch(failureCallback);
- },
- });
-
- config.defaultView = defaultView;
-
- return this.init(node, config);
- }
-
- static submitDatePicker() {
- let picked_date = jQuery('#booking-plan-jmpdate').val();
- if (!picked_date) {
- //Not a booking plan date selector.
- picked_date = jQuery('#date_select').val();
- }
- let iso_date_string = '';
- if (picked_date) {
- if (picked_date.includes('.')) {
- let [day, month, year] = picked_date.split('.');
- iso_date_string = year.padStart(4, '20') + '-' + month.padStart(2, '0') + '-' + day.padStart(2, '0');
- } else if (picked_date.includes('/')) {
- let [day, month, year] = picked_date.split('/');
- iso_date_string = year.padStart(4, '20') + '-' + month.padStart(2, '0') + '-' + day.padStart(2, '0');
- } else if (picked_date.includes('-')) {
- iso_date_string = picked_date;
- }
- }
- if (iso_date_string) {
- jQuery('[data-fullcalendar="1"],[data-resources-fullcalendar="1"]').each(function () {
- this.calendar.gotoDate(iso_date_string);
- });
- Fullcalendar.updateDateURL();
- }
- }
-
- static updateDateURL() {
- let changedMoment;
- jQuery('[data-fullcalendar="1"],[data-resources-fullcalendar="1"]').each(function () {
- changedMoment = this.calendar.getDate();
- });
- if (changedMoment) {
- let changed_date = STUDIP.Fullcalendar.toRFC3339String(changedMoment).split('T')[0];
- //Get the timestamp:
- let timestamp = changedMoment.getTime() / 1000;
-
- jQuery('a.resource-bookings-actions, a.calendar-action').each(function () {
- const url = new URL(this.href);
- url.searchParams.set('timestamp', timestamp.toString())
- url.searchParams.set('defaultDate', changed_date)
- this.href = url.toString();
- });
- jQuery('.sidebar-widget.calendar-action').each(function() {
- //Each sidebar widget is different. The placement of the defaultDate URL parameter
- //has to reflect that.
- jQuery(this).find('button[formaction]').each(function() {
- //Modify the formaction attribute:
- let url = new URL(jQuery(this).attr('formaction'));
- url.searchParams.set('defaultDate', changed_date);
- jQuery(this).attr('formaction', url.toString());
- });
- jQuery(this).find('form[action]').each(function() {
- //Add a hidden input with the defaultDate:
- let hidden_input = jQuery(this).find('input[name="defaultDate"]')[0];
- if (!hidden_input) {
- hidden_input = jQuery('<input type="hidden" name="defaultDate">');
- jQuery(this).append(hidden_input);
- }
- jQuery(hidden_input).val(changed_date);
- });
- });
-
- // Now change the URL of the window.
- const url = new URL(window.location.href);
- url.searchParams.set('defaultDate', changed_date);
-
- // Update url in history
- history.pushState({}, null, url.toString());
-
- // Adjust links accordingly
- url.searchParams.delete('allday');
- jQuery('.booking-plan-std_view').attr('href', url.toString());
-
- url.searchParams.set('allday', 1);
- jQuery('.booking-plan-allday_view').attr('href', url.toString());
-
- // Update sidebar value
- let element = jQuery('#booking-plan-jmpdate,#date_select').first();
- let padded_date = pad(changedMoment.getDate(), 2, '0')
- + '.' + pad(changedMoment.getMonth() + 1, 2, '0')
- + '.' + changedMoment.getFullYear();
- element.val(padded_date);
- if (element.is('#booking-plan-jmpdate')) {
- //Store the date in the sessionStorage:
- sessionStorage.setItem('booking_plan_date', changed_date);
- }
- }
- }
-
- static renderDateForColumn(date) {
- let format = new Intl.DateTimeFormat(String.locale, {weekday: 'short'});
- let date_html = STUDIP.DateTime.getStudipDate(date, false, true, true);
- return '<span class="dow">' + format.format(date) + '.</span> ' + date_html;
- }
-}
-
-export default Fullcalendar;