/** * Find a Trainer Page JavaScript - Safari Compatible Version * Handles filtering, modals, and AJAX interactions * ES5 compatible for Safari browser support * * @package HVAC_Plugin * @since 1.0.0 */ (function($) { 'use strict'; // Cache DOM elements var $filterModal, $trainerModal, $contactForm; var currentFilter = ''; var activeFilters = {}; var currentPage = 1; var isLoading = false; // Initialize on document ready $(document).ready(function() { initializeElements(); bindEvents(); // Handle direct profile URL access handleDirectProfileAccess(); // Enable MapGeo interaction handling (only if not showing direct profile) if (!hvac_find_trainer.show_direct_profile) { preventMapGeoSidebarContent(); interceptMapGeoMarkers(); // Additional MapGeo integration after map loads setTimeout(function() { initializeMapGeoEvents(); }, 2000); // Give MapGeo time to initialize } }); /** * Initialize cached elements */ function initializeElements() { $filterModal = $('#hvac-filter-modal'); $trainerModal = $('#hvac-trainer-modal'); $contactForm = $('#hvac-contact-form'); } /** * Prevent MapGeo from displaying content in its sidebar * This ensures trainer cards only appear in our Container 5 */ function preventMapGeoSidebarContent() { // Remove any MapGeo sidebar content immediately $('.igm_content_right_1_3').remove(); $('.igm_content_gutter').remove(); // Watch for any dynamic content injection from MapGeo var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.addedNodes.length) { mutation.addedNodes.forEach(function(node) { if (node.nodeType === 1) { // Element node // Remove any MapGeo sidebar that gets added if ($(node).hasClass('igm_content_right_1_3') || $(node).hasClass('igm_content_gutter')) { $(node).remove(); } // Also check children $(node).find('.igm_content_right_1_3, .igm_content_gutter').remove(); } }); } }); }); // Observe the map section for changes var mapSection = document.querySelector('.hvac-map-section'); if (mapSection) { observer.observe(mapSection, { childList: true, subtree: true }); } // Also observe the entire page for MapGeo injections observer.observe(document.body, { childList: true, subtree: true }); } /** * Initialize MapGeo-specific event handlers after map loads */ function initializeMapGeoEvents() { console.log('Initializing MapGeo events...'); // Create the main MapGeo handler function // This replaces the early version created in the page template window.hvacMainShowTrainerModal = function(data) { console.log('MapGeo custom action triggered with data:', data); // Method 1: Use profile_id if available (most reliable) var profileId = null; if (data && data.profile_id && data.profile_id.trim() !== '') { profileId = data.profile_id.trim(); } else if (data && data.id && data.id.toString().startsWith('trainer_')) { // Extract profile ID from marker ID format "trainer_123" profileId = data.id.replace('trainer_', ''); } else if (data && data.name && data.name.match(/^\d+$/)) { // Check if name field actually contains the profile ID (common case) profileId = data.name; } else if (data && data.id && data.id.match(/^\d+$/)) { // Check if id field contains just the profile ID number profileId = data.id; } console.log('Extracted profile ID:', profileId); if (profileId) { // Find trainer card by profile ID (most reliable method) var $matchingCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]'); if ($matchingCard.length > 0 && !$matchingCard.hasClass('hvac-champion-card')) { console.log('Found matching trainer card by profile ID:', profileId); // Extract trainer data from the card var trainerData = { profile_id: profileId, name: $matchingCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(), city: $matchingCard.find('.hvac-trainer-location').text().split(',')[0], state: $matchingCard.find('.hvac-trainer-location').text().split(',')[1] ? $matchingCard.find('.hvac-trainer-location').text().split(',')[1].trim() : '', certification_type: $matchingCard.find('.hvac-trainer-certification').text(), profile_image: $matchingCard.find('.hvac-trainer-image img:not(.hvac-mq-badge)').attr('src') || '', business_type: 'Independent Contractor', // Mock data event_count: parseInt($matchingCard.data('event-count')) || 0, training_formats: 'In-Person, Virtual', training_locations: 'On-site, Remote', upcoming_events: [] }; // Show the trainer modal showTrainerModal(trainerData); return; // Successfully handled } else if ($matchingCard.length > 0 && $matchingCard.hasClass('hvac-champion-card')) { console.log('Clicked marker is for a Champion, not showing modal'); return; // Champions don't get modals } else { console.warn('No trainer card found for profile ID:', profileId); } } // Fallback Method 2: Try to extract trainer name and match var trainerName = null; // Try various name fields if (data && data.name && data.name.trim() !== '+' && !data.name.match(/^\d+$/)) { trainerName = data.name.trim(); } else if (data && data.title && data.title.trim() !== '+') { trainerName = data.title.trim(); } // Try content field if (!trainerName && data && data.content) { console.log('Trying to extract trainer from content:', data.content); var tempDiv = document.createElement('div'); tempDiv.innerHTML = data.content; var nameElements = tempDiv.querySelectorAll('h4, strong, .trainer-name, [class*="name"]'); if (nameElements.length > 0) { trainerName = nameElements[0].textContent.trim(); } } // Try tooltipContent if (!trainerName && data && data.tooltipContent) { var tempDiv = document.createElement('div'); tempDiv.innerHTML = data.tooltipContent; var strongElements = tempDiv.querySelectorAll('strong'); if (strongElements.length > 0) { trainerName = strongElements[0].textContent.trim(); } } console.log('Extracted trainer name (fallback):', trainerName); if (trainerName && trainerName !== '+') { // Try to find matching trainer by name var $matchingCard = $('.hvac-trainer-card').filter(function() { var cardName = $(this).find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(); return cardName === trainerName; }); // If exact match not found, try partial matching if ($matchingCard.length === 0) { $matchingCard = $('.hvac-trainer-card').filter(function() { var cardName = $(this).find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(); return cardName.toLowerCase().includes(trainerName.toLowerCase()) || trainerName.toLowerCase().includes(cardName.toLowerCase()); }); } if ($matchingCard.length > 0 && !$matchingCard.hasClass('hvac-champion-card')) { console.log('Found matching trainer card by name:', trainerName); // Extract trainer data from the card var trainerData = { profile_id: $matchingCard.data('profile-id'), name: $matchingCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(), city: $matchingCard.find('.hvac-trainer-location').text().split(',')[0], state: $matchingCard.find('.hvac-trainer-location').text().split(',')[1] ? $matchingCard.find('.hvac-trainer-location').text().split(',')[1].trim() : '', certification_type: $matchingCard.find('.hvac-trainer-certification').text(), profile_image: $matchingCard.find('.hvac-trainer-image img:not(.hvac-mq-badge)').attr('src') || '', business_type: 'Independent Contractor', // Mock data event_count: parseInt($matchingCard.data('event-count')) || 0, training_formats: 'In-Person, Virtual', training_locations: 'On-site, Remote', upcoming_events: [] }; // Show the trainer modal showTrainerModal(trainerData); } else if ($matchingCard.length > 0 && $matchingCard.hasClass('hvac-champion-card')) { console.log('Matched trainer is a Champion, not showing modal'); } else { console.warn('No matching trainer found for name:', trainerName); console.log('Available trainers:', $('.hvac-trainer-card .hvac-trainer-name a, .hvac-trainer-card .hvac-trainer-name .hvac-champion-name').map(function() { return $(this).text().trim(); }).get() ); } } else { console.warn('Could not extract valid trainer identifier from MapGeo data:', data); console.log('Available data properties:', data ? Object.keys(data) : []); console.log('Available profile IDs on page:', $('.hvac-trainer-card').map(function() { return $(this).data('profile-id'); }).get() ); } }; // Replace the early function with the main one window.hvacShowTrainerModal = window.hvacMainShowTrainerModal; // Process any queued calls from before the main script loaded if (window.hvacPendingModalCalls && window.hvacPendingModalCalls.length > 0) { console.log('Processing', window.hvacPendingModalCalls.length, 'queued MapGeo calls'); window.hvacPendingModalCalls.forEach(function(data) { window.hvacMainShowTrainerModal(data); }); window.hvacPendingModalCalls = []; // Clear the queue } console.log('MapGeo custom action function created: window.hvacShowTrainerModal'); } /** * Prevent MapGeo from showing content in sidebar (if needed) */ function interceptMapGeoMarkers() { // This function now primarily handles preventing MapGeo sidebar content // The actual marker clicks are handled via the MapGeo custom action: window.hvacShowTrainerModal // Handle any legacy view profile links if they exist in tooltips/popups $(document).on('click', '.hvac-view-profile, .hvac-marker-popup button', function(e) { e.preventDefault(); e.stopPropagation(); var profileId = $(this).data('profile-id'); if (profileId) { // Find the corresponding trainer data from the cards var $trainerCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]'); if ($trainerCard.length > 0 && !$trainerCard.hasClass('hvac-champion-card')) { // Get trainer name and trigger the MapGeo custom action var trainerName = $trainerCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(); // Trigger the same function MapGeo would call if (window.hvacShowTrainerModal) { window.hvacShowTrainerModal({ name: trainerName }); } } } }); } /** * Bind all event handlers */ function bindEvents() { // Filter button clicks $('.hvac-filter-btn').on('click', handleFilterClick); // Filter modal apply $('.hvac-filter-apply').on('click', applyFilters); // Clear all filters button $('.hvac-clear-filters').on('click', clearAllFilters); // Trainer profile clicks - using event delegation $(document).on('click', '.hvac-open-profile', handleProfileClick); // Modal close buttons and backdrop clicks $('.hvac-modal-close').on('click', closeModals); // Click on modal backdrop to close $filterModal.on('click', function(e) { if ($(e.target).is('#hvac-filter-modal')) { closeModals(); } }); $trainerModal.on('click', function(e) { if ($(e.target).is('#hvac-trainer-modal')) { closeModals(); } }); // Escape key to close modals $(document).on('keydown', function(e) { if (e.key === 'Escape') { closeModals(); } }); // Search input $('#hvac-trainer-search').on('input', debounce(handleSearch, 500)); // Contact form submission (both modal and direct forms) $contactForm.on('submit', handleContactSubmit); $(document).on('submit', '#hvac-direct-contact-form', handleContactSubmit); // Pagination clicks $(document).on('click', '.hvac-pagination a, .hvac-page-link', handlePagination); // Active filter removal $(document).on('click', '.hvac-active-filter button', removeActiveFilter); } /** * Handle filter button click */ function handleFilterClick(e) { e.preventDefault(); e.stopPropagation(); currentFilter = $(this).data('filter'); // For now, show mock filter options showFilterModal(getMockFilterOptions(currentFilter)); } /** * Get mock filter options (replace with AJAX later) */ function getMockFilterOptions(filterType) { var options = { state: [ {value: 'Alabama', label: 'Alabama'}, {value: 'Alaska', label: 'Alaska'}, {value: 'Arizona', label: 'Arizona'}, {value: 'Arkansas', label: 'Arkansas'}, {value: 'California', label: 'California'}, {value: 'Colorado', label: 'Colorado'}, {value: 'Florida', label: 'Florida'}, {value: 'Georgia', label: 'Georgia'}, {value: 'Illinois', label: 'Illinois'}, {value: 'Michigan', label: 'Michigan'}, {value: 'Minnesota', label: 'Minnesota'}, {value: 'Ohio', label: 'Ohio'}, {value: 'Texas', label: 'Texas'}, {value: 'Wisconsin', label: 'Wisconsin'} ], business_type: [ {value: 'Independent Contractor', label: 'Independent Contractor'}, {value: 'Small Business', label: 'Small Business'}, {value: 'Corporation', label: 'Corporation'}, {value: 'Non-Profit', label: 'Non-Profit'} ], training_format: [ {value: 'In-Person', label: 'In-Person'}, {value: 'Virtual', label: 'Virtual'}, {value: 'Hybrid', label: 'Hybrid'}, {value: 'Self-Paced', label: 'Self-Paced'} ], training_resources: [ {value: 'Video Tutorials', label: 'Video Tutorials'}, {value: 'Written Guides', label: 'Written Guides'}, {value: 'Hands-On Training', label: 'Hands-On Training'}, {value: 'Certification Programs', label: 'Certification Programs'} ] }; return { options: options[filterType] || [] }; } /** * Show filter modal with options */ function showFilterModal(data) { var $modalTitle = $filterModal.find('.hvac-filter-modal-title'); var $modalOptions = $filterModal.find('.hvac-filter-options'); // Set title var title = currentFilter.replace(/_/g, ' '); title = title.charAt(0).toUpperCase() + title.slice(1); $modalTitle.text(title); // Build options HTML var optionsHtml = ''; var currentValues = activeFilters[currentFilter] || []; data.options.forEach(function(option) { var checked = currentValues.indexOf(option.value) !== -1 ? 'checked' : ''; optionsHtml += '
' + '' + '' + '
'; }); $modalOptions.html(optionsHtml); $filterModal.fadeIn(300); } /** * Apply selected filters */ function applyFilters() { var selectedValues = []; $filterModal.find('.hvac-filter-option input:checked').each(function() { selectedValues.push($(this).val()); }); if (selectedValues.length > 0) { activeFilters[currentFilter] = selectedValues; } else { delete activeFilters[currentFilter]; } updateActiveFiltersDisplay(); updateClearButtonVisibility(); currentPage = 1; loadFilteredTrainers(); closeModals(); } /** * Update active filters display */ function updateActiveFiltersDisplay() { var $container = $('.hvac-active-filters'); var html = ''; // Convert Object.entries to compatible approach for (var filter in activeFilters) { if (activeFilters.hasOwnProperty(filter)) { var values = activeFilters[filter]; values.forEach(function(value) { html += '
' + value + '' + '
'; }); } } $container.html(html); } /** * Remove active filter */ function removeActiveFilter(e) { e.preventDefault(); var $filter = $(this).parent(); var filter = $filter.data('filter'); var value = $filter.data('value'); if (activeFilters[filter]) { activeFilters[filter] = activeFilters[filter].filter(function(v) { return v !== value; }); if (activeFilters[filter].length === 0) { delete activeFilters[filter]; } } updateActiveFiltersDisplay(); updateClearButtonVisibility(); currentPage = 1; loadFilteredTrainers(); } /** * Handle trainer profile click */ function handleProfileClick(e) { e.preventDefault(); e.stopPropagation(); var $card = $(this).closest('.hvac-trainer-card'); // Don't allow clicks on Champion cards if ($card.hasClass('hvac-champion-card')) { return false; } var profileId = $(this).data('profile-id'); // Get trainer data from the card var trainerData = { profile_id: profileId, name: $card.find('.hvac-trainer-name a').text(), city: $card.find('.hvac-trainer-location').text().split(',')[0], state: $card.find('.hvac-trainer-location').text().split(',')[1] ? $card.find('.hvac-trainer-location').text().split(',')[1].trim() : '', certification_type: $card.find('.hvac-trainer-certification').text(), profile_image: $card.find('.hvac-trainer-image img').attr('src') || '', business_type: 'Independent Contractor', // Mock data event_count: parseInt($card.data('event-count')) || 0, // Real event count from data attribute training_formats: 'In-Person, Virtual', training_locations: 'On-site, Remote', upcoming_events: [] // Mock empty events }; showTrainerModal(trainerData); } /** * Show trainer profile modal * Made global so MapGeo can access it */ function showTrainerModal(trainer) { // Update modal title $trainerModal.find('.hvac-modal-title').text(trainer.name); // Update profile image var $imgContainer = $trainerModal.find('.hvac-modal-image'); var imageHtml = ''; if (trainer.profile_image) { imageHtml = '' + trainer.name + ''; } else { imageHtml = '
'; } // Add mQ badge overlay for certified trainers if (trainer.certification_type === 'Certified measureQuick Trainer') { imageHtml += '
measureQuick Certified Trainer
'; } $imgContainer.html(imageHtml); // Update profile info $trainerModal.find('.hvac-modal-location').text(trainer.city + ', ' + trainer.state); $trainerModal.find('.hvac-modal-certification').text(trainer.certification_type || 'HVAC Trainer'); $trainerModal.find('.hvac-modal-business').text(trainer.business_type || ''); $trainerModal.find('.hvac-modal-events span').text(trainer.event_count || 0); // Update training details $trainerModal.find('.hvac-training-formats').text(trainer.training_formats || 'Various'); $trainerModal.find('.hvac-training-locations').text(trainer.training_locations || 'On-site'); // Show loading state for events $trainerModal.find('.hvac-events-list').html('
  • Loading upcoming events...
  • '); // Set hidden fields for contact form $contactForm.find('input[name="trainer_id"]').val(trainer.user_id || ''); $contactForm.find('input[name="trainer_profile_id"]').val(trainer.profile_id); // Reset contact form $contactForm[0].reset(); $('.hvac-form-message').hide(); // Show modal $trainerModal.fadeIn(300); // Fetch upcoming events via AJAX fetchUpcomingEvents(trainer.profile_id); } /** * Fetch upcoming events for a trainer via AJAX */ function fetchUpcomingEvents(profileId) { if (!profileId) { $trainerModal.find('.hvac-events-list').html('
  • No upcoming events scheduled
  • '); return; } // Use SafariAjaxHandler if available for robust retry logic if (window.SafariAjaxHandler && SafariAjaxHandler.isSafari()) { SafariAjaxHandler.request('hvac_get_trainer_upcoming_events', { profile_id: profileId }, { progressCallback: function(progress) { if (progress.status === 'retrying') { console.log('[Safari] Retrying event fetch, attempt ' + progress.attempt); } } }).done(function(response) { handleEventsResponse(response); }).fail(function() { $trainerModal.find('.hvac-events-list').html('
  • Unable to load events. Please try again.
  • '); }); } else { // Fallback to standard jQuery AJAX $.post(hvac_find_trainer.ajax_url, { action: 'hvac_get_trainer_upcoming_events', nonce: hvac_find_trainer.nonce, profile_id: profileId }, function(response) { handleEventsResponse(response); }).fail(function() { $trainerModal.find('.hvac-events-list').html('
  • Unable to load events. Please try again.
  • '); }); } } /** * Handle events response */ function handleEventsResponse(response) { if (response.success && response.data.events) { var eventsHtml = ''; if (response.data.events.length > 0) { response.data.events.forEach(function(event) { eventsHtml += '
  • ' + event.title + ' - ' + event.date + '
  • '; }); } else { eventsHtml = '
  • No upcoming events scheduled
  • '; } $trainerModal.find('.hvac-events-list').html(eventsHtml); } else { $trainerModal.find('.hvac-events-list').html('
  • No upcoming events scheduled
  • '); } } /** * Handle contact form submission */ function handleContactSubmit(e) { e.preventDefault(); var $form = $(this); var $submitBtn = $form.find('.hvac-form-submit'); var $successMsg = $form.find('.hvac-form-success'); var $errorMsg = $form.find('.hvac-form-error'); var originalText = $submitBtn.text(); $submitBtn.text('Sending...').prop('disabled', true); // For now, just show success message setTimeout(function() { $successMsg.show(); $errorMsg.hide(); $form[0].reset(); $submitBtn.text(originalText).prop('disabled', false); // Hide success message after 5 seconds setTimeout(function() { $successMsg.fadeOut(); }, 5000); }, 1000); } /** * Handle search input */ function handleSearch() { var searchTerm = $('#hvac-trainer-search').val(); if (isLoading) return; updateClearButtonVisibility(); currentPage = 1; loadFilteredTrainers(); } /** * Handle pagination */ function handlePagination(e) { e.preventDefault(); currentPage = $(this).data('page'); loadFilteredTrainers(); // Scroll to top of trainer grid $('html, body').animate({ scrollTop: $('.hvac-trainer-directory-container').offset().top - 100 }, 500); } /** * Load filtered trainers via AJAX */ function loadFilteredTrainers() { if (isLoading) return; isLoading = true; var $container = $('.hvac-trainer-grid'); var $pagination = $('.hvac-pagination'); // Show loading state $container.addClass('hvac-loading'); // Prepare data - ES5 compatible object merge var searchValue = $('#hvac-trainer-search').val(); var filters = {}; // Copy activeFilters to filters for (var key in activeFilters) { if (activeFilters.hasOwnProperty(key)) { filters[key] = activeFilters[key]; } } filters.search = searchValue; var data = { action: 'hvac_filter_trainers', nonce: hvac_find_trainer.nonce, page: currentPage, filters: filters }; // Make AJAX request $.post(hvac_find_trainer.ajax_url, data, function(response) { if (response.success) { // Update trainer grid $container.html(response.data.html); // Update pagination if (response.data.pagination && response.data.pagination.length > 0) { // Pagination HTML returned - replace existing or create new if ($pagination.length > 0) { $pagination.replaceWith(response.data.pagination); } else { $('.hvac-trainer-directory-container').append(response.data.pagination); } } else { // No pagination HTML - either hide existing or ensure container exists for later if ($pagination.length > 0) { $pagination.empty(); } else if (response.data.max_pages > 1) { // Add empty pagination container for when pages increase later $('.hvac-trainer-directory-container').append('
    '); } } // Update pagination reference after potential DOM changes $pagination = $('.hvac-pagination'); // Update count display if exists if (response.data.count !== undefined) { $('.hvac-trainer-count').text(response.data.count + ' trainers found'); } } else { console.error('Failed to load trainers:', response); } }).fail(function(xhr) { console.error('AJAX error:', xhr); }).always(function() { isLoading = false; $container.removeClass('hvac-loading'); }); } /** * Close all modals */ function closeModals() { $filterModal.fadeOut(300); $trainerModal.fadeOut(300); } /** * Debounce helper function */ function debounce(func, wait) { var timeout; return function executedFunction() { var context = this; var args = arguments; var later = function() { clearTimeout(timeout); func.apply(context, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * Clear all filters */ function clearAllFilters() { activeFilters = {}; $('#hvac-trainer-search').val(''); updateActiveFiltersDisplay(); updateClearButtonVisibility(); currentPage = 1; loadFilteredTrainers(); } /** * Update clear button visibility */ function updateClearButtonVisibility() { var filterCount = 0; for (var key in activeFilters) { if (activeFilters.hasOwnProperty(key)) { filterCount++; break; // At least one filter exists } } var hasFilters = filterCount > 0; var hasSearch = $('#hvac-trainer-search').val().trim() !== ''; if (hasFilters || hasSearch) { $('.hvac-clear-filters').show(); } else { $('.hvac-clear-filters').hide(); } } /** * Handle direct profile URL access * When someone accesses /find-a-trainer/profile/{id}, show the profile and handle interactions */ function handleDirectProfileAccess() { // Check if we're showing a direct profile if (hvac_find_trainer.show_direct_profile && hvac_find_trainer.direct_profile_id) { console.log('Direct profile access detected for profile ID:', hvac_find_trainer.direct_profile_id); // Update page title in browser if (document.title.indexOf('Find a Trainer') !== -1) { document.title = document.title.replace('Find a Trainer', 'Trainer Profile'); } // Bind contact trainer button $(document).on('click', '.hvac-contact-trainer-btn', function(e) { e.preventDefault(); var profileId = $(this).data('profile-id'); showTrainerModal(profileId); }); // Update URL without page reload for clean sharing var currentUrl = window.location.href; if (currentUrl.indexOf('/profile/') !== -1 && window.history && window.history.replaceState) { var cleanUrl = currentUrl.split('?')[0]; // Remove any query parameters window.history.replaceState({}, document.title, cleanUrl); } } } // Expose showTrainerModal globally for MapGeo integration window.showTrainerModal = showTrainerModal; })(jQuery);