PageSourceSearch

https://www.meffis.org/wp-content/plugins/pie-calendar/includes/js/piecal.js?ver=1.3.8

js meffis.org collected 2026-10-05 07:00:02 UTC 10,217 bytes, 263 lines download raw bytes

1const { __ } = wp.i18n;
2
3let piecalJS = (function() {
4    function init( options ) {
5        if( typeof options !== 'object' ) {
6            throw new Error('Options must be an object');
7        }
8
9        if( !options.mode || !getAllowedModes().includes(options.mode) ) {
10            throw new Error('Mode must be one of: ' + getAllowedModes().join(', '));
11        }
12
13        if( !options.container ) {
14            throw new Error('Container must be provided');
15        }
16
17        window.calendarInstances = window.calendarInstances || [];
18
19        let containers = document.querySelectorAll('[data-piecal-instance]');
20        let instance = -1;
21
22        for( let container of containers ) {
23            instance++;
24            container.dataset.piecalInstance = instance;
25
26        let calendar = new FullCalendar.Calendar(container, {
27            headerToolbar: false,
28            initialView: getModeInitialView(options.mode),
29            editable: false,
30            locale: options.locale,
31            contentHeight: 'auto',
32            events: options.events,
33            views: {
34                listMonth: {
35                    duration: {
36                        days: options.lookahead || 30
37                    }
38                }
39            },
40            eventClick: options.eventClick
41        });
42
43        calendar.render();
44
45        window.calendarInstances.push(calendar);
46        }
47    }
48
49    function getAllowedModes() {
50        return ['recurrence-list', 'full'];
51    }
52
53    function getModeInitialView( mode ) {
54        let modeViewMap = {
55            'recurrence-list': 'listMonth',
56            'full': 'dayGridMonth'
57        };
58
59        return modeViewMap[mode];
60    }
61  
62    function validateOptions( options ) {
63        let validOptions = {
64            calendarIndex: 'int',
65            appendOffset: 'boolean',
66            useAdaptiveTimezones: 'boolean'
67        };
68    }
69
70    function eventClick( info, options = {} ) {
71        Alpine.store("calendarEngine").eventTitle = info.event._def.title;
72        Alpine.store("calendarEngine").eventStart = info.event.start;
73        Alpine.store("calendarEngine").eventEnd = info.event.end;
74        Alpine.store("calendarEngine").eventDetails = info.event._def.extendedProps.details;
75        Alpine.store("calendarEngine").eventUrl = info.event._def.extendedProps.permalink;
76        Alpine.store("calendarEngine").eventAllDay = info.event.allDay;
77        Alpine.store("calendarEngine").eventType = info.event._def.extendedProps.postType;
78        Alpine.store('calendarEngine').showPopover = true;
79        Alpine.store('calendarEngine').eventActualEnd = info.event._def.extendedProps.actualEnd;
80        Alpine.store('calendarEngine').appendOffset = options.appendOffset;
81
82        // Always pass through event data via the URL if it's a recurring instance, or if adaptive timezones are enabled.
83        // Do not pass through event data via the URL if it's a non-recurring instance and adaptive timezones are disabled.
84        if( info.event._def.extendedProps.isRecurringInstance || ( !info.event._def.extendedProps.isRecurringInstance && piecalVars.useAdaptiveTimezones && Alpine.store('calendarEngine').appendOffset ) ) {
85            // Construct the URL with parameters
86            const baseUrl    = info.event._def.extendedProps.permalink;
87            const eventStart = new Date( info.event.start );
88            const eventEnd   = new Date( info.event.end );
89            const viewerTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
90
91
92            const url        = new URL( baseUrl );
93            url.searchParams.append( 'eventstart', Math.floor( eventStart.getTime() / 1000 ) );
94            url.searchParams.append( 'eventend', Math.floor( eventEnd.getTime() / 1000 ) );
95            url.searchParams.append( 'timezone', viewerTimezone );
96
97            // Assign the constructed URL to the store
98            Alpine.store("calendarEngine").eventUrl = url.toString();
99        }
100
101        if( info.jsEvent.type == "keydown" ) {
102            setTimeout( () => {
103                document.querySelector('.piecal-popover__inner > button').focus();
104            }, 100);
105        }
106
107        return info;
108    }
109
110    function eventDidMount( info, options = {} ) {
111        let link = info.el;
112
113        const locale = info.view.dateEnv.locale.codeArg;
114
115        const formattedTime = new Intl.DateTimeFormat(locale, {
116            hour: 'numeric',
117            minute: 'numeric',
118            hour12: true
119        });
120
121        const formattedDate = new Intl.DateTimeFormat(locale, {
122            day: 'numeric',
123            month: 'numeric',
124            year: 'numeric'
125        });
126
127        if( link.tagName == 'TR' ) {
128            link = info.el.querySelector('a');
129        }
130
131        if( !link || link.tagName != "A" ) return;
132
133        link.setAttribute('role', 'button');
134        link.setAttribute('href', 'javascript:void(0)');
135
136        if( info.event.allDay ) {
137            const allDayDescriptionText = __('All-day event', 'piecal');
138
139            link.setAttribute('aria-label', `${allDayDescriptionText} - ${info.event.title}`);
140        }
141
142        // Handle multi-day event aria label to let screen readers know the event spans multiple days
143        if( info.event.end && (info.event.end - info.event.start) > (24 * 60 * 60 * 1000) ) {
144            const startDate = formattedDate.format(info.event.start);
145            const startTime = info.event.allDay ? '' : formattedTime.format(info.event.start);
146
147            const endDate = formattedDate.format(info.event.end);
148            const endTime = info.event.allDay ? '' :formattedTime.format(info.event.end);
149
150            /* Translators: Text describing span of multi-day event. */
151            const spanText = __('to', 'piecal');
152
153            /* Translators: Text for multi-day event description. */
154            const multiDayDescriptionText = __('Multi-day event running from', 'piecal'); // <?php _e("'Multi-day event running from'", 'piecal'); ?>;
155
156            /* Translators: Text for multi-day all-day event description. */
157            const multiDayAllDayDescriptionText = __('Multi-day, all-day event running from', 'piecal'); // <?php _e("'Multi-day, all-day event running from'", 'piecal'); ?>;
158
159            const descriptionText = info.event.allDay ? multiDayAllDayDescriptionText : multiDayDescriptionText;
160
161            /* Translators: Text describing span of multi-day event. */
162            link.setAttribute('aria-label', `${descriptionText} ${startDate} ${startTime} ${spanText} ${endDate} ${endTime} - ${info.event.title}`);
163        }
164
165        return info;
166    }
167
168    function dayCellDidMount( info, options = {} ) {
169        let dayLink = info.el.querySelector('.fc-daygrid-day-top a');
170
171        if( !dayLink ) return;
172
173        dayLink.setAttribute('role', 'button');
174        dayLink.setAttribute('href', 'javascript:void(0)');
175
176        // Prevent double read out of button label
177        dayLink.closest('td').removeAttribute('aria-labelledby');
178        
179        setTimeout( () => {
180            if( info.el.querySelector('.fc-daygrid-day-events .fc-daygrid-event-harness') ) {
181                dayLink.setAttribute('aria-label', dayLink.getAttribute('aria-label') + ', has events.');
182            }
183        }, 100);
184
185        dayLink.addEventListener('keydown', (event) => {
186            if( event.key == "Enter" || event.key == ' ' ) {
187                event.preventDefault();
188                window.calendar.gotoDate(info.date);
189                piecalChangeView('listDay');
190
191                setTimeout( () => {
192                    let focusTarget = document.querySelector('.fc-list-day-text');
193                    focusTarget?.setAttribute('tabindex', '0');
194                    focusTarget?.focus();
195                }, 100);
196            }
197        })
198
199        return info;
200    }
201
202    function eventDataTransform( event, options = {} ) {
203         // Safely decode encoded HTML entities for output as titles
204         let scrubber = document.createElement('textarea');
205         scrubber.innerHTML = event.title;
206         event.title = scrubber.value;
207
208         // Extend end date for all day events that span multiple days
209         let { actualEnd, end } = piecalUtils.getAlldayMultidayEventEnd( event ) ?? {};
210
211         if( actualEnd && end ) {    
212             event.actualEnd = actualEnd;
213             event.end = end;
214         }
215
216         return event;
217    }
218
219    function dateClick( info, options = {} ) {
220        if( info.jsEvent.target.tagName != 'A' ) return;
221
222        if( !options.calendarIndex ) {
223            window.calendar.gotoDate(info.dateStr);
224        } else {
225            window.calendarInstances[options.calendarIndex].gotoDate(info.dateStr);
226        }
227
228        piecalChangeView('listDay');
229
230        return info;
231    }
232
233    function dayHeaderContent( info, options = {} ) {
234        let overriddenDayHeaderViews = ['dayGridMonth', 'timeGridWeek', 'dayGridWeek'];
235
236        if( overriddenDayHeaderViews.includes(info.view.type) ) {
237            return '';
238        }
239
240        return info;
241    }
242
243    function dayHeaderDidMount( info, options = {} ) {
244        let dayHeaderLink = info.el.querySelector('a');
245
246        let fullDayName = piecalUtils.getShortenedDayNames(info.text, 'full');
247        let shortDayName = piecalUtils.getShortenedDayNames(info.text, 'short');
248        let singleLetterDayName = piecalUtils.getShortenedDayNames(info.text, 'single');
249
250        let shortenableViews = ['dayGridMonth', 'timeGridWeek', 'dayGridWeek'];
251
252        if( shortenableViews.includes(info.view.type) ) {
253            dayHeaderLink.innerHTML = `<span class="piecal-grid-day-header-text piecal-grid-day-header-text--full">${fullDayName}</span>
254                                       <span class="piecal-grid-day-header-text piecal-grid-day-header-text--short">${shortDayName}</span>
255                                       <span class="piecal-grid-day-header-text piecal-grid-day-header-text--single-letter">${singleLetterDayName}</span>`;
256        }
257
258        return info;
259    }
260
261    // Return methods that should be accessible when invoking piecalJS.
262    return {init, eventDidMount, dayCellDidMount, eventClick, eventDataTransform, dateClick, dayHeaderContent, dayHeaderDidMount};
263})();

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.