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 174 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 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

          @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 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