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 451 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
      last edited by

      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?

      S 1 Reply Last reply Reply Quote 0
      • S Do not disturb
        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 Do not disturb
          sdetweil @redux703
          last edited by

          @redux703 can you show the ext module config please

          Sam

          How to add modules

          learning how to use browser developers window for css changes

          R 2 Replies Last reply Reply Quote 0
          • 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 Do not disturb
                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 Do not disturb
                  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

                    Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                    Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                    With your input, this post could be even better 💗

                    Register Login
                    • 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