0byt3m1n1
Path:
/
home
/
mgatv524
/
mixmidia.mgaplay.com.br
/
modules
/
src
/
[
Home
]
File: xibo-calendar-render.js
/** * Copyright (C) 2021 Xibo Signage Ltd * * Xibo - Digital Signage - http://www.xibo.org.uk * * This file is part of Xibo. * * Xibo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * Xibo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Xibo. If not, see <http://www.gnu.org/licenses/>. */ jQuery.fn.extend({ xiboCalendarRender: function(options, events) { // Default options const defaults = { duration: '30', previewWidth: 0, previewHeight: 0, scaleOverride: 0, }; options = $.extend({}, defaults, options); // Global constants const TODAY = moment(); const INITIAL_YEAR = moment().year(); // NOTE: month format for momentjs is 1-12 and month value is zero indexed const INITIAL_MONTH = moment().month(); const INITIAL_DATE = moment().date(); const TIME_FORMAT = options.timeFormat || 'HH:mm'; const DEFAULT_DAY_START_TIME = moment().startOf('day').format(TIME_FORMAT); const DEFAULT_DAY_END_TIME = moment().endOf('day').format(TIME_FORMAT); const GRID_STEP = options.gridStep && options.gridStep > 0 ? options.gridStep : 60; const DEFAULT_FONT_SIZE = 16; const DEFAULT_FONT_SCALE = options.textScale || 1; // Global vars for all calendar types let maxEventPerDay; let maxEventPerDayWithExtra; let weekdaysNames = moment.weekdays(true); if (options.weekdayNameLength == 'short') { weekdaysNames = moment.weekdaysMin(true); } else if (options.weekdayNameLength == 'medium') { weekdaysNames = moment.weekdaysShort(true); } let monthsNames = moment.months(); if (options.monthNameLength == 'short') { monthsNames = moment.monthsShort(); } // Main functions to be overriden let createCalendar = () => {}; let addEventsToCalendar = () => {}; /** * Apply style based on options */ function applyStyleOptions() { $('#content').toggleClass('hide-header', options.showHeader != '1'); $('#content').toggleClass('hide-weekend', options.excludeWeekendDays === '1'); $(':root').css('font-size', DEFAULT_FONT_SIZE * DEFAULT_FONT_SCALE); options.mainBackgroundColor && $(':root').css('--main-background-color', options.mainBackgroundColor); options.gridColor && $(':root').css('--grid-color', options.gridColor); options.gridTextColor && $(':root').css('--grid-text-color', options.gridTextColor); options.dayBgColor && $(':root').css('--day-bg-color', options.dayBgColor); options.dayTextColor && $(':root').css('--day-text-color', options.dayTextColor); options.todayTextColor && $(':root').css('--today-text-color', options.todayTextColor); options.nowMarkerColor && $(':root').css('--now-marker-color', options.nowMarkerColor); options.dayOtherMonthBgColor && $(':root').css( '--day-other-month-bg-color', options.dayOtherMonthBgColor, ); options.dayOtherMonthTextColor && $(':root').css( '--day-other-month-text-color', options.dayOtherMonthTextColor, ); options.headerBgColor && $(':root').css('--header-bg-color', options.headerBgColor); options.headerTextColor && $(':root').css('--header-text-color', options.headerTextColor); options.weekDaysHeaderBgColor && $(':root').css('--weekdays-bg-color', options.weekDaysHeaderBgColor); options.weekDaysHeaderTextColor && $(':root').css( '--weekdays-text-color', options.weekDaysHeaderTextColor, ); options.eventBgColor && $(':root').css('--event-bg-color', options.eventBgColor); options.eventTextColor && $(':root').css('--event-text-color', options.eventTextColor); options.dailyEventBgColor && $(':root').css('--daily-event-bg-color', options.dailyEventBgColor); options.dailyEventTextColor && $(':root').css('--daily-event-text-color', options.dailyEventTextColor); options.multiDayEventBgColor && $(':root').css( '--multi-day-event-bg-color', options.multiDayEventBgColor, ); options.multiDayEventTextColor && $(':root').css( '--multi-day-event-text-color', options.multiDayEventTextColor, ); options.aditionalEventsBgColor && $(':root').css( '--aditional-events-bg-color', options.aditionalEventsBgColor, ); options.aditionalEventsTextColor && $(':root').css( '--aditional-events-text-color', options.aditionalEventsTextColor, ); options.noEventsBgColor && $(':root').css( '--no-events-bg-color', options.noEventsBgColor, ); options.noEventsTextColor && $(':root').css( '--no-events-text-color', options.noEventsTextColor, ); } /** * Get week day number by date * @param {string} date date string * @return {number} week day number */ function getWeekday(date) { return moment(date).weekday() + 1; } /** * Create a marker showing current time * @param {object} $container target container * @param {object} timeData data with start and end dates for the view */ function createNowMarker($container, timeData) { const dayViewDuration = timeData.end - timeData.start; const $nowMarker = $('<div class="now-marker">'); const nowTimeInMinutes = moment .duration( moment(TODAY).diff( moment(TODAY).startOf('day'), ), ) .as('minutes'); // Skip if it's not included in the selected delta time view if ( nowTimeInMinutes >= timeData.end || nowTimeInMinutes <= timeData.start ) { return; } // Calculate position const eventPositionPerc = ( nowTimeInMinutes / dayViewDuration - timeData.start / dayViewDuration ) * 100; $nowMarker.css( 'top', eventPositionPerc + '%', ); // Append marker to container $nowMarker.appendTo($container); } /** * Add events to calendar */ function addEventsToCalendarBase() { events.forEach((event) => { const startDate = moment(event.startDate).startOf('date'); // Check if event is an all day // (startDate 00:00 day 1, endDate 00:00 day after last day) const allDayEvent = moment(event.startDate).isSame(startDate) && moment(event.endDate).isSame(moment(event.endDate).startOf('date')); event.allDay = allDayEvent; const endDate = allDayEvent ? moment(event.endDate).startOf('date').subtract(1, 'd') : moment(event.endDate).startOf('date'); const eventTotalDays = endDate.diff(startDate, 'days') + 1; let currentDayOfEvent = 1; // Days loop const momentAux = moment(startDate); while (momentAux <= endDate) { addEventToDay(momentAux, event, eventTotalDays, currentDayOfEvent); currentDayOfEvent++; momentAux.add(1, 'd'); } }); } /** * Add event to specific day * @param {object} date momentjs date * @param {object} event * @param {number} eventTotalDays * @param {number} currentDayOfEvent */ function addEventToDay(date, event, eventTotalDays, currentDayOfEvent) { /** * Get container by date * @param {object} date * @return {object} Jquery container */ function getEventContainer(date) { return (options.calendarType == 2) ? $('.calendar-day .calendar-events-container') : $('#day_' + date.date()).find('.calendar-events-container'); } /** * Get all days container by date * @param {object} date * @return {object} Jquery container */ function getAllDayEventsContainer(date) { return (options.calendarType == 2) ? $('.calendar-day .calendar-all-day-events-container') : $('#day_' + date.date()).find('.calendar-all-day-events-container'); } const $newEvent = $('<div class="calendar-event">'); const weekDay = getWeekday(date); let eventDuration = 1; // Mark event as an all day if (event.allDay) { $newEvent.addClass('all-day'); } if (eventTotalDays > 1) { // Multiple day event let htmlToAdd = '<span class="event-summary">' + event.summary + '</span>'; // Mark as multi event $newEvent.addClass('multi-day'); // Draw only on the first day of the event // or at the beggining of the weeks when it breaks if (currentDayOfEvent == 1 || weekDay == 1) { if (currentDayOfEvent == 1 && !event.allDay) { htmlToAdd = '<div class="event-time">' + moment(event.startDate).format(TIME_FORMAT) + '</div>' + htmlToAdd; } // Show event content in multiple days $newEvent.html(htmlToAdd); // Update element duration based on event duration eventDuration = eventTotalDays - (currentDayOfEvent - 1); const remainingDays = 8 - weekDay; if (eventDuration > remainingDays) { eventDuration = remainingDays; $newEvent.addClass('cropped-event-end'); } if (currentDayOfEvent > 1) { $newEvent.addClass('cropped-event-start'); } $newEvent.css( 'width', 'calc(' + eventDuration * 100 + '% + ' + eventDuration * 2 + 'px)', ); } else { // Multiple event that was extended, no need to be rendered return; } } else { // Single day event let htmlToAdd = '<div class="event-summary">' + event.summary + '</div>'; // Mark event as an all day if (event.allDay) { $newEvent.addClass('all-day'); } else { htmlToAdd = htmlToAdd + '<div class="event-time">' + moment(event.startDate).format(TIME_FORMAT) + ' - ' + moment(event.endDate).format(TIME_FORMAT) + '</div>'; } // Add inner html $newEvent.html(htmlToAdd); } // All day or multi day events if (eventTotalDays > 1 || event.allDay) { // If there's at least one daily event // enable the container in the calendar view $('.calendar-container').addClass('show-all-day-events'); const $dailyEventsContainer = getAllDayEventsContainer(date); // Calculate event slot let slots = $dailyEventsContainer.data('slots'); let daySlot; if (slots != undefined) { for (let index = 0; index < slots.length; index++) { const slot = slots[index]; if (slot === undefined) { daySlot = index; slots[index] = 1; break; } } if (daySlot === undefined) { daySlot = slots.length; slots.push(1); } } else { daySlot = 0; slots = [1]; } $dailyEventsContainer.data('slots', slots); // Extend event to the remaining days if (eventDuration > 1 && options.calendarType != 2) { for (let dayAfter = 1; dayAfter < eventDuration; dayAfter++) { const $newContainer = getAllDayEventsContainer( moment(date).add(dayAfter, 'd'), ); let dataSlots = $newContainer.data('slots'); if (dataSlots === undefined) { dataSlots = []; } dataSlots[daySlot] = 2; $newContainer.data('slots', dataSlots); } } $newEvent.css('top', 1.875 * daySlot + 'rem'); // Append event to container $newEvent.appendTo($dailyEventsContainer); // Check container height and slots to show number of extra events updateContainerExtraEvents($dailyEventsContainer, slots); } else { // Daily timed event const $eventsContainer = getEventContainer(date); const containerData = $('.hour-grid').data(); const dayViewDuration = containerData.end - containerData.start; const eventData = { start: moment .duration( moment(event.startDate).diff( moment(event.startDate).startOf('day'), ), ) .as('minutes'), duration: moment .duration(moment(event.endDate).diff(moment(event.startDate))) .as('minutes'), }; // Skip event if it's not included in the selected delta time view if ( eventData.start >= containerData.end || eventData.start + eventData.duration <= containerData.start ) { return; } // Calculate position let eventPositionPerc = ( eventData.start / dayViewDuration - containerData.start / dayViewDuration ) * 100; let eventHeightAdj = 0; // Check if event starts before view time if (eventPositionPerc < 0) { $newEvent.addClass('before-view'); eventHeightAdj = eventPositionPerc; eventPositionPerc = 0; } $newEvent.css( 'top', eventPositionPerc + '%', ); // Calculate event slot let slots = $eventsContainer.data('slots'); let eventLevel = 0; if (slots != undefined) { let newLevel = 0; for (let index = 0; index < slots.length; index++) { const slot = slots[index]; if ( !( eventData.start >= slot.end || eventData.start + eventData.duration <= slot.start ) ) { newLevel = slot.level + 1; } } slots.push({ level: newLevel, start: eventData.start, end: eventData.start + eventData.duration, }); eventLevel = newLevel; } else { slots = [ { level: 0, start: eventData.start, end: eventData.start + eventData.duration, }, ]; eventLevel = 0; } // Update container slots $eventsContainer.data('slots', slots); // Assign level $newEvent.addClass('level-' + eventLevel); $newEvent.toggleClass('concurrent', eventLevel > 0); // Calculate height const eventHeight = ((eventData.duration / dayViewDuration) * 100) + eventHeightAdj; $newEvent.height(eventHeight + '%'); // Append event to container $newEvent.appendTo($eventsContainer); // Mark shorter events to be styled if ($newEvent.height() < parseFloat($newEvent.css('font-size')) * 2) { $newEvent.addClass('shorter-event'); $newEvent.find('.event-time').prependTo($newEvent); } } } /** * Update container and hide the number of extra events * @param {object} $container jquery container * @param {object} slots occupied slots */ function updateContainerExtraEvents($container, slots) { // Calculate max events per container (if not defined) if (!maxEventPerDay) { const bodyFontSize = parseInt( $('body').css('font-size').split('px')[0], ); const singleEventHeight = bodyFontSize * 1.25 + bodyFontSize * 0.5 + bodyFontSize * 0.125; const extraEventsHeight = bodyFontSize * 1.25 + bodyFontSize * 0.125; maxEventPerDay = Math.floor($container.height() / singleEventHeight); maxEventPerDayWithExtra = Math.floor( ($container.height() - extraEventsHeight) / singleEventHeight, ); } // If we show extra. get number of events // that fit inside that space ( smaller ) let maxEventsForThisDay = maxEventPerDay; if (slots.length > maxEventPerDay) { maxEventsForThisDay = maxEventPerDayWithExtra; } // Get number of dummy events that were generated // by extended events in the visible elements const numberOfExtendedEvents = slots.filter((ev, idx) => { return ev == 2 && idx <= maxEventsForThisDay; }).length; // Remove extra elements if ( maxEventsForThisDay > 0 && maxEventsForThisDay > numberOfExtendedEvents ) { $container .find( '.calendar-event:gt(' + (maxEventsForThisDay - numberOfExtendedEvents - 1) + ')', ) .remove(); } else { $container.find('.calendar-event').remove(); } // Calculate number of events that were hidden let numEventsToHide = 0; for (let index = maxEventsForThisDay; index < slots.length; index++) { if (slots[index] != undefined) { numEventsToHide++; } } // Fix for extended events let numExtendedEventsFix = 0; if (maxEventsForThisDay > 0 && maxEventsForThisDay < numberOfExtendedEvents) { numExtendedEventsFix = numberOfExtendedEvents - maxEventsForThisDay; } numEventsToHide -= numExtendedEventsFix; // Update extra events label if (numEventsToHide > 0) { if ($container.find('.extra-events').length > 0) { $container.find('.extra-events span').html('+ ' + numEventsToHide); } else { $container.append( '<div class="extra-events"><span>+ ' + numEventsToHide + '</span></div>', ); } } } /** * Create hour grid * @param {object} $container jquery container * @param {string} start * @param {string} end */ function createCalendarHourGrid($container, start, end) { start = start == '' ? moment(DEFAULT_DAY_START_TIME, TIME_FORMAT) : moment(start, TIME_FORMAT); end = end == '' ? moment(DEFAULT_DAY_END_TIME, TIME_FORMAT) : moment(end, TIME_FORMAT); // Hour loop const momentAux = moment(start); while (momentAux <= end) { $container.append( '<li class="hour-time">' + momentAux.format(TIME_FORMAT) + '</li>', ); momentAux.add(GRID_STEP, 'm'); } // Save properties to the container for later use const containerData = { start: moment .duration(moment(start).diff(moment(start).startOf('day'))) .as('minutes'), end: moment .duration(moment(end).diff(moment(end).startOf('day'))) .as('minutes'), }; $container.data(containerData); // Calculate hour grid spacing const gridSpacing = (1 / ((containerData.end - containerData.start) / GRID_STEP)) * 100; $container.find('.hour-time').height(gridSpacing + '%'); } // Functions by calendar type if (options.calendarType == 1) { // Schedule View const $calendarContainer = $('.calendar-container'); let addedEvents = 0; /** * Add event to specific day (override) * @param {object} date momentjs date * @param {object} event * @param {number} eventTotalDays * @param {number} currentDayOfEvent */ function addEventToDay(date, event, eventTotalDays, currentDayOfEvent) { /** * Get container by date * @param {object} date * @return {object} Jquery container */ function getDayContainer(date) { let $dayContainerTemp; let $dayContainerTitle; if ($('#day_' + date.month() + '_' + date.date()).length > 0) { $dayContainerTemp = $('#day_' + date.month() + '_' + date.date()); } else { $dayContainerTemp = $('<div>').attr('id', 'day_' + date.month() + '_' + date.date()).addClass('day-container'); $dayContainerTitle = $('<div class="title-container">').appendTo( $dayContainerTemp); if (moment(date).startOf('d').isSame(moment(TODAY).startOf('d'))) { $dayContainerTemp.addClass('today'); } $('<div class="day-title-date">' + date.date() + '</div>').appendTo($dayContainerTitle); $('<div class="day-title-month">' + monthsNames[date.month()] + '</div>').appendTo($dayContainerTitle); $('<div class="day-title-weekday">' + weekdaysNames[date.weekday()] + '</div>').appendTo($dayContainerTitle); $('<div class="day-events">').appendTo($dayContainerTemp); $dayContainerTemp.appendTo($calendarContainer); } // $calendarContainer return $dayContainerTemp; } const $newEvent = $('<div class="calendar-event">'); const $dayContainer = getDayContainer(date); const $dayContainerEvents = $dayContainer.find('.day-events'); let htmlToAdd = ''; // Add time if (!event.allDay) { if (currentDayOfEvent == 1) { htmlToAdd += '<span class="event-start-time">' + moment(event.startDate).format(TIME_FORMAT) + '</span>'; // Save start date to object data $newEvent.data('start', event.startDate); if (eventTotalDays == 1) { htmlToAdd += ' - <span class="event-end-time">' + moment(event.endDate).format(TIME_FORMAT) + '</span>'; } } } else { // Mark event as an all day $newEvent.addClass('all-day'); } // Mark event as a multi-day if (eventTotalDays > 1) { $newEvent.addClass('multi'); } // Add summary htmlToAdd += '<span class="event-summary">' + event.summary + '</span>'; if (options.showDescription == 1 && event.description) { htmlToAdd += '<div class="event-description">' + event.description + '</div>'; } // Add inner html $newEvent.html(htmlToAdd); // Append event to container if (event.allDay) { $newEvent.prependTo($dayContainerEvents); } else { $newEvent.appendTo($dayContainerEvents); } addedEvents++; } // Override addEventsToCalendarBase addEventsToCalendar = function(events) { events.forEach((event) => { const startDate = moment(event.startDate).startOf('date'); // Check if event is an all day // (startDate 00:00 day 1, endDate 00:00 day after last day) const allDayEvent = moment(event.startDate).isSame(startDate) && moment(event.endDate).isSame(moment(event.endDate).startOf('date')); event.allDay = allDayEvent; const endDate = allDayEvent ? moment(event.endDate).startOf('date').subtract(1, 'd') : moment(event.endDate).startOf('date'); const eventTotalDays = endDate.diff(startDate, 'days') + 1; let currentDayOfEvent = 1; // Days loop const momentAux = moment(startDate); while (momentAux <= endDate) { let startDate = null; let endDate = null; // If we're using a date range // show only events from the start date onwards // and before the end date if (options.useDateRange == 1) { (options.rangeStart) && (startDate = moment(options.rangeStart)); (options.rangeEnd) && (endDate = moment(options.rangeEnd)); } else { startDate = TODAY.startOf('d'); } // Add event if ((!startDate || momentAux >= startDate) && (!endDate || momentAux <= endDate)) { addEventToDay( momentAux, event, eventTotalDays, currentDayOfEvent); } currentDayOfEvent++; momentAux.add(1, 'd'); } }); // Create now marker if (options.showNowMarker == 1) { const $nowMarker = $('<div class="now-marker">'); const $dayEventsContainer = $calendarContainer.find('#day_' + moment(TODAY).month() + '_' + moment(TODAY).date() + ' .day-events'); let $targetElement; if ($dayEventsContainer.length > 0) { // Calculate position $dayEventsContainer.find('.calendar-event').each((idx, event) => { const start = $(event).data('start'); if (start && TODAY < moment(start)) { $targetElement = $(event); $nowMarker.insertBefore($targetElement); return false; } }); // Append marker to container if (!$targetElement) { $nowMarker.appendTo($dayEventsContainer); } } } // Show no events message if (options.noEventsMessage != '' && addedEvents == 0) { $calendarContainer.append('<div class="no-events-message">' + options.noEventsMessage + '</div>'); } // noEventsMessage }; } else if (options.calendarType == 2) { // Daily View const $hourGrid = $('.hour-grid'); const $dayTitle = $('.day-title'); const $dayContainer = $('.day-container'); createCalendar = function() { // Add day label const $weekDay = $('<li>'); $dayTitle.append($weekDay); $weekDay.html('<div class="week-day">' + weekdaysNames[TODAY.weekday()] + '</div>'); const today = moment(TODAY); const day = { date: today.format('YYYY-MM-DD'), dayOfMonth: today.date(), }; const $dayOfMonthElement = $('<div class="week-day-date">'); $dayOfMonthElement.html(day.dayOfMonth); $weekDay.append($dayOfMonthElement); // Add hour grid createCalendarHourGrid($hourGrid, options.startTime, options.endTime); // Add day element const $dayElement = $('<li id="day_' + day.dayOfMonth + '">'); $dayElement.addClass('calendar-day'); // Append all day events container $dayElement.append( $('<div class="calendar-all-day-events-container">'), ); // Append normal events container $dayElement.append($('<div class="calendar-events-container">')); // Append day to container $dayContainer.append($dayElement); if (options.showNowMarker == 1) { createNowMarker( $dayElement.find('.calendar-events-container'), $('.hour-grid').data(), ); } }; addEventsToCalendar = addEventsToCalendarBase; } else if (options.calendarType == 3) { // Weekly View const $daysOfWeek = $('.day-of-week'); const $calendarDays = $('.days-row'); const $hourGrid = $('.hour-grid'); createCalendar = function() { const year = INITIAL_YEAR; const month = INITIAL_MONTH; const date = INITIAL_DATE; weekdaysNames.forEach((weekday, idx) => { const $weekDay = $('<li id="' + idx + '">'); $daysOfWeek.append($weekDay); $weekDay.html('<div class="week-day">' + weekday + '</div>'); }); // Add hour grid createCalendarHourGrid($hourGrid, options.startTime, options.endTime); // Remove all days $calendarDays.find('.calendar-day').remove(); currentWeekDays = createDaysForCurrentWeek(year, month, date); currentWeekDays.forEach((day) => { appendDay(day, $calendarDays); }); }; /** * Create days for current week * @param {string} year * @param {string} month * @param {string} date * @return {array} Days of the week */ function createDaysForCurrentWeek(year, month, date) { const startOfWeek = moment({ year: year, month: month, date: date, }).startOf('week'); return [...Array(7)].map((day, index) => { const weekDay = moment(startOfWeek).add(index, 'd'); return { date: weekDay.format('YYYY-MM-DD'), dayOfMonth: weekDay.date(), dayOfWeek: index, }; }); } /** * Append new day * @param {object} day * @param {object} $calendarDays */ function appendDay(day, $calendarDays) { const $daysOfWeek = $('.day-of-week'); const $dayElement = $('<li id="day_' + day.dayOfMonth + '">'); $dayElement.addClass('calendar-day'); // Append all day events container $dayElement.append( $('<div class="calendar-all-day-events-container">'), ); // Append normal events container $dayElement.append($('<div class="calendar-events-container">')); // Append week day name element const $dayOfMonthElement = $('<div class="week-day-date">'); $dayOfMonthElement.html(day.dayOfMonth); $daysOfWeek.find('#' + day.dayOfWeek).append($dayOfMonthElement); $calendarDays.append($dayElement); if (day.date === moment(TODAY).format('YYYY-MM-DD')) { $daysOfWeek.find('#' + day.dayOfWeek).addClass('day-of-week--today'); if (options.showNowMarker == 1) { createNowMarker( $dayElement.find('.calendar-events-container'), $('.hour-grid').data(), ); } } } // Override addEventsToCalendarBase addEventsToCalendar = function(events) { events.forEach((event) => { const startDate = moment(event.startDate).startOf('date'); // Check if event is an all day // (startDate 00:00 day 1, endDate 00:00 day after last day) const allDayEvent = moment(event.startDate).isSame(startDate) && moment(event.endDate).isSame(moment(event.endDate).startOf('date')); event.allDay = allDayEvent; const endDate = allDayEvent ? moment(event.endDate).startOf('date').subtract(1, 'd') : moment(event.endDate).startOf('date'); const eventTotalDays = endDate.diff(startDate, 'days') + 1; let currentDayOfEvent = 1; // Days loop const momentAux = moment(startDate); while (momentAux <= endDate) { addEventToDay(momentAux, event, eventTotalDays, currentDayOfEvent); currentDayOfEvent++; momentAux.add(1, 'd'); } }); }; } else if (options.calendarType == 4) { // Monthly View let currentMonthDays; let previousMonthDays; let nextMonthDays; createCalendar = function() { const $calendarDays = $('#calendarDays'); const year = INITIAL_YEAR; const month = INITIAL_MONTH; const $daysOfWeek = $('#daysOfWeek'); weekdaysNames.forEach((weekday) => { const $weekDay = $('<li>'); $daysOfWeek.append($weekDay); $weekDay.html(weekday); }); $('#selectedMonth').html( moment({ year: year, month: month, }).format('MMMM YYYY'), ); // Remove all days $calendarDays.find('.calendar-day').remove(); currentMonthDays = createDaysForCurrentMonth(year, month); previousMonthDays = createDaysForPreviousMonth(year, month - 1); nextMonthDays = createDaysForNextMonth(year, month + 1); const days = [ ...previousMonthDays, ...currentMonthDays, ...nextMonthDays, ]; days.forEach((day) => { appendDay(day, $calendarDays); }); }; /** * Append new day * @param {object} day * @param {object} $calendarDays */ function appendDay(day, $calendarDays) { const $dayElement = $( '<li id="day_' + day.month + '_' + day.dayOfMonth + '">', ); $dayElement.addClass('calendar-day'); // Append events container const $eventsContainer = $('<div class="calendar-events-container">'); $dayElement.append($eventsContainer); // Append day const $dayOfMonthElement = $('<span class="date">'); $dayOfMonthElement.html(day.dayOfMonth); $dayElement.append($dayOfMonthElement); $calendarDays.append($dayElement); if (!day.isCurrentMonth) { $dayElement.addClass('calendar-day--not-current'); } if (day.date === moment(TODAY).format('YYYY-MM-DD')) { $dayElement.addClass('calendar-day--today'); } } /** * Get the number of days in a given month * @param {number} year * @param {number} month * @return {array} day objects */ function getNumberOfDaysInMonth(year, month) { return moment({ year: year, month: month, }).daysInMonth(); } /** * Create days on current month * @param {number} year * @param {number} month * @return {array} day objects */ function createDaysForCurrentMonth(year, month) { return [...Array(getNumberOfDaysInMonth(year, month))].map( (day, index) => { return { date: moment({ year: year, month: month, day: index + 1, }).format('YYYY-MM-DD'), dayOfMonth: index + 1, month: month, isCurrentMonth: true, }; }, ); } /** * Create days in previous month * @param {number} year * @param {number} month * @return {array} day objects */ function createDaysForPreviousMonth(year, month) { const firstDayOfTheMonthWeekday = getWeekday(currentMonthDays[0].date); const previousMonth = moment({ year: year, month: month - 1, }); // Cover first day of the month // being sunday (firstDayOfTheMonthWeekday === 0) const visibleNumberOfDaysFromPreviousMonth = firstDayOfTheMonthWeekday ? firstDayOfTheMonthWeekday - 1 : 6; const previousMonthLastMondayDayOfMonth = moment( currentMonthDays[0].date, ) .subtract(visibleNumberOfDaysFromPreviousMonth, 'day') .date(); return [...Array(visibleNumberOfDaysFromPreviousMonth)].map( (day, index) => { return { date: moment({ year: previousMonth.year(), month: previousMonth.month(), day: previousMonthLastMondayDayOfMonth + index, }).format('YYYY-MM-DD'), month: previousMonth.month(), dayOfMonth: previousMonthLastMondayDayOfMonth + index, isCurrentMonth: false, }; }, ); } /** * Create days in next month * @param {number} year * @param {number} month * @return {array} day objects */ function createDaysForNextMonth(year, month) { const lastDayOfTheMonthWeekday = getWeekday({ year: year, month: month - 1, date: currentMonthDays.length, }); const nextMonth = moment({ year: year, month: month, }); const visibleNumberOfDaysFromNextMonth = lastDayOfTheMonthWeekday ? 7 - lastDayOfTheMonthWeekday : lastDayOfTheMonthWeekday; return [...Array(visibleNumberOfDaysFromNextMonth)].map( (day, index) => { return { date: moment({ year: nextMonth.year(), month: nextMonth.month(), day: index + 1, }).format('YYYY-MM-DD'), month: nextMonth.month(), dayOfMonth: index + 1, isCurrentMonth: false, }; }, ); } addEventsToCalendar = function(events) { events.forEach((event) => { const startDate = moment(event.startDate).startOf('date'); // Check if event is an all day // (startDate 00:00 day 1, endDate 00:00 day after last day) const allDayEvent = moment(event.startDate).isSame(startDate) && moment(event.endDate).isSame(moment(event.endDate).startOf('date')); event.allDay = allDayEvent; const endDate = allDayEvent ? moment(event.endDate).startOf('date').subtract(1, 'd') : moment(event.endDate).startOf('date'); const eventTotalDays = endDate.diff(startDate, 'days') + 1; // Days loop let currentDayOfEvent = 1; const momentAux = moment(startDate); while (momentAux <= endDate) { addEventToDay(momentAux, event, eventTotalDays, currentDayOfEvent); currentDayOfEvent++; momentAux.add(1, 'd'); } }); }; /** * Add event to specific day (override) * @param {object} date momentjs date * @param {object} event * @param {number} eventTotalDays * @param {number} currentDayOfEvent */ function addEventToDay(date, event, eventTotalDays, currentDayOfEvent) { /** * Get container by date * @param {object} date * @return {object} Jquery container */ function getEventContainer(date) { return $('#day_' + date.month() + '_' + date.date()).find( '.calendar-events-container', ); } const $newEvent = $('<div class="calendar-event">'); const $eventsContainer = getEventContainer(date); const weekDay = getWeekday(date); let eventDuration = 1; // Mark event as an all day if (event.allDay) { $newEvent.addClass('all-day'); } if (eventTotalDays > 1) { // Multiple day event let htmlToAdd = '<span class="event-summary">' + event.summary + '</span>'; // Mark as multi event $newEvent.addClass('multi-day'); // Draw only on the first day of the event // or at the beggining of the weeks when it breaks if (currentDayOfEvent == 1 || weekDay == 1) { if (currentDayOfEvent == 1 && !event.allDay) { htmlToAdd = '<span class="event-time">' + moment(event.startDate).format(TIME_FORMAT) + '</span>' + htmlToAdd; } // Show event content in multiple days $newEvent.html(htmlToAdd); // Update element duration based on event duration eventDuration = eventTotalDays - (currentDayOfEvent - 1); const remainingDays = 8 - weekDay; if (eventDuration > remainingDays) { eventDuration = remainingDays; $newEvent.addClass('cropped-event-end'); } if (currentDayOfEvent > 1) { $newEvent.addClass('cropped-event-start'); } $newEvent.css( 'width', 'calc(' + eventDuration * 100 + '% + ' + eventDuration * 2 + 'px)', ); } else { // Multiple event that was extended, no need to be rendered return; } } else { // Single day event let htmlToAdd = '<span class="event-summary">' + event.summary + '</span>'; // Mark event as an all day if (event.allDay) { $newEvent.addClass('all-day'); } else { htmlToAdd = '<span class="event-time">' + moment(event.startDate).format(TIME_FORMAT) + '</span>' + htmlToAdd; } // Add inner html $newEvent.html(htmlToAdd); } // Calculate event slot let slots = $eventsContainer.data('slots'); let daySlot; if (slots != undefined) { for (let index = 0; index < slots.length; index++) { const slot = slots[index]; if (slot === undefined) { daySlot = index; slots[index] = 1; break; } } if (daySlot === undefined) { daySlot = slots.length; slots.push(1); } } else { daySlot = 0; slots = [1]; } $eventsContainer.data('slots', slots); // Extend event to the remaining days if (eventDuration > 1) { for (let dayAfter = 1; dayAfter < eventDuration; dayAfter++) { const $newContainer = getEventContainer( moment(date).add(dayAfter, 'd'), ); let dataSlots = $newContainer.data('slots'); if (dataSlots === undefined) { dataSlots = []; } dataSlots[daySlot] = 2; $newContainer.data('slots', dataSlots); } } $newEvent.css('top', 2 + 1.875 * daySlot + 'rem'); // Append event to container $newEvent.appendTo($eventsContainer); // Check container height and slots to show number of extra events updateContainerExtraEvents($eventsContainer, slots); } } // Create calendar applyStyleOptions(options); createCalendar(); addEventsToCalendar(events); return true; }, });