1+ console . log ( '🚀 Events.js loading...' ) ;
2+
3+ // Simple constants
4+ const EVENTS_JSON_URL = '/assets/data/events.json' ;
5+
6+ // Simple build function - 2019 style
7+ const 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 = `
83+ <div class="card-section">
84+ <div class="event-top-section clearfix">
85+ <div class="sub-head">
86+ <div class="event-header-container">
87+ <div class="event-flag">${ flagImage } </div>
88+ <h3 class="event-header">${ event . name } </h3>
89+ </div>
90+ </div>
91+ </div>
92+ <div class="event-maindetails clearfix">
93+ <div class="event-details-left">
94+ <span class="emphasizedNumber">${ startDate . day } </span>
95+ <p><b>${ startDate . toFormat ( 'MMMM' ) } </b></p>
96+ </div>
97+ <div class="event-details-right">
98+ <div class="textbox" style="padding-top:8px">
99+ <p>${ location } </p>
100+ <p class="event-details">${ startDate . toFormat ( 'cccc' ) } , ${ timeDisplay } </p>
101+ <div class="event-buttons" style="display: flex; gap: 10px; flex-wrap: wrap;">
102+ <a href="${ event . url } " target="_blank" rel="noopener noreferrer"
103+ class="btn">
104+ View Event
105+ </a>
106+ <a href="${ joinUrl } " target="_blank" rel="noopener noreferrer"
107+ class="btn btn-grn">
108+ Attend
109+ </a>
110+ </div>
111+ </div>
112+ </div>
113+ </div>
114+ </div>
115+ ` ;
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 = `
130+ <div class="card-section">
131+ <div class="event-top-section clearfix">
132+ <div class="sub-head">
133+ <h3 class="event-header">${ event . name } </h3>
134+ </div>
135+ </div>
136+ <div class="event-maindetails clearfix">
137+ <div class="event-details-right">
138+ <div class="textbox" style="padding-top:8px">
139+ <p>Error loading event details</p>
140+ <div class="event-buttons" style="display: flex; gap: 10px; flex-wrap: wrap;">
141+ <a href="${ event . url } " target="_blank" rel="noopener noreferrer"
142+ class="btn">
143+ View Event
144+ </a>
145+ <a href="${ joinUrl } " target="_blank" rel="noopener noreferrer"
146+ class="btn btn-grn">
147+ Attend
148+ </a>
149+ </div>
150+ </div>
151+ </div>
152+ </div>
153+ </div>
154+ ` ;
155+ eventList . appendChild ( simpleContainer ) ;
156+ }
157+ } ) ;
158+
159+ console . log ( '✅ All events built with Foundation cards!' ) ;
160+ } ;
161+
162+ // Fetch events
163+ const fetchEvents = async ( ) => {
164+ try {
165+ console . log ( '📡 Fetching events...' ) ;
166+ const response = await fetch ( EVENTS_JSON_URL ) ;
167+ if ( ! response . ok ) throw new Error ( 'HTTP ' + response . status ) ;
168+ const events = await response . json ( ) ;
169+ console . log ( '📦 Got' , events . length , 'events' ) ;
170+ return events ;
171+ } catch ( error ) {
172+ console . error ( '💥 Fetch error:' , error ) ;
173+ return [ ] ;
174+ }
175+ } ;
176+
177+ // Main init
178+ const init = async ( ) => {
179+ console . log ( '🎯 Initializing...' ) ;
180+
181+ // Check Luxon
182+ if ( typeof luxon === 'undefined' ) {
183+ console . error ( '💥 Luxon not available!' ) ;
184+ return ;
185+ }
186+ console . log ( '✅ Luxon available' ) ;
187+
188+ const events = await fetchEvents ( ) ;
189+ buildEvents ( events ) ;
190+ } ;
191+
192+ // Start when DOM ready
193+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
194+ console . log ( '📄 DOM ready!' ) ;
195+
196+ const eventList = document . getElementById ( 'eventList' ) ;
197+ if ( ! eventList ) {
198+ console . error ( '💥 eventList not found!' ) ;
199+ return ;
200+ }
201+
202+ eventList . innerHTML = '<div class="column"><h3>Loading Missing Maps events...</h3></div>' ;
203+
204+ init ( ) ;
205+ } ) ;
206+
207+ console . log ( '✅ Events.js loaded!' ) ;
0 commit comments