MagicMirror Forum
    • Recent
    • Tags
    • Unsolved
    • Solved
    • MagicMirror² Repository
    • Documentation
    • 3rd-Party-Modules
    • Donate
    • Discord
    • Register
    • Login
    A New Chapter for MagicMirror: The Community Takes the Lead
    Read the statement by Michael Teeuw here.

    MMM-calendarext3 wrong month

    Scheduled Pinned Locked Moved Solved Troubleshooting
    7 Posts 2 Posters 175 Views 2 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • R Offline
      redux703 @sdetweil
      last edited by sdetweil

      @sdetweil

      /* 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
                  }
                }
              }
          )
        }
      })
      
      
      S 1 Reply Last reply Reply Quote 0
      • R Offline
        redux703 @sdetweil
        last edited by sdetweil

        @sdetweil

        {
                    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"],
                    }
                },
        
        S 1 Reply Last reply Reply Quote 0
        • S Offline
          sdetweil @redux703
          last edited by

          @redux703 replying with the code

          you NEVER edit the source supplied by MagicMirror or modules

          ALL config goes in config.js
          ALL css goes in css/custom.css

          Sam

          How to add modules

          learning how to use browser developers window for css changes

          R 1 Reply Last reply Reply Quote 0
          • S Offline
            sdetweil @redux703
            last edited by sdetweil

            @redux703 replying to config

            broadcastPastEvents is for default calendar, not here
            mode: month,
            then weeksInView is not used

            monthIndex commented out, default in module source is -1 (last month)

            Sam

            How to add modules

            learning how to use browser developers window for css changes

            1 Reply Last reply Reply Quote 0
            • R Offline
              redux703 @sdetweil
              last edited by

              @sdetweil I changed it back now it works again thanks!

              1 Reply Last reply Reply Quote 0
              • R redux703 has marked this topic as solved on
              • 1 / 1
              • First post
                Last post
              Enjoying MagicMirror? Please consider a donation!
              MagicMirror created by Michael Teeuw.
              Forum managed by Sam, technical setup by Karsten.
              This forum is using NodeBB as its core | Contributors
              Contact | Privacy Policy