Add massive collection of CSS, JavaScript and theme assets that were previously excluded: **CSS Files (681 total):** - HVAC plugin-specific styles (hvac-*.css): 34 files including dashboard, certificates, registration, mobile nav, accessibility fixes, animations, and welcome popup - Theme framework files (Astra, builder systems, layouts): 200+ files - Plugin compatibility styles (WooCommerce, WPForms, Elementor, Contact Form 7): 150+ files - WordPress core and editor styles: 50+ files - Responsive and RTL language support: 200+ files **JavaScript Files (400+ total):** - HVAC plugin functionality (hvac-*.js): 27 files including menu systems, dashboard enhancements, profile sharing, mobile responsive features, accessibility, and animations - Framework and library files: jQuery plugins, GSAP, AOS, Swiper, Chart.js, Lottie, Isotope - Plugin compatibility scripts: WPForms, WooCommerce, Elementor, Contact Form 7, LifterLMS - WordPress core functionality: customizer, admin, block editor compatibility - Third-party integrations: Stripe, SMTP, analytics, search functionality **Assets:** - Certificate background images and logos - Comprehensive theme styling infrastructure - Mobile-responsive design systems - Cross-browser compatibility assets - Performance-optimized minified versions **Updated .gitignore:** - Fixed asset directory whitelisting patterns to properly include CSS/JS/images - Added proper directory structure recognition (!/assets/css/, !/assets/js/, etc.) - Maintains security by excluding sensitive files while including essential assets This commit provides the complete frontend infrastructure needed for: - Full theme functionality and styling - Plugin feature implementations - Mobile responsiveness and accessibility - Cross-browser compatibility - Performance optimization - Developer workflow support
		
			
				
	
	
		
			832 lines
		
	
	
		
			No EOL
		
	
	
		
			33 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			832 lines
		
	
	
		
			No EOL
		
	
	
		
			33 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | ||
|  * Find a Trainer Page JavaScript
 | ||
|  * Handles filtering, modals, and AJAX interactions
 | ||
|  * 
 | ||
|  * @package HVAC_Plugin
 | ||
|  * @since 1.0.0
 | ||
|  */
 | ||
| 
 | ||
| (function($) {
 | ||
|     'use strict';
 | ||
| 
 | ||
|     // Cache DOM elements
 | ||
|     let $filterModal, $trainerModal, $contactForm;
 | ||
|     let currentFilter = '';
 | ||
|     let activeFilters = {};
 | ||
|     let currentPage = 1;
 | ||
|     let 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');
 | ||
|         
 | ||
|         // CRITICAL: Ensure modals are hidden on initialization
 | ||
|         if ($filterModal.length) {
 | ||
|             $filterModal.removeClass('modal-active active show').css({
 | ||
|                 'display': 'none',
 | ||
|                 'visibility': 'hidden',
 | ||
|                 'opacity': '0'
 | ||
|             });
 | ||
|         }
 | ||
|         if ($trainerModal.length) {
 | ||
|             $trainerModal.css('display', 'none');
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * 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
 | ||
|         const 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
 | ||
|         const 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)
 | ||
|             let 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)
 | ||
|                 const $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
 | ||
|                     const 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]?.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
 | ||
|             let 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);
 | ||
|                 const tempDiv = document.createElement('div');
 | ||
|                 tempDiv.innerHTML = data.content;
 | ||
|                 const 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) {
 | ||
|                 const tempDiv = document.createElement('div');
 | ||
|                 tempDiv.innerHTML = data.tooltipContent;
 | ||
|                 const 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
 | ||
|                 let $matchingCard = $('.hvac-trainer-card').filter(function() {
 | ||
|                     const 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() {
 | ||
|                         const 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
 | ||
|                     const 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]?.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:', 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();
 | ||
|             const profileId = $(this).data('profile-id');
 | ||
|             if (profileId) {
 | ||
|                 // Find the corresponding trainer data from the cards
 | ||
|                 const $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
 | ||
|                     const 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) {
 | ||
|         const 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) {
 | ||
|         const $modalTitle = $filterModal.find('.hvac-filter-modal-title');
 | ||
|         const $modalOptions = $filterModal.find('.hvac-filter-options');
 | ||
|         
 | ||
|         // Set title
 | ||
|         let title = currentFilter.replace(/_/g, ' ');
 | ||
|         title = title.charAt(0).toUpperCase() + title.slice(1);
 | ||
|         $modalTitle.text(title);
 | ||
|         
 | ||
|         // Build options HTML
 | ||
|         let optionsHtml = '';
 | ||
|         const currentValues = activeFilters[currentFilter] || [];
 | ||
|         
 | ||
|         data.options.forEach(function(option) {
 | ||
|             const checked = currentValues.includes(option.value) ? 'checked' : '';
 | ||
|             optionsHtml += `
 | ||
|                 <div class="hvac-filter-option">
 | ||
|                     <input type="checkbox" id="filter_${option.value.replace(/\s+/g, '_')}" value="${option.value}" ${checked}>
 | ||
|                     <label for="filter_${option.value.replace(/\s+/g, '_')}">${option.label}</label>
 | ||
|                 </div>
 | ||
|             `;
 | ||
|         });
 | ||
|         
 | ||
|         $modalOptions.html(optionsHtml);
 | ||
|         // Use the new modal-active class for proper visibility control
 | ||
|         $filterModal.addClass('modal-active').css('display', 'flex').fadeIn(300);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Apply selected filters
 | ||
|      */
 | ||
|     function applyFilters() {
 | ||
|         const 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() {
 | ||
|         const $container = $('.hvac-active-filters');
 | ||
|         let html = '';
 | ||
|         
 | ||
|         for (const [filter, values] of Object.entries(activeFilters)) {
 | ||
|             values.forEach(function(value) {
 | ||
|                 html += `
 | ||
|                     <div class="hvac-active-filter" data-filter="${filter}" data-value="${value}">
 | ||
|                         ${value}
 | ||
|                         <button type="button" aria-label="Remove filter">×</button>
 | ||
|                     </div>
 | ||
|                 `;
 | ||
|             });
 | ||
|         }
 | ||
|         
 | ||
|         $container.html(html);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Remove active filter
 | ||
|      */
 | ||
|     function removeActiveFilter(e) {
 | ||
|         e.preventDefault();
 | ||
|         const $filter = $(this).parent();
 | ||
|         const filter = $filter.data('filter');
 | ||
|         const value = $filter.data('value');
 | ||
|         
 | ||
|         if (activeFilters[filter]) {
 | ||
|             activeFilters[filter] = activeFilters[filter].filter(v => 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();
 | ||
|         
 | ||
|         const $card = $(this).closest('.hvac-trainer-card');
 | ||
|         
 | ||
|         // Don't allow clicks on Champion cards
 | ||
|         if ($card.hasClass('hvac-champion-card')) {
 | ||
|             return false;
 | ||
|         }
 | ||
|         
 | ||
|         const profileId = $(this).data('profile-id');
 | ||
|         
 | ||
|         // Get trainer data from the card
 | ||
|         const 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]?.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
 | ||
|         const $imgContainer = $trainerModal.find('.hvac-modal-image');
 | ||
|         let imageHtml = '';
 | ||
|         
 | ||
|         if (trainer.profile_image) {
 | ||
|             imageHtml = `<img src="${trainer.profile_image}" alt="${trainer.name}">`;
 | ||
|         } else {
 | ||
|             imageHtml = '<div class="hvac-trainer-avatar"><span class="dashicons dashicons-businessperson"></span></div>';
 | ||
|         }
 | ||
|         
 | ||
|         // Add mQ badge overlay for certified trainers
 | ||
|         if (trainer.certification_type === 'Certified measureQuick Trainer') {
 | ||
|             imageHtml += '<div class="hvac-mq-badge-overlay"><img src="/wp-content/uploads/2025/08/mQ-Certified-trainer.png" alt="measureQuick Certified Trainer" class="hvac-mq-badge"></div>';
 | ||
|         }
 | ||
|         
 | ||
|         $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('<li>Loading upcoming events...</li>');
 | ||
|         
 | ||
|         // 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('<li>No upcoming events scheduled</li>');
 | ||
|             return;
 | ||
|         }
 | ||
|         
 | ||
|         $.post(hvac_find_trainer.ajax_url, {
 | ||
|             action: 'hvac_get_trainer_upcoming_events',
 | ||
|             nonce: hvac_find_trainer.nonce,
 | ||
|             profile_id: profileId
 | ||
|         }, function(response) {
 | ||
|             if (response.success && response.data.events) {
 | ||
|                 let eventsHtml = '';
 | ||
|                 if (response.data.events.length > 0) {
 | ||
|                     response.data.events.forEach(function(event) {
 | ||
|                         eventsHtml += `<li><a href="${event.url}" target="_blank">${event.title}</a> - ${event.date}</li>`;
 | ||
|                     });
 | ||
|                 } else {
 | ||
|                     eventsHtml = '<li>No upcoming events scheduled</li>';
 | ||
|                 }
 | ||
|                 $trainerModal.find('.hvac-events-list').html(eventsHtml);
 | ||
|             } else {
 | ||
|                 $trainerModal.find('.hvac-events-list').html('<li>No upcoming events scheduled</li>');
 | ||
|             }
 | ||
|         }).fail(function() {
 | ||
|             $trainerModal.find('.hvac-events-list').html('<li>Unable to load events</li>');
 | ||
|         });
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Handle contact form submission
 | ||
|      */
 | ||
|     function handleContactSubmit(e) {
 | ||
|         e.preventDefault();
 | ||
|         
 | ||
|         const $form = $(this);
 | ||
|         const $submitBtn = $form.find('.hvac-form-submit');
 | ||
|         const $successMsg = $form.find('.hvac-form-success');
 | ||
|         const $errorMsg = $form.find('.hvac-form-error');
 | ||
|         const 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() {
 | ||
|         const 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;
 | ||
|         const $container = $('.hvac-trainer-grid');
 | ||
|         let $pagination = $('.hvac-pagination');
 | ||
|         
 | ||
|         // Show loading state
 | ||
|         $container.addClass('hvac-loading');
 | ||
|         
 | ||
|         // Prepare data
 | ||
|         const data = {
 | ||
|             action: 'hvac_filter_trainers',
 | ||
|             nonce: hvac_find_trainer.nonce,
 | ||
|             page: currentPage,
 | ||
|             filters: {
 | ||
|                 ...activeFilters,
 | ||
|                 search: $('#hvac-trainer-search').val()
 | ||
|             }
 | ||
|         };
 | ||
|         
 | ||
|         // 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('<div class="hvac-pagination"></div>');
 | ||
|                     }
 | ||
|                 }
 | ||
|                 
 | ||
|                 // 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() {
 | ||
|         // Remove the modal-active class to ensure proper hiding
 | ||
|         $filterModal.removeClass('modal-active').fadeOut(300, function() {
 | ||
|             $(this).css('display', 'none');
 | ||
|         });
 | ||
|         $trainerModal.fadeOut(300);
 | ||
|     }
 | ||
| 
 | ||
|     /**
 | ||
|      * Debounce helper function
 | ||
|      */
 | ||
|     function debounce(func, wait) {
 | ||
|         let timeout;
 | ||
|         return function executedFunction(...args) {
 | ||
|             const later = () => {
 | ||
|                 clearTimeout(timeout);
 | ||
|                 func(...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() {
 | ||
|         const hasFilters = Object.keys(activeFilters).length > 0;
 | ||
|         const 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.includes('Find a Trainer')) {
 | ||
|                 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();
 | ||
|                 const profileId = $(this).data('profile-id');
 | ||
|                 showTrainerModal(profileId);
 | ||
|             });
 | ||
|             
 | ||
|             // Update URL without page reload for clean sharing
 | ||
|             const currentUrl = window.location.href;
 | ||
|             if (currentUrl.includes('/profile/') && window.history && window.history.replaceState) {
 | ||
|                 const cleanUrl = currentUrl.split('?')[0]; // Remove any query parameters
 | ||
|                 window.history.replaceState({}, document.title, cleanUrl);
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     // Expose showTrainerModal globally for MapGeo integration
 | ||
|     window.showTrainerModal = showTrainerModal;
 | ||
| 
 | ||
| })(jQuery); |