๐Ÿ“ Fall River, MA
๐Ÿ”

Loading weather data...

โ€บ

MBTA Routes

Transit routes from Fall River to Boston ยท Real-time schedules and predictions

On time Delayed Cancelled
โ†‘

To Boston

Inbound routes

โณ

Loading routesโ€ฆ

MBTA API

โ†“

From Boston

Outbound routes

โณ

Loading routesโ€ฆ

MBTA API

Loading MBTA dataโ€ฆ Data from Massachusetts Bay Transportation Authority
(function() { 'use strict'; // Build-time data embedded so static page shows data without runtime fetch window.FRNA_MBTA_BUILD_DATA = {}; // MBTA API Configuration const MBTA_API_BASE = 'https://api-v3.mbta.com'; const MBTA_API_KEY = '01df058309eb41ad8981a0a6e0be0953'; // Route type mappings const ROUTE_TYPES = { 0: { name: 'Light Rail', class: 'route-light-rail' }, 1: { name: 'Heavy Rail', class: 'route-subway' }, 2: { name: 'Commuter Rail', class: 'route-commuter-rail' }, 3: { name: 'Bus', class: 'route-bus' }, 4: { name: 'Ferry', class: 'route-ferry' } }; // Known Fall River to Boston routes const FALL_RIVER_ROUTES = [ 'CR-Newburyport', 'CR-Kingston', 'CR-Providence', 'CR-Middleborough', 'CR-Greenbush', 'CR-Fairmount', 'CR-Fitchburg', 'CR-Worcester', 'CR-Franklin', 'CR-Haverhill', 'CR-Lowell', 'CR-Needham' ]; function renderMBTABuildData() { var build = window.FRNA_MBTA_BUILD_DATA; if (!build || !build.mbta || !build.mbta.routes || build.mbta.routes.length === 0) return false; var routes = build.mbta.routes; var inboundHtml = []; var outboundHtml = []; var lastUpdated = build.mbta.last_updated || ''; function fmtTime(iso) { if (!iso) return 'โ€”'; try { var d = new Date(iso); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } catch (e) { return iso; } } routes.forEach(function(route) { var name = route.name || route.short_name || route.id || 'Route'; var typeClass = (route.type === 'commuter_rail') ? 'route-commuter-rail' : 'route-bus'; var preds = route.predictions || []; var inboundPreds = preds.filter(function(p) { return p.direction_id === 1; }); var outboundPreds = preds.filter(function(p) { return p.direction_id === 0; }); if (inboundPreds.length > 0) { var nextTime = fmtTime(inboundPreds[0].arrival_time || inboundPreds[0].departure_time); inboundHtml.push('
' + '
' + escapeHtml(name) + '
' + '
' + 'Commuter Rail' + nextTime + '
' + '
On time
'); } if (outboundPreds.length > 0) { var nextTime = fmtTime(outboundPreds[0].arrival_time || outboundPreds[0].departure_time); outboundHtml.push('
' + '
' + escapeHtml(name) + '
' + '
' + 'Commuter Rail' + nextTime + '
' + '
On time
'); } }); var inboundEl = document.getElementById('mbta-inbound-data'); var outboundEl = document.getElementById('mbta-outbound-data'); var statusEl = document.getElementById('mbta-status'); if (inboundEl) inboundEl.innerHTML = inboundHtml.length > 0 ? inboundHtml.join('') : '
๐Ÿš†

No inbound routes

'; if (outboundEl) outboundEl.innerHTML = outboundHtml.length > 0 ? outboundHtml.join('') : '
๐Ÿš†

No outbound routes

'; if (statusEl) statusEl.innerHTML = 'Build-time data ยท ' + (lastUpdated ? 'Updated ' + lastUpdated : '') + 'Data from MBTA'; return true; } function initializeMBTAData() { fetchMBTARoutes(); setInterval(fetchMBTARoutes, 60000); // Update every minute } async function fetchMBTARoutes() { try { const inboundEl = document.getElementById('mbta-inbound-data'); const outboundEl = document.getElementById('mbta-outbound-data'); const statusEl = document.getElementById('mbta-status'); // Update status if (statusEl) { const now = new Date(); statusEl.innerHTML = '' + '' + 'Updated ' + now.toLocaleTimeString() + '' + 'Data from Massachusetts Bay Transportation Authority'; } // Fetch routes that serve Fall River area const routesUrl = `${MBTA_API_BASE}/routes?api_key=${MBTA_API_KEY}&filter[type]=2`; // Commuter Rail only const response = await fetch(routesUrl); if (!response.ok) throw new Error('HTTP ' + response.status); const data = await response.json(); const routes = data.data || []; // Filter routes that might serve Fall River to Boston const relevantRoutes = routes.filter(route => FALL_RIVER_ROUTES.includes(route.id) || route.attributes.long_name?.toLowerCase().includes('providence') || route.attributes.long_name?.toLowerCase().includes('newburyport') ); // For each route, fetch schedules/predictions const routePromises = relevantRoutes.map(route => fetchRouteDetails(route)); const routeDetails = await Promise.allSettled(routePromises); const processedRoutes = routeDetails .filter(result => result.status === 'fulfilled') .map(result => result.value) .filter(route => route !== null); // Separate inbound (to Boston) and outbound (from Boston) const inboundRoutes = processedRoutes.filter(route => route.direction === 'inbound' || route.direction_id === 0 ); const outboundRoutes = processedRoutes.filter(route => route.direction === 'outbound' || route.direction_id === 1 ); // Render routes inboundEl.innerHTML = inboundRoutes.length > 0 ? inboundRoutes.map(route => generateRouteCard(route)).join('') : '
๐Ÿš†

No inbound routes

Check back soon

'; outboundEl.innerHTML = outboundRoutes.length > 0 ? outboundRoutes.map(route => generateRouteCard(route)).join('') : '
๐Ÿš†

No outbound routes

Check back soon

'; } catch (error) { ; showMBTAError('Cannot load MBTA data. ' + (error.message || '')); } } async function fetchRouteDetails(route) { try { // Fetch predictions for this route const predictionsUrl = `${MBTA_API_BASE}/predictions?api_key=${MBTA_API_KEY}&filter[route]=${route.id}&include=trip,stop&sort=departure_time`; const response = await fetch(predictionsUrl); if (!response.ok) return null; const data = await response.json(); const predictions = data.data || []; // Get next few departures const now = new Date(); const upcoming = predictions .filter(pred => { const depTime = new Date(pred.attributes.departure_time); return depTime > now; }) .slice(0, 3); return { ...route, predictions: upcoming, nextDeparture: upcoming.length > 0 ? upcoming[0] : null }; } catch (error) { ; return route; // Return route without predictions } } function generateRouteCard(route) { const routeType = ROUTE_TYPES[route.attributes.type] || { name: 'Unknown', class: 'route-bus' }; const routeName = route.attributes.long_name || route.attributes.short_name || route.id; let status = 'on-time'; let statusText = 'On time'; let nextTime = 'โ€”'; if (route.nextDeparture) { const depTime = new Date(route.nextDeparture.attributes.departure_time); const now = new Date(); const diffMinutes = Math.floor((depTime - now) / (1000 * 60)); if (diffMinutes < 0) { status = 'delayed'; statusText = 'Departed'; } else if (diffMinutes <= 5) { status = 'delayed'; statusText = 'Soon'; } else { nextTime = depTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } } return `
${escapeHtml(routeName)}
${routeType.name} ${nextTime}
${statusText}
`; } function escapeHtml(s) { if (!s) return ''; var div = document.createElement('div'); div.textContent = s; return div.innerHTML; } function showMBTAError(message) { const statusEl = document.getElementById('mbta-status'); if (statusEl) { statusEl.innerHTML = '' + '' + 'Error loading data'; } const msg = (typeof message === 'string') ? message : 'Error loading data'; const safe = (msg + '').replace(//g, '>'); const inboundEl = document.getElementById('mbta-inbound-data'); const outboundEl = document.getElementById('mbta-outbound-data'); const errHtml = '
' + '
โš 
' + '

' + safe + '

' + '

Retrying every 60 sec

'; if (inboundEl) inboundEl.innerHTML = errHtml; if (outboundEl) outboundEl.innerHTML = errHtml; } function runMBTAInit() { // Show build-time data immediately so static page is never blank if (renderMBTABuildData()) { // Still start live refresh; it will overwrite when API succeeds initializeMBTAData(); } else { initializeMBTAData(); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runMBTAInit); } else { runMBTAInit(); } })();