iobroker.jetframe 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +357 -0
  3. package/admin/SF-Pro.ttf +0 -0
  4. package/admin/admin.d.ts +65 -0
  5. package/admin/frame.html +982 -0
  6. package/admin/frame.html.bak-aircraft-card-real-row-20260518-1608 +1236 -0
  7. package/admin/frame.html.bak-aircraft-card-structure-20260518-1517 +1236 -0
  8. package/admin/frame.html.bak-aircraft-logo-id-fix-20260518-1639 +1239 -0
  9. package/admin/frame.html.bak-shortcut-test +1236 -0
  10. package/admin/frame.html.bak-tablet-class-20260518-1729 +1239 -0
  11. package/admin/heatmap.html +216 -0
  12. package/admin/index.html +268 -0
  13. package/admin/index_m.html +1749 -0
  14. package/admin/jetframe.css +1260 -0
  15. package/admin/jetframe.css.bak-airbus-landscape-fix +4630 -0
  16. package/admin/jetframe.css.bak-aircraft-card-clean-equal-20260518-1438 +4899 -0
  17. package/admin/jetframe.css.bak-aircraft-card-real-row-20260518-1608 +4814 -0
  18. package/admin/jetframe.css.bak-aircraft-card-row-left-20260518-1525 +4604 -0
  19. package/admin/jetframe.css.bak-aircraft-card-slim-equal-20260518-1446 +4647 -0
  20. package/admin/jetframe.css.bak-aircraft-card-structure-20260518-1517 +4646 -0
  21. package/admin/jetframe.css.bak-aircraft-inline-final-20260518-1527 +4654 -0
  22. package/admin/jetframe.css.bak-aircraft-row-compact-fix-20260518-1639 +4763 -0
  23. package/admin/jetframe.css.bak-before-aircrafttype-purge +4818 -0
  24. package/admin/jetframe.css.bak-before-cleanup +4670 -0
  25. package/admin/jetframe.css.bak-before-remove-tablet-only-20260518-1711 +4896 -0
  26. package/admin/jetframe.css.bak-before-tablet-layout-rework-20260518-1650 +4914 -0
  27. package/admin/jetframe.css.bak-clean-duplicate-fonts-20260518-1340 +4975 -0
  28. package/admin/jetframe.css.bak-clean-old-index-fix-20260518-1937 +5167 -0
  29. package/admin/jetframe.css.bak-hardleft-airbus +4751 -0
  30. package/admin/jetframe.css.bak-index-iphone-landscape-20260518-1931 +5030 -0
  31. package/admin/jetframe.css.bak-index-landscape-final-20260518-1941 +5167 -0
  32. package/admin/jetframe.css.bak-index-landscape-real-20260518-1936 +5186 -0
  33. package/admin/jetframe.css.bak-landscape-compact-jumbo-bold-20260518-1343 +4802 -0
  34. package/admin/jetframe.css.bak-logo-align-final +4551 -0
  35. package/admin/jetframe.css.bak-logo-final2 +4551 -0
  36. package/admin/jetframe.css.bak-narrowbody-font-fix +4992 -0
  37. package/admin/jetframe.css.bak-nuke-airbus-align +4790 -0
  38. package/admin/jetframe.css.bak-pill-balance-20260518-1603 +4773 -0
  39. package/admin/jetframe.css.bak-pill-balance-fix +4910 -0
  40. package/admin/jetframe.css.bak-radar-fix-fonts +4710 -0
  41. package/admin/jetframe.css.bak-shortcut-test +4899 -0
  42. package/admin/jetframe.css.bak-smaller-aircraft-card-fonts-20260518-1345 +4897 -0
  43. package/admin/jetframe.css.bak-tablet-fix-real-20260518-1748 +4945 -0
  44. package/admin/jetframe.css.bak-tablet-fullscreen-fix-20260518-1804 +4972 -0
  45. package/admin/jetframe.css.bak-tablet-landscape-layout-20260518-1645 +4802 -0
  46. package/admin/jetframe.css.bak-tablet-layout-final-20260518-1839 +4802 -0
  47. package/admin/jetframe.css.bak-tablet-layout-v3-20260518-1729 +4802 -0
  48. package/admin/jetframe.css.bak-tablet-layout-v4-20260518-1801 +4957 -0
  49. package/admin/jetframe.css.bak-tablet-layout-v5-20260518-1843 +4970 -0
  50. package/admin/jetframe.css.bak-tablet-layout-v6-20260518-1848 +4958 -0
  51. package/admin/jetframe.css.bak-tablet-layout-v7-20260518-1909 +4985 -0
  52. package/admin/jetframe.css.bak-tablet-only-landscape-v2-20260518-1707 +4802 -0
  53. package/admin/jetframe.css.bak-tablet-pages-final-20260519-1857 +5188 -0
  54. package/admin/jetframe.css.bak-tablet-pages-final-20260519-1859 +5347 -0
  55. package/admin/jetframe.css.bak-tablet-pages-v2-20260519-190807 +5349 -0
  56. package/admin/jetframe.css.bak-typography-align-final +4818 -0
  57. package/admin/jetframe.png +0 -0
  58. package/admin/manifest.webmanifest +15 -0
  59. package/admin/src/app.tsx +58 -0
  60. package/admin/src/components/settings.tsx +97 -0
  61. package/admin/src/i18n/de.json +11 -0
  62. package/admin/src/i18n/en.json +11 -0
  63. package/admin/src/i18n/es.json +11 -0
  64. package/admin/src/i18n/fr.json +11 -0
  65. package/admin/src/i18n/i18n.d.ts +28 -0
  66. package/admin/src/i18n/it.json +11 -0
  67. package/admin/src/i18n/nl.json +11 -0
  68. package/admin/src/i18n/pl.json +11 -0
  69. package/admin/src/i18n/pt.json +11 -0
  70. package/admin/src/i18n/ru.json +11 -0
  71. package/admin/src/i18n/uk.json +11 -0
  72. package/admin/src/i18n/zh-cn.json +11 -0
  73. package/admin/src/index.tsx +25 -0
  74. package/admin/stats.html +228 -0
  75. package/admin/style.css +32 -0
  76. package/admin/tsconfig.json +11 -0
  77. package/admin/words.js +46 -0
  78. package/build/lib/adsb.js +218 -0
  79. package/build/lib/adsb.js.map +7 -0
  80. package/build/lib/airportNamesDe.js +131 -0
  81. package/build/lib/airportNamesDe.js.map +7 -0
  82. package/build/lib/airports.js +281 -0
  83. package/build/lib/airports.js.map +7 -0
  84. package/build/lib/classify.js +339 -0
  85. package/build/lib/classify.js.map +7 -0
  86. package/build/lib/config.js +103 -0
  87. package/build/lib/config.js.map +7 -0
  88. package/build/lib/flightInfo.js +1409 -0
  89. package/build/lib/flightInfo.js.map +7 -0
  90. package/build/lib/geo.js +84 -0
  91. package/build/lib/geo.js.map +7 -0
  92. package/build/lib/images.js +422 -0
  93. package/build/lib/images.js.map +7 -0
  94. package/build/lib/specialLiveries.js +342 -0
  95. package/build/lib/specialLiveries.js.map +7 -0
  96. package/build/lib/states.js +971 -0
  97. package/build/lib/states.js.map +7 -0
  98. package/build/lib/staticFiles.js +73 -0
  99. package/build/lib/staticFiles.js.map +7 -0
  100. package/build/lib/types.js +17 -0
  101. package/build/lib/types.js.map +7 -0
  102. package/build/lib/visConfig.js +52 -0
  103. package/build/lib/visConfig.js.map +7 -0
  104. package/build/main.js +1454 -0
  105. package/build/main.js.map +7 -0
  106. package/io-package.json +169 -0
  107. package/package.json +82 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/flightInfo.ts"],
4
+ "sourcesContent": ["import type { Aircraft, JetFrameConfig, AdapterLike } from './types';\n\nexport type HttpJson = (url: string) => Promise<any>;\nexport type HttpText = (url: string) => Promise<string>;\n\nconst flighteraPlaneRouteCache: Record<string, CacheEntry<any>> = {};\nconst fr24LiveRouteCache: Record<string, CacheEntry<any>> = {};\nconst fr24AircraftCache: Record<string, CacheEntry<any>> = {};\nconst adsbdbCallsignCache: Record<string, CacheEntry<any>> = {};\nconst hexdbRouteCache: Record<string, CacheEntry<any>> = {};\nconst hexdbAirlineCache: Record<string, CacheEntry<any>> = {};\n\nconst CACHE = {\n\tflighteraMs: 12 * 60 * 60 * 1000,\n\tfr24LiveMs: 60 * 60 * 1000,\n\tfr24Ms: 24 * 60 * 60 * 1000,\n\tadsbdbMs: 12 * 60 * 60 * 1000,\n\thexdbRouteMs: 6 * 60 * 60 * 1000,\n\thexdbAirlineMs: 24 * 60 * 60 * 1000,\n};\n\ninterface CacheEntry<T> {\n\tts: number;\n\tdata?: T | null;\n\timageUrl?: string;\n}\n\n/**\n *\n */\nexport async function enrichFlightInfo(\n\tadapter: AdapterLike,\n\tconfig: JetFrameConfig,\n\ta: Aircraft,\n\thttpJson: HttpJson,\n\thttpText: HttpText,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<Aircraft> {\n\tif (!a.callsign) {\n\t\treturn {\n\t\t\t...a,\n\t\t\taircraftType: a.aircraftType || a.type || '',\n\t\t\t...buildSpecialInfo(a),\n\t\t};\n\t}\n\n\ttry {\n\t\tconst operationalCallsign = clean(a.callsign).toUpperCase();\n\n\t\tconst operationalData = await loadAdsbdbByCallsign(operationalCallsign, httpJson, logDebug, logWarn);\n\n\t\tconst hexAirline = await resolveAirlineViaHexDb(a.hex, httpText, logDebug, logWarn);\n\n\t\tlet parsed = parseAdsbdbResponse(config, operationalData, a, operationalCallsign, operationalCallsign);\n\n\t\tif (hexAirline?.name) {\n\t\t\tparsed.airlineName = hexAirline.name;\n\t\t\tparsed.airlineIata = hexAirline.iata || parsed.airlineIata || '';\n\t\t\tparsed.airlineIcao = hexAirline.icao || parsed.airlineIcao || guessAirlineIcao(operationalCallsign);\n\t\t\tparsed.logoUrl = buildExternalAirlineLogoUrl(\n\t\t\t\tconfig,\n\t\t\t\tparsed.airlineIcao || guessAirlineIcao(operationalCallsign),\n\t\t\t\tparsed.airlineIata || '',\n\t\t\t);\n\t\t\tlogDebug(`HexDB Airline bevorzugt: ${parsed.airlineName}`);\n\t\t}\n\n\t\tconst regForRoute = parsed.registration || a.registration;\n\n\t\tlet routeFound = false;\n\n\t\tconst hexRoute = await resolveRouteViaHexDb(adapter, operationalCallsign, httpJson, logDebug, logWarn, config);\n\n\t\tconst flighteraRoute = await resolveRouteViaFlighteraPlane(\n\t\t\tregForRoute,\n\t\t\toperationalCallsign,\n\t\t\ta.mode || '',\n\t\t\thttpText,\n\t\t\tlogDebug,\n\t\t\tlogWarn,\n\t\t\tconfig,\n\t\t);\n\n\t\tconst mergedRoute = mergeHexAndFlighteraRoute(hexRoute, flighteraRoute);\n\n\t\tif (mergedRoute?.originIata && mergedRoute?.destIata) {\n\t\t\tparsed.routeCallsign =\n\t\t\t\tflighteraRoute?.routeCallsign || hexRoute?.routeCallsign || parsed.routeCallsign || operationalCallsign;\n\n\t\t\tparsed.originIata = mergedRoute.originIata;\n\t\t\tparsed.destIata = mergedRoute.destIata;\n\t\t\tparsed.routeReliable = true;\n\n\t\t\tif (\n\t\t\t\thexRoute?.originIata &&\n\t\t\t\thexRoute?.destIata &&\n\t\t\t\tflighteraRoute?.originIata &&\n\t\t\t\tflighteraRoute?.destIata &&\n\t\t\t\t(hexRoute.originIata !== flighteraRoute.originIata || hexRoute.destIata !== flighteraRoute.destIata)\n\t\t\t) {\n\t\t\t\tparsed.routeWarning = flighteraRoute?.isLive\n\t\t\t\t\t? 'Flightera Live bevorzugt, HexDB abweichend'\n\t\t\t\t\t: 'HexDB bevorzugt, Flightera abweichend';\n\t\t\t\tparsed.routeSource = flighteraRoute?.isLive\n\t\t\t\t\t? 'flightera-live-route-conflict-hexdb+airportjson'\n\t\t\t\t\t: 'hexdb-route-verified-conflict+airportjson';\n\t\t\t} else if (hexRoute?.originIata && hexRoute?.destIata) {\n\t\t\t\tparsed.routeWarning = flighteraRoute ? 'HexDB + Flightera gepr\u00FCft' : 'HexDB Route';\n\t\t\t\tparsed.routeSource = flighteraRoute\n\t\t\t\t\t? 'hexdb-route+flightera-check+airportjson'\n\t\t\t\t\t: 'hexdb-route+airportjson';\n\t\t\t} else {\n\t\t\t\tparsed.routeWarning = flighteraRoute?.isLive ? 'Live-Flug erkannt' : '';\n\t\t\t\tparsed.routeSource = flighteraRoute?.isLive\n\t\t\t\t\t? 'flightera-plane-live-route+airportjson'\n\t\t\t\t\t: 'flightera-plane-callsign-route+airportjson';\n\t\t\t}\n\n\t\t\tparsed.routeText = `${parsed.originIata} \u2192 ${parsed.destIata}`;\n\t\t\trouteFound = true;\n\t\t}\n\n\t\tif (!routeFound) {\n\t\t\tconst fr24Live = await resolveRouteViaFr24Live(\n\t\t\t\toperationalCallsign,\n\t\t\t\ta.mode || '',\n\t\t\t\thttpText,\n\t\t\t\tlogDebug,\n\t\t\t\tlogWarn,\n\t\t\t\tconfig,\n\t\t\t);\n\n\t\t\tif (fr24Live?.originIata && fr24Live?.destIata) {\n\t\t\t\tparsed.routeCallsign = fr24Live.routeCallsign || parsed.routeCallsign || operationalCallsign;\n\n\t\t\t\tparsed.originIata = fr24Live.originIata;\n\t\t\t\tparsed.destIata = fr24Live.destIata;\n\t\t\t\tparsed.routeReliable = true;\n\t\t\t\tparsed.routeWarning = 'FR24 Live-Fallback';\n\t\t\t\tparsed.routeSource = 'fr24-live-route+airportjson';\n\t\t\t\tparsed.routeText = `${parsed.originIata} \u2192 ${parsed.destIata}`;\n\t\t\t\trouteFound = true;\n\t\t\t}\n\n\t\t\tif (fr24Live?.imageUrl) {\n\t\t\t\tparsed.fr24ImageUrl = fr24Live.imageUrl;\n\t\t\t}\n\t\t}\n\n\t\tif (!routeFound) {\n\t\t\tconst adsbdbRoute = parseAdsbdbRouteFallback(operationalData, a.mode || '', config);\n\n\t\t\tif (adsbdbRoute?.originIata && adsbdbRoute?.destIata) {\n\t\t\t\tparsed.routeCallsign = adsbdbRoute.routeCallsign || parsed.routeCallsign || operationalCallsign;\n\n\t\t\t\tparsed.originIata = adsbdbRoute.originIata;\n\t\t\t\tparsed.destIata = adsbdbRoute.destIata;\n\t\t\t\tparsed.routeReliable = true;\n\t\t\t\tparsed.routeWarning = 'ADSBDB Fallback';\n\t\t\t\tparsed.routeSource = 'adsbdb-route-fallback+airportjson';\n\t\t\t\tparsed.routeText = `${parsed.originIata} \u2192 ${parsed.destIata}`;\n\t\t\t\trouteFound = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!routeFound) {\n\t\t\tparsed = makeUnknownAirportRoute(a.mode || '', parsed, config);\n\t\t}\n\n\t\t// Kein FR24-Bildabruf in flightInfo.\n\t\t// Bildlogik l\u00E4uft zentral in images.ts:\n\t\t// Cache \u2192 HexDB \u2192 FR24-Fallback nur bei HexDB-Fehler.\n\t\tif (!parsed.fr24ImageUrl) {\n\t\t\tparsed.fr24ImageUrl = '';\n\t\t}\n\n\t\tparsed = await applyAirportNamesFromJson(adapter, config, parsed, logWarn);\n\n\t\tconst jet = parsed.fr24ImageUrl ? { best: parsed.fr24ImageUrl } : { best: '' };\n\n\t\tconst baseInfo: Partial<Aircraft> = {\n\t\t\t...parsed,\n\n\t\t\toperationalCallsign,\n\n\t\t\tjetphotosUrl: parsed.registration\n\t\t\t\t? `https://www.flightradar24.com/data/aircraft/${encodeURIComponent(\n\t\t\t\t\t\tString(parsed.registration).toLowerCase(),\n\t\t\t\t\t)}`\n\t\t\t\t: '',\n\n\t\t\tjetphotosImageUrl: jet.best || '',\n\n\t\t\taircraftType: parsed.aircraftType || a.aircraftType || a.type || '',\n\n\t\t\taircraftModel:\n\t\t\t\tparsed.aircraftModel || parsed.aircraftType || a.aircraftModel || a.aircraftType || a.type || '',\n\t\t};\n\n\t\tconst specialInfo = buildSpecialInfo({\n\t\t\t...a,\n\t\t\t...baseInfo,\n\t\t});\n\n\t\treturn {\n\t\t\t...a,\n\t\t\t...baseInfo,\n\t\t\t...specialInfo,\n\t\t};\n\t} catch (e) {\n\t\tlogWarn(`FlightInfo Fehler: ${errorText(e)}`);\n\n\t\treturn {\n\t\t\t...a,\n\t\t\taircraftType: a.aircraftType || a.type || '',\n\t\t\t...buildSpecialInfo(a),\n\t\t};\n\t}\n}\n\nasync function resolveAirlineViaHexDb(\n\thex: string,\n\thttpText: HttpText,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<{ name: string; iata: string; icao: string } | null> {\n\tconst cleanHex = clean(hex)\n\t\t.toLowerCase()\n\t\t.replace(/[^a-f0-9]/g, '');\n\n\tif (!cleanHex) {\n\t\treturn null;\n\t}\n\n\tconst now = Date.now();\n\tconst cached = hexdbAirlineCache[cleanHex];\n\n\tif (cached && now - cached.ts < CACHE.hexdbAirlineMs) {\n\t\tlogDebug(`HexDB Airline Cache hit: ${cleanHex}`);\n\t\treturn cached.data || null;\n\t}\n\n\ttry {\n\t\tlogDebug(`HexDB Airline Anfrage: ${cleanHex}`);\n\n\t\tconst data = await httpText(`https://hexdb.io/hex-airline?hex=${encodeURIComponent(cleanHex)}`);\n\n\t\tconst name = clean(data);\n\n\t\tif (!name || name.toLowerCase().includes('not found')) {\n\t\t\thexdbAirlineCache[cleanHex] = {\n\t\t\t\tts: now,\n\t\t\t\tdata: null,\n\t\t\t};\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = normalizeHexDbAirlineName(name);\n\n\t\thexdbAirlineCache[cleanHex] = {\n\t\t\tts: now,\n\t\t\tdata: result,\n\t\t};\n\n\t\treturn result;\n\t} catch (e) {\n\t\thexdbAirlineCache[cleanHex] = {\n\t\t\tts: now,\n\t\t\tdata: null,\n\t\t};\n\n\t\tlogDebug(`HexDB Airline nicht nutzbar: ${errorText(e)}`);\n\t\treturn null;\n\t}\n}\n\nfunction normalizeHexDbAirlineName(name: string): { name: string; iata: string; icao: string } {\n\treturn {\n\t\tname: clean(name),\n\t\tiata: '',\n\t\ticao: '',\n\t};\n}\n\n/************************************************************\n * ADSBDB\n ************************************************************/\n\nasync function loadAdsbdbByCallsign(\n\tcallsign: string,\n\thttpJson: HttpJson,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<null> {\n\tconst cs = clean(callsign).toUpperCase();\n\n\tif (!cs || cs.length < 3) {\n\t\tlogDebug('ADSBDB \u00FCbersprungen: ung\u00FCltiger Callsign');\n\t\treturn null;\n\t}\n\n\tconst now = Date.now();\n\tconst cached = adsbdbCallsignCache[cs];\n\n\tif (cached && now - cached.ts < CACHE.adsbdbMs) {\n\t\tlogDebug(`ADSBDB Cache hit: ${cs}`);\n\t\treturn cached.data || null;\n\t}\n\n\ttry {\n\t\tlogDebug(`ADSBDB Anfrage EINMALIG: ${cs}`);\n\n\t\tconst data = await httpJson(`https://api.adsbdb.com/v0/callsign/${encodeURIComponent(cs)}`);\n\n\t\tadsbdbCallsignCache[cs] = {\n\t\t\tts: now,\n\t\t\tdata: data || null,\n\t\t};\n\n\t\treturn data || null;\n\t} catch (e) {\n\t\tadsbdbCallsignCache[cs] = {\n\t\t\tts: now,\n\t\t\tdata: null,\n\t\t};\n\n\t\tlogWarn(`ADSBDB Fehler gecached f\u00FCr ${cs}: ${errorText(e)}`);\n\t\treturn null;\n\t}\n}\n\nfunction parseAdsbdbResponse(\n\tconfig: JetFrameConfig,\n\tdata: any,\n\ta: Aircraft,\n\toperationalCallsign: string,\n\trouteCallsign: string,\n): ParsedInfo {\n\tconst response = data?.response || {};\n\tconst route = response.flightroute || null;\n\tconst aircraft = response.aircraft || null;\n\n\tconst airlineName = route?.airline ? clean(route.airline.name) : guessAirlineName(operationalCallsign);\n\n\tconst airlineIata = route?.airline ? clean(route.airline.iata) : guessAirlineIata(operationalCallsign);\n\n\tconst airlineIcao = route?.airline ? clean(route.airline.icao) : guessAirlineIcao(operationalCallsign);\n\n\tconst aircraftType = aircraft ? clean(aircraft.type) : a.type || '';\n\tconst aircraftModel = (aircraft ? clean(aircraft.model) : '') || aircraftType || a.aircraftModel || '';\n\tconst registration = aircraft ? clean(aircraft.registration) : a.registration || '';\n\tconst logoKey = airlineIcao || guessAirlineIcao(operationalCallsign);\n\tconst logoUrl = buildExternalAirlineLogoUrl(config, logoKey, airlineIata);\n\tconst logoFallbackUrl = '';\n\treturn {\n\t\toperationalCallsign,\n\t\trouteCallsign,\n\n\t\tairlineName,\n\t\tairlineIata,\n\t\tairlineIcao,\n\n\t\toriginIata: '',\n\t\tdestIata: '',\n\t\toriginName: '',\n\t\tdestName: '',\n\t\trouteText: '',\n\t\trouteTextLong: '',\n\t\trouteReliable: false,\n\t\trouteWarning: '',\n\t\trouteSource: 'adsbdb-no-route',\n\n\t\taircraftModel,\n\t\taircraftType,\n\t\tregistration,\n\n\t\tlogoUrl,\n\t\tlogoFallbackUrl,\n\t\tfr24ImageUrl: '',\n\t};\n}\n\nfunction buildExternalAirlineLogoUrl(config: JetFrameConfig, airlineIcao: string, airlineIata: string): string {\n\tif (!config.externalAirlineLogos) {\n\t\treturn '';\n\t}\n\n\tconst base = String(config.airlineLogoBaseUrl || '').trim();\n\n\tif (!base || !airlineIcao) {\n\t\treturn '';\n\t}\n\n\tconst icao = clean(airlineIcao).toUpperCase();\n\tconst iata = clean(airlineIata).toUpperCase();\n\n\tif (base.includes('{icao}') || base.includes('{iata}') || base.includes('{code}')) {\n\t\treturn base\n\t\t\t.replace(/\\{icao\\}/g, encodeURIComponent(icao))\n\t\t\t.replace(/\\{iata\\}/g, encodeURIComponent(iata || icao))\n\t\t\t.replace(/\\{code\\}/g, encodeURIComponent(icao));\n\t}\n\n\treturn `${base.replace(/\\/+$/, '')}/${encodeURIComponent(icao)}.png`;\n}\n\nfunction parseAdsbdbRouteFallback(data: any, mode: string, config: JetFrameConfig): RouteResult | null {\n\tconst response = data?.response || {};\n\tconst route = response.flightroute || null;\n\n\tif (!route) {\n\t\treturn null;\n\t}\n\n\tconst originIata = route.origin ? clean(route.origin.iata_code).toUpperCase() : '';\n\n\tconst destIata = route.destination ? clean(route.destination.iata_code).toUpperCase() : '';\n\n\tconst callsign = clean(route.callsign || route.flight_number || '').toUpperCase();\n\n\tif (!isIataCode(originIata) || !isIataCode(destIata)) {\n\t\treturn null;\n\t}\n\tif (originIata === destIata) {\n\t\treturn null;\n\t}\n\n\tif (mode === 'TAKEOFF' && originIata !== config.airport.iata) {\n\t\treturn null;\n\t}\n\tif (mode === 'LANDING' && destIata !== config.airport.iata) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trouteCallsign: callsign,\n\t\toriginIata,\n\t\tdestIata,\n\t};\n}\n\n/************************************************************\n * HexDB Route\n ************************************************************/\n\nasync function resolveRouteViaHexDb(\n\tadapter: AdapterLike,\n\toperationalCallsign: string,\n\thttpJson: HttpJson,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n\tconfig: JetFrameConfig,\n): Promise<RouteResult | null> {\n\tconst op = clean(operationalCallsign).toUpperCase();\n\n\tif (!op) {\n\t\treturn null;\n\t}\n\n\tconst now = Date.now();\n\tconst cached = hexdbRouteCache[op];\n\n\tif (cached && now - cached.ts < CACHE.hexdbRouteMs) {\n\t\tlogDebug(`HexDB Route Cache hit: ${op}`);\n\t\treturn cached.data || null;\n\t}\n\n\ttry {\n\t\tconst url = `https://hexdb.io/api/v1/route/icao/${encodeURIComponent(op)}`;\n\n\t\tlogDebug(`HexDB Route Anfrage: ${op} \u2192 ${url}`);\n\n\t\tconst data = await httpJson(url);\n\n\t\tif (data?.status === '404' || data?.error) {\n\t\t\thexdbRouteCache[op] = {\n\t\t\t\tts: now,\n\t\t\t\tdata: null,\n\t\t\t};\n\n\t\t\tlogDebug(`HexDB Route nicht gefunden: ${op}`);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst routeRaw = clean(data?.route).toUpperCase();\n\n\t\tif (!routeRaw || !routeRaw.includes('-')) {\n\t\t\thexdbRouteCache[op] = {\n\t\t\t\tts: now,\n\t\t\t\tdata: null,\n\t\t\t};\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst parts = routeRaw.split('-').map(x => clean(x).toUpperCase());\n\t\tconst originIcao = parts[0] || '';\n\t\tconst destIcao = parts[1] || '';\n\n\t\tif (!isIcaoCode(originIcao) || !isIcaoCode(destIcao)) {\n\t\t\thexdbRouteCache[op] = {\n\t\t\t\tts: now,\n\t\t\t\tdata: null,\n\t\t\t};\n\n\t\t\treturn null;\n\t\t}\n\n\t\tconst originIata = await iataFromIcao(adapter, config, originIcao);\n\t\tconst destIata = await iataFromIcao(adapter, config, destIcao);\n\n\t\tif (!originIata || !destIata) {\n\t\t\thexdbRouteCache[op] = {\n\t\t\t\tts: now,\n\t\t\t\tdata: null,\n\t\t\t};\n\n\t\t\tlogDebug(`HexDB Route ohne IATA-Mapping: ${originIcao}-${destIcao}`);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result: RouteResult = {\n\t\t\trouteCallsign: clean(data?.flight).toUpperCase() || op,\n\t\t\toriginIata,\n\t\t\tdestIata,\n\t\t\tisLive: true,\n\t\t};\n\n\t\thexdbRouteCache[op] = {\n\t\t\tts: now,\n\t\t\tdata: result,\n\t\t};\n\n\t\tlogDebug(`HexDB Route parsed: ${op} | ${originIata} \u2192 ${destIata}`);\n\n\t\treturn result;\n\t} catch (e) {\n\t\thexdbRouteCache[op] = {\n\t\t\tts: now,\n\t\t\tdata: null,\n\t\t};\n\n\t\tlogDebug(`HexDB Route Fehler f\u00FCr ${op}: ${errorText(e)}`);\n\t\treturn null;\n\t}\n}\n\nasync function iataFromIcao(adapter: AdapterLike, config: JetFrameConfig, icao: string): Promise<string> {\n\ticao = clean(icao).toUpperCase();\n\n\tif (!icao) {\n\t\treturn '';\n\t}\n\n\t// schnelle lokale Hauptairport-Abk\u00FCrzung\n\tif (icao === clean(config.airport.icao).toUpperCase()) {\n\t\treturn clean(config.airport.iata).toUpperCase();\n\t}\n\n\ttry {\n\t\tconst st = await adapter.getForeignStateAsync(config.airportJsonDp);\n\t\tconst raw = st?.val ? String(st.val) : '';\n\n\t\tif (!raw || raw === '[]') {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst airports = JSON.parse(raw);\n\n\t\tif (!Array.isArray(airports)) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst found = airports.find((a: any) => clean(a.icao || a.ICAO).toUpperCase() === icao);\n\n\t\tif (!found) {\n\t\t\treturn '';\n\t\t}\n\n\t\treturn clean(found.iata || found.IATA || '').toUpperCase();\n\t} catch {\n\t\treturn '';\n\t}\n}\n\nfunction mergeHexAndFlighteraRoute(\n\thexRoute: RouteResult | null,\n\tflighteraRoute: RouteResult | null,\n): RouteResult | null {\n\tconst flighteraComplete = !!flighteraRoute?.originIata && !!flighteraRoute?.destIata;\n\n\tconst hexComplete = !!hexRoute?.originIata && !!hexRoute?.destIata;\n\n\t// Wichtig:\n\t// Wenn Flightera eine vollst\u00E4ndige LIVE-Zeile hat,\n\t// gewinnt Flightera gegen HexDB.\n\t// HexDB ist oft gut, aber bei historischen/alten Callsigns\n\t// teilweise falsch.\n\tif (flighteraComplete && flighteraRoute?.isLive) {\n\t\treturn flighteraRoute;\n\t}\n\n\t// Wenn Flightera vollst\u00E4ndig ist und Hex fehlt,\n\t// gewinnt ebenfalls Flightera.\n\tif (flighteraComplete && !hexComplete) {\n\t\treturn flighteraRoute;\n\t}\n\n\t// Sonst HexDB als schneller/stabiler Fallback.\n\tif (hexComplete) {\n\t\treturn hexRoute;\n\t}\n\n\t// Falls nur Flightera vollst\u00E4ndig ist, nehmen.\n\tif (flighteraComplete) {\n\t\treturn flighteraRoute;\n\t}\n\n\treturn hexRoute || flighteraRoute || null;\n}\n\nfunction isIcaoCode(code: string): boolean {\n\tcode = clean(code).toUpperCase();\n\treturn /^[A-Z]{4}$/.test(code);\n}\n\n/************************************************************\n * Flightera\n ************************************************************/\n\nasync function resolveRouteViaFlighteraPlane(\n\tregistration: string | undefined,\n\toperationalCallsign: string,\n\tmode: string,\n\thttpText: HttpText,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n\tconfig: JetFrameConfig,\n): Promise<RouteResult | null> {\n\tconst reg = clean(registration).toUpperCase();\n\tconst op = clean(operationalCallsign).toUpperCase();\n\n\tif (!reg || !op) {\n\t\treturn null;\n\t}\n\n\tconst cacheKey = `${reg}|${op}`;\n\tconst now = Date.now();\n\tconst cached = flighteraPlaneRouteCache[cacheKey];\n\n\tif (cached && now - cached.ts < CACHE.flighteraMs) {\n\t\tlogDebug(`Flightera Plane Cache hit: ${cacheKey}`);\n\t\treturn cached.data || null;\n\t}\n\n\tconst urls = [\n\t\t`https://www.flightera.net/de/planes/${encodeURIComponent(reg)}`,\n\t\t`https://www.flightera.net/en/planes/${encodeURIComponent(reg)}`,\n\t];\n\n\tfor (const url of urls) {\n\t\ttry {\n\t\t\tlogDebug(`Flightera Plane Anfrage EINMALIG: ${cacheKey} \u2192 ${url}`);\n\n\t\t\tconst htmlRaw = await httpText(url);\n\t\t\tconst html = normalizeHtml(htmlRaw);\n\t\t\tconst text = htmlToText(html);\n\n\t\t\tconst parsed = parseFlighteraPlaneRoute(html, text, op, mode, config, logDebug);\n\n\t\t\tif (parsed?.originIata && parsed?.destIata) {\n\t\t\t\tflighteraPlaneRouteCache[cacheKey] = {\n\t\t\t\t\tts: now,\n\t\t\t\t\tdata: parsed,\n\t\t\t\t};\n\n\t\t\t\tlogDebug(\n\t\t\t\t\t`Flightera Plane Route parsed: ${op} | ${parsed.originIata} \u2192 ${parsed.destIata} | routeCallsign=${parsed.routeCallsign || '?'} | live=${parsed.isLive ? 'ja' : 'nein'}`,\n\t\t\t\t);\n\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlogWarn(`Flightera Plane Fehler f\u00FCr ${cacheKey}: ${errorText(e)}`);\n\t\t}\n\t}\n\n\tflighteraPlaneRouteCache[cacheKey] = {\n\t\tts: now,\n\t\tdata: null,\n\t};\n\n\tlogDebug(`Flightera Plane keine Route gefunden f\u00FCr ${cacheKey}`);\n\treturn null;\n}\n\nfunction parseFlighteraPlaneRoute(\n\thtml: string,\n\ttext: string,\n\toperationalCallsign: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n\tlogDebug: (msg: string, level?: number) => void,\n): RouteResult | null {\n\tconst op = clean(operationalCallsign).toUpperCase();\n\n\tconst rows = extractFlighteraRowsStrict(html, text, op, mode, config, logDebug);\n\n\tconst picked = pickBestFlighteraRow(rows, op, mode, config, logDebug);\n\n\tif (picked) {\n\t\treturn {\n\t\t\trouteCallsign: /[A-Z]/.test(picked.routeCallsign || '') ? picked.routeCallsign : op,\n\t\t\toriginIata: picked.originIata,\n\t\t\tdestIata: picked.destIata,\n\t\t\tisLive: !!picked.isLive,\n\t\t};\n\t}\n\n\tlogDebug('[Flightera] Keine passende Live/Callsign-Zeile \u2192 Route verworfen.');\n\treturn null;\n}\n\nfunction extractFlighteraRowsStrict(\n\thtml: string,\n\ttext: string,\n\toperationalCallsign: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n\tlogDebug: (msg: string, level?: number) => void,\n): FlighteraRow[] {\n\tconst blocks: Array<{ text: string; source: string; index: number }> = [];\n\tconst rows: FlighteraRow[] = [];\n\n\tfunction addBlock(raw: string, source: string, index: number): void {\n\t\tconst plain = htmlToText(raw);\n\n\t\tif (!plain || plain.length < 25) {\n\t\t\treturn;\n\t\t}\n\n\t\tblocks.push({\n\t\t\ttext: plain.replace(/\\s+/g, ' ').trim(),\n\t\t\tsource,\n\t\t\tindex: index || 0,\n\t\t});\n\t}\n\n\tlet m: RegExpExecArray | null;\n\n\tconst trRegex = /<tr[\\s\\S]*?<\\/tr>/gi;\n\n\twhile ((m = trRegex.exec(String(html || ''))) !== null) {\n\t\taddBlock(m[0], 'tr', m.index);\n\t}\n\n\tconst fullText = String(text || '')\n\t\t.replace(/\\s+/g, ' ')\n\t\t.trim();\n\tconst upperText = fullText.toUpperCase();\n\tconst op = clean(operationalCallsign).toUpperCase();\n\tconst opIndex = upperText.indexOf(op);\n\n\tif (opIndex >= 0) {\n\t\tconst start = Math.max(0, opIndex - 500);\n\t\tconst end = Math.min(fullText.length, opIndex + 1800);\n\n\t\taddBlock(fullText.substring(start, end), 'op-live-scope', opIndex);\n\t} else {\n\t\tlogDebug(`Flightera op-live-scope: Operational Callsign nicht gefunden: ${op}`);\n\t}\n\n\tconst iataLike = operationalToLikelyIataCallsign(op);\n\n\tif (iataLike && iataLike !== op) {\n\t\tconst marketingIndex = upperText.indexOf(iataLike.toUpperCase());\n\n\t\tif (marketingIndex >= 0) {\n\t\t\tconst start = Math.max(0, marketingIndex - 500);\n\t\t\tconst end = Math.min(fullText.length, marketingIndex + 1800);\n\n\t\t\taddBlock(fullText.substring(start, end), 'marketing-scope', marketingIndex);\n\t\t}\n\t}\n\n\tconst liveRegex = /\\bLIVE\\b/gi;\n\tlet liveCount = 0;\n\n\twhile ((m = liveRegex.exec(fullText)) !== null && liveCount < 5) {\n\t\tconst liveIndex = m.index;\n\t\tconst start = Math.max(0, liveIndex - 900);\n\t\tconst end = Math.min(fullText.length, liveIndex + 2400);\n\n\t\taddBlock(fullText.substring(start, end), 'live-fallback', liveIndex);\n\t\tliveCount++;\n\t}\n\n\tfor (const b of blocks) {\n\t\tconst row = parseFlighteraSingleRow(b.text, operationalCallsign, mode, b.source, b.index, config);\n\n\t\tif (row) {\n\t\t\trows.push(row);\n\t\t}\n\t}\n\n\tconst unique: FlighteraRow[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const r of rows) {\n\t\tconst key = [\n\t\t\tr.routeCallsign || '',\n\t\t\tr.operationalCallsign || '',\n\t\t\tr.originIata || '',\n\t\t\tr.destIata || '',\n\t\t\tr.isLive ? 'live' : 'no',\n\t\t\tr.source || '',\n\t\t].join('|');\n\n\t\tif (!seen.has(key)) {\n\t\t\tseen.add(key);\n\t\t\tunique.push(r);\n\t\t}\n\t}\n\n\treturn unique;\n}\n\nfunction parseFlighteraSingleRow(\n\trowText: string,\n\toperationalCallsign: string,\n\tmode: string,\n\tsource: string,\n\tindex: number,\n\tconfig: JetFrameConfig,\n): FlighteraRow | null {\n\tconst text = String(rowText || '')\n\t\t.replace(/\\s+/g, ' ')\n\t\t.trim();\n\tconst upper = text.toUpperCase();\n\tconst op = clean(operationalCallsign).toUpperCase();\n\tconst iataLike = operationalToLikelyIataCallsign(op);\n\n\tconst isLive = /\\bLIVE\\b/i.test(text);\n\tconst containsOp = !!op && upper.indexOf(op) !== -1;\n\tconst containsIataLike = !!iataLike && upper.indexOf(iataLike.toUpperCase()) !== -1;\n\n\tconst calls: string[] = [];\n\tlet cm: RegExpExecArray | null;\n\n\tconst callRegex = /\\b([A-Z]{2,3}\\d{1,4}[A-Z]?)\\b/g;\n\n\t// LIVE Tabellenzeilen enthalten oft:\n\t// EN8760 DLA7WL\n\t// LH54 DLH1KN\n\t// -> erstes = Marketing\n\t// -> zweites = operational\n\n\tconst livePairRegex = /\\b([A-Z]{2,3}\\d{1,4}[A-Z]?)\\s+([A-Z]{3}\\d+[A-Z]{0,2})\\b/g;\n\n\twhile ((cm = callRegex.exec(upper)) !== null) {\n\t\tconst cs = clean(cm[1]).toUpperCase();\n\n\t\tif (!looksLikeMarketingCallsign(cs)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!calls.includes(cs)) {\n\t\t\tcalls.push(cs);\n\t\t}\n\t}\n\n\tlet routeCallsign = '';\n\n\t// WICHTIG:\n\t// Zuerst LIVE-Zeilen-Paare pr\u00FCfen:\n\t// EN8760 DLA7WL\n\t// LH54 DLH1KN\n\n\tconst pairMatches = [...text.matchAll(livePairRegex)];\n\n\tfor (const pm of pairMatches) {\n\t\tconst marketing = clean(pm[1]).toUpperCase();\n\t\tconst operational = clean(pm[2]).toUpperCase();\n\n\t\tif (operational === op) {\n\t\t\trouteCallsign = marketing;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Fallback alt\n\tif (!routeCallsign) {\n\t\tif (calls.includes(iataLike)) {\n\t\t\trouteCallsign = iataLike;\n\t\t} else if (calls.length) {\n\t\t\trouteCallsign = calls.find(cs => /[A-Z]/.test(cs)) || '';\n\t\t} else if (containsOp) {\n\t\t\trouteCallsign = op;\n\t\t}\n\t}\n\n\tif (!/[A-Z]/.test(routeCallsign)) {\n\t\trouteCallsign = containsOp ? op : '';\n\t}\n\n\tconst airportPairs: string[] = [];\n\tlet m: RegExpExecArray | null;\n\n\tconst pairRegex = /([A-Za-z\u00C4\u00D6\u00DC\u00E4\u00F6\u00FC\u00DF .'-]+?)\\s*\\(([A-Z]{3})\\s*\\/\\s*[A-Z]{4}\\)/g;\n\n\twhile ((m = pairRegex.exec(text)) !== null) {\n\t\tconst code = clean(m[2]).toUpperCase();\n\n\t\tif (isIataCode(code)) {\n\t\t\tairportPairs.push(code);\n\t\t}\n\t}\n\n\tlet originIata = '';\n\tlet destIata = '';\n\n\tif (airportPairs.length >= 2) {\n\t\toriginIata = airportPairs[0];\n\t\tdestIata = airportPairs[1];\n\t} else {\n\t\tconst codes: string[] = [];\n\t\tconst codeRegex = /\\b([A-Z]{3})\\s*\\/\\s*[A-Z]{4}\\b/g;\n\n\t\twhile ((m = codeRegex.exec(upper)) !== null) {\n\t\t\tconst code = clean(m[1]).toUpperCase();\n\n\t\t\tif (isIataCode(code) && !codes.includes(code)) {\n\t\t\t\tcodes.push(code);\n\t\t\t}\n\t\t}\n\n\t\tif (codes.length >= 2) {\n\t\t\toriginIata = codes[0];\n\t\t\tdestIata = codes[1];\n\t\t}\n\t}\n\n\toriginIata = clean(originIata).toUpperCase();\n\tdestIata = clean(destIata).toUpperCase();\n\n\tif (!originIata || !destIata) {\n\t\treturn null;\n\t}\n\tif (!isIataCode(originIata) || !isIataCode(destIata)) {\n\t\treturn null;\n\t}\n\tif (originIata === destIata) {\n\t\treturn null;\n\t}\n\n\tif (mode === 'TAKEOFF' && originIata !== config.airport.iata) {\n\t\treturn null;\n\t}\n\tif (mode === 'LANDING' && destIata !== config.airport.iata) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trouteCallsign,\n\t\toperationalCallsign: containsOp ? op : '',\n\t\toriginIata,\n\t\tdestIata,\n\t\tisLive,\n\t\tcontainsOp,\n\t\tcontainsIataLike,\n\t\tsource,\n\t\tindex,\n\t};\n}\n\nfunction pickBestFlighteraRow(\n\trows: FlighteraRow[],\n\toperationalCallsign: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n\tlogDebug: (msg: string, level?: number) => void,\n): FlighteraRow | null {\n\tif (!rows.length) {\n\t\treturn null;\n\t}\n\n\tconst op = clean(operationalCallsign).toUpperCase();\n\tconst iataLike = operationalToLikelyIataCallsign(op);\n\n\tconst scoreAndSort = (list: FlighteraRow[], bonus: number): FlighteraRow => {\n\t\tfor (const r of list) {\n\t\t\tr.score = scoreFlighteraRow(r, op, iataLike, mode, config) + bonus;\n\t\t}\n\n\t\tlist.sort((a, b) => (b.score || 0) - (a.score || 0));\n\t\treturn list[0];\n\t};\n\n\tconst liveExact = rows.filter(r => r.isLive && r.containsOp);\n\tif (liveExact.length) {\n\t\treturn scoreAndSort(liveExact, 50000);\n\t}\n\n\tconst liveMarketing = rows.filter(r => r.isLive && r.containsIataLike);\n\tif (liveMarketing.length) {\n\t\treturn scoreAndSort(liveMarketing, 40000);\n\t}\n\n\tconst liveRows = rows.filter(r => r.isLive);\n\tif (liveRows.length) {\n\t\treturn scoreAndSort(liveRows, 30000);\n\t}\n\n\tconst exact = rows.filter(r => r.containsOp || r.containsIataLike);\n\tif (exact.length) {\n\t\treturn scoreAndSort(exact, 10000);\n\t}\n\n\tlogDebug(`Flightera: keine passende Live/Callsign-Zeile f\u00FCr ${op}`);\n\treturn null;\n}\n\nfunction scoreFlighteraRow(\n\tr: FlighteraRow,\n\top: string,\n\tiataLike: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n): number {\n\tlet score = 0;\n\n\tif (r.isLive) {\n\t\tscore += 10000;\n\t}\n\tif (r.containsOp) {\n\t\tscore += 5000;\n\t}\n\tif (r.containsIataLike) {\n\t\tscore += 2500;\n\t}\n\n\tif (r.routeCallsign && iataLike && r.routeCallsign === iataLike) {\n\t\tscore += 1500;\n\t}\n\n\tif (r.routeCallsign && iataLike && r.routeCallsign.startsWith(iataLike.substring(0))) {\n\t\tscore += 400;\n\t}\n\n\tif (mode === 'TAKEOFF' && r.originIata === config.airport.iata) {\n\t\tscore += 1000;\n\t}\n\tif (mode === 'LANDING' && r.destIata === config.airport.iata) {\n\t\tscore += 1000;\n\t}\n\n\tscore -= Math.min(r.index || 0, 200000) / 1000;\n\n\treturn score;\n}\n\n/************************************************************\n * FR24 Live\n ************************************************************/\n\nasync function resolveRouteViaFr24Live(\n\toperationalCallsign: string,\n\tmode: string,\n\thttpText: HttpText,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n\tconfig: JetFrameConfig,\n): Promise<(RouteResult & { imageUrl?: string }) | null> {\n\tconst op = clean(operationalCallsign).toUpperCase();\n\n\tif (!op) {\n\t\treturn null;\n\t}\n\n\tconst now = Date.now();\n\tconst cached = fr24LiveRouteCache[op];\n\n\tif (cached && now - cached.ts < CACHE.fr24LiveMs) {\n\t\tlogDebug(`FR24 Live Cache hit: ${op}`);\n\t\treturn cached.data || null;\n\t}\n\n\tconst url = `https://www.flightradar24.com/${encodeURIComponent(op)}`;\n\n\ttry {\n\t\tlogDebug(`FR24 Live Anfrage EINMALIG: ${op} \u2192 ${url}`);\n\n\t\tconst htmlRaw = await httpText(url);\n\t\tconst html = normalizeHtml(htmlRaw);\n\t\tconst text = htmlToText(html);\n\n\t\tconst imageUrl = pickBestFr24Image(collectFr24Images(html));\n\t\tconst parsed = parseFr24LiveRoute(html, text, op, mode, config);\n\n\t\tconst result: RouteResult & { imageUrl?: string } = parsed || {\n\t\t\trouteCallsign: '',\n\t\t\toriginIata: '',\n\t\t\tdestIata: '',\n\t\t};\n\n\t\tif (imageUrl) {\n\t\t\tresult.imageUrl = imageUrl;\n\t\t}\n\n\t\tfr24LiveRouteCache[op] = {\n\t\t\tts: now,\n\t\t\tdata: result,\n\t\t};\n\n\t\treturn result;\n\t} catch (e) {\n\t\tfr24LiveRouteCache[op] = {\n\t\t\tts: now,\n\t\t\tdata: null,\n\t\t};\n\n\t\tlogWarn(`FR24 Live Fehler gecached f\u00FCr ${op}: ${errorText(e)}`);\n\t\treturn null;\n\t}\n}\n\nfunction parseFr24LiveRoute(\n\thtml: string,\n\ttext: string,\n\toperationalCallsign: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n): RouteResult | null {\n\tconst op = clean(operationalCallsign).toUpperCase();\n\tconst iataLike = operationalToLikelyIataCallsign(op);\n\n\tconst fullText = String(text || '')\n\t\t.replace(/\\s+/g, ' ')\n\t\t.trim();\n\tconst upper = fullText.toUpperCase();\n\n\tlet scope = fullText;\n\tlet idx = upper.indexOf(op);\n\n\tif (idx < 0 && iataLike) {\n\t\tidx = upper.indexOf(iataLike.toUpperCase());\n\t}\n\n\tif (idx >= 0) {\n\t\tconst start = Math.max(0, idx - 700);\n\t\tconst end = Math.min(fullText.length, idx + 2000);\n\t\tscope = fullText.substring(start, end);\n\t}\n\n\tconst row = parseRouteFromAirportPairs(scope, op, mode, config);\n\n\tif (row) {\n\t\trow.routeCallsign = findBestMarketingCallsign(scope, op) || iataLike || op;\n\t\treturn row;\n\t}\n\n\tconst jsonRoute = parseJsonLikeRouteFromHtml(html, op, mode, config);\n\tif (jsonRoute) {\n\t\treturn jsonRoute;\n\t}\n\n\treturn null;\n}\n\nfunction parseRouteFromAirportPairs(\n\tscope: string,\n\toperationalCallsign: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n): RouteResult | null {\n\tconst text = String(scope || '')\n\t\t.replace(/\\s+/g, ' ')\n\t\t.trim();\n\n\tconst pairs: string[] = [];\n\tlet m: RegExpExecArray | null;\n\n\tconst pairRegex = /([A-Za-z\u00C4\u00D6\u00DC\u00E4\u00F6\u00FC\u00DF .'-]+?)\\s*\\(([A-Z]{3})\\s*\\/\\s*[A-Z]{4}\\)/g;\n\n\twhile ((m = pairRegex.exec(text)) !== null) {\n\t\tconst code = clean(m[2]).toUpperCase();\n\n\t\tif (isIataCode(code)) {\n\t\t\tpairs.push(code);\n\t\t}\n\t}\n\n\tif (pairs.length < 2) {\n\t\treturn null;\n\t}\n\n\tlet originIata = '';\n\tlet destIata = '';\n\n\tif (mode === 'TAKEOFF') {\n\t\tconst aptIndex = pairs.indexOf(config.airport.iata);\n\n\t\tif (aptIndex >= 0 && pairs[aptIndex + 1]) {\n\t\t\toriginIata = config.airport.iata;\n\t\t\tdestIata = pairs[aptIndex + 1];\n\t\t}\n\t} else if (mode === 'LANDING') {\n\t\tconst aptIndex = pairs.indexOf(config.airport.iata);\n\n\t\tif (aptIndex > 0) {\n\t\t\toriginIata = pairs[aptIndex - 1];\n\t\t\tdestIata = config.airport.iata;\n\t\t}\n\t} else {\n\t\toriginIata = pairs[0];\n\t\tdestIata = pairs[1];\n\t}\n\n\toriginIata = clean(originIata).toUpperCase();\n\tdestIata = clean(destIata).toUpperCase();\n\n\tif (!isIataCode(originIata) || !isIataCode(destIata)) {\n\t\treturn null;\n\t}\n\tif (originIata === destIata) {\n\t\treturn null;\n\t}\n\n\tif (mode === 'TAKEOFF' && originIata !== config.airport.iata) {\n\t\treturn null;\n\t}\n\tif (mode === 'LANDING' && destIata !== config.airport.iata) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trouteCallsign: findBestMarketingCallsign(text, operationalCallsign),\n\t\toriginIata,\n\t\tdestIata,\n\t};\n}\n\nfunction parseJsonLikeRouteFromHtml(\n\thtml: string,\n\toperationalCallsign: string,\n\tmode: string,\n\tconfig: JetFrameConfig,\n): RouteResult | null {\n\thtml = normalizeHtml(html);\n\n\tconst iataHits: string[] = [];\n\tlet m: RegExpExecArray | null;\n\n\tconst iataRegexes = [\n\t\t/\"iata\"\\s*:\\s*\"([A-Z]{3})\"/g,\n\t\t/\"iataCode\"\\s*:\\s*\"([A-Z]{3})\"/g,\n\t\t/\"iata_code\"\\s*:\\s*\"([A-Z]{3})\"/g,\n\t];\n\n\tfor (const re of iataRegexes) {\n\t\twhile ((m = re.exec(html)) !== null) {\n\t\t\tconst code = clean(m[1]).toUpperCase();\n\n\t\t\tif (isIataCode(code) && !iataHits.includes(code)) {\n\t\t\t\tiataHits.push(code);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (iataHits.length < 2) {\n\t\treturn null;\n\t}\n\n\tlet originIata = '';\n\tlet destIata = '';\n\n\tif (mode === 'TAKEOFF') {\n\t\tconst aptIndex = iataHits.indexOf(config.airport.iata);\n\n\t\tif (aptIndex >= 0 && iataHits[aptIndex + 1]) {\n\t\t\toriginIata = config.airport.iata;\n\t\t\tdestIata = iataHits[aptIndex + 1];\n\t\t}\n\t} else if (mode === 'LANDING') {\n\t\tconst aptIndex = iataHits.indexOf(config.airport.iata);\n\n\t\tif (aptIndex > 0) {\n\t\t\toriginIata = iataHits[aptIndex - 1];\n\t\t\tdestIata = config.airport.iata;\n\t\t}\n\t} else {\n\t\toriginIata = iataHits[0];\n\t\tdestIata = iataHits[1];\n\t}\n\n\tif (!isIataCode(originIata) || !isIataCode(destIata)) {\n\t\treturn null;\n\t}\n\tif (originIata === destIata) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trouteCallsign: findBestMarketingCallsign(htmlToText(html), operationalCallsign),\n\t\toriginIata,\n\t\tdestIata,\n\t};\n}\n\n/************************************************************\n * FR24 Bild\n ************************************************************/\n\n/**\n *\n */\nexport async function resolveImageViaFr24Aircraft(\n\tregistration: string,\n\toperationalCallsign: string,\n\thttpText: HttpText,\n\tlogDebug: (msg: string, level?: number) => void,\n\tlogWarn: (msg: string) => void,\n): Promise<string> {\n\tconst reg = clean(registration).toLowerCase();\n\tconst op = clean(operationalCallsign).toUpperCase();\n\n\tif (!reg) {\n\t\treturn '';\n\t}\n\n\tconst now = Date.now();\n\tconst cached = fr24AircraftCache[reg];\n\n\tif (cached && now - cached.ts < CACHE.fr24Ms) {\n\t\tlogDebug(`FR24 Bild Cache hit: ${reg}`);\n\t\treturn cached.imageUrl || '';\n\t}\n\n\tconst url = `https://www.flightradar24.com/data/aircraft/${encodeURIComponent(reg)}`;\n\n\ttry {\n\t\tlogDebug(`FR24 Aircraft Bild Anfrage EINMALIG: ${reg} / ${op} \u2192 ${url}`);\n\n\t\tconst htmlRaw = await httpText(url);\n\t\tconst html = normalizeHtml(htmlRaw);\n\n\t\tconst imageUrl = pickBestFr24Image(collectFr24Images(html));\n\n\t\tfr24AircraftCache[reg] = {\n\t\t\tts: now,\n\t\t\timageUrl: imageUrl || '',\n\t\t};\n\n\t\treturn imageUrl || '';\n\t} catch (e) {\n\t\tfr24AircraftCache[reg] = {\n\t\t\tts: now,\n\t\t\timageUrl: '',\n\t\t};\n\n\t\tlogWarn(`FR24 Bild Fehler gecached f\u00FCr ${reg}: ${errorText(e)}`);\n\t\treturn '';\n\t}\n}\n\n/************************************************************\n * Airport names / unknown route\n ************************************************************/\n\nasync function applyAirportNamesFromJson(\n\tadapter: AdapterLike,\n\tconfig: JetFrameConfig,\n\tparsed: ParsedInfo,\n\tlogWarn: (msg: string) => void,\n): Promise<ParsedInfo> {\n\tconst originName = parsed.originIata ? await cityNameFromIata(adapter, config, parsed.originIata, logWarn) : '';\n\n\tconst destName = parsed.destIata ? await cityNameFromIata(adapter, config, parsed.destIata, logWarn) : '';\n\n\tparsed.originName = originName;\n\tparsed.destName = destName;\n\n\tif (parsed.originIata && parsed.destIata) {\n\t\tparsed.routeText = `${parsed.originIata} \u2192 ${parsed.destIata}`;\n\t}\n\n\tif (originName && destName) {\n\t\tparsed.routeTextLong = `${originName} \u2192 ${destName}`;\n\t} else if (originName && !destName && parsed.destIata) {\n\t\tparsed.routeTextLong = `${originName} \u2192 ${parsed.destIata}`;\n\t} else if (!originName && destName && parsed.originIata) {\n\t\tparsed.routeTextLong = `${parsed.originIata} \u2192 ${destName}`;\n\t} else {\n\t\tparsed.routeTextLong = '';\n\t}\n\n\treturn parsed;\n}\n\nasync function cityNameFromIata(\n\tadapter: AdapterLike,\n\tconfig: JetFrameConfig,\n\tiata: string,\n\tlogWarn: (msg: string) => void,\n): Promise<string> {\n\tconst lang = await getSystemLanguage(adapter);\n\tconst useGermanNames = lang.toLowerCase().startsWith('de');\n\tconst code = clean(iata).toUpperCase();\n\n\tif (!code) {\n\t\treturn '';\n\t}\n\n\ttry {\n\t\tconst st = await adapter.getForeignStateAsync(config.airportJsonDp);\n\t\tconst raw = st?.val ? String(st.val) : '';\n\n\t\tif (!raw || raw === '[]') {\n\t\t\treturn code;\n\t\t}\n\n\t\tconst airports = JSON.parse(raw);\n\n\t\tif (!Array.isArray(airports)) {\n\t\t\treturn code;\n\t\t}\n\n\t\tconst found = airports.find((a: any) => clean(a.iata || a.IATA).toUpperCase() === code);\n\n\t\tif (!found) {\n\t\t\treturn code;\n\t\t}\n\n\t\t// municipality bevorzugen:\n\t\t// IAD => Washington statt Dulles\n\t\t// CDG => Paris statt Roissy\n\n\t\tconst cityDe = clean(found.city_DE);\n\n\t\tconst municipality = clean(found.municipality);\n\n\t\tconst city = clean(found.city);\n\n\t\tconst airport = clean(found.airport || found.name);\n\n\t\tif (useGermanNames && cityDe && cityDe.length >= 3) {\n\t\t\treturn cityDe;\n\t\t}\n\n\t\tif (municipality && municipality.length >= 3 && !/airport|flug|intl|international/i.test(municipality)) {\n\t\t\treturn municipality;\n\t\t}\n\n\t\tif (city && city.length >= 3) {\n\t\t\treturn city;\n\t\t}\n\n\t\tif (airport) {\n\t\t\treturn airport\n\t\t\t\t.replace(/international airport/gi, '')\n\t\t\t\t.replace(/international/gi, '')\n\t\t\t\t.replace(/airport/gi, '')\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t.trim();\n\t\t}\n\n\t\treturn code;\n\t} catch (e) {\n\t\tlogWarn(`airportjson Lookup Fehler f\u00FCr ${code}: ${errorText(e)}`);\n\t\treturn code;\n\t}\n}\n\nasync function getSystemLanguage(adapter: AdapterLike): Promise<string> {\n\ttry {\n\t\tconst obj = await adapter.getForeignObjectAsync('system.config');\n\n\t\treturn String(obj?.common?.language || obj?.native?.language || '').trim();\n\t} catch {\n\t\treturn '';\n\t}\n}\n\nfunction makeUnknownAirportRoute(mode: string, parsed: ParsedInfo, config: JetFrameConfig): ParsedInfo {\n\tconst A = config.airport.iata;\n\n\tif (mode === 'TAKEOFF') {\n\t\treturn {\n\t\t\t...parsed,\n\t\t\toriginIata: A,\n\t\t\tdestIata: '',\n\t\t\toriginName: '',\n\t\t\tdestName: '',\n\t\t\trouteText: `${A} \u2192 ?`,\n\t\t\trouteTextLong: '',\n\t\t\trouteReliable: false,\n\t\t\trouteWarning: 'Ziel unbekannt',\n\t\t\trouteSource: 'no-route',\n\t\t};\n\t}\n\n\tif (mode === 'LANDING') {\n\t\treturn {\n\t\t\t...parsed,\n\t\t\toriginIata: '',\n\t\t\tdestIata: A,\n\t\t\toriginName: '',\n\t\t\tdestName: '',\n\t\t\trouteText: `? \u2192 ${A}`,\n\t\t\trouteTextLong: '',\n\t\t\trouteReliable: false,\n\t\t\trouteWarning: 'Start unbekannt',\n\t\t\trouteSource: 'no-route',\n\t\t};\n\t}\n\n\treturn {\n\t\t...parsed,\n\t\toriginIata: '',\n\t\tdestIata: '',\n\t\trouteText: '',\n\t\trouteTextLong: '',\n\t\trouteReliable: false,\n\t\trouteWarning: 'Route unbekannt',\n\t\trouteSource: 'no-route',\n\t};\n}\n\n/************************************************************\n * Image helpers\n ************************************************************/\n\nfunction collectFr24Images(html: string): string[] {\n\thtml = normalizeHtml(html);\n\n\tconst found: string[] = [];\n\n\tfunction add(url: string): void {\n\t\turl = normalizeImageUrl(url);\n\n\t\tif (!url) {\n\t\t\treturn;\n\t\t}\n\t\tif (!/\\.(jpg|jpeg|webp|png)(\\?|$)/i.test(url)) {\n\t\t\treturn;\n\t\t}\n\t\tif (found.includes(url)) {\n\t\t\treturn;\n\t\t}\n\t\tif (/logo|icon|sprite|avatar|upgrade|plans|app-store|google-play/i.test(url)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfound.push(url);\n\t}\n\n\tlet m: RegExpExecArray | null;\n\n\tconst regexes = [\n\t\t/<img[^>]+src=[\"']([^\"']+)[\"'][^>]*alt=[\"'][^\"']*(?:aircraft|plane|photo|picture)[^\"']*[\"']/gi,\n\t\t/<img[^>]+alt=[\"'][^\"']*(?:aircraft|plane|photo|picture)[^\"']*[\"'][^>]+src=[\"']([^\"']+)[\"']/gi,\n\t\t/https?:\\/\\/[^\"'<> ]+\\.(?:jpg|jpeg|webp|png)(?:\\?[^\"'<> ]*)?/gi,\n\t\t/src=[\"']([^\"']+\\.(?:jpg|jpeg|webp|png)(?:\\?[^\"']*)?)[\"']/gi,\n\t\t/content=[\"']([^\"']+\\.(?:jpg|jpeg|webp|png)(?:\\?[^\"']*)?)[\"']/gi,\n\t];\n\n\tfor (const re of regexes) {\n\t\twhile ((m = re.exec(html)) !== null) {\n\t\t\tadd(m[1] || m[0]);\n\t\t}\n\t}\n\n\treturn found;\n}\n\nfunction pickBestFr24Image(images: string[]): string {\n\tif (!images.length) {\n\t\treturn '';\n\t}\n\n\treturn images.sort((a, b) => scoreFr24Image(b) - scoreFr24Image(a))[0];\n}\n\nfunction scoreFr24Image(url: string): number {\n\turl = String(url || '').toLowerCase();\n\n\tlet score = 0;\n\n\tif (url.includes('fr24')) {\n\t\tscore += 60;\n\t}\n\tif (url.includes('cdn')) {\n\t\tscore += 50;\n\t}\n\tif (url.includes('aircraft')) {\n\t\tscore += 50;\n\t}\n\tif (url.includes('large')) {\n\t\tscore += 40;\n\t}\n\tif (url.includes('full')) {\n\t\tscore += 40;\n\t}\n\tif (url.includes('photo')) {\n\t\tscore += 20;\n\t}\n\n\tif (url.includes('thumb')) {\n\t\tscore -= 80;\n\t}\n\tif (url.includes('small')) {\n\t\tscore -= 50;\n\t}\n\tif (url.includes('logo') || url.includes('icon')) {\n\t\tscore -= 100;\n\t}\n\n\treturn score;\n}\n\n/************************************************************\n * Guessing / special\n ************************************************************/\n\nfunction buildSpecialInfo(a: Partial<Aircraft>): Partial<Aircraft> {\n\tconst model = String(a.aircraftModel || a.aircraftType || a.type || '').toUpperCase();\n\n\tconst callsign = String(a.callsign || '').toUpperCase();\n\n\tconst tags: string[] = [];\n\tlet score = 0;\n\n\t// --------------------------------------------------\n\t// A380\n\t// --------------------------------------------------\n\n\tif (/A38/i.test(model)) {\n\t\ttags.push('Airbus A380');\n\t\tscore += 10;\n\t}\n\t// --------------------------------------------------\n\t// B747 / Jumbo\n\t// --------------------------------------------------\n\n\tif (/B74/i.test(model)) {\n\t\ttags.push('Boeing 747');\n\t\tscore += 8;\n\t}\n\n\t// --------------------------------------------------\n\t// Beluga\n\t// --------------------------------------------------\n\n\tif (containsAny(model, ['BELUGA'])) {\n\t\ttags.push('Airbus Beluga');\n\t\tscore += 10;\n\t}\n\n\t// --------------------------------------------------\n\t// Antonov\n\t// --------------------------------------------------\n\n\tif (containsAny(model, ['AN-124', 'ANTONOV', 'AN225', 'AN-225'])) {\n\t\ttags.push('Antonov');\n\t\tscore += 10;\n\t}\n\n\t// --------------------------------------------------\n\t// Regierungs-/Milit\u00E4rflug\n\t// --------------------------------------------------\n\n\tif (containsAny(callsign, ['GAF', 'GOV', 'BAF', 'NAF', 'RCH', 'IAM'])) {\n\t\ttags.push('Regierungs-/Milit\u00E4rflug');\n\t\tscore += 8;\n\t}\n\n\treturn {\n\t\tisSpecial: score >= 8,\n\t\tspecialText: tags.length ? tags.join(', ') : '',\n\t};\n}\n\nfunction guessAirlineIata(callsign: string): string {\n\tcallsign = clean(callsign).toUpperCase();\n\n\tconst map: Record<string, string> = {\n\t\tDLH: 'LH',\n\t\tCFG: 'DE',\n\t\tCPA: 'CX',\n\t\tKAL: 'KE',\n\t\tGEC: 'LH',\n\t\tBOX: '3S',\n\t\tEWG: 'EW',\n\t\tRYR: 'FR',\n\t\tEZY: 'U2',\n\t\tBAW: 'BA',\n\t\tKLM: 'KL',\n\t\tAFR: 'AF',\n\t\tSWR: 'LX',\n\t\tAUA: 'OS',\n\t\tSIA: 'SQ',\n\t\tTHY: 'TK',\n\t\tUAE: 'EK',\n\t\tQTR: 'QR',\n\t\tCCA: 'CA',\n\t\tROT: 'RO',\n\t\tSAS: 'SK',\n\t\tSEH: 'GQ',\n\t};\n\n\treturn map[callsign.substring(0)] || '';\n}\n\nfunction guessAirlineIcao(callsign: string): string {\n\tcallsign = clean(callsign).toUpperCase();\n\treturn callsign.length >= 3 ? callsign.substring(0, 3) : '';\n}\n\nfunction guessAirlineName(callsign: string): string {\n\tcallsign = clean(callsign).toUpperCase();\n\n\tif (callsign.startsWith('DLH')) {\n\t\treturn 'Lufthansa';\n\t}\n\tif (callsign.startsWith('CFG')) {\n\t\treturn 'Condor';\n\t}\n\tif (callsign.startsWith('CPA')) {\n\t\treturn 'Cathay Pacific';\n\t}\n\tif (callsign.startsWith('KAL')) {\n\t\treturn 'Korean Air Cargo';\n\t}\n\tif (callsign.startsWith('GEC')) {\n\t\treturn 'Lufthansa Cargo';\n\t}\n\tif (callsign.startsWith('BOX')) {\n\t\treturn 'AeroLogic';\n\t}\n\tif (callsign.startsWith('EWG')) {\n\t\treturn 'Eurowings';\n\t}\n\tif (callsign.startsWith('RYR')) {\n\t\treturn 'Ryanair';\n\t}\n\tif (callsign.startsWith('EZY')) {\n\t\treturn 'easyJet';\n\t}\n\tif (callsign.startsWith('BAW')) {\n\t\treturn 'British Airways';\n\t}\n\tif (callsign.startsWith('KLM')) {\n\t\treturn 'KLM';\n\t}\n\tif (callsign.startsWith('AFR')) {\n\t\treturn 'Air France';\n\t}\n\tif (callsign.startsWith('SWR')) {\n\t\treturn 'SWISS';\n\t}\n\tif (callsign.startsWith('AUA')) {\n\t\treturn 'Austrian';\n\t}\n\tif (callsign.startsWith('SIA')) {\n\t\treturn 'Singapore Airlines';\n\t}\n\tif (callsign.startsWith('THY')) {\n\t\treturn 'Turkish Airlines';\n\t}\n\tif (callsign.startsWith('UAE')) {\n\t\treturn 'Emirates';\n\t}\n\tif (callsign.startsWith('QTR')) {\n\t\treturn 'Qatar Airways';\n\t}\n\tif (callsign.startsWith('CCA')) {\n\t\treturn 'Air China';\n\t}\n\tif (callsign.startsWith('ROT')) {\n\t\treturn 'TAROM';\n\t}\n\tif (callsign.startsWith('SAS')) {\n\t\treturn 'SAS';\n\t}\n\tif (callsign.startsWith('SEH')) {\n\t\treturn 'Sky Express';\n\t}\n\n\treturn '';\n}\n\nfunction operationalToLikelyIataCallsign(callsign: string): string {\n\tcallsign = clean(callsign).toUpperCase();\n\n\tconst map: Record<string, string> = {\n\t\tDLH: 'LH',\n\t\tCFG: 'DE',\n\t\tCPA: 'CX',\n\t\tKAL: 'KE',\n\t\tGEC: 'LH',\n\t\tBOX: '3S',\n\t\tEWG: 'EW',\n\t\tRYR: 'FR',\n\t\tEZY: 'U2',\n\t\tBAW: 'BA',\n\t\tKLM: 'KL',\n\t\tAFR: 'AF',\n\t\tSWR: 'LX',\n\t\tAUA: 'OS',\n\t\tSIA: 'SQ',\n\t\tTHY: 'TK',\n\t\tUAE: 'EK',\n\t\tQTR: 'QR',\n\t\tETD: 'EY',\n\t\tIBE: 'IB',\n\t\tTAP: 'TP',\n\t\tSAS: 'SK',\n\t\tFIN: 'AY',\n\t\tLOT: 'LO',\n\t\tCCA: 'CA',\n\t\tROT: 'RO',\n\t\tSEH: 'GQ',\n\t};\n\n\tconst prefix = callsign.substring(0, 3);\n\tconst rest = callsign.substring(3);\n\n\tif (map[prefix] && rest) {\n\t\treturn map[prefix] + rest;\n\t}\n\n\treturn callsign;\n}\n\n/************************************************************\n * Helpers\n ************************************************************/\n\nfunction findBestMarketingCallsign(text: string, operationalCallsign: string): string {\n\tconst op = clean(operationalCallsign).toUpperCase();\n\tconst iataLike = operationalToLikelyIataCallsign(op);\n\tconst upper = String(text || '').toUpperCase();\n\n\tif (iataLike && upper.includes(iataLike)) {\n\t\treturn iataLike;\n\t}\n\n\tconst calls: string[] = [];\n\tlet m: RegExpExecArray | null;\n\n\tconst re = /\\b([A-Z0-9]{2}\\d{1,4}[A-Z]?)\\b/g;\n\n\twhile ((m = re.exec(upper)) !== null) {\n\t\tconst cs = clean(m[1]).toUpperCase();\n\n\t\tif (!looksLikeMarketingCallsign(cs)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!calls.includes(cs)) {\n\t\t\tcalls.push(cs);\n\t\t}\n\t}\n\n\treturn calls.length ? calls[0] : '';\n}\n\nfunction looksLikeMarketingCallsign(cs: string): boolean {\n\tcs = clean(cs).toUpperCase();\n\n\tif (!/^[A-Z0-9]{2}\\d{1,4}[A-Z]?$/.test(cs)) {\n\t\treturn false;\n\t}\n\tif (/^[A-Z]{3}\\d/.test(cs)) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nfunction isIataCode(code: string): boolean {\n\tcode = clean(code).toUpperCase();\n\n\tif (!/^[A-Z]{3}$/.test(code)) {\n\t\treturn false;\n\t}\n\n\tconst bad = [\n\t\t'THE',\n\t\t'AND',\n\t\t'FOR',\n\t\t'YOU',\n\t\t'ARE',\n\t\t'NOT',\n\t\t'YES',\n\t\t'NEW',\n\t\t'OLD',\n\t\t'AIR',\n\t\t'API',\n\t\t'APP',\n\t\t'MAP',\n\t\t'UTC',\n\t\t'ETA',\n\t\t'STD',\n\t\t'STA',\n\t\t'ATD',\n\t\t'ATA',\n\t\t'IMG',\n\t\t'PNG',\n\t\t'JPG',\n\t\t'WEB',\n\t\t'CSS',\n\t\t'DIV',\n\t\t'SVG',\n\t\t'WWW',\n\t\t'TOP',\n\t\t'VAR',\n\t\t'REL',\n\t\t'ORG',\n\t\t'USE',\n\t\t'DAY',\n\t\t'PER',\n\t\t'MMM',\n\t\t'MAY',\n\t\t'BTN',\n\t\t'HEX',\n\t\t'NET',\n\t\t'SRC',\n\t\t'PAN',\n\t\t'COL',\n\t\t'VON',\n\t\t'NACH',\n\t\t'ABF',\n\t\t'ANK',\n\t\t'LIVE',\n\t];\n\n\treturn !bad.includes(code);\n}\n\nfunction normalizeHtml(html: string): string {\n\treturn String(html || '')\n\t\t.replace(/\\\\\\//g, '/')\n\t\t.replace(/&amp;/g, '&')\n\t\t.replace(/&#x2F;/g, '/')\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.replace(/\\\\u002F/g, '/')\n\t\t.replace(/\\\\/g, '');\n}\n\nfunction htmlToText(html: string): string {\n\treturn decodeHtml(html)\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n\t\t.replace(/<br\\s*\\/?>/gi, ' ')\n\t\t.replace(/<\\/(?:tr|td|th|div|section|article|li|p)>/gi, ' ')\n\t\t.replace(/<[^>]+>/g, ' ')\n\t\t.replace(/\\s+/g, ' ')\n\t\t.trim();\n}\n\nfunction decodeHtml(s: string): string {\n\treturn String(s || '')\n\t\t.replace(/&amp;/g, '&')\n\t\t.replace(/&nbsp;/g, ' ')\n\t\t.replace(/&rarr;/g, '\u2192')\n\t\t.replace(/&#8594;/g, '\u2192')\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.replace(/&lt;/g, '<')\n\t\t.replace(/&gt;/g, '>');\n}\n\nfunction normalizeImageUrl(url: string): string {\n\treturn String(url || '')\n\t\t.replace(/\\\\\\//g, '/')\n\t\t.replace(/&amp;/g, '&')\n\t\t.replace(/&#x2F;/g, '/')\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.trim();\n}\n\nfunction containsAny(text: string, arr: string[]): boolean {\n\ttext = String(text || '').toUpperCase();\n\treturn arr.some(x => text.includes(String(x).toUpperCase()));\n}\n\nfunction clean(v: unknown): string {\n\treturn String(v || '').trim();\n}\n\nfunction errorText(e: unknown): string {\n\tif (!e) {\n\t\treturn 'unbekannter Fehler';\n\t}\n\tif (typeof e === 'string') {\n\t\treturn e;\n\t}\n\tif (e instanceof Error) {\n\t\treturn e.message;\n\t}\n\n\ttry {\n\t\treturn JSON.stringify(e);\n\t} catch {\n\t\treturn String(e);\n\t}\n}\n\n/************************************************************\n * Interfaces\n ************************************************************/\n\ninterface ParsedInfo extends Partial<Aircraft> {\n\tfr24ImageUrl?: string;\n\tlogoFallbackUrl?: string;\n}\n\ninterface RouteResult {\n\trouteCallsign?: string;\n\toriginIata: string;\n\tdestIata: string;\n\tisLive?: boolean;\n}\n\ninterface FlighteraRow {\n\trouteCallsign: string;\n\toperationalCallsign: string;\n\toriginIata: string;\n\tdestIata: string;\n\tisLive: boolean;\n\tcontainsOp: boolean;\n\tcontainsIataLike: boolean;\n\tsource: string;\n\tindex: number;\n\tscore?: number;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var geo_exports = {};
20
+ __export(geo_exports, {
21
+ bearingDeg: () => bearingDeg,
22
+ deg2rad: () => deg2rad,
23
+ distanceNm: () => distanceNm,
24
+ normalizeDeg: () => normalizeDeg,
25
+ rad2deg: () => rad2deg,
26
+ round: () => round,
27
+ signedAngleDiff: () => signedAngleDiff,
28
+ smallestAngleDiff: () => smallestAngleDiff
29
+ });
30
+ module.exports = __toCommonJS(geo_exports);
31
+ function deg2rad(d) {
32
+ return d * Math.PI / 180;
33
+ }
34
+ function rad2deg(r) {
35
+ return r * 180 / Math.PI;
36
+ }
37
+ function normalizeDeg(d) {
38
+ if (!Number.isFinite(d)) {
39
+ return 0;
40
+ }
41
+ return (d % 360 + 360) % 360;
42
+ }
43
+ function smallestAngleDiff(a, b) {
44
+ let diff = Math.abs(normalizeDeg(a) - normalizeDeg(b));
45
+ if (diff > 180) {
46
+ diff = 360 - diff;
47
+ }
48
+ return diff;
49
+ }
50
+ function signedAngleDiff(targetDeg, referenceDeg) {
51
+ return (normalizeDeg(targetDeg) - normalizeDeg(referenceDeg) + 540) % 360 - 180;
52
+ }
53
+ function distanceNm(lat1, lon1, lat2, lon2) {
54
+ const R = 3440.065;
55
+ const dLat = deg2rad(lat2 - lat1);
56
+ const dLon = deg2rad(lon2 - lon1);
57
+ const a = Math.sin(dLat / 2) ** 2 + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) ** 2;
58
+ return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
59
+ }
60
+ function bearingDeg(lat1, lon1, lat2, lon2) {
61
+ const phi1 = deg2rad(lat1);
62
+ const phi2 = deg2rad(lat2);
63
+ const lambda1 = deg2rad(lon1);
64
+ const lambda2 = deg2rad(lon2);
65
+ const y = Math.sin(lambda2 - lambda1) * Math.cos(phi2);
66
+ const x = Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(lambda2 - lambda1);
67
+ return normalizeDeg(rad2deg(Math.atan2(y, x)));
68
+ }
69
+ function round(v, digits) {
70
+ const f = Math.pow(10, digits);
71
+ return Math.round(Number(v || 0) * f) / f;
72
+ }
73
+ // Annotate the CommonJS export names for ESM import in node:
74
+ 0 && (module.exports = {
75
+ bearingDeg,
76
+ deg2rad,
77
+ distanceNm,
78
+ normalizeDeg,
79
+ rad2deg,
80
+ round,
81
+ signedAngleDiff,
82
+ smallestAngleDiff
83
+ });
84
+ //# sourceMappingURL=geo.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/geo.ts"],
4
+ "sourcesContent": ["/**\n *\n * @param d\n */\nexport function deg2rad(d: number): number {\n\treturn (d * Math.PI) / 180;\n}\n\n/**\n *\n */\nexport function rad2deg(r: number): number {\n\treturn (r * 180) / Math.PI;\n}\n\n/**\n *\n */\nexport function normalizeDeg(d: number): number {\n\tif (!Number.isFinite(d)) {\n\t\treturn 0;\n\t}\n\treturn ((d % 360) + 360) % 360;\n}\n\n/**\n *\n */\nexport function smallestAngleDiff(a: number, b: number): number {\n\tlet diff = Math.abs(normalizeDeg(a) - normalizeDeg(b));\n\tif (diff > 180) {\n\t\tdiff = 360 - diff;\n\t}\n\treturn diff;\n}\n/**\n *\n */\nexport function signedAngleDiff(targetDeg: number, referenceDeg: number): number {\n\treturn ((normalizeDeg(targetDeg) - normalizeDeg(referenceDeg) + 540) % 360) - 180;\n}\n/**\n *\n */\nexport function distanceNm(lat1: number, lon1: number, lat2: number, lon2: number): number {\n\tconst R = 3440.065;\n\n\tconst dLat = deg2rad(lat2 - lat1);\n\tconst dLon = deg2rad(lon2 - lon1);\n\n\tconst a = Math.sin(dLat / 2) ** 2 + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) ** 2;\n\n\treturn R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n}\n\n/**\n *\n */\nexport function bearingDeg(lat1: number, lon1: number, lat2: number, lon2: number): number {\n\tconst phi1 = deg2rad(lat1);\n\tconst phi2 = deg2rad(lat2);\n\tconst lambda1 = deg2rad(lon1);\n\tconst lambda2 = deg2rad(lon2);\n\n\tconst y = Math.sin(lambda2 - lambda1) * Math.cos(phi2);\n\n\tconst x = Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(lambda2 - lambda1);\n\n\treturn normalizeDeg(rad2deg(Math.atan2(y, x)));\n}\n\n/**\n *\n */\nexport function round(v: number, digits: number): number {\n\tconst f = Math.pow(10, digits);\n\treturn Math.round(Number(v || 0) * f) / f;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,SAAS,QAAQ,GAAmB;AAC1C,SAAQ,IAAI,KAAK,KAAM;AACxB;AAKO,SAAS,QAAQ,GAAmB;AAC1C,SAAQ,IAAI,MAAO,KAAK;AACzB;AAKO,SAAS,aAAa,GAAmB;AAC/C,MAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACxB,WAAO;AAAA,EACR;AACA,UAAS,IAAI,MAAO,OAAO;AAC5B;AAKO,SAAS,kBAAkB,GAAW,GAAmB;AAC/D,MAAI,OAAO,KAAK,IAAI,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC;AACrD,MAAI,OAAO,KAAK;AACf,WAAO,MAAM;AAAA,EACd;AACA,SAAO;AACR;AAIO,SAAS,gBAAgB,WAAmB,cAA8B;AAChF,UAAS,aAAa,SAAS,IAAI,aAAa,YAAY,IAAI,OAAO,MAAO;AAC/E;AAIO,SAAS,WAAW,MAAc,MAAc,MAAc,MAAsB;AAC1F,QAAM,IAAI;AAEV,QAAM,OAAO,QAAQ,OAAO,IAAI;AAChC,QAAM,OAAO,QAAQ,OAAO,IAAI;AAEhC,QAAM,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,KAAK,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK;AAE9G,SAAO,IAAI,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC;AACzD;AAKO,SAAS,WAAW,MAAc,MAAc,MAAc,MAAsB;AAC1F,QAAM,OAAO,QAAQ,IAAI;AACzB,QAAM,OAAO,QAAQ,IAAI;AACzB,QAAM,UAAU,QAAQ,IAAI;AAC5B,QAAM,UAAU,QAAQ,IAAI;AAE5B,QAAM,IAAI,KAAK,IAAI,UAAU,OAAO,IAAI,KAAK,IAAI,IAAI;AAErD,QAAM,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,UAAU,OAAO;AAExG,SAAO,aAAa,QAAQ,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9C;AAKO,SAAS,MAAM,GAAW,QAAwB;AACxD,QAAM,IAAI,KAAK,IAAI,IAAI,MAAM;AAC7B,SAAO,KAAK,MAAM,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI;AACzC;",
6
+ "names": []
7
+ }