mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:31:08 -05:00
- Implemented font color configuration based on numeric values in various sections. - Added resizing functionality for input fields to accommodate text length. - Initialized filters on document ready for improved user interaction. - Created visualization for profile data using fetched dot format. - Enhanced SQL detail page with click event handling for row navigation. - Ensured consistent highlighting for code blocks across multiple pages.
553 lines
16 KiB
JavaScript
553 lines
16 KiB
JavaScript
/**
|
|
* ThrillWiki Map Integration - Master Integration Script
|
|
*
|
|
* This module coordinates all map components, handles initialization order,
|
|
* manages component communication, and provides a unified API
|
|
*/
|
|
|
|
class MapIntegration {
|
|
constructor(options = {}) {
|
|
this.options = {
|
|
autoInit: true,
|
|
enableLogging: true,
|
|
enablePerformanceMonitoring: true,
|
|
initTimeout: 10000,
|
|
retryAttempts: 3,
|
|
components: {
|
|
maps: true,
|
|
filters: true,
|
|
roadtrip: true,
|
|
geolocation: true,
|
|
markers: true,
|
|
htmx: true,
|
|
mobileTouch: true,
|
|
darkMode: true
|
|
},
|
|
...options
|
|
};
|
|
|
|
this.components = {};
|
|
this.initOrder = [
|
|
'darkMode',
|
|
'mobileTouch',
|
|
'maps',
|
|
'markers',
|
|
'filters',
|
|
'geolocation',
|
|
'htmx',
|
|
'roadtrip'
|
|
];
|
|
this.initialized = false;
|
|
this.initStartTime = null;
|
|
this.errors = [];
|
|
|
|
if (this.options.autoInit) {
|
|
this.init();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize all map components
|
|
*/
|
|
async init() {
|
|
this.initStartTime = performance.now();
|
|
this.log('Starting map integration initialization...');
|
|
|
|
try {
|
|
// Wait for DOM to be ready
|
|
await this.waitForDOM();
|
|
|
|
// Initialize components in order
|
|
await this.initializeComponents();
|
|
|
|
// Connect components
|
|
this.connectComponents();
|
|
|
|
// Setup global event handlers
|
|
this.setupGlobalHandlers();
|
|
|
|
// Verify integration
|
|
this.verifyIntegration();
|
|
|
|
this.initialized = true;
|
|
this.logPerformance();
|
|
this.log('Map integration initialized successfully');
|
|
|
|
// Emit ready event
|
|
this.emitEvent('mapIntegrationReady', {
|
|
components: Object.keys(this.components),
|
|
initTime: performance.now() - this.initStartTime
|
|
});
|
|
|
|
} catch (error) {
|
|
this.handleInitError(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for DOM to be ready
|
|
*/
|
|
waitForDOM() {
|
|
return new Promise((resolve) => {
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', resolve);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize components in the correct order
|
|
*/
|
|
async initializeComponents() {
|
|
for (const componentName of this.initOrder) {
|
|
if (!this.options.components[componentName]) {
|
|
this.log(`Skipping ${componentName} (disabled)`);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
await this.initializeComponent(componentName);
|
|
this.log(`✓ ${componentName} initialized`);
|
|
} catch (error) {
|
|
this.error(`✗ Failed to initialize ${componentName}:`, error);
|
|
this.errors.push({ component: componentName, error });
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize individual component
|
|
*/
|
|
async initializeComponent(componentName) {
|
|
switch (componentName) {
|
|
case 'darkMode':
|
|
if (window.DarkModeMaps) {
|
|
this.components.darkMode = window.darkModeMaps || new DarkModeMaps();
|
|
}
|
|
break;
|
|
|
|
case 'mobileTouch':
|
|
if (window.MobileTouchSupport) {
|
|
this.components.mobileTouch = window.mobileTouchSupport || new MobileTouchSupport();
|
|
}
|
|
break;
|
|
|
|
case 'maps':
|
|
// Look for existing map instances or create new ones
|
|
if (window.thrillwikiMap) {
|
|
this.components.maps = window.thrillwikiMap;
|
|
} else if (window.ThrillWikiMap) {
|
|
const mapContainer = document.getElementById('map-container');
|
|
if (mapContainer) {
|
|
this.components.maps = new ThrillWikiMap('map-container');
|
|
window.thrillwikiMap = this.components.maps;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'markers':
|
|
if (window.MapMarkers && this.components.maps) {
|
|
this.components.markers = window.mapMarkers || new MapMarkers(this.components.maps);
|
|
}
|
|
break;
|
|
|
|
case 'filters':
|
|
if (window.MapFilters) {
|
|
const filterForm = document.getElementById('map-filters');
|
|
if (filterForm) {
|
|
this.components.filters = window.mapFilters || new MapFilters('map-filters');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'geolocation':
|
|
if (window.UserLocation) {
|
|
this.components.geolocation = window.userLocation || new UserLocation();
|
|
}
|
|
break;
|
|
|
|
case 'htmx':
|
|
if (window.HTMXMapIntegration && typeof htmx !== 'undefined') {
|
|
this.components.htmx = window.htmxMapIntegration || new HTMXMapIntegration();
|
|
}
|
|
break;
|
|
|
|
case 'roadtrip':
|
|
if (window.RoadTripPlanner) {
|
|
const roadtripContainer = document.getElementById('roadtrip-planner');
|
|
if (roadtripContainer) {
|
|
this.components.roadtrip = window.roadTripPlanner || new RoadTripPlanner('roadtrip-planner');
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Connect components together
|
|
*/
|
|
connectComponents() {
|
|
this.log('Connecting components...');
|
|
|
|
// Connect maps to other components
|
|
if (this.components.maps) {
|
|
// Connect to dark mode
|
|
if (this.components.darkMode) {
|
|
this.components.darkMode.registerMapInstance(this.components.maps);
|
|
}
|
|
|
|
// Connect to mobile touch
|
|
if (this.components.mobileTouch) {
|
|
this.components.mobileTouch.registerMapInstance(this.components.maps);
|
|
}
|
|
|
|
// Connect to geolocation
|
|
if (this.components.geolocation) {
|
|
this.components.geolocation.connectToMap(this.components.maps);
|
|
}
|
|
|
|
// Connect to road trip planner
|
|
if (this.components.roadtrip) {
|
|
this.components.roadtrip.connectToMap(this.components.maps);
|
|
}
|
|
}
|
|
|
|
// Connect filters to other components
|
|
if (this.components.filters) {
|
|
// Connect to maps
|
|
if (this.components.maps) {
|
|
this.components.filters.connectToMap(this.components.maps);
|
|
}
|
|
|
|
// Connect to HTMX
|
|
if (this.components.htmx) {
|
|
this.components.htmx.connectToFilter(this.components.filters);
|
|
}
|
|
}
|
|
|
|
// Connect HTMX to maps
|
|
if (this.components.htmx && this.components.maps) {
|
|
this.components.htmx.connectToMap(this.components.maps);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup global event handlers
|
|
*/
|
|
setupGlobalHandlers() {
|
|
// Handle global map events
|
|
document.addEventListener('mapDataUpdate', (e) => {
|
|
this.handleMapDataUpdate(e.detail);
|
|
});
|
|
|
|
// Handle filter changes
|
|
document.addEventListener('filterChange', (e) => {
|
|
this.handleFilterChange(e.detail);
|
|
});
|
|
|
|
// Handle theme changes
|
|
document.addEventListener('themeChanged', (e) => {
|
|
this.handleThemeChange(e.detail);
|
|
});
|
|
|
|
// Handle orientation changes
|
|
document.addEventListener('orientationChanged', (e) => {
|
|
this.handleOrientationChange(e.detail);
|
|
});
|
|
|
|
// Handle visibility changes for performance
|
|
document.addEventListener('visibilitychange', () => {
|
|
this.handleVisibilityChange();
|
|
});
|
|
|
|
// Handle errors
|
|
window.addEventListener('error', (e) => {
|
|
if (this.isMapRelatedError(e)) {
|
|
this.handleGlobalError(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handle map data updates
|
|
*/
|
|
handleMapDataUpdate(data) {
|
|
if (this.components.maps) {
|
|
this.components.maps.updateMarkers(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle filter changes
|
|
*/
|
|
handleFilterChange(filters) {
|
|
if (this.components.maps) {
|
|
this.components.maps.updateFilters(filters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle theme changes
|
|
*/
|
|
handleThemeChange(themeData) {
|
|
// All components should already be listening for this
|
|
// Just log for monitoring
|
|
this.log(`Theme changed to ${themeData.newTheme}`);
|
|
}
|
|
|
|
/**
|
|
* Handle orientation changes
|
|
*/
|
|
handleOrientationChange(orientationData) {
|
|
// Invalidate map sizes after orientation change
|
|
if (this.components.maps) {
|
|
setTimeout(() => {
|
|
this.components.maps.invalidateSize();
|
|
}, 300);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle visibility changes
|
|
*/
|
|
handleVisibilityChange() {
|
|
const isHidden = document.hidden;
|
|
|
|
// Pause/resume location watching
|
|
if (this.components.geolocation) {
|
|
if (isHidden) {
|
|
this.components.geolocation.stopWatching();
|
|
} else if (this.components.geolocation.options.watchPosition) {
|
|
this.components.geolocation.startWatching();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if error is map-related
|
|
*/
|
|
isMapRelatedError(error) {
|
|
const mapKeywords = ['leaflet', 'map', 'marker', 'tile', 'geolocation', 'htmx'];
|
|
const errorMessage = error.message ? error.message.toLowerCase() : '';
|
|
const errorStack = error.error && error.error.stack ? error.error.stack.toLowerCase() : '';
|
|
|
|
return mapKeywords.some(keyword =>
|
|
errorMessage.includes(keyword) || errorStack.includes(keyword)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handle global errors
|
|
*/
|
|
handleGlobalError(error) {
|
|
this.error('Global map error:', error);
|
|
this.errors.push({ type: 'global', error });
|
|
|
|
// Emit error event
|
|
this.emitEvent('mapError', { error, timestamp: Date.now() });
|
|
}
|
|
|
|
/**
|
|
* Verify integration is working
|
|
*/
|
|
verifyIntegration() {
|
|
const issues = [];
|
|
|
|
// Check required components
|
|
if (this.options.components.maps && !this.components.maps) {
|
|
issues.push('Maps component not initialized');
|
|
}
|
|
|
|
// Check component connections
|
|
if (this.components.maps && this.components.darkMode) {
|
|
if (!this.components.darkMode.mapInstances.has(this.components.maps)) {
|
|
issues.push('Maps not connected to dark mode');
|
|
}
|
|
}
|
|
|
|
// Check DOM elements
|
|
const mapContainer = document.getElementById('map-container');
|
|
if (this.components.maps && !mapContainer) {
|
|
issues.push('Map container not found in DOM');
|
|
}
|
|
|
|
if (issues.length > 0) {
|
|
this.warn('Integration issues found:', issues);
|
|
}
|
|
|
|
return issues.length === 0;
|
|
}
|
|
|
|
/**
|
|
* Handle initialization errors
|
|
*/
|
|
handleInitError(error) {
|
|
this.error('Map integration initialization failed:', error);
|
|
|
|
// Emit error event
|
|
this.emitEvent('mapIntegrationError', {
|
|
error,
|
|
errors: this.errors,
|
|
timestamp: Date.now()
|
|
});
|
|
|
|
// Try to initialize what we can
|
|
this.attemptPartialInit();
|
|
}
|
|
|
|
/**
|
|
* Attempt partial initialization
|
|
*/
|
|
attemptPartialInit() {
|
|
this.log('Attempting partial initialization...');
|
|
|
|
// Try to initialize at least the core map
|
|
if (!this.components.maps && window.ThrillWikiMap) {
|
|
try {
|
|
const mapContainer = document.getElementById('map-container');
|
|
if (mapContainer) {
|
|
this.components.maps = new ThrillWikiMap('map-container');
|
|
this.log('✓ Core map initialized in fallback mode');
|
|
}
|
|
} catch (error) {
|
|
this.error('✗ Fallback map initialization failed:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get component by name
|
|
*/
|
|
getComponent(name) {
|
|
return this.components[name] || null;
|
|
}
|
|
|
|
/**
|
|
* Get all components
|
|
*/
|
|
getAllComponents() {
|
|
return { ...this.components };
|
|
}
|
|
|
|
/**
|
|
* Check if integration is ready
|
|
*/
|
|
isReady() {
|
|
return this.initialized;
|
|
}
|
|
|
|
/**
|
|
* Get initialization status
|
|
*/
|
|
getStatus() {
|
|
return {
|
|
initialized: this.initialized,
|
|
components: Object.keys(this.components),
|
|
errors: this.errors,
|
|
initTime: this.initStartTime ? performance.now() - this.initStartTime : null
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Emit custom event
|
|
*/
|
|
emitEvent(eventName, detail) {
|
|
const event = new CustomEvent(eventName, { detail });
|
|
document.dispatchEvent(event);
|
|
}
|
|
|
|
/**
|
|
* Log performance metrics
|
|
*/
|
|
logPerformance() {
|
|
if (!this.options.enablePerformanceMonitoring) return;
|
|
|
|
const initTime = performance.now() - this.initStartTime;
|
|
const componentCount = Object.keys(this.components).length;
|
|
|
|
this.log(`Performance: ${initTime.toFixed(2)}ms to initialize ${componentCount} components`);
|
|
|
|
// Send to analytics if available
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'map_integration_performance', {
|
|
event_category: 'performance',
|
|
value: Math.round(initTime),
|
|
custom_map: {
|
|
component_count: componentCount,
|
|
errors: this.errors.length
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logging methods
|
|
*/
|
|
log(message, ...args) {
|
|
if (this.options.enableLogging) {
|
|
console.log(`[MapIntegration] ${message}`, ...args);
|
|
}
|
|
}
|
|
|
|
warn(message, ...args) {
|
|
if (this.options.enableLogging) {
|
|
console.warn(`[MapIntegration] ${message}`, ...args);
|
|
}
|
|
}
|
|
|
|
error(message, ...args) {
|
|
if (this.options.enableLogging) {
|
|
console.error(`[MapIntegration] ${message}`, ...args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroy integration
|
|
*/
|
|
destroy() {
|
|
// Destroy all components
|
|
Object.values(this.components).forEach(component => {
|
|
if (component && typeof component.destroy === 'function') {
|
|
try {
|
|
component.destroy();
|
|
} catch (error) {
|
|
this.error('Error destroying component:', error);
|
|
}
|
|
}
|
|
});
|
|
|
|
this.components = {};
|
|
this.initialized = false;
|
|
this.log('Map integration destroyed');
|
|
}
|
|
}
|
|
|
|
// Auto-initialize map integration
|
|
let mapIntegration;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Only initialize if we have map-related elements
|
|
const hasMapElements = document.querySelector('#map-container, .map-container, [data-map], [data-roadtrip]');
|
|
|
|
if (hasMapElements) {
|
|
mapIntegration = new MapIntegration();
|
|
window.mapIntegration = mapIntegration;
|
|
}
|
|
});
|
|
|
|
// Global API for external access
|
|
window.ThrillWikiMaps = {
|
|
getIntegration: () => mapIntegration,
|
|
isReady: () => mapIntegration && mapIntegration.isReady(),
|
|
getComponent: (name) => mapIntegration ? mapIntegration.getComponent(name) : null,
|
|
getStatus: () => mapIntegration ? mapIntegration.getStatus() : { initialized: false }
|
|
};
|
|
|
|
// Export for module systems
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = MapIntegration;
|
|
} else {
|
|
window.MapIntegration = MapIntegration;
|
|
} |