Weiter zum Inhalt
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Visualisierung
  4. Google Kalender (ICS) für Material Design & Mini-Kalender

NEWS

  • Der neue Monatsrückblick für Mai und Juni 2026 ist online!
    BluefoxB
    Bluefox
    8
    1
    647

  • wichtiges UPDATE für controller 7.2.2 im stable
    HomoranH
    Homoran
    10
    1
    3.3k

  • Neues YouTube-Video: Visualisierung im Devices-Adapter
    BluefoxB
    Bluefox
    16
    1
    5.2k

Google Kalender (ICS) für Material Design & Mini-Kalender

Geplant Angeheftet Gesperrt Verschoben Visualisierung
vis
67 Beiträge 7 Kommentatoren 700 Aufrufe 9 Beobachtet
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • Ro75R Ro75

    Version 1.0.1 - 23.07.2026

    • Ganztagstermine, über mehrere Tage, werden korrekt dargestellt.

    Script in Post #1 wurde aktualisiert.

    Ro75.

    sigi234S Online
    sigi234S Online
    sigi234
    Forum Testing Most Active
    schrieb am zuletzt editiert von sigi234
    #51

    @Ro75 sagte:

    Version 1.0.1 - 23.07.2026

    • Ganztagstermine, über mehrere Tage, werden korrekt dargestellt.

    Script in Post #1 wurde aktualisiert.

    Ro75.

    Bei mir leider nicht.

    Screenshot (2971).png

    Screenshot (2972).png

    Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.
    Immer Daten sichern!

    Ro75R 1 Antwort Letzte Antwort
    0
    • sigi234S sigi234

      @Ro75 sagte:

      Version 1.0.1 - 23.07.2026

      • Ganztagstermine, über mehrere Tage, werden korrekt dargestellt.

      Script in Post #1 wurde aktualisiert.

      Ro75.

      Bei mir leider nicht.

      Screenshot (2971).png

      Screenshot (2972).png

      Ro75R Online
      Ro75R Online
      Ro75
      schrieb zuletzt editiert von Ro75
      #52

      @sigi234 sagte:

      Bei mir leider nicht.

      Ich habe es gerade nochmal getestet. Bei mir kommt der Termin. Welchen Datenpunkt nimmst Du? M1 oder C1? Schon mal reingesehen was drin steht?

      Ro75.

      EDIT: Ich habe über die Android-App einen Ganztagstermin über 4 Tage gemacht - Ergebnis: OK. Dann habe ich einen Ganztagstermin über 14 Tage gemacht via Google im Browser - Ergebnis OK.

      Unbenannt2.png

      Zur Sicherheit habe ich meinen Code hier mit dem Code in Post #1 verglichen: passt. Prüfe bitte C1 und M1. Eventuell auch ein Fehler in der Darstellung?

      Unbenannt_anonymized.png

      SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

      1 Antwort Letzte Antwort
      0
      • NashraN Offline
        NashraN Offline
        Nashra
        Most Active Forum Testing
        schrieb zuletzt editiert von Nashra
        #53

        @ro75
        sigi234 nutzt wohl den kleinen m1.
        Habe auch mal getestet und da kommt nur ein Eintrag
        Screenshot 2026-07-24 135155.png Screenshot 2026-07-24 135241.png

        beim großen C1 ist alles ok
        Screenshot 2026-07-24 135320.png
        Screenshot 2026-07-24 135714.png

        Gruß Ralf
        Mir egal, wer Dein Vater ist! Wenn ich hier angel, wird nicht übers Wasser gelaufen!!

        Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

        1 Antwort Letzte Antwort
        0
        • Ro75R Online
          Ro75R Online
          Ro75
          schrieb zuletzt editiert von
          #54

          @nashra vielen Dank für den Hinweis. Ich habe das Problem gefunden. Ich passe das Script an. @sigi234 ich werde noch heute eine neue Scriptversion zur Verfügung stellen, damit ist das Problem dann auch behoben. Bitte noch etwas Geduld. Danke.

          Ro75.

          SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

          1 Antwort Letzte Antwort
          1
          • NashraN Offline
            NashraN Offline
            Nashra
            Most Active Forum Testing
            schrieb zuletzt editiert von Nashra
            #55

            @ro75
            ich habe da gerade noch was gefunden....
            oben im Bild für den m1 fehlt ein Termin d.h. wird nicht gelistet.
            Es ist wie im c1 der "In Memory Aponi" am 29.7.
            taucht auch nicht in der m1.json auf

              {
                "img": "http://192.168.1.14:8082/vis.0/kalender/muell.png",
                "zeit": "27.07.2026 - 27.07.2026",
                "ereignis": "Wertstoffe",
                "ort": ""
              },
              {
                "img": "http://192.168.1.14:8082/vis.0/kalender/muell.png",
                "zeit": "07.08.2026 - 07.08.2026",
                "ereignis": "Restmüll",
                "ort": ""
              },
            
            

            Gruß Ralf
            Mir egal, wer Dein Vater ist! Wenn ich hier angel, wird nicht übers Wasser gelaufen!!

            Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

            1 Antwort Letzte Antwort
            0
            • Ro75R Online
              Ro75R Online
              Ro75
              schrieb zuletzt editiert von
              #56

              Verstehe ich gerade nicht. Wo fehlt er denn? M1 oder C1?

              Ro75.

              SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

              NashraN 1 Antwort Letzte Antwort
              0
              • Ro75R Ro75

                Verstehe ich gerade nicht. Wo fehlt er denn? M1 oder C1?

                Ro75.

                NashraN Offline
                NashraN Offline
                Nashra
                Most Active Forum Testing
                schrieb zuletzt editiert von
                #57

                @Ro75 sagte:

                Verstehe ich gerade nicht. Wo fehlt er denn? M1 oder C1?

                Ro75.

                In der m1, habe ich doch geschrieben

                Gruß Ralf
                Mir egal, wer Dein Vater ist! Wenn ich hier angel, wird nicht übers Wasser gelaufen!!

                Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                1 Antwort Letzte Antwort
                0
                • Ro75R Online
                  Ro75R Online
                  Ro75
                  schrieb zuletzt editiert von
                  #58

                  @nashra Problem gefunden und auch beseitigt. Bitte noch etwas Geduld bis ich das neue Script freigebe - teste noch etwas.

                  Ro75.

                  SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                  NashraN 1 Antwort Letzte Antwort
                  1
                  • Ro75R Ro75

                    @nashra Problem gefunden und auch beseitigt. Bitte noch etwas Geduld bis ich das neue Script freigebe - teste noch etwas.

                    Ro75.

                    NashraN Offline
                    NashraN Offline
                    Nashra
                    Most Active Forum Testing
                    schrieb zuletzt editiert von
                    #59

                    @Ro75 sagte:

                    @nashra Problem gefunden und auch beseitigt. Bitte noch etwas Geduld bis ich das neue Script freigebe - teste noch etwas.

                    Ro75.

                    Kein Problem, nutze den großen Kalender. Ist mir eigentlich auch nur zufällig aufgefallen bei dem kleinen.

                    Gruß Ralf
                    Mir egal, wer Dein Vater ist! Wenn ich hier angel, wird nicht übers Wasser gelaufen!!

                    Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                    1 Antwort Letzte Antwort
                    0
                    • Ro75R Online
                      Ro75R Online
                      Ro75
                      schrieb zuletzt editiert von
                      #60

                      Version 1.0.2 - 24.07.2026

                      • Ganztagstermine, über mehrere Tage, werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt.
                      • "Spezialtermine" wie "In Memory" oder auch Feiertage, etc. werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt, inklusive Emoji

                      Script in Post #1 angepasst.

                      Ro75.

                      SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                      sigi234S 1 Antwort Letzte Antwort
                      1
                      • OliverIOO Offline
                        OliverIOO Offline
                        OliverIO
                        schrieb zuletzt editiert von OliverIO
                        #61

                        Ich habe mal was für den großen Kalender probiert, welcher nur auf dem jsonTemplate und dem m1 Datenpunkt basiert

                        <%
                        /* ==========================================================================
                          KONFIGURATION
                          ========================================================================== */
                        
                        const calendarConfig = {
                           /* Startansicht: "month", "week" oder "day" */
                           defaultView: "month",
                        
                           /* Sprache für Datumsausgaben */
                           locale: "de-DE",
                        
                           /* Erster Wochentag: 1 = Montag, 0 = Sonntag */
                           firstDayOfWeek: 1,
                        
                           /* Wochenende hervorheben */
                           highlightWeekend: true,
                        
                           /* Kalenderwoche anzeigen */
                           showWeekNumbers: true,
                        
                           /* Bilder der Termine anzeigen */
                           showImages: true,
                        
                           /* Leere Orte ausblenden */
                           hideEmptyLocation: true,
                        
                           /*
                            * Farbmodus:
                            * "light" = helle Darstellung
                            * "dark"  = dunkle Darstellung
                            * "auto"  = Systemeinstellung des Browsers verwenden
                            */
                           theme: "auto",
                        
                           /*
                            * Methode zur automatischen Gruppierung und Farbzuordnung.
                            *
                            * "img":
                            * Termine mit derselben Bild-URL erhalten dieselbe Farbe.
                            *
                            * "eventPrefix":
                            * Der Teil des Ereignisnamens vor dem ersten Doppelpunkt wird verwendet.
                            */
                           colorGrouping: "img",
                        
                           /*
                            * Optional feste Farben vorgeben.
                            *
                            * matchField darf nur "ereignis" oder "img" sein.
                            * match enthält einen Text, der im entsprechenden JSON-Feld gesucht wird.
                            *
                            * Regeln werden von oben nach unten geprüft.
                            * Nicht passende Termine erhalten automatisch eine Farbe.
                            */
                           colorRules: [
                               {
                                   matchField: "ereignis",
                                   match: "Altpapier",
                                   color: "#d32f2f"
                               },
                               {
                                   matchField: "ereignis",
                                   match: "Restmüll",
                                   color: "#546e7a"
                               },
                               {
                                   matchField: "ereignis",
                                   match: "Biotonne",
                                   color: "#2e7d32"
                               },
                               {
                                   matchField: "ereignis",
                                   match: "gelber Sack",
                                   color: "#f9a825"
                               },
                               {
                                   matchField: "ereignis",
                                   match: "Geburtstag",
                                   color: "#8e24aa"
                               }
                           ],
                        
                           /*
                            * Automatisch verwendete Farben.
                            * Die Farben sollten einen ausreichenden Kontrast zu weißer Schrift haben.
                            */
                           autoColors: [
                               "#1565c0",
                               "#2e7d32",
                               "#c62828",
                               "#6a1b9a",
                               "#00838f",
                               "#ef6c00",
                               "#4527a0",
                               "#ad1457",
                               "#00695c",
                               "#283593",
                               "#558b2f",
                               "#5d4037"
                           ],
                        
                           /*
                            * Optionale Oberflächenfarben.
                            * Ein leerer Wert verwendet die Farbe des jeweiligen Themes.
                            */
                           colors: {
                               light: {
                                   background: "",
                                   surface: "",
                                   surfaceSecondary: "",
                                   text: "",
                                   textSecondary: "",
                                   border: "",
                                   today: "",
                                   weekend: ""
                               },
                               dark: {
                                   background: "",
                                   surface: "",
                                   surfaceSecondary: "",
                                   text: "",
                                   textSecondary: "",
                                   border: "",
                                   today: "",
                                   weekend: ""
                               }
                           },
                        
                           /* Beschriftungen */
                           labels: {
                               today: "Heute",
                               month: "Monat",
                               week: "Woche",
                               day: "Tag",
                               previous: "Zurück",
                               next: "Weiter",
                               allDay: "Ganztägig",
                               noEvents: "Keine Termine",
                               location: "Ort",
                               calendarWeekShort: "KW",
                               invalidDate: "Ungültiges Datum"
                           }
                        };
                        
                        
                        /* ==========================================================================
                          DATEN GLOBAL FÜR DIESES WIDGET BEREITSTELLEN
                          ========================================================================== */
                        
                        const calendarGlobalStoreName = "__visJsontemplateCalendarWidgetStore";
                        
                        if (typeof window !== "undefined") {
                           if (
                               !window[calendarGlobalStoreName] ||
                               typeof window[calendarGlobalStoreName] !== "object"
                           ) {
                               window[calendarGlobalStoreName] = Object.create(null);
                           }
                        
                           /*
                            * Einen bereits vorhandenen Zustand nicht überschreiben.
                            * Bei einer Datenaktualisierung wird das Template komplett neu erzeugt,
                            * die globale window-Variable bleibt jedoch erhalten.
                            */
                           const existingWidgetStore =
                               window[calendarGlobalStoreName][widgetid] &&
                               typeof window[calendarGlobalStoreName][widgetid] === "object"
                                   ? window[calendarGlobalStoreName][widgetid]
                                   : {};
                        
                           window[calendarGlobalStoreName][widgetid] = {
                               data: data,
                               config: calendarConfig,
                        
                               state:
                                   existingWidgetStore.state &&
                                   typeof existingWidgetStore.state === "object"
                                       ? existingWidgetStore.state
                                       : {
                                           view: calendarConfig.defaultView,
                                           currentDate: null
                                       }
                           };
                        }
                        %>
                        
                        <style>
                        #<%- widgetid %>
                        {
                           --jscal-background-light: <%- calendarConfig.colors.light.background || "#f4f6f8" %>;
                           --jscal-surface-light: <%- calendarConfig.colors.light.surface || "#ffffff" %>;
                           --jscal-surface-secondary-light: <%- calendarConfig.colors.light.surfaceSecondary || "#f7f8fa" %>;
                           --jscal-text-light: <%- calendarConfig.colors.light.text || "#17212b" %>;
                           --jscal-text-secondary-light: <%- calendarConfig.colors.light.textSecondary || "#66717d" %>;
                           --jscal-border-light: <%- calendarConfig.colors.light.border || "#d7dce2" %>;
                           --jscal-today-light: <%- calendarConfig.colors.light.today || "#e3f2fd" %>;
                           --jscal-weekend-light: <%- calendarConfig.colors.light.weekend || "#faf4f4" %>;
                        
                           --jscal-background-dark: <%- calendarConfig.colors.dark.background || "#12171d" %>;
                           --jscal-surface-dark: <%- calendarConfig.colors.dark.surface || "#1c232b" %>;
                           --jscal-surface-secondary-dark: <%- calendarConfig.colors.dark.surfaceSecondary || "#252e38" %>;
                           --jscal-text-dark: <%- calendarConfig.colors.dark.text || "#f1f4f7" %>;
                           --jscal-text-secondary-dark: <%- calendarConfig.colors.dark.textSecondary || "#aeb8c2" %>;
                           --jscal-border-dark: <%- calendarConfig.colors.dark.border || "#3b4652" %>;
                           --jscal-today-dark: <%- calendarConfig.colors.dark.today || "#173b55" %>;
                           --jscal-weekend-dark: <%- calendarConfig.colors.dark.weekend || "#271f24" %>;
                        
                           --jscal-background: var(--jscal-background-light);
                           --jscal-surface: var(--jscal-surface-light);
                           --jscal-surface-secondary: var(--jscal-surface-secondary-light);
                           --jscal-text: var(--jscal-text-light);
                           --jscal-text-secondary: var(--jscal-text-secondary-light);
                           --jscal-border: var(--jscal-border-light);
                           --jscal-today: var(--jscal-today-light);
                           --jscal-weekend: var(--jscal-weekend-light);
                        
                           box-sizing: border-box;
                           width: 100%;
                           height: 100%;
                           min-height: 320px;
                           overflow: hidden;
                           color: var(--jscal-text);
                           background: var(--jscal-background);
                           font-family: Arial, Helvetica, sans-serif;
                        }
                        
                        #<%- widgetid %> *,
                        #<%- widgetid %> *::before,
                        #<%- widgetid %> *::after
                        {
                           box-sizing: border-box;
                        }
                        
                        #<%- widgetid %>[data-jscal-theme="dark"]
                        {
                           --jscal-background: var(--jscal-background-dark);
                           --jscal-surface: var(--jscal-surface-dark);
                           --jscal-surface-secondary: var(--jscal-surface-secondary-dark);
                           --jscal-text: var(--jscal-text-dark);
                           --jscal-text-secondary: var(--jscal-text-secondary-dark);
                           --jscal-border: var(--jscal-border-dark);
                           --jscal-today: var(--jscal-today-dark);
                           --jscal-weekend: var(--jscal-weekend-dark);
                        }
                        
                        @media (prefers-color-scheme: dark)
                        {
                           #<%- widgetid %>[data-jscal-theme="auto"]
                           {
                               --jscal-background: var(--jscal-background-dark);
                               --jscal-surface: var(--jscal-surface-dark);
                               --jscal-surface-secondary: var(--jscal-surface-secondary-dark);
                               --jscal-text: var(--jscal-text-dark);
                               --jscal-text-secondary: var(--jscal-text-secondary-dark);
                               --jscal-border: var(--jscal-border-dark);
                               --jscal-today: var(--jscal-today-dark);
                               --jscal-weekend: var(--jscal-weekend-dark);
                           }
                        }
                        
                        #<%- widgetid %> .jscal
                        {
                           display: flex;
                           flex-direction: column;
                           width: 100%;
                           height: 100%;
                           min-height: 320px;
                           background: var(--jscal-background);
                        }
                        
                        #<%- widgetid %> .jscal-toolbar
                        {
                           display: flex;
                           flex: 0 0 auto;
                           align-items: center;
                           gap: 8px;
                           min-height: 58px;
                           padding: 8px 10px;
                           border-bottom: 1px solid var(--jscal-border);
                           background: var(--jscal-surface);
                        }
                        
                        #<%- widgetid %> .jscal-navigation,
                        #<%- widgetid %> .jscal-view-buttons
                        {
                           display: flex;
                           align-items: center;
                           gap: 4px;
                        }
                        
                        #<%- widgetid %> .jscal-view-buttons
                        {
                           margin-left: auto;
                        }
                        
                        #<%- widgetid %> .jscal-title
                        {
                           min-width: 0;
                           margin: 0 8px;
                           overflow: hidden;
                           color: var(--jscal-text);
                           font-size: 18px;
                           font-weight: 700;
                           line-height: 1.2;
                           text-align: center;
                           text-overflow: ellipsis;
                           white-space: nowrap;
                        }
                        
                        #<%- widgetid %> .jscal-button
                        {
                           display: inline-flex;
                           align-items: center;
                           justify-content: center;
                           min-width: 36px;
                           height: 36px;
                           padding: 0 10px;
                           border: 1px solid var(--jscal-border);
                           border-radius: 7px;
                           color: var(--jscal-text);
                           background: var(--jscal-surface-secondary);
                           font: inherit;
                           font-size: 13px;
                           font-weight: 600;
                           line-height: 1;
                           cursor: pointer;
                           user-select: none;
                           -webkit-tap-highlight-color: transparent;
                        }
                        
                        #<%- widgetid %> .jscal-button:hover
                        {
                           filter: brightness(0.96);
                        }
                        
                        #<%- widgetid %> .jscal-button:focus-visible
                        {
                           outline: 2px solid #1976d2;
                           outline-offset: 2px;
                        }
                        
                        #<%- widgetid %> .jscal-button.is-active
                        {
                           border-color: #1976d2;
                           color: #ffffff;
                           background: #1976d2;
                        }
                        
                        #<%- widgetid %> .jscal-button-icon
                        {
                           width: 36px;
                           padding: 0;
                        }
                        
                        #<%- widgetid %> .jscal-button svg
                        {
                           width: 20px;
                           height: 20px;
                           fill: currentColor;
                        }
                        
                        #<%- widgetid %> .jscal-content
                        {
                           position: relative;
                           flex: 1 1 auto;
                           min-height: 0;
                           overflow: auto;
                           overscroll-behavior: contain;
                           background: var(--jscal-background);
                        }
                        
                        #<%- widgetid %> .jscal-month
                        {
                           display: grid;
                           min-width: 640px;
                           grid-template-columns: auto repeat(7, minmax(80px, 1fr));
                           background: var(--jscal-border);
                           gap: 1px;
                        }
                        
                        #<%- widgetid %> .jscal-month.no-week-numbers
                        {
                           grid-template-columns: repeat(7, minmax(80px, 1fr));
                        }
                        
                        #<%- widgetid %> .jscal-week-header
                        {
                           position: sticky;
                           top: 0;
                           z-index: 4;
                           display: flex;
                           align-items: center;
                           justify-content: center;
                           min-height: 36px;
                           padding: 5px;
                           color: var(--jscal-text-secondary);
                           background: var(--jscal-surface-secondary);
                           font-size: 12px;
                           font-weight: 700;
                           text-align: center;
                        }
                        
                        #<%- widgetid %> .jscal-week-number
                        {
                           display: flex;
                           align-items: center;
                           justify-content: center;
                           min-height: 112px;
                           padding: 4px;
                           color: var(--jscal-text-secondary);
                           background: var(--jscal-surface-secondary);
                           font-size: 11px;
                           font-weight: 700;
                        }
                        
                        #<%- widgetid %> .jscal-month-day
                        {
                           position: relative;
                           min-height: 112px;
                           padding: 5px;
                           overflow: hidden;
                           background: var(--jscal-surface);
                        }
                        
                        #<%- widgetid %> .jscal-month-day.is-outside
                        {
                           opacity: 0.52;
                        }
                        
                        #<%- widgetid %> .jscal-month-day.is-today
                        {
                           background: var(--jscal-today);
                        }
                        
                        #<%- widgetid %> .jscal-month-day.is-weekend
                        {
                           background: var(--jscal-weekend);
                        }
                        
                        #<%- widgetid %> .jscal-month-day.is-weekend.is-today
                        {
                           background: var(--jscal-today);
                        }
                        
                        #<%- widgetid %> .jscal-day-number
                        {
                           display: flex;
                           align-items: center;
                           justify-content: flex-end;
                           height: 22px;
                           margin-bottom: 3px;
                           color: var(--jscal-text);
                           font-size: 12px;
                           font-weight: 700;
                        }
                        
                        #<%- widgetid %> .jscal-day-number span
                        {
                           display: inline-flex;
                           align-items: center;
                           justify-content: center;
                           min-width: 22px;
                           height: 22px;
                           padding: 0 4px;
                           border-radius: 11px;
                        }
                        
                        #<%- widgetid %> .jscal-month-day.is-today .jscal-day-number span
                        {
                           color: #ffffff;
                           background: #1976d2;
                        }
                        
                        #<%- widgetid %> .jscal-events
                        {
                           display: flex;
                           flex-direction: column;
                           gap: 3px;
                        }
                        
                        #<%- widgetid %> .jscal-event
                        {
                           position: relative;
                           display: flex;
                           align-items: center;
                           gap: 5px;
                           min-width: 0;
                           min-height: 24px;
                           padding: 3px 6px;
                           border: 0;
                           border-left: 4px solid var(--jscal-event-color);
                           border-radius: 4px;
                           color: #ffffff;
                           background: var(--jscal-event-color);
                           box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.07);
                           font: inherit;
                           font-size: 11px;
                           line-height: 1.25;
                           text-align: left;
                           cursor: pointer;
                           -webkit-tap-highlight-color: transparent;
                        }
                        
                        #<%- widgetid %> .jscal-event:hover,
                        #<%- widgetid %> .jscal-event:focus-visible,
                        #<%- widgetid %> .jscal-event.is-selected
                        {
                           filter: brightness(1.12);
                           outline: none;
                        }
                        
                        #<%- widgetid %> .jscal-event-image
                        {
                           flex: 0 0 auto;
                           width: 17px;
                           height: 17px;
                           border-radius: 3px;
                           object-fit: contain;
                           background: rgba(255, 255, 255, 0.82);
                        }
                        
                        #<%- widgetid %> .jscal-event-time
                        {
                           flex: 0 0 auto;
                           font-weight: 700;
                           white-space: nowrap;
                        }
                        
                        #<%- widgetid %> .jscal-event-title
                        {
                           min-width: 0;
                           overflow: hidden;
                           text-overflow: ellipsis;
                           white-space: nowrap;
                        }
                        
                        #<%- widgetid %> .jscal-more
                        {
                           padding: 2px 5px;
                           color: var(--jscal-text-secondary);
                           font-size: 11px;
                           font-weight: 700;
                        }
                        
                        #<%- widgetid %> .jscal-week
                        {
                           display: grid;
                           min-width: 720px;
                           min-height: 100%;
                           grid-template-columns: repeat(7, minmax(100px, 1fr));
                           gap: 1px;
                           background: var(--jscal-border);
                        }
                        
                        #<%- widgetid %> .jscal-week-day
                        {
                           display: flex;
                           flex-direction: column;
                        
                           min-height: 100%;
                           overflow: hidden;
                        
                           background: var(--jscal-surface);
                        }
                        
                        #<%- widgetid %> .jscal-week-day.is-weekend
                        {
                           background: var(--jscal-weekend);
                        }
                        
                        #<%- widgetid %> .jscal-week-day.is-today
                        {
                           background: var(--jscal-today);
                        }
                        
                        #<%- widgetid %> .jscal-week-day-header
                        {
                           position: sticky;
                           top: 0;
                           z-index: 3;
                           display: flex;
                           flex-direction: column;
                           align-items: center;
                           gap: 2px;
                           min-height: 55px;
                           padding: 7px 4px;
                           border-bottom: 1px solid var(--jscal-border);
                           background: inherit;
                        }
                        
                        #<%- widgetid %> .jscal-week-day-name
                        {
                           color: var(--jscal-text-secondary);
                           font-size: 11px;
                           font-weight: 700;
                           text-transform: uppercase;
                        }
                        
                        #<%- widgetid %> .jscal-week-day-date
                        {
                           display: inline-flex;
                           align-items: center;
                           justify-content: center;
                           min-width: 30px;
                           height: 30px;
                           padding: 0 5px;
                           border-radius: 15px;
                           color: var(--jscal-text);
                           font-size: 16px;
                           font-weight: 700;
                        }
                        
                        #<%- widgetid %> .jscal-week-day.is-today .jscal-week-day-date
                        {
                           color: #ffffff;
                           background: #1976d2;
                        }
                        
                        #<%- widgetid %> .jscal-week-events
                        {
                           flex: 1;
                           overflow: hidden;
                        }
                        
                        #<%- widgetid %> .jscal-week .jscal-event
                        {
                           display: flex;
                           align-items: flex-start;
                           gap: 6px;
                        
                           min-height: 36px;
                           max-height: 54px;
                        
                           padding: 6px;
                        
                           overflow: hidden;
                        }
                        
                        #<%- widgetid %> .jscal-week .jscal-event-title
                        {
                           flex: 1;
                           min-width: 0;
                        
                           overflow: hidden;
                           text-overflow: ellipsis;
                        
                           display: -webkit-box;
                           -webkit-box-orient: vertical;
                           -webkit-line-clamp: 2;
                        
                           line-height: 1.2;
                           white-space: normal;
                        
                           overflow-wrap: anywhere;
                           word-break: break-word;
                        }
                        
                        #<%- widgetid %> .jscal-empty
                        {
                           padding: 14px 6px;
                           color: var(--jscal-text-secondary);
                           font-size: 12px;
                           text-align: center;
                        }
                        
                        #<%- widgetid %> .jscal-day-view
                        {
                           min-height: 100%;
                           padding: 12px;
                        }
                        
                        #<%- widgetid %> .jscal-day-section
                        {
                           margin-bottom: 16px;
                        }
                        
                        #<%- widgetid %> .jscal-day-section-title
                        {
                           margin: 0 0 7px;
                           padding: 0 2px 6px;
                           border-bottom: 1px solid var(--jscal-border);
                           color: var(--jscal-text-secondary);
                           font-size: 12px;
                           font-weight: 700;
                           text-transform: uppercase;
                        }
                        
                        #<%- widgetid %> .jscal-day-event
                        {
                           display: grid;
                           grid-template-columns: 70px minmax(0, 1fr);
                           margin-bottom: 8px;
                           border: 1px solid var(--jscal-border);
                           border-left: 5px solid var(--jscal-event-color);
                           border-radius: 7px;
                           overflow: hidden;
                           background: var(--jscal-surface);
                        }
                        
                        #<%- widgetid %> .jscal-day-event-time
                        {
                           display: flex;
                           align-items: flex-start;
                           justify-content: center;
                           padding: 11px 6px;
                           color: var(--jscal-text-secondary);
                           background: var(--jscal-surface-secondary);
                           font-size: 12px;
                           font-weight: 700;
                           text-align: center;
                        }
                        
                        #<%- widgetid %> .jscal-day-event-content
                        {
                           display: flex;
                           align-items: flex-start;
                           gap: 9px;
                           min-width: 0;
                           padding: 9px;
                           border: 0;
                           color: var(--jscal-text);
                           background: transparent;
                           font: inherit;
                           text-align: left;
                           cursor: pointer;
                        }
                        
                        #<%- widgetid %> .jscal-day-event-content:hover,
                        #<%- widgetid %> .jscal-day-event-content:focus-visible
                        {
                           background: var(--jscal-surface-secondary);
                           outline: none;
                        }
                        
                        #<%- widgetid %> .jscal-day-event-image
                        {
                           flex: 0 0 auto;
                           width: 34px;
                           height: 34px;
                           border-radius: 5px;
                           object-fit: contain;
                        }
                        
                        #<%- widgetid %> .jscal-day-event-text
                        {
                           min-width: 0;
                        }
                        
                        #<%- widgetid %> .jscal-day-event-title
                        {
                           color: var(--jscal-text);
                           font-size: 14px;
                           font-weight: 700;
                           line-height: 1.35;
                        }
                        
                        #<%- widgetid %> .jscal-day-event-location
                        {
                           margin-top: 4px;
                           color: var(--jscal-text-secondary);
                           font-size: 12px;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip
                        {
                           position: fixed;
                           z-index: 2147483000;
                           display: none;
                           width: min(320px, calc(100vw - 20px));
                           max-height: min(360px, calc(100vh - 20px));
                           padding: 12px;
                           overflow: auto;
                           border: 1px solid var(--jscal-border);
                           border-radius: 9px;
                           color: var(--jscal-text);
                           background: var(--jscal-surface);
                           box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
                           pointer-events: none;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip.is-visible
                        {
                           display: block;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip-header
                        {
                           display: flex;
                           align-items: flex-start;
                           gap: 9px;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip-image
                        {
                           flex: 0 0 auto;
                           width: 42px;
                           height: 42px;
                           border-radius: 6px;
                           object-fit: contain;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip-title
                        {
                           min-width: 0;
                           color: var(--jscal-text);
                           font-size: 14px;
                           font-weight: 700;
                           line-height: 1.35;
                           overflow-wrap: anywhere;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip-row
                        {
                           display: grid;
                           grid-template-columns: 62px minmax(0, 1fr);
                           gap: 8px;
                           margin-top: 10px;
                           color: var(--jscal-text);
                           font-size: 12px;
                           line-height: 1.4;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip-label
                        {
                           color: var(--jscal-text-secondary);
                           font-weight: 700;
                        }
                        
                        #<%- widgetid %> .jscal-tooltip-value
                        {
                           overflow-wrap: anywhere;
                        }
                        
                        #<%- widgetid %> .jscal-invalid
                        {
                           margin: 6px;
                           padding: 7px;
                           border: 1px solid var(--jscal-border);
                           border-left: 4px solid #c62828;
                           border-radius: 5px;
                           color: var(--jscal-text);
                           background: var(--jscal-surface);
                           font-size: 12px;
                        }
                        
                        @media (max-width: 700px)
                        {
                           #<%- widgetid %> .jscal-toolbar
                           {
                               flex-wrap: wrap;
                               gap: 6px;
                           }
                        
                           #<%- widgetid %> .jscal-title
                           {
                               order: -1;
                               flex: 1 0 100%;
                               width: 100%;
                               margin: 0;
                               padding: 3px;
                               font-size: 16px;
                           }
                        
                           #<%- widgetid %> .jscal-view-buttons
                           {
                               margin-left: auto;
                           }
                        
                           #<%- widgetid %> .jscal-button
                           {
                               min-width: 34px;
                               height: 34px;
                               padding: 0 8px;
                               font-size: 12px;
                           }
                        
                           #<%- widgetid %> .jscal-month-day
                           {
                               min-height: 96px;
                           }
                        }
                        </style>
                        
                        <div class="jscal" data-jscal-container>
                           <div class="jscal-toolbar">
                               <div class="jscal-navigation">
                                   <button
                                       type="button"
                                       class="jscal-button jscal-button-icon"
                                       data-jscal-action="previous"
                                       aria-label="<%- calendarConfig.labels.previous %>"
                                       title="<%- calendarConfig.labels.previous %>"
                                   >
                                       <svg viewBox="0 0 24 24" aria-hidden="true">
                                           <path d="M15.4 5.4 8.8 12l6.6 6.6-1.4 1.4L6 12l8-8z"></path>
                                       </svg>
                                   </button>
                        
                                   <button
                                       type="button"
                                       class="jscal-button"
                                       data-jscal-action="today"
                                   >
                                       <%- calendarConfig.labels.today %>
                                   </button>
                        
                                   <button
                                       type="button"
                                       class="jscal-button jscal-button-icon"
                                       data-jscal-action="next"
                                       aria-label="<%- calendarConfig.labels.next %>"
                                       title="<%- calendarConfig.labels.next %>"
                                   >
                                       <svg viewBox="0 0 24 24" aria-hidden="true">
                                           <path d="M8.6 18.6 15.2 12 8.6 5.4 10 4l8 8-8 8z"></path>
                                       </svg>
                                   </button>
                               </div>
                        
                               <div class="jscal-title" data-jscal-title></div>
                        
                               <div class="jscal-view-buttons">
                                   <button
                                       type="button"
                                       class="jscal-button"
                                       data-jscal-view="month"
                                   >
                                       <%- calendarConfig.labels.month %>
                                   </button>
                        
                                   <button
                                       type="button"
                                       class="jscal-button"
                                       data-jscal-view="week"
                                   >
                                       <%- calendarConfig.labels.week %>
                                   </button>
                        
                                   <button
                                       type="button"
                                       class="jscal-button"
                                       data-jscal-view="day"
                                   >
                                       <%- calendarConfig.labels.day %>
                                   </button>
                               </div>
                           </div>
                        
                           <div class="jscal-content" data-jscal-content></div>
                        
                           <div
                               class="jscal-tooltip"
                               data-jscal-tooltip
                               role="tooltip"
                           ></div>
                        </div>
                        
                        <script>
                        (function ()
                        {
                           "use strict";
                           const widgetId = "<%- widgetid %>";
                           const globalStoreName = "__visJsontemplateCalendarWidgetStore";
                        
                           const widgetStore =
                           	window[globalStoreName] &&
                           	window[globalStoreName][widgetId]
                           		? window[globalStoreName][widgetId]
                           		: null;
                        
                           if (!widgetStore)
                           {
                           	console.warn(
                           		"JSON-Template-Kalender: Keine globalen Daten für Widget gefunden:",
                           		widgetId
                           	);
                        
                           	return;
                           }
                        
                           const rawData = widgetStore.data;
                           const config = widgetStore.config;
                        
                           const root = document.getElementById(widgetId);
                        
                           if (!root)
                           {
                               return;
                           }
                        
                           if (typeof root.__jsonCalendarCleanup === "function")
                           {
                               root.__jsonCalendarCleanup();
                           }
                        
                           root.setAttribute("data-jscal-theme", config.theme || "auto");
                        
                           const container = root.querySelector("[data-jscal-container]");
                           const contentElement = root.querySelector("[data-jscal-content]");
                           const titleElement = root.querySelector("[data-jscal-title]");
                           const tooltipElement = root.querySelector("[data-jscal-tooltip]");
                        
                           if (!container || !contentElement || !titleElement || !tooltipElement)
                           {
                               return;
                           }
                        
                           const storedState =
                           widgetStore.state &&
                           typeof widgetStore.state === "object"
                               ? widgetStore.state
                               : {};
                        
                           const storedView = ["month", "week", "day"].includes(storedState.view)
                           	? storedState.view
                           	: null;
                        
                           const storedCurrentDate =
                           	typeof storedState.currentDate === "string"
                           		? parseStoredDate(storedState.currentDate)
                           		: null;
                        
                           const state = {
                           	view:
                           		storedView ||
                           		(
                           			["month", "week", "day"].includes(config.defaultView)
                           				? config.defaultView
                           				: "month"
                           		),
                        
                           	currentDate:
                           		storedCurrentDate ||
                           		startOfDay(new Date()),
                        
                           	selectedEventElement: null,
                           	tooltipEventId: null
                           };
                        
                           const formatterCache = {};
                           const groupColorMap = new Map();
                        
                           const events = Array.isArray(rawData)
                               ? rawData
                                   .map(function (item, index)
                                   {
                                       return normalizeEvent(item, index);
                                   })
                                   .filter(function (item)
                                   {
                                       return item !== null;
                                   })
                               : [];
                        
                           const invalidItems = Array.isArray(rawData)
                               ? rawData
                                   .map(function (item, index)
                                   {
                                       return {
                                           item: item,
                                           index: index,
                                           parsed: normalizeEvent(item, index)
                                       };
                                   })
                                   .filter(function (entry)
                                   {
                                       return entry.parsed === null;
                                   })
                               : [];
                        
                           function pad(value)
                           {
                               return String(value).padStart(2, "0");
                           }
                        
                           function escapeHtml(value)
                           {
                               return String(value === undefined || value === null ? "" : value)
                                   .replace(/&/g, "&amp;")
                                   .replace(/</g, "&lt;")
                                   .replace(/>/g, "&gt;")
                                   .replace(/"/g, "&quot;")
                                   .replace(/'/g, "&#039;");
                           }
                        
                           function escapeAttribute(value)
                           {
                               return escapeHtml(value);
                           }
                        
                           function createLocalDate(year, month, day, hour, minute)
                           {
                               const date = new Date(
                                   year,
                                   month - 1,
                                   day,
                                   hour || 0,
                                   minute || 0,
                                   0,
                                   0
                               );
                        
                               if (
                                   date.getFullYear() !== year ||
                                   date.getMonth() !== month - 1 ||
                                   date.getDate() !== day
                               )
                               {
                                   return null;
                               }
                        
                               return date;
                           }
                        
                           function parseStoredDate(value)
                           {
                           	if (typeof value !== "string")
                           	{
                           		return null;
                           	}
                        
                           	const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
                        
                           	if (!match)
                           	{
                           		return null;
                           	}
                        
                           	return createLocalDate(
                           		Number(match[1]),
                           		Number(match[2]),
                           		Number(match[3]),
                           		0,
                           		0
                           	);
                           }
                        
                        
                           function parseDateTimeText(value)
                           {
                               if (typeof value !== "string")
                               {
                                   return null;
                               }
                        
                               const normalized = value
                                   .trim()
                                   .replace(/\s+/g, " ");
                        
                               const separatorMatch = normalized.match(/\s+-\s+/);
                        
                               if (!separatorMatch)
                               {
                                   return null;
                               }
                        
                               const separatorIndex = separatorMatch.index;
                               const separatorLength = separatorMatch[0].length;
                               const startText = normalized.slice(0, separatorIndex).trim();
                               const endText = normalized
                                   .slice(separatorIndex + separatorLength)
                                   .trim();
                        
                               const fullDatePattern = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
                               const timeOnlyPattern = /^(\d{1,2}):(\d{2})$/;
                        
                               const startMatch = startText.match(fullDatePattern);
                        
                               if (!startMatch)
                               {
                                   return null;
                               }
                        
                               const startDay = Number(startMatch[1]);
                               const startMonth = Number(startMatch[2]);
                               const startYear = Number(startMatch[3]);
                               const startHasTime = startMatch[4] !== undefined;
                               const startHour = startHasTime ? Number(startMatch[4]) : 0;
                               const startMinute = startHasTime ? Number(startMatch[5]) : 0;
                        
                               if (
                                   startHour < 0 ||
                                   startHour > 23 ||
                                   startMinute < 0 ||
                                   startMinute > 59
                               )
                               {
                                   return null;
                               }
                        
                               const start = createLocalDate(
                                   startYear,
                                   startMonth,
                                   startDay,
                                   startHour,
                                   startMinute
                               );
                        
                               if (!start)
                               {
                                   return null;
                               }
                        
                               let end = null;
                               let endHasTime = false;
                        
                               const endFullDateMatch = endText.match(fullDatePattern);
                               const endTimeOnlyMatch = endText.match(timeOnlyPattern);
                        
                               if (endFullDateMatch)
                               {
                                   const endDay = Number(endFullDateMatch[1]);
                                   const endMonth = Number(endFullDateMatch[2]);
                                   const endYear = Number(endFullDateMatch[3]);
                        
                                   endHasTime = endFullDateMatch[4] !== undefined;
                        
                                   const endHour = endHasTime ? Number(endFullDateMatch[4]) : 0;
                                   const endMinute = endHasTime ? Number(endFullDateMatch[5]) : 0;
                        
                                   if (
                                       endHour < 0 ||
                                       endHour > 23 ||
                                       endMinute < 0 ||
                                       endMinute > 59
                                   )
                                   {
                                       return null;
                                   }
                        
                                   end = createLocalDate(
                                       endYear,
                                       endMonth,
                                       endDay,
                                       endHour,
                                       endMinute
                                   );
                               } else if (endTimeOnlyMatch)
                               {
                                   endHasTime = true;
                        
                                   const endHour = Number(endTimeOnlyMatch[1]);
                                   const endMinute = Number(endTimeOnlyMatch[2]);
                        
                                   if (
                                       endHour < 0 ||
                                       endHour > 23 ||
                                       endMinute < 0 ||
                                       endMinute > 59
                                   )
                                   {
                                       return null;
                                   }
                        
                                   end = createLocalDate(
                                       startYear,
                                       startMonth,
                                       startDay,
                                       endHour,
                                       endMinute
                                   );
                               }
                        
                               if (!end)
                               {
                                   return null;
                               }
                        
                               const allDay = !startHasTime && !endHasTime;
                        
                               if (allDay)
                               {
                                   end.setHours(23, 59, 59, 999);
                               }
                        
                               if (end.getTime() < start.getTime())
                               {
                                   return null;
                               }
                        
                               return {
                                   start: start,
                                   end: end,
                                   allDay: allDay,
                                   sourceText: normalized
                               };
                           }
                        
                           function normalizeEvent(item, index)
                           {
                               if (!item || typeof item !== "object")
                               {
                                   return null;
                               }
                        
                               const parsedDate = parseDateTimeText(item.zeit);
                        
                               if (!parsedDate)
                               {
                                   return null;
                               }
                        
                               const event = {
                                   id: String(index),
                                   img: typeof item.img === "string"
                                       ? item.img.trim()
                                       : "",
                                   zeit: typeof item.zeit === "string"
                                       ? item.zeit.trim()
                                       : "",
                                   ereignis: typeof item.ereignis === "string"
                                       ? item.ereignis.trim()
                                       : "",
                                   ort: typeof item.ort === "string"
                                       ? item.ort.trim()
                                       : "",
                                   start: parsedDate.start,
                                   end: parsedDate.end,
                                   allDay: parsedDate.allDay
                               };
                        
                               event.color = getEventColor(event);
                        
                               return event;
                           }
                        
                           function getGroupingKey(event)
                           {
                               if (config.colorGrouping === "eventPrefix")
                               {
                                   const eventText = event.ereignis || "";
                                   const separatorPosition = eventText.indexOf(":");
                        
                                   if (separatorPosition >= 0)
                                   {
                                       return eventText.slice(0, separatorPosition).trim().toLowerCase();
                                   }
                        
                                   return eventText.trim().toLowerCase();
                               }
                        
                               return (event.img || event.ereignis || event.id).toLowerCase();
                           }
                        
                           function getEventColor(event)
                           {
                               if (Array.isArray(config.colorRules))
                               {
                                   for (const rule of config.colorRules)
                                   {
                                       if (
                                           !rule ||
                                           !["ereignis", "img"].includes(rule.matchField) ||
                                           typeof rule.match !== "string" ||
                                           typeof rule.color !== "string"
                                       )
                                       {
                                           continue;
                                       }
                        
                                       const fieldValue = String(event[rule.matchField] || "");
                        
                                       if (
                                           fieldValue
                                               .toLocaleLowerCase(config.locale)
                                               .includes(rule.match.toLocaleLowerCase(config.locale))
                                       )
                                       {
                                           return rule.color;
                                       }
                                   }
                               }
                        
                               const groupKey = getGroupingKey(event);
                        
                               if (groupColorMap.has(groupKey))
                               {
                                   return groupColorMap.get(groupKey);
                               }
                        
                               const palette = Array.isArray(config.autoColors) && config.autoColors.length > 0
                                   ? config.autoColors
                                   : ["#1565c0"];
                        
                               const color = palette[groupColorMap.size % palette.length];
                        
                               groupColorMap.set(groupKey, color);
                        
                               return color;
                           }
                        
                           function startOfDay(date)
                           {
                               return new Date(
                                   date.getFullYear(),
                                   date.getMonth(),
                                   date.getDate(),
                                   0,
                                   0,
                                   0,
                                   0
                               );
                           }
                        
                           function endOfDay(date)
                           {
                               return new Date(
                                   date.getFullYear(),
                                   date.getMonth(),
                                   date.getDate(),
                                   23,
                                   59,
                                   59,
                                   999
                               );
                           }
                        
                           function addDays(date, amount)
                           {
                               const result = new Date(date);
                               result.setDate(result.getDate() + amount);
                               return result;
                           }
                        
                           function addMonths(date, amount)
                           {
                               const targetDay = date.getDate();
                               const result = new Date(
                                   date.getFullYear(),
                                   date.getMonth() + amount,
                                   1
                               );
                        
                               const lastDay = new Date(
                                   result.getFullYear(),
                                   result.getMonth() + 1,
                                   0
                               ).getDate();
                        
                               result.setDate(Math.min(targetDay, lastDay));
                        
                               return result;
                           }
                        
                           function startOfWeek(date)
                           {
                               const result = startOfDay(date);
                               const currentWeekday = result.getDay();
                               const firstDay = Number(config.firstDayOfWeek) === 0 ? 0 : 1;
                               const difference = (currentWeekday - firstDay + 7) % 7;
                        
                               result.setDate(result.getDate() - difference);
                        
                               return result;
                           }
                        
                           function sameDay(first, second)
                           {
                               return (
                                   first.getFullYear() === second.getFullYear() &&
                                   first.getMonth() === second.getMonth() &&
                                   first.getDate() === second.getDate()
                               );
                           }
                        
                           function dateKey(date)
                           {
                               return [
                                   date.getFullYear(),
                                   pad(date.getMonth() + 1),
                                   pad(date.getDate())
                               ].join("-");
                           }
                        
                           function saveState()
                           {
                           	if (
                           		!window[globalStoreName] ||
                           		!window[globalStoreName][widgetId]
                           	)
                           	{
                           		return;
                           	}
                        
                           	window[globalStoreName][widgetId].state = {
                           		view: state.view,
                           		currentDate: dateKey(state.currentDate)
                           	};
                           }
                        
                        
                           function getFormatter(options)
                           {
                               const key = JSON.stringify(options);
                        
                               if (!formatterCache[key])
                               {
                                   formatterCache[key] = new Intl.DateTimeFormat(
                                       config.locale || "de-DE",
                                       options
                                   );
                               }
                        
                               return formatterCache[key];
                           }
                        
                           function formatDate(date, options)
                           {
                               return getFormatter(options).format(date);
                           }
                        
                           function formatTime(date)
                           {
                               return formatDate(date, {
                                   hour: "2-digit",
                                   minute: "2-digit"
                               });
                           }
                        
                           function formatEventTime(event)
                           {
                               if (event.allDay)
                               {
                                   if (sameDay(event.start, event.end))
                                   {
                                       return config.labels.allDay;
                                   }
                        
                                   return [
                                       formatDate(event.start, {
                                           day: "2-digit",
                                           month: "2-digit",
                                           year: "numeric"
                                       }),
                                       "–",
                                       formatDate(event.end, {
                                           day: "2-digit",
                                           month: "2-digit",
                                           year: "numeric"
                                       })
                                   ].join(" ");
                               }
                        
                               if (sameDay(event.start, event.end))
                               {
                                   return formatTime(event.start) + "–" + formatTime(event.end);
                               }
                        
                               return [
                                   formatDate(event.start, {
                                       day: "2-digit",
                                       month: "2-digit",
                                       year: "numeric",
                                       hour: "2-digit",
                                       minute: "2-digit"
                                   }),
                                   "–",
                                   formatDate(event.end, {
                                       day: "2-digit",
                                       month: "2-digit",
                                       year: "numeric",
                                       hour: "2-digit",
                                       minute: "2-digit"
                                   })
                               ].join(" ");
                           }
                        
                           function getIsoWeekNumber(date)
                           {
                               const target = new Date(
                                   Date.UTC(
                                       date.getFullYear(),
                                       date.getMonth(),
                                       date.getDate()
                                   )
                               );
                        
                               const weekday = target.getUTCDay() || 7;
                        
                               target.setUTCDate(target.getUTCDate() + 4 - weekday);
                        
                               const yearStart = new Date(
                                   Date.UTC(target.getUTCFullYear(), 0, 1)
                               );
                        
                               return Math.ceil(
                                   (((target - yearStart) / 86400000) + 1) / 7
                               );
                           }
                        
                           function eventOccursOnDay(event, day)
                           {
                               const dayStart = startOfDay(day);
                               const dayEnd = endOfDay(day);
                        
                               return (
                                   event.start.getTime() <= dayEnd.getTime() &&
                                   event.end.getTime() >= dayStart.getTime()
                               );
                           }
                        
                           function getEventsForDay(day)
                           {
                               return events
                                   .filter(function (event)
                                   {
                                       return eventOccursOnDay(event, day);
                                   })
                                   .sort(function (first, second)
                                   {
                                       if (first.allDay !== second.allDay)
                                       {
                                           return first.allDay ? -1 : 1;
                                       }
                        
                                       const timeDifference =
                                           first.start.getTime() - second.start.getTime();
                        
                                       if (timeDifference !== 0)
                                       {
                                           return timeDifference;
                                       }
                        
                                       return first.ereignis.localeCompare(
                                           second.ereignis,
                                           config.locale || "de-DE"
                                       );
                                   });
                           }
                        
                           function isWeekend(date)
                           {
                               return date.getDay() === 0 || date.getDay() === 6;
                           }
                        
                           function renderSmallEvent(event, options)
                           {
                               const settings = options || {};
                               const showTime = settings.showTime !== false;
                               const imageHtml =
                                   config.showImages &&
                                   event.img
                                       ? '<img class="jscal-event-image" src="' +
                                           escapeAttribute(event.img) +
                                           '" alt="">'
                                       : "";
                        
                               let timeHtml = "";
                        
                               if (showTime)
                               {
                                   timeHtml = '<span class="jscal-event-time">' +
                                       escapeHtml(
                                           event.allDay
                                               ? ""
                                               : formatTime(event.start)
                                       ) +
                                       "</span>";
                               }
                        
                               return [
                                   '<button type="button"',
                                   ' class="jscal-event"',
                                   ' data-jscal-event-id="',
                                   escapeAttribute(event.id),
                                   '"',
                                   ' style="--jscal-event-color:',
                                   escapeAttribute(event.color),
                                   ';"',
                                   ' aria-label="',
                                   escapeAttribute(event.ereignis),
                                   '">',
                                   imageHtml,
                                   timeHtml,
                                   '<span class="jscal-event-title">',
                                   escapeHtml(event.ereignis || ""),
                                   "</span>",
                                   "</button>"
                               ].join("");
                           }
                        
                           function renderMonth()
                           {
                               const monthStart = new Date(
                                   state.currentDate.getFullYear(),
                                   state.currentDate.getMonth(),
                                   1
                               );
                        
                               const monthEnd = new Date(
                                   state.currentDate.getFullYear(),
                                   state.currentDate.getMonth() + 1,
                                   0
                               );
                        
                               const gridStart = startOfWeek(monthStart);
                               let gridEnd = addDays(startOfWeek(monthEnd), 6);
                        
                               if (gridEnd.getTime() < monthEnd.getTime())
                               {
                                   gridEnd = addDays(gridEnd, 7);
                               }
                        
                               const dayHeaders = [];
                               const headerReference = startOfWeek(new Date(2026, 0, 5));
                        
                               if (config.showWeekNumbers)
                               {
                                   dayHeaders.push(
                                       '<div class="jscal-week-header">' +
                                       escapeHtml(config.labels.calendarWeekShort) +
                                       "</div>"
                                   );
                               }
                        
                               for (let index = 0; index < 7; index++)
                               {
                                   const headerDate = addDays(headerReference, index);
                        
                                   dayHeaders.push(
                                       '<div class="jscal-week-header">' +
                                       escapeHtml(
                                           formatDate(headerDate, {
                                               weekday: "short"
                                           })
                                       ) +
                                       "</div>"
                                   );
                               }
                        
                               const cells = [];
                               let cursor = new Date(gridStart);
                               let rowIndex = 0;
                        
                               while (cursor.getTime() <= gridEnd.getTime())
                               {
                                   if (config.showWeekNumbers && rowIndex % 7 === 0)
                                   {
                                       cells.push(
                                           '<div class="jscal-week-number">' +
                                           escapeHtml(
                                               config.labels.calendarWeekShort +
                                               " " +
                                               getIsoWeekNumber(cursor)
                                           ) +
                                           "</div>"
                                       );
                                   }
                        
                                   const currentDay = new Date(cursor);
                                   const currentEvents = getEventsForDay(currentDay);
                                   const visibleEvents = currentEvents.slice(0, 4);
                                   const remainingCount = currentEvents.length - visibleEvents.length;
                        
                                   const classes = ["jscal-month-day"];
                        
                                   if (currentDay.getMonth() !== state.currentDate.getMonth())
                                   {
                                       classes.push("is-outside");
                                   }
                        
                                   if (sameDay(currentDay, new Date()))
                                   {
                                       classes.push("is-today");
                                   }
                        
                                   if (config.highlightWeekend && isWeekend(currentDay))
                                   {
                                       classes.push("is-weekend");
                                   }
                        
                                   let eventHtml = visibleEvents
                                       .map(function (event)
                                       {
                                           return renderSmallEvent(event, {
                                               showTime: true
                                           });
                                       })
                                       .join("");
                        
                                   if (remainingCount > 0)
                                   {
                                       eventHtml +=
                                           '<div class="jscal-more">+' +
                                           escapeHtml(remainingCount) +
                                           "</div>";
                                   }
                        
                                   cells.push(
                                       '<div class="' +
                                       classes.join(" ") +
                                       '" data-jscal-date="' +
                                       escapeAttribute(dateKey(currentDay)) +
                                       '">' +
                                       '<div class="jscal-day-number"><span>' +
                                       escapeHtml(currentDay.getDate()) +
                                       "</span></div>" +
                                       '<div class="jscal-events">' +
                                       eventHtml +
                                       "</div>" +
                                       "</div>"
                                   );
                        
                                   cursor = addDays(cursor, 1);
                                   rowIndex++;
                               }
                        
                               const className = config.showWeekNumbers
                                   ? "jscal-month"
                                   : "jscal-month no-week-numbers";
                        
                               titleElement.textContent = formatDate(state.currentDate, {
                                   month: "long",
                                   year: "numeric"
                               });
                        
                               contentElement.innerHTML =
                                   '<div class="' +
                                   className +
                                   '">' +
                                   dayHeaders.join("") +
                                   cells.join("") +
                                   "</div>";
                           }
                        
                           function renderWeek()
                           {
                               const weekStart = startOfWeek(state.currentDate);
                               const weekEnd = addDays(weekStart, 6);
                               const columns = [];
                        
                               for (let index = 0; index < 7; index++)
                               {
                                   const day = addDays(weekStart, index);
                                   const dayEvents = getEventsForDay(day);
                                   const classes = ["jscal-week-day"];
                        
                                   if (sameDay(day, new Date()))
                                   {
                                       classes.push("is-today");
                                   }
                        
                                   if (config.highlightWeekend && isWeekend(day))
                                   {
                                       classes.push("is-weekend");
                                   }
                        
                                   let eventsHtml = "";
                        
                                   if (dayEvents.length === 0)
                                   {
                                       eventsHtml =
                                           '<div class="jscal-empty">' +
                                           escapeHtml(config.labels.noEvents) +
                                           "</div>";
                                   } else
                                   {
                                       eventsHtml = dayEvents
                                           .map(function (event)
                                           {
                                               return renderSmallEvent(event, {
                                                   showTime: true
                                               });
                                           })
                                           .join("");
                                   }
                        
                                   columns.push(
                                       '<section class="' +
                                       classes.join(" ") +
                                       '" data-jscal-date="' +
                                       escapeAttribute(dateKey(day)) +
                                       '">' +
                                       '<div class="jscal-week-day-header">' +
                                       '<div class="jscal-week-day-name">' +
                                       escapeHtml(
                                           formatDate(day, {
                                               weekday: "short"
                                           })
                                       ) +
                                       "</div>" +
                                       '<div class="jscal-week-day-date">' +
                                       escapeHtml(day.getDate()) +
                                       "</div>" +
                                       "</div>" +
                                       '<div class="jscal-week-events">' +
                                       eventsHtml +
                                       "</div>" +
                                       "</section>"
                                   );
                               }
                        
                               const sameMonth =
                                   weekStart.getMonth() === weekEnd.getMonth() &&
                                   weekStart.getFullYear() === weekEnd.getFullYear();
                        
                               if (sameMonth)
                               {
                                   titleElement.textContent =
                                       config.labels.calendarWeekShort +
                                       " " +
                                       getIsoWeekNumber(weekStart) +
                                       " · " +
                                       formatDate(weekStart, {
                                           month: "long",
                                           year: "numeric"
                                       });
                               } else
                               {
                                   titleElement.textContent =
                                       config.labels.calendarWeekShort +
                                       " " +
                                       getIsoWeekNumber(weekStart) +
                                       " · " +
                                       formatDate(weekStart, {
                                           day: "2-digit",
                                           month: "2-digit"
                                       }) +
                                       " – " +
                                       formatDate(weekEnd, {
                                           day: "2-digit",
                                           month: "2-digit",
                                           year: "numeric"
                                       });
                               }
                        
                               contentElement.innerHTML =
                                   '<div class="jscal-week">' +
                                   columns.join("") +
                                   "</div>";
                           }
                        
                           function renderDayEvent(event)
                           {
                               const imageHtml =
                                   config.showImages &&
                                   event.img
                                       ? '<img class="jscal-day-event-image" src="' +
                                           escapeAttribute(event.img) +
                                           '" alt="">'
                                       : "";
                        
                               const locationHtml =
                                   event.ort &&
                                   !(config.hideEmptyLocation && event.ort.length === 0)
                                       ? '<div class="jscal-day-event-location">' +
                                           escapeHtml(event.ort) +
                                           "</div>"
                                       : "";
                        
                               return [
                                   '<article class="jscal-day-event"',
                                   ' style="--jscal-event-color:',
                                   escapeAttribute(event.color),
                                   ';">',
                                   '<div class="jscal-day-event-time">',
                                   escapeHtml(
                                       event.allDay
                                           ? config.labels.allDay
                                           : formatTime(event.start)
                                   ),
                                   "</div>",
                                   '<button type="button"',
                                   ' class="jscal-day-event-content"',
                                   ' data-jscal-event-id="',
                                   escapeAttribute(event.id),
                                   '"',
                                   ' aria-label="',
                                   escapeAttribute(event.ereignis),
                                   '">',
                                   imageHtml,
                                   '<div class="jscal-day-event-text">',
                                   '<div class="jscal-day-event-title">',
                                   escapeHtml(event.ereignis),
                                   "</div>",
                                   locationHtml,
                                   "</div>",
                                   "</button>",
                                   "</article>"
                               ].join("");
                           }
                        
                           function renderDay()
                           {
                               const dayEvents = getEventsForDay(state.currentDate);
                               const allDayEvents = dayEvents.filter(function (event)
                               {
                                   return event.allDay;
                               });
                               const timedEvents = dayEvents.filter(function (event)
                               {
                                   return !event.allDay;
                               });
                        
                               const sections = [];
                        
                               if (allDayEvents.length > 0)
                               {
                                   sections.push(
                                       '<section class="jscal-day-section">' +
                                       '<h3 class="jscal-day-section-title">' +
                                       escapeHtml(config.labels.allDay) +
                                       "</h3>" +
                                       allDayEvents.map(renderDayEvent).join("") +
                                       "</section>"
                                   );
                               }
                        
                               if (timedEvents.length > 0)
                               {
                                   sections.push(
                                       '<section class="jscal-day-section">' +
                                       timedEvents.map(renderDayEvent).join("") +
                                       "</section>"
                                   );
                               }
                        
                               if (sections.length === 0)
                               {
                                   sections.push(
                                       '<div class="jscal-empty">' +
                                       escapeHtml(config.labels.noEvents) +
                                       "</div>"
                                   );
                               }
                        
                               titleElement.textContent = formatDate(state.currentDate, {
                                   weekday: "long",
                                   day: "2-digit",
                                   month: "long",
                                   year: "numeric"
                               });
                        
                               contentElement.innerHTML =
                                   '<div class="jscal-day-view">' +
                                   sections.join("") +
                                   "</div>";
                           }
                        
                           function renderInvalidItems()
                           {
                               if (invalidItems.length === 0)
                               {
                                   return;
                               }
                        
                               const invalidHtml = invalidItems
                                   .map(function (entry)
                                   {
                                       const timeText =
                                           entry.item &&
                                           typeof entry.item.zeit === "string"
                                               ? entry.item.zeit
                                               : "";
                        
                                       return (
                                           '<div class="jscal-invalid">' +
                                           escapeHtml(config.labels.invalidDate) +
                                           ": " +
                                           escapeHtml(timeText) +
                                           "</div>"
                                       );
                                   })
                                   .join("");
                        
                               contentElement.insertAdjacentHTML(
                                   "beforeend",
                                   invalidHtml
                               );
                           }
                        
                           function updateViewButtons()
                           {
                               const buttons = root.querySelectorAll("[data-jscal-view]");
                        
                               buttons.forEach(function (button)
                               {
                                   const isActive =
                                       button.getAttribute("data-jscal-view") === state.view;
                        
                                   button.classList.toggle("is-active", isActive);
                                   button.setAttribute(
                                       "aria-pressed",
                                       isActive ? "true" : "false"
                                   );
                               });
                           }
                        
                           function render()
                           {
                               saveState();
                           	hideTooltip();
                        
                               if (state.view === "week")
                               {
                                   renderWeek();
                               } else if (state.view === "day")
                               {
                                   renderDay();
                               } else
                               {
                                   renderMonth();
                               }
                        
                               renderInvalidItems();
                               updateViewButtons();
                           }
                        
                           function findEventById(eventId)
                           {
                               return events.find(function (event)
                               {
                                   return event.id === String(eventId);
                               }) || null;
                           }
                        
                           function buildTooltipHtml(event)
                           {
                               const imageHtml =
                                   config.showImages &&
                                   event.img
                                       ? '<img class="jscal-tooltip-image" src="' +
                                           escapeAttribute(event.img) +
                                           '" alt="">'
                                       : "";
                        
                               const locationRow =
                                   event.ort &&
                                   !(config.hideEmptyLocation && event.ort.length === 0)
                                       ? '<div class="jscal-tooltip-row">' +
                                           '<div class="jscal-tooltip-label">' +
                                           escapeHtml(config.labels.location) +
                                           "</div>" +
                                           '<div class="jscal-tooltip-value">' +
                                           escapeHtml(event.ort) +
                                           "</div>" +
                                           "</div>"
                                       : "";
                        
                               return (
                                   '<div class="jscal-tooltip-header">' +
                                   imageHtml +
                                   '<div class="jscal-tooltip-title">' +
                                   escapeHtml(event.ereignis) +
                                   "</div>" +
                                   "</div>" +
                                   '<div class="jscal-tooltip-row">' +
                                   '<div class="jscal-tooltip-label">Zeit</div>' +
                                   '<div class="jscal-tooltip-value">' +
                                   escapeHtml(formatEventTime(event)) +
                                   "</div>" +
                                   "</div>" +
                                   locationRow
                               );
                           }
                        
                           function positionTooltip(targetElement)
                           {
                               const targetRect = targetElement.getBoundingClientRect();
                               const tooltipRect = tooltipElement.getBoundingClientRect();
                               const margin = 8;
                        
                               let left = targetRect.left;
                               let top = targetRect.bottom + margin;
                        
                               if (left + tooltipRect.width > window.innerWidth - margin)
                               {
                                   left = window.innerWidth - tooltipRect.width - margin;
                               }
                        
                               if (left < margin)
                               {
                                   left = margin;
                               }
                        
                               if (top + tooltipRect.height > window.innerHeight - margin)
                               {
                                   top = targetRect.top - tooltipRect.height - margin;
                               }
                        
                               if (top < margin)
                               {
                                   top = margin;
                               }
                        
                               tooltipElement.style.left = Math.round(left) + "px";
                               tooltipElement.style.top = Math.round(top) + "px";
                           }
                        
                           function showTooltip(targetElement)
                           {
                               const eventId = targetElement.getAttribute("data-jscal-event-id");
                               const event = findEventById(eventId);
                        
                               if (!event)
                               {
                                   return;
                               }
                        
                               if (
                                   state.selectedEventElement &&
                                   state.selectedEventElement !== targetElement
                               )
                               {
                                   state.selectedEventElement.classList.remove("is-selected");
                               }
                        
                               state.selectedEventElement = targetElement;
                               state.tooltipEventId = event.id;
                        
                               targetElement.classList.add("is-selected");
                               tooltipElement.innerHTML = buildTooltipHtml(event);
                               tooltipElement.classList.add("is-visible");
                               tooltipElement.setAttribute("aria-hidden", "false");
                        
                               positionTooltip(targetElement);
                           }
                        
                           function hideTooltip()
                           {
                               if (state.selectedEventElement)
                               {
                                   state.selectedEventElement.classList.remove("is-selected");
                               }
                        
                               state.selectedEventElement = null;
                               state.tooltipEventId = null;
                        
                               tooltipElement.classList.remove("is-visible");
                               tooltipElement.setAttribute("aria-hidden", "true");
                               tooltipElement.innerHTML = "";
                           }
                        
                           function navigate(direction)
                           {
                               if (state.view === "month")
                               {
                                   state.currentDate = addMonths(
                                       state.currentDate,
                                       direction
                                   );
                               } else if (state.view === "week")
                               {
                                   state.currentDate = addDays(
                                       state.currentDate,
                                       direction * 7
                                   );
                               } else
                               {
                                   state.currentDate = addDays(
                                       state.currentDate,
                                       direction
                                   );
                               }
                        
                               render();
                           }
                        
                           function handleClick(event)
                           {
                               const actionButton = event.target.closest("[data-jscal-action]");
                        
                               if (actionButton && root.contains(actionButton))
                               {
                                   const action = actionButton.getAttribute("data-jscal-action");
                        
                                   if (action === "previous")
                                   {
                                       navigate(-1);
                                   } else if (action === "next")
                                   {
                                       navigate(1);
                                   } else if (action === "today")
                                   {
                                       state.currentDate = startOfDay(new Date());
                                       render();
                                   }
                        
                                   return;
                               }
                        
                               const viewButton = event.target.closest("[data-jscal-view]");
                        
                               if (viewButton && root.contains(viewButton))
                               {
                                   const nextView = viewButton.getAttribute("data-jscal-view");
                        
                                   if (["month", "week", "day"].includes(nextView))
                                   {
                                       state.view = nextView;
                                       render();
                                   }
                        
                                   return;
                               }
                        
                               const eventElement = event.target.closest("[data-jscal-event-id]");
                        
                               if (eventElement && root.contains(eventElement))
                               {
                                   const eventId =
                                       eventElement.getAttribute("data-jscal-event-id");
                        
                                   if (
                                       state.tooltipEventId === eventId &&
                                       tooltipElement.classList.contains("is-visible")
                                   )
                                   {
                                       hideTooltip();
                                   } else
                                   {
                                       showTooltip(eventElement);
                                   }
                        
                                   event.preventDefault();
                                   event.stopPropagation();
                                   return;
                               }
                        
                               const dayElement = event.target.closest("[data-jscal-date]");
                        
                               if (dayElement && root.contains(dayElement))
                               {
                                   const dateText = dayElement.getAttribute("data-jscal-date");
                                   const match = dateText
                                       ? dateText.match(/^(\d{4})-(\d{2})-(\d{2})$/)
                                       : null;
                        
                                   if (match)
                                   {
                                       const selectedDate = createLocalDate(
                                           Number(match[1]),
                                           Number(match[2]),
                                           Number(match[3]),
                                           0,
                                           0
                                       );
                        
                                       if (selectedDate)
                                       {
                                           state.currentDate = selectedDate;
                                           state.view = "day";
                                           render();
                                       }
                                   }
                               }
                           }
                        
                           function handlePointerOver(event)
                           {
                               if (
                                   event.pointerType &&
                                   event.pointerType !== "mouse"
                               )
                               {
                                   return;
                               }
                        
                               const eventElement = event.target.closest("[data-jscal-event-id]");
                        
                               if (!eventElement || !root.contains(eventElement))
                               {
                                   return;
                               }
                        
                               const relatedTarget = event.relatedTarget;
                        
                               if (
                                   relatedTarget &&
                                   eventElement.contains(relatedTarget)
                               )
                               {
                                   return;
                               }
                        
                               showTooltip(eventElement);
                           }
                        
                           function handlePointerOut(event)
                           {
                               if (
                                   event.pointerType &&
                                   event.pointerType !== "mouse"
                               )
                               {
                                   return;
                               }
                        
                               const eventElement = event.target.closest("[data-jscal-event-id]");
                        
                               if (!eventElement || !root.contains(eventElement))
                               {
                                   return;
                               }
                        
                               const relatedTarget = event.relatedTarget;
                        
                               if (
                                   relatedTarget &&
                                   eventElement.contains(relatedTarget)
                               )
                               {
                                   return;
                               }
                        
                               hideTooltip();
                           }
                        
                           function handleDocumentPointerDown(event)
                           {
                               if (!root.contains(event.target))
                               {
                                   hideTooltip();
                               }
                           }
                        
                           function handleResize()
                           {
                               if (
                                   state.selectedEventElement &&
                                   tooltipElement.classList.contains("is-visible")
                               )
                               {
                                   positionTooltip(state.selectedEventElement);
                               }
                           }
                        
                           root.addEventListener("click", handleClick);
                           root.addEventListener("pointerover", handlePointerOver);
                           root.addEventListener("pointerout", handlePointerOut);
                           document.addEventListener("pointerdown", handleDocumentPointerDown);
                           window.addEventListener("resize", handleResize);
                        
                           root.__jsonCalendarCleanup = function ()
                           {
                               root.removeEventListener("click", handleClick);
                               root.removeEventListener("pointerover", handlePointerOver);
                               root.removeEventListener("pointerout", handlePointerOut);
                               document.removeEventListener(
                                   "pointerdown",
                                   handleDocumentPointerDown
                               );
                               window.removeEventListener("resize", handleResize);
                           };
                        
                           render();
                        })();
                        </script>
                        

                        Das widget hat eine Monats,Wochen,Tagessicht. Details über MouseOver, verschiedene Farben für Ereignisse, was im Template konfiguriert werden kann. Hell/Dunkel/Auto (also nach browser-Einstellung) Farben und ein wenig Navigation

                        Monatssicht
                        6ca992eb-2a64-4c41-a76a-de5ad89f44ca-image.jpeg

                        Wochensicht
                        da7a0855-76a0-4597-9385-ead7e19858d2-image.jpeg

                        Tagessicht
                        04901f5e-2f32-4610-b979-ca30f279c9be-image.jpeg

                        Theoretisch werden auch Bilder angezeigt. habe die aber nicht, daher wird das leer angezeigt. Habe das dann auch nicht weiters getestet.

                        Als Testdaten habe ich die hier schon mal geposteten verwendet.

                        Ich habe noch einen kleinen Effekt mit dem jsontemplate entdeckt.
                        Bitte mal testen ob euch auch etwas auffällt.
                        Auch werde ich dem KI-Prompt noch was hinzufügen müssen, da der erste Wurf nicht funktioniert hat, wie es die KI gemacht hat.
                        Sie hat versucht die Daten per serialisierung und einfügen in das javascript für die browser-Ebene zu übergeben. Das hat nicht funktioniert. Daher wird nun eine globale window variable verwendet.

                        Meine Adapter und Widgets
                        TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                        Links im Profil

                        Eduard77E 1 Antwort Letzte Antwort
                        2
                        • Ro75R Ro75

                          Version 1.0.2 - 24.07.2026

                          • Ganztagstermine, über mehrere Tage, werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt.
                          • "Spezialtermine" wie "In Memory" oder auch Feiertage, etc. werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt, inklusive Emoji

                          Script in Post #1 angepasst.

                          Ro75.

                          sigi234S Online
                          sigi234S Online
                          sigi234
                          Forum Testing Most Active
                          schrieb zuletzt editiert von
                          #62

                          @Ro75 sagte:

                          Version 1.0.2 - 24.07.2026

                          • Ganztagstermine, über mehrere Tage, werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt.

                          Ja, aber mehrfach!

                          Screenshot (3024).png

                          • "Spezialtermine" wie "In Memory" oder auch Feiertage, etc. werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt, inklusive Emoji

                          Ja

                          Im Grossen Kalender ist der Termin immer noch nur ein Tag.

                          Screenshot (3025).png

                          Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.
                          Immer Daten sichern!

                          Ro75R 1 Antwort Letzte Antwort
                          0
                          • sigi234S sigi234

                            @Ro75 sagte:

                            Version 1.0.2 - 24.07.2026

                            • Ganztagstermine, über mehrere Tage, werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt.

                            Ja, aber mehrfach!

                            Screenshot (3024).png

                            • "Spezialtermine" wie "In Memory" oder auch Feiertage, etc. werden jetzt auch im kleinen Kalender (m1) korrekt dargestellt, inklusive Emoji

                            Ja

                            Im Grossen Kalender ist der Termin immer noch nur ein Tag.

                            Screenshot (3025).png

                            Ro75R Online
                            Ro75R Online
                            Ro75
                            schrieb zuletzt editiert von
                            #63

                            sigi234 sagte:

                            m Grossen Kalender ist der Termin immer noch nur ein Tag.

                            Das ist auch nicht der Kalender den ich nutze. Der Kalender von Scrounger (materialdesign) stellt es korrekt dar. Eventuell kann @oliverio da was "zaubern". Was den kleinen Kalender betrifft. Für mich völlig korrekt, da dieser für die zeilenweise Darstellung für Termine gedacht ist, von daher kommt für jeden Tag in diesem Zeitraum ein Eintrag.

                            Ro75.

                            SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                            1 Antwort Letzte Antwort
                            0
                            • Ro75R Online
                              Ro75R Online
                              Ro75
                              schrieb zuletzt editiert von
                              #64

                              Vielleicht können sich ALLE Widget-Designer ja an dieser Stelle dem großen Kalender mal annehmen. Bisher gibt es für VIS-2 noch nichts vergleichbares wie der Kalender von Scrounger (materialdesign 0.5.94) unter VIS-1. Das ist für VIS-2 ein echtes Manko und hat mich bisher auch von einer Migration abgehalten.

                              C1 ist das spezielle JSON für den Kalender von Scrounger. Das wäre ein echter Mehrwert für VIS-2 und ein Pluspunkt im Bereich Widget.

                              Ro75.

                              SERVER = Beelink U59 16GB DDR4 RAM 512GB SSD, FB 7490, FritzDect 200+301+440, ConBee II, Zigbee Aqara Sensoren + NOUS A1Z, NOUS A1T, Philips Hue ** ioBroker, REDIS, influxdb2, Grafana, PiHole, Plex-Mediaserver, paperless-ngx (Docker), MariaDB + phpmyadmin *** VIS-Runtime = Intel NUC 8GB RAM 128GB SSD + 24" Touchscreen

                              1 Antwort Letzte Antwort
                              2
                              • skvarelS Online
                                skvarelS Online
                                skvarel
                                Developer
                                schrieb zuletzt editiert von
                                #65

                                @jkvarel ... kannst du was machen?

                                #TeamInventwo
                                Unsere Adapter:
                                Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                                Wer uns mit einem Kaffee unterstützen möchte: PayPal

                                1 Antwort Letzte Antwort
                                0
                                • OliverIOO OliverIO

                                  Ich habe mal was für den großen Kalender probiert, welcher nur auf dem jsonTemplate und dem m1 Datenpunkt basiert

                                  <%
                                  /* ==========================================================================
                                    KONFIGURATION
                                    ========================================================================== */
                                  
                                  const calendarConfig = {
                                     /* Startansicht: "month", "week" oder "day" */
                                     defaultView: "month",
                                  
                                     /* Sprache für Datumsausgaben */
                                     locale: "de-DE",
                                  
                                     /* Erster Wochentag: 1 = Montag, 0 = Sonntag */
                                     firstDayOfWeek: 1,
                                  
                                     /* Wochenende hervorheben */
                                     highlightWeekend: true,
                                  
                                     /* Kalenderwoche anzeigen */
                                     showWeekNumbers: true,
                                  
                                     /* Bilder der Termine anzeigen */
                                     showImages: true,
                                  
                                     /* Leere Orte ausblenden */
                                     hideEmptyLocation: true,
                                  
                                     /*
                                      * Farbmodus:
                                      * "light" = helle Darstellung
                                      * "dark"  = dunkle Darstellung
                                      * "auto"  = Systemeinstellung des Browsers verwenden
                                      */
                                     theme: "auto",
                                  
                                     /*
                                      * Methode zur automatischen Gruppierung und Farbzuordnung.
                                      *
                                      * "img":
                                      * Termine mit derselben Bild-URL erhalten dieselbe Farbe.
                                      *
                                      * "eventPrefix":
                                      * Der Teil des Ereignisnamens vor dem ersten Doppelpunkt wird verwendet.
                                      */
                                     colorGrouping: "img",
                                  
                                     /*
                                      * Optional feste Farben vorgeben.
                                      *
                                      * matchField darf nur "ereignis" oder "img" sein.
                                      * match enthält einen Text, der im entsprechenden JSON-Feld gesucht wird.
                                      *
                                      * Regeln werden von oben nach unten geprüft.
                                      * Nicht passende Termine erhalten automatisch eine Farbe.
                                      */
                                     colorRules: [
                                         {
                                             matchField: "ereignis",
                                             match: "Altpapier",
                                             color: "#d32f2f"
                                         },
                                         {
                                             matchField: "ereignis",
                                             match: "Restmüll",
                                             color: "#546e7a"
                                         },
                                         {
                                             matchField: "ereignis",
                                             match: "Biotonne",
                                             color: "#2e7d32"
                                         },
                                         {
                                             matchField: "ereignis",
                                             match: "gelber Sack",
                                             color: "#f9a825"
                                         },
                                         {
                                             matchField: "ereignis",
                                             match: "Geburtstag",
                                             color: "#8e24aa"
                                         }
                                     ],
                                  
                                     /*
                                      * Automatisch verwendete Farben.
                                      * Die Farben sollten einen ausreichenden Kontrast zu weißer Schrift haben.
                                      */
                                     autoColors: [
                                         "#1565c0",
                                         "#2e7d32",
                                         "#c62828",
                                         "#6a1b9a",
                                         "#00838f",
                                         "#ef6c00",
                                         "#4527a0",
                                         "#ad1457",
                                         "#00695c",
                                         "#283593",
                                         "#558b2f",
                                         "#5d4037"
                                     ],
                                  
                                     /*
                                      * Optionale Oberflächenfarben.
                                      * Ein leerer Wert verwendet die Farbe des jeweiligen Themes.
                                      */
                                     colors: {
                                         light: {
                                             background: "",
                                             surface: "",
                                             surfaceSecondary: "",
                                             text: "",
                                             textSecondary: "",
                                             border: "",
                                             today: "",
                                             weekend: ""
                                         },
                                         dark: {
                                             background: "",
                                             surface: "",
                                             surfaceSecondary: "",
                                             text: "",
                                             textSecondary: "",
                                             border: "",
                                             today: "",
                                             weekend: ""
                                         }
                                     },
                                  
                                     /* Beschriftungen */
                                     labels: {
                                         today: "Heute",
                                         month: "Monat",
                                         week: "Woche",
                                         day: "Tag",
                                         previous: "Zurück",
                                         next: "Weiter",
                                         allDay: "Ganztägig",
                                         noEvents: "Keine Termine",
                                         location: "Ort",
                                         calendarWeekShort: "KW",
                                         invalidDate: "Ungültiges Datum"
                                     }
                                  };
                                  
                                  
                                  /* ==========================================================================
                                    DATEN GLOBAL FÜR DIESES WIDGET BEREITSTELLEN
                                    ========================================================================== */
                                  
                                  const calendarGlobalStoreName = "__visJsontemplateCalendarWidgetStore";
                                  
                                  if (typeof window !== "undefined") {
                                     if (
                                         !window[calendarGlobalStoreName] ||
                                         typeof window[calendarGlobalStoreName] !== "object"
                                     ) {
                                         window[calendarGlobalStoreName] = Object.create(null);
                                     }
                                  
                                     /*
                                      * Einen bereits vorhandenen Zustand nicht überschreiben.
                                      * Bei einer Datenaktualisierung wird das Template komplett neu erzeugt,
                                      * die globale window-Variable bleibt jedoch erhalten.
                                      */
                                     const existingWidgetStore =
                                         window[calendarGlobalStoreName][widgetid] &&
                                         typeof window[calendarGlobalStoreName][widgetid] === "object"
                                             ? window[calendarGlobalStoreName][widgetid]
                                             : {};
                                  
                                     window[calendarGlobalStoreName][widgetid] = {
                                         data: data,
                                         config: calendarConfig,
                                  
                                         state:
                                             existingWidgetStore.state &&
                                             typeof existingWidgetStore.state === "object"
                                                 ? existingWidgetStore.state
                                                 : {
                                                     view: calendarConfig.defaultView,
                                                     currentDate: null
                                                 }
                                     };
                                  }
                                  %>
                                  
                                  <style>
                                  #<%- widgetid %>
                                  {
                                     --jscal-background-light: <%- calendarConfig.colors.light.background || "#f4f6f8" %>;
                                     --jscal-surface-light: <%- calendarConfig.colors.light.surface || "#ffffff" %>;
                                     --jscal-surface-secondary-light: <%- calendarConfig.colors.light.surfaceSecondary || "#f7f8fa" %>;
                                     --jscal-text-light: <%- calendarConfig.colors.light.text || "#17212b" %>;
                                     --jscal-text-secondary-light: <%- calendarConfig.colors.light.textSecondary || "#66717d" %>;
                                     --jscal-border-light: <%- calendarConfig.colors.light.border || "#d7dce2" %>;
                                     --jscal-today-light: <%- calendarConfig.colors.light.today || "#e3f2fd" %>;
                                     --jscal-weekend-light: <%- calendarConfig.colors.light.weekend || "#faf4f4" %>;
                                  
                                     --jscal-background-dark: <%- calendarConfig.colors.dark.background || "#12171d" %>;
                                     --jscal-surface-dark: <%- calendarConfig.colors.dark.surface || "#1c232b" %>;
                                     --jscal-surface-secondary-dark: <%- calendarConfig.colors.dark.surfaceSecondary || "#252e38" %>;
                                     --jscal-text-dark: <%- calendarConfig.colors.dark.text || "#f1f4f7" %>;
                                     --jscal-text-secondary-dark: <%- calendarConfig.colors.dark.textSecondary || "#aeb8c2" %>;
                                     --jscal-border-dark: <%- calendarConfig.colors.dark.border || "#3b4652" %>;
                                     --jscal-today-dark: <%- calendarConfig.colors.dark.today || "#173b55" %>;
                                     --jscal-weekend-dark: <%- calendarConfig.colors.dark.weekend || "#271f24" %>;
                                  
                                     --jscal-background: var(--jscal-background-light);
                                     --jscal-surface: var(--jscal-surface-light);
                                     --jscal-surface-secondary: var(--jscal-surface-secondary-light);
                                     --jscal-text: var(--jscal-text-light);
                                     --jscal-text-secondary: var(--jscal-text-secondary-light);
                                     --jscal-border: var(--jscal-border-light);
                                     --jscal-today: var(--jscal-today-light);
                                     --jscal-weekend: var(--jscal-weekend-light);
                                  
                                     box-sizing: border-box;
                                     width: 100%;
                                     height: 100%;
                                     min-height: 320px;
                                     overflow: hidden;
                                     color: var(--jscal-text);
                                     background: var(--jscal-background);
                                     font-family: Arial, Helvetica, sans-serif;
                                  }
                                  
                                  #<%- widgetid %> *,
                                  #<%- widgetid %> *::before,
                                  #<%- widgetid %> *::after
                                  {
                                     box-sizing: border-box;
                                  }
                                  
                                  #<%- widgetid %>[data-jscal-theme="dark"]
                                  {
                                     --jscal-background: var(--jscal-background-dark);
                                     --jscal-surface: var(--jscal-surface-dark);
                                     --jscal-surface-secondary: var(--jscal-surface-secondary-dark);
                                     --jscal-text: var(--jscal-text-dark);
                                     --jscal-text-secondary: var(--jscal-text-secondary-dark);
                                     --jscal-border: var(--jscal-border-dark);
                                     --jscal-today: var(--jscal-today-dark);
                                     --jscal-weekend: var(--jscal-weekend-dark);
                                  }
                                  
                                  @media (prefers-color-scheme: dark)
                                  {
                                     #<%- widgetid %>[data-jscal-theme="auto"]
                                     {
                                         --jscal-background: var(--jscal-background-dark);
                                         --jscal-surface: var(--jscal-surface-dark);
                                         --jscal-surface-secondary: var(--jscal-surface-secondary-dark);
                                         --jscal-text: var(--jscal-text-dark);
                                         --jscal-text-secondary: var(--jscal-text-secondary-dark);
                                         --jscal-border: var(--jscal-border-dark);
                                         --jscal-today: var(--jscal-today-dark);
                                         --jscal-weekend: var(--jscal-weekend-dark);
                                     }
                                  }
                                  
                                  #<%- widgetid %> .jscal
                                  {
                                     display: flex;
                                     flex-direction: column;
                                     width: 100%;
                                     height: 100%;
                                     min-height: 320px;
                                     background: var(--jscal-background);
                                  }
                                  
                                  #<%- widgetid %> .jscal-toolbar
                                  {
                                     display: flex;
                                     flex: 0 0 auto;
                                     align-items: center;
                                     gap: 8px;
                                     min-height: 58px;
                                     padding: 8px 10px;
                                     border-bottom: 1px solid var(--jscal-border);
                                     background: var(--jscal-surface);
                                  }
                                  
                                  #<%- widgetid %> .jscal-navigation,
                                  #<%- widgetid %> .jscal-view-buttons
                                  {
                                     display: flex;
                                     align-items: center;
                                     gap: 4px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-view-buttons
                                  {
                                     margin-left: auto;
                                  }
                                  
                                  #<%- widgetid %> .jscal-title
                                  {
                                     min-width: 0;
                                     margin: 0 8px;
                                     overflow: hidden;
                                     color: var(--jscal-text);
                                     font-size: 18px;
                                     font-weight: 700;
                                     line-height: 1.2;
                                     text-align: center;
                                     text-overflow: ellipsis;
                                     white-space: nowrap;
                                  }
                                  
                                  #<%- widgetid %> .jscal-button
                                  {
                                     display: inline-flex;
                                     align-items: center;
                                     justify-content: center;
                                     min-width: 36px;
                                     height: 36px;
                                     padding: 0 10px;
                                     border: 1px solid var(--jscal-border);
                                     border-radius: 7px;
                                     color: var(--jscal-text);
                                     background: var(--jscal-surface-secondary);
                                     font: inherit;
                                     font-size: 13px;
                                     font-weight: 600;
                                     line-height: 1;
                                     cursor: pointer;
                                     user-select: none;
                                     -webkit-tap-highlight-color: transparent;
                                  }
                                  
                                  #<%- widgetid %> .jscal-button:hover
                                  {
                                     filter: brightness(0.96);
                                  }
                                  
                                  #<%- widgetid %> .jscal-button:focus-visible
                                  {
                                     outline: 2px solid #1976d2;
                                     outline-offset: 2px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-button.is-active
                                  {
                                     border-color: #1976d2;
                                     color: #ffffff;
                                     background: #1976d2;
                                  }
                                  
                                  #<%- widgetid %> .jscal-button-icon
                                  {
                                     width: 36px;
                                     padding: 0;
                                  }
                                  
                                  #<%- widgetid %> .jscal-button svg
                                  {
                                     width: 20px;
                                     height: 20px;
                                     fill: currentColor;
                                  }
                                  
                                  #<%- widgetid %> .jscal-content
                                  {
                                     position: relative;
                                     flex: 1 1 auto;
                                     min-height: 0;
                                     overflow: auto;
                                     overscroll-behavior: contain;
                                     background: var(--jscal-background);
                                  }
                                  
                                  #<%- widgetid %> .jscal-month
                                  {
                                     display: grid;
                                     min-width: 640px;
                                     grid-template-columns: auto repeat(7, minmax(80px, 1fr));
                                     background: var(--jscal-border);
                                     gap: 1px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-month.no-week-numbers
                                  {
                                     grid-template-columns: repeat(7, minmax(80px, 1fr));
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-header
                                  {
                                     position: sticky;
                                     top: 0;
                                     z-index: 4;
                                     display: flex;
                                     align-items: center;
                                     justify-content: center;
                                     min-height: 36px;
                                     padding: 5px;
                                     color: var(--jscal-text-secondary);
                                     background: var(--jscal-surface-secondary);
                                     font-size: 12px;
                                     font-weight: 700;
                                     text-align: center;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-number
                                  {
                                     display: flex;
                                     align-items: center;
                                     justify-content: center;
                                     min-height: 112px;
                                     padding: 4px;
                                     color: var(--jscal-text-secondary);
                                     background: var(--jscal-surface-secondary);
                                     font-size: 11px;
                                     font-weight: 700;
                                  }
                                  
                                  #<%- widgetid %> .jscal-month-day
                                  {
                                     position: relative;
                                     min-height: 112px;
                                     padding: 5px;
                                     overflow: hidden;
                                     background: var(--jscal-surface);
                                  }
                                  
                                  #<%- widgetid %> .jscal-month-day.is-outside
                                  {
                                     opacity: 0.52;
                                  }
                                  
                                  #<%- widgetid %> .jscal-month-day.is-today
                                  {
                                     background: var(--jscal-today);
                                  }
                                  
                                  #<%- widgetid %> .jscal-month-day.is-weekend
                                  {
                                     background: var(--jscal-weekend);
                                  }
                                  
                                  #<%- widgetid %> .jscal-month-day.is-weekend.is-today
                                  {
                                     background: var(--jscal-today);
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-number
                                  {
                                     display: flex;
                                     align-items: center;
                                     justify-content: flex-end;
                                     height: 22px;
                                     margin-bottom: 3px;
                                     color: var(--jscal-text);
                                     font-size: 12px;
                                     font-weight: 700;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-number span
                                  {
                                     display: inline-flex;
                                     align-items: center;
                                     justify-content: center;
                                     min-width: 22px;
                                     height: 22px;
                                     padding: 0 4px;
                                     border-radius: 11px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-month-day.is-today .jscal-day-number span
                                  {
                                     color: #ffffff;
                                     background: #1976d2;
                                  }
                                  
                                  #<%- widgetid %> .jscal-events
                                  {
                                     display: flex;
                                     flex-direction: column;
                                     gap: 3px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-event
                                  {
                                     position: relative;
                                     display: flex;
                                     align-items: center;
                                     gap: 5px;
                                     min-width: 0;
                                     min-height: 24px;
                                     padding: 3px 6px;
                                     border: 0;
                                     border-left: 4px solid var(--jscal-event-color);
                                     border-radius: 4px;
                                     color: #ffffff;
                                     background: var(--jscal-event-color);
                                     box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.07);
                                     font: inherit;
                                     font-size: 11px;
                                     line-height: 1.25;
                                     text-align: left;
                                     cursor: pointer;
                                     -webkit-tap-highlight-color: transparent;
                                  }
                                  
                                  #<%- widgetid %> .jscal-event:hover,
                                  #<%- widgetid %> .jscal-event:focus-visible,
                                  #<%- widgetid %> .jscal-event.is-selected
                                  {
                                     filter: brightness(1.12);
                                     outline: none;
                                  }
                                  
                                  #<%- widgetid %> .jscal-event-image
                                  {
                                     flex: 0 0 auto;
                                     width: 17px;
                                     height: 17px;
                                     border-radius: 3px;
                                     object-fit: contain;
                                     background: rgba(255, 255, 255, 0.82);
                                  }
                                  
                                  #<%- widgetid %> .jscal-event-time
                                  {
                                     flex: 0 0 auto;
                                     font-weight: 700;
                                     white-space: nowrap;
                                  }
                                  
                                  #<%- widgetid %> .jscal-event-title
                                  {
                                     min-width: 0;
                                     overflow: hidden;
                                     text-overflow: ellipsis;
                                     white-space: nowrap;
                                  }
                                  
                                  #<%- widgetid %> .jscal-more
                                  {
                                     padding: 2px 5px;
                                     color: var(--jscal-text-secondary);
                                     font-size: 11px;
                                     font-weight: 700;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week
                                  {
                                     display: grid;
                                     min-width: 720px;
                                     min-height: 100%;
                                     grid-template-columns: repeat(7, minmax(100px, 1fr));
                                     gap: 1px;
                                     background: var(--jscal-border);
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day
                                  {
                                     display: flex;
                                     flex-direction: column;
                                  
                                     min-height: 100%;
                                     overflow: hidden;
                                  
                                     background: var(--jscal-surface);
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day.is-weekend
                                  {
                                     background: var(--jscal-weekend);
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day.is-today
                                  {
                                     background: var(--jscal-today);
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day-header
                                  {
                                     position: sticky;
                                     top: 0;
                                     z-index: 3;
                                     display: flex;
                                     flex-direction: column;
                                     align-items: center;
                                     gap: 2px;
                                     min-height: 55px;
                                     padding: 7px 4px;
                                     border-bottom: 1px solid var(--jscal-border);
                                     background: inherit;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day-name
                                  {
                                     color: var(--jscal-text-secondary);
                                     font-size: 11px;
                                     font-weight: 700;
                                     text-transform: uppercase;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day-date
                                  {
                                     display: inline-flex;
                                     align-items: center;
                                     justify-content: center;
                                     min-width: 30px;
                                     height: 30px;
                                     padding: 0 5px;
                                     border-radius: 15px;
                                     color: var(--jscal-text);
                                     font-size: 16px;
                                     font-weight: 700;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-day.is-today .jscal-week-day-date
                                  {
                                     color: #ffffff;
                                     background: #1976d2;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week-events
                                  {
                                     flex: 1;
                                     overflow: hidden;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week .jscal-event
                                  {
                                     display: flex;
                                     align-items: flex-start;
                                     gap: 6px;
                                  
                                     min-height: 36px;
                                     max-height: 54px;
                                  
                                     padding: 6px;
                                  
                                     overflow: hidden;
                                  }
                                  
                                  #<%- widgetid %> .jscal-week .jscal-event-title
                                  {
                                     flex: 1;
                                     min-width: 0;
                                  
                                     overflow: hidden;
                                     text-overflow: ellipsis;
                                  
                                     display: -webkit-box;
                                     -webkit-box-orient: vertical;
                                     -webkit-line-clamp: 2;
                                  
                                     line-height: 1.2;
                                     white-space: normal;
                                  
                                     overflow-wrap: anywhere;
                                     word-break: break-word;
                                  }
                                  
                                  #<%- widgetid %> .jscal-empty
                                  {
                                     padding: 14px 6px;
                                     color: var(--jscal-text-secondary);
                                     font-size: 12px;
                                     text-align: center;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-view
                                  {
                                     min-height: 100%;
                                     padding: 12px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-section
                                  {
                                     margin-bottom: 16px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-section-title
                                  {
                                     margin: 0 0 7px;
                                     padding: 0 2px 6px;
                                     border-bottom: 1px solid var(--jscal-border);
                                     color: var(--jscal-text-secondary);
                                     font-size: 12px;
                                     font-weight: 700;
                                     text-transform: uppercase;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event
                                  {
                                     display: grid;
                                     grid-template-columns: 70px minmax(0, 1fr);
                                     margin-bottom: 8px;
                                     border: 1px solid var(--jscal-border);
                                     border-left: 5px solid var(--jscal-event-color);
                                     border-radius: 7px;
                                     overflow: hidden;
                                     background: var(--jscal-surface);
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-time
                                  {
                                     display: flex;
                                     align-items: flex-start;
                                     justify-content: center;
                                     padding: 11px 6px;
                                     color: var(--jscal-text-secondary);
                                     background: var(--jscal-surface-secondary);
                                     font-size: 12px;
                                     font-weight: 700;
                                     text-align: center;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-content
                                  {
                                     display: flex;
                                     align-items: flex-start;
                                     gap: 9px;
                                     min-width: 0;
                                     padding: 9px;
                                     border: 0;
                                     color: var(--jscal-text);
                                     background: transparent;
                                     font: inherit;
                                     text-align: left;
                                     cursor: pointer;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-content:hover,
                                  #<%- widgetid %> .jscal-day-event-content:focus-visible
                                  {
                                     background: var(--jscal-surface-secondary);
                                     outline: none;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-image
                                  {
                                     flex: 0 0 auto;
                                     width: 34px;
                                     height: 34px;
                                     border-radius: 5px;
                                     object-fit: contain;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-text
                                  {
                                     min-width: 0;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-title
                                  {
                                     color: var(--jscal-text);
                                     font-size: 14px;
                                     font-weight: 700;
                                     line-height: 1.35;
                                  }
                                  
                                  #<%- widgetid %> .jscal-day-event-location
                                  {
                                     margin-top: 4px;
                                     color: var(--jscal-text-secondary);
                                     font-size: 12px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip
                                  {
                                     position: fixed;
                                     z-index: 2147483000;
                                     display: none;
                                     width: min(320px, calc(100vw - 20px));
                                     max-height: min(360px, calc(100vh - 20px));
                                     padding: 12px;
                                     overflow: auto;
                                     border: 1px solid var(--jscal-border);
                                     border-radius: 9px;
                                     color: var(--jscal-text);
                                     background: var(--jscal-surface);
                                     box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
                                     pointer-events: none;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip.is-visible
                                  {
                                     display: block;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip-header
                                  {
                                     display: flex;
                                     align-items: flex-start;
                                     gap: 9px;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip-image
                                  {
                                     flex: 0 0 auto;
                                     width: 42px;
                                     height: 42px;
                                     border-radius: 6px;
                                     object-fit: contain;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip-title
                                  {
                                     min-width: 0;
                                     color: var(--jscal-text);
                                     font-size: 14px;
                                     font-weight: 700;
                                     line-height: 1.35;
                                     overflow-wrap: anywhere;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip-row
                                  {
                                     display: grid;
                                     grid-template-columns: 62px minmax(0, 1fr);
                                     gap: 8px;
                                     margin-top: 10px;
                                     color: var(--jscal-text);
                                     font-size: 12px;
                                     line-height: 1.4;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip-label
                                  {
                                     color: var(--jscal-text-secondary);
                                     font-weight: 700;
                                  }
                                  
                                  #<%- widgetid %> .jscal-tooltip-value
                                  {
                                     overflow-wrap: anywhere;
                                  }
                                  
                                  #<%- widgetid %> .jscal-invalid
                                  {
                                     margin: 6px;
                                     padding: 7px;
                                     border: 1px solid var(--jscal-border);
                                     border-left: 4px solid #c62828;
                                     border-radius: 5px;
                                     color: var(--jscal-text);
                                     background: var(--jscal-surface);
                                     font-size: 12px;
                                  }
                                  
                                  @media (max-width: 700px)
                                  {
                                     #<%- widgetid %> .jscal-toolbar
                                     {
                                         flex-wrap: wrap;
                                         gap: 6px;
                                     }
                                  
                                     #<%- widgetid %> .jscal-title
                                     {
                                         order: -1;
                                         flex: 1 0 100%;
                                         width: 100%;
                                         margin: 0;
                                         padding: 3px;
                                         font-size: 16px;
                                     }
                                  
                                     #<%- widgetid %> .jscal-view-buttons
                                     {
                                         margin-left: auto;
                                     }
                                  
                                     #<%- widgetid %> .jscal-button
                                     {
                                         min-width: 34px;
                                         height: 34px;
                                         padding: 0 8px;
                                         font-size: 12px;
                                     }
                                  
                                     #<%- widgetid %> .jscal-month-day
                                     {
                                         min-height: 96px;
                                     }
                                  }
                                  </style>
                                  
                                  <div class="jscal" data-jscal-container>
                                     <div class="jscal-toolbar">
                                         <div class="jscal-navigation">
                                             <button
                                                 type="button"
                                                 class="jscal-button jscal-button-icon"
                                                 data-jscal-action="previous"
                                                 aria-label="<%- calendarConfig.labels.previous %>"
                                                 title="<%- calendarConfig.labels.previous %>"
                                             >
                                                 <svg viewBox="0 0 24 24" aria-hidden="true">
                                                     <path d="M15.4 5.4 8.8 12l6.6 6.6-1.4 1.4L6 12l8-8z"></path>
                                                 </svg>
                                             </button>
                                  
                                             <button
                                                 type="button"
                                                 class="jscal-button"
                                                 data-jscal-action="today"
                                             >
                                                 <%- calendarConfig.labels.today %>
                                             </button>
                                  
                                             <button
                                                 type="button"
                                                 class="jscal-button jscal-button-icon"
                                                 data-jscal-action="next"
                                                 aria-label="<%- calendarConfig.labels.next %>"
                                                 title="<%- calendarConfig.labels.next %>"
                                             >
                                                 <svg viewBox="0 0 24 24" aria-hidden="true">
                                                     <path d="M8.6 18.6 15.2 12 8.6 5.4 10 4l8 8-8 8z"></path>
                                                 </svg>
                                             </button>
                                         </div>
                                  
                                         <div class="jscal-title" data-jscal-title></div>
                                  
                                         <div class="jscal-view-buttons">
                                             <button
                                                 type="button"
                                                 class="jscal-button"
                                                 data-jscal-view="month"
                                             >
                                                 <%- calendarConfig.labels.month %>
                                             </button>
                                  
                                             <button
                                                 type="button"
                                                 class="jscal-button"
                                                 data-jscal-view="week"
                                             >
                                                 <%- calendarConfig.labels.week %>
                                             </button>
                                  
                                             <button
                                                 type="button"
                                                 class="jscal-button"
                                                 data-jscal-view="day"
                                             >
                                                 <%- calendarConfig.labels.day %>
                                             </button>
                                         </div>
                                     </div>
                                  
                                     <div class="jscal-content" data-jscal-content></div>
                                  
                                     <div
                                         class="jscal-tooltip"
                                         data-jscal-tooltip
                                         role="tooltip"
                                     ></div>
                                  </div>
                                  
                                  <script>
                                  (function ()
                                  {
                                     "use strict";
                                     const widgetId = "<%- widgetid %>";
                                     const globalStoreName = "__visJsontemplateCalendarWidgetStore";
                                  
                                     const widgetStore =
                                     	window[globalStoreName] &&
                                     	window[globalStoreName][widgetId]
                                     		? window[globalStoreName][widgetId]
                                     		: null;
                                  
                                     if (!widgetStore)
                                     {
                                     	console.warn(
                                     		"JSON-Template-Kalender: Keine globalen Daten für Widget gefunden:",
                                     		widgetId
                                     	);
                                  
                                     	return;
                                     }
                                  
                                     const rawData = widgetStore.data;
                                     const config = widgetStore.config;
                                  
                                     const root = document.getElementById(widgetId);
                                  
                                     if (!root)
                                     {
                                         return;
                                     }
                                  
                                     if (typeof root.__jsonCalendarCleanup === "function")
                                     {
                                         root.__jsonCalendarCleanup();
                                     }
                                  
                                     root.setAttribute("data-jscal-theme", config.theme || "auto");
                                  
                                     const container = root.querySelector("[data-jscal-container]");
                                     const contentElement = root.querySelector("[data-jscal-content]");
                                     const titleElement = root.querySelector("[data-jscal-title]");
                                     const tooltipElement = root.querySelector("[data-jscal-tooltip]");
                                  
                                     if (!container || !contentElement || !titleElement || !tooltipElement)
                                     {
                                         return;
                                     }
                                  
                                     const storedState =
                                     widgetStore.state &&
                                     typeof widgetStore.state === "object"
                                         ? widgetStore.state
                                         : {};
                                  
                                     const storedView = ["month", "week", "day"].includes(storedState.view)
                                     	? storedState.view
                                     	: null;
                                  
                                     const storedCurrentDate =
                                     	typeof storedState.currentDate === "string"
                                     		? parseStoredDate(storedState.currentDate)
                                     		: null;
                                  
                                     const state = {
                                     	view:
                                     		storedView ||
                                     		(
                                     			["month", "week", "day"].includes(config.defaultView)
                                     				? config.defaultView
                                     				: "month"
                                     		),
                                  
                                     	currentDate:
                                     		storedCurrentDate ||
                                     		startOfDay(new Date()),
                                  
                                     	selectedEventElement: null,
                                     	tooltipEventId: null
                                     };
                                  
                                     const formatterCache = {};
                                     const groupColorMap = new Map();
                                  
                                     const events = Array.isArray(rawData)
                                         ? rawData
                                             .map(function (item, index)
                                             {
                                                 return normalizeEvent(item, index);
                                             })
                                             .filter(function (item)
                                             {
                                                 return item !== null;
                                             })
                                         : [];
                                  
                                     const invalidItems = Array.isArray(rawData)
                                         ? rawData
                                             .map(function (item, index)
                                             {
                                                 return {
                                                     item: item,
                                                     index: index,
                                                     parsed: normalizeEvent(item, index)
                                                 };
                                             })
                                             .filter(function (entry)
                                             {
                                                 return entry.parsed === null;
                                             })
                                         : [];
                                  
                                     function pad(value)
                                     {
                                         return String(value).padStart(2, "0");
                                     }
                                  
                                     function escapeHtml(value)
                                     {
                                         return String(value === undefined || value === null ? "" : value)
                                             .replace(/&/g, "&amp;")
                                             .replace(/</g, "&lt;")
                                             .replace(/>/g, "&gt;")
                                             .replace(/"/g, "&quot;")
                                             .replace(/'/g, "&#039;");
                                     }
                                  
                                     function escapeAttribute(value)
                                     {
                                         return escapeHtml(value);
                                     }
                                  
                                     function createLocalDate(year, month, day, hour, minute)
                                     {
                                         const date = new Date(
                                             year,
                                             month - 1,
                                             day,
                                             hour || 0,
                                             minute || 0,
                                             0,
                                             0
                                         );
                                  
                                         if (
                                             date.getFullYear() !== year ||
                                             date.getMonth() !== month - 1 ||
                                             date.getDate() !== day
                                         )
                                         {
                                             return null;
                                         }
                                  
                                         return date;
                                     }
                                  
                                     function parseStoredDate(value)
                                     {
                                     	if (typeof value !== "string")
                                     	{
                                     		return null;
                                     	}
                                  
                                     	const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
                                  
                                     	if (!match)
                                     	{
                                     		return null;
                                     	}
                                  
                                     	return createLocalDate(
                                     		Number(match[1]),
                                     		Number(match[2]),
                                     		Number(match[3]),
                                     		0,
                                     		0
                                     	);
                                     }
                                  
                                  
                                     function parseDateTimeText(value)
                                     {
                                         if (typeof value !== "string")
                                         {
                                             return null;
                                         }
                                  
                                         const normalized = value
                                             .trim()
                                             .replace(/\s+/g, " ");
                                  
                                         const separatorMatch = normalized.match(/\s+-\s+/);
                                  
                                         if (!separatorMatch)
                                         {
                                             return null;
                                         }
                                  
                                         const separatorIndex = separatorMatch.index;
                                         const separatorLength = separatorMatch[0].length;
                                         const startText = normalized.slice(0, separatorIndex).trim();
                                         const endText = normalized
                                             .slice(separatorIndex + separatorLength)
                                             .trim();
                                  
                                         const fullDatePattern = /^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/;
                                         const timeOnlyPattern = /^(\d{1,2}):(\d{2})$/;
                                  
                                         const startMatch = startText.match(fullDatePattern);
                                  
                                         if (!startMatch)
                                         {
                                             return null;
                                         }
                                  
                                         const startDay = Number(startMatch[1]);
                                         const startMonth = Number(startMatch[2]);
                                         const startYear = Number(startMatch[3]);
                                         const startHasTime = startMatch[4] !== undefined;
                                         const startHour = startHasTime ? Number(startMatch[4]) : 0;
                                         const startMinute = startHasTime ? Number(startMatch[5]) : 0;
                                  
                                         if (
                                             startHour < 0 ||
                                             startHour > 23 ||
                                             startMinute < 0 ||
                                             startMinute > 59
                                         )
                                         {
                                             return null;
                                         }
                                  
                                         const start = createLocalDate(
                                             startYear,
                                             startMonth,
                                             startDay,
                                             startHour,
                                             startMinute
                                         );
                                  
                                         if (!start)
                                         {
                                             return null;
                                         }
                                  
                                         let end = null;
                                         let endHasTime = false;
                                  
                                         const endFullDateMatch = endText.match(fullDatePattern);
                                         const endTimeOnlyMatch = endText.match(timeOnlyPattern);
                                  
                                         if (endFullDateMatch)
                                         {
                                             const endDay = Number(endFullDateMatch[1]);
                                             const endMonth = Number(endFullDateMatch[2]);
                                             const endYear = Number(endFullDateMatch[3]);
                                  
                                             endHasTime = endFullDateMatch[4] !== undefined;
                                  
                                             const endHour = endHasTime ? Number(endFullDateMatch[4]) : 0;
                                             const endMinute = endHasTime ? Number(endFullDateMatch[5]) : 0;
                                  
                                             if (
                                                 endHour < 0 ||
                                                 endHour > 23 ||
                                                 endMinute < 0 ||
                                                 endMinute > 59
                                             )
                                             {
                                                 return null;
                                             }
                                  
                                             end = createLocalDate(
                                                 endYear,
                                                 endMonth,
                                                 endDay,
                                                 endHour,
                                                 endMinute
                                             );
                                         } else if (endTimeOnlyMatch)
                                         {
                                             endHasTime = true;
                                  
                                             const endHour = Number(endTimeOnlyMatch[1]);
                                             const endMinute = Number(endTimeOnlyMatch[2]);
                                  
                                             if (
                                                 endHour < 0 ||
                                                 endHour > 23 ||
                                                 endMinute < 0 ||
                                                 endMinute > 59
                                             )
                                             {
                                                 return null;
                                             }
                                  
                                             end = createLocalDate(
                                                 startYear,
                                                 startMonth,
                                                 startDay,
                                                 endHour,
                                                 endMinute
                                             );
                                         }
                                  
                                         if (!end)
                                         {
                                             return null;
                                         }
                                  
                                         const allDay = !startHasTime && !endHasTime;
                                  
                                         if (allDay)
                                         {
                                             end.setHours(23, 59, 59, 999);
                                         }
                                  
                                         if (end.getTime() < start.getTime())
                                         {
                                             return null;
                                         }
                                  
                                         return {
                                             start: start,
                                             end: end,
                                             allDay: allDay,
                                             sourceText: normalized
                                         };
                                     }
                                  
                                     function normalizeEvent(item, index)
                                     {
                                         if (!item || typeof item !== "object")
                                         {
                                             return null;
                                         }
                                  
                                         const parsedDate = parseDateTimeText(item.zeit);
                                  
                                         if (!parsedDate)
                                         {
                                             return null;
                                         }
                                  
                                         const event = {
                                             id: String(index),
                                             img: typeof item.img === "string"
                                                 ? item.img.trim()
                                                 : "",
                                             zeit: typeof item.zeit === "string"
                                                 ? item.zeit.trim()
                                                 : "",
                                             ereignis: typeof item.ereignis === "string"
                                                 ? item.ereignis.trim()
                                                 : "",
                                             ort: typeof item.ort === "string"
                                                 ? item.ort.trim()
                                                 : "",
                                             start: parsedDate.start,
                                             end: parsedDate.end,
                                             allDay: parsedDate.allDay
                                         };
                                  
                                         event.color = getEventColor(event);
                                  
                                         return event;
                                     }
                                  
                                     function getGroupingKey(event)
                                     {
                                         if (config.colorGrouping === "eventPrefix")
                                         {
                                             const eventText = event.ereignis || "";
                                             const separatorPosition = eventText.indexOf(":");
                                  
                                             if (separatorPosition >= 0)
                                             {
                                                 return eventText.slice(0, separatorPosition).trim().toLowerCase();
                                             }
                                  
                                             return eventText.trim().toLowerCase();
                                         }
                                  
                                         return (event.img || event.ereignis || event.id).toLowerCase();
                                     }
                                  
                                     function getEventColor(event)
                                     {
                                         if (Array.isArray(config.colorRules))
                                         {
                                             for (const rule of config.colorRules)
                                             {
                                                 if (
                                                     !rule ||
                                                     !["ereignis", "img"].includes(rule.matchField) ||
                                                     typeof rule.match !== "string" ||
                                                     typeof rule.color !== "string"
                                                 )
                                                 {
                                                     continue;
                                                 }
                                  
                                                 const fieldValue = String(event[rule.matchField] || "");
                                  
                                                 if (
                                                     fieldValue
                                                         .toLocaleLowerCase(config.locale)
                                                         .includes(rule.match.toLocaleLowerCase(config.locale))
                                                 )
                                                 {
                                                     return rule.color;
                                                 }
                                             }
                                         }
                                  
                                         const groupKey = getGroupingKey(event);
                                  
                                         if (groupColorMap.has(groupKey))
                                         {
                                             return groupColorMap.get(groupKey);
                                         }
                                  
                                         const palette = Array.isArray(config.autoColors) && config.autoColors.length > 0
                                             ? config.autoColors
                                             : ["#1565c0"];
                                  
                                         const color = palette[groupColorMap.size % palette.length];
                                  
                                         groupColorMap.set(groupKey, color);
                                  
                                         return color;
                                     }
                                  
                                     function startOfDay(date)
                                     {
                                         return new Date(
                                             date.getFullYear(),
                                             date.getMonth(),
                                             date.getDate(),
                                             0,
                                             0,
                                             0,
                                             0
                                         );
                                     }
                                  
                                     function endOfDay(date)
                                     {
                                         return new Date(
                                             date.getFullYear(),
                                             date.getMonth(),
                                             date.getDate(),
                                             23,
                                             59,
                                             59,
                                             999
                                         );
                                     }
                                  
                                     function addDays(date, amount)
                                     {
                                         const result = new Date(date);
                                         result.setDate(result.getDate() + amount);
                                         return result;
                                     }
                                  
                                     function addMonths(date, amount)
                                     {
                                         const targetDay = date.getDate();
                                         const result = new Date(
                                             date.getFullYear(),
                                             date.getMonth() + amount,
                                             1
                                         );
                                  
                                         const lastDay = new Date(
                                             result.getFullYear(),
                                             result.getMonth() + 1,
                                             0
                                         ).getDate();
                                  
                                         result.setDate(Math.min(targetDay, lastDay));
                                  
                                         return result;
                                     }
                                  
                                     function startOfWeek(date)
                                     {
                                         const result = startOfDay(date);
                                         const currentWeekday = result.getDay();
                                         const firstDay = Number(config.firstDayOfWeek) === 0 ? 0 : 1;
                                         const difference = (currentWeekday - firstDay + 7) % 7;
                                  
                                         result.setDate(result.getDate() - difference);
                                  
                                         return result;
                                     }
                                  
                                     function sameDay(first, second)
                                     {
                                         return (
                                             first.getFullYear() === second.getFullYear() &&
                                             first.getMonth() === second.getMonth() &&
                                             first.getDate() === second.getDate()
                                         );
                                     }
                                  
                                     function dateKey(date)
                                     {
                                         return [
                                             date.getFullYear(),
                                             pad(date.getMonth() + 1),
                                             pad(date.getDate())
                                         ].join("-");
                                     }
                                  
                                     function saveState()
                                     {
                                     	if (
                                     		!window[globalStoreName] ||
                                     		!window[globalStoreName][widgetId]
                                     	)
                                     	{
                                     		return;
                                     	}
                                  
                                     	window[globalStoreName][widgetId].state = {
                                     		view: state.view,
                                     		currentDate: dateKey(state.currentDate)
                                     	};
                                     }
                                  
                                  
                                     function getFormatter(options)
                                     {
                                         const key = JSON.stringify(options);
                                  
                                         if (!formatterCache[key])
                                         {
                                             formatterCache[key] = new Intl.DateTimeFormat(
                                                 config.locale || "de-DE",
                                                 options
                                             );
                                         }
                                  
                                         return formatterCache[key];
                                     }
                                  
                                     function formatDate(date, options)
                                     {
                                         return getFormatter(options).format(date);
                                     }
                                  
                                     function formatTime(date)
                                     {
                                         return formatDate(date, {
                                             hour: "2-digit",
                                             minute: "2-digit"
                                         });
                                     }
                                  
                                     function formatEventTime(event)
                                     {
                                         if (event.allDay)
                                         {
                                             if (sameDay(event.start, event.end))
                                             {
                                                 return config.labels.allDay;
                                             }
                                  
                                             return [
                                                 formatDate(event.start, {
                                                     day: "2-digit",
                                                     month: "2-digit",
                                                     year: "numeric"
                                                 }),
                                                 "–",
                                                 formatDate(event.end, {
                                                     day: "2-digit",
                                                     month: "2-digit",
                                                     year: "numeric"
                                                 })
                                             ].join(" ");
                                         }
                                  
                                         if (sameDay(event.start, event.end))
                                         {
                                             return formatTime(event.start) + "–" + formatTime(event.end);
                                         }
                                  
                                         return [
                                             formatDate(event.start, {
                                                 day: "2-digit",
                                                 month: "2-digit",
                                                 year: "numeric",
                                                 hour: "2-digit",
                                                 minute: "2-digit"
                                             }),
                                             "–",
                                             formatDate(event.end, {
                                                 day: "2-digit",
                                                 month: "2-digit",
                                                 year: "numeric",
                                                 hour: "2-digit",
                                                 minute: "2-digit"
                                             })
                                         ].join(" ");
                                     }
                                  
                                     function getIsoWeekNumber(date)
                                     {
                                         const target = new Date(
                                             Date.UTC(
                                                 date.getFullYear(),
                                                 date.getMonth(),
                                                 date.getDate()
                                             )
                                         );
                                  
                                         const weekday = target.getUTCDay() || 7;
                                  
                                         target.setUTCDate(target.getUTCDate() + 4 - weekday);
                                  
                                         const yearStart = new Date(
                                             Date.UTC(target.getUTCFullYear(), 0, 1)
                                         );
                                  
                                         return Math.ceil(
                                             (((target - yearStart) / 86400000) + 1) / 7
                                         );
                                     }
                                  
                                     function eventOccursOnDay(event, day)
                                     {
                                         const dayStart = startOfDay(day);
                                         const dayEnd = endOfDay(day);
                                  
                                         return (
                                             event.start.getTime() <= dayEnd.getTime() &&
                                             event.end.getTime() >= dayStart.getTime()
                                         );
                                     }
                                  
                                     function getEventsForDay(day)
                                     {
                                         return events
                                             .filter(function (event)
                                             {
                                                 return eventOccursOnDay(event, day);
                                             })
                                             .sort(function (first, second)
                                             {
                                                 if (first.allDay !== second.allDay)
                                                 {
                                                     return first.allDay ? -1 : 1;
                                                 }
                                  
                                                 const timeDifference =
                                                     first.start.getTime() - second.start.getTime();
                                  
                                                 if (timeDifference !== 0)
                                                 {
                                                     return timeDifference;
                                                 }
                                  
                                                 return first.ereignis.localeCompare(
                                                     second.ereignis,
                                                     config.locale || "de-DE"
                                                 );
                                             });
                                     }
                                  
                                     function isWeekend(date)
                                     {
                                         return date.getDay() === 0 || date.getDay() === 6;
                                     }
                                  
                                     function renderSmallEvent(event, options)
                                     {
                                         const settings = options || {};
                                         const showTime = settings.showTime !== false;
                                         const imageHtml =
                                             config.showImages &&
                                             event.img
                                                 ? '<img class="jscal-event-image" src="' +
                                                     escapeAttribute(event.img) +
                                                     '" alt="">'
                                                 : "";
                                  
                                         let timeHtml = "";
                                  
                                         if (showTime)
                                         {
                                             timeHtml = '<span class="jscal-event-time">' +
                                                 escapeHtml(
                                                     event.allDay
                                                         ? ""
                                                         : formatTime(event.start)
                                                 ) +
                                                 "</span>";
                                         }
                                  
                                         return [
                                             '<button type="button"',
                                             ' class="jscal-event"',
                                             ' data-jscal-event-id="',
                                             escapeAttribute(event.id),
                                             '"',
                                             ' style="--jscal-event-color:',
                                             escapeAttribute(event.color),
                                             ';"',
                                             ' aria-label="',
                                             escapeAttribute(event.ereignis),
                                             '">',
                                             imageHtml,
                                             timeHtml,
                                             '<span class="jscal-event-title">',
                                             escapeHtml(event.ereignis || ""),
                                             "</span>",
                                             "</button>"
                                         ].join("");
                                     }
                                  
                                     function renderMonth()
                                     {
                                         const monthStart = new Date(
                                             state.currentDate.getFullYear(),
                                             state.currentDate.getMonth(),
                                             1
                                         );
                                  
                                         const monthEnd = new Date(
                                             state.currentDate.getFullYear(),
                                             state.currentDate.getMonth() + 1,
                                             0
                                         );
                                  
                                         const gridStart = startOfWeek(monthStart);
                                         let gridEnd = addDays(startOfWeek(monthEnd), 6);
                                  
                                         if (gridEnd.getTime() < monthEnd.getTime())
                                         {
                                             gridEnd = addDays(gridEnd, 7);
                                         }
                                  
                                         const dayHeaders = [];
                                         const headerReference = startOfWeek(new Date(2026, 0, 5));
                                  
                                         if (config.showWeekNumbers)
                                         {
                                             dayHeaders.push(
                                                 '<div class="jscal-week-header">' +
                                                 escapeHtml(config.labels.calendarWeekShort) +
                                                 "</div>"
                                             );
                                         }
                                  
                                         for (let index = 0; index < 7; index++)
                                         {
                                             const headerDate = addDays(headerReference, index);
                                  
                                             dayHeaders.push(
                                                 '<div class="jscal-week-header">' +
                                                 escapeHtml(
                                                     formatDate(headerDate, {
                                                         weekday: "short"
                                                     })
                                                 ) +
                                                 "</div>"
                                             );
                                         }
                                  
                                         const cells = [];
                                         let cursor = new Date(gridStart);
                                         let rowIndex = 0;
                                  
                                         while (cursor.getTime() <= gridEnd.getTime())
                                         {
                                             if (config.showWeekNumbers && rowIndex % 7 === 0)
                                             {
                                                 cells.push(
                                                     '<div class="jscal-week-number">' +
                                                     escapeHtml(
                                                         config.labels.calendarWeekShort +
                                                         " " +
                                                         getIsoWeekNumber(cursor)
                                                     ) +
                                                     "</div>"
                                                 );
                                             }
                                  
                                             const currentDay = new Date(cursor);
                                             const currentEvents = getEventsForDay(currentDay);
                                             const visibleEvents = currentEvents.slice(0, 4);
                                             const remainingCount = currentEvents.length - visibleEvents.length;
                                  
                                             const classes = ["jscal-month-day"];
                                  
                                             if (currentDay.getMonth() !== state.currentDate.getMonth())
                                             {
                                                 classes.push("is-outside");
                                             }
                                  
                                             if (sameDay(currentDay, new Date()))
                                             {
                                                 classes.push("is-today");
                                             }
                                  
                                             if (config.highlightWeekend && isWeekend(currentDay))
                                             {
                                                 classes.push("is-weekend");
                                             }
                                  
                                             let eventHtml = visibleEvents
                                                 .map(function (event)
                                                 {
                                                     return renderSmallEvent(event, {
                                                         showTime: true
                                                     });
                                                 })
                                                 .join("");
                                  
                                             if (remainingCount > 0)
                                             {
                                                 eventHtml +=
                                                     '<div class="jscal-more">+' +
                                                     escapeHtml(remainingCount) +
                                                     "</div>";
                                             }
                                  
                                             cells.push(
                                                 '<div class="' +
                                                 classes.join(" ") +
                                                 '" data-jscal-date="' +
                                                 escapeAttribute(dateKey(currentDay)) +
                                                 '">' +
                                                 '<div class="jscal-day-number"><span>' +
                                                 escapeHtml(currentDay.getDate()) +
                                                 "</span></div>" +
                                                 '<div class="jscal-events">' +
                                                 eventHtml +
                                                 "</div>" +
                                                 "</div>"
                                             );
                                  
                                             cursor = addDays(cursor, 1);
                                             rowIndex++;
                                         }
                                  
                                         const className = config.showWeekNumbers
                                             ? "jscal-month"
                                             : "jscal-month no-week-numbers";
                                  
                                         titleElement.textContent = formatDate(state.currentDate, {
                                             month: "long",
                                             year: "numeric"
                                         });
                                  
                                         contentElement.innerHTML =
                                             '<div class="' +
                                             className +
                                             '">' +
                                             dayHeaders.join("") +
                                             cells.join("") +
                                             "</div>";
                                     }
                                  
                                     function renderWeek()
                                     {
                                         const weekStart = startOfWeek(state.currentDate);
                                         const weekEnd = addDays(weekStart, 6);
                                         const columns = [];
                                  
                                         for (let index = 0; index < 7; index++)
                                         {
                                             const day = addDays(weekStart, index);
                                             const dayEvents = getEventsForDay(day);
                                             const classes = ["jscal-week-day"];
                                  
                                             if (sameDay(day, new Date()))
                                             {
                                                 classes.push("is-today");
                                             }
                                  
                                             if (config.highlightWeekend && isWeekend(day))
                                             {
                                                 classes.push("is-weekend");
                                             }
                                  
                                             let eventsHtml = "";
                                  
                                             if (dayEvents.length === 0)
                                             {
                                                 eventsHtml =
                                                     '<div class="jscal-empty">' +
                                                     escapeHtml(config.labels.noEvents) +
                                                     "</div>";
                                             } else
                                             {
                                                 eventsHtml = dayEvents
                                                     .map(function (event)
                                                     {
                                                         return renderSmallEvent(event, {
                                                             showTime: true
                                                         });
                                                     })
                                                     .join("");
                                             }
                                  
                                             columns.push(
                                                 '<section class="' +
                                                 classes.join(" ") +
                                                 '" data-jscal-date="' +
                                                 escapeAttribute(dateKey(day)) +
                                                 '">' +
                                                 '<div class="jscal-week-day-header">' +
                                                 '<div class="jscal-week-day-name">' +
                                                 escapeHtml(
                                                     formatDate(day, {
                                                         weekday: "short"
                                                     })
                                                 ) +
                                                 "</div>" +
                                                 '<div class="jscal-week-day-date">' +
                                                 escapeHtml(day.getDate()) +
                                                 "</div>" +
                                                 "</div>" +
                                                 '<div class="jscal-week-events">' +
                                                 eventsHtml +
                                                 "</div>" +
                                                 "</section>"
                                             );
                                         }
                                  
                                         const sameMonth =
                                             weekStart.getMonth() === weekEnd.getMonth() &&
                                             weekStart.getFullYear() === weekEnd.getFullYear();
                                  
                                         if (sameMonth)
                                         {
                                             titleElement.textContent =
                                                 config.labels.calendarWeekShort +
                                                 " " +
                                                 getIsoWeekNumber(weekStart) +
                                                 " · " +
                                                 formatDate(weekStart, {
                                                     month: "long",
                                                     year: "numeric"
                                                 });
                                         } else
                                         {
                                             titleElement.textContent =
                                                 config.labels.calendarWeekShort +
                                                 " " +
                                                 getIsoWeekNumber(weekStart) +
                                                 " · " +
                                                 formatDate(weekStart, {
                                                     day: "2-digit",
                                                     month: "2-digit"
                                                 }) +
                                                 " – " +
                                                 formatDate(weekEnd, {
                                                     day: "2-digit",
                                                     month: "2-digit",
                                                     year: "numeric"
                                                 });
                                         }
                                  
                                         contentElement.innerHTML =
                                             '<div class="jscal-week">' +
                                             columns.join("") +
                                             "</div>";
                                     }
                                  
                                     function renderDayEvent(event)
                                     {
                                         const imageHtml =
                                             config.showImages &&
                                             event.img
                                                 ? '<img class="jscal-day-event-image" src="' +
                                                     escapeAttribute(event.img) +
                                                     '" alt="">'
                                                 : "";
                                  
                                         const locationHtml =
                                             event.ort &&
                                             !(config.hideEmptyLocation && event.ort.length === 0)
                                                 ? '<div class="jscal-day-event-location">' +
                                                     escapeHtml(event.ort) +
                                                     "</div>"
                                                 : "";
                                  
                                         return [
                                             '<article class="jscal-day-event"',
                                             ' style="--jscal-event-color:',
                                             escapeAttribute(event.color),
                                             ';">',
                                             '<div class="jscal-day-event-time">',
                                             escapeHtml(
                                                 event.allDay
                                                     ? config.labels.allDay
                                                     : formatTime(event.start)
                                             ),
                                             "</div>",
                                             '<button type="button"',
                                             ' class="jscal-day-event-content"',
                                             ' data-jscal-event-id="',
                                             escapeAttribute(event.id),
                                             '"',
                                             ' aria-label="',
                                             escapeAttribute(event.ereignis),
                                             '">',
                                             imageHtml,
                                             '<div class="jscal-day-event-text">',
                                             '<div class="jscal-day-event-title">',
                                             escapeHtml(event.ereignis),
                                             "</div>",
                                             locationHtml,
                                             "</div>",
                                             "</button>",
                                             "</article>"
                                         ].join("");
                                     }
                                  
                                     function renderDay()
                                     {
                                         const dayEvents = getEventsForDay(state.currentDate);
                                         const allDayEvents = dayEvents.filter(function (event)
                                         {
                                             return event.allDay;
                                         });
                                         const timedEvents = dayEvents.filter(function (event)
                                         {
                                             return !event.allDay;
                                         });
                                  
                                         const sections = [];
                                  
                                         if (allDayEvents.length > 0)
                                         {
                                             sections.push(
                                                 '<section class="jscal-day-section">' +
                                                 '<h3 class="jscal-day-section-title">' +
                                                 escapeHtml(config.labels.allDay) +
                                                 "</h3>" +
                                                 allDayEvents.map(renderDayEvent).join("") +
                                                 "</section>"
                                             );
                                         }
                                  
                                         if (timedEvents.length > 0)
                                         {
                                             sections.push(
                                                 '<section class="jscal-day-section">' +
                                                 timedEvents.map(renderDayEvent).join("") +
                                                 "</section>"
                                             );
                                         }
                                  
                                         if (sections.length === 0)
                                         {
                                             sections.push(
                                                 '<div class="jscal-empty">' +
                                                 escapeHtml(config.labels.noEvents) +
                                                 "</div>"
                                             );
                                         }
                                  
                                         titleElement.textContent = formatDate(state.currentDate, {
                                             weekday: "long",
                                             day: "2-digit",
                                             month: "long",
                                             year: "numeric"
                                         });
                                  
                                         contentElement.innerHTML =
                                             '<div class="jscal-day-view">' +
                                             sections.join("") +
                                             "</div>";
                                     }
                                  
                                     function renderInvalidItems()
                                     {
                                         if (invalidItems.length === 0)
                                         {
                                             return;
                                         }
                                  
                                         const invalidHtml = invalidItems
                                             .map(function (entry)
                                             {
                                                 const timeText =
                                                     entry.item &&
                                                     typeof entry.item.zeit === "string"
                                                         ? entry.item.zeit
                                                         : "";
                                  
                                                 return (
                                                     '<div class="jscal-invalid">' +
                                                     escapeHtml(config.labels.invalidDate) +
                                                     ": " +
                                                     escapeHtml(timeText) +
                                                     "</div>"
                                                 );
                                             })
                                             .join("");
                                  
                                         contentElement.insertAdjacentHTML(
                                             "beforeend",
                                             invalidHtml
                                         );
                                     }
                                  
                                     function updateViewButtons()
                                     {
                                         const buttons = root.querySelectorAll("[data-jscal-view]");
                                  
                                         buttons.forEach(function (button)
                                         {
                                             const isActive =
                                                 button.getAttribute("data-jscal-view") === state.view;
                                  
                                             button.classList.toggle("is-active", isActive);
                                             button.setAttribute(
                                                 "aria-pressed",
                                                 isActive ? "true" : "false"
                                             );
                                         });
                                     }
                                  
                                     function render()
                                     {
                                         saveState();
                                     	hideTooltip();
                                  
                                         if (state.view === "week")
                                         {
                                             renderWeek();
                                         } else if (state.view === "day")
                                         {
                                             renderDay();
                                         } else
                                         {
                                             renderMonth();
                                         }
                                  
                                         renderInvalidItems();
                                         updateViewButtons();
                                     }
                                  
                                     function findEventById(eventId)
                                     {
                                         return events.find(function (event)
                                         {
                                             return event.id === String(eventId);
                                         }) || null;
                                     }
                                  
                                     function buildTooltipHtml(event)
                                     {
                                         const imageHtml =
                                             config.showImages &&
                                             event.img
                                                 ? '<img class="jscal-tooltip-image" src="' +
                                                     escapeAttribute(event.img) +
                                                     '" alt="">'
                                                 : "";
                                  
                                         const locationRow =
                                             event.ort &&
                                             !(config.hideEmptyLocation && event.ort.length === 0)
                                                 ? '<div class="jscal-tooltip-row">' +
                                                     '<div class="jscal-tooltip-label">' +
                                                     escapeHtml(config.labels.location) +
                                                     "</div>" +
                                                     '<div class="jscal-tooltip-value">' +
                                                     escapeHtml(event.ort) +
                                                     "</div>" +
                                                     "</div>"
                                                 : "";
                                  
                                         return (
                                             '<div class="jscal-tooltip-header">' +
                                             imageHtml +
                                             '<div class="jscal-tooltip-title">' +
                                             escapeHtml(event.ereignis) +
                                             "</div>" +
                                             "</div>" +
                                             '<div class="jscal-tooltip-row">' +
                                             '<div class="jscal-tooltip-label">Zeit</div>' +
                                             '<div class="jscal-tooltip-value">' +
                                             escapeHtml(formatEventTime(event)) +
                                             "</div>" +
                                             "</div>" +
                                             locationRow
                                         );
                                     }
                                  
                                     function positionTooltip(targetElement)
                                     {
                                         const targetRect = targetElement.getBoundingClientRect();
                                         const tooltipRect = tooltipElement.getBoundingClientRect();
                                         const margin = 8;
                                  
                                         let left = targetRect.left;
                                         let top = targetRect.bottom + margin;
                                  
                                         if (left + tooltipRect.width > window.innerWidth - margin)
                                         {
                                             left = window.innerWidth - tooltipRect.width - margin;
                                         }
                                  
                                         if (left < margin)
                                         {
                                             left = margin;
                                         }
                                  
                                         if (top + tooltipRect.height > window.innerHeight - margin)
                                         {
                                             top = targetRect.top - tooltipRect.height - margin;
                                         }
                                  
                                         if (top < margin)
                                         {
                                             top = margin;
                                         }
                                  
                                         tooltipElement.style.left = Math.round(left) + "px";
                                         tooltipElement.style.top = Math.round(top) + "px";
                                     }
                                  
                                     function showTooltip(targetElement)
                                     {
                                         const eventId = targetElement.getAttribute("data-jscal-event-id");
                                         const event = findEventById(eventId);
                                  
                                         if (!event)
                                         {
                                             return;
                                         }
                                  
                                         if (
                                             state.selectedEventElement &&
                                             state.selectedEventElement !== targetElement
                                         )
                                         {
                                             state.selectedEventElement.classList.remove("is-selected");
                                         }
                                  
                                         state.selectedEventElement = targetElement;
                                         state.tooltipEventId = event.id;
                                  
                                         targetElement.classList.add("is-selected");
                                         tooltipElement.innerHTML = buildTooltipHtml(event);
                                         tooltipElement.classList.add("is-visible");
                                         tooltipElement.setAttribute("aria-hidden", "false");
                                  
                                         positionTooltip(targetElement);
                                     }
                                  
                                     function hideTooltip()
                                     {
                                         if (state.selectedEventElement)
                                         {
                                             state.selectedEventElement.classList.remove("is-selected");
                                         }
                                  
                                         state.selectedEventElement = null;
                                         state.tooltipEventId = null;
                                  
                                         tooltipElement.classList.remove("is-visible");
                                         tooltipElement.setAttribute("aria-hidden", "true");
                                         tooltipElement.innerHTML = "";
                                     }
                                  
                                     function navigate(direction)
                                     {
                                         if (state.view === "month")
                                         {
                                             state.currentDate = addMonths(
                                                 state.currentDate,
                                                 direction
                                             );
                                         } else if (state.view === "week")
                                         {
                                             state.currentDate = addDays(
                                                 state.currentDate,
                                                 direction * 7
                                             );
                                         } else
                                         {
                                             state.currentDate = addDays(
                                                 state.currentDate,
                                                 direction
                                             );
                                         }
                                  
                                         render();
                                     }
                                  
                                     function handleClick(event)
                                     {
                                         const actionButton = event.target.closest("[data-jscal-action]");
                                  
                                         if (actionButton && root.contains(actionButton))
                                         {
                                             const action = actionButton.getAttribute("data-jscal-action");
                                  
                                             if (action === "previous")
                                             {
                                                 navigate(-1);
                                             } else if (action === "next")
                                             {
                                                 navigate(1);
                                             } else if (action === "today")
                                             {
                                                 state.currentDate = startOfDay(new Date());
                                                 render();
                                             }
                                  
                                             return;
                                         }
                                  
                                         const viewButton = event.target.closest("[data-jscal-view]");
                                  
                                         if (viewButton && root.contains(viewButton))
                                         {
                                             const nextView = viewButton.getAttribute("data-jscal-view");
                                  
                                             if (["month", "week", "day"].includes(nextView))
                                             {
                                                 state.view = nextView;
                                                 render();
                                             }
                                  
                                             return;
                                         }
                                  
                                         const eventElement = event.target.closest("[data-jscal-event-id]");
                                  
                                         if (eventElement && root.contains(eventElement))
                                         {
                                             const eventId =
                                                 eventElement.getAttribute("data-jscal-event-id");
                                  
                                             if (
                                                 state.tooltipEventId === eventId &&
                                                 tooltipElement.classList.contains("is-visible")
                                             )
                                             {
                                                 hideTooltip();
                                             } else
                                             {
                                                 showTooltip(eventElement);
                                             }
                                  
                                             event.preventDefault();
                                             event.stopPropagation();
                                             return;
                                         }
                                  
                                         const dayElement = event.target.closest("[data-jscal-date]");
                                  
                                         if (dayElement && root.contains(dayElement))
                                         {
                                             const dateText = dayElement.getAttribute("data-jscal-date");
                                             const match = dateText
                                                 ? dateText.match(/^(\d{4})-(\d{2})-(\d{2})$/)
                                                 : null;
                                  
                                             if (match)
                                             {
                                                 const selectedDate = createLocalDate(
                                                     Number(match[1]),
                                                     Number(match[2]),
                                                     Number(match[3]),
                                                     0,
                                                     0
                                                 );
                                  
                                                 if (selectedDate)
                                                 {
                                                     state.currentDate = selectedDate;
                                                     state.view = "day";
                                                     render();
                                                 }
                                             }
                                         }
                                     }
                                  
                                     function handlePointerOver(event)
                                     {
                                         if (
                                             event.pointerType &&
                                             event.pointerType !== "mouse"
                                         )
                                         {
                                             return;
                                         }
                                  
                                         const eventElement = event.target.closest("[data-jscal-event-id]");
                                  
                                         if (!eventElement || !root.contains(eventElement))
                                         {
                                             return;
                                         }
                                  
                                         const relatedTarget = event.relatedTarget;
                                  
                                         if (
                                             relatedTarget &&
                                             eventElement.contains(relatedTarget)
                                         )
                                         {
                                             return;
                                         }
                                  
                                         showTooltip(eventElement);
                                     }
                                  
                                     function handlePointerOut(event)
                                     {
                                         if (
                                             event.pointerType &&
                                             event.pointerType !== "mouse"
                                         )
                                         {
                                             return;
                                         }
                                  
                                         const eventElement = event.target.closest("[data-jscal-event-id]");
                                  
                                         if (!eventElement || !root.contains(eventElement))
                                         {
                                             return;
                                         }
                                  
                                         const relatedTarget = event.relatedTarget;
                                  
                                         if (
                                             relatedTarget &&
                                             eventElement.contains(relatedTarget)
                                         )
                                         {
                                             return;
                                         }
                                  
                                         hideTooltip();
                                     }
                                  
                                     function handleDocumentPointerDown(event)
                                     {
                                         if (!root.contains(event.target))
                                         {
                                             hideTooltip();
                                         }
                                     }
                                  
                                     function handleResize()
                                     {
                                         if (
                                             state.selectedEventElement &&
                                             tooltipElement.classList.contains("is-visible")
                                         )
                                         {
                                             positionTooltip(state.selectedEventElement);
                                         }
                                     }
                                  
                                     root.addEventListener("click", handleClick);
                                     root.addEventListener("pointerover", handlePointerOver);
                                     root.addEventListener("pointerout", handlePointerOut);
                                     document.addEventListener("pointerdown", handleDocumentPointerDown);
                                     window.addEventListener("resize", handleResize);
                                  
                                     root.__jsonCalendarCleanup = function ()
                                     {
                                         root.removeEventListener("click", handleClick);
                                         root.removeEventListener("pointerover", handlePointerOver);
                                         root.removeEventListener("pointerout", handlePointerOut);
                                         document.removeEventListener(
                                             "pointerdown",
                                             handleDocumentPointerDown
                                         );
                                         window.removeEventListener("resize", handleResize);
                                     };
                                  
                                     render();
                                  })();
                                  </script>
                                  

                                  Das widget hat eine Monats,Wochen,Tagessicht. Details über MouseOver, verschiedene Farben für Ereignisse, was im Template konfiguriert werden kann. Hell/Dunkel/Auto (also nach browser-Einstellung) Farben und ein wenig Navigation

                                  Monatssicht
                                  6ca992eb-2a64-4c41-a76a-de5ad89f44ca-image.jpeg

                                  Wochensicht
                                  da7a0855-76a0-4597-9385-ead7e19858d2-image.jpeg

                                  Tagessicht
                                  04901f5e-2f32-4610-b979-ca30f279c9be-image.jpeg

                                  Theoretisch werden auch Bilder angezeigt. habe die aber nicht, daher wird das leer angezeigt. Habe das dann auch nicht weiters getestet.

                                  Als Testdaten habe ich die hier schon mal geposteten verwendet.

                                  Ich habe noch einen kleinen Effekt mit dem jsontemplate entdeckt.
                                  Bitte mal testen ob euch auch etwas auffällt.
                                  Auch werde ich dem KI-Prompt noch was hinzufügen müssen, da der erste Wurf nicht funktioniert hat, wie es die KI gemacht hat.
                                  Sie hat versucht die Daten per serialisierung und einfügen in das javascript für die browser-Ebene zu übergeben. Das hat nicht funktioniert. Daher wird nun eine globale window variable verwendet.

                                  Eduard77E Online
                                  Eduard77E Online
                                  Eduard77
                                  schrieb zuletzt editiert von Eduard77
                                  #66

                                  @OliverIO
                                  bei mir zeigt dein Kalender keine Termine.

                                  007096d3-d2a3-459a-ae3a-dec58bd5004a-image.jpeg

                                  0232057e-fa4b-4f76-abb1-33ed645665fa-image.jpeg

                                  Und im ioBroker Protokol kommen Fehler von Web Adapter.

                                  web.0 2026-07-24 21:27:44.157	error	Invalid pattern on subscribe: The pattern "1,2" is not a valid ID pattern
                                  web.0 2026-07-24 21:26:11.809	error	Invalid pattern on subscribe: The pattern "1,2" is not a valid ID pattern
                                  
                                  sigi234S 1 Antwort Letzte Antwort
                                  0
                                  • Eduard77E Eduard77

                                    @OliverIO
                                    bei mir zeigt dein Kalender keine Termine.

                                    007096d3-d2a3-459a-ae3a-dec58bd5004a-image.jpeg

                                    0232057e-fa4b-4f76-abb1-33ed645665fa-image.jpeg

                                    Und im ioBroker Protokol kommen Fehler von Web Adapter.

                                    web.0 2026-07-24 21:27:44.157	error	Invalid pattern on subscribe: The pattern "1,2" is not a valid ID pattern
                                    web.0 2026-07-24 21:26:11.809	error	Invalid pattern on subscribe: The pattern "1,2" is not a valid ID pattern
                                    
                                    sigi234S Online
                                    sigi234S Online
                                    sigi234
                                    Forum Testing Most Active
                                    schrieb zuletzt editiert von sigi234
                                    #67

                                    @Eduard77 sagte:

                                    bei mir zeigt dein Kalender keine Termine.

                                    Skriptversion?

                                    Aha, jetzt bei mir auch. Hatte vorher aber einwandfrei funktioniert.

                                    web.0
                                    2026-07-24 21:54:06.041	error	Invalid pattern on subscribe: The pattern "1,2" is not a valid ID pattern
                                    

                                    Edit:

                                    Jetzt sind sie wieder da. Sehr komisch. Ich denke mal es hat was mit dem Skript auf sich. Da er auch vorher Zeitweise einfach keine Daten bekommt oder zu lange dauert oder was den DP blockiert oder es einfach nicht auslesbar ist? Wie gesagt nach einer gewissen Zeit kommen die Daten aber mal.

                                    Edit2:

                                    Termine werden im Editor angezeigt, in der Runtime nicht!

                                    Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.
                                    Immer Daten sichern!

                                    1 Antwort Letzte Antwort
                                    0

                                    Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.

                                    Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.

                                    Mit deinem Input könnte dieser Beitrag noch besser werden 💗

                                    Registrieren Anmelden
                                    Antworten
                                    • In einem neuen Thema antworten
                                    Anmelden zum Antworten
                                    • Älteste zuerst
                                    • Neuste zuerst
                                    • Meiste Stimmen


                                    Support us

                                    ioBroker
                                    Community Adapters
                                    Donate

                                    358

                                    Online

                                    33.0k

                                    Benutzer

                                    83.5k

                                    Themen

                                    1.3m

                                    Beiträge
                                    Community
                                    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                    ioBroker Community 2014-2026
                                    logo
                                    • Anmelden

                                    • Du hast noch kein Konto? Registrieren

                                    • Anmelden oder registrieren, um zu suchen
                                    • Erster Beitrag
                                      Letzter Beitrag
                                    0
                                    • Home
                                    • Aktuell
                                    • Tags
                                    • Ungelesen 0
                                    • Kategorien
                                    • Unreplied
                                    • Beliebt
                                    • GitHub
                                    • Docu
                                    • Hilfe