PageSourceSearch

https://www.kravis.org/wp-content/themes/kravis/dist/scripts/DatePickerDialog.js?ver=1790292984

js kravis.org collected 2026-09-24 23:41:40 UTC 40,935 bytes, 1,335 lines download raw bytes

1jQuery(function ($) {
2  class DatePickerDialog {
3    constructor(cdp) {
4      this.buttonLabelChoose = "Choose Date";
5      this.buttonLabelChange = "Change Date";
6      this.dayLabels = [
7        "Sunday",
8        "Monday",
9        "Tuesday",
10        "Wednesday",
11        "Thursday",
12        "Friday",
13        "Saturday",
14      ];
15      this.monthLabels = [
16        "January",
17        "February",
18        "March",
19        "April",
20        "May",
21        "June",
22        "July",
23        "August",
24        "September",
25        "October",
26        "November",
27        "December",
28      ];
29
30      this.messageCursorKeys = "Cursor keys can navigate dates";
31      this.lastMessage = "";
32
33      // this.textboxNode = cdp.querySelector('input[type="text"');
34      // this.buttonNode = cdp.querySelector(".group button");
35      this.dialogNode = cdp.querySelector('.datepicker-dialog');
36      // this.messageNode = this.dialogNode.querySelector(".dialog-message");
37
38      this.monthYearNode = this.dialogNode.querySelector(".month-year");
39
40      this.prevYearNode = this.dialogNode.querySelector(".prev-year");
41      this.prevMonthNode = this.dialogNode.querySelector(".prev-month");
42      this.nextMonthNode = this.dialogNode.querySelector(".next-month");
43      this.nextYearNode = this.dialogNode.querySelector(".next-year");
44
45      // this.okButtonNode = this.dialogNode.querySelector('button[value="ok"]');
46      // this.cancelButtonNode = this.dialogNode.querySelector('button[value="cancel"]');
47
48      this.tbodyNode = this.dialogNode.querySelector("table.dates tbody");
49
50      this.lastRowNode = null;
51
52      this.days = [];
53      this.dayButtons = []; // Store references to the button elements
54
55      this.focusDay = new Date();
56      this.selectedDay = new Date(0, 0, 1);
57
58      this.lastDate = -1;
59
60      this.isMouseDownOnBackground = false;
61
62      // this.textboxNode.addEventListener("blur", this.setDateForButtonLabel.bind(this));
63
64      // this.buttonNode.addEventListener("keydown", this.handleButtonKeydown.bind(this));
65      // this.buttonNode.addEventListener("click", this.handleButtonClick.bind(this));
66
67      // this.okButtonNode.addEventListener("click", this.handleOkButton.bind(this));
68      // this.okButtonNode.addEventListener("keydown", this.handleOkButton.bind(this));
69
70      // this.cancelButtonNode.addEventListener("click", this.handleCancelButton.bind(this));
71      // this.cancelButtonNode.addEventListener("keydown", this.handleCancelButton.bind(this));
72
73      this.prevMonthNode.addEventListener("click", this.handlePreviousMonthButton.bind(this));
74      this.nextMonthNode.addEventListener("click", this.handleNextMonthButton.bind(this));
75      this.prevYearNode.addEventListener("click", this.handlePreviousYearButton.bind(this));
76      this.nextYearNode.addEventListener("click", this.handleNextYearButton.bind(this));
77
78      this.prevMonthNode.addEventListener("keydown", this.handlePreviousMonthButton.bind(this));
79      this.nextMonthNode.addEventListener("keydown", this.handleNextMonthButton.bind(this));
80      this.prevYearNode.addEventListener("keydown", this.handlePreviousYearButton.bind(this));
81      this.nextYearNode.addEventListener("keydown", this.handleNextYearButton.bind(this));
82
83      // document.body.addEventListener("pointerup", this.handleBackgroundMouseUp.bind(this), true);
84
85      // Create Grid of Dates
86
87      this.tbodyNode.innerHTML = "";
88      for (let i = 0; i < 6; i++) {
89        const row = this.tbodyNode.insertRow(i);
90        this.lastRowNode = row;
91        for (let j = 0; j < 7; j++) {
92          const cell = document.createElement("td");
93          const button = document.createElement("button");
94
95          // Set button properties instead of cell properties
96          button.tabIndex = -1;
97          button.addEventListener("click", this.handleDayClick.bind(this));
98          button.addEventListener("keydown", this.handleDayKeyDown.bind(this));
99          button.addEventListener("focus", this.handleDayFocus.bind(this));
100
101          button.textContent = "";
102          button.type = "button"; // Ensure it's a button type
103          button.className = "calendar-day-button disabled"; // Add a class for styling
104          button.setAttribute("aria-hidden", "true");
105
106          // Add button to cell
107          cell.appendChild(button);
108          row.appendChild(cell);
109
110          this.days.push(cell);
111          this.dayButtons.push(button); // Store button reference
112        }
113      }
114
115      this.updateGrid();
116      // this.setDateForButtonLabel();
117    }
118
119    isSameDay(day1, day2) {
120      return (
121        day1.getFullYear() == day2.getFullYear() &&
122        day1.getMonth() == day2.getMonth() &&
123        day1.getDate() == day2.getDate()
124      );
125    }
126
127    isNotSameMonth(day1, day2) {
128      return day1.getFullYear() != day2.getFullYear() || day1.getMonth() != day2.getMonth();
129    }
130
131    updateGrid() {
132      const fd = this.focusDay;
133
134      this.monthYearNode.textContent = this.monthLabels[fd.getMonth()] + " " + fd.getFullYear();
135
136      let firstDayOfMonth = new Date(fd.getFullYear(), fd.getMonth(), 1);
137      let dayOfWeek = firstDayOfMonth.getDay();
138
139      firstDayOfMonth.setDate(firstDayOfMonth.getDate() - dayOfWeek);
140
141      const d = new Date(firstDayOfMonth);
142
143      for (let i = 0; i < this.days.length; i++) {
144        const flag = d.getMonth() != fd.getMonth();
145        this.updateDate(this.days[i], flag, d, this.isSameDay(d, this.selectedDay));
146        d.setDate(d.getDate() + 1);
147
148        // Hide last row if all dates are disabled (e.g. in next month)
149        if (i === 35) {
150          if (flag) {
151            this.lastRowNode.style.visibility = "hidden";
152          } else {
153            this.lastRowNode.style.visibility = "visible";
154          }
155        }
156      }
157    }
158
159    updateDate(domNode, disable, day, selected) {
160      let d = day.getDate().toString();
161      if (day.getDate() <= 9) {
162        d = "0" + d;
163      }
164
165      let m = day.getMonth() + 1;
166      if (day.getMonth() < 9) {
167        m = "0" + m;
168      }
169
170      // Get the button inside the td
171      const button = domNode.querySelector("button");
172      if (!button) return;
173
174      // Set properties on the button instead of the td
175      button.tabIndex = -1;
176      button.removeAttribute("aria-selected");
177      button.setAttribute("data-date", day.getFullYear() + "-" + m + "-" + d);
178
179      if (disable) {
180        domNode.classList.add("disabled");
181        domNode.setAttribute("aria-hidden", "true");
182        button.classList.add("disabled");
183        button.disabled = true;
184        button.tabIndex = -1;
185        button.textContent = "";
186        button.removeAttribute("aria-label");
187        button.setAttribute("aria-hidden", "true");
188      } else {
189        domNode.classList.remove("disabled");
190        domNode.removeAttribute("aria-hidden");
191        button.classList.remove("disabled");
192        button.disabled = false;
193        button.removeAttribute("aria-hidden");
194        button.textContent = day.getDate();
195
196        // Name starts with the visible day number (2.5.3) and keeps the full date
197        const dayName = this.dayLabels[day.getDay()];
198        const monthName = this.monthLabels[day.getMonth()];
199        button.setAttribute(
200          "aria-label",
201          `${day.getDate()}, ${dayName}, ${monthName} ${day.getDate()}, ${day.getFullYear()}`
202        );
203
204        if (selected) {
205          button.setAttribute("aria-selected", "true");
206          button.tabIndex = 0;
207        } else if (domNode.classList.contains("highlight")) {
208          // Keep tabindex="0" for highlighted dates (dates with events)
209          button.tabIndex = 0;
210        }
211      }
212    }
213
214    moveFocusToDay(day) {
215      const d = this.focusDay;
216
217      this.focusDay = day;
218
219      if (
220        d.getMonth() != this.focusDay.getMonth() ||
221        d.getFullYear() != this.focusDay.getFullYear()
222      ) {
223        this.updateGrid();
224      }
225      this.setFocusDay();
226    }
227
228    setFocusDay(flag) {
229      if (typeof flag !== "boolean") {
230        flag = true;
231      }
232
233      for (let i = 0; i < this.days.length; i++) {
234        const dayNode = this.days[i];
235        const button = dayNode.querySelector("button");
236        if (!button) continue;
237
238        const day = this.getDayFromDataDateAttribute(button);
239
240        button.tabIndex = -1;
241        if (this.isSameDay(day, this.focusDay)) {
242          button.tabIndex = 0;
243          if (flag) {
244            button.focus();
245          }
246        }
247      }
248    }
249
250    open() {
251      this.dialogNode.style.display = "block";
252      this.dialogNode.style.zIndex = 2;
253
254      // Initialize with current date if no date is set
255      if (!this.focusDay || this.focusDay.getTime() === new Date(0, 0, 1).getTime()) {
256        this.focusDay = new Date();
257      }
258      this.updateGrid();
259      this.lastDate = this.focusDay.getDate();
260    }
261
262    close() {
263      if (this.dialogNode) {
264        // this.dialogNode.style.display = "none";
265      }
266    }
267
268    // Implement the missing setTextboxDate method
269    setTextboxDate(buttonNode) {
270      let d = this.focusDay;
271
272      if (buttonNode) {
273        d = this.getDayFromDataDateAttribute(buttonNode);
274        // Update selected day
275        this.selectedDay = new Date(d);
276
277        // Remove selected class from all buttons and add to current selection
278        this.dayButtons.forEach((button) => {
279          const parentCell = button.parentNode;
280          if (parentCell) {
281            parentCell.classList.remove("selected");
282          }
283          button.removeAttribute("aria-selected");
284
285          if (button === buttonNode) {
286            button.setAttribute("aria-selected", "true");
287            if (parentCell) {
288              parentCell.classList.add("selected");
289            }
290          }
291        });
292      }
293
294      // Trigger custom event for date selection
295      const event = new CustomEvent("dateSelected", {
296        detail: { date: d },
297      });
298      this.dialogNode.dispatchEvent(event);
299    }
300
301    // Implement the missing setMessage method
302    setMessage(str) {}
303
304    changeMonth(currentDate, numMonths) {
305      const getDays = (year, month) => new Date(year, month, 0).getDate();
306
307      const isPrev = numMonths < 0;
308      const numYears = Math.trunc(Math.abs(numMonths) / 12);
309      numMonths = Math.abs(numMonths) % 12;
310
311      const newYear = isPrev
312        ? currentDate.getFullYear() - numYears
313        : currentDate.getFullYear() + numYears;
314
315      const newMonth = isPrev
316        ? currentDate.getMonth() - numMonths
317        : currentDate.getMonth() + numMonths;
318
319      const newDate = new Date(newYear, newMonth, 1);
320
321      const daysInMonth = getDays(newDate.getFullYear(), newDate.getMonth() + 1);
322
323      // If lastDat is not initialized set to current date
324      this.lastDate = this.lastDate ? this.lastDate : currentDate.getDate();
325
326      if (this.lastDate > daysInMonth) {
327        newDate.setDate(daysInMonth);
328      } else {
329        newDate.setDate(this.lastDate);
330      }
331
332      return newDate;
333    }
334
335    moveToNextYear() {
336      this.focusDay = this.changeMonth(this.focusDay, 13);
337      this.updateGrid();
338    }
339
340    moveToPreviousYear() {
341      this.focusDay = this.changeMonth(this.focusDay, -11);
342      this.updateGrid();
343    }
344
345    moveToNextMonth() {
346      this.focusDay = this.changeMonth(this.focusDay, 2);
347      this.updateGrid();
348    }
349
350    moveToPreviousMonth() {
351      this.focusDay = this.changeMonth(this.focusDay, -0);
352      this.updateGrid();
353    }
354
355    moveFocusToNextDay() {
356      const d = new Date(this.focusDay);
357      d.setDate(d.getDate() + 1);
358      this.lastDate = d.getDate();
359      this.moveFocusToDay(d);
360    }
361
362    moveFocusToNextWeek() {
363      const d = new Date(this.focusDay);
364      d.setDate(d.getDate() + 7);
365      this.lastDate = d.getDate();
366      this.moveFocusToDay(d);
367    }
368
369    moveFocusToPreviousDay() {
370      const d = new Date(this.focusDay);
371      d.setDate(d.getDate() - 1);
372      this.lastDate = d.getDate();
373      this.moveFocusToDay(d);
374    }
375
376    moveFocusToPreviousWeek() {
377      const d = new Date(this.focusDay);
378      d.setDate(d.getDate() - 7);
379      this.lastDate = d.getDate();
380      this.moveFocusToDay(d);
381    }
382
383    moveFocusToFirstDayOfWeek() {
384      const d = new Date(this.focusDay);
385      d.setDate(d.getDate() - d.getDay());
386      this.lastDate = d.getDate();
387      this.moveFocusToDay(d);
388    }
389
390    moveFocusToLastDayOfWeek() {
391      const d = new Date(this.focusDay);
392      d.setDate(d.getDate() + (6 - d.getDay()));
393      this.lastDate = d.getDate();
394      this.moveFocusToDay(d);
395    }
396
397    // Day methods
398
399    isDayDisabled(buttonNode) {
400      return buttonNode.disabled || buttonNode.parentNode.classList.contains("disabled");
401    }
402
403    getDayFromDataDateAttribute(buttonNode) {
404      const parts = buttonNode.getAttribute("data-date").split("-");
405      return new Date(parts[0], parseInt(parts[1]) - 1, parts[2]);
406    }
407
408    // Textbox methods
409
410    // setTextboxDate(domNode) {
411    //   let d = this.focusDay;
412
413    //   if (domNode) {
414    //     d = this.getDayFromDataDateAttribute(domNode);
415    //     // updated aria-selected
416    //     this.days.forEach((day) =>
417    //       day === domNode
418    //         ? day.setAttribute("aria-selected", "true")
419    //         : day.removeAttribute("aria-selected")
420    //     );
421    //   }
422
423    //   this.textboxNode.value = d.getMonth() + 1 + "/" + d.getDate() + "/" + d.getFullYear();
424    //   this.setDateForButtonLabel();
425    // }
426
427    // getDateFromTextbox() {
428    //   const parts = this.textboxNode.value.split("/");
429    //   const month = parseInt(parts[0]);
430    //   const day = parseInt(parts[1]);
431    //   let year = parseInt(parts[2]);
432
433    //   if (
434    //     parts.length === 3 &&
435    //     Number.isInteger(month) &&
436    //     Number.isInteger(day) &&
437    //     Number.isInteger(year)
438    //   ) {
439    //     if (year < 100) {
440    //       year = 2000 + year;
441    //     }
442    //     this.focusDay = new Date(year, month - 1, day);
443    //     this.selectedDay = new Date(this.focusDay);
444    //   } else {
445    //     // If not a valid date (MM/DD/YY) initialize with todays date
446    //     this.focusDay = new Date();
447    //     this.selectedDay = new Date(0, 0, 1);
448    //   }
449    // }
450
451    // setDateForButtonLabel() {
452    //   const parts = this.textboxNode.value.split("/");
453
454    //   if (
455    //     parts.length === 3 &&
456    //     Number.isInteger(parseInt(parts[0])) &&
457    //     Number.isInteger(parseInt(parts[1])) &&
458    //     Number.isInteger(parseInt(parts[2]))
459    //   ) {
460    //     const day = new Date(parseInt(parts[2]), parseInt(parts[0]) - 1, parseInt(parts[1]));
461
462    //     let label = this.buttonLabelChange;
463    //     label += ", " + this.dayLabels[day.getDay()];
464    //     label += " " + this.monthLabels[day.getMonth()];
465    //     label += " " + day.getDate();
466    //     label += ", " + day.getFullYear();
467    //     this.buttonNode.setAttribute("aria-label", label);
468    //   } else {
469    //     // If not a valid date, initialize with "Choose Date"
470    //     this.buttonNode.setAttribute("aria-label", this.buttonLabelChoose);
471    //   }
472    // }
473
474    // setMessage(str) {
475    //   function setMessageDelayed() {
476    //     this.messageNode.textContent = str;
477    //   }
478
479    //   if (str !== this.lastMessage) {
480    //     setTimeout(setMessageDelayed.bind(this), 200);
481    //     this.lastMessage = str;
482    //   }
483    // }
484
485    // Event handlers
486
487    // handleOkButton(event) {
488    //   let flag = false;
489
490    //   switch (event.type) {
491    //     case "keydown":
492    //       switch (event.key) {
493    //         case "Tab":
494    //           if (!event.shiftKey) {
495    //             this.prevYearNode.focus();
496    //             flag = true;
497    //           }
498    //           break;
499
500    //         case "Esc":
501    //         case "Escape":
502    //           this.close();
503    //           flag = true;
504    //           break;
505
506    //         default:
507    //           break;
508    //       }
509    //       break;
510
511    //     case "click":
512    //       this.setTextboxDate();
513    //       this.close();
514    //       flag = true;
515    //       break;
516
517    //     default:
518    //       break;
519    //   }
520
521    //   if (flag) {
522    //     event.stopPropagation();
523    //     event.preventDefault();
524    //   }
525    // }
526
527    // handleCancelButton(event) {
528    //   let flag = false;
529
530    //   switch (event.type) {
531    //     case "keydown":
532    //       switch (event.key) {
533    //         case "Esc":
534    //         case "Escape":
535    //           this.close();
536    //           flag = true;
537    //           break;
538
539    //         default:
540    //           break;
541    //       }
542    //       break;
543
544    //     case "click":
545    //       this.close();
546    //       flag = true;
547    //       break;
548
549    //     default:
550    //       break;
551    //   }
552
553    //   if (flag) {
554    //     event.stopPropagation();
555    //     event.preventDefault();
556    //   }
557    // }
558
559    handleNextYearButton(event) {
560      let flag = false;
561
562      switch (event.type) {
563        case "keydown":
564          switch (event.key) {
565            case "Esc":
566            case "Escape":
567              this.close();
568              flag = true;
569              break;
570
571            case "Enter":
572              this.moveToNextYear();
573              this.setFocusDay(false);
574              flag = true;
575              break;
576          }
577
578          break;
579
580        case "click":
581          this.moveToNextYear();
582          this.setFocusDay(false);
583          break;
584
585        default:
586          break;
587      }
588
589      if (flag) {
590        event.stopPropagation();
591        event.preventDefault();
592      }
593    }
594
595    handlePreviousYearButton(event) {
596      let flag = false;
597
598      switch (event.type) {
599        case "keydown":
600          switch (event.key) {
601            case "Enter":
602              this.moveToPreviousYear();
603              this.setFocusDay(false);
604              flag = true;
605              break;
606
607            case "Tab":
608              if (event.shiftKey) {
609                // this.okButtonNode.focus();
610                flag = true;
611              }
612              break;
613
614            case "Esc":
615            case "Escape":
616              this.close();
617              flag = true;
618              break;
619
620            default:
621              break;
622          }
623
624          break;
625
626        case "click":
627          this.moveToPreviousYear();
628          this.setFocusDay(false);
629          break;
630
631        default:
632          break;
633      }
634
635      if (flag) {
636        event.stopPropagation();
637        event.preventDefault();
638      }
639    }
640
641    handleNextMonthButton(event) {
642      let flag = false;
643
644      switch (event.type) {
645        case "keydown":
646          switch (event.key) {
647            case "Esc":
648            case "Escape":
649              this.close();
650              flag = true;
651              break;
652
653            case "Enter":
654              this.moveToNextMonth();
655              this.setFocusDay(false);
656              flag = true;
657              break;
658          }
659
660          break;
661
662        case "click":
663          this.moveToNextMonth();
664          this.setFocusDay(false);
665          break;
666
667        default:
668          break;
669      }
670
671      if (flag) {
672        event.stopPropagation();
673        event.preventDefault();
674      }
675    }
676
677    handlePreviousMonthButton(event) {
678      let flag = false;
679
680      switch (event.type) {
681        case "keydown":
682          switch (event.key) {
683            case "Esc":
684            case "Escape":
685              this.close();
686              flag = true;
687              break;
688
689            case "Enter":
690              this.moveToPreviousMonth();
691              this.setFocusDay(false);
692              flag = true;
693              break;
694          }
695
696          break;
697
698        case "click":
699          this.moveToPreviousMonth();
700          this.setFocusDay(false);
701          flag = true;
702          break;
703
704        default:
705          break;
706      }
707
708      if (flag) {
709        event.stopPropagation();
710        event.preventDefault();
711      }
712    }
713
714    handleDayKeyDown(event) {
715      let flag = false;
716
717      switch (event.key) {
718        case "Esc":
719        case "Escape":
720          this.close();
721          break;
722
723        case " ":
724          this.setTextboxDate(event.currentTarget);
725          flag = true;
726          break;
727
728        case "Enter":
729          this.setTextboxDate(event.currentTarget);
730          this.close();
731          flag = true;
732          break;
733
734        case "Tab":
735          // this.cancelButtonNode.focus();
736          if (event.shiftKey) {
737            this.nextYearNode.focus();
738          }
739          this.setMessage("");
740          flag = true;
741          break;
742
743        case "Right":
744        case "ArrowRight":
745          this.moveFocusToNextDay();
746          flag = true;
747          break;
748
749        case "Left":
750        case "ArrowLeft":
751          this.moveFocusToPreviousDay();
752          flag = true;
753          break;
754
755        case "Down":
756        case "ArrowDown":
757          this.moveFocusToNextWeek();
758          flag = true;
759          break;
760
761        case "Up":
762        case "ArrowUp":
763          this.moveFocusToPreviousWeek();
764          flag = true;
765          break;
766
767        case "PageUp":
768          if (event.shiftKey) {
769            this.moveToPreviousYear();
770          } else {
771            this.moveToPreviousMonth();
772          }
773          this.setFocusDay();
774          flag = true;
775          break;
776
777        case "PageDown":
778          if (event.shiftKey) {
779            this.moveToNextYear();
780          } else {
781            this.moveToNextMonth();
782          }
783          this.setFocusDay();
784          flag = true;
785          break;
786
787        case "Home":
788          this.moveFocusToFirstDayOfWeek();
789          flag = true;
790          break;
791
792        case "End":
793          this.moveFocusToLastDayOfWeek();
794          flag = true;
795          break;
796      }
797
798      if (flag) {
799        event.stopPropagation();
800        event.preventDefault();
801      }
802    }
803
804    handleDayClick(event) {
805      if (!this.isDayDisabled(event.currentTarget) && event.which !== 3) {
806        this.setTextboxDate(event.currentTarget);
807        this.close();
808      }
809
810      event.stopPropagation();
811      event.preventDefault();
812    }
813
814    handleDayFocus() {
815      this.setMessage(this.messageCursorKeys);
816    }
817
818    handleButtonKeydown(event) {
819      if (event.key === "Enter" || event.key === " ") {
820        this.open();
821        this.setFocusDay();
822
823        event.stopPropagation();
824        event.preventDefault();
825      }
826    }
827
828    handleButtonClick(event) {
829      this.setFocusDay();
830
831      event.stopPropagation();
832      event.preventDefault();
833    }
834
835    handleBackgroundMouseUp(event) {
836      if (!this.dialogNode.contains(event.target)) {
837        event.stopPropagation();
838        event.preventDefault();
839      }
840    }
841  }
842
843  const PerformanceCalendar = (function () {
844    const today = dayjs();
845    const state = {
846      datepicker: null,
847      performances: [],
848      selectedDay: today.date(),
849      selectedMonth: today.month(),
850      startOfMonth: today.startOf("month"),
851      prevHighlights: [],
852      hasUserSelectedDate: false,
853    };
854
855    // Adapt getHTMLForDay to accept a Date object
856    function getCurrentPerformanceListForDate(dateObj, performances) {
857      if (!dateObj || !performances) {
858        return [];
859      }
860      var month = dateObj.getMonth();
861      var day = dateObj.getDate();
862
863      var filteredEvents = performances.filter(function (event) {
864        var hasMatchingDate = (event.dates || []).some(function (d) {
865          var dateStr = d.date.split("@")[0];
866          var dObj = new Date(dateStr);
867          var matches = dObj.getMonth() === month && dObj.getDate() === day;
868          return matches;
869        });
870        return hasMatchingDate;
871      });
872
873      return filteredEvents;
874    }
875
876    function getHTMLForDay(dateObj) {
877      var list = getCurrentPerformanceListForDate(dateObj, state.performances);
878
879      // Find the event titled "KRAVIS CENTER BLOCK PARTY" and separate it
880      var kravisEvent = null;
881      var otherEvents = [];
882
883      list.forEach(function (event) {
884        if (event.title.includes("KRAVIS CENTER BLOCK PARTY")) {
885          kravisEvent = event;
886        } else {
887          otherEvents.push(event);
888        }
889      });
890
891      // If the KRAVIS CENTER BLOCK PARTY event exists, put it first
892      if (kravisEvent) {
893        otherEvents.unshift(kravisEvent);
894      }
895
896      // Now, map through the adjusted event list to generate HTML
897      var newHTML;
898      var html = otherEvents.map(function (event) {
899        var html = "";
900
901        // HARDCODED - hotfix
902        if (event.title.includes("KRAVIS CENTER BLOCK PARTY")) {
903          newHTML = ", 1pm - 8pm";
904        } else {
905          newHTML = "";
906        }
907
908        event.thumbnail =
909          event.thumbnail || "/wp-content/themes/kravis/dist/img/event-placeholder.png";
910        html += "<div class='vc_row'><a href='" + event.link + "'>";
911        html += "<img class='vc_col-xs-3' src='" + event.thumbnail + "' alt='" + event.title + "'>";
912        html += "<div class='vc_col-xs-8'>";
913        html += "<h2>" + event.title + "</h2>";
914        html += "<span>" + event.location + newHTML + "</span>";
915        html += "</div>";
916        html += "</a></div>";
917        return html;
918      });
919
920      return html.join("");
921    }
922
923    function setLoading($value) {
924      $("#cal-loading-div").css("display", $value ? "flex" : "none");
925    }
926
927    async function getEvents() {
928      var startOfMonth = state.startOfMonth.toString();
929
930      // Use REST API endpoint (avoids admin-ajax.php rate limiting)
931      var apiUrl = (ajax_object.rest_url || ajax_object.ajax_url.replace('admin-ajax.php', 'wp-json/kravis/v1/'));
932
933      return await $.ajax({
934        url: apiUrl + "performances-by-month",
935        method: "GET",
936        data: {
937          date: startOfMonth,
938        },
939      })
940        .done(function (data) {
941          state.performances = data && R.values(data.data.performances);
942
943          // Update the event list for the currently selected date after loading events
944          updateEventListForCurrentDate();
945
946          // Apply highlights to the calendar after events are loaded
947          updateCalendarWithEvents();
948        })
949        .fail(function (e) {
950          console.error("Failed to load events:", e);
951        });
952    }
953
954    // Helper function to update event list for current selected date
955    function updateEventListForCurrentDate() {
956      var currentDate = new Date();
957      currentDate.setDate(state.selectedDay);
958      currentDate.setMonth(state.selectedMonth);
959
960      var html = getHTMLForDay(currentDate) || "<p>No showtimes available.</p>";
961      $("#event-cal-datepicker-list").html(html);
962    }
963
964    function setHighlights() {
965      // Calculate all dates that should be highlighted
966      var highlightDates = [];
967
968      state.performances.forEach(function (performance) {
969        if (performance.dates && performance.dates.length > 0) {
970          performance.dates.forEach(function (dateInfo) {
971            var dateStr = dateInfo.date.split("@")[0]; // Remove time portion
972            var startDate = new Date(dateStr);
973
974            // Check if this event has an end date (for multi-day events)
975            if (dateInfo.end_date) {
976              var endDateStr = dateInfo.end_date.split("@")[0];
977              var endDate = new Date(endDateStr);
978
979              // Add all dates from start to end (inclusive)
980              var currentDate = new Date(startDate);
981              while (currentDate <= endDate) {
982                highlightDates.push(formatDateForDataAttribute(currentDate));
983                currentDate.setDate(currentDate.getDate() + 1);
984              }
985            } else {
986              // Single day event
987              highlightDates.push(formatDateForDataAttribute(startDate));
988            }
989          });
990        }
991      });
992
993      // Remove duplicates
994      highlightDates = [...new Set(highlightDates)];
995
996      // Apply highlights to the DOM
997      applyHighlightsToCalendar(highlightDates);
998    }
999
1000    // Helper function to format date as YYYY-MM-DD for data-date attribute
1001    function formatDateForDataAttribute(date) {
1002      var year = date.getFullYear();
1003      var month = (date.getMonth() + 1).toString().padStart(2, "0");
1004      var day = date.getDate().toString().padStart(2, "0");
1005      return year + "-" + month + "-" + day;
1006    }
1007
1008    // Function to apply highlight class to calendar cells
1009    function applyHighlightsToCalendar(highlightDates) {
1010      // First, remove all existing highlight classes (but preserve selected class)
1011      $("#event-cal-datepicker td").removeClass("highlight");
1012
1013      // Then add highlight class to cells with matching dates, but only if they're not disabled
1014      highlightDates.forEach(function (dateStr) {
1015        var $button = $("#event-cal-datepicker button[data-date='" + dateStr + "']");
1016        var $cell = $button.parent("td");
1017        // Only highlight if the button exists and is not disabled
1018        if ($button.length && !$button.prop("disabled")) {
1019          $cell.addClass("highlight");
1020          // Set tabindex="0" for highlighted buttons to make them focusable
1021          $button.attr("tabindex", "0");
1022        }
1023      });
1024
1025      // After adding highlight classes, update the grid to apply correct tabindex values
1026      if (state.datepicker) {
1027        state.datepicker.updateGrid();
1028
1029        // Only reapply selected class if we're in the same month as the selected date
1030        // and if there's actually a selected date that's not just the default
1031        if (
1032          state.datepicker.selectedDay &&
1033          state.datepicker.selectedDay.getTime() !== new Date(0, 0, 1).getTime()
1034        ) {
1035          var currentMonth = state.datepicker.focusDay.getMonth();
1036          var currentYear = state.datepicker.focusDay.getFullYear();
1037          var selectedMonth = state.datepicker.selectedDay.getMonth();
1038          var selectedYear = state.datepicker.selectedDay.getFullYear();
1039
1040          // Only apply selected class if we're viewing the same month as the selected date
1041          if (currentMonth === selectedMonth && currentYear === selectedYear) {
1042            var selectedDateStr = formatDateForDataAttribute(state.datepicker.selectedDay);
1043            $("#event-cal-datepicker td").removeClass("selected");
1044            var $selectedButton = $(
1045              "#event-cal-datepicker button[data-date='" + selectedDateStr + "']"
1046            );
1047            $selectedButton.parent("td").addClass("selected");
1048          }
1049        }
1050      }
1051    }
1052
1053    // Helper: update event list for selected date
1054    function updateEventListForDate(dateObj) {
1055      // Adapt getHTMLForDay to accept a Date object
1056      var html = getHTMLForDay(dateObj) || "<p>No showtimes available.</p>";
1057      $("#event-cal-datepicker-list").html(html);
1058    }
1059
1060    async function init() {
1061      var trigger = $("#cal-trigger"),
1062        calendar = $(".event-calendar-widget");
1063      var $widgetOuter = $("#cal-widget-outer");
1064
1065      function setCalendarOpen(isOpen) {
1066        if (isOpen) {
1067          calendar.addClass("open");
1068          trigger.attr("aria-expanded", "true");
1069          trigger.attr("aria-label", "See calendar, close performance calendar");
1070          $widgetOuter.attr("aria-hidden", "false");
1071          $widgetOuter.removeAttr("inert");
1072          $widgetOuter.prop("inert", false);
1073          $(".content.row").attr("aria-hidden", "true");
1074          $(".banner").attr("aria-hidden", "true");
1075        } else {
1076          calendar.removeClass("open");
1077          trigger.attr("aria-expanded", "false");
1078          trigger.attr("aria-label", "See calendar, open performance calendar");
1079          $widgetOuter.attr("aria-hidden", "true");
1080          $widgetOuter.attr("inert", "");
1081          $widgetOuter.prop("inert", true);
1082          $(".content.row").removeAttr("aria-hidden");
1083          $(".banner").removeAttr("aria-hidden");
1084        }
1085      }
1086
1087      // Initialize the DatePickerDialog instance
1088      const datePickerElement = document.getElementById("event-cal-datepicker");
1089      if (datePickerElement) {
1090        state.datepicker = new DatePickerDialog(datePickerElement);
1091      }
1092
1093      setCalendarOpen(false);
1094
1095      // Completely rewrite the trigger click handler
1096      trigger.on("click", function (e) {
1097        e.preventDefault();
1098        e.stopPropagation();
1099
1100        if (calendar.hasClass("open")) {
1101          setCalendarOpen(false);
1102        } else {
1103          setCalendarOpen(true);
1104          $("#event-cal-datepicker").attr("tabindex", "-1").focus();
1105        }
1106      });
1107
1108      // Add keyboard support - close calendar with ESC key
1109      $(document).on("keydown", function (e) {
1110        if (e.key === "Escape" && calendar.hasClass("open")) {
1111          setCalendarOpen(false);
1112          trigger.focus();
1113        }
1114      });
1115
1116      // Add click handler for the close button
1117      $("#cal-trigger .fa-close")
1118        .parent()
1119        .on("click", function (e) {
1120          e.preventDefault();
1121          e.stopPropagation();
1122          setCalendarOpen(false);
1123          trigger.focus();
1124        });
1125
1126      // Trap focus within the calendar widget when it's open
1127      $("#cal-widget-outer").on("keydown", function (e) {
1128        if (e.key === "Tab") {
1129          var focusableElements = $("#cal-widget-outer").find(
1130            'a[href], button:not(:disabled), [tabindex]:not([tabindex="-1"])'
1131          ).filter(function () {
1132            return $(this).is(':visible') && !$(this).closest('[aria-hidden="true"]').length;
1133          });
1134
1135          if (focusableElements.length === 0) return;
1136
1137          var firstFocusableElement = focusableElements[0];
1138          var lastFocusableElement = focusableElements[focusableElements.length - 1];
1139
1140          if (e.shiftKey && document.activeElement === firstFocusableElement) {
1141            e.preventDefault();
1142            lastFocusableElement.focus();
1143          } else if (!e.shiftKey && document.activeElement === lastFocusableElement) {
1144            e.preventDefault();
1145            firstFocusableElement.focus();
1146          }
1147        }
1148      });
1149
1150      // Note: The new DatePickerDialog class doesn't support onSelect and onNavigation methods
1151      // Event handling is done through custom events and direct DOM event listeners below
1152
1153      setLoading(true);
1154      await getEvents();
1155      setTimeout(setInitialDate, 500);
1156      setLoading(false);
1157      // Only set up event handlers if datepicker was successfully created
1158      if (state.datepicker) {
1159        // Override navigation methods to trigger event updates
1160        const originalMoveToNextMonth = state.datepicker.moveToNextMonth;
1161        const originalMoveToPreviousMonth = state.datepicker.moveToPreviousMonth;
1162        const originalMoveToNextYear = state.datepicker.moveToNextYear;
1163        const originalMoveToPreviousYear = state.datepicker.moveToPreviousYear;
1164
1165        state.datepicker.moveToNextMonth = async function () {
1166          const oldMonth = this.focusDay.getMonth();
1167          const oldYear = this.focusDay.getFullYear();
1168
1169          // Call original method
1170          originalMoveToNextMonth.call(this);
1171
1172          // Check if month/year changed and trigger updates
1173          const newMonth = this.focusDay.getMonth();
1174          const newYear = this.focusDay.getFullYear();
1175
1176          if (oldMonth !== newMonth || oldYear !== newYear) {
1177            setLoading(true);
1178
1179            $("#event-cal-datepicker td").removeClass("selected");
1180            state.selectedMonth = newMonth;
1181            state.startOfMonth = dayjs(state.datepicker.focusDay).startOf("month");
1182            await getEvents();
1183            setLoading(false);
1184          }
1185        };
1186
1187        state.datepicker.moveToPreviousMonth = async function () {
1188          const oldMonth = this.focusDay.getMonth();
1189          const oldYear = this.focusDay.getFullYear();
1190
1191          // Call original method
1192          originalMoveToPreviousMonth.call(this);
1193
1194          // Check if month/year changed and trigger updates
1195          const newMonth = this.focusDay.getMonth();
1196          const newYear = this.focusDay.getFullYear();
1197
1198          if (oldMonth !== newMonth || oldYear !== newYear) {
1199            setLoading(true);
1200
1201            $("#event-cal-datepicker td").removeClass("selected");
1202            state.selectedMonth = newMonth;
1203            state.startOfMonth = dayjs(state.datepicker.focusDay).startOf("month");
1204            await getEvents();
1205            setLoading(false);
1206          }
1207        };
1208
1209        state.datepicker.moveToNextYear = async function () {
1210          const oldMonth = this.focusDay.getMonth();
1211          const oldYear = this.focusDay.getFullYear();
1212
1213          // Call original method
1214          originalMoveToNextYear.call(this);
1215
1216          // Check if month/year changed and trigger updates
1217          const newMonth = this.focusDay.getMonth();
1218          const newYear = this.focusDay.getFullYear();
1219
1220          if (oldMonth !== newMonth || oldYear !== newYear) {
1221            setLoading(true);
1222
1223            $("#event-cal-datepicker td").removeClass("selected");
1224            state.selectedMonth = newMonth;
1225            state.startOfMonth = dayjs(state.datepicker.focusDay).startOf("month");
1226            await getEvents();
1227            setLoading(false);
1228          }
1229        };
1230
1231        state.datepicker.moveToPreviousYear = async function () {
1232          const oldMonth = this.focusDay.getMonth();
1233          const oldYear = this.focusDay.getFullYear();
1234
1235          // Call original method
1236          originalMoveToPreviousYear.call(this);
1237
1238          // Check if month/year changed and trigger updates
1239          const newMonth = this.focusDay.getMonth();
1240          const newYear = this.focusDay.getFullYear();
1241
1242          if (oldMonth !== newMonth || oldYear !== newYear) {
1243            setLoading(true);
1244
1245            $("#event-cal-datepicker td").removeClass("selected");
1246            state.selectedMonth = newMonth;
1247            state.startOfMonth = dayjs(state.datepicker.focusDay).startOf("month");
1248            await getEvents();
1249            setLoading(false);
1250          }
1251        };
1252
1253        // Listen for custom date selection events
1254        $(state.datepicker.dialogNode).on("dateSelected", function (e) {
1255          var selectedDate = e.originalEvent.detail.date;
1256
1257          // Mark that user has made a selection
1258          state.hasUserSelectedDate = true;
1259
1260          state.selectedDay = selectedDate.getDate();
1261          state.selectedMonth = selectedDate.getMonth();
1262          updateEventListForDate(selectedDate);
1263        });
1264
1265        // Set up date selection handler for direct clicks
1266        $(document).on("click", "#event-cal-datepicker button:not(:disabled)", function () {
1267          var dateStr = $(this).attr("data-date");
1268          if (dateStr) {
1269            // Mark that user has made a selection
1270            state.hasUserSelectedDate = true;
1271
1272            // Remove selected class from all cells
1273            $("#event-cal-datepicker td").removeClass("selected");
1274
1275            // Add selected class to clicked button's parent cell
1276            $(this).parent("td").addClass("selected");
1277
1278            var dateParts = dateStr.split("-");
1279            var selectedDate = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
1280            state.selectedDay = selectedDate.getDate();
1281            state.selectedMonth = selectedDate.getMonth();
1282            updateEventListForDate(selectedDate);
1283          }
1284        });
1285      }
1286    }
1287
1288    function setInitialDate() {
1289      if (state.performances.length === 0) {
1290        setTimeout(setInitialDate, 500);
1291      } else if (state.datepicker) {
1292        // Set initial focus to today's date
1293        var today = new Date();
1294        state.datepicker.focusDay = today;
1295        state.datepicker.selectedDay = today; // Also set selectedDay
1296        state.datepicker.updateGrid();
1297        state.selectedDay = today.getDate();
1298        state.selectedMonth = today.getMonth();
1299
1300        // Only add selected class to today's date on initial load
1301        // Check if this is the initial load by seeing if no date has been explicitly selected
1302        if (!state.hasUserSelectedDate) {
1303          var todayStr = formatDateForDataAttribute(today);
1304          setTimeout(function () {
1305            $("#event-cal-datepicker td").removeClass("selected");
1306            var $todayButton = $("#event-cal-datepicker button[data-date='" + todayStr + "']");
1307            $todayButton.parent("td").addClass("selected");
1308          }, 100);
1309        }
1310
1311        // Update the event list for today's date
1312        updateEventListForCurrentDate();
1313      }
1314    }
1315
1316    function updateCalendarWithEvents() {
1317      // This function can be used to highlight dates with events
1318      // You can implement highlighting logic here if needed
1319      if (state.datepicker && state.performances.length > 0) {
1320        // Update the calendar display
1321        state.datepicker.updateGrid();
1322
1323        setHighlights();
1324      }
1325    }
1326
1327    return {
1328      init: init,
1329    };
1330  })();
1331
1332  $(document).ready(function () {
1333    PerformanceCalendar.init();
1334  });
1335});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.