diff --git a/public/js/components/Calendar/Abstract.js b/public/js/components/Calendar/Abstract.js
deleted file mode 100644
index 54b0304c7..000000000
--- a/public/js/components/Calendar/Abstract.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import CalendarHeader from './Header.js';
-
-export default {
- components: {
- CalendarHeader
- },
- inject: [
- 'date',
- 'focusDate',
- 'size'
- ],
- emits: [
- 'updateMode',
- 'change:range',
- 'input'
- ]
-}
diff --git a/public/js/components/Calendar/Calendar.js b/public/js/components/Calendar/Calendar.js
deleted file mode 100644
index ab7dac2b3..000000000
--- a/public/js/components/Calendar/Calendar.js
+++ /dev/null
@@ -1,301 +0,0 @@
-import CalendarMonth from './Month.js';
-import CalendarMonths from './Months.js';
-import CalendarYears from './Years.js';
-import CalendarWeek from './Week.js';
-import CalendarWeeks from './Weeks.js';
-import CalendarDay from './Day.js';
-import CalendarMinimized from './Minimized.js';
-import CalendarDate from '../../composables/CalendarDate.js';
-import CalendarDates from '../../composables/CalendarDates.js';
-
-const todayDate = new Date(new Date().setHours(0, 0, 0, 0));
-const today = todayDate.getTime()
-
-export default {
- components: {
- CalendarMonth,
- CalendarMonths,
- CalendarYears,
- CalendarWeek,
- CalendarWeeks,
- CalendarDay,
- CalendarMinimized,
- },
- provide() {
- return {
- today,
- todayDate,
- date: this.date,
- focusDate: this.focusDate,
- size: Vue.computed({ get: () => this.size }),
- containerHeight: Vue.computed({ get: () => this.containerHeight }),
- containerWidth: Vue.computed({ get: () => this.containerWidth }),
-
- events: Vue.computed(() => this.eventsPerDay),
- filteredEvents: Vue.computed(() => this.filteredEvents),
- minimized: Vue.computed({ get: () => this.minimized, set: v => this.$emit('update:minimized', v) }),
- showWeeks: this.showWeeks,
- noMonthView: this.noMonthView,
- noWeekView: this.noWeekView,
- eventsAreNull: Vue.computed(() => this.events === null),
- mode: Vue.computed(()=>this.mode),
- selectedEvent: Vue.computed(() => this.selectedEvent),
- setSelectedEvent: (event)=>{this.selectedEvent = event;},
- };
- },
- props: {
- events: Array,
- initialDate: {
- type: [Date, String],
- default: new Date()
- },
- showWeeks: {
- type: Boolean,
- default: true
- },
- initialMode: {
- type: String,
- default: 'month'
- },
- minimized: Boolean,
- noWeekView: Boolean,
- noMonthView: Boolean
- },
- watch:{
- mode(newVal) {
- this.$emit('change:mode', newVal)
- },
- selectedEvent:{
- handler(newSelectedEvent) {
- this.$emit('selectedEvent', newSelectedEvent);
- },
- immediate: true,
- },
- // scroll to the first event if the html element was found
- scrollTime(newValue,oldValue){
- // return early if the scrollTime is not set
- if (!newValue.scrollTime || !newValue?.doScroll) return;
- if (newValue?.scrollTime == oldValue?.scrollTime && newValue?.focusDate.d==oldValue?.focusDate.d) {
- return;
- }
- // scroll the LvPlan to the closest event
- let previousScrollAnchor = document.getElementById('scroll' + (newValue.scrollTime - 1) + this.focusDate.d + this.focusDate.w)
- let scrollAnchor = document.getElementById('scroll' + newValue.scrollTime + this.focusDate.d + this.focusDate.w);
- if (previousScrollAnchor) {
- previousScrollAnchor.scrollIntoView({ behavior: 'smooth', block: 'start' });
- }
- else {
- if (scrollAnchor) {
- scrollAnchor.scrollIntoView({ behavior: 'smooth', block: 'start' });
- }
- }
-
- },
- },
- emits: [
- 'select:day',
- 'select:event',
- 'change:range',
- 'change:mode',
- 'update:minimized',
- 'selectedEvent',
- 'change:offset'
- ],
- data() {
- return {
- header: '',
- prevMode: null,
- currMode: null,
- date: new CalendarDate(),
- focusDate: new CalendarDate(),
- size: 0,
- containerWidth: 0,
- containerHeight: 0,
- selectedEvent:null,
- }
- },
- computed: {
- sizeClass() {
- // mainly determines calendar font-size
- return 'fhc-calendar-' + ['xs', 'sm', 'md', 'lg'][this.size];
- },
- mode: {
- get() { return this.minimized ? 'minimized' : this.currMode; },
- set(v) {
- if (!v && this.prevMode) {
- this.currMode = this.prevMode;
- this.prevMode = null;
- } else {
- this.prevMode = this.currMode;
- this.currMode = v;
- }
- }
- },
- eventsPerDay() {
- if (!this.events)
- return {};
- return this.events.reduce((result, event) => {
- let days = Math.ceil((event.end - event.start) / 86400000) || 1;
- while (days-- > 0) {
- let day = (new Date(event.start.getFullYear(), event.start.getMonth(), event.start.getDate() + days)).toDateString();
- if (!result[day])
- result[day] = [];
- result[day].push(event);
- }
- return result;
- }, {});
- },
- // returns the hour of the earliest event, used to scroll to the events in the calendar (week / day view)
- scrollTime() {
- let doScroll = true;
- // return the first beginning time of the filtered events
- if(this.filteredEvents && Array.isArray(this.filteredEvents) && this.filteredEvents.length > 0)
- {
- let scrollTimeEvents = this.filteredEvents.filter(event=>{
- return event.type !== 'moodle';
- });
- // do not compute a new scroll time if there are no other events than moodle events
- if(!(scrollTimeEvents.length >0)){
- doScroll = false;
- }
- let scrollTime = parseInt(scrollTimeEvents.sort((a, b) => parseInt(a.beginn) - parseInt(b.beginn))[0]?.beginn);
- // to ensure that the scrollTime watcher triggers even if the scrollTime doesn't change, it returns both the scrollTime and the focusDate
- return { focusDate: { d: this.focusDate.d, w: this.focusDate.w}, doScroll, scrollTime };
- }
- // there is no event that matches the current view mode constraints
- else
- {
- return { focusDate: { d: this.focusDate.d, w: this.focusDate.w }, doScroll,scrollTime: null };
- }
- },
- // filters the events based on the current calendar view mode
- // week view - filter events based on their week
- // day view - filter events based on their day and week
- // month view - does not filter the events
- filteredEvents: function(){
- if (this.events && Array.isArray(this.events) && this.events.length > 0) {
- let filteredEvents = this.events.filter(event => {
- let eventDate = new CalendarDate(new Date(event.datum));
- if (this.mode == 'week' || this.mode == 'Week')
- {
- // week view filters the elements only for the same week
- return this.focusDate.w == eventDate.w;
- }
- else if (this.mode == 'day' || this.mode == 'Day')
- {
- // day view filters the elements for the same day and the same week
- return this.focusDate.d == eventDate.d && this.focusDate.w == eventDate.w;
- }
- else
- {
- // returns all the events, does not filter the events
- return true;
- }
- })
-
- return filteredEvents;
- }
- else
- {
- return null;
- }
- },
- },
- methods: {
- handleRangeOffset(event){
-
- let date = new Date(
- this.focusDate.y,
- this.focusDate.m,
- this.focusDate.d
- );
-
- date = date.getFullYear() + "-" +
- String(date.getMonth() + 1).padStart(2, "0") + "-" +
- String(date.getDate()).padStart(2, "0");
-
-
- this.$router.push({
- name: "LvPlan",
- params: {
- mode: this.mode[0].toUpperCase() + this.mode.slice(1),
- focus_date: date,
- lv_id: this.$route.params.lv_id || null
- }
- });
-
- this.$emit('change:range',event);
- },
- setMode(mode) {
- this.mode = mode
- },
- handleInput(day) {
- this.$emit(day[0], day[1]);
- },
- },
- created() {
- const initMode = this.initialMode.toLowerCase()
- const allowedInitialModes = ['day'];
- if (!this.noWeekView)
- allowedInitialModes.push('week');
- if (!this.noMonthView)
- allowedInitialModes.push('month');
- this.mode = allowedInitialModes[allowedInitialModes.indexOf(initMode)] || allowedInitialModes.pop();
- this.date.set(new Date(this.initialDate));
- this.focusDate.set(this.date);
- },
- mounted() {
- if (this.$refs.container) {
- new ResizeObserver(entries => {
- for (const entry of entries) {
- const w = entry.contentBoxSize ? entry.contentBoxSize[0].inlineSize : entry.contentRect.width;
- const h = entry.contentBoxSize ? entry.contentBoxSize[0].blockSize : entry.contentRect.height;
-
- // https://getbootstrap.com/docs/5.0/layout/breakpoints/
- // bootstrap breakpoints watch window size and this function monitors container size of calendar itself.
- // calendar is using bootstrap breakpoints which influence layout, which retriggers this function
- // -> some width constellations will loop so we dont use values around bs5 breakpoints
- // ['xs', 'sm', 'md', 'lg'][this.size]
- if (w >= 600)
- this.size = 3;
- else if (w >= 350)
- this.size = 2;
- else if (w >= 250)
- this.size = 1;
- else
- this.size = 0;
-
- this.containerWidth = w
- this.containerHeight = h
- }
- }).observe(this.$refs.container);
- }
- },
- unmounted(){
- CalendarDates.cleanup();
- },
- template: /*html*/`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
`
-}
diff --git a/public/js/components/Calendar/Day.js b/public/js/components/Calendar/Day.js
deleted file mode 100644
index 13128a520..000000000
--- a/public/js/components/Calendar/Day.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-import CalendarPane from './Pane.js';
-import CalendarDayPage from './Day/Page.js';
-import CalendarDate from '../../composables/CalendarDate.js';
-
-export default {
- mixins: [
- CalendarAbstract
- ],
- components: {
- CalendarDayPage,
- CalendarPane
- },
- computed: {
- title() {
- return this.focusDate.wYear + ' KW ' + this.focusDate.w;
- }
- },
- methods: {
- paneChanged(dir) {
- let previousDate = new CalendarDate(this.focusDate);
- this.focusDate.d += dir;
- this.emitRangeChanged(previousDate);
- },
- emitRangeChanged(previousDate, mounted) {
- this.$emit('change:range', { start: previousDate, end:this.focusDate });
- },
- prev() {
- this.$refs.pane.prev();
- this.$emit('change:offset', { y: 0, m: 0, d: -1 });
- },
- next() {
- this.$refs.pane.next();
- this.$emit('change:offset', { y: 0, m: 0, d: 1 });
- },
- selectEvent(event) {
- this.$emit('input', ['select:event', event]);
- }
- },
- mounted() {
- this.emitRangeChanged(new CalendarDate(this.focusDate.y, this.focusDate.m, this.focusDate.d -1), true);
- },
- template: /*html*/`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
`
-}
diff --git a/public/js/components/Calendar/Day/Page.js b/public/js/components/Calendar/Day/Page.js
deleted file mode 100644
index 43154aa0a..000000000
--- a/public/js/components/Calendar/Day/Page.js
+++ /dev/null
@@ -1,490 +0,0 @@
-import CalendarDate from '../../../composables/CalendarDate.js';
-import LvModal from "../../../components/Cis/Mylv/LvModal.js";
-
-import ApiLvPlan from '../../../api/factory/lvPlan.js';
-import ApiAddons from '../../../api/factory/addons.js';
-
-function ggt(m, n) {
- return n == 0 ? m : ggt(n, m % n);
-}
-
-function kgv(m, n) {
- return (m * n) / ggt(m, n);
-}
-
-export default {
- name: 'DayPage',
- components: {
- LvModal,
- },
- data() {
- return {
- hourPosition: null,
- curHourPosition: null,
- hourPositionTime: null,
- lvMenu: null,
- }
- },
- inject: [
- 'today',
- 'todayDate',
- 'date',
- 'focusDate',
- 'size',
- 'events',
- 'noMonthView',
- 'filteredEvents',
- 'isSliding',
- 'calendarScrollTop',
- 'calendarClientHeight',
- 'setSelectedEvent',
- 'selectedEvent',
- 'rowMinHeight'
- ],
- props: {
- year: Number,
- week: Number,
- active: Boolean,
- },
- emits: [
- 'updateMode',
- 'page:back',
- 'page:forward',
- 'input'
- ],
- watch: {
- //TODO: on first render non of the day-page components are active and the watcher on selectedEvent does not fetch the lvMenu
- //TODO: workaround is to watch the active state and refetch in case the lvMenu is empty
- activeAndEventsReady: {
- handler({active,events}) {
- // handles fetching the lvMenu
- if (active) {
- if (!this.lvMenu) {
- this.fetchLvMenu(this.selectedEvent);
- }
-
- if(events){
- // if no event is selected, select the first event of the day
- if (this.selectedEvent == null && this.events[this.day.toDateString()]?.length > 0) {
- let events = this.events[this.day.toDateString()].sort((a,b)=>{
- let [a_stunde,a_minute,a_sekunden]= a.beginn.split(":");
- let [b_stunde, b_minute, b_sekunden] = b.beginn.split(":");
- a_stunde = Number(a_stunde);
- a_minute = Number(a_minute);
- a_sekunden= Number(a_sekunden);
- b_stunde = Number(b_stunde);
- b_minute = Number(b_minute);
- b_sekunden = Number(b_sekunden);
- if(a_stunde > b_stunde){
- return 1;
- }
- else if(b_stunde > a_stunde){
- return -1;
- }
- else if(a_minute > b_minute){
- return 1;
- }
- else if (b_minute > a_minute){
- return -1;
- }
- else{
- return a_sekunden > b_sekunden? 1:-1;
- }
-
- });
- if (Array.isArray(events) && events.length > 0) {
- this.setSelectedEvent(events[0]);
- }
- }
- }
- }
- },
- immediate: true,
- },
- selectedEvent: {
- handler(event) {
- // return early if the day-page component is not the active carousel item
- if (!this.active) {
- return;
- }
- this.lvMenu = null;
- this.fetchLvMenu(event);
- },
- immediate: true,
- },
- isSliding: {
- handler(value) {
- if (value) {
- this.setSelectedEvent(null);
- }
- }
- }
- },
- computed: {
- activeAndEventsReady(){
- return {
- active: this.active,
- events: this.events,
- }
- },
- allDayEvents() {
- let allDayEvents = {};
- for (let day in this.events) {
- const filteredAllDayEvents = this.events[day].filter(event => event.allDayEvent);
- if (filteredAllDayEvents.length > 0) {
- allDayEvents[day] = filteredAllDayEvents;
- }
- };
- return allDayEvents;
- },
- overlayStyle() {
- return {
- height: this.getDayTimePercent + '%',
- }
- },
- pageHeaderStyle() {
- return {
- 'z-index': 4,
- 'grid-template-columns': 'repeat(' + this.day.length + ', 1fr)',
- 'grid-template-rows': 1,
- position: 'sticky',
- top: 0,
- }
- },
- dayText(){
- if(!this.day)return {};
- return {
- heading: this.day.toLocaleString(this.$p.user_locale.value, { dateStyle: 'short' }),
- tag: this.day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 2 ? 'narrow' : (this.size < 3 ? 'short' : 'long') }),
- datum: this.day.toLocaleString(this.$p.user_locale.value, [{ day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { dateStyle: 'short' }][this.size]),
- }
- },
- noLvStyle() {
- return {
- top: (this.calendarScrollTop + 100) + 'px',
- position: 'absolute',
- left: 0,
- 'text-align': 'center',
- width: '100%',
- 'z-index': 1,
- }
- },
- indicatorStyle() {
- return {
- 'pointer-events': 'none',
- 'padding-left': '3.5rem',
- 'margin-top': '-1px',
- 'z-index': 2,
- 'border-color': 'var(--fhc-border)',
- top: this.hourPosition + 'px',
- left: 0,
- right: 0,
- }
- },
- curTime() {
- const now = new Date();
- return String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0');
- },
- curIndicatorStyle() {
- return {
- top: this.getDayTimePercent + '%',
- }
- },
- noEventsCondition() {
- return !this.isSliding && (this.filteredEvents?.length === 0 || !this.filteredEvents);
- },
- hours() {
- // returns an array with elements starting at 7 and ending at 24
- return [...Array(24).keys()].filter(hour => hour >= 7 && hour <= 24);
- },
- day() {
- return new Date(this.focusDate.y, this.focusDate.m, this.focusDate.d);
- },
- eventsPerDayAndHour() {
- // return early if the calendar pane is sliding
- if (this.isSliding) return {};
-
- const res = {};
-
- let key = this.day.toDateString();
-
- let nextDay = new Date(this.day);
- nextDay.setDate(nextDay.getDate() + 1);
- nextDay.setMilliseconds(nextDay.getMilliseconds() - 1);
- let d = {events: [], lanes: 1};
- if (this.events[key]) {
- this.events[key].forEach(evt => {
- if (evt.allDayEvent) return;
- let event = {
- orig: evt,
- lane: 1,
- maxLane: 1,
- start: evt.start < this.day ? this.day : evt.start,
- end: evt.end > nextDay ? nextDay : evt.end,
- shared: [],
- setSharedMaxRecursive(doneItems) {
- this.maxLane = Math.max(doneItems[0].maxLane, this.maxLane);
- doneItems.push(this);
- this.shared.filter(other => !doneItems.includes(other)).forEach(i => i.setSharedMaxRecursive(doneItems));
- }
- };
- event.shared = d.events.filter(other => other.start < event.end && other.end > event.start);
- event.shared.forEach(other => other.shared.push(event));
- let occupiedLanes = event.shared.map(other => other.lane);
- while (occupiedLanes.includes(event.lane))
- event.lane++;
- event.maxLane = Math.max(...[event.lane], ...occupiedLanes);
- if (event.maxLane > 1) {
- event.setSharedMaxRecursive([event]);
- }
- d.events.push(event);
- });
- d.lanes = d.events.map(e => e.maxLane).reduce((res, i) => kgv(res, i), 1);
- }
- res[key] = d;
-
- return res;
- },
- smallestTimeFrame() {
- return [30, 15, 10, 5][this.size];
- },
- lookingAtToday() {
- return this.date.compare(this.todayDate)
- },
- getDayTimePercent() {
- const now = new Date(Date.now())
- const currentMinutes = now.getMinutes() + now.getHours() * 60
- let timePercentage = ((currentMinutes - (this.hours[0] * 60)) / (this.hours.length * 60)) * 100;
-
- return timePercentage
- }
- },
- methods: {
- dayScrollBehavior(event){
- this.$refs.dayScrollContainer?.scrollBy({ top: Math.sign(event.deltaY) * 100, behavior: 'instant' });
- },
- dayGridStyle(day) {
- const styleObj = {
- 'grid-template-columns': '1 1fr',
- 'grid-template-rows': 'repeat(' + (this.hours.length * 60 / this.smallestTimeFrame) + ', 1fr)',
- }
-
- if(this.date.compare(this.todayDate)) {
- styleObj['backgroundImage'] = 'linear-gradient(to bottom, var(--calendar-past) '+this.getDayTimePercent+'%, transparent '+this.getDayTimePercent+'%)'
- // styleObj.opacity = 0.5; // would opaque the whole column
- }
-
- return styleObj
- },
- fetchLvMenu(event) {
- if (event && event.type == 'lehreinheit') {
- this.$api
- .call(ApiLvPlan.getLehreinheitStudiensemester(event.lehreinheit_id[0]))
- .then(res => res.data)
- .then(studiensemester_kurzbz => this.$api.call(
- ApiAddons.getLvMenu(
- event.lehrveranstaltung_id,
- studiensemester_kurzbz
- )
- ))
- .then(res => {
- if (res.data) {
- this.lvMenu = res.data;
- }
- });
- }
- },
- hourGridIdentifier(hour) {
- // this is the id attribute that is responsible to scroll the calender to the first event
- return 'scroll' + hour + this.focusDate.d + this.week;
- },
- hourGridStyle(hour) {
- return {
- 'pointer-events': 'none',
- top: this.getAbsolutePositionForHour(hour),
- left: 0,
- right: 0,
- 'z-index': 0,
- }
- },
- eventGridStyle(day, event) {
- return {
- 'z-index': 1,
- 'grid-column-start': 1 + (event.lane - 1) * day.lanes / event.maxLane,
- 'grid-column-end': 1 + event.lane * day.lanes / event.maxLane,
- 'grid-row-start': this.dateToMinutesOfDay(event.start),
- 'grid-row-end': this.dateToMinutesOfDay(event.end),
- 'background-color': event.orig.color,
- '--test': this.dateToMinutesOfDay(event.end),
- }
- },
- eventClick(evt) {
- let event = evt.orig || evt;
- this.setSelectedEvent(event);
- this.$emit('input', event);
- },
- calcHourPosition(event) {
- let height = this.$refs.events.getBoundingClientRect().height;
- let top = this.$refs.events.getBoundingClientRect().top;
- let position = event.clientY - top;
- // position percentage of total height
- let timePercentage = (position / height) * 100;
- // minute percentage of total minutes
- let result = (this.hours.length * 60) * (timePercentage / 100);
- // calculate time in float
- let currentMinutes = ((result + (this.hours[0] * 60)) / 60);
- // get hour part of time
- let currentHour = Math.floor(currentMinutes);
- // get float part of time
- let minutePercentage = currentMinutes % currentHour;
- // calculate minutes from float part of time
- let minute = Math.round(60 * minutePercentage);
- // convert minutes to 5 minute interval
- if (minute % 5 != 0) {
- minute = Math.round(minute / 5) * 5;
- }
- // in case the rounding made the minutes 60, increase the hour and reset the minutes
- if (minute == 60) {
- currentHour++;
- minute = 0;
- }
-
- // ## after rounding the time to the nearest 5 Minute interval, we have to convert the time back to the relative position
- // convert current time in minutes
- currentMinutes = currentHour * 60 + minute;
- // calculate the minutes percentage of the total minutes
- timePercentage = ((currentMinutes - (this.hours[0] * 60)) / (this.hours.length * 60)) * 100;
- // calculate the relative position of the time percentage
- position = height * (timePercentage / 100);
- this.hourPosition = position;
-
- // add padding to minutes that consist of only one digit
- minute.toString().length == 1 ? minute = "0" + minute : minute;
- this.hourPositionTime = currentHour + ":" + minute;
- },
- getAbsolutePositionForHour(hour) {
- // used for the absolute positioning of the gutters of hours
- return (100 / this.hours.length) * (hour - (24 - this.hours.length)) + '%';
- },
- changeToMonth(day) {
- if (!this.noMonthView) {
- this.date.set(day);
- this.focusDate.set(day);
- this.$emit('updateMode', 'month');
- }
- },
- dateToMinutesOfDay(day) {
- return Math.floor(((day.getHours() - 7) * 60 + day.getMinutes()) / this.smallestTimeFrame) + 1;
- }
-
- },
- template: /*html*/`
-
-
-
-
-
-
-
- this is a slot placeholder
-
-
-
- Keine Lehrveranstaltungen
-
-
-
-
-
-
-
-
-
-
-
-`
-}
diff --git a/public/js/components/Calendar/Header.js b/public/js/components/Calendar/Header.js
deleted file mode 100644
index 7ddb384f2..000000000
--- a/public/js/components/Calendar/Header.js
+++ /dev/null
@@ -1,99 +0,0 @@
-export default {
- data(){
- return{
- selected: this.mode,
- modes:{
- day: { mode_bezeichnung: "day", icon: "fa-calendar-day" , condition:true},
- week: { mode_bezeichnung: "week", icon: "fa-calendar-week", condition: !this.noWeekView },
- month: { mode_bezeichnung: "month", icon: "fa-calendar-days", condition: !this.noMonthView },
- },
- headerPadding:null,
- }
- },
- inject: [
- 'eventsAreNull',
- 'size',
- 'mode',
- 'noWeekView',
- 'noMonthView',
- 'containerWidth'
- ],
- props: {
- title: String
- },
- emits: [
- 'updateMode',
- 'prev',
- 'next',
- 'click'
- ],
- methods:{
- modeAriaLabelText(mode) {
- switch (mode.toLowerCase()) {
- case "day": return this.$p.t('LvPlan', 'modeDay');
- case "week": return this.$p.t('LvPlan', 'modeWeek');
- case "month": return this.$p.t('LvPlan', 'modeMonth');
- }
- },
- },
- computed: {
- getHeaderClassSide() {
- return this.containerWidth > 780 ? 'col-3' : 'col-12'
- },
- getHeaderClassMiddle() {
- return this.containerWidth > 780 ? 'col-6' : 'col-12'
- },
- previousButtonAriaLabelText(){
- switch(this.mode.toLowerCase()){
- case "day": return this.$p.t('LvPlan', 'previousDay');
- case "week": return this.$p.t('LvPlan', 'previousWeek');
- case "weeks": return this.$p.t('LvPlan', 'previousYear');
- case "month": return this.$p.t('LvPlan', 'previousMonth');
- }
- },
- nextButtonAriaLabelText() {
- switch (this.mode.toLowerCase()) {
- case "day": return this.$p.t('LvPlan', 'nextDay');
- case "week": return this.$p.t('LvPlan', 'nextWeek');
- case "weeks": return this.$p.t('LvPlan', 'nextYear');
- case "month": return this.$p.t('LvPlan', 'nextMonth');
- }
- },
-
- },
- template: /*html*/`
- `
-}
\ No newline at end of file
diff --git a/public/js/components/Calendar/Minimized.js b/public/js/components/Calendar/Minimized.js
deleted file mode 100644
index 96db36ada..000000000
--- a/public/js/components/Calendar/Minimized.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-
-export default {
- mixins: [
- CalendarAbstract
- ],
- inject: [
- 'size',
- 'minimized',
- 'date',
- ],
- data() {
- return {
- start: 0
- }
- },
- methods: {
- maximize() {
- this.minimized = false;
- }
- },
- template: `
-
-
-
`
-}
diff --git a/public/js/components/Calendar/Month.js b/public/js/components/Calendar/Month.js
deleted file mode 100644
index 27ec50fbf..000000000
--- a/public/js/components/Calendar/Month.js
+++ /dev/null
@@ -1,103 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-import CalendarPane from './Pane.js';
-import CalendarMonthPage from './Month/Page.js';
-import BsModal from "../Bootstrap/Modal.js";
-import Months from "./Months.js";
-export default {
- mixins: [
- CalendarAbstract
- ],
- components: {
- CalendarMonthPage,
- CalendarPane,
- BsModal,
- Months
- },
- emits: [
- "change:offset"
- ],
- data() {
- return {
- syncOnNextChange: false
- }
- },
- computed: {
- title() {
- return this.focusDate.format({month: ['short','long','long','long'][this.size], year: 'numeric'}, this.$p.user_locale.value);
- }
- },
- methods: {
- handleMonthChanged(month) {
- this.$emit('change:offset', { y: 0, m: month - this.focusDate.m, d: 0 });
- this.$refs.modalDatepickerContainer.hide()
- },
- hideMonthsModal() {
- this.$refs.modalDatepickerContainer.hide()
- },
- handleHeaderClickMonth() {
- this.$refs.modalDatepickerContainer.show()
- },
- paneChanged(dir) {
- if (this.syncOnNextChange) {
- this.syncOnNextChange = false;
- this.focusDate.set(this.date);
- } else {
- this.focusDate.moveMonthInDirection(dir)
- }
- this.emitRangeChanged()
- },
- emitRangeChanged(mounted = false) {
- this.$emit('change:range', {
- start: new Date(this.focusDate.y, this.focusDate.m, 1),
- end: new Date(this.focusDate.y, this.focusDate.m+1, 0),
- mounted
- });
- },
- prev() {
- this.$refs.pane.prev();
- this.$emit('change:offset', { y: 0, m: -1, d: 0 });
- },
- next() {
- this.$refs.pane.next();
- this.$emit('change:offset', { y: 0, m: 1, d: 0 });
- },
- selectDay(day) {
- let m = day.getMonth();
- if (this.focusDate.m != m) {
- this.syncOnNextChange = true;
- if (this.focusDate.m-1 == m || (m == 11 && !this.focusDate.m))
- this.$refs.pane.prev();
- else
- this.$refs.pane.next();
- } else {
- this.focusDate.set(this.date);
- }
- this.$emit('input', ['select:day',day])
- }
- },
- mounted() {
- this.emitRangeChanged(true)
- },
- template: `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`
-}
diff --git a/public/js/components/Calendar/Month/Page.js b/public/js/components/Calendar/Month/Page.js
deleted file mode 100644
index bd974afde..000000000
--- a/public/js/components/Calendar/Month/Page.js
+++ /dev/null
@@ -1,170 +0,0 @@
-import CalendarDate from '../../../composables/CalendarDate.js';
-
-export default {
- name: 'MonthPage',
- data(){
- return{
- highlightedWeek: null,
- highlightedDay: null,
- }
- },
- inject: [
- 'today',
- 'todayDate',
- 'date',
- 'focusDate',
- 'size',
- 'events',
- 'showWeeks',
- 'noWeekView',
- 'selectedEvent',
- 'setSelectedEvent'
- ],
- props: {
- year: Number,
- month: Number
- },
- emits: [
- 'updateMode',
- 'page:back',
- 'page:forward',
- 'input'
- ],
- computed: {
- dayText(){
- if (!this.size || !this.weeks[0]?.days) return {};
- let dayTextMap ={};
- this.weeks[0].days.forEach((day)=>{
- dayTextMap[day] = day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 1 ? 'narrow' : (this.size < 3 ? 'short' : 'long') });
- });
- return dayTextMap;
- },
- weeks() {
- let firstDayOfMonth = new CalendarDate(this.year, this.month, 1);
- let startDay = firstDayOfMonth.firstDayOfCalendarMonth;
- let endDay = firstDayOfMonth.lastDayOfCalendarMonth;
-
- let res = [];
- let week = {no:0,y:0,days:[]};
- while (startDay <= endDay) {
- week.days.push(new Date(startDay));
-
- if (week.days.length == 7) {
- let d = new CalendarDate(week.days[5]);
- week.no = d.w;
- week.y = d.y;
- res.push(week);
- week = {no:0,y:0,days:[]};
- }
- startDay.setDate(startDay.getDate() + 1);
- }
- return res;
- },
-
- },
- methods: {
- getDayClass(week, day) {
- let classstring = 'fhc-calendar-month-page-day text-decoration-none overflow-hidden'
- const isHighlightedWeek = this.isHighlightedWeek(week)
- const isHighlightedDay = this.isHighlightedDay(day)
- const isThisDate = this.focusDate.compare(day)
-
- const isNotThisMonth = day.getMonth() != this.month
- const isInThePast = day.getTime() < this.today // this.date is just the focusDate but not the initial Date
-
- if(isThisDate) classstring += ' fhc-calendar-month-page-day-focusday'
- if(isHighlightedWeek) classstring += ' fhc-highlight-week'
- if(isHighlightedDay) classstring += ' fhc-highlight-day'
-
- if(isNotThisMonth) classstring += ' opacity-25'
- if(isInThePast) classstring += ' fhc-calendar-past'
- return classstring
- },
- selectDay(day, event) {
- this.setSelectedEvent(event);
- this.date.set(day);
- this.$emit('input', day);
- },
- changeToWeek(week) {
- if (!this.noWeekView) {
- if (!this.focusDate.isInWeek(week.no, week.y))
- this.focusDate.set(week.days[0]);
- this.$emit('updateMode', 'week');
- }
- },
- highlight(week, day){
- this.highlightedWeek = week.no;
- this.highlightedDay = day;
- },
- isHighlightedDay(day) {
- return day == this.highlightedDay
- },
- isHighlightedWeek(week) {
- return week.no == this.highlightedWeek
- },
- clickEvent(day,week) {
- if(!this.noWeekView)
- {
- this.focusDate.set(day);
- this.$emit('updateMode', 'day');
- }
- this.selectDay(day);
- },
- getNumberStyle(day) {
-
- const styleObj = {}
- styleObj.display = 'inline-block';
- styleObj.height = '32px';
- styleObj['line-height'] = '32px';
- styleObj['text-align'] = 'center';
- styleObj['font-weight'] = 'bold';
- styleObj['font-size'] = '14px';
-
- if(day.getDate() === this.todayDate.getDate()
- && day.getMonth() === this.todayDate.getMonth()
- && day.getFullYear() === this.todayDate.getFullYear()) {
- styleObj['background-color'] = 'var(--fhc-primary)';
- styleObj.color = 'white';
- }
-
- return styleObj
- }
- },
- mounted() {
- const container = document.getElementById("calendarContainer")
- if(container) container.style['overflow-y'] = 'auto'
-
- },
- template: /*html*/`
-
-`
-}
diff --git a/public/js/components/Calendar/Months.js b/public/js/components/Calendar/Months.js
deleted file mode 100644
index 6263e1b01..000000000
--- a/public/js/components/Calendar/Months.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-
-export default {
- mixins: [
- CalendarAbstract
- ],
- emits: [
- 'change'
- ],
- inject: [
- 'size'
- ],
- data() {
- return {
- // TODO: 36, 24, 16 (2+ 12 + 2) months to enable year switch?
- monthIndices: [...Array(12).keys()]
- }
- },
- computed: {
- title() {
- return this.focusDate.format({year: 'numeric'});
- },
- months() {
- return this.monthIndices.map(i => (new Date(0, i, 1)).toLocaleString(this.$p.user_locale.value, {month: this.size < 2 ? 'short' : 'long'}));
- }
- },
- template: `
- `
-}
diff --git a/public/js/components/Calendar/Pane.js b/public/js/components/Calendar/Pane.js
deleted file mode 100644
index 6bc00752b..000000000
--- a/public/js/components/Calendar/Pane.js
+++ /dev/null
@@ -1,94 +0,0 @@
-export default {
- name: 'Pane',
- emits: [
- 'slid'
- ],
- data() {
- return {
- carousel: null,
- queue: 0,
- offset: 0,
- slideAnimation:false,
- scrollTop:null,
- clientHeight:null,
- carouselItems:null,
- }
- },
- provide() {
- return {
- isSliding: Vue.computed(() => this.slideAnimation),
- calendarScrollTop: Vue.computed(() =>this.scrollTop),
- calendarClientHeight: Vue.computed(() => this.clientHeight),
- }
- },
- computed: {
- offsets() {
- return [...Array(3).keys()].map(i => (3+i-this.offset)%3-1);
- },
- activeCarouselItemIndex() {
- if (Array.isArray(this.carouselItems) && this.carouselItems.length > 0) {
- for(let index=0; index < this.carouselItems.length; index++){
- if (this.carouselItems[index] == true){
- return index;
- }
- }
-
- }
- return -1;
- }
-
- },
- methods: {
- scrollBehavior(event){
- this.$refs.calendarContainer?.scrollBy({ top: Math.sign(event.deltaY) * 100, behavior: 'instant' });
- },
- scrollCalendar(event){
- this.scrollTop = this.$refs.calendarContainer.scrollTop;
- this.clientHeight = this.$refs.calendarContainer.clientHeight;
- },
- prev() {
- if (!this.queue--)
- this.carousel.prev();
- },
- next() {
- if (!this.queue++)
- this.carousel.next();
- },
- slid(evt) {
- let dir = evt.direction == 'left' ? 1 : -1;
- this.queue -= dir;
- this.$emit('slid', dir);
- this.offset = (3+this.offset+dir)%3;
- if (this.queue) {
- if (this.queue > 0)
- this.carousel.next();
- else
- this.carousel.prev();
- }
- this.carouselItems = this.$refs.carouselItems.map((item) => { return item.classList.contains('active') });
- this.slideAnimation = false;
- },
- slide(evt) {
- this.slideAnimation = true;
- }
- },
- mounted() {
- if (this.$refs.carousel) {
- this.$refs.carousel.children[0].children[1].classList.add('active');
- this.carousel = new window.bootstrap.Carousel(this.$refs.carousel, {
- interval: false
- });
- }
- this.carouselItems = this.$refs.carouselItems.map((item)=>{return item.classList.contains('active')});
- this.scrollTop = this.$refs.calendarContainer.scrollTop;
- this.clientHeight = this.$refs.calendarContainer.clientHeight;
- },
- template: `
- `
-}
diff --git a/public/js/components/Calendar/Week.js b/public/js/components/Calendar/Week.js
deleted file mode 100644
index 2cca97a27..000000000
--- a/public/js/components/Calendar/Week.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-import CalendarPane from './Pane.js';
-import CalendarWeekPage from './Week/Page.js';
-import BsModal from "../Bootstrap/Modal.js";
-import Weeks from "./Weeks.js";
-
-export default {
- mixins: [
- CalendarAbstract
- ],
- components: {
- CalendarWeekPage,
- CalendarPane,
- BsModal,
- Weeks
- },
- emits: [
- "change:offset"
- ],
- computed: {
- title() {
- return this.focusDate.wYear + ' KW ' + this.focusDate.w;
- }
- },
- methods: {
- handleWeekChanged(week) {
- // this.$emit('change:offset', { y: 0, m: month - this.focusDate.m, d: 0 });
- this.$refs.modalDatepickerContainer.hide()
- },
- hideMonthsModal() {
- this.$refs.modalDatepickerContainer.hide()
- },
- handleHeaderClickWeek() {
- this.$emit('updateMode', 'weeks');//
- //this.$refs.modalDatepickerContainer.show()
- },
- paneChanged(dir) {
- this.focusDate.d += dir * 7;
- this.emitRangeChanged();
- },
- emitRangeChanged(mounted = false) {
- let start = this.focusDate.firstDayOfWeek;
- let end = this.focusDate.lastDayOfWeek;
- this.$emit('change:range', { start, end, mounted });
- },
- prev() {
- this.$refs.pane.prev();
- this.$emit('change:offset', { y: 0, m: 0, d: -7 });
- },
- next() {
- this.$refs.pane.next();
- this.$emit('change:offset', { y: 0, m: 0, d: 7 });
- },
- selectEvent(event) {
- this.$emit('input', ['select:event',event]);
- }
- },
- mounted() {
- this.emitRangeChanged(true);
- },
- template: /*html*/`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`
-}
diff --git a/public/js/components/Calendar/Week/Page.js b/public/js/components/Calendar/Week/Page.js
deleted file mode 100644
index b5bfcd3c3..000000000
--- a/public/js/components/Calendar/Week/Page.js
+++ /dev/null
@@ -1,387 +0,0 @@
-import CalendarDate from '../../../composables/CalendarDate.js';
-
-function ggt(m,n) { return n==0 ? m : ggt(n, m%n); }
-function kgv(m,n) { return (m*n) / ggt(m,n); }
-
-export default {
- name: 'WeekPage',
- data(){
- return{
- hourPosition:null,
- hourPositionTime:null,
- resizeObserver: null,
- width: 0,
- }
- },
- inject: [
- 'today',
- 'todayDate',
- 'date',
- 'focusDate',
- 'size',
- 'events',
- 'noMonthView',
- 'isSliding',
- 'selectedEvent',
- 'setSelectedEvent',
- 'rowMinHeight'
- ],
- props: {
- year: Number,
- week: Number,
- active: Boolean
- },
- emits: [
- 'updateMode',
- 'page:back',
- 'page:forward',
- 'input',
- ],
- computed: {
- allDayEvents(){
- let allDayEvents = {};
- for(let day in this.events){
- const filteredAllDayEvents = this.events[day].filter(event=>event.allDayEvent);
- if (filteredAllDayEvents.length > 0){
- allDayEvents[day]=filteredAllDayEvents;
- }
- };
- return allDayEvents;
- },
- getGridStyle() {
- return {
- 'min-height': this.rowMinHeight,
- // this.size is the magic number anyway which directs font-size,
- // which in turn influences a lot of layout
- width: '42px'
- }
- },
- laneWidth() {
- return (this.width - 42) / this.days.length
- },
- curTime() {
- const now = new Date();
- return String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0');
- },
- curIndicatorStyle() {
- return {
- top: this.getDayTimePercent + '%',
- }
- },
- pageHeaderStyle(){
- return {
- 'z-index': 4,
- 'grid-template-columns': 'repeat(' + this.days.length + ', 1fr)',
- 'grid-template-rows': 1,
- position: 'sticky',
- top: 0,
- }
- },
- overlayStyle() {
- return {
- height: this.getDayTimePercent + '%',
- width: this.laneWidth + 'px',
- }
- },
-
- hours(){
- // returns an array with elements starting at 7 and ending at 24
- return [...Array(24).keys()].filter(hour => hour >= 7 && hour <= 24);
- },
- dayText() {
- if (!this.size || !this.days) return {};
- let dayTextMap ={};
- this.days.forEach((day)=>{
- dayTextMap[day] = {
- heading: day.toLocaleString(this.$p.user_locale.value, { dateStyle: 'short' }),
- tag: day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 2 ? 'narrow' : (this.size < 3 ? 'short' : 'long') }),
- datum: day.toLocaleString(this.$p.user_locale.value, [{ day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { day: 'numeric', month: 'numeric' }, { dateStyle: 'short' }][this.size]),
- };
- });
- return dayTextMap;
- },
- days() {
-
- let tmpDate = new CalendarDate(this.year,1,1); // NOTE(chris): somewhere in the middle of the year
- tmpDate.w = this.week;
- let startDay = tmpDate.firstDayOfWeek;
- let result = [];
- for (let i = 0; i < 7; i++) {
- result.push(new Date(startDay.getFullYear(), startDay.getMonth(), startDay.getDate() + i));
- }
- return result;
-
- },
- eventsPerDayAndHour() {
- // return early if the calendar pane is sliding
- if (this.isSliding) return {};
- const res = {};
- this.days.forEach(day => {
- let key = day.toDateString();
-
- let nextDay = new Date(day);
- nextDay.setDate(nextDay.getDate()+1);
- nextDay.setMilliseconds(nextDay.getMilliseconds()-1);
- let d = {events:[],lanes:1, isPast: false};
- d.isPast = nextDay.getTime() < this.today
- d.isToday = nextDay.getFullYear() === this.todayDate.getFullYear() && nextDay.getMonth() === this.todayDate.getMonth() && nextDay.getDate() === this.todayDate.getDate()
- if (this.events[key]) {
- this.events[key].forEach(evt => {
- if (evt.allDayEvent) return;
- let event = {orig:evt,lane:1,maxLane:1,start: evt.start < day ? day : evt.start, end: evt.end > nextDay ? nextDay : evt.end,shared:[],setSharedMaxRecursive(doneItems) {
- this.maxLane = Math.max(doneItems[0].maxLane, this.maxLane);
- doneItems.push(this);
- this.shared.filter(other => !doneItems.includes(other)).forEach(i => i.setSharedMaxRecursive(doneItems));
- }};
- event.shared = d.events.filter(other => other.start < event.end && other.end > event.start);
- event.shared.forEach(other => other.shared.push(event));
- let occupiedLanes = event.shared.map(other => other.lane);
- while (occupiedLanes.includes(event.lane))
- event.lane++;
- event.maxLane = Math.max(...[event.lane], ...occupiedLanes);
- if (event.maxLane > 1) {
- event.setSharedMaxRecursive([event]);
- }
- d.events.push(event);
- });
- d.lanes = d.events.map(e => e.maxLane).reduce((res, i) => kgv(res, i), 1);
- }
- res[key] = d;
- });
- return res;
- },
- smallestTimeFrame() {
- return [30,15,10,5][this.size];
- },
- lookingAtToday() {
- return this.days.some(d =>
- d.getFullYear() === this.todayDate.getFullYear() &&
- d.getMonth() === this.todayDate.getMonth() &&
- d.getDate() === this.todayDate.getDate()
- )
- },
- indicatorStyle() {
- return {
- 'pointer-events': 'none',
- 'padding-left': '3.5rem',
- 'margin-top': '-1px',
- 'z-index': 2,
- 'border-color': 'var(--fhc-border)',
- top: this.hourPosition + 'px',
- left: 0,
- right: 0,
- }
- },
- getDayTimePercent() {
- const now = new Date(Date.now())
- const currentMinutes = now.getMinutes() + now.getHours() * 60
- let timePercentage = ((currentMinutes - (this.hours[0] * 60)) / (this.hours.length * 60)) * 100;
-
- return timePercentage
- }
- },
- methods: {
- hourGridIdentifier(hour) {
- // this is the id attribute that is responsible to scroll the calender to the first event
- return 'scroll' + hour + this.focusDate.d + this.week;
- },
- hourGridStyle(hour) {
- return {
- 'pointer-events': 'none',
- top: this.getAbsolutePositionForHour(hour),
- left: 0,
- right: 0,
- 'z-index': 0
- }
- },
- dayGridStyle(day) {
- const styleObj = {
- 'grid-template-columns': 'repeat(' + day.lanes + ', 1fr)',
- 'grid-template-rows': 'repeat(' + (this.hours.length * 60 / this.smallestTimeFrame) + ', 1fr)',
- }
- if(day.isPast) {
- //styleObj['background-color'] = 'var(--calendar-past)'
- styleObj['border-color'] = 'var(--fhc-border)';
- } else if (day.isToday) {
-
- //styleObj['backgroundImage'] = 'linear-gradient(to bottom, var(--calendar-past) '+this.getDayTimePercent+'%, transparent '+this.getDayTimePercent+'%)'
- styleObj['border-color'] = 'var(--fhc-border)';
- }
-
- return styleObj
- },
- eventGridStyle(day, event) {
- return {
- 'z-index': 1,
- 'grid-column-start': 1 + (event.lane - 1) * day.lanes / event.maxLane,
- 'grid-column-end': 1 + event.lane * day.lanes / event.maxLane,
- 'grid-row-start': this.dateToMinutesOfDay(event.start),
- 'grid-row-end': this.dateToMinutesOfDay(event.end),
- 'background-color': event.orig.color,
- 'max-height': '75px'
- };
- },
- calcHourPosition(event) {
- let height = this.$refs['eventsRef' + this.week].getBoundingClientRect().height;
- let top = this.$refs['eventsRef' + this.week].getBoundingClientRect().top;
- let position = event.clientY - top;
- // position percentage of total height
- let timePercentage = (position / height) * 100;
- // minute percentage of total minutes
- let result = (this.hours.length * 60) * (timePercentage / 100);
- // calculate time in float
- let currentMinutes = ((result + (this.hours[0] * 60)) / 60);
- // get hour part of time
- let currentHour = Math.floor(currentMinutes);
- // get float part of time
- let minutePercentage = currentMinutes % currentHour;
- // calculate minutes from float part of time
- let minute = Math.round(60 * minutePercentage);
- // convert minutes to 5 minute interval
- if (minute % 5 != 0) {
- minute = Math.round(minute / 5) * 5;
- }
- // in case the rounding made the minutes 60, increase the hour and reset the minutes
- if (minute == 60) {
- currentHour++;
- minute = 0;
- }
-
- // ## after rounding the time to the nearest 5 Minute interval, we have to convert the time back to the relative position
- // convert current time in minutes
- currentMinutes = currentHour * 60 + minute;
- // calculate the minutes percentage of the total minutes
- timePercentage = ((currentMinutes - (this.hours[0] * 60)) / (this.hours.length * 60)) * 100;
- // calculate the relative position of the time percentage
- position = height * (timePercentage / 100);
- this.hourPosition = position;
-
- // add padding to minutes that consist of only one digit
- minute.toString().length == 1 ? minute = "0" + minute : minute;
- this.hourPositionTime = currentHour + ":" + minute;
- },
- getAbsolutePositionForHour(hour){
- // used for the absolute positioning of the gutters of hours
- return (100 / this.hours.length) * (hour - (24-this.hours.length)) + '%';
- },
- changeToMonth(day) {
- if (!this.noMonthView) {
- this.date.set(day);
- this.focusDate.set(day);
- this.$emit('updateMode', 'month');
- }
- },
- changeToDay(day) {
- this.date.set(day);
- this.focusDate.set(day);
- this.$emit('updateMode', 'day');
- },
- dateToMinutesOfDay(day) {
- // subtract 7 from the total hours because the hours range from 7 to 24
- return Math.floor(((day.getHours()-7) * 60 + day.getMinutes()) / this.smallestTimeFrame) + 1;
- },
- weekPageClick(event, day) {
- this.setSelectedEvent(event);
- this.focusDate.set(new CalendarDate(new Date(event.datum)));
- this.$emit('input', event)
- },
- initResizeObserver() {
- const events = this.$refs['eventsRef'+this.week];
- if (!events) return;
-
- this.resizeObserver = new ResizeObserver((entries) => {
- for (let entry of entries) {
- const { width, height } = entry.contentRect;
- if(width > 0) this.width = width
- }
- });
-
- this.resizeObserver.observe(events);
- },
- destroyResizeObserver() {
- if (this.resizeObserver) {
- this.resizeObserver.disconnect();
- this.resizeObserver = null;
- }
- },
- },
- mounted() {
-
- setTimeout(() => this.$refs.eventcontainer.scrollTop = this.$refs.eventcontainer.scrollHeight / 3 + 1, 0);
-
- const container = document.getElementById("calendarContainer")
- if(container) {
- container.style['overflow-y'] = 'scroll'
- container.style['overflow-x'] = 'auto'
- }
-
- this.initResizeObserver();
- },
- beforeUnmount() {
- this.destroyResizeObserver();
- },
- template: /*html*/`
-
-
-
-
-
-
-
-
-
-
- this is a placeholder which means that no template was passed to the Calendar Page slot
-
-
-
-
-
-
-
-
-
- {{hourPositionTime}}
-
-
-
-
-
-
- {{curTime}}
-
-
-
-
-
- this is a placeholder which means that no template was passed to the Calendar Page slot
-
-
-
-
-
-
-
-
`
-}
diff --git a/public/js/components/Calendar/Weeks.js b/public/js/components/Calendar/Weeks.js
deleted file mode 100644
index 057889e83..000000000
--- a/public/js/components/Calendar/Weeks.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-
-export default {
- mixins: [
- CalendarAbstract
- ],
- emits: [
- 'change'
- ],
- inject: [
- 'size',
- 'focusDate',
- 'mode',
- ],
- props: {
- header: {
- type: Boolean,
- default: true
- }
- },
- computed: {
- weeks(){
- return [...Array(this.focusDate.numWeeks).keys()].map(i => i + 1);
- },
- title() {
- return this.focusDate.format({year: 'numeric'});
- }
- },
- methods: {
- setWeek(week) {
- // TODO(chris): test is there a week jump on year select? => yes there is if the same month/day are in different weeks ... should we prevent that?
- this.focusDate.w = week;
- this.$emit('updateMode', 'week');
- Vue.nextTick(()=>{
- let date = new Date(this.focusDate.y, this.focusDate.m, this.focusDate.d);
- date = date.getFullYear() + "-" +
- String(date.getMonth() + 1).padStart(2, "0") + "-" +
- String(date.getDate()).padStart(2, "0");
- this.$router.push({
- name: "LvPlan",
- params: {
- mode: this.mode[0].toUpperCase() + this.mode.slice(1),
- focus_date: date,
- lv_id: this.$route.params.lv_id || null
- }
- })
- })
- },
- prev(){
- this.focusDate.y--;
- this.focusDate._clean();
- },
- next() {
- this.focusDate.y++;
- this.focusDate._clean();
- },
- },
- template: `
- `
-}
diff --git a/public/js/components/Calendar/Years.js b/public/js/components/Calendar/Years.js
deleted file mode 100644
index baff2eb6c..000000000
--- a/public/js/components/Calendar/Years.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import CalendarAbstract from './Abstract.js';
-import CalendarPane from './Pane.js';
-import CalendarYearsPage from './Years/Page.js';
-
-export default {
- mixins: [
- CalendarAbstract
- ],
- components: {
- CalendarYearsPage,
- CalendarPane
- },
- inject: [
- 'size'
- ],
- data() {
- return {
- start: 0
- }
- },
- computed: {
- range() {
- switch (this.size) {
- case 3:
- // eslint-disable-next-line
- case 2:
- return 24;
- }
- return 12;
- },
- end() {
- return this.start + this.range - 1;
- },
- title() {
- return this.start + ' - ' + this.end;
- }
- },
- methods: {
- paneChanged(dir) {
- this.start += this.range * dir;
- },
- prev() {
- this.$refs.pane.prev();
- this.$emit('change:offset', { y: -1, m: 0, d: 0 });
- },
- next() {
- this.$refs.pane.next();
- this.$emit('change:offset', { y: 1, m: 0, d: 0 });
- }
- },
- created() {
- this.start = this.focusDate.y - this.focusDate.y%this.range;
- },
- template: `
-
-
-
-
-
-
`
-}
diff --git a/public/js/components/Calendar/Years/Page.js b/public/js/components/Calendar/Years/Page.js
deleted file mode 100644
index b7461cd03..000000000
--- a/public/js/components/Calendar/Years/Page.js
+++ /dev/null
@@ -1,36 +0,0 @@
-export default {
- inject: [
- 'focusDate'
- ],
- props: {
- start: Number,
- end: Number
- },
- emits: [
- 'updateMode'
- ],
- data() {
- return {
- }
- },
- computed: {
- years() {
- return [...Array(this.end - this.start).keys()].map(i => i + this.start);
- }
- },
- mounted() {
- const container = document.getElementById("calendarContainer")
- if(container) {
- container.style['overflow-y'] = 'scroll'
- container.style['overflow-x'] = 'auto'
- }
- },
- template: `
- `
-}
diff --git a/public/js/components/Cis/Mylv/LvModal.js b/public/js/components/Cis/Mylv/LvModal.js
deleted file mode 100644
index 3f52cec8f..000000000
--- a/public/js/components/Cis/Mylv/LvModal.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import BsModal from "../../Bootstrap/Modal.js";
-import Alert from "../../Bootstrap/Alert.js";
-import LvMenu from "./LvMenu.js"
-
-import ApiLvPlan from '../../../api/factory/lvPlan.js';
-import ApiAddons from '../../../api/factory/addons.js';
-
-export default {
- components: {
- BsModal,
- Alert,
- LvMenu,
- },
- mixins: [BsModal],
- props: {
- event:Object,
- title:{
- type:String,
- default:"title"
- },
- showMenu:{
- type:Boolean,
- default:true,
- },
- /*
- * NOTE(chris):
- * Hack to expose in "emits" declared events to $props which we use
- * in the v-bind directive to forward all events.
- * @see: https://github.com/vuejs/core/issues/3432
- */
- onHideBsModal: Function,
- onHiddenBsModal: Function,
- onHidePreventedBsModal: Function,
- onShowBsModal: Function,
- onShownBsModal: Function,
- },
- data() {
- return {
- menu: null,
- result: false,
- info: null,
- };
- },
- methods:{
- onHideModal: function(){
- this.menu = null;
- },
- onModalShow: function()
- {
- // do not load the menu if the menu is not getting rendered
- if(!this.showMenu) return;
-
- if (this.event.type == 'lehreinheit') {
- this.$api
- .call(ApiLvPlan.getLehreinheitStudiensemester(Array.isArray(this.event.lehreinheit_id) ? this.event.lehreinheit_id[0] : this.event.lehreinheit_id))
- .then(res => res.data)
- .then(studiensemester_kurzbz => this.$api.call(
- ApiAddons.getLvMenu(
- this.event.lehrveranstaltung_id,
- studiensemester_kurzbz
- )
- ))
- .then(res => {
- if (res.data) {
- this.menu = res.data;
- }
- });
- }
- }
- },
- mounted() {
- this.modal = this.$refs.modalContainer.modal;
- },
- popup(options) {
- return BsModal.popup.bind(this)(null, options);
- },
- template: /*html*/ `
-
-
-
-
-
-
-
-
- {{$p.t('lehre','lehrveranstaltungsmenue')}}
-
-
-
-
-
-
-
-
- `,
-};
diff --git a/public/js/composables/CalendarDate.js b/public/js/composables/CalendarDate.js
deleted file mode 100644
index e85cbeeac..000000000
--- a/public/js/composables/CalendarDate.js
+++ /dev/null
@@ -1,274 +0,0 @@
-import {user_locale} from "../plugin/Phrasen.js";
-import CalendarDates from "./CalendarDates.js";
-
-const monthDayRanges = []
-for(let i = 1; i < 13; i++) {
- monthDayRanges.push(new Date(new Date(Date.now()).getFullYear(), i, 0).getDate())
-}
-
-class CalendarDate {
- constructor(y, m, d) {
- this.weekStart = CalendarDate.getWeekStart();
- this.watchLocale = Vue.watch(
- user_locale,
- (newLocale, oldLocale, onCleanup) =>{
- this.weekStart = CalendarDate.getWeekStart();
- this._clean();
- onCleanup((cleanup)=>{
- // do clean up
- });
- },
-
- );
- this.set(y, m, d);
- this._clean();
- CalendarDates.subscribe(this);
- }
- get y() { return this._y }
- set y(v) { this._y = v; this._clean() }
- get m() { return this._m }
- set m(v) { this._m = v; this._clean() }
- get d() { return this._d }
- set d(v) { this._d = v; this._clean() }
- /**
- * @see https://www.smart-rechner.de/kalenderwochen/rechner.php
- */
- get w() {
- if (this._w === null) {
- if (this.weekStart == 1 && this._m == 11 && this._d > 28 && this.wd <= this._d-29) {
- this._w = 1;
- } else if (this.weekStart == 1 && this._m == 0 && this._d < 4 && 3-this.wd <= -this._d) {
- let weekStartOfTheYear = new Date(this.y-1, 0, this.weekStart == 1 ? 4 : 1);
- weekStartOfTheYear.setDate(weekStartOfTheYear.getDate() - (weekStartOfTheYear.getDay() + 7 - this.weekStart)%7);
- this._w = Math.ceil((Math.floor((new Date(this.y, this.m, this.d) - weekStartOfTheYear) / 86400000) + 1) / 7);
- } else {
- let weekStartOfTheYear = new Date(this.y, 0, this.weekStart == 1 ? 4 : 1);
- weekStartOfTheYear.setDate(weekStartOfTheYear.getDate() - (weekStartOfTheYear.getDay() + 7 - this.weekStart)%7);
- this._w = Math.ceil((Math.floor((new Date(this.y, this.m, this.d) - weekStartOfTheYear) / 86400000) + 1) / 7);
- }
- }
- return this._w;
- }
- set w(v) {
- if (this.w != v) {
- let lw = this.numWeeks;
-
- this.d += (v - this.w) * 7;
-
- if (v > 0 && v <= lw && this.w != v) {
- if (this.weekStart != 1) {
- if (this.w == 1) {
- this.set(this.firstDayOfWeek);
- } else {
- this.set(this.lastDayOfWeek);
- }
- }
- if (this.w != v) {
- console.error('couldn\'t set the week', this, v);
- }
- }
- }
- }
- get wYear() {
- if( this.w === 1 ) {
- return this.cdLastDayOfWeek.y;
- }
- return this.cdFirstDayOfWeek.y;
- }
- get wd() {
- if (this._wd === null) {
- // the .getDay() method from js Date object ALWAYS returns values from 0 to 6, where 0 is Sunday and 6 is Saturday
- // aligns the getDay() result of the Date to the weekStart of the CalendarDate
- this._wd = ((new Date(this.y, this.m, this.d)).getDay()+7-this.weekStart)%7;
- }
- return this._wd;
- }
- get firstDayOfWeek() {
- let firstDayOfWeek = new Date(this.y, this.m, this.d);
- // to ensure that firstDayOfWeek.getDay() is always greater than this.weekStart we add 7 and wrap the result around with %7 to avoid negative numbers
- firstDayOfWeek.setDate(this.d -(firstDayOfWeek.getDay()+7-this.weekStart)%7);
- return firstDayOfWeek;
- }
- get cdFirstDayOfWeek() {
- let FirstDayOfWeek = new CalendarDate(this.firstDayOfWeek);
- return FirstDayOfWeek;
- }
- get lastDayOfWeek() {
- let lastDayOfWeek = new Date(this.y, this.m, this.d);
- // uses the calculation from firstDayOfWeek and adds 6 days to the result to get the last day of the week
- lastDayOfWeek.setDate(this.d -(lastDayOfWeek.getDay()+7-this.weekStart)%7 +6);
- return lastDayOfWeek;
- }
- get wholeWorkWeek() {
- const days = []
- const date = new Date(this.y, this.m, this.d);
- for(let i = 0; i < 5; i++) {
- days[i] = new Date(this.y, this.m, this.d)
- days[i].setDate(this.d -(date.getDay()+7-this.weekStart)%7 + i)
- }
- return days
- }
- get cdLastDayOfWeek() {
- let LastDayOfWeek = new CalendarDate(this.lastDayOfWeek);
- return LastDayOfWeek;
- }
- get firstDayOfCalendarMonth() {
- let firstDayOfMonth = new Date(this.y, this.m, 1);
- let offset = (firstDayOfMonth.getDay() + 7 - this.weekStart) % 7;
- // offset will be greater than 1 most of the time, using a negative number for a date returns a date in the past
- return new Date(this.y, this.m, 1-offset);
- }
- get cdFirstDayOfCalendarMonth() {
- let firstDayOfMonth = new Date(this.y, this.m, 1);
- let offset = (firstDayOfMonth.getDay() + 7 - this.weekStart) % 7;
- let FirstDayOfCalendarMonth = new CalendarDate(this.y, this.m, 1 - offset);
- return FirstDayOfCalendarMonth;
- }
- get lastDayOfCalendarMonth() {
- // In JavaScript, the Date constructor interprets: A day of 0 as the last day of the previous month
- let lastDayOfMonth = new Date(this.y, this.m+1, 0);
- let offset = (lastDayOfMonth.getDay() + 7 - this.weekStart) % 7;
- return new Date(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth(), lastDayOfMonth.getDate()+6-offset);
- }
- get cdLastDayOfCalendarMonth() {
- let lastDayOfMonth = new Date(this.y, this.m+1, 0);
- let offset = (lastDayOfMonth.getDay() + 7 - this.weekStart) % 7;
- let LasyDayOfCalendarMonth = new CalendarDate(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth(), lastDayOfMonth.getDate() + 6 - offset);
- return LasyDayOfCalendarMonth;
- }
- get cdLastDayOfNextCalendarMonth() {
- let lastDayOfMonth = new Date(this.y, this.m+1, 0);
- let offset = (lastDayOfMonth.getDay() + 7 - this.weekStart) % 7;
- let LastDayOfNextCalendarMonth = new CalendarDate(lastDayOfMonth.getFullYear(), lastDayOfMonth.getMonth() + 1, lastDayOfMonth.getDate() + 6 - offset);
- return LastDayOfNextCalendarMonth;
- }
- get nextSevenDays() {
- const days = []
-
- for(let i = 0; i < 7; i++) {
- days[i] = new Date(this.y, this.m, this.d + i)
- }
- return days
- }
- get numWeeks() {
- // if the week starts with Monday we have to go 3 days in the past from the start of the next year to get the correct numWeek of the current year
- // this is because for example 30.12.2024 - 05.01.2025 is the first calendarWeek of 2025
- let lastCalendarWeek = new CalendarDate(this.y + 1, 0, this.weekStart == 1 ? -3 : 0);
- return lastCalendarWeek.w;
- }
- set(y,m,d,noClean) {
-
- if (y !== undefined && (m === undefined || m === true) && d === undefined) {
- if (this.isDate(y))
- {
- // set year/month/day from date object
- return this.set(y.getFullYear(), y.getMonth(), y.getDate(), m);
- }
- if (y.y !== undefined && y.m !== undefined && y.d !== undefined)
- {
- // set year/month/day from CalendarDate object
- return this.set(y.y, y.m, y.d, m);
- }
- }
- // initialize year/month/day
- this._y = y ?? 0;
- this._m = m ?? 0;
- this._d = d ?? 0;
-
- if (!noClean)
- this._clean();
- }
- _clean() {
- this.set(new Date(this._y, this._m, this._d), true);
- this._w = null;
- this._wd = null;
- }
- format(options, lang=undefined) {
- return (new Date(this._y, this._m, this._d)).toLocaleString(lang, options);
- }
- compare(d) {
- if (this.isDate(d))
- return (this.y === d.getFullYear() && this.m === d.getMonth() && this.d === d.getDate());
- return (this.y === d.y && this.m === d.m && this.d === d.d);
- }
- isInWeek(w, y) {
- if (this.y == y && this.w == w)
- return true;
- if (this.weekStart == 1)
- return false;
- let edgeDay = this.cdFirstDayOfWeek;console.log(edgeDay);
- if (edgeDay.y == y && edgeDay.w == w)
- return true;
- edgeDay = this.cdLastDayOfWeek;
- if (edgeDay.y == y && edgeDay.w == w)
- return true;
- return false;
- }
-
- setLocale(locale) {
- this.weekStart = CalendarDate.getWeekStart(locale);
- }
- // method that checks if the parameter is of type Date
- isDate(obj){
- return Object.prototype.toString.call(obj) === '[object Date]';
- }
- cleanup(){
- if(this.watchLocale && this.watchLocale.stop) this.watchLocale.stop(); // TODO: ?
- }
- moveMonthInDirection (dir) {
- // avoid setting date in wrong month if we try to create a date which does not exist like 30th of february
- const newM = this.m + dir
- const maxDaysTarget = monthDayRanges[newM]
-
- if (this.d > maxDaysTarget) this.d = maxDaysTarget
-
- this.m = newM // calls _clean which sets a new Date
- }
-}
-/**
- * Returns the weekday number (Date.getDay()) on which the week starts depending on the locale.
- * This can be Saturday(6), Sunday(0) or Monday(1)
- *
- * @see https://stackoverflow.com/questions/53382465/how-can-i-determine-if-week-starts-on-monday-or-sunday-based-on-locale-in-pure-j
- *
- * @param string locale
- *
- * @return integer
- */
-CalendarDate.getWeekStart = function(locale) {
-
- locale = user_locale.value || locale || navigator.language;
- const parts = locale.match(/^([a-z]{2,3})(?:-([a-z]{3})(?=$|-))?(?:-([a-z]{4})(?=$|-))?(?:-([a-z]{2}|\d{3})(?=$|-))?/i);
-
- const language_code = parts[1];
- const language_starting_Sat = ['ar','arq','arz','fa'];
- const language_starting_Sun = 'amasbndzengnguhehiidjajvkmknkolomhmlmrmtmyneomorpapssdsmsnsutatethtnurzhzu'.match(/../g);
-
- const region_code = parts[4];
- const region_starting_Sat = 'AEAFBHDJDZEGIQIRJOKWLYOMQASDSY'.match(/../g);
- const region_starting_Sun = 'AGARASAUBDBRBSBTBWBZCACNCODMDOETGTGUHKHNIDILINJMJPKEKHKRLAMHMMMOMTMXMZNINPPAPEPHPKPRPTPYSASGSVTHTTTWUMUSVEVIWSYEZAZW'.match(/../g);
-
- if (region_code){
- if (region_starting_Sun.includes(region_code))
- return 0;
- else if (region_starting_Sat.includes(region_code))
- return 6;
- else
- return 1;
- }
- else if(language_code)
- {
- if (language_starting_Sun.includes(language_code))
- return 0;
- else if (language_starting_Sat.includes(language_code))
- return 6;
- else
- return 1;
- }
- else
- {
- return 1;
- }
-}
-
-export default CalendarDate
diff --git a/public/js/composables/CalendarDates.js b/public/js/composables/CalendarDates.js
deleted file mode 100644
index a34198441..000000000
--- a/public/js/composables/CalendarDates.js
+++ /dev/null
@@ -1,16 +0,0 @@
-class CalendarDates{
-
- static subscribers_array = [];
-
- static subscribe(subscriber){
- this.subscribers_array.push(subscriber);
- }
- static unsubscribe(subscriber){
- this.subscribers_array = this.subscribers_array.filter(sub => !sub.compare(subscriber));
- }
- static cleanup(){
- this.subscribers_array.forEach(sub => { sub.cleanup() });
- }
-}
-
-export default CalendarDates;
\ No newline at end of file