@@ -5,81 +5,81 @@ const EVENTS_JSON_URL = '/assets/data/events.json';
55
66// Simple build function - 2019 style
77const buildEvents = ( events ) => {
8- console . log ( '📋 Building' , events . length , 'events...' ) ;
9-
10- const eventList = document . getElementById ( 'eventList' ) ;
11- if ( ! eventList ) {
12- console . error ( '❌ No eventList found!' ) ;
13- return ;
14- }
15-
16- // Clear and show progress
17- eventList . innerHTML = '' ;
18-
19- if ( events . length === 0 ) {
20- eventList . innerHTML = '<div class="column"><h3>No upcoming Missing Maps events found.</h3></div>' ;
21- return ;
22- }
23-
24- // Process each event using old template style in Foundation cards
25- events . forEach ( ( event , index ) => {
26- console . log ( '📅 Processing:' , event . name ) ;
27-
28- try {
29- // Parse date with Luxon
30- const startDate = luxon . DateTime . fromISO ( event . date . start ) ;
31- const endDate = event . date . end ? luxon . DateTime . fromISO ( event . date . end ) : null ;
32-
33- // Extract event ID from URL for join link
34- const urlParts = event . url . split ( '/' ) . filter ( part => part . length > 0 ) ; // Remove empty parts
35- const eventId = urlParts [ urlParts . length - 1 ] ; // Get last non-empty part
36- const joinUrl = `https://osmcal.org/event/${ eventId } /join` ;
37-
38- // Get country code for flag
39- const getCountryCode = ( locationString ) => {
40- const countryMap = {
41- 'United States' : 'us' , 'USA' : 'us' , 'US' : 'us' ,
42- 'United Kingdom' : 'gb' , 'UK' : 'gb' , 'Britain' : 'gb' ,
43- 'Germany' : 'de' , 'Deutschland' : 'de' ,
44- 'France' : 'fr' , 'République française' : 'fr' ,
45- 'Canada' : 'ca' , 'España' : 'es' , 'Spain' : 'es' ,
46- 'Italy' : 'it' , 'Italia' : 'it' , 'Netherlands' : 'nl' ,
47- 'Switzerland' : 'ch' , 'Czech Republic' : 'cz' , 'Czechia' : 'cz' ,
48- 'Slovakia' : 'sk' , 'Norway' : 'no' , 'Norge' : 'no'
49- } ;
50-
51- // Extract country from location string (last part after last comma)
52- if ( ! locationString ) return 'online' ; // Default for online events
53- const parts = locationString . split ( ',' ) ;
54- const country = parts [ parts . length - 1 ] . trim ( ) ;
55-
56- return countryMap [ country ] || country . toLowerCase ( ) . substring ( 0 , 2 ) ;
57- } ;
58-
59- // Handle events with or without location data
60- const hasLocation = event . location && event . location . detailed ;
61- const countryCode = hasLocation ? getCountryCode ( event . location . detailed ) : 'online' ;
62-
63- // Format times like the old template
64- const startTime = startDate . toFormat ( 'HH:mm' ) ;
65- const endTime = endDate ? endDate . toFormat ( 'HH:mm' ) : '' ;
66- const timeDisplay = endTime ? `${ startTime } - ${ endTime } ` : startTime ;
67-
68- // Create location string - handle online events
69- const location = hasLocation
70- ? `${ event . location . venue } , ${ event . location . detailed } `
71- : 'Online Event' ;
72-
73- // Determine flag image or Missing Maps logo for online events
74- const flagImage = hasLocation
75- ? `<img class="event-images" src="/assets/graphics/flags/4x3/${ countryCode } .svg" width="72px" />`
76- : '<img class="event-images mm-logo" src="/assets/graphics/content/MMlogo-Outlined.svg" width="72px" />' ;
77-
78- // Create event container - Foundation card with old template style
79- const eventContainer = document . createElement ( 'div' ) ;
80- eventContainer . className = 'column event-card' ;
81-
82- eventContainer . innerHTML = `
8+ console . log ( '📋 Building' , events . length , 'events...' ) ;
9+
10+ const eventList = document . getElementById ( 'eventList' ) ;
11+ if ( ! eventList ) {
12+ console . error ( '❌ No eventList found!' ) ;
13+ return ;
14+ }
15+
16+ // Clear and show progress
17+ eventList . innerHTML = '' ;
18+
19+ if ( events . length === 0 ) {
20+ eventList . innerHTML = '<div class="column"><h3>No upcoming Missing Maps events found.</h3></div>' ;
21+ return ;
22+ }
23+
24+ // Process each event using old template style in Foundation cards
25+ events . forEach ( ( event , _index ) => {
26+ console . log ( '📅 Processing:' , event . name ) ;
27+
28+ try {
29+ // Parse date with Luxon
30+ const startDate = luxon . DateTime . fromISO ( event . date . start ) ;
31+ const endDate = event . date . end ? luxon . DateTime . fromISO ( event . date . end ) : null ;
32+
33+ // Extract event ID from URL for join link
34+ const urlParts = event . url . split ( '/' ) . filter ( part => part . length > 0 ) ; // Remove empty parts
35+ const eventId = urlParts [ urlParts . length - 1 ] ; // Get last non-empty part
36+ const joinUrl = `https://osmcal.org/event/${ eventId } /join` ;
37+
38+ // Get country code for flag
39+ const getCountryCode = ( locationString ) => {
40+ const countryMap = {
41+ 'United States' : 'us' , 'USA' : 'us' , 'US' : 'us' ,
42+ 'United Kingdom' : 'gb' , 'UK' : 'gb' , 'Britain' : 'gb' ,
43+ 'Germany' : 'de' , 'Deutschland' : 'de' ,
44+ 'France' : 'fr' , 'République française' : 'fr' ,
45+ 'Canada' : 'ca' , 'España' : 'es' , 'Spain' : 'es' ,
46+ 'Italy' : 'it' , 'Italia' : 'it' , 'Netherlands' : 'nl' ,
47+ 'Switzerland' : 'ch' , 'Czech Republic' : 'cz' , 'Czechia' : 'cz' ,
48+ 'Slovakia' : 'sk' , 'Norway' : 'no' , 'Norge' : 'no'
49+ } ;
50+
51+ // Extract country from location string (last part after last comma)
52+ if ( ! locationString ) { return 'online' ; } // Default for online events
53+ const parts = locationString . split ( ',' ) ;
54+ const country = parts [ parts . length - 1 ] . trim ( ) ;
55+
56+ return countryMap [ country ] || country . toLowerCase ( ) . substring ( 0 , 2 ) ;
57+ } ;
58+
59+ // Handle events with or without location data
60+ const hasLocation = event . location && event . location . detailed ;
61+ const countryCode = hasLocation ? getCountryCode ( event . location . detailed ) : 'online' ;
62+
63+ // Format times like the old template
64+ const startTime = startDate . toFormat ( 'HH:mm' ) ;
65+ const endTime = endDate ? endDate . toFormat ( 'HH:mm' ) : '' ;
66+ const timeDisplay = endTime ? `${ startTime } - ${ endTime } ` : startTime ;
67+
68+ // Create location string - handle online events
69+ const location = hasLocation
70+ ? `${ event . location . venue } , ${ event . location . detailed } `
71+ : 'Online Event' ;
72+
73+ // Determine flag image or Missing Maps logo for online events
74+ const flagImage = hasLocation
75+ ? `<img class="event-images" src="/assets/graphics/flags/4x3/${ countryCode } .svg" width="72px" />`
76+ : '<img class="event-images mm-logo" src="/assets/graphics/content/MMlogo-Outlined.svg" width="72px" />' ;
77+
78+ // Create event container - Foundation card with old template style
79+ const eventContainer = document . createElement ( 'div' ) ;
80+ eventContainer . className = 'column event-card' ;
81+
82+ eventContainer . innerHTML = `
8383 <div class="card-section">
8484 <div class="event-top-section clearfix">
8585 <div class="sub-head">
@@ -113,20 +113,20 @@ const buildEvents = (events) => {
113113 </div>
114114 </div>
115115 ` ;
116-
117- eventList . appendChild ( eventContainer ) ;
118- console . log ( '✅ Added event:' , event . name ) ;
119- } catch ( error ) {
120- console . error ( '❌ Error processing event:' , event . name , error ) ;
121- // Extract event ID for join link even in error case
122- const urlParts = event . url . split ( '/' ) . filter ( part => part . length > 0 ) ;
123- const eventId = urlParts [ urlParts . length - 1 ] ;
124- const joinUrl = `https://osmcal.org/event/${ eventId } /join` ;
125-
126- // Still add a simple version using old template style
127- const simpleContainer = document . createElement ( 'div' ) ;
128- simpleContainer . className = 'column event-card' ;
129- simpleContainer . innerHTML = `
116+
117+ eventList . appendChild ( eventContainer ) ;
118+ console . log ( '✅ Added event:' , event . name ) ;
119+ } catch ( error ) {
120+ console . error ( '❌ Error processing event:' , event . name , error ) ;
121+ // Extract event ID for join link even in error case
122+ const urlParts = event . url . split ( '/' ) . filter ( part => part . length > 0 ) ;
123+ const eventId = urlParts [ urlParts . length - 1 ] ;
124+ const joinUrl = `https://osmcal.org/event/${ eventId } /join` ;
125+
126+ // Still add a simple version using old template style
127+ const simpleContainer = document . createElement ( 'div' ) ;
128+ simpleContainer . className = 'column event-card' ;
129+ simpleContainer . innerHTML = `
130130 <div class="card-section">
131131 <div class="event-top-section clearfix">
132132 <div class="sub-head">
@@ -152,44 +152,44 @@ const buildEvents = (events) => {
152152 </div>
153153 </div>
154154 ` ;
155- eventList . appendChild ( simpleContainer ) ;
156- }
157- } ) ;
158-
159- console . log ( '✅ All events built with Foundation cards!' ) ;
155+ eventList . appendChild ( simpleContainer ) ;
156+ }
157+ } ) ;
158+
159+ console . log ( '✅ All events built with Foundation cards!' ) ;
160160} ;
161161
162162// Show last updated timestamp
163163const showLastUpdated = ( buildTime ) => {
164- try {
165- const lastUpdate = luxon . DateTime . fromISO ( buildTime ) ;
166- const now = luxon . DateTime . now ( ) ;
167- const hoursSinceUpdate = now . diff ( lastUpdate , 'hours' ) . hours ;
168-
169- console . log ( `📅 Events last updated: ${ lastUpdate . toRelative ( ) } (${ hoursSinceUpdate . toFixed ( 1 ) } hours ago)` ) ;
170-
171- // Only show visual indicator if data is getting old (more than 12 hours)
172- if ( hoursSinceUpdate > 12 ) {
173- const eventList = document . getElementById ( 'eventList' ) ;
174- if ( eventList && hoursSinceUpdate > 24 ) {
175- const notice = document . createElement ( 'div' ) ;
176- notice . className = 'text-center' ;
177- notice . style . cssText = 'font-size: 0.9em; color: #666; margin-bottom: 20px;' ;
178- notice . innerHTML = `<em>Events last updated ${ lastUpdate . toRelative ( ) } </em>` ;
179- eventList . parentNode . insertBefore ( notice , eventList ) ;
180- }
181- }
182- } catch ( error ) {
183- console . warn ( 'Could not parse build time:' , error ) ;
184- }
164+ try {
165+ const lastUpdate = luxon . DateTime . fromISO ( buildTime ) ;
166+ const now = luxon . DateTime . now ( ) ;
167+ const hoursSinceUpdate = now . diff ( lastUpdate , 'hours' ) . hours ;
168+
169+ console . log ( `📅 Events last updated: ${ lastUpdate . toRelative ( ) } (${ hoursSinceUpdate . toFixed ( 1 ) } hours ago)` ) ;
170+
171+ // Only show visual indicator if data is getting old (more than 12 hours)
172+ if ( hoursSinceUpdate > 12 ) {
173+ const eventList = document . getElementById ( 'eventList' ) ;
174+ if ( eventList && hoursSinceUpdate > 24 ) {
175+ const notice = document . createElement ( 'div' ) ;
176+ notice . className = 'text-center' ;
177+ notice . style . cssText = 'font-size: 0.9em; color: #666; margin-bottom: 20px;' ;
178+ notice . innerHTML = `<em>Events last updated ${ lastUpdate . toRelative ( ) } </em>` ;
179+ eventList . parentNode . insertBefore ( notice , eventList ) ;
180+ }
181+ }
182+ } catch ( error ) {
183+ console . warn ( 'Could not parse build time:' , error ) ;
184+ }
185185} ;
186186
187187// Fallback display when events can't be loaded
188188const showEventsFallback = ( ) => {
189- const eventList = document . getElementById ( 'eventList' ) ;
190- if ( ! eventList ) return ;
191-
192- eventList . innerHTML = `
189+ const eventList = document . getElementById ( 'eventList' ) ;
190+ if ( ! eventList ) { return ; }
191+
192+ eventList . innerHTML = `
193193 <div class="column">
194194 <div class="textbox-container" style="text-align: center; padding: 40px 20px;">
195195 <h3>Events Temporarily Unavailable</h3>
@@ -211,70 +211,70 @@ const showEventsFallback = () => {
211211
212212// Fetch events with enhanced error handling and metadata support
213213const fetchEvents = async ( ) => {
214- try {
215- console . log ( '📡 Fetching events from' , EVENTS_JSON_URL ) ;
216- const response = await fetch ( EVENTS_JSON_URL ) ;
217- if ( ! response . ok ) throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
218-
219- const eventsData = await response . json ( ) ;
220- console . log ( '📦 Raw events data:' , eventsData ) ;
221-
222- // Handle both old format (array) and new format (object with metadata)
223- let events , buildTime ;
224-
225- if ( Array . isArray ( eventsData ) ) {
226- // Old format - just an array of events
227- events = eventsData ;
228- buildTime = null ;
229- console . log ( '📦 Got' , events . length , 'events (legacy format)' ) ;
230- } else {
231- // New format - object with metadata
232- events = eventsData . events || [ ] ;
233- buildTime = eventsData . buildTime ;
234- console . log ( '📦 Got' , events . length , 'events (new format with metadata)' ) ;
235-
236- if ( buildTime ) {
237- console . log ( '🕒 Events built at:' , buildTime ) ;
238- showLastUpdated ( buildTime ) ;
239- }
240- }
241-
242- return events ;
243- } catch ( error ) {
244- console . error ( '💥 Fetch error:' , error ) ;
245- showEventsFallback ( ) ;
246- return [ ] ;
247- }
214+ try {
215+ console . log ( '📡 Fetching events from' , EVENTS_JSON_URL ) ;
216+ const response = await fetch ( EVENTS_JSON_URL ) ;
217+ if ( ! response . ok ) { throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ; }
218+
219+ const eventsData = await response . json ( ) ;
220+ console . log ( '📦 Raw events data:' , eventsData ) ;
221+
222+ // Handle both old format (array) and new format (object with metadata)
223+ let events , buildTime ;
224+
225+ if ( Array . isArray ( eventsData ) ) {
226+ // Old format - just an array of events
227+ events = eventsData ;
228+ buildTime = null ;
229+ console . log ( '📦 Got' , events . length , 'events (legacy format)' ) ;
230+ } else {
231+ // New format - object with metadata
232+ events = eventsData . events || [ ] ;
233+ buildTime = eventsData . buildTime ;
234+ console . log ( '📦 Got' , events . length , 'events (new format with metadata)' ) ;
235+
236+ if ( buildTime ) {
237+ console . log ( '🕒 Events built at:' , buildTime ) ;
238+ showLastUpdated ( buildTime ) ;
239+ }
240+ }
241+
242+ return events ;
243+ } catch ( error ) {
244+ console . error ( '💥 Fetch error:' , error ) ;
245+ showEventsFallback ( ) ;
246+ return [ ] ;
247+ }
248248} ;
249249
250250// Main init
251251const init = async ( ) => {
252- console . log ( '🎯 Initializing...' ) ;
253-
254- // Check Luxon
255- if ( typeof luxon === 'undefined' ) {
256- console . error ( '💥 Luxon not available!' ) ;
257- return ;
258- }
259- console . log ( '✅ Luxon available' ) ;
260-
261- const events = await fetchEvents ( ) ;
262- buildEvents ( events ) ;
252+ console . log ( '🎯 Initializing...' ) ;
253+
254+ // Check Luxon
255+ if ( typeof luxon === 'undefined' ) {
256+ console . error ( '💥 Luxon not available!' ) ;
257+ return ;
258+ }
259+ console . log ( '✅ Luxon available' ) ;
260+
261+ const events = await fetchEvents ( ) ;
262+ buildEvents ( events ) ;
263263} ;
264264
265265// Start when DOM ready
266266document . addEventListener ( 'DOMContentLoaded' , ( ) => {
267- console . log ( '📄 DOM ready!' ) ;
268-
269- const eventList = document . getElementById ( 'eventList' ) ;
270- if ( ! eventList ) {
271- console . error ( '💥 eventList not found!' ) ;
272- return ;
273- }
274-
275- eventList . innerHTML = '<div class="column"><h3>Loading Missing Maps events...</h3></div>' ;
276-
277- init ( ) ;
267+ console . log ( '📄 DOM ready!' ) ;
268+
269+ const eventList = document . getElementById ( 'eventList' ) ;
270+ if ( ! eventList ) {
271+ console . error ( '💥 eventList not found!' ) ;
272+ return ;
273+ }
274+
275+ eventList . innerHTML = '<div class="column"><h3>Loading Missing Maps events...</h3></div>' ;
276+
277+ init ( ) ;
278278} ) ;
279279
280280console . log ( '✅ Events.js loaded!' ) ;
0 commit comments