PageSourceSearch

https://collegestreetmusichall.com/wp-content/plugins/freemius-premium/frontend/js/calendar.js?ver=6.5.12

js collegestreetmusichall.com collected 2026-09-30 20:35:25 UTC 26,929 bytes, 599 lines download raw bytes

1function wfea_render_calendar() {
2    // Safety check - ensure moment.js is loaded before proceeding.
3    // This handles cases where JS optimizers defer scripts and break load order.
4    if (typeof moment === 'undefined') {
5        console.warn('WFEA: moment.js not loaded yet, retrying in 100ms');
6        setTimeout(wfea_render_calendar, 100);
7        return;
8    }
9
10    // Safety check - ensure FullCalendar is available.
11    if (typeof jQuery.fn.fullCalendar === 'undefined') {
12        console.warn('WFEA: FullCalendar not loaded yet, retrying in 100ms');
13        setTimeout(wfea_render_calendar, 100);
14        return;
15    }
16
17    // Select all elements with the class name (include Elementor widgets)
18    let elements = jQuery(".wfea.wfea-cal, .wfea.wfea-calendar-list, .wfea-elementor-widget .wfea.wfea-cal, .wfea-elementor-widget .wfea.wfea-calendar-list");
19
20    console.debug('WFEA: Found ' + elements.length + ' calendar elements on page');
21
22    // Log instance IDs for debugging
23    elements.each(function (index) {
24        console.debug('WFEA: Calendar ' + index + ' instance ID:', jQuery(this).attr('data-wfea-instance'));
25    });
26
27    if (elements.length > 0) {
28        // Iterator over all found elements
29        elements.each(function () {
30            let dataAttributes = {};
31            let element = jQuery(this); // Current element in the loop
32            let inlineData = null;
33            let calOptions = null;
34
35            // For Elementor widgets, check if we're inside a preview container
36            let isElementorWidget = element.closest('.wfea-elementor-widget').length > 0;
37
38            // Check if we have JavaScript variables with our data (primary approach)
39            let eventsVar = element.attr('data-wfea-events-var');
40            let optionsVar = element.attr('data-wfea-options-var');
41
42            console.debug('WFEA: Checking for JavaScript variables');
43            console.debug('WFEA: data-wfea-events-var attribute:', eventsVar);
44            console.debug('WFEA: data-wfea-options-var attribute:', optionsVar);
45
46            // Check if the variables are defined in the window object
47            if (eventsVar) {
48                console.debug('WFEA: JS variable ' + eventsVar + ' exists:', window[eventsVar] !== undefined);
49            }
50            if (optionsVar) {
51                console.debug('WFEA: JS variable ' + optionsVar + ' exists:', window[optionsVar] !== undefined);
52            }
53
54            if (eventsVar && optionsVar && window[eventsVar] && window[optionsVar]) {
55                console.debug('WFEA: Found JavaScript variables for calendar data');
56
57                inlineData = window[eventsVar];
58                calOptions = window[optionsVar];
59
60                console.debug('WFEA: Loaded events count:', Array.isArray(inlineData) ? inlineData.length : 'not an array');
61
62                renderCalendarWithData(element, calOptions, inlineData);
63
64                // Setup search for both regular and Elementor widgets
65                if (isElementorWidget) {
66                    // For Elementor, we need to look for the search form in the parent container
67                    let container = element.closest('.wfea-elementor-widget');
68                    setupElementorSearch(container, element, inlineData);
69                } else {
70                    setupSearch(element, inlineData);
71                }
72                return; // Skip REST API call
73            } else {
74                console.debug('WFEA: JavaScript variables not found or not ready, falling back to REST API');
75            }
76
77            // Collect data attributes for REST API
78            jQuery.each(this.attributes, function (index, attribute) {
79                if (attribute.name.startsWith('data-wfea')) {
80                    // Extract the attribute name and value
81                    let dataName = attribute.name.replace('data-wfea-', '');
82                    let dataValue = attribute.value;
83
84                    // Check if the value is not undefined before storing it
85                    if (typeof dataValue !== 'undefined') {
86                        // Store it in key-value format
87                        dataAttributes[dataName] = dataValue;
88                    }
89                }
90            });
91
92            // Convert object to JSON
93            let attributesString = JSON.stringify(dataAttributes);
94
95            // Log that we're falling back to REST API
96            console.debug('WFEA: Using REST API fallback for calendar data');
97
98            // Call API here (only for editor/admin or when inline data not available)
99            wp.apiFetch({
100                path: '/widget-for-eventbrite-api/v1/wfea_cal_options',
101                method: 'POST',
102                headers: {'Content-Type': 'application/json'},
103                body: attributesString
104            }).then(response => {
105                console.debug('WFEA: REST API options response received');
106
107                // Handle the response and perform rendering
108                let calendar = element.find('.wfea-fullcalendar');
109                let options = jQuery.extend(response, wfea_extend_fullcalendar(
109))
110                calendar.fullCalendar(options);
111                let sources = calendar.fullCalendar('getEventSources');
112                calendar.fullCalendar('removeEventSources');
113
114                // Prepare new URL with updated query parameters
115                let updatedUrl = sources[0].url.split('/eb_events')[0] + '/eb_events?' + attributesToQuery(dataAttributes);
116                sources[0].url = updatedUrl;
117
118                console.debug('WFEA: REST API events source URL:', updatedUrl);
119
120                calendar.fullCalendar('addEventSource', sources[0]);
121                calendar.fullCalendar('refetchEventSources', sources[0]);
122            }).catch(error => {
123                // Handle any errors
124                console.error('WFEA: Error performing REST API lookup:', error);
125            });
126        });
127    } else {
128        // console.log('The ".wfea" element does not exist on the page.');
129    }
130
131    function renderCalendarWithData(element, calOptions, eventData) {
132        let calendar = element.find('.wfea-fullcalendar');
133        let options = jQuery.extend(calOptions, wfea_extend_fullcalendar());
134
135        // Initialize calendar with options
136        calendar.fullCalendar(options);
137
138        // Clear existing events
139        calendar.fullCalendar('removeEventSources');
140
141        // Add events directly without REST API call
142        calendar.fullCalendar('addEventSource', {
143            events: eventData
144        });
145    }
146
147    function setupSearch(element, allEventData) {
148        // Find the search form associated with this calendar
149        let instance = element.data('wfea-instance');
150        let searchForm = jQuery('.wfea-search__form').filter(function () {
151            return jQuery(this).find('input[name="wfea_s_target"]').val() === 'wfea_instance_' + instance;
152        });
153
154        if (searchForm.length) {
155            // Check if this is cal or cal_list layout
156            let isCalLayout = element.hasClass('wfea-cal');
157            let isCalListLayout = element.hasClass('wfea-calendar-list');
158
159            // For both calendar layouts, prevent default form submission and use JS filtering
160            if (isCalLayout || isCalListLayout) {
161                // Remove any existing handlers to prevent duplication
162                searchForm.off('submit.wfeaSearch');
163
164                // Bind the submit event
165                searchForm.on('submit.wfeaSearch', function (e) {
166                    e.preventDefault();
167
168                    // Get search term
169                    let searchTerm = jQuery(this).find('input[name="wfea_s"]').val().toLowerCase();
170                    let calendar = element.find('.wfea-fullcalendar');
171
172                    // Filter events based on search term
173                    let filteredEvents = allEventData;
174                    if (searchTerm) {
175                        filteredEvents = allEventData.filter(function (event) {
176                            return event.title.toLowerCase().includes(searchTerm) ||
177                                (event.excerpt && event.excerpt.toLowerCase().includes(searchTerm)) ||
178                                (event.location && event.location.toLowerCase().includes(searchTerm)) ||
179                                (event.venue && event.venue.toLowerCase().includes(searchTerm));
180                        });
181                    }
182
183                    // Update calendar with filtered events
184                    calendar.fullCalendar('removeEventSources');
185                    calendar.fullCalendar('addEventSource', {
186                        events: filteredEvents
187                    });
188
189                    return false;
190                });
191
192                // Also bind to the submit button click directly for better compatibility
193                let submitButton = searchForm.find('input[type="submit"]');
194                submitButton.off('click.wfeaSearch');
195                submitButton.on('click.wfeaSearch', function (e) {
196                    e.preventDefault();
197                    e.stopPropagation(); // Stop event propagation
198                    searchForm.trigger('submit.wfeaSearch');
199                    return false;
200                });
201            }
202        }
203    }
204
205    function setupElementorSearch(container, element, allEventData) {
206        // For Elementor widgets, find the search form inside the container
207        let searchForm = container.find('.wfea-search__form');
208
209        if (searchForm.length) {
210            // Remove any existing event handlers first (in case of duplicates)
211            searchForm.off('submit.wfeaSearch');
212
213            // For Elementor widgets, bind to both form submit and button click
214            searchForm.on('submit.wfeaSearch', function (e) {
215                e.preventDefault();
216                performElementorSearch(searchForm, element, allEventData);
217                return false;
218            });
219
220            // Also bind to the submit button click directly
221            let submitButton = searchForm.find('input[type="submit"]');
222            submitButton.off('click.wfeaSearch');
223            submitButton.on('click.wfeaSearch', function (e) {
224                e.preventDefault();
225                e.stopPropagation(); // Stop event propagation
226                performElementorSearch(searchForm, element, allEventData);
227                return false;
228            });
229
230            // Add event delegation for the document to catch any other click
231            jQuery(document).off('click.wfeaButtonSearch', '.wfea-elementor-widget .wfea-search_cal__submit');
232            jQuery(document).on('click.wfeaButtonSearch', '.wfea-elementor-widget .wfea-search_cal__submit', function (e) {
233                e.preventDefault();
234                e.stopPropagation();
235
236                // Find the closest form and calendar
237                let clickedForm = jQuery(this).closest('.wfea-search__form');
238                let widgetContainer = jQuery(this).closest('.wfea-elementor-widget');
239                let calendarElement = widgetContainer.find('.wfea.wfea-cal, .wfea.wfea-calendar-list');
240
241                // Make sure we have the right elements
242                if (clickedForm.length && calendarElement.length) {
243                    let eventData = calendarElement.data('wfea-inline-events');
244                    if (eventData) {
245                        performElementorSearch(clickedForm, calendarElement, eventData);
246                    }
247                }
248
249                return false;
250            });
251        }
252    }
253
254    // Helper function to perform search on Elementor widgets
255    function performElementorSearch(form, element, allEventData) {
256        // Get search term
257        let searchTerm = form.find('input[name="wfea_s"]').val().toLowerCase();
258        let calendar = element.find('.wfea-fullcalendar');
259
260        // Filter events based on search term
261        let filteredEvents = allEventData;
262        if (searchTerm) {
263            filteredEvents = allEventData.filter(function (event) {
264                return event.title.toLowerCase().includes(searchTerm) ||
265                    (event.excerpt && event.excerpt.toLowerCase().includes(searchTerm)) ||
266                    (event.location && event.location.toLowerCase().includes(searchTerm)) ||
267                    (event.venue && event.venue.toLowerCase().includes(searchTerm));
268            });
269        }
270
271        // Update calendar with filtered events
272        calendar.fullCalendar('removeEventSources');
273        calendar.fullCalendar('addEventSource', {
274            events: filteredEvents
275        });
276
277        // Log for debugging
278        console.log('Elementor search performed with term:', searchTerm, 'found', filteredEvents.length, 'events');
279    }
280
281    function attributesToQuery(dataAttributes) {
282        let query = Object.keys(dataAttributes)
283            .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(dataAttributes[k]))
284            .join('&');
285        return query;
286    }
287}
288
289function wfea_extend_fullcalendar() {
290    return {
291        eventAfterAllRender: function (view) {
292            jQuery("[data-eb-id]").wfeaBindLinksToEB();
293        },
294        eventRender: function (event, element) {
295            let img = '';
296
297            element.filter(".fc-event").prop('id', 'wfea-popup-booknow-' + event._id);
298            element.filter(".fc-event").addClass(event.classes);
299            element.filter(".fc-list-item").addClass(event.classes);
300            element.find(".fc-list-item-title").prop('id', 'wfea-popup-booknow-' + event._id);
301            if (false != event.popup_id) {
302                element.filter(".fc-event").prop('id', 'wfea-popup-booknow-' + event._id).attr('data-eb-id', event.popup_id);
303                element.find(".fc-list-item-title").prop('id', 'wfea-popup-booknow-' + event._id).attr('data-eb-id', event.popup_id);
304            }
305
306            element.find(".fc-list-item-title > a").wrapInner("<div class=\"cal-list-text-wrap\"></div>");
307            if (event.show_inline_content) {
308                element.find(".cal-list-text-wrap").append("<div class=\"cal-list-content-wrap\">" + event.excerpt + "</div>");
309            }
310            if (event.imageurl) {
311                // Create image markup with lazy loading for visible list items
312                let imgHtml = "";
313                if (event.imageurl_original) {
314                    // Log URLs for debugging
315                    console.log('Calendar images - Standard:', event.imageurl);
316                    console.log('Calendar images - Original:', event.imageurl_original);
317
318                    // When thumb_original is true, use the original image directly (not as srcset)
319                    imgHtml = "<div class=\"cal-list-img-wrap\"><img class=\"cal-list\" src='" +
320                        event.imageurl_original + "' loading='lazy' alt=''></div>";
321                } else {
322                    // Basic lazy loading (only for visible calendar images)
323                    imgHtml = "<div class=\"cal-list-img-wrap\"><img class=\"cal-list\" src='" +
324                        event.imageurl + "' loading='lazy' alt=''></div>";
325                }
326                element.find(".fc-list-item-title > a").append(imgHtml);
327
328                element.find(".fc-list-item-title > a").wrapInner("<div class=\"cal-list-link-wrap\"></div>");
329
330                // For tooltip/qtip image - use data attributes instead of src to prevent automatic loading
331                if (event.imageurl_original) {
332                    // When thumb_original is true, use the original image directly
333                    img = "<img class=\"cal-list-qtip\" data-src='" + event.imageurl_original +
334                        "' style='width: 50%;float:right;' alt=''>";
335
336                    // Add debug data attributes to help troubleshoot
337                    img = img.replace('>',
338                        ' data-debug-original="' + event.imageurl_original + '"' +
339                        ' data-debug-standard="' + event.imageurl + '">'
340                    );
341                } else {
342                    img = "<img class=\"cal-list-qtip\" data-src='" + event.imageurl +
343                        "' style='width: 50%;float:right;' alt=''>";
344                }
345            }
346            let loc = '';
347            if (event.location) {
348                loc = "<p class='wfea-tip wfea-tip-location'>" + event.location + "</p>"
349            }
350            let loc_title = '';
351            if (event.location_title) {
352                loc_title = "<h5 class='wfea-tip wfea-tip-title'>" + event.location_title + "</h5>"
353            }
354            let presented_by = '';
355            if (event.presented_by) {
356                presented_by = "<p class='wfea-tip wfea-tip-presented-by'>" + event.presented_by + "</p>"
357            }
358            let prices = '';
359            if (event.prices) {
360                prices = "<p class='wfea-tip wfea-tip-prices'>" + event.prices + "</p>"
361            }
362            let availability = '';
363            if (event.availability) {
364                availability = "<p class='wfea-tip wfea-tip-availabilty'>" + event.availability + "</p>"
365            }
366            let door_time = '';
367            if (event.door_time) {
368                door_time = "<p class='wfea-tip wfea-tip-door-time'>" + event.door_time + "</p>"
369            }
370            let age_restriction = '';
371            if (event.age_restriction) {
372                age_restriction = "<p class='wfea-tip wfea-tip-age-restriction'>" + event.age_restriction + "</p>"
373            }
374
375            var help_tip = `
376                    <div  class="wfea-help-tip">
377                       <div class="wfea-help-tip-titlebar">
378                          <div class="wfea-help-tip-title" aria-atomic="true">${event.title}</div>
379                       </div>
380                       <div class="wfea-help-tip-content" aria-atomic="true">
381                            ${loc_title + loc + presented_by + img + prices + availability + door_time + age_restriction + event.excerpt}
382                       </div>
383                    </div>
384                `;
385
386            if (element.find(".fc-content").length > 0) {
387                element.find(".fc-content").after(help_tip);
388            }
389
390            if (element.find(".fc-list-item-title").length > 0) {
391                element.find(".fc-list-item-title").append(help_tip);
392            }
393        }
394    };
395}
396
397(function ($) {
398    'use strict';
399    /* -------- new calendar code -------- */
400    // dom ready and scripts loaded
401    $(document).ready(function () {
402
403        // Enhanced debugging
404        console.debug('WFEA Calendar initialized with deferred image loading');
405
406        // Log browser capabilities information
407        console.debug('WFEA Browser info - userAgent:', navigator.userAgent);
408        console.debug('WFEA Browser limitations check:', {
409            'localStorage available': (function () {
410                try {
411                    return window.localStorage !== undefined;
412                } catch (e) {
413                    return false;
414                }
415            })(),
416            'jQuery version': jQuery.fn.jquery,
417            'Max attribute length test': (function () {
418                try {
419                    // Create a test element to check attribute size limits
420                    let testEl = jQuery('<div></div>');
421                    let testStr = '';
422                    const chunk = 'X'.repeat(1000); // 1KB chunk
423                    let chunks = 0;
424                    let maxAttrLength = 0;
425
426                    // Try to set increasingly large data attributes
427                    try {
428                        // Start with 1KB and increase
429                        for (let i = 1; i <= 512; i++) { // Up to 512KB
430                            testStr += chunk;
431                            testEl.attr('data-test-size', testStr);
432                            maxAttrLength = testStr.length;
433                            chunks = i;
434
435                            // If we get past 100KB, that's usually enough for testing
436                            if (i >= 100) break;
437                        }
438                    } catch (e) {
439                        // We've hit a limit
440                        console.error('Attribute size limit reached at', maxAttrLength, 'bytes');
441                    }
442
443                    return {
444                        'Maximum attribute size': maxAttrLength + ' bytes',
445                        'Number of 1KB chunks': chunks,
446                        'Likely supports large data attributes': maxAttrLength >= 65536
447                    };
448                } catch (e) {
449                    return 'Error testing attribute sizes: ' + e.message;
450                }
451            })()
452        });
453    });
454
455    // For fullcalendar smart tooltips
456    $(document).on('mouseenter', '.fc-event, .fc-list-item', function () {
457        var $event = $(this);
458        var $tip = $event.find('.wfea-help-tip');
459
460        if ($tip.length) {
461            // 1. Move tip to body to escape overflow:hidden containers
462            $tip.data('original-parent', $tip.parent());
463            $tip.appendTo('body');
464
465            // 2. Handle deferred image loading
466            $tip.find('img[data-src]').each(function () {
467                var $img = $(this);
468                if ($img.attr('data-src') && !$img.attr('src')) {
469                    $img.attr('src', $img.attr('data-src'));
470                    if ($img.attr('data-srcset')) {
471                        $img.attr('srcset', $img.attr('data-srcset'));
472                    }
473                    $img.removeAttr('data-src').removeAttr('data-srcset');
474                }
475            });
476
477            // 3. Calculate position
478            var eventRect = $event[0].getBoundingClientRect();
479            var tipRect = $tip[0].getBoundingClientRect();
480            var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
481            var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
482
483            // Default position: centered above the event
484            var top = eventRect.top + scrollTop - tipRect.height - 10; // 10px margin
485            var left = eventRect.left + scrollLeft + (eventRect.width / 2) - (tipRect.width / 2);
486
487            // 4. Viewport collision detection and correction (flipping)
488            if (top - scrollTop < 0) { // If it goes above the viewport
489                top = eventRect.bottom + scrollTop + 10; // Position it below
490            }
491            if (left < 0) { // If it goes off the left
492                left = 5;
493            }
494            if (left + tipRect.width > window.innerWidth) { // If it goes off the right
495                left = window.innerWidth - tipRect.width - 5;
496            }
497
498            // 5. Apply position and show the tip
499            $tip.css({
500                position: 'absolute',
501                top: top + 'px',
502                left: left + 'px',
503                visibility: 'visible',
504                opacity: 1,
505                'z-index': 999999
506            });
507        }
508    });
509
510    $(document).on('mouseleave', '.fc-event, .fc-list-item', function() {
511        var $tip = $('body > .wfea-help-tip');
512        if ($tip.length) {
513            // Hide and return to original parent
514            $tip.css({
515                visibility: 'hidden',
516                opacity: 0
517            });
518            var $originalParent = $tip.data('original-parent');
519            if ($originalParent && $originalParent.length) {
520                $tip.appendTo($originalParent);
521            }
522        }
523    });
524
525    // Define a function to initialize calendars that will be called after document.ready
526    function wfea_initialize_calendars() {
527        console.debug('WFEA: Delayed initialization to ensure attributes are set');
528        // Give a small delay to make sure all DOM attributes are set
529        setTimeout(function () {
530            wfea_render_calendar();
531        }, 100); // 100ms delay should be sufficient
532    }
533
534    $(document).ready(function () {
535        // Use the delayed initialization function
536        wfea_initialize_calendars();
537
538        // Global click handler for all calendar search buttons (extra fallback)
539        // This handles both Elementor widgets and shortcode renders
540        $(document).on('click', '.wfea-search_cal__submit', function (e) {
541            e.preventDefault();
542
543            // Find the closest form
544            let form = $(this).closest('form');
545            if (!form.length) return false;
546
547            let searchTerm = form.find('input[name="wfea_s"]').val().toLowerCase();
548            let targetInput = form.find('input[name="wfea_s_target"]');
549
550            if (!targetInput.length) return false;
551
552            // Extract the instance ID to find the associated calendar
553            let instanceTarget = targetInput.val();
554            let instanceNumber = instanceTarget.replace('wfea_instance_', '');
555
556            // Find any calendar with matching instance ID
557            let calElement = $('.wfea[data-wfea-instance="' + instanceNumber + '"]');
558
559            // If not found, try looking in containers
560            if (!calElement.length) {
561                // Try finding in Elementor widget or block containers
562                let container = $(this).closest('.wfea-elementor-widget, .wp-block-widget-for-eventbrite-api-display-eventbrite-calendar');
563                calElement = container.find('.wfea.wfea-cal, .wfea.wfea-calendar-list');
564            }
565
566            if (calElement.length) {
567                let eventData = calElement.data('wfea-inline-events');
568                let calendar = calElement.find('.wfea-fullcalendar');
569
570                if (eventData && calendar.length) {
571                    // Filter events based on search term
572                    let filteredEvents = eventData;
573                    if (searchTerm) {
574                        filteredEvents = eventData.filter(function (event) {
575                            return event.title.toLowerCase().includes(searchTerm) ||
576                                (event.excerpt && event.excerpt.toLowerCase().includes(searchTerm)) ||
577                                (event.location && event.location.toLowerCase().includes(searchTerm)) ||
578                                (event.venue && event.venue.toLowerCase().includes(searchTerm));
579                        });
580                    }
581
582                    // Update calendar with filtered events
583                    calendar.fullCalendar('removeEventSources');
584                    calendar.fullCalendar('addEventSource', {
585                        events: filteredEvents
586                    });
587
588                    console.log('Global handler - Calendar search performed with term:', searchTerm, 'Instance:', instanceNumber);
589                }
590            }
591
592            return false;
593        });
594    });
595
596    /* ---------end new calendar code --------- */
597
598
599})(jQuery);

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.