@sdetweil Thanks! This worked like a charm. Also had to add to normal calendar module
Read the statement by Michael Teeuw here.
Best posts made by redux703
-
RE: mmm-calendarEXT3 multiple questions
Latest posts made by redux703
-
RE: MMM-calendarext3 wrong month
@sdetweil I changed it back now it works again thanks!
-
RE: MMM-calendarext3 wrong month
{ module: "MMM-CalendarExt3", //Maand Agenda classes: "page0", position: "middle_center", title: "", config: { broadcastPastEvents: true, // <= IMPORTANT to see past events mode: "month", instanceId: "basicCalendar", locale: 'nl-NL', maxEventLines: 6, firstDayOfWeek: 1, weeksInView: 5, //monthIndex: -1, calendarSet: ["Algemeen"], } },
-
RE: MMM-calendarext3 wrong month
/* global Log, Module, config */ /* eslint-disable no-unused-vars */ const popoverSupported = Object.prototype.hasOwnProperty.call(HTMLElement.prototype, "popover") /* Even though `.prototype.hasOwnProperty` is not recommended, it's the only way to check the existence of `popover` feature at this moment. console.log(Object.prototype.hasOwnProperty.call(HTMLElement, "popover")) // false console.log(Object.hasOwn(HTMLElement, "popover")) // false console.log(HTMLElement.prototype.showPopover === 'function') // false consoe.log(HTMLElement.prototype.hasOwnProperty("popover")) // true */ if (!popoverSupported) console.info("This browser doesn't support popover yet. Update your system.") const animationSupported = (typeof window !== "undefined" && window?.mmVersion) ? +(window.mmVersion.split(".").join("")) >= 2250 : false Module.register("MMM-CalendarExt3", { defaults: { mode: "week", // or 'month', 'day' weekIndex: -1, // Which week from this week starts in a view. Ignored on mode 'month' dayIndex: -1, weeksInView: 3, // How many weeks will be displayed. Ignored on mode 'month' instanceId: null, firstDayOfWeek: 1, // 0: Sunday, 1: Monday minimalDaysOfNewYear: null, // When the first week of new year starts in your country. weekends: [], // or [0, 6]. 0: Sunday, 6: Saturday locale: null, // 'de' or 'en-US' or prefer array like ['en-CA', 'en-US', 'en'] cellDateOptions: { month: "short", day: "numeric" }, eventTimeOptions: { timeStyle: "short" }, headerWeekDayOptions: { weekday: "long" }, headerTitleOptions: { month: "long" }, calendarSet: [], maxEventLines: 5, // How many events will be shown in a day cell. // It could be possible to use {} like {"4": 6, "5": 5, "6": 4} to set different lines by the number of the week of the month. // Also, it could be possible to use [] like [8, 8, 7, 6, 5] to set different lines by the number of week of the month. fontSize: "20px", eventHeight: "22px", eventFilter: (ev) => { return true }, eventSorter: null, eventTransformer: (ev) => { return ev }, refreshInterval: 1000 * 60 * 2, // too frequent refresh. 10 minutes is enough. waitFetch: 1000 * 2, glanceTime: 1000 * 60, // deprecated, use refreshInterval instead. animationSpeed: 2000, useSymbol: true, displayLegend: false, useWeather: false, weatherLocationName: null, //notification: 'CALENDAR_EVENTS', /* reserved */ manipulateDateCell: (cellDom, events) => { }, weatherNotification: "WEATHER_UPDATED", weatherPayload: (payload) => { return payload }, eventNotification: "CALENDAR_EVENTS", eventPayload: (payload) => { return payload }, displayEndTime: false, displayWeatherTemp: false, popoverTemplate: "./popover.html", popoverTimeout: 1000 * 30, popoverPeriodOptions: { dateStyle: "short", timeStyle: "short" }, popoverDateOptions: { dateStyle: "full" }, displayCW: true, // Weeknummer animateIn: "fadeIn", animateOut: "fadeOut", skipPassedEventToday: false, showMore: true, useIconify: true, useMarquee: false, skipDuplicated: true, monthIndex: 1, referenceDate: null, customHeader: false // true or function }, defaulNotifications: { weatherNotification: "WEATHER_UPDATED", weatherPayload: (payload) => { return payload }, eventNotification: "CALENDAR_EVENTS", eventPayload: (payload) => { return payload } }, getStyles () { let css = ["MMM-CalendarExt3.css"] return css }, getMoment (options) { let moment = (options.referenceDate) ? new Date(options.referenceDate) : new Date(Date.now()) //let moment = (this.tempMoment) ? new Date(this.tempMoment.valueOf()) : new Date() switch (options.mode) { case "day": moment = new Date(moment.getFullYear(), moment.getMonth(), moment.getDate() + options.dayIndex) break case "month": moment = new Date(moment.getFullYear(), moment.getMonth() + options.monthIndex, 1) break case "week": default: moment = new Date(moment.getFullYear(), moment.getMonth(), moment.getDate() + (7 * options.weekIndex)) } return moment }, regularizeConfig (options) { const weekInfoFallback = { firstDay: 1, minimalDays: 4, weekend: [0, 6] } options.locale = Intl.getCanonicalLocales(options.locale ?? config?.locale ?? config?.language)?.[0] ?? "" const calInfo = new Intl.Locale(options.locale) if (calInfo?.weekInfo) { options.firstDayOfWeek = (options.firstDayOfWeek !== null) ? options.firstDayOfWeek : (calInfo.weekInfo?.firstDay ?? weekInfoFallback.firstDay) options.minimalDaysOfNewYear = (options.minimalDaysOfNewYear !== null) ? options.minimalDaysOfNewYear : (calInfo.weekInfo?.minimalDays ?? weekInfoFallback.minDays) options.weekends = ((Array.isArray(options.weekends) && options.weekends?.length) ? options.weekends : (calInfo.weekInfo?.weekend ?? [])).map((d) => d % 7) } options.instanceId = options.instanceId ?? this.identifier this.notifications = { weatherNotification: options.weatherNotification ?? this.defaulNotifications.weatherNotification, weatherPayload: (typeof options.weatherPayload === "function") ? options.weatherPayload : this.defaulNotifications.weatherPayload, eventNotification: options.eventNotification ?? this.defaulNotifications.eventNotification, eventPayload: (typeof options.eventPayload === "function") ? options.eventPayload : this.defaulNotifications.eventPayload } options.mode = (["day", "month", "week"].includes(options.mode)) ? options.mode : "week" options.weekIndex = (options.mode === "month") ? 0 : options.weekIndex options.weeksInView = (options.mode === "month") ? 6 : options.weeksInView options.dayIndex = (options.mode === "day") ? options.dayIndex : 0 return options }, start () { this.activeConfig = this.regularizeConfig({ ...this.config }) this.originalConfig = { ...this.activeConfig } this.fetchTimer = null this.refreshTimer = null this.forecast = [] this.eventPool = new Map() this.popoverTimer = null this._ready = false let _moduleLoaded = new Promise((resolve, reject) => { import(`/${this.file("CX3_Shared/CX3_shared.mjs")}`).then((m) => { this.library = m if (this.config.useIconify) this.library.prepareIconify() resolve() }).catch((err) => { console.error(err) reject(err) }) }) let _domCreated = new Promise((resolve) => { this._domReady = resolve }) Promise.allSettled([_moduleLoaded, _domCreated]).then(() => { this._ready = true this.library.prepareMagic() setTimeout(() => { this.updateAnimate() }, this.activeConfig.waitFetch) }) if (popoverSupported) { this.preparePopover() } }, preparePopover () { if (!popoverSupported) return if (document.getElementById("CX3_POPOVER")) return fetch(this.file(this.config.popoverTemplate)).then((response) => { return response.text() }).then((text) => { const template = new DOMParser().parseFromString(text, "text/html").querySelector("#CX3_T_POPOVER").content.querySelector(".popover") const popover = document.importNode(template, true) popover.id = "CX3_POPOVER" document.body.append(popover) popover.ontoggle = (ev) => { if (this.popoverTimer) { clearTimeout(this.popoverTimer) this.popoverTimer = null } if (ev.newState === "open") { this.popoverTimer = setTimeout(() => { try { popover.hidePopover() popover.querySelector(".container").innerHTML = "" } catch { // do nothing } }, this.activeConfig.popoverTimeout) } else { // closed popover.querySelector(".container").innerHTML = "" } } }).catch((err) => { console.error("[CX3]", err) }) }, dayPopover(cDom, events, options) { const popover = document.getElementById("CX3_POPOVER") if (!popover) return const container = popover.querySelector(".container") container.innerHTML = "" const ht = popover.querySelector("template#CX3_T_EVENTLIST").content.cloneNode(true) container.append(document.importNode(ht, true)) let header = container.querySelector(".header") header.innerHTML = new Intl.DateTimeFormat(options.locale, { dateStyle: "full" }).formatToParts(new Date(+cDom.dataset.date)) .reduce((prev, cur, curIndex) => { const result = `${prev}<span class="eventTimeParts ${cur.type} seq_${curIndex} ${cur.source}">${cur.value}</span>` return result }, "") let list = container.querySelector(".list") list.innerHTML = "" const { renderSymbol } = this.library events.forEach((e) => { const pOption = (e.fullDayEvent) ? { dateStyle: "short" } : { dateStyle: "short", timeStyle: "short" } const item = popover.querySelector("template#CX3_T_EVENTITEM").content.firstElementChild.cloneNode(true) item.style.setProperty("--calendarColor", e.color) item.classList.add("event") const symbol = item.querySelector(".symbol") renderSymbol(symbol, e, config) const time = item.querySelector(".time") time.innerHTML = new Intl.DateTimeFormat(options.locale, pOption).formatRangeToParts(new Date(+e.startDate), new Date(+e.endDate)) .reduce((prev, cur, curIndex) => { const result = `${prev}<span class="eventTimeParts ${cur.type} seq_${curIndex} ${cur.source}">${cur.value}</span>` return result }, "") const title = item.querySelector(".title") title.innerHTML = e.title list.append(item) }) this.activatePopover(popover) }, eventPopover(eDom, options) { const popover = document.getElementById("CX3_POPOVER") if (!popover) return const container = popover.querySelector(".container") container.innerHTML = "" const ht = popover.querySelector("template#CX3_T_EVENTDETAIL").content.cloneNode(true) container.append(document.importNode(ht, true)) let eSymbol = eDom.querySelector(".symbol").cloneNode(true) container.querySelector(".symbol").append(eSymbol) let eTitle = eDom.querySelector(".title").cloneNode(true) container.querySelector(".title").append(eTitle) let header = container.querySelector(".header") header.style.setProperty("--calendarColor", eDom.style.getPropertyValue("--calendarColor")) header.style.setProperty("--oppositeColor", eDom.style.getPropertyValue("--oppositeColor")) header.dataset.isFullday = eDom.dataset.fullDayEvent let criteria = container.querySelector(".criteria") criteria.innerHTML = "" const ps = ["location", "description", "calendar"] ps.forEach((c) => { if (eDom.dataset[c]) { const ct = popover.querySelector("template#CX3_T_CRITERIA").content.firstElementChild.cloneNode(true) //ct.querySelector('.name').innerHTML = c //ct.querySelector('.name').classList.add(c) ct.classList.add(c) ct.querySelector(".value").innerHTML = eDom.dataset[c] criteria.append(document.importNode(ct, true)) } }) let start = new Date(+(eDom.dataset.startDate)) let end = new Date(+(eDom.dataset.endDate)) const ct = popover.querySelector("template#CX3_T_CRITERIA").content.firstElementChild.cloneNode(true) criteria.append(document.importNode(ct, true)) const n = Array.from(criteria.childNodes).at(-1) n.classList.add("period") const pOption = (eDom.dataset.fullDayEvent === "true") ? { dateStyle: "short" } : { dateStyle: "short", timeStyle: "short" } n.querySelector(".value").innerHTML = new Intl.DateTimeFormat(options.locale, pOption).formatRangeToParts(start, end) .reduce((prev, cur, curIndex) => { const result = `${prev}<span class="eventTimeParts ${cur.type} seq_${curIndex} ${cur.source}">${cur.value}</span>` return result }, "") this.activatePopover(popover) }, activatePopover (popover) { let opened = document.querySelectorAll("[popover-opened]") for (const o of Array.from(opened)) { o.hidePopover() } popover.showPopover() }, notificationReceived (notification, payload, sender) { const replyCurrentConfig = ({ callback }) => { if (typeof callback === "function") { callback({ ...this.activeConfig }) } } if (notification === this.notifications.eventNotification) { let convertedPayload = this.notifications.eventPayload(payload) this.eventPool.set(sender.identifier, JSON.parse(JSON.stringify(convertedPayload))) } if (notification === "MODULE_DOM_CREATED") { this._domReady() const moduleContainer = document.querySelector(`#${this.identifier} .module-content`) const callback = (mutationsList) => { for (let mutation of mutationsList) { const content = document.querySelector(`#${this.identifier} .module-content .CX3`) if (mutation.addedNodes.length > 0) this.updated(content, this.activeConfig) } } const MutationObserver = window.MutationObserver || window.WebKitMutationObserver const observer = new MutationObserver(callback) observer.observe(moduleContainer, { childList: true }) } if (notification === this.notifications.weatherNotification) { let convertedPayload = this.notifications.weatherPayload(payload) if ( (this.activeConfig.useWeather && ((this.activeConfig.weatherLocationName && convertedPayload.locationName.includes(this.activeConfig.weatherLocationName)) || !this.activeConfig.weatherLocationName)) && (Array.isArray(convertedPayload?.forecastArray) && convertedPayload?.forecastArray.length) ) { this.forecast = [...convertedPayload.forecastArray].map((o) => { let d = new Date(o.date) o.dateId = d.toLocaleDateString("en-CA") return o }) } else { if (this.activeConfig.weatherLocationName && !convertedPayload.locationName.includes(this.activeConfig.weatherLocationName)) { Log.warn(`"weatherLocationName: '${this.activeConfig.weatherLocationName}'" doesn't match with location of weather module ('${convertedPayload.locationName}')`) } } } if (["CX3_GLANCE_CALENDAR", "CX3_MOVE_CALENDAR", "CX3_SET_DATE"].includes(notification)) { console.warn("[DEPRECATED]'CX3_GLANCE_CALENDAR' notification was deprecated. Use 'CX3_SET_CONFIG' instead. (README.md)") } if (payload?.instanceId && payload?.instanceId !== this.activeConfig?.instanceId) return if (notification === "CX3_GET_CONFIG") { replyCurrentConfig(payload) } if (notification === "CX3_SET_CONFIG") { this.activeConfig = this.regularizeConfig({ ...this.activeConfig, ...payload }) this.updateAnimate() replyCurrentConfig(payload) } if (notification === "CX3_RESET") { this.activeConfig = this.regularizeConfig({ ...this.originalConfig }) this.updateAnimate() replyCurrentConfig(payload) } }, getDom () { let dom = document.createElement("div") dom.innerHTML = "" dom.classList.add("bodice", `CX3_${this.activeConfig.instanceId}`, "CX3") if (this.activeConfig.fontSize) dom.style.setProperty("--fontsize", this.activeConfig.fontSize) if (!this.library?.loaded) { Log.warn("[CX3] Module is not prepared yet, wait a while.") return dom } dom = this.draw(dom, this.activeConfig) if (this.refreshTimer) { clearTimeout(this.refreshTimer) this.refreshTimer = null } this.refreshTimer = setTimeout(() => { clearTimeout(this.refreshTimer) this.refreshTimer = null this.updateAnimate() }, this.activeConfig.refreshInterval) this.sendNotification("CX3_DOM_UPDATED", { instanceId: this.activeConfig.instanceId }) return dom }, updated (dom, options) { if (!dom) return dom.querySelectorAll(".title")?.forEach((e) => { const parent = e.closest(".event") const { offsetWidth, scrollWidth } = e if (options.useMarquee && parent?.dataset?.noMarquee !== "true" && offsetWidth < scrollWidth) { const m = document.createElement("span") m.innerHTML = e.innerHTML e.innerHTML = "" e.append(m) e.classList.add("marquee") m.classList.add("marqueeText") const length = m.offsetWidth m.style.setProperty("--marqueeOffset", `${offsetWidth}px`) m.style.setProperty("--marqueeScroll", `${scrollWidth}px`) m.style.setProperty("--marqueeLength", `${length}s`) } }) }, async draw (dom, options) { if (!this.library?.loaded) return dom const { isToday, isPastDay, isFutureDay, isThisMonth, isThisYear, getWeekNo, renderEventAgenda, prepareEvents, getBeginOfWeek, getEndOfWeek, displayLegend, regularizeEvents } = this.library const startDayOfWeek = getBeginOfWeek(new Date(Date.now()), options).getDay() dom.innerHTML = "" //dom.style.setProperty("--maxeventlines", options.maxEventLines) dom.style.setProperty("--eventheight", options.eventHeight) dom.style.setProperty("--displayEndTime", (options.displayEndTime) ? "inherit" : "none") dom.style.setProperty("--displayWeatherTemp", (options.displayWeatherTemp) ? "inline-block" : "none") dom.dataset.mode = options.mode const makeCellDom = (d) => { let tm = new Date(d.valueOf()) let cell = document.createElement("div") cell.classList.add("cell") if (isPastDay(tm)) cell.classList.add("past") if (isToday(tm)) cell.classList.add("today") if (isFutureDay(tm)) cell.classList.add("future") if (isThisMonth(tm)) cell.classList.add("thisMonth") if (isThisYear(tm)) cell.classList.add("thisYear") cell.classList.add( `year_${tm.getFullYear()}`, `month_${tm.getMonth() + 1}`, `date_${tm.getDate()}`, `weekday_${tm.getDay()}` ) cell.dataset.date = new Date(tm.getFullYear(), tm.getMonth(), tm.getDate()).valueOf() options.weekends.forEach((w, i) => { if (tm.getDay() === w) cell.classList.add("weekend", `weekend_${i + 1}`) }) let h = document.createElement("div") h.classList.add("cellHeader") let cwDom = document.createElement("div") cwDom.innerHTML = getWeekNo(tm, options) cwDom.classList.add("cw") if (tm.getDay() === startDayOfWeek) { cwDom.classList.add("cwFirst") } h.append(cwDom) let forecasted = this.forecast.find((e) => { return (tm.toLocaleDateString("en-CA") === e.dateId) }) if (forecasted && forecasted?.weatherType) { let weatherDom = document.createElement("div") weatherDom.classList.add("cellWeather") let icon = document.createElement("span") icon.classList.add("wi", `wi-${forecasted.weatherType}`) weatherDom.append(icon) let maxTemp = document.createElement("span") maxTemp.classList.add("maxTemp", "temperature") maxTemp.innerHTML = Math.round(forecasted.maxTemperature) weatherDom.append(maxTemp) let minTemp = document.createElement("span") minTemp.classList.add("minTemp", "temperature") minTemp.innerHTML = Math.round(forecasted.minTemperature) weatherDom.append(minTemp) h.append(weatherDom) } let dateDom = document.createElement("div") dateDom.classList.add("cellDate") let dParts = new Intl.DateTimeFormat(options.locale, options.cellDateOptions).formatToParts(tm) let dateHTML = dParts.reduce((prev, cur, curIndex) => { const result = `${prev}<span class="dateParts ${cur.type} seq_${curIndex}">${cur.value}</span>` return result }, "") dateDom.innerHTML = dateHTML h.append(dateDom) let b = document.createElement("div") b.classList.add("cellBody") let f = document.createElement("div") f.classList.add("cellFooter") cell.append(h) cell.append(b) cell.append(f) return cell } const rangeCalendar = (moment, options) => { let boc, eoc switch (options.mode) { case "day": boc = new Date(moment.getFullYear(), moment.getMonth(), moment.getDate()) eoc = new Date(boc.valueOf()) eoc.setDate(boc.getDate() + 7 * options.weeksInView) eoc.setMilliseconds(-1) break case "month": boc = getBeginOfWeek(new Date(moment.getFullYear(), moment.getMonth(), 1), options) eoc = getEndOfWeek(new Date(moment.getFullYear(), moment.getMonth() + 1, 0), options) break case "week": default: boc = getBeginOfWeek(new Date(moment.getFullYear(), moment.getMonth(), moment.getDate()), options) eoc = getEndOfWeek(new Date(boc.getFullYear(), boc.getMonth(), boc.getDate() + (7 * (options.weeksInView - 1))), options) break } return { boc, eoc } } const makeDayHeaderDom = (dom, options, range) => { let wm = new Date(range.boc.valueOf()) let dayDom = document.createElement("div") dayDom.classList.add("headerContainer", "weekGrid") for (let i = 0; i < 7; i++) { let dm = new Date(wm.getFullYear(), wm.getMonth(), wm.getDate() + i) let day = (dm.getDay() + 7) % 7 let dDom = document.createElement("div") dDom.classList.add("weekday", `weekday_${day}`) options.weekends.forEach((w, i) => { if (day === w) dDom.classList.add("weekend", `weekend_${i + 1}`) }) dDom.innerHTML = new Intl.DateTimeFormat(options.locale, options.headerWeekDayOptions).format(dm) dayDom.append(dDom) } dom.append(dayDom) } const makeWeekGridDom = (dom, options, events, { boc, eoc }) => { const getMaxEventLines = ({ maxEventLines }, weekCount) => { if (Number.isInteger(+maxEventLines) && Number.isFinite(+maxEventLines)) return +maxEventLines let lines = [] if (typeof maxEventLines === "object") { if (!Array.isArray(maxEventLines)) { const maxKeys = Object.keys(maxEventLines) .filter((k) => Number.isInteger(+k) && Number.isFinite(+k)).map((k) => +k) .reduce((p, v) => { return (p > v ? p : v) }, 0) lines.length = maxKeys + 1 const defaultLines = maxEventLines?.[0] ?? maxEventLines?.["0"] ?? this.defaults.maxEventLines lines.fill(defaultLines) for (let [key, value] of Object.entries(maxEventLines)) { if (Number.isInteger(+key) && Number.isFinite(+key) && Number.isInteger(+value) && Number.isFinite(+value)) { lines[+key] = +value } } } else { lines = [...maxEventLines.filter((v) => Number.isInteger(+v) && Number.isFinite(+v))] } } return +(lines?.[weekCount] ?? lines?.[0] ?? this.defaults.maxEventLines) } // how many weeks between boc(begin of calendar) and eoc(end of calendar) let count = 1; let eocWeek = getWeekNo(eoc, options) let w = new Date(boc.valueOf()) do { w.setDate(w.getDate() + 7) count++ } while (getWeekNo(w, options) !== eocWeek) count = (options.mode === "month") ? count : options.weeksInView const maxEventLines = getMaxEventLines(options, count) dom.style.setProperty("--maxeventlines", maxEventLines) dom.dataset.maxEventLines = maxEventLines let wm = new Date(boc.valueOf()) do { let wDom = document.createElement("div") wDom.classList.add("week") wDom.dataset.weekNo = getWeekNo(wm, options) let ccDom = document.createElement("div") ccDom.classList.add("cellContainer", "weekGrid") let ecDom = document.createElement("div") ecDom.classList.add("eventContainer", "weekGrid", "weekGridRow") let boundary = [] let cm = new Date(wm.valueOf()) for (let i = 0; i < 7; i++) { if (i) cm = new Date(cm.getFullYear(), cm.getMonth(), cm.getDate() + 1) ccDom.append(makeCellDom(cm, i)) boundary.push(cm.getTime()) } boundary.push(cm.setHours(23, 59, 59, 999)) let sw = new Date(wm.valueOf()) let ew = new Date(sw.getFullYear(), sw.getMonth(), sw.getDate() + 6, 23, 59, 59, 999) let eventsOfWeek = events.filter((ev) => { return !(ev.endDate <= sw.getTime() || ev.startDate >= ew.getTime()) }) for (let event of eventsOfWeek) { if (options.skipPassedEventToday) { if (event.today && event.isPassed && !event.isFullday && !event.isMultiday && !event.isCurrent) event.skip = true } if (event?.skip) continue let eDom = renderEventAgenda(event, options, moment) let startLine = 0 if (event.startDate >= boundary.at(0)) { startLine = boundary.findIndex((b, idx, bounds) => { return (event.startDate >= b && event.startDate < bounds[idx + 1]) }) } else { eDom.classList.add("continueFromPreviousWeek") } let endLine = boundary.length - 1 if (event.endDate <= boundary.at(-1)) { endLine = boundary.findIndex((b, idx, bounds) => { return (event.endDate <= b && event.endDate > bounds[idx - 1]) }) } else { eDom.classList.add("continueToNextWeek") } eDom.style.gridColumnStart = startLine + 1 eDom.style.gridColumnEnd = endLine + 1 if (event?.noMarquee) { eDom.dataset.noMarquee = true } if (event?.skip) { eDom.dataset.skip = true } if (popoverSupported) { if (!eDom.id) eDom.id = `${eDom.dataset.calendarSeq}_${eDom.dataset.startDate}_${eDom.dataset.endDate}_${new Date(Date.now()).getTime()}` eDom.dataset.popoverble = true eDom.onclick = () => { this.eventPopover(eDom, options) } } ecDom.append(eDom) } let dateCells = ccDom.querySelectorAll(".cell") for (let i = 0; i < dateCells.length; i++) { let dateCell = dateCells[i] let dateStart = new Date(+dateCell.dataset.date) let dateEnd = new Date(dateStart.getFullYear(), dateStart.getMonth(), dateStart.getDate(), 23, 59, 59, 999) let thatDayEvents = eventsOfWeek.filter((ev) => { return !(ev.endDate <= dateStart.valueOf() || ev.startDate > dateEnd.valueOf()) }) dateCell.dataset.events = thatDayEvents.length dateCell.dataset.hasEvents = (thatDayEvents.length > 0) ? "true" : "false" if (typeof options.manipulateDateCell === "function") { options.manipulateDateCell(dateCell, thatDayEvents) } if (options.showMore) { const skipped = thatDayEvents.filter((ev) => ev.skip).length const noskip = thatDayEvents.length - skipped const noskipButOverflowed = (noskip > maxEventLines) ? noskip - maxEventLines : 0 const hidden = skipped + noskipButOverflowed if (hidden) { dateCell.classList.add("hasMore") dateCell.style.setProperty("--more", hidden) } } if (popoverSupported) { if (!dateCell.id) dateCell.id = `${dateCell.dataset.date}_${new Date(Date.now()).getTime()}` dateCell.dataset.popoverble = true dateCell.onclick = () => { this.dayPopover(dateCell, thatDayEvents, options) } } } wDom.append(ccDom) wDom.append(ecDom) dom.append(wDom) wm = new Date(wm.getFullYear(), wm.getMonth(), wm.getDate() + 7) } while (wm.valueOf() <= eoc.valueOf()) } const customHeaderDom = (dom, options, { boc, eoc }) => { const defaultCustomHeader = (options, boc, eoc) => { try { const locale = options.locale const titleOptions = options.headerTitleOptions if (options.mode === "month") { const moment = this.getMoment(options) return new Intl.DateTimeFormat(locale, titleOptions) .formatToParts(new Date(moment.valueOf())) .reduce((prev, cur, curIndex) => { const result = `${prev}<span class="headerTimeParts ${cur.type} seq_${curIndex} ${cur.source}">${cur.value}</span>` return result }, "") } else { const begin = new Date(boc.valueOf()) const end = new Date(eoc.valueOf()) return new Intl.DateTimeFormat(locale, titleOptions) .formatRangeToParts(begin, end) .reduce((prev, cur, curIndex) => { const result = `${prev}<span class="headerTimeParts ${cur.type} seq_${curIndex} ${cur.source}">${cur.value}</span>` return result }, "") } } catch (e) { Log.error(e) return "" } } const header = document.createElement("h1") header.classList.add("headerTitle") header.innerHTML = (typeof options.customHeader === "function") ? options.customHeader(options, boc, eoc) : defaultCustomHeader(options, boc, eoc) dom.prepend(header) } let moment = this.getMoment(options) let { boc, eoc } = rangeCalendar(moment, options) dom.dataset.beginOfCalendar = boc.valueOf() dom.dataset.endOfCalendar = eoc.valueOf() const targetEvents = prepareEvents({ targetEvents: regularizeEvents({ eventPool: this.eventPool, config: options }), config: options, range: [boc, eoc] }) makeDayHeaderDom(dom, options, { boc, eoc }) makeWeekGridDom(dom, options, targetEvents, { boc, eoc }) if (options.displayLegend) displayLegend(dom, targetEvents, options) if (options.customHeader) customHeaderDom(dom, options, { boc, eoc }) return dom }, getHeader () { if (this.data.header && this.data.header.trim() !== "") return this.data.header if (!this.activeConfig.customHeader && this.activeConfig.mode === "month") { const moment = this.getMoment(this.activeConfig) const locale = this.activeConfig.locale const titleOptions = this.activeConfig.headerTitleOptions return new Intl.DateTimeFormat(locale, titleOptions).format(new Date(moment.valueOf())) } return this.data.header }, updateAnimate () { this.updateDom( (!animationSupported) ? this.config.animationSpeed : { options: { speed: this.config.animationSpeed, animate: { in: this.config.animateIn, out: this.config.animateOut } } } ) } })
-
MMM-calendarext3 wrong month
Hello,
I have the calendarExt3 module working. But now I have an strange error.
It is now august, and when i start the calendar it shows july, and with monthIndex: -1, it shows september.What can i do to show the actual month?
-
RE: MMM-calendarExt3 filter agendas
@sdetweil hey the error was probably comming from the agenda it self… it was put in the samsung agenda app, which would add it to the hotmail calemdar that would sync with google agenda.
The only problem i now still have is the calendarSet.
It still doesnt filter out the calendars i dont want… -
RE: MMM-calendarExt3 filter agendas
@sdetweil
Yep it still shows there.
While in the source agenda (google calendar on the phone etc) it is still correct. It also seems only to happen with “jarig” in the title. But some are also correct -
RE: MMM-calendarExt3 filter agendas
@BKeyport
Yeah the calendarSet: (‘’) worked so far. But for some reason it gives two strange errors:
1, it is now (2025-08-01), and the month calendar shows 2025-09-01, and the week calender is still right.
2, for some reason, there are some dates that get mixed up. For example: someones birthday that is in December, now shows on July, while the same day. The month is different. -
MMM-calendarExt3 filter agendas
Hello,
I have multiple pages with calendars, is it possible to filter it? So:
Page 0, all agenda’s
Page 1, person 1
Page 2, person 2
Page 3, person 3 -
RE: mmm-calendarEXT3 multiple questions
@sdetweil Thanks! This worked like a charm. Also had to add to normal calendar module