1var routes; 2var platforms; 3var selectedStopId; 4var refreshTimeoutId; 5 6function ensureArray(item) { 7 return _.isArray(item) ? item: [item]; 8} 9 10function formatStop(stop) { 11 return { 12 name: stop['@attributes'].Name, 13 stopId: stop['@attributes'].PlatformTag 14 } 15} 16 17jQuery(function($) { 18 function populateRoutes(routes) { 19 $('#real-time-arrival-route option').not(':first-child').remove(); 20 $('#real-time-arrival-route').append(routes.map(route => { 21 return $('<option>').val(route.No).text(route.No + ' - ' + route.Name); 22 })) 23 .prop('disabled', false); 24 25 clearStops(); 26 } 27 28 function populateStops(stops) { 29 clearStops(); 30 $('#real-time-arrival-stop').append(stops.map(function(stop) { 31 return $('<option>').val(stop.Tag).text(stop.Name); 32 })) 33 .prop('disabled', false); 34 } 35 36 function resetRoutes() { 37 $('#real-time-arrival-route option').first().prop('selected', true); 38 } 39 40 function clearStops() { 41 $('#real-time-arrival-stop option').not(':first-child').remove(); 42 $('#real-time-arrival-stop').prop('disabled', true); 43 selectedStopId = undefined; 44 clearResults(); 45 } 46 47 function clearResults() { 48 $('.real-time-arrival-results').empty(); 49 } 50 51 function normalizeArrivals(results) { 52 // Current API shape: 53 // results.Projects[].Routes[].Destinations[].Trips[]: { ET: number|null, ST: ... } 54 // where ET appears to be minutes until arrival. 55 if (!results || !Array.isArray(results.Projects) || results.Projects.length === 0) return []; 56 57 const arrivals = []; 58 results.Projects.forEach(project => { 59 (project.Routes || []).forEach(route => { 60 (route.Destinations || []).forEach(destination => { 61 const minutes = (destination.Trips || []) 62 .map(t => (t && typeof t.ET === 'number' ? t.ET : null)) 63 .filter(m => typeof m === 'number' && Number.isFinite(m) && m >= 0) 64 .sort((a, b) => a - b); 65 66 if (minutes.length === 0) return; 67 arrivals.push({ 68 routeNo: route.No, 69 destinationName: destination.Name, 70 minutes 71 }); 72 }); 73 }); 74 }); 75 76 return arrivals; 77 } 78 79 function getTimes() { 80 if (!selectedStopId) { 81 return 82 } 83 84 if (refreshTimeoutId) { 85 clearTimeout(refreshTimeoutId); 86 refreshTimeoutId = undefined; 87 } 88 89 fetch(`/wp-content/themes/kartbus/realtime.php?type=times&stop_id=${selectedStopId}`) 90 .then(response => { 91 if (!response.ok) { 92 throw new Error('Network response was not ok'); 93 } 94 return response.json(); 95 }) 96 .then(results => { 97 clearResults(); 98 99 if (!results || !results.Projects) { 100 console.error('unable to fetch times'); 101 $('.real-time-arrival-results').append($('<h3>').text('Unable to fetch times')); 102 return; 103 } 104 105 const selectedStop = platforms.find(platform => platform.Tag === selectedStopId); 106 107 $('.real-time-arrival-results').append($('<h3>').text(selectedStop.Name)); 108 $('.real-time-arrival-results').append($('<div>').text(`Stop ID: ${selectedStop.No}`)); 109 110 const arrivals = normalizeArrivals(results); 111 if (arrivals.length === 0) { 112 $('.real-time-arrival-results').append($('<p>').text('No times')) 113 return; 114 } 115 116 arrivals.forEach(arrival => {
117 var container = $('<div>').addClass('d-flex align-items-center real-time-arrival-prediction'); 118 var route = routes.find(route => route.No === arrival.routeNo); 119 if (!route) { 120 // If route list is out of sync with arrivals, still render something useful. 121 route = { No: arrival.routeNo, Name: arrival.destinationName || '', Color: '#6c757d' }; 122 } 123 124 $('<div>').text(route.No).addClass('route-number').css('background-color', route.Color).appendTo(container); 125 126 $('<div>').text(route.Name).addClass('ml-2 mr-2').appendTo(container); 127 128 arrival.minutes.forEach(minute => { 129 $('<div>').addClass('ml-2') 130 .append($('<span>').text(minute).addClass('real-time-arrival-prediction-time')) 131 .append($('<span>').text('min')) 132 .appendTo(container); 133 }); 134 135 $('.real-time-arrival-results').append(container); 136 }) 137 }); 138 139 // Refresh every minute; 140 refreshTimeoutId = setTimeout(getTimes, 60 * 1000); 141 } 142 143 fetch('/wp-content/themes/kartbus/realtime.php?type=routes') 144 .then(response => { 145 if (!response.ok) { 146 throw new Error('Network response was not ok'); 147 } 148 return response.json(); 149 }) 150 .then(results => { 151 if (!results || !results.Projects || !results.Platforms) { 152 console.error('unable to fetch routes'); 153 return; 154 } 155 156 routes = results.Projects[0].Routes; 157 platforms = results.Platforms; 158 159 populateRoutes(routes); 160 }) 161 .catch(error => console.log(error)) 162 163 $('#real-time-arrival-route').change(function() { 164 var routeNo = $('#real-time-arrival-route').val(); 165 166 if (routeNo === '') { 167 clearStops(); 168 return; 169 } 170 171 var route = _.find(routes, {No: routeNo}); 172 var stops = route.Platforms.map(stopId => { 173 return platforms.find(platform => platform.Tag === stopId) 174 }); 175 populateStops(stops); 176 }); 177 178 $('#real-time-arrival-stop').change(function() { 179 var stopId = $('#real-time-arrival-stop').val(); 180 181 if (stopId === '') { 182 return; 183 } 184 185 selectedStopId = parseInt(stopId, 10) 186 getTimes(); 187 }); 188 189 $('input[name="arrival_type"]').change(function() { 190 var type = $('input[name="arrival_type"]:checked').val(); 191 192 $('#' + type).prop('hidden', false) 193 .siblings().prop('hidden', true); 194 195 resetRoutes(); 196 clearStops(); 197 }); 198 199 $('#real-time-arrival-stop-id-form').submit(function(e) { 200 e.preventDefault(); 201 202 var stopId = $('#real-time-arrival-stop-id').val(); 203 204 if (!stopId) { 205 alert('Please enter a stop ID'); 206 return; 207 } 208 209 var selectedStop = platforms.find(platform => platform.No === stopId); 210 211 if (!selectedStop) { 212 alert('Invalid stop ID'); 213 return; 214 } 215 216 selectedStopId = selectedStop.Tag; 217 getTimes(); 218 }) 219});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.