/** * MapGeo Safety System * Prevents MapGeo and third-party map plugins from crashing the page * Works in all browsers including Safari and Chrome * * @package HVAC_Community_Events * @since 2.0.0 */ (function() { 'use strict'; // Safety configuration const config = window.HVAC_MapGeo_Config || { maxRetries: 3, retryDelay: 2000, timeout: 10000, fallbackEnabled: true, debugMode: false }; const log = config.debugMode ? console.log.bind(console) : () => {}; const error = config.debugMode ? console.error.bind(console) : () => {}; log('[MapGeo Safety] Initializing protection system'); /** * Resource Load Monitor * Tracks and manages external script loading */ class ResourceLoadMonitor { constructor() { this.resources = new Map(); this.criticalResources = [ 'amcharts', 'mapgeo', 'interactive-geo-maps', 'map-widget' ]; this.setupMonitoring(); } setupMonitoring() { // Monitor script loading const originalAppendChild = Element.prototype.appendChild; const self = this; Element.prototype.appendChild = function(element) { if (element.tagName === 'SCRIPT' && element.src) { self.monitorScript(element); } return originalAppendChild.call(this, element); }; // Monitor existing scripts document.querySelectorAll('script[src]').forEach(script => { this.monitorScript(script); }); } monitorScript(script) { const src = script.src; const isCritical = this.criticalResources.some(resource => src.toLowerCase().includes(resource) ); if (isCritical) { log('[MapGeo Safety] Monitoring critical resource:', src); const timeoutId = setTimeout(() => { error('[MapGeo Safety] Resource timeout:', src); this.handleResourceFailure(src); }, config.timeout); script.addEventListener('load', () => { clearTimeout(timeoutId); log('[MapGeo Safety] Resource loaded:', src); this.resources.set(src, 'loaded'); }); script.addEventListener('error', () => { clearTimeout(timeoutId); error('[MapGeo Safety] Resource failed:', src); this.handleResourceFailure(src); }); } } handleResourceFailure(src) { this.resources.set(src, 'failed'); // Check if this is a MapGeo CDN resource if (src.includes('cdn') || src.includes('amcharts')) { log('[MapGeo Safety] CDN resource failed, activating fallback'); this.activateFallback(); } } activateFallback() { // Hide map container const mapContainers = document.querySelectorAll( '.igm-map-container, [class*="mapgeo"], [id*="map-"], .map-widget-container' ); mapContainers.forEach(container => { container.style.display = 'none'; }); // Show fallback content const fallback = document.getElementById('hvac-map-fallback'); if (fallback) { fallback.style.display = 'block'; } // Dispatch custom event window.dispatchEvent(new CustomEvent('hvac:mapgeo:fallback', { detail: { reason: 'resource_failure' } })); } } /** * MapGeo API Wrapper * Safely wraps MapGeo API calls */ class MapGeoAPIWrapper { constructor() { this.wrapAPIs(); } wrapAPIs() { // Wrap potential MapGeo global functions const mapGeoAPIs = [ 'MapGeoWidget', 'InteractiveGeoMaps', 'IGM', 'mapWidget' ]; mapGeoAPIs.forEach(api => { if (typeof window[api] !== 'undefined') { this.wrapAPI(api); } // Set up getter to wrap when loaded Object.defineProperty(window, `_original_${api}`, { value: window[api], writable: true }); Object.defineProperty(window, api, { get() { return window[`_wrapped_${api}`] || window[`_original_${api}`]; }, set(value) { window[`_original_${api}`] = value; window[`_wrapped_${api}`] = new Proxy(value, { construct(target, args) { try { return new target(...args); } catch (e) { error('[MapGeo Safety] Construction error:', e); return {}; } }, apply(target, thisArg, args) { try { return target.apply(thisArg, args); } catch (e) { error('[MapGeo Safety] Execution error:', e); return null; } } }); } }); }); } wrapAPI(apiName) { const original = window[apiName]; window[apiName] = new Proxy(original, { construct(target, args) { try { log(`[MapGeo Safety] Creating ${apiName} instance`); return new target(...args); } catch (e) { error(`[MapGeo Safety] Failed to create ${apiName}:`, e); return {}; } }, apply(target, thisArg, args) { try { log(`[MapGeo Safety] Calling ${apiName}`); return target.apply(thisArg, args); } catch (e) { error(`[MapGeo Safety] Failed to call ${apiName}:`, e); return null; } } }); } } /** * DOM Ready Safety * Ensures MapGeo only runs when DOM is safe */ class DOMReadySafety { constructor() { this.setupSafety(); } setupSafety() { // Intercept jQuery ready calls that might contain MapGeo code if (typeof jQuery !== 'undefined') { const originalReady = jQuery.fn.ready; jQuery.fn.ready = function(callback) { const wrappedCallback = function() { try { // Check if MapGeo elements exist before running const hasMapElements = document.querySelector( '.igm-map-container, [class*="mapgeo"], [id*="map-"]' ); if (hasMapElements || !callback.toString().includes('map')) { return callback.apply(this, arguments); } else { log('[MapGeo Safety] Skipping map-related ready callback - no map elements found'); } } catch (e) { error('[MapGeo Safety] Error in ready callback:', e); } }; return originalReady.call(this, wrappedCallback); }; } } } /** * Initialize all safety systems */ function initializeSafetySystems() { // Only initialize on pages with potential maps if (!document.querySelector('[class*="map"], [id*="map"]')) { log('[MapGeo Safety] No map elements detected, skipping initialization'); return; } // Initialize monitors new ResourceLoadMonitor(); new MapGeoAPIWrapper(); new DOMReadySafety(); // Set up periodic health check let healthCheckCount = 0; const healthCheckInterval = setInterval(() => { healthCheckCount++; // Check if map loaded successfully const mapLoaded = document.querySelector('.igm-map-loaded, .mapgeo-loaded, .map-initialized'); if (mapLoaded) { log('[MapGeo Safety] Map loaded successfully'); clearInterval(healthCheckInterval); } else if (healthCheckCount >= 10) { // After 10 seconds, consider it failed error('[MapGeo Safety] Map failed to load after 10 seconds'); clearInterval(healthCheckInterval); // Activate fallback if configured if (config.fallbackEnabled) { const monitor = new ResourceLoadMonitor(); monitor.activateFallback(); } } }, 1000); log('[MapGeo Safety] All safety systems initialized'); } // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeSafetySystems); } else { // DOM already loaded initializeSafetySystems(); } // Expose safety API for debugging window.HVACMapGeoSafety = { config: config, reinitialize: initializeSafetySystems, activateFallback: () => { const monitor = new ResourceLoadMonitor(); monitor.activateFallback(); } }; })();