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*/` -
-
-
-
-
-
-
{{dayText.tag}}
- {{dayText.datum}} -
-
-
- -
-
-
- -
-
- - - -
-

{{ $p.t('lehre/noLvFound') }}

-
- -
- {{hourPositionTime}} -
-
- -
- {{curTime}} -
-
-
-
-
{{hour}}:00
-
-
- -
-
- -
- -

this is a slot placeholder

-
-
- -
- -

this is a slot placeholder

-
-
- -
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
- -` -} 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*/` -
-
-
- {{dayText[day]}} -
- -
-` -} 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*/` -
-
-
-
-
{{dayText[day]?.tag}}
- {{dayText[day]?.datum}} -
-
-
-
-
- -
-
-
-
- -
- {{hourPositionTime}} -
-
-
-
{{hour}}:00
-
-
- -
- {{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*/ ` - - - - - - - `, -}; 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