PageSourceSearch

https://trains.fyi/_next/static/chunks/pages/freight-88703ea7077a7089.js

js trains.fyi collected 2026-09-24 09:54:49 UTC 26,901 bytes, 10 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9945],{14788:(e,t,r)=>{"use strict";function a(e,t){if(e?.observed_route?.length)return{type:"MultiLineString",coordinates:e.observed_route};let r=e?.predicted_route;if(!r?.length)return null;let a=[...r[r.length-1],...(e.lookahead_path||[]).slice(1)];if(!t||a.length<2)return{type:"MultiLineString",coordinates:r};let n=Math.cos(t[1]*Math.PI/180),s={distance:1/0};for(let e=1;e<a.length;e++){let r=a[e-1],i=a[e],l=(i[0]-r[0])*n,o=i[1]-r[1],d=l*l+o*o,c=d?Math.max(0,Math.min(1,((t[0]-r[0])*n*l+(t[1]-r[1])*o)/d)):0,u=[r[0]+(i[0]-r[0])*c,r[1]+(i[1]-r[1])*c],g=((t[0]-u[0])*n)**2+(t[1]-u[1])**2;g<=s.distance&&(s={distance:g,point:u,i:e})}return s.distance>(100/111320)**2?{type:"MultiLineString",coordinates:r}:{type:"MultiLineString",coordinates:[...r.slice(0,-1),[...a.slice(0,s.i),s.point]]}}r.d(t,{D:()=>a})},23854:(e,t,r)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/freight",function(){return r(49986)}])},35176:(e,t,r)=>{"use strict";r.d(t,{A:()=>n});var a=r(37876);function n({train:e}){let t="gps"===e.position_source;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"mt-5 text-sm text-gray-600",children:t?"GPS position reported by the railway. The marker stays at the latest fix until a new report arrives.":"Last reported railway location. The train may have moved since this report; its position between reporting points is unknown."}),(0,a.jsxs)("dl",{className:"mt-5 space-y-4 text-sm",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Direction of travel"}),(0,a.jsx)("dd",{children:Number.isFinite(e.current_bearing)?`${Math.round(e.current_bearing)}\xb0 \xb7 estimated from ${t?"recent GPS fixes":"successive reporting locations"}`:"Unknown — waiting for movement between reports"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Last reported"}),(0,a.jsx)("dd",{children:new Date(e.last_seen).toLocaleString()})]}),e.location_name&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Reporting location"}),(0,a.jsx)("dd",{children:e.location_name})]}),Number.isFinite(e.effective_speed_kmh)&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Reported speed"}),(0,a.jsxs)("dd",{children:[Math.round(e.effective_speed_kmh)," km/h"]})]}),e.operator&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Operator code"}),(0,a.jsx)("dd",{children:e.operator})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Report expires"}),(0,a.jsx)("dd",{children:new Date(e.valid_until).toLocaleTimeString()})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Position history"}),(0,a.jsxs)("dd",{children:[e.history_count||1," reports",e.history_since?` since ${new Date(e.history_since).toLocaleTimeString()}`:""]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{className:"text-gray-500",children:"Source"}),(0,a.jsxs)("dd",{children:[(0,a.jsx)("a",{className:"text-blue-800 underline",href:e.source_url,target:"_blank",rel:"noreferrer",children:e.source_name}),"banenor"===e.source?(0,a.jsxs)(a.Fragment,{children:[" \xb7 ",(0,a.jsx)("a",{className:"underline",href:"https://data.norge.no/nlod/en/1.0",target:"_blank",rel:"noreferrer",children:"Norwegian Licence for Open Government Data (NLOD)"})]}):t?" \xb7 CC BY 4.0":" \xb7 Open Government Licence"]})]})]}),(0,a.jsx)("p",{className:"mt-4 text-xs text-gray-500",children:"The dashed trail joins recorded positions from the last three hours. It does not show the exact track between reports. Gaps mark missing data or location corrections."}),"banenor"===e.source&&(0,a.jsxs)("p",{className:"mt-4 text-xs text-gray-500",children:["Contains data under NLOD distributed by Bane NOR. Station coordinates are extracted from ",(0,a.jsx)("a",{className:"underline",href:"https://data.norge.no/en/datasets/e30d98a2-0edf-4fb3-b71d-dd8819410c9c/jernbane-banenettverk",target:"_blank",rel:"noreferrer",children:"Bane NOR’s railway network dataset"}),"; headings and trails are calculated by trains.fyi. Reports may be sparse; unconfirmed schedules are omitted."]}),"networkrail"===e.source&&(0,a.jsxs)("p",{className:"mt-4 text-xs text-gray-500",children:["Reporting-point coordinates: ",(0,a.jsx)("a",{className:"underline",href:"https://github.com/davwheat/YATIPLOCL#data-sources",target:"_blank",rel:"noreferrer",children:"YATIPLOCL and its contributing sources"}),"."]})]})}},42891:(e,t,r)=>{"use strict";r.d(t,{b:()=>g,o:()=>c});
1var a=r(75656),n=r(14232),s=r(57090),i=r(45434),l=r(22128),o=r(1300);async function d(e){let t=await fetch(e);if(!t.ok)throw Error("Freight details are unavailable");return t.json()}function c(e){return(0,a.Ay)(e?`/api/freight/trains/${encodeURIComponent(e)}`:null,d,{refreshInterval:3e4,revalidateOnFocus:!1,shouldRetryOnError:!1})}async function u([,e,t]){for(let r of e)try{if(r.startsWith("camera:")){let e=r.split(":")[1],t=await d(`/api/sightings/${encodeURIComponent(e)}`),a=t.file_urls?.frame||t.file_urls?.gif;if(a)return{url:a,caption:t.camera_name||"Camera sighting",href:`/sightings?id=${encodeURIComponent(e)}`}}else if(r.startsWith("user:")){if(!t||o.j.currentUser?.uid!==t)continue;let e=(await (0,i.x7)((0,i.H9)(o.db,"sightings",r.slice(5)))).data(),a=e?.photoStoragePaths?.[0]||e?.photoStoragePath;if(a&&"approved"===e.photoStatus)return{url:await (0,l.qk)((0,l.KR)((0,l.c7)(o.A),a)),caption:"Original sighting photo"}}}catch{}return null}function g(e){let[t,r]=(0,n.useState)(null);(0,n.useEffect)(()=>(0,s.hg)(o.j,e=>r(e?.uid||null)),[]);let i=[...new Set((e?.sightings||[]).map(e=>e.source_id||(e.description?.match(/trainspotter id=([a-zA-Z0-9_]+)/)?.[1]?`camera:${e.description.match(/trainspotter id=([a-zA-Z0-9_]+)/)[1]}`:null)).filter(Boolean))];return(0,a.Ay)(i.length?["freight-sighting-photo",i,t]:null,u,{revalidateOnFocus:!1,shouldRetryOnError:!1})}},49986:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>I});var a=r(37876),n=r(69609),s=r.n(n),i=r(89099),l=r(48564),o=r(35176),d=r(42891),c=r(14788),u=r(60371),g=r(28168),h=r(14232),m=r(75656);r(4402);var x=r(85392),p=r(74),f=r(93408),b=r(73865),j=r(91024),y=r(59531),v=r(77604);let k=/\bid=([a-f0-9]{12})\b/i,w=/detection:(\d{8}_\d{6}_\d+)/i,N=e=>u.A.get(e).then(e=>e.data),_=e=>{if(!e)return null;let t=k.exec(e);if(t)return t[1].toLowerCase();let r=w.exec(e);return r?r[1]:null},L=e=>{let t=e?.current_lat??e?.last_snapped_lat,r=e?.current_lon??e?.last_snapped_lon;return Number.isFinite(t)&&Number.isFinite(r)?{lat:t,lng:r}:null},S=e=>Number.isFinite(e?.effective_speed_kmh)?e.effective_speed_kmh:e?.last_speed_kmh,C=({className:e="h-4 w-4"})=>(0,a.jsx)("svg",{viewBox:"0 0 24 24",className:e,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:(0,a.jsx)("path",{d:"M19 12H5M12 19l-7-7 7-7"})}),M=({className:e="h-4 w-4"})=>(0,a.jsx)("svg",{viewBox:"0 0 24 24",className:e,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:(0,a.jsx)("path",{d:"M9 6l6 6-6 6"})}),A=({className:e="h-3.5 w-3.5"})=>
1(0,a.jsx)("svg",{viewBox:"0 0 24 24",className:e,fill:"currentColor","aria-hidden":"true",children:(0,a.jsx)("path",{d:"M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z"})}),F=({className:e="h-3.5 w-3.5"})=>(0,a.jsx)("svg",{viewBox:"0 0 24 24",className:e,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:(0,a.jsx)("path",{d:"M14 3h7v7M21 3l-9 9M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"})}),T=({className:e=""})=>(0,a.jsx)("div",{className:`freight-shimmer ${e}`}),E=()=>(0,a.jsx)("article",{className:"overflow-hidden rounded-xl bg-white ring-1 ring-gray-200",children:(0,a.jsxs)("div",{className:"flex items-center gap-3 px-4 py-3",children:[(0,a.jsx)(T,{className:"h-9 w-9 rounded-full"}),(0,a.jsxs)("div",{className:"flex-1 space-y-2",children:[(0,a.jsx)(T,{className:"h-2.5 w-28 rounded"}),(0,a.jsx)(T,{className:"h-3.5 w-40 rounded"}),(0,a.jsx)(T,{className:"h-3 w-32 rounded"})]})]})}),R=()=>{let{t:e}=(0,b.B)();return(0,a.jsx)("div",{className:"pointer-events-none absolute inset-0 z-20 flex items-center justify-center bg-white/55 backdrop-blur-[2px]",children:(0,a.jsxs)("div",{className:"flex items-center gap-2.5 rounded-full bg-white/95 px-3.5 py-2 shadow-md ring-1 ring-gray-200",children:[(0,a.jsxs)("span",{className:"relative inline-flex h-4 w-4 items-center justify-center",children:[(0,a.jsx)("span",{className:"absolute inset-0 animate-ping rounded-full bg-amber-700/30"}),(0,a.jsx)("span",{className:"relative inline-block h-4 w-4 animate-spin rounded-full border-2 border-amber-200 border-t-amber-700"})]}),(0,a.jsx)("span",{className:"text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-700",children:e("freightLoadingMap")})]})})},$="freight-train-route",P="freight-train-route-line",D=({trains:e,selectedTrainId:t,onSelectTrain:r})=>{let n=(0,h.useRef)(null),s=(0,h.useRef)(null),i=(0,h.useRef)(new Map),l=(0,h.useRef)(null),o=(0,h.useRef)(!1),[u,m]=(0,h.useState)(!1),x=(0,h.useRef)(r),p=(0,h.useRef)(e),f=(0,h.useMemo)(()=>e.find(e=>e.id===t)||null,[e,t]),{data:b}=(0,d.o)(f?.source?f.id:null);return(0,h.useEffect)(()=>{x.current=r},[r]),(0,h.useEffect)(()=>{p.current=e},[e]),(0,h.useEffect)(()=>{if(s.current||!n.current)return;let e=new g.A.Map({container:n.current,style:(0,y.pI)(),center:[-73.6,45.5],zoom:9,attributionControl:{compact:!0}});return e.addControl(new g.A.NavigationControl({visualizePitch:!1,showCompass:!1}),"top-right"),e.on("load",()=>{e.getSource($)||(e.addSource($,{type:"geojson",data:{type:"FeatureCollection",features:[]}}),e.addLayer({id:"freight-train-route-casing",type:"line",source:$,layout:{"line-join":"round","line-cap":"round"},paint:{"line-color":"#ffffff","line-width":8,"line-opacity":.9}}),e.addLayer({id:P,type:"line",source:$,layout:{"line-join":"round","line-cap":"round"},paint:{"line-color":"#1d4ed8","line-width":4,"line-opacity":.95}})),m(!0)}),s.current=e,()=>{e.remove(),s.current=null}},[]),(0,h.useEffect)(()=>{if(!u||!s.current)return;let t=s.current,r=i.current,a=new Set;for(let[n,s]of(e.forEach(e=>{let n,s,i=L(e);if(!i)return;a.add(e.id);let l=Number.isFinite(n=e?.current_bearing??e?.last_bearing)?n:e?.source?null:0,o=r.get(e.id);if(o){o.marker.setLngLat([i.lng,i.lat]),o.bearing!==l&&(o.element.style.setProperty("--bearing",`${l??0}deg`),o.bearing=l),o.element.classList.toggle("freight-marker--unknown-heading",null==l);return}let d=((s=document.createElement("div")).className="freight-marker",s.innerHTML=`
2    <div class="freight-marker__inner">
3      <span class="freight-marker__pulse" aria-hidden="true"></span>
4      <span class="freight-marker__icon">
5        <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
6          <path d="M12 2 L20 20 L12 16 L4 20 Z" fill="currentColor"/>
7        </svg>
8      </span>
9    </div>
10  `,s);d.style.setProperty("--bearing",`${l??0}deg`),d.classList.toggle("freight-marker--unknown-heading",null==l),d.addEventListener("click",t=>{t.stopPropagation(),x.current?.(e.id)});let c=new g.A.Marker({element:d,anchor:"center"}).setLngLat([i.lng,i.lat]).addTo(t);r.set(e.id,{marker:c,element:d,bearing:l})}),r))a.has(n)||(s.marker.remove(),r.delete(n));if(e.length>0&&!o.current){let r=new g.A.LngLatBounds;
10if(e.forEach(e=>{let t=L(e);t&&r.extend([t.lng,t.lat])}),!r.isEmpty())try{t.fitBounds(r,{padding:80,maxZoom:11,duration:0,animate:!1})}catch{}o.current=!0}},[e,u]),(0,h.useEffect)(()=>{u&&i.current.forEach((e,r)=>{r===t?e.element.classList.add("freight-marker--selected"):e.element.classList.remove("freight-marker--selected")})},[t,u]),(0,h.useEffect)(()=>{if(!u||!s.current||null==t)return;let e=p.current.find(e=>e.id===t),r=e?L(e):null;r&&s.current.flyTo({center:[r.lng,r.lat],zoom:Math.max(s.current.getZoom(),11),speed:1.4,curve:1.6,essential:!0})},[t,u]),(0,h.useEffect)(()=>{if(!u||!s.current)return;let e=s.current,t=e.getSource($);if(!t)return;if(!f){t.setData({type:"FeatureCollection",features:[]}),l.current&&(l.current.remove(),l.current=null);return}if(e.setPaintProperty(P,"line-dasharray",f.source?[2,2]:[1,0]),f.source){let e=b?.id===f.id?(0,c.D)(b):null;t.setData({type:"FeatureCollection",features:e?[{type:"Feature",geometry:e,properties:{train_id:f.id}}]:[]}),l.current&&(l.current.remove(),l.current=null);return}let r=f.last_snapped_lat,a=f.last_snapped_lon,n=Number.isFinite(r)&&Number.isFinite(a),i=L(f),o=[];if(n&&o.push([a,r]),i&&(!n||i.lng!==a||i.lat!==r)&&o.push([i.lng,i.lat]),o.length>=2?t.setData({type:"FeatureCollection",features:[{type:"Feature",geometry:{type:"LineString",coordinates:o},properties:{train_id:f.id}}]}):t.setData({type:"FeatureCollection",features:[]}),l.current&&(l.current.remove(),l.current=null),n){let t=(0,j.l)(),n=document.createElement("div");n.className="freight-last-sighting",n.innerHTML='<span class="freight-last-sighting__dot" aria-hidden="true"></span>',n.title=t("freightLastSightedHere"),l.current=new g.A.Marker({element:n,anchor:"center"}).setLngLat([a,r]).addTo(e)}},[f,b,u]),(0,a.jsx)("div",{ref:n,className:"h-full w-full"})},O=({train:e,onActivate:t})=>{let{t:r}=(0,b.B)(),n=S(e),s=Number.isFinite(n)&&n>0,i=e.sighting_count||0,l=e.last_seen,o=s?`${e.source?"":"~"}${Math.round(n)} km/h`:e.source?0===n?"Stopped at last report":"Speed unavailable":r("freightStoppedOrCoasting"),d=i>0?` \xb7 ${1===i?r("freightSightingSingular").replace("{n}",i):r("freightSightingPlural").replace("{n}",i)}`:"";return(0,a.jsx)("article",{onClick:t,className:"group relative cursor-pointer overflow-hidden rounded-xl bg-white ring-1 ring-gray-200 transition-all duration-200 hover:shadow-md hover:ring-gray-300",children:(0,a.jsxs)("div",{className:"flex items-center gap-3 px-4 py-3",children:[(0,a.jsx)("span",{className:"inline-flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-amber-100 text-amber-800 ring-1 ring-amber-200",children:(0,a.jsx)("svg",{viewBox:"0 0 24 24",width:"14",height:"14","aria-hidden":"true",children:(0,a.jsx)("path",{d:"M12 2 L20 20 L12 16 L4 20 Z",fill:"currentColor"})})}),(0,a.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,a.jsxs)("p",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-gray-500",children:[(0,a.jsx)("span",{className:"inline-block h-1.5 w-1.5 rounded-full bg-amber-500 align-middle"}),(0,a.jsx)("span",{className:"ml-1.5 align-middle",children:e.name||r("freightTrainLabel").replace("{id}",e.id)})]}),(0,a.jsxs)("p",{className:"mt-0.5 text-sm font-semibold leading-snug text-gray-900",children:[o,d]}),l&&(0,a.jsx)("p",{className:"mt-0.5 text-xs text-gray-600",children:r("freightLastSeen").replace("{time}",(0,v.ak)(l))})]}),(0,a.jsx)(M,{className:"h-4 w-4 flex-shrink-0 text-gray-300 transition-colors group-hover:text-gray-500"})]})})},B=({status:e,train:t,sighting:r,detection:n,onBack:s})=>{let i,{t:l}=(0,b.B)();if("loading"===e)i=(0,a.jsxs)("div",{className:"flex flex-col items-center gap-3 px-8 py-12 text-center text-sm text-gray-600",children:[(0,a.jsx)("span",{className:"inline-block h-5 w-5 animate-spin rounded-full border-2 border-gray-300 border-t-blue-700","aria-hidden":"true"}),(0,a.jsx)("span",{children:l("freightLoadingSighting")})]});else if(t?.source)i=(0,a.jsxs)("div",{className:"p-5",children:[(0,a.jsx)("h2",{className:"font-semibold",children:t.name}),(0,a.jsx)(o.A,{train:t})]});else if("missing"!==e&&r){let e=n?.file_urls?.gif,s=n?.file_urls?.frame,o=e||s,d=n?.camera_name||"Camera",c=(n?.tracks?.[0]?.train_type||"").toLowerCase(),u="freight"===c||"passenger"===c?c:null,g=S(t),h=Number.isFinite(g)&&g>0?` \xb7 ~${Math.round(g)} km/h`:"",m=n?.id,x=l("freight"===u?"freightFreightTrain":"freightTrain");i=(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"aspect-video w-full overflow-hidden bg-black",children:o?(0,a.jsx)("img",{src:o,alt:`${d} clip`,className:"block h-full w-full object-cover"}):(0,a.jsx)("div",{className:"h-full w-full bg-gray-800"})}),(0,a.jsxs)("div",{className:"px-5 py-4",children:[(0,a.jsxs)("p",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-gray-500",children:[(0,a.jsx)("span",{className:`inline-block h-1.5 w-1.5 rounded-full align-middle ${"freight"===u?"bg-amber-500":"passenger"===u?"bg-sky-500":"bg-gray-400"}`}),(0,a.jsxs)("span",{className:"ml-1.5 align-middle",children:[l("freightTrainLabel").replace("{id}",t?.id),r?.created_at?` \xb7 ${(0,v.ak)(r.created_at)}`:""]})]}),(0,a.jsx)("h3",{className:"mt-1.5 text-[15px] font-semibold leading-snug text-gray-900 sm:text-base",children:l("freightSpotted").replace("{type}",x).replace("{speed}",h)}),(0,a.jsxs)("p",{className:"mt-1.5 inline-flex items-center gap-1 text-xs text-gray-600",children:[(0,a.jsx)(A,{className:"h-3 w-3 flex-shrink-0 text-gray-400"}),(0,a.jsx)("span",{className:"truncate",children:d})]}),m&&(0,a.jsxs)("a",{href:`/sightings?id=${m}`,className:"mt-4 inline-flex items-center gap-1.5 text-sm font-medium text-blue-800 hover:text-blue-900 hover:underline",children:[l("freightViewInFeed"),(0,a.jsx)(F,{})]})]})]})}else i=(0,a.jsxs)("div",{className:"px-5 py-10 text-center text-sm",children:[(0,a.jsx)("p",{className:"text-base font-semibold text-gray-900",children:l("freightNoSightingTitle")}),(0,a.jsx)("p",{className:"mt-1.5 text-gray-600",children:l("freightNoSightingDesc")})]});return(0,a.jsxs)("div",{className:"overflow-hidden rounded-xl bg-white shadow-sm ring-1 ring-gray-200",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between border-b border-gray-200 px-3 py-2",children:[(0,a.jsxs)("button",{type:"button",onClick:s,className:"inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900",children:[(0,a.jsx)(C,{}),l("freightAllTrains")]}),t?.id!=null&&(0,a.jsxs)("span",{className:"text-[11px] font-medium uppercase tracking-[0.16em] text-gray-500",children:["#",t.id]})]}),i]})};function I(){let e=(0,i.useRouter)(),[t,r]=(0,h.useState)(()=>Date.now());(0,h.useEffect)(()=>{let e=setInterval(()=>r(Date.now()),1e3);return()=>clearInterval(e)},[]);let{t:n}=(0,b.B)(),[o,d]=(0,h.useState)(null),c=(0,h.useRef)(new Map),[u,g]=(0,h.useState)({status:"idle",train:null,sighting:null,detection:null});(0,h.useEffect)(()=>{window.gtag&&window.gtag("event","page_view",{page_title:"Freight | trains.fyi",page_location:window.location.href,page_path:"/freight/"})},[]);let{data:j,error:y,isLoading:v,isValidating:k}=(0,m.Ay)("/api/freight/trains",N,{refreshInterval:3e4,revalidateOnFocus:!1,dedupingInterval:15e3,keepPreviousData:!0}),w=(Array.isArray(j)?j:[]).map(e=>(0,l._f)(e,t)?"1":"0").join(""),S=(0,h.useMemo)(()=>Array.isArray(j)?j.filter((e,t)=>
10null!==L(e)&&"1"===w[t]):[],[j,w]),C=(0,h.useMemo)(()=>[...S].sort((e,t)=>{let r=new Date(e.last_seen||0).getTime()||0;return(new Date(t.last_seen||0).getTime()||0)-r}),[S]),M=(0,h.useCallback)(async e=>{d(e),g({status:"loading",train:null,sighting:null,detection:null});let t=c.current.get(e);if(t)return void g(t);try{let t=await fetch(`/api/freight/trains/${e}`);if(!t.ok)return void g({status:"missing",train:null,sighting:null,detection:null});let r=await t.json(),a=(r.sightings||[]).find(e=>_(e.description))||null,n=_(a?.description);if(!n){let t={status:"ready",train:r,sighting:a,detection:null};c.current.set(e,t),g(t);return}let s=await fetch(`/api/sightings/${n}`),i=null;s.ok&&(i=await s.json());let l={status:"ready",train:r,sighting:a,detection:i};c.current.set(e,l),g(l)}catch{g({status:"missing",train:null,sighting:null,detection:null})}},[]);(0,h.useEffect)(()=>{let t=e.query.train;e.isReady&&"string"==typeof t&&(0,l.tW)(t)&&M(/^\d+$/.test(t)?Number(t):t)},[e.isReady,e.query.train,M]);let A=(0,h.useCallback)(()=>{d(null),g({status:"idle",train:null,sighting:null,detection:null})},[]),F=S.length,T=null!=o;return(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197 min-h-screen bg-[#f6f5f0]",children:[(0,a.jsx)(f.A,{title:"Freight Trains | trains.fyi",description:"Live extrapolated positions of freight trains detected by trains.fyi cameras.",noIndex:!0}),(0,a.jsx)(p.A,{}),(0,a.jsxs)("main",{className:"jsx-18a6e30d89e52197 mx-auto max-w-[1400px] px-4 pb-12 pt-6 sm:px-6 lg:px-8",children:[(0,a.jsxs)("header",{className:"jsx-18a6e30d89e52197 mb-5 flex flex-wrap items-end justify-between gap-3",children:[(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197",children:[(0,a.jsx)("p",{className:"jsx-18a6e30d89e52197 text-[11px] font-semibold uppercase tracking-[0.22em] text-blue-800/80",children:n("freightTrackerLabel")}),(0,a.jsxs)("h1",{className:"jsx-18a6e30d89e52197 mt-1 flex flex-wrap items-center gap-2 text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl",children:[n("freightActiveTrainsH1"),(0,a.jsx)("span",{title:"This feature is still in development",className:"jsx-18a6e30d89e52197 inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-amber-800 ring-1 ring-amber-300",children:n("freightAlpha")})]}),(0,a.jsx)("p",{className:"jsx-18a6e30d89e52197 mt-1.5 text-sm text-gray-600",children:"Railway GPS fixes, movement reports and positions estimated from sightings."})]}),(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197 flex items-center gap-2 text-xs text-gray-500",children:[(0,a.jsx)("span",{className:`jsx-18a6e30d89e52197 inline-block h-2 w-2 rounded-full ${k?"animate-pulse bg-blue-600":"bg-emerald-500"}`}),(0,a.jsxs)("span",{className:"jsx-18a6e30d89e52197 tabular-nums",children:[n("freightActiveCount").replace("{n}",F),k&&(0,a.jsxs)("span",{className:"jsx-18a6e30d89e52197 ml-1 text-gray-400",children:["\xb7 ",n("freightRefreshing")]})]})]})]}),(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197 grid grid-cols-12 gap-5 lg:gap-6",children:[(0,a.jsx)("section",{className:"jsx-18a6e30d89e52197 col-span-12 lg:col-span-5 xl:col-span-5",children:T?(0,a.jsx)(B,{status:u.status,train:u.train,sighting:u.sighting,detection:u.detection,onBack:A}):(0,a.jsxs)(a.Fragment,{children:[y&&!j&&(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197 rounded-xl bg-red-50 p-4 text-sm text-red-800 ring-1 ring-red-200",children:[(0,a.jsx)("p",{className:"jsx-18a6e30d89e52197 font-medium",children:n("freightErrorTitle")}),(0,a.jsx)("p",{className:"jsx-18a6e30d89e52197 mt-1 text-red-700/90",children:n("freightErrorDesc")})]}),y&&j&&(0,a.jsx)("div",{className:"jsx-18a6e30d89e52197 mb-3 rounded-xl bg-amber-50 px-3 py-1.5 text-xs text-amber-900 ring-1 ring-amber-200",children:n("freightRefreshFailed")}),!y&&v&&!j&&(0,a.jsxs)("div",{"aria-busy":"true","aria-live":"polite",className:"jsx-18a6e30d89e52197 space-y-3",children:[(0,a.jsxs)("span",{className:"jsx-18a6e30d89e52197 sr-only",children:[n("freightLoadingMap"),"…"]}),Array.from({length:4}).map((e,t)=>(0,a.jsx)(E,{},t))]}),!y&&!v&&0===C.length&&(0,a.jsx)("div",{className:"jsx-18a6e30d89e52197 rounded-xl border border-dashed border-gray-300 bg-white p-8 text-center",children:(0,a.jsx)("p",{className:"jsx-18a6e30d89e52197 text-sm text-gray-600",children:n("freightNoTrains")})}),C.length>0&&(0,a.jsx)("div",{className:"jsx-18a6e30d89e52197 space-y-3",children:C.map(e=>(0,a.jsx)(O,{train:e,onActivate:()=>M(e.id)},e.id))})]})}),(0,a.jsx)("aside",{className:"jsx-18a6e30d89e52197 col-span-12 lg:col-span-7 xl:col-span-7",children:(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197 sticky top-2 h-[420px] overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm lg:h-[calc(100vh-6rem)]",children:[(0,a.jsx)(D,{trains:S,selectedTrainId:o,onSelectTrain:M}),!y&&v&&!j&&(0,a.jsx)(R,{}),(0,a.jsxs)("div",{className:"jsx-18a6e30d89e52197 pointer-events-none absolute left-3 top-3 z-10 inline-flex items-center gap-1.5 rounded-full bg-white/90 px-2.5 py-1 text-[11px] font-medium uppercase tracking-wider text-gray-700 shadow ring-1 ring-gray-200 backdrop-blur",children:[(0,a.jsx)("span",{className:"jsx-18a6e30d89e52197 inline-block h-1.5 w-1.5 rounded-full bg-amber-600"}),n("freightActiveCount").replace("{n}",F)]})]})})]})]}),(0,a.jsx)(x.A,{}),(0,a.jsx)(s(),{id:"18a6e30d89e52197",children:'.freight-shimmer{background-color:#eef0f3;background-image:linear-gradient(90deg,rgba(238,240,243,0)0%,rgba(255,255,255,.85)50%,rgba(238,240,243,0)100%);background-size:200%100%;background-repeat:no-repeat;background-position:200%0;animation:freight-shimmer 1.4s ease-in-out infinite}@keyframes freight-shimmer{0%{background-position:200%0}100%{background-position:-200%0}}@media(prefers-reduced-motion:reduce){.freight-shimmer{animation:none}}.freight-marker{width:30px;height:30px;cursor:pointer}.freight-marker__inner{position:relative;width:100%;height:100%}.freight-marker__icon{position:absolute;inset:0;border-radius:9999px;background:#b45309;color:#fff;display:inline-flex;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(15,23,42,.35),0 0 0 3px#fff;transition:background 180ms ease,box-shadow 180ms ease,transform 180ms cubic-bezier(.4,0,.2,1);will-change:transform}.freight-marker__icon svg{transform:rotate(var(--bearing,0deg));transition:transform 400ms ease}.freight-marker:hover .freight-marker__icon{background:#92400e;box-shadow:0 4px 10px rgba(15,23,42,.45),0 0 0 3px#fff;transform:translatey(-2px)}.freight-marker__pulse{position:absolute;inset:0;border-radius:9999px;background:rgba(180,83,9,.45);opacity:0;pointer-events:none;transform-origin:center}.freight-marker--selected{z-index:10}.freight-marker--selected .freight-marker__icon{background:#1d4ed8;box-shadow:0 4px 12px rgba(29,78,216,.5),0 0 0 3px#fff;transform:scale(1.18)}.freight-marker--selected:hover .freight-marker__icon{transform:scale(1.18)translatey(-2px)}.freight-marker--selected .freight-marker__pulse{background:rgba(29,78,216,.5);opacity:1;animation:freight-marker-pulse 1.6s ease-out infinite}@keyframes freight-marker-pulse{0%{transform:scale(.7);opacity:.7}80%,100%{transform:scale(2.6);opacity:0}
10}.freight-last-sighting{width:16px;height:16px;pointer-events:none}.freight-marker--unknown-heading .freight-marker__icon svg{display:none}.freight-marker--unknown-heading .freight-marker__icon::after{content:"";display:block;width:10px;height:10px;border-radius:50%;background:currentColor}.freight-last-sighting__dot{display:block;width:100%;height:100%;border-radius:9999px;background:#1d4ed8;box-shadow:0 0 0 3px#fff,0 1px 4px rgba(15,23,42,.4)}'})]})}},91024:(e,t,r)=>{"use strict";r.d(t,{l:()=>n});var a=r(35634);function n(){let e="en";try{let t=localStorage.getItem("trains_fyi_settings");if(t){let r=JSON.parse(t);r.language&&a.P[r.language]&&(e=r.language)}}catch(e){}return t=>a.P[e][t]??t}}},e=>{e.O(0,[6701,3368,3927,2325,1808,5750,3990,8447,5392,8710,636,6593,8792],()=>e(e.s=23854)),_N_E=e.O()}]);

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.