@rr0/ufoathome 0.137.0 → 0.139.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 (61) hide show
  1. package/dist-embed-scene/BeamSystem-Dp1WU1Zx.js +1 -0
  2. package/dist-embed-scene/ContrailGrowth-DwHS8If9.js +1 -0
  3. package/dist-embed-scene/ContrailSystem-GO0Hkyj3.js +1 -0
  4. package/dist-embed-scene/{SceneNames_es-D_98QraV.js → SceneNames_es-BS2ioieu.js} +1 -1
  5. package/dist-embed-scene/{SceneNames_fr-y6Z57BDf.js → SceneNames_fr-B-fRwUUu.js} +1 -1
  6. package/dist-embed-scene/{SceneNames_it-DQt9yIh1.js → SceneNames_it-B-QEwmuM.js} +1 -1
  7. package/dist-embed-scene/rr0-scene.mjs +346 -346
  8. package/dist-embed-scene/sightingSchema-Dn9Nacua.js +1 -0
  9. package/{dist-embed-sighting/trafficRuntime-sSIo5Lms.js → dist-embed-scene/trafficRuntime-BPEJW9KZ.js} +2 -2
  10. package/dist-embed-sighting/BeamSystem-Ddt2lTBT.js +1 -0
  11. package/dist-embed-sighting/ContrailGrowth-DwHS8If9.js +1 -0
  12. package/dist-embed-sighting/ContrailSystem-D_EBfThB.js +1 -0
  13. package/dist-embed-sighting/{SceneNames_es-Zb4vw-Vp.js → SceneNames_es-wkWHMHet.js} +1 -1
  14. package/dist-embed-sighting/{SceneNames_fr-C6_IIN2c.js → SceneNames_fr-DbbzZ1bB.js} +1 -1
  15. package/dist-embed-sighting/{SceneNames_it-D7GivBq2.js → SceneNames_it-BsxnweGu.js} +1 -1
  16. package/dist-embed-sighting/rr0-sighting.mjs +330 -330
  17. package/dist-embed-sighting/sightingSchema-Dn9Nacua.js +1 -0
  18. package/dist-embed-sighting/trafficRuntime-CfPqH9j0.js +2 -0
  19. package/dist-embed-sighting-editor/BeamSystem-B3qYVuHd.js +1 -0
  20. package/dist-embed-sighting-editor/ContrailGrowth-DwHS8If9.js +1 -0
  21. package/dist-embed-sighting-editor/ContrailSystem-D9rPfOBE.js +1 -0
  22. package/dist-embed-sighting-editor/{InterviewEditorElement-DHxRISPC.js → InterviewEditorElement-CJxgekiU.js} +2 -2
  23. package/dist-embed-sighting-editor/LaunchEditor-CPa-pltK.js +18 -0
  24. package/dist-embed-sighting-editor/LaunchEditorMessages_en-DH8gI1vo.js +1 -0
  25. package/dist-embed-sighting-editor/LaunchEditorMessages_es-WCfaY4uY.js +1 -0
  26. package/dist-embed-sighting-editor/LaunchEditorMessages_fr-CWCGUG_A.js +1 -0
  27. package/dist-embed-sighting-editor/LaunchEditorMessages_it-DxSSiAd7.js +1 -0
  28. package/dist-embed-sighting-editor/{SceneNames_es-7RR79EmC.js → SceneNames_es-umTyyCxN.js} +1 -1
  29. package/dist-embed-sighting-editor/{SceneNames_fr-Da_S9KXa.js → SceneNames_fr-BNgnxIHx.js} +1 -1
  30. package/dist-embed-sighting-editor/{SceneNames_it-BwFZROII.js → SceneNames_it-5UYhmJHo.js} +1 -1
  31. package/dist-embed-sighting-editor/SightingEditorMessages_es-UiUywEk4.js +12 -0
  32. package/dist-embed-sighting-editor/{SightingEditorMessages_fr-90Ojknvv.js → SightingEditorMessages_fr-D5pajHZd.js} +2 -2
  33. package/dist-embed-sighting-editor/SightingEditorMessages_it-9SduHYDB.js +12 -0
  34. package/dist-embed-sighting-editor/{SightingFileEditor-DcOCyimm.js → SightingFileEditor-CJX1-U4q.js} +1 -1
  35. package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +458 -435
  36. package/dist-embed-sighting-editor/sightingSchema-Dn9Nacua.js +1 -0
  37. package/dist-embed-sighting-editor/surroundingsSources-BK1okKkz.js +1 -0
  38. package/dist-embed-sighting-editor/trafficRuntime-BAGbGiVc.js +2 -0
  39. package/package.json +1 -1
  40. package/dist-embed-scene/BeamSystem-DDqbr8Bb.js +0 -1
  41. package/dist-embed-scene/ContrailGrowth-CuAzwIoc.js +0 -1
  42. package/dist-embed-scene/ContrailSystem-CG5BhBF7.js +0 -1
  43. package/dist-embed-scene/sightingSchema-CNCtrneh.js +0 -1
  44. package/dist-embed-scene/trafficRuntime-CgM31CwB.js +0 -2
  45. package/dist-embed-sighting/BeamSystem-wkZ6I_8Y.js +0 -1
  46. package/dist-embed-sighting/ContrailGrowth-CuAzwIoc.js +0 -1
  47. package/dist-embed-sighting/ContrailSystem-XVzDvxFV.js +0 -1
  48. package/dist-embed-sighting/sightingSchema-CNCtrneh.js +0 -1
  49. package/dist-embed-sighting-editor/BeamSystem-YGYbbAkU.js +0 -1
  50. package/dist-embed-sighting-editor/ContrailGrowth-CuAzwIoc.js +0 -1
  51. package/dist-embed-sighting-editor/ContrailSystem-CyYvtUTV.js +0 -1
  52. package/dist-embed-sighting-editor/LaunchEditor-qoB8RSgQ.js +0 -13
  53. package/dist-embed-sighting-editor/LaunchEditorMessages_en-BzymQ_VU.js +0 -1
  54. package/dist-embed-sighting-editor/LaunchEditorMessages_es-DzI2xAcW.js +0 -1
  55. package/dist-embed-sighting-editor/LaunchEditorMessages_fr-BLOwKfkH.js +0 -1
  56. package/dist-embed-sighting-editor/LaunchEditorMessages_it-CuovusOf.js +0 -1
  57. package/dist-embed-sighting-editor/SightingEditorMessages_es-BFzjoSef.js +0 -12
  58. package/dist-embed-sighting-editor/SightingEditorMessages_it-BtQIjKie.js +0 -12
  59. package/dist-embed-sighting-editor/sightingSchema-CNCtrneh.js +0 -1
  60. package/dist-embed-sighting-editor/surroundingsSources-B1931yh4.js +0 -1
  61. package/dist-embed-sighting-editor/trafficRuntime-lbQ2eKSF.js +0 -2
@@ -0,0 +1 @@
1
+ const e={type:"string",doc:"The schema this file was written against, for an editor to check it with: https://ufoathome.org/schema/1/sighting.schema.json."},t={type:"number",doc:"The version of the format this file is written in (see FormatVersion): 1, the only one so far."},o={type:"string",doc:'Which account this is, unique across every recording anywhere: the day it happened, then who saw it ("1964-04-24-ZamoraLonnie"), or where for an anonymous observer.'},r={type:"object",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},season:{type:"enum",doc:'The season, when the date is only known to be "in the spring of 2022" (EDTF `2022-21`): then `month` and `day` are undefined.',values:["spring","summer","autumn","winter"]},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},a={type:"object",doc:"See SightingEvent.endTime.",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},season:{type:"enum",doc:'The season, when the date is only known to be "in the spring of 2022" (EDTF `2022-21`): then `month` and `day` are undefined.',values:["spring","summer","autumn","winter"]},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},n={type:"number",doc:"See SightingEvent.durationSeconds."},s={type:"string",doc:"See SightingEvent.durationText."},i={type:"number",doc:"See SightingEvent.utcOffsetHours — the legal time zone `time`/`endTime` are expressed in."},d={type:"string",doc:"See SightingEvent.timeZone — the IANA rule `utcOffsetHours` was derived from, when it was."},h={type:"array",fields:{lat:{type:"number"},lng:{type:"number"},name:{type:"string",doc:'The place as it is named, fully qualified — "Valensole, Alpes-de-Haute-Provence, …, France".'}}},c={type:"object",doc:"See Sighting.observer.",fields:{id:{type:"string",doc:'Who this is, as a reference: last name then first names, run together ("ZamoraLonnie"), whether or not a page exists for them yet.'},title:{type:"string",doc:'What the observer is called when no name is given or wanted — "Witness 2" in one language, "Témoin 2" in another — or their name as they gave it.',loose:!0},lastName:{type:"string"},firstNames:{type:"array"}}},l={type:"object",doc:"Who saw it and how their account travelled — see Account, and Sighting.account.",fields:{observerAgeYears:{type:"number",doc:"The main observer's age in years AT THE TIME, not today — a 1974 account is read by whoever finds it decades later, and the number that means anything is the one from then."},observerBirth:{type:"object",doc:"The main observer's birth date, as precisely as it is known (a full date, a year, or one marked approximate): the other way of stating `observerAgeYears`, which the editor keeps in step with it against the observation date (see ObserverAge).",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},season:{type:"enum",doc:'The season, when the date is only known to be "in the spring of 2022" (EDTF `2022-21`): then `month` and `day` are undefined.',values:["spring","summer","autumn","winter"]},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},observerPerception:{type:"object",doc:"How the main observer's own perception differs from a typical one — colour vision, alcohol, fatigue — as the values of the ObserverProviders' settings, by provider id."},observerOccupation:{type:"string",doc:'What the main observer did, as they are described: "boulanger", "pilote de ligne", "gendarme".',loose:!0},source:{type:"enum",doc:"How the account reached the person who wrote this recording.",values:["on-site","interview","telephone","questionnaire","letter","social-media","press"]},followedUp:{type:"boolean",doc:"Whether the observer was gone back to after the first account."},conditions:{type:"object",doc:"The conditions of this account and of the sighting as the observer lived it: what the error laws of the values they give read (see docs/witness-error-model.md and WitnessError).",fields:{delayDays:{type:"number",doc:"Days between the sighting and this account."},version:{type:"number",doc:"Which version of the observer's account this is: 1 for the first given."},word:{type:"enum",values:["object","light","craft","ufo","other"]},exposure:{type:"enum",values:["press","none","broadcast","other-witnesses","investigators"]},independent:{type:"boolean",doc:"Whether the observers gave their accounts without talking to each other first."},darkAdapted:{type:"boolean",doc:"Whether the observer's eyes were used to the dark."},correctedVision:{type:"boolean",doc:"Whether the observer wore the glasses or lenses they need."},glare:{type:"boolean",doc:"Whether a bright light dazzled the observer just before."},moonShownDeg:{type:"number",doc:"How wide the observer showed the full Moon, in degrees, the way they showed the object: their own angular-size factor is this over the Moon's real 0.52°, applied to every angular size they give (see WitnessError.MOON_DEG)."}}}}},p={type:"number",doc:"How much the observer's body sways the view at rest: 1 a person, 0 a tripod — see Sighting.sway."},u={type:"object",doc:"The vehicle the observer was in, when the decor does not draw it — see Sighting.vehicle.",fields:{kind:{type:"enum",values:["car","van","truck","motorcycle","generic"]},windowsOpen:{type:"boolean",doc:"Whether the windows were open: the road and the wind come in, the cabin no longer muffles the engine."},noise:{type:"number",doc:'How loud it was, against an ordinary one of its kind: 1 by default, 1.5 for "very noisy".'},headlights:{type:"enum",doc:'How its lights are set (see HeadlightMode), or "auto": the dipped beam from the dusk on and nothing by day, which is what a driver does.',values:["off","position","dipped","main","fog","auto"]},headlightKeyframes:{type:"array",doc:"Sorted by t.",fields:{t:{type:"number"},mode:{type:"enum",values:["off","position","dipped","main","fog","auto"]}}}}},y={type:"string",doc:"See SightingEvent.description — a plain string, or one per language.",loose:!0},m={type:"string",doc:"See SightingEvent.image — the picture a shared link to this recording shows."},g={type:"string",doc:"See SightingEvent.summary — what a shared link to this recording says of it.",loose:!0},b={type:"number",doc:"See SightingEvent.summaryTime — the second the picture of this recording's card is taken at."},f={type:"array",doc:"See SightingEvent.tags."},w={type:"object",fields:{keyframes:{type:"array",fields:{t:{type:"number"},shapes:{type:"array",fields:{sourceId:{type:"string"},shape:{type:"object",fields:{kind:{type:"enum",values:["oval","polygon"]},bounds:{type:"object",fields:{x:{type:"number"},y:{type:"number"},width:{type:"number"},height:{type:"number"}}},color:{type:"string",doc:"CSS color"},angle:{type:"number",doc:"Rotation in radians about the shape's centre, positive = clockwise on screen; 0 = no rotation"},transparency:{type:"number",doc:"0 = opaque, 1 = fully transparent"},haloScale:{type:"number",doc:"0 = no halo/glow, >1 = larger glow radius"},blur:{type:"number",doc:"How indistinct the observer said its edges were — 0 for a hard outline, 1 for a shape with no edge at all."},brightness:{type:"number",doc:"How dazzling the observer said it was — 0 for a light you can look at, 1 for one you cannot."},selected:{type:"boolean"},title:{type:"string",doc:"What the observer calls it, if anything.",loose:!0},angular:{type:"object",doc:"How big this looked to the observer, in degrees of arc (see AngularExtent) — the recording's OWN statement of size, and the one it is allowed to make.",fields:{widthDeg:{type:"number",doc:"How wide it looked, in degrees of arc."},heightDeg:{type:"number",doc:"How tall it looked, in degrees of arc."}}},aim:{type:"object",doc:"Where the phenomenon itself was in the observer's sky (the direction of the shape's centre, not the way the observer was facing) — degrees of azimuth from true north and of altitude above the horizon, the same conventions as ObserverPose.",fields:{azimuthDeg:{type:"number",doc:"Degrees clockwise from true north, the same convention as ObserverPose.headingDeg."},altitudeDeg:{type:"number",doc:"Degrees above (positive) or below the horizon."}}},points:{type:"array",doc:"Points relative to bounds origin (0,0 = bounds.x, bounds.y)",fields:{x:{type:"number"},y:{type:"number"}}}}}}}}},order:{type:"array",doc:"Back-to-front paint/hit-test order — see Timeline's own `order` field."},groups:{type:"array",doc:"Each inner array is one group's member sourceIds (always >= 2 members) — see Timeline's own `groups` field."},span:{type:"number",doc:"The length of the observation the keyframes were placed against, in ms, when it is longer than the last keyframe — a shape placed at 0 s then moved at 5 s of a 10 s observation has keyframes at 0 and 5000 only, and without this the recording would end (and be stretched over the whole observation) at 5 s."}}},v={type:"object",doc:"The observer's position/elevation/orientation over time — absent for older recordings, which fall back to the legacy static place[0] (see Sighting.ts's resolveObserverPoseAt).",fields:{keyframes:{type:"array",fields:{t:{type:"number"},pose:{type:"object",fields:{lat:{type:"number",doc:"Decimal degrees."},lng:{type:"number"},elevationM:{type:"number",doc:"Metres above the local ground under the observer, not above sea level: 0 for someone standing on it, the height of a window or a cliff top otherwise."},headingDeg:{type:"number",doc:"Degrees clockwise from true north."},pitchDeg:{type:"number",doc:"Degrees above (positive) or below (negative) the local horizontal — how far up/down the observer was looking, e.g."},rollDeg:{type:"number",doc:"How far the instrument itself was tilted about its own line of sight, degrees, positive clockwise as the observer saw it — a camera held askew, a head leaned over."},fovDeg:{type:"number",doc:"Vertical field of view in degrees, as a Three.js PerspectiveCamera would take it."},fNumber:{type:"number",doc:"How far the lens was stopped down — undefined for anything the recording did not state, which then falls back to the instrument's own setting (see Instrument.fNumber)."},focusDistanceM:{type:"number",doc:"How far away the lens was focused, metres — undefined meaning at infinity, which is where a camera pointed at the sky sits and the only setting under which everything celestial comes out sharp."},filter:{type:"string",doc:"What was held in front of the instrument — a FILTERS id, a solar filter for an eclipse."}}}}}}},k={type:"object",doc:"Weather over time — absent for older recordings, which fall back to the legacy static `weather` field below (see Sighting.ts's resolveWeatherAt).",fields:{keyframes:{type:"array",fields:{t:{type:"number"},weather:{type:"object",fields:{cloudLayers:{type:"array",doc:"Explicit layers override the historical cloud fields; [] means no clouds.",fields:{id:{type:"string",doc:"Stable across keyframes: reordering layers must not regenerate their cloud pattern."},instances:{type:"array",doc:"Optional explicit masses, independent of the generated coverage percentage.",fields:{id:{type:"string"},eastM:{type:"number",doc:"Position relative to the recording's initial observer, in metres east/north."},northM:{type:"number"},baseM:{type:"number"},thicknessM:{type:"number"},widthM:{type:"number"},depthM:{type:"number"},rotationDeg:{type:"number"},density:{type:"number"},darkness:{type:"number",doc:"Visual shading, 0 (white) to 1 (very dark); absent inherits the parent layer."},seed:{type:"number"}}},type:{type:"enum",values:["cumulus","stratus","stratocumulus","cirrus","unknown"]},baseM:{type:"number",doc:"Base in metres above the recording's reference ground, not above the moving observer."},thicknessM:{type:"number",doc:"Vertical extent in metres."},coverage:{type:"number",doc:"Horizontal coverage, 0–1."},sizeM:{type:"number",doc:"Characteristic horizontal cloud size in metres."},density:{type:"number",doc:"Relative optical density, 0–2; zero is transparent."},darkness:{type:"number",doc:"Visual shading, 0 (white) to 1 (very dark)."},iceCrystalAlignment:{type:"number",doc:"Degree of preferential crystal orientation, meaningful only for cirrus."},seed:{type:"number",doc:"Reproducible variation; defaults to a hash of the stable layer id."},windDirectionDeg:{type:"number",doc:"Optional wind overrides; bearing TOWARD which the cloud travels, clockwise from north."},windSpeed:{type:"number"}}},cloudCover:{type:"number",doc:"0 = clear sky, 1 = fully overcast."},cloudDarkness:{type:"number",doc:"0 = light/clear clouds, 1 = dark/stormy character."},cloudBaseM:{type:"number",doc:"Height of the cloud layer's BASE above the ground, in meters — 600 to 2000 m covers most real low cloud."},highCloudCover:{type:"number",doc:"How much of the sky the HIGH deck covered, 0 to 1 — cirrus and cirrostratus, above about six kilometres, where anything present is ice rather than water."},lowerCloudCover:{type:"number",doc:"How much of the sky the LOW and MIDDLE decks covered, 0 to 1 — the cloud that stands between a observer and anything higher up."},iceCrystalAlignment:{type:"number",doc:"How steadily the ice crystals were falling, 0 to 1 — the one thing about the sky that decides which halo forms stood, and the one thing no record of any sighting holds."},rareIceCrystals:{type:"number",doc:"The share of a cirrus deck's crystals that are the rare kinds, 0 to 1: columns capped with pyramids, which draw the odd-radius rings (9, 20, 35 degrees), and plates free to turn about a level axis, which draw Lowitz's arcs sloping from the sundogs."},relativeHumidity:{type:"number",doc:"Relative humidity near the ground, 0 to 1."},precipitationType:{type:"enum",values:["none","rain","snow","hail"]},precipitationIntensity:{type:"number",doc:'0-1; meaningless while precipitationType is "none".'},precipitationAmount:{type:"number",doc:"How much of that precipitation is actually falling yet, 0 to 1 — absent meaning all of it."},windDirectionDeg:{type:"number",doc:"Degrees clockwise from true north — same convention as ObserverPose.headingDeg, and the direction the wind blows *toward*, not the one it comes from: SceneRenderer drifts every precipitation particle and cloud field along exactly this bearing (`sin/-cos`, north = -Z), so 0 means snow drifting northward."},windSpeed:{type:"number",doc:"Real-world wind speed in m/s (0 = calm, ~30 = violent storm/hurricane-force) — drives both cloud advection, visual precipitation drift and wind audio volume directly."},storm:{type:"boolean",doc:"Whether a thunderstorm was reported — drives both a scene-fog lightning flash and a delayed thunderclap (see SceneRenderer's lightning-flash scheduling, named for the visual mechanism it implements internally, not for this field)."}}}}}}},S={type:"object",doc:`Legacy static weather condition, kept only as weatherTrack's own fallback for recordings made before it existed — absent means "unknown/not recorded", not "clear skies" (renderers default to DEFAULT_WEATHER, see Weather.ts).`,fields:{cloudLayers:{type:"array",doc:"Explicit layers override the historical cloud fields; [] means no clouds.",fields:{id:{type:"string",doc:"Stable across keyframes: reordering layers must not regenerate their cloud pattern."},instances:{type:"array",doc:"Optional explicit masses, independent of the generated coverage percentage.",fields:{id:{type:"string"},eastM:{type:"number",doc:"Position relative to the recording's initial observer, in metres east/north."},northM:{type:"number"},baseM:{type:"number"},thicknessM:{type:"number"},widthM:{type:"number"},depthM:{type:"number"},rotationDeg:{type:"number"},density:{type:"number"},darkness:{type:"number",doc:"Visual shading, 0 (white) to 1 (very dark); absent inherits the parent layer."},seed:{type:"number"}}},type:{type:"enum",values:["cumulus","stratus","stratocumulus","cirrus","unknown"]},baseM:{type:"number",doc:"Base in metres above the recording's reference ground, not above the moving observer."},thicknessM:{type:"number",doc:"Vertical extent in metres."},coverage:{type:"number",doc:"Horizontal coverage, 0–1."},sizeM:{type:"number",doc:"Characteristic horizontal cloud size in metres."},density:{type:"number",doc:"Relative optical density, 0–2; zero is transparent."},darkness:{type:"number",doc:"Visual shading, 0 (white) to 1 (very dark)."},iceCrystalAlignment:{type:"number",doc:"Degree of preferential crystal orientation, meaningful only for cirrus."},seed:{type:"number",doc:"Reproducible variation; defaults to a hash of the stable layer id."},windDirectionDeg:{type:"number",doc:"Optional wind overrides; bearing TOWARD which the cloud travels, clockwise from north."},windSpeed:{type:"number"}}},cloudCover:{type:"number",doc:"0 = clear sky, 1 = fully overcast."},cloudDarkness:{type:"number",doc:"0 = light/clear clouds, 1 = dark/stormy character."},cloudBaseM:{type:"number",doc:"Height of the cloud layer's BASE above the ground, in meters — 600 to 2000 m covers most real low cloud."},highCloudCover:{type:"number",doc:"How much of the sky the HIGH deck covered, 0 to 1 — cirrus and cirrostratus, above about six kilometres, where anything present is ice rather than water."},lowerCloudCover:{type:"number",doc:"How much of the sky the LOW and MIDDLE decks covered, 0 to 1 — the cloud that stands between a observer and anything higher up."},iceCrystalAlignment:{type:"number",doc:"How steadily the ice crystals were falling, 0 to 1 — the one thing about the sky that decides which halo forms stood, and the one thing no record of any sighting holds."},rareIceCrystals:{type:"number",doc:"The share of a cirrus deck's crystals that are the rare kinds, 0 to 1: columns capped with pyramids, which draw the odd-radius rings (9, 20, 35 degrees), and plates free to turn about a level axis, which draw Lowitz's arcs sloping from the sundogs."},relativeHumidity:{type:"number",doc:"Relative humidity near the ground, 0 to 1."},precipitationType:{type:"enum",values:["none","rain","snow","hail"]},precipitationIntensity:{type:"number",doc:'0-1; meaningless while precipitationType is "none".'},precipitationAmount:{type:"number",doc:"How much of that precipitation is actually falling yet, 0 to 1 — absent meaning all of it."},windDirectionDeg:{type:"number",doc:"Degrees clockwise from true north — same convention as ObserverPose.headingDeg, and the direction the wind blows *toward*, not the one it comes from: SceneRenderer drifts every precipitation particle and cloud field along exactly this bearing (`sin/-cos`, north = -Z), so 0 means snow drifting northward."},windSpeed:{type:"number",doc:"Real-world wind speed in m/s (0 = calm, ~30 = violent storm/hurricane-force) — drives both cloud advection, visual precipitation drift and wind audio volume directly."},storm:{type:"boolean",doc:"Whether a thunderstorm was reported — drives both a scene-fog lightning flash and a delayed thunderclap (see SceneRenderer's lightning-flash scheduling, named for the visual mechanism it implements internally, not for this field)."}}},M={type:"object",doc:"What the sighting sounded like over time — see SoundTrack.",fields:{keyframes:{type:"array",fields:{t:{type:"number"},sound:{type:"object",fields:{kind:{type:"enum",values:["none","hum","whistle","rumble","crackle"]},volume:{type:"number",doc:"How loud it was, 0 (inaudible) to 1 (as loud as the observer could describe) — deliberately relative rather than a dB SPL figure: nobody reports a sound level in decibels, and inventing one would claim a measurement where there is only an impression."},pitchHz:{type:"number",doc:`The sound's own frequency in Hz — the oscillator's pitch for the pitched kinds ("hum", "whistle"), and the noise filter's centre for the unpitched ones ("rumble", "crackle"), where it reads as how deep or how sharp the noise is.`},src:{type:"string",doc:"An actual audio recording of the sound, when one exists (a observer who filmed the event, a later recording of the same phenomenon) — replaces synthesis entirely while it is set, with `volume` still scaling it."}}}}}}},T=JSON.parse(`{"type":"array","doc":"See Sighting.decor.","fields":{"id":{"type":"string"},"kind":{"type":"enum","values":["terrace","wall","building","tree","crop","mound","streetlight","vehicle","observer","aircraft","trafficlight","crossing","roadsign","shrub","bridge","wire","entity"]},"title":{"type":"string","doc":"Optional display name, same role as Shape.title — falls back to a generic \\"{kind} {n}\\" label (see SightingEditorElement.decorLabel) when absent.","loose":true},"eastM":{"type":"number","doc":"Meters from the observer's own position — eastM positive = east, northM positive = north."},"northM":{"type":"number"},"headingDeg":{"type":"number","doc":"Degrees clockwise from true north, same convention as ObserverPose.headingDeg — which way a vehicle is facing, or a observer looking."},"lit":{"type":"boolean","doc":"Streetlight lamp / vehicle headlights switched on."},"litKeyframes":{"type":"array","doc":"Sorted by t.","fields":{"t":{"type":"number"},"lit":{"type":"boolean"}}},"headlights":{"type":"enum","doc":"For a vehicle: how its lights are set (see HeadlightMode).","values":["off","position","dipped","main","fog"]},"headlightKeyframes":{"type":"array","doc":"Sorted by t.","fields":{"t":{"type":"number"},"mode":{"type":"enum","values":["off","position","dipped","main","fog"]}}},"lights":{"type":"array","doc":"The object's individual lamps, each with its own place on the body and its own rhythm — see DecorLight, and LIGHT_RIGS for the ready-made sets.","fields":{"id":{"type":"string"},"offsetM":{"type":"object","doc":"Where it sits on the object, in meters relative to the object's own centre and heading: +x is its right, +y up, +z its front.","fields":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"color":{"type":"string","doc":"CSS hex colour, as emitted — the regulated red/green/white of navigation lights, the amber of a hazard flasher."},"intensity":{"type":"number","doc":"Peak luminous intensity as a ratio to an ordinary navigation light's (1, about a hundred candela — see DecorSystem's LAMP_CANDELA); the airliner rig (LIGHT_RIGS) gives its beacon 1.5 and its wingtip strobes 20."},"pattern":{"type":"object","fields":{"kind":{"type":"enum","values":["steady","flash"]},"perMinute":{"type":"number"},"dutyCycle":{"type":"number"},"phase":{"type":"number"}}},"radiusM":{"type":"number","doc":"The radius of what shines, metres, where it is not a quarter of a metre: the lens of a traffic light is ten centimetres."},"beamDeg":{"type":"number","doc":"Where the lamp is seen from, when it is not from everywhere: the angle off the object's front (−z, where its heading points) at which it gives half its light, degrees."}}},"track":{"type":"array","doc":"Where the object is over time, when it moves — an aircraft crossing the sky, a car driving past.","fields":{"t":{"type":"number"},"eastM":{"type":"number"},"northM":{"type":"number"},"altitudeM":{"type":"number","doc":"Meters above the observer's own elevation."},"headingDeg":{"type":"number"},"gazeAzimuthDeg":{"type":"number","doc":"Where a figure looks, as a compass azimuth: its head turns to face it, as far as a neck turns (85 degrees either side of where its body points), if its model has a node named \\"head\\" (scripts/people/split_head.py)."}}},"sightingUrl":{"type":"string","doc":"For kind \\"observer\\" only: the URL of that observer's own sighting.json recording, if known — lets the 3D scene's own context menu offer \\"view this observer's account\\" (loading it the same way SightingEditorElement.importFromUrl already does)."},"windows":{"type":"object","doc":"Per-side window opacity, 0 (fully open — the pane is invisible, and DecorSystem.addRoom's own interior wall panel is skipped there too, same as no window at all) to 100 (fully closed — a solid-looking pane)."},"color":{"type":"string","doc":"What colour this object actually is, as a CSS colour — the same way a shape states its own (see Shape.color), because a recording is written and read by people and \\"#8f86a8\\" is a colour a person can type."},"observerSide":{"type":"enum","values":["front","behind","left","right","front-left","front-right","behind-left","behind-right"]},"observerFacing":{"type":"enum","doc":"Which window the observer looks through, when it is not the one at their own seat: a driver sits at \\"front-left\\" and looks through the windshield (\\"front\\").","values":["front","behind","left","right","front-left","front-right","behind-left","behind-right"]},"observerKeyframes":{"type":"array","doc":"Where the observer sits over time, when they do not stay where they are: a driver is in the car until the moment they get out of it.","fields":{"t":{"type":"number"},"side":{"type":"enum","doc":"The seat they are in from this instant; absent means they are outside the object.","values":["front","behind","left","right","front-left","front-right","behind-left","behind-right"]},"facing":{"type":"enum","doc":"The window they look through from this instant, when it is not the one at their seat.","values":["front","behind","left","right","front-left","front-right","behind-left","behind-right"]}}},"engine":{"type":"object","doc":"A vehicle heard running — see DecorEngine: its engine turning with its own \`track\`, heard less the further it is from the observer.","fields":{"kind":{"type":"enum","doc":"Which sound: a decor object's own \`kind\` says only \\"vehicle\\".","values":["car","van","truck","motorcycle","generic"]},"noise":{"type":"number","doc":"As ObserverVehicle.noise."}}},"aircraftSound":{"type":"enum","doc":"An aircraft heard while it flies — what sort of noise it makes (see AircraftHearing.noiseOfKind), or \\"silent\\" for one that makes none (a glider's cousin, a stand-in for something nobody heard).","values":["unknown","airliner-narrow","airliner-wide","regional-turboprop","regional-jet","business-jet","light-piston","turboprop-light","helicopter-light","helicopter-medium","helicopter-heavy","military-jet","glider","balloon","ultralight","unmanned","silent"]},"surveyedOn":{"type":"string","doc":"The day (YYYY-MM-DD) of the survey this object was taken from, where it comes from one (see \`origin\`): the latest of the days of everything it was drawn from (the laser flight, the aerial photograph, the map's edition)."},"facade":{"type":"object","doc":"Kind \\"building\\" only: the colour of its walls, and the windows and door drawn on them.","fields":{"color":{"type":"string","doc":"The colour of the wall, \\"#rrggbb\\": plaster, stone, brick."},"plain":{"type":"boolean","doc":"A plain wall, with no windows and no door."},"openings":{"type":"enum","doc":"How the walls are opened.","values":["tall","lancet"]},"storeyM":{"type":"number","doc":"The height of a storey, metres, where it is known (the floors a survey states, or those of an old building): absent is three."},"closedAtNight":{"type":"boolean","doc":"The windows of a place nobody is in at night (a shop, an office, a pool, a gymnasium): it has windows, but a fifth as many of them lit as a home has at the same hour (see DecorSystem.CLOSED_AT_NIGHT_SHARE)."}}},"roof":{"type":"object","doc":"Kind \\"building\\" only: the shape and colour of its roof.","fields":{"shape":{"type":"enum","doc":"\\"flat\\" is the box; \\"gable\\" rises to a ridge along the building's longer side; \\"hip\\" slopes down on all four; \\"dome\\" is a cupola over the ellipse the walls hold.","values":["flat","gable","hip","dome"]},"ridge":{"type":"enum","doc":"Which side of the building a gable's ridge runs along: \\"width\\" (the longer side, the default) or \\"depth\\".","values":["width","depth"]},"riseM":{"type":"number","doc":"How far the ridge stands above the eaves, metres."},"color":{"type":"string","doc":"The colour of the roof seen from above, \\"#rrggbb\\", where it was measured (see RoofShapes): the walls keep their own."}}},"bridge":{"type":"object","doc":"Kind \\"bridge\\" only: how it is built, where that is known — see BridgeStructure.","fields":{"spanM":{"type":"number","doc":"Metres of open span under the deck, centred on the object."},"deckThicknessM":{"type":"number","doc":"Thickness of the deck slab, metres."},"railing":{"type":"object","doc":"The railing along both edges: its height above the road, the spacing of its posts, and how many horizontal rails it has — two rails make two rows of rectangular openings.","fields":{"heightM":{"type":"number"},"postSpacingM":{"type":"number"},"rails":{"type":"number"}}}}},"wire":{"type":"object","doc":"Kind \\"wire\\" only: how many strands, where they hang, and which ends stand on a pole — see WireStructure.","fields":{"strands":{"type":"array","doc":"The strands: each one's height above the ground and, for a crossarm, how far it hangs to one side of the line (positive to the right of its heading).","fields":{"heightM":{"type":"number"},"acrossM":{"type":"number"}}},"poles":{"type":"enum","doc":"Which ends stand on a pole: both (default), the one the object's heading points away from (\`start\`) or towards (\`end\`), or none, for a line anchored on buildings at both ends.","values":["none","both","start","end"]},"diameterM":{"type":"number","doc":"How thick a strand is, metres."},"poleHeightM":{"type":"number","doc":"How tall a pole is, metres."}}},"lamp":{"type":"object","doc":"Kind \\"streetlight\\" only: the lamp it carries, where it is known (a town's inventory says it).","fields":{"colorTemperatureK":{"type":"number","doc":"The colour of its light, kelvins: 2 000 a sodium lamp's orange, 2 700 to 3 000 a warm white LED's, 4 000 a cold one."},"lumens":{"type":"number","doc":"Its luminous flux, lumens."},"mount":{"type":"enum","doc":"\\"wall\\": a bracket on a facade, with no column of its own.","values":["wall","column"]},"supposed":{"type":"boolean","doc":"True when nobody surveyed where it stands: it was put there because a map says the street is lit, at the spacing of a town's lamps (see OsmStreetLamps)."}}},"sign":{"type":"object","doc":"Kind \\"roadsign\\" only: what its face says.","fields":{"face":{"type":"enum","doc":"\\"stop\\" is the red octagon, \\"give-way\\" the triangle point down, \\"speed-limit\\" the disc ringed in red; \\"other\\" a sign whose face is not drawn.","values":["other","stop","give-way","speed-limit"]},"code":{"type":"string","doc":"The sign as the map codes it, where it does (\\"FR:B14[50]\\")."},"value":{"type":"number","doc":"The limit, km/h, for a \\"speed-limit\\"."}}},"floors":{"type":"number","doc":"Kind \\"building\\" only: number of upper stories above the ground floor, set when the building is created (default 2, see SightingEditorElement.addDecor) and editable afterward."},"occupiedFloor":{"type":"number","doc":"Kind \\"building\\" only, meaningful once observerSide is set: which floor the observer is on, 0 (ground floor) up to floors."},"occludesSourceIds":{"type":"array","doc":"Shape sourceIds (see Timeline.sourceIds/Shape) this decor object sits in front of, from the observer's own reported viewpoint — occluding that shape wherever their screen positions line up (see SceneRenderer.isScreenPointOccluded)."},"sizeM":{"type":"object","doc":"How big it really is, in meters, along its own axes — see DecorSize.","fields":{"widthM":{"type":"number"},"lengthM":{"type":"number"},"heightM":{"type":"number"}}},"modelParameters":{"type":"object","doc":"The settings of the model this object is drawn as, by the key the model publishes them under (see ModelParameters.parameters): a being's costume colour, say."},"model":{"type":"object","doc":"The real 3D model that stands in for the primitive, if one does — see DecorModelRef.","fields":{"id":{"type":"string","doc":"Catalogue entry, resolved by the live DecorModelProvider."},"url":{"type":"string","doc":"Direct glTF/GLB address, taking precedence over \`id\`."},"headingOffsetDeg":{"type":"number","doc":"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},"credit":{"type":"object","doc":"Who made it and under what licence.","fields":{"title":{"type":"string"},"author":{"type":"string"},"license":{"type":"string","doc":"Licence name as its author states it, e.g."},"sourceUrl":{"type":"string","doc":"Where it came from, so the claim can be checked."}}}}},"origin":{"type":"string","doc":"The survey this object was taken from, by the id of its provider (\\"ign-lidar-hd\\"), when the author did not place it by hand: the trees the editor plants from a canopy survey (see CanopyProvider)."}}}`),D={type:"array",doc:"The roads the account's own plan draws — see Sighting.roads and Road.ts.",fields:{id:{type:"string"},title:{type:"string",doc:"What the account calls it, where it calls it anything.",loose:!0},surface:{type:"enum",values:["paved","gravel","dirt"]},widthM:{type:"number",doc:"Carriageway width, metres."},path:{type:"array",doc:"The centre line, in metres east and north of the observer's place at t=0.",fields:{eastM:{type:"number"},northM:{type:"number"}}},source:{type:"string",doc:"Where this line comes from — the plan, the survey, the report it was read off.",loose:!0}}},j={type:"array",doc:"What an investigator drew over the place, imported from their file — see Sighting.traces and Trace.ts.",fields:{id:{type:"string"},title:{type:"string",doc:'What its author called it ("Ligne de visée depuis la ferme").',loose:!0},kind:{type:"enum",values:["polygon","point","line"]},points:{type:"array",doc:"One point for a marker, the vertices of a line, the vertices of an outline (not repeated at the end: it is closed by being a polygon).",fields:{lat:{type:"number"},lng:{type:"number"},altM:{type:"number",doc:"Metres, read as `InvestigatorTrace.altitude` says."}}},altitude:{type:"enum",doc:"How `altM` is read.",values:["ground","relative","absolute"]},color:{type:"string",doc:"CSS colour the author gave it, `#rrggbb`."},source:{type:"string",doc:"Whose it is and where it comes from — the person, the report, the file.",loose:!0}}},x={type:"object",doc:"Which meteorological record `weatherTrack` was looked up from, when it wasn't the observer who stated the conditions — see Sighting.weatherSource.",fields:{id:{type:"string",doc:"Stable dataset id, e.g."},name:{type:"string",doc:"Human-readable dataset name, e.g."},url:{type:"string",doc:"The exact request that produced these values, so the claim stays checkable years later."}}},W={type:"string",doc:"Which INSTRUMENTS entry this was observed through — see Sighting.instrumentId."},H={type:"number",doc:"How long the shutter stayed open, seconds — one value for the whole observation, see Sighting.exposureSeconds."},A={type:"number",doc:"The speed of the film or sensor this picture was taken on — see Sighting.iso."},R={type:"number",doc:`How bright the night sky is at the place, towns and all: the zenith of a moonless night in magnitudes per square arcsecond, a Sky Quality Meter's reading or the World Atlas's "SQM" figure (Falchi et al.`},z={type:"boolean",doc:"Whether the map of where the observer stood starts open — see Sighting.observerMap."},E={type:"array",doc:"The named moments of the account — see Milestone.",fields:{t:{type:"number",doc:"When, in milliseconds from the start of the recording — the same scale every track uses."},label:{type:"string",doc:'Short label, shown on the seek bar: the letter or number the account itself uses ("A", "B"), or a couple of words where it uses none.',loose:!0},note:{type:"string",doc:"What happened at that moment, in the account's own words where possible.",loose:!0}}},I={type:"array",doc:"Pictures of the place, laid over the reconstruction at the direction each was registered in — see SceneReference.",fields:{id:{type:"string",doc:"Stable id, what the editor's own list and a future trace on the picture name."},kind:{type:"enum",values:["photo","panorama"]},src:{type:"string",doc:"Where the picture is: an address, or the picture itself as a `data:` URL."},title:{type:"string",doc:'What to call it — "Vue depuis le muret, fin été 1968".',loose:!0},credit:{type:"string",doc:"Who made it and on what terms — shown wherever the picture is, because a credit that is not displayed is not a licence (see DataSource for the same rule on the data)."},creditUrl:{type:"string",doc:"Where that credit points: the source page, the licence."},t:{type:"number",doc:"The instant of the timeline it was taken at, milliseconds, for a picture made DURING the observation — by the observer, or by whoever was there."},from:{type:"object",doc:"Where it was taken from — the one point its registration holds for.",fields:{lat:{type:"number"},lng:{type:"number"}}},drawing:{type:"boolean",doc:"Whether somebody drew on it — a trajectory, an outline, an arrow — so that what the picture shows of the phenomenon is a statement made afterwards, not a thing the camera recorded."},opacity:{type:"number",doc:"How much of it shows, 0 to 1 — the starting point of the reader's own slider, since the comparison is made by sliding: all picture, all render, and the registration checked at every step between."},registration:{type:"object",fields:{headingDeg:{type:"number",doc:"Degrees clockwise from true north, the direction the picture's centre looks at."},pitchDeg:{type:"number",doc:"Degrees above the local horizontal, positive up."},rollDeg:{type:"number",doc:"Degrees the picture is tilted about its own axis, positive clockwise as it is looked at — see ObserverPose.rollDeg."},fovDeg:{type:"number",doc:"The picture's VERTICAL field, degrees — what the lens took in top to bottom, its horizontal field following from the image's own aspect ratio."}}},landmarks:{type:"array",doc:"The details it was lined up on — see PictureLandmark.",fields:{id:{type:"string"},label:{type:"string",doc:'What the detail is — "Arbre masquant", "barrière".',loose:!0},picture:{type:"object",doc:"Where it is on the picture, 0..1 from the top-left corner.",fields:{u:{type:"number"},v:{type:"number"}}},scene:{type:"object",doc:"Where it is from the observer, in the scene.",fields:{azimuthDeg:{type:"number"},altitudeDeg:{type:"number"}}}}}}},O={type:"array",doc:"Where the account can be read as it was given, each one a source — see RecordingSource.",fields:{type:{type:"enum",values:["book","article"]},title:{type:"string"},authors:{type:"array",doc:"The author(s) name(s): for a post, the account that posted it."},url:{type:"string",doc:"Where it can be read."},publication:{type:"object",doc:"Who published it and when (EDTF).",fields:{publisher:{type:"string"},time:{type:"string"}}},index:{type:"string",doc:"Chapter, page, etc."}}},C=JSON.parse(`{"type":"object","doc":"What the observer took it to be, in metres — see InterpretationJson.","fields":{"title":{"type":"string","doc":"What the claim is, in a few words — \\"Craft on its legs\\", \\"Weather balloon at 3 km\\".","loose":true},"bodies":{"type":"array","fields":{"id":{"type":"string"},"title":{"type":"string","loose":true},"explains":{"type":"array","doc":"The phenomena of the account (their \`sourceId\`s) this body claims to be — what its projected outline is measured against at each instant, and what is drawn as a ghost beside it."},"model":{"type":"object","doc":"What it is shaped like.","fields":{"id":{"type":"string","doc":"Catalogue entry, resolved by the live DecorModelProvider."},"url":{"type":"string","doc":"Direct glTF/GLB address, taking precedence over \`id\`."},"headingOffsetDeg":{"type":"number","doc":"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},"credit":{"type":"object","doc":"Who made it and under what licence.","fields":{"title":{"type":"string"},"author":{"type":"string"},"license":{"type":"string","doc":"Licence name as its author states it, e.g."},"sourceUrl":{"type":"string","doc":"Where it came from, so the claim can be checked."}}}}},"withObserver":{"type":"boolean","doc":"Whether the observer carries it: its keyframes, stated FROM THE OBSERVER, are offsets from their eye that go with the eye, instead of points of the world fixed where the eye was at each keyframe's instant."},"outlineNode":{"type":"string","doc":"The node of the model that IS what the observer drew — \\"hull\\" for a craft on legs the observer drew as an oval — and so what its outline is measured by (see BodyConfrontation)."},"track":{"type":"array","fields":{"t":{"type":"number"},"eastM":{"type":"number"},"northM":{"type":"number"},"azimuthDeg":{"type":"number"},"altitudeDeg":{"type":"number"},"distanceM":{"type":"number"},"onGround":{"type":"boolean","doc":"Standing on the ground."},"present":{"type":"boolean","doc":"Gone from here on — \`false\` — or back — \`true\`."},"altitudeAboveGroundM":{"type":"number","doc":"Metres between the ground under it and its lowest point."},"altitudeM":{"type":"number","doc":"Metres between its lowest point and the ground at the recording's own origin (where the observer stands at the start) — a height in the air that does NOT follow what is under the body."},"sizeM":{"type":"object","fields":{"widthM":{"type":"number"},"lengthM":{"type":"number"},"heightM":{"type":"number"}}},"attitude":{"type":"object","fields":{"headingDeg":{"type":"number"},"pitchDeg":{"type":"number"},"rollDeg":{"type":"number"}}},"appearance":{"type":"object","fields":{"color":{"type":"string","doc":"Its surface colour, CSS."},"albedo":{"type":"number","doc":"How much of the light falling on it it sends back, 0-1: aluminium ~0.7, a matt dark hull ~0.1."},"luminanceCdM2":{"type":"number","doc":"What it gives out of itself, candela per square metre — read through the scene's own photometry exactly as a flame's is (see BodyFlame), so that the same figure is a lamp at night and nothing at all against a noon sky."},"transparency":{"type":"number","doc":"How much of what is behind it shows through, 0 (opaque, the default) to 1 (gone): the same statement a drawn shape's transparency makes, for a body of the scene."},"haloScale":{"type":"number","doc":"How far the glare it throws round itself reaches, as a multiple of what its light gives (1, the default): 0 puts the glare out, 2 doubles its width."}}},"flame":{"type":"object","doc":"A flame coming out of it, from here on — see BodyFlame.","fields":{"node":{"type":"string"},"lengthM":{"type":"number","doc":"How far it reaches from the node, metres."},"widthM":{"type":"number","doc":"How wide it is at its widest, metres."},"color":{"type":"string","doc":"Its colour where it leaves the node, CSS."},"tipColor":{"type":"string","doc":"Its colour at its far end; the same as \`color\` when absent."},"luminanceCdM2":{"type":"number"},"raisesDust":{"type":"boolean","doc":"Whether it raises dust where it meets the ground — a flame reaching loose, dry soil does, the more the nearer it gets."}}},"motions":{"type":"object","doc":"How far along each of its model's own movements it is, by the movement's name in the model (a glTF animation): 0 at its start, 1 at its end, and on past 1 for one that repeats — a turn made three times is 3."},"lights":{"type":"object","doc":"The luminance of each of its model's own lights, cd/m², by the name of the light's material in the model: what lets one body carry lights that behave each their own way — a steady white at each end, a red one flashing between."}}},"sound":{"type":"object","doc":"What it sounds like: heard from where the observer stands, late by the time sound takes to come, shifted in pitch as it comes and goes, and masked by the noise of the place (see BodyHearing).","fields":{"kind":{"type":"enum","values":["unknown","airliner-narrow","airliner-wide","regional-turboprop","regional-jet","business-jet","light-piston","turboprop-light","helicopter-light","helicopter-medium","helicopter-heavy","military-jet","glider","balloon","ultralight","unmanned"]},"quieterDb":{"type":"number","doc":"Decibels less than the class's own sound power, across its spectrum."}}}}},"smoke":{"type":"array","doc":"Fires the interpretation lights on the ground, as their smoke — see SmokeSource.","fields":{"eastM":{"type":"number"},"northM":{"type":"number"},"fromT":{"type":"number"},"untilT":{"type":"number"},"halfLifeS":{"type":"number","doc":"How fast it dies down, seconds to half its thickness — brush flares and smoulders."}}},"flares":{"type":"array","doc":"Salvoes of decoy flares an aircraft dispensed, computed into bodies when shown — see FlareSalvoJson.","fields":{"id":{"type":"string"},"title":{"type":"string","loose":true},"aircraft":{"type":"object","doc":"The aircraft at one instant of the recording, flying straight and level from there on (and before).","fields":{"t":{"type":"number"},"eastM":{"type":"number"},"northM":{"type":"number"},"altitudeM":{"type":"number","doc":"Metres above the ground at the recording's origin — see BodyKeyframe.altitudeM."},"headingDeg":{"type":"number"},"speedMS":{"type":"number"},"originElevationM":{"type":"number","doc":"Its height above sea level at \`t\` would be this plus \`altitudeM\`: what sets how thick the air braking the flares is."},"model":{"type":"object","doc":"Drawn when given (a catalogue model or a primitive), dark and unlit as a fighter in an exercise often is.","fields":{"id":{"type":"string","doc":"Catalogue entry, resolved by the live DecorModelProvider."},"url":{"type":"string","doc":"Direct glTF/GLB address, taking precedence over \`id\`."},"headingOffsetDeg":{"type":"number","doc":"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},"credit":{"type":"object","doc":"Who made it and under what licence.","fields":{"title":{"type":"string"},"author":{"type":"string"},"license":{"type":"string","doc":"Licence name as its author states it, e.g."},"sourceUrl":{"type":"string","doc":"Where it came from, so the claim can be checked."}}}}},"sizeM":{"type":"object","fields":{"widthM":{"type":"number"},"lengthM":{"type":"number"},"heightM":{"type":"number"}}},"glow":{"type":"object","doc":"A light it gives off as it flies — its afterburner — which an observer may have taken for a phenomenon of its own.","fields":{"explains":{"type":"array","doc":"The phenomena of the account it claims to be — see BodyJson.explains."},"sizeM":{"type":"number","doc":"How wide it looks, m."},"luminousIntensityCd":{"type":"number"},"color":{"type":"string"},"fromT":{"type":"number","doc":"Ms of the recording."},"untilT":{"type":"number"},"onS":{"type":"number","doc":"Seconds lit, then seconds out, repeated from \`fromT\`; always lit when absent."},"offS":{"type":"number"}}},"sound":{"type":"object","doc":"What it sounds like (see BodySound): heard from the two minutes before the recording on, as its sound takes that long to come from afar.","fields":{"kind":{"type":"enum","values":["unknown","airliner-narrow","airliner-wide","regional-turboprop","regional-jet","business-jet","light-piston","turboprop-light","helicopter-light","helicopter-medium","helicopter-heavy","military-jet","glider","balloon","ultralight","unmanned"]},"quieterDb":{"type":"number","doc":"Decibels less than the class's own sound power, across its spectrum."}}}}},"releases":{"type":"array","doc":"When each flare leaves it, and which phenomena of the account it claims to be.","fields":{"t":{"type":"number","doc":"Ms of the recording."},"explains":{"type":"array","doc":"The phenomena of the account this flare claims to be — see BodyJson.explains."}}},"flare":{"type":"object","doc":"The flare dispensed: an MJU-7 when absent (see DecoyFlare.MJU_7).","fields":{"massKg":{"type":"number","doc":"Mass at ejection, kg."},"burnedKg":{"type":"number","doc":"Mass burned away by burnout, kg."},"dragAreaM2":{"type":"number","doc":"Drag coefficient times reference area, m²: how hard the air brakes it."},"ejectionMS":{"type":"number","doc":"The dispenser's kick, m/s, straight down out of the aircraft."},"burnS":{"type":"number","doc":"How long it burns, s."},"luminousIntensityCd":{"type":"number","doc":"Its luminous intensity at full burn, candela, in the visible: what the eye gets of it."},"flameSizeM":{"type":"number","doc":"How wide its burning plume looks, m: the size of the glowing sphere drawn."},"color":{"type":"string","doc":"The colour of its light, CSS."}}},"wind":{"type":"object","doc":"The wind at the flares' height: where it blows TOWARDS, degrees, and its speed.","fields":{"towardDeg":{"type":"number"},"speedMS":{"type":"number"}}}}},"variant":{"type":"object","doc":"When this interpretation is a VARIANT of the account: what the observer would have to have erred by for the account to be a misperception of a candidate, as the candidate search found it (see CandidateSearch, docs/witness-error-model.md).","fields":{"candidate":{"type":"string","doc":"Which candidate: a body of the sky (\\"Venus\\"), a satellite, a re-entry, an aircraft, or another interpretation."},"label":{"type":"string","doc":"What the candidate is called, as it was when the variant was kept."},"phenomenon":{"type":"string","doc":"The known phenomenon of Poher's matrix it is an instance of, when it is one."},"parameters":{"type":"object","doc":"The witness parameters applied: a clock offset in minutes, a rotation of the heading in degrees, factors on elevation and size.","fields":{"clockMinutes":{"type":"number"},"headingDeg":{"type":"number"},"elevationFactor":{"type":"number"},"sizeFactor":{"type":"number"},"durationFactor":{"type":"number"}}},"moved":{"type":"array","doc":"The values it moves: what the account says, what the variant reads, the bias and its status.","fields":{"field":{"type":"enum","values":["time","heading","elevation","size","duration","sweep"]},"stated":{"type":"number"},"variant":{"type":"number"},"parameter":{"type":"string"},"amount":{"type":"number"},"z":{"type":"number"},"status":{"type":"string"}}},"readings":{"type":"array","doc":"The structural readings it uses (see CandidateSearch's StructuralReading), each with what the studies say of it.","fields":{"reading":{"type":"string"},"status":{"type":"string"}}},"cost":{"type":"number","doc":"What it costs, in units of the laws' spread (σ), and the verdict it got."},"verdict":{"type":"enum","values":["consolidated","contested","incompatible"]},"modelId":{"type":"string","doc":"The error model it was computed with (see WitnessError.MODELS)."}}}}}`),L=JSON.parse(`{"type":"array","doc":"The variants of the account that were kept: each an interpretation saying what the observer would have erred by for it to be a misperception of a candidate (see InterpretationJson.variant).","fields":{"title":{"type":"string","doc":"What the claim is, in a few words — \\"Craft on its legs\\", \\"Weather balloon at 3 km\\".","loose":true},"bodies":{"type":"array","fields":{"id":{"type":"string"},"title":{"type":"string","loose":true},"explains":{"type":"array","doc":"The phenomena of the account (their \`sourceId\`s) this body claims to be — what its projected outline is measured against at each instant, and what is drawn as a ghost beside it."},"model":{"type":"object","doc":"What it is shaped like.","fields":{"id":{"type":"string","doc":"Catalogue entry, resolved by the live DecorModelProvider."},"url":{"type":"string","doc":"Direct glTF/GLB address, taking precedence over \`id\`."},"headingOffsetDeg":{"type":"number","doc":"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},"credit":{"type":"object","doc":"Who made it and under what licence.","fields":{"title":{"type":"string"},"author":{"type":"string"},"license":{"type":"string","doc":"Licence name as its author states it, e.g."},"sourceUrl":{"type":"string","doc":"Where it came from, so the claim can be checked."}}}}},"withObserver":{"type":"boolean","doc":"Whether the observer carries it: its keyframes, stated FROM THE OBSERVER, are offsets from their eye that go with the eye, instead of points of the world fixed where the eye was at each keyframe's instant."},"outlineNode":{"type":"string","doc":"The node of the model that IS what the observer drew — \\"hull\\" for a craft on legs the observer drew as an oval — and so what its outline is measured by (see BodyConfrontation)."},"track":{"type":"array","fields":{"t":{"type":"number"},"eastM":{"type":"number"},"northM":{"type":"number"},"azimuthDeg":{"type":"number"},"altitudeDeg":{"type":"number"},"distanceM":{"type":"number"},"onGround":{"type":"boolean","doc":"Standing on the ground."},"present":{"type":"boolean","doc":"Gone from here on — \`false\` — or back — \`true\`."},"altitudeAboveGroundM":{"type":"number","doc":"Metres between the ground under it and its lowest point."},"altitudeM":{"type":"number","doc":"Metres between its lowest point and the ground at the recording's own origin (where the observer stands at the start) — a height in the air that does NOT follow what is under the body."},"sizeM":{"type":"object","fields":{"widthM":{"type":"number"},"lengthM":{"type":"number"},"heightM":{"type":"number"}}},"attitude":{"type":"object","fields":{"headingDeg":{"type":"number"},"pitchDeg":{"type":"number"},"rollDeg":{"type":"number"}}},"appearance":{"type":"object","fields":{"color":{"type":"string","doc":"Its surface colour, CSS."},"albedo":{"type":"number","doc":"How much of the light falling on it it sends back, 0-1: aluminium ~0.7, a matt dark hull ~0.1."},"luminanceCdM2":{"type":"number","doc":"What it gives out of itself, candela per square metre — read through the scene's own photometry exactly as a flame's is (see BodyFlame), so that the same figure is a lamp at night and nothing at all against a noon sky."},"transparency":{"type":"number","doc":"How much of what is behind it shows through, 0 (opaque, the default) to 1 (gone): the same statement a drawn shape's transparency makes, for a body of the scene."},"haloScale":{"type":"number","doc":"How far the glare it throws round itself reaches, as a multiple of what its light gives (1, the default): 0 puts the glare out, 2 doubles its width."}}},"flame":{"type":"object","doc":"A flame coming out of it, from here on — see BodyFlame.","fields":{"node":{"type":"string"},"lengthM":{"type":"number","doc":"How far it reaches from the node, metres."},"widthM":{"type":"number","doc":"How wide it is at its widest, metres."},"color":{"type":"string","doc":"Its colour where it leaves the node, CSS."},"tipColor":{"type":"string","doc":"Its colour at its far end; the same as \`color\` when absent."},"luminanceCdM2":{"type":"number"},"raisesDust":{"type":"boolean","doc":"Whether it raises dust where it meets the ground — a flame reaching loose, dry soil does, the more the nearer it gets."}}},"motions":{"type":"object","doc":"How far along each of its model's own movements it is, by the movement's name in the model (a glTF animation): 0 at its start, 1 at its end, and on past 1 for one that repeats — a turn made three times is 3."},"lights":{"type":"object","doc":"The luminance of each of its model's own lights, cd/m², by the name of the light's material in the model: what lets one body carry lights that behave each their own way — a steady white at each end, a red one flashing between."}}},"sound":{"type":"object","doc":"What it sounds like: heard from where the observer stands, late by the time sound takes to come, shifted in pitch as it comes and goes, and masked by the noise of the place (see BodyHearing).","fields":{"kind":{"type":"enum","values":["unknown","airliner-narrow","airliner-wide","regional-turboprop","regional-jet","business-jet","light-piston","turboprop-light","helicopter-light","helicopter-medium","helicopter-heavy","military-jet","glider","balloon","ultralight","unmanned"]},"quieterDb":{"type":"number","doc":"Decibels less than the class's own sound power, across its spectrum."}}}}},"smoke":{"type":"array","doc":"Fires the interpretation lights on the ground, as their smoke — see SmokeSource.","fields":{"eastM":{"type":"number"},"northM":{"type":"number"},"fromT":{"type":"number"},"untilT":{"type":"number"},"halfLifeS":{"type":"number","doc":"How fast it dies down, seconds to half its thickness — brush flares and smoulders."}}},"flares":{"type":"array","doc":"Salvoes of decoy flares an aircraft dispensed, computed into bodies when shown — see FlareSalvoJson.","fields":{"id":{"type":"string"},"title":{"type":"string","loose":true},"aircraft":{"type":"object","doc":"The aircraft at one instant of the recording, flying straight and level from there on (and before).","fields":{"t":{"type":"number"},"eastM":{"type":"number"},"northM":{"type":"number"},"altitudeM":{"type":"number","doc":"Metres above the ground at the recording's origin — see BodyKeyframe.altitudeM."},"headingDeg":{"type":"number"},"speedMS":{"type":"number"},"originElevationM":{"type":"number","doc":"Its height above sea level at \`t\` would be this plus \`altitudeM\`: what sets how thick the air braking the flares is."},"model":{"type":"object","doc":"Drawn when given (a catalogue model or a primitive), dark and unlit as a fighter in an exercise often is.","fields":{"id":{"type":"string","doc":"Catalogue entry, resolved by the live DecorModelProvider."},"url":{"type":"string","doc":"Direct glTF/GLB address, taking precedence over \`id\`."},"headingOffsetDeg":{"type":"number","doc":"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},"credit":{"type":"object","doc":"Who made it and under what licence.","fields":{"title":{"type":"string"},"author":{"type":"string"},"license":{"type":"string","doc":"Licence name as its author states it, e.g."},"sourceUrl":{"type":"string","doc":"Where it came from, so the claim can be checked."}}}}},"sizeM":{"type":"object","fields":{"widthM":{"type":"number"},"lengthM":{"type":"number"},"heightM":{"type":"number"}}},"glow":{"type":"object","doc":"A light it gives off as it flies — its afterburner — which an observer may have taken for a phenomenon of its own.","fields":{"explains":{"type":"array","doc":"The phenomena of the account it claims to be — see BodyJson.explains."},"sizeM":{"type":"number","doc":"How wide it looks, m."},"luminousIntensityCd":{"type":"number"},"color":{"type":"string"},"fromT":{"type":"number","doc":"Ms of the recording."},"untilT":{"type":"number"},"onS":{"type":"number","doc":"Seconds lit, then seconds out, repeated from \`fromT\`; always lit when absent."},"offS":{"type":"number"}}},"sound":{"type":"object","doc":"What it sounds like (see BodySound): heard from the two minutes before the recording on, as its sound takes that long to come from afar.","fields":{"kind":{"type":"enum","values":["unknown","airliner-narrow","airliner-wide","regional-turboprop","regional-jet","business-jet","light-piston","turboprop-light","helicopter-light","helicopter-medium","helicopter-heavy","military-jet","glider","balloon","ultralight","unmanned"]},"quieterDb":{"type":"number","doc":"Decibels less than the class's own sound power, across its spectrum."}}}}},"releases":{"type":"array","doc":"When each flare leaves it, and which phenomena of the account it claims to be.","fields":{"t":{"type":"number","doc":"Ms of the recording."},"explains":{"type":"array","doc":"The phenomena of the account this flare claims to be — see BodyJson.explains."}}},"flare":{"type":"object","doc":"The flare dispensed: an MJU-7 when absent (see DecoyFlare.MJU_7).","fields":{"massKg":{"type":"number","doc":"Mass at ejection, kg."},"burnedKg":{"type":"number","doc":"Mass burned away by burnout, kg."},"dragAreaM2":{"type":"number","doc":"Drag coefficient times reference area, m²: how hard the air brakes it."},"ejectionMS":{"type":"number","doc":"The dispenser's kick, m/s, straight down out of the aircraft."},"burnS":{"type":"number","doc":"How long it burns, s."},"luminousIntensityCd":{"type":"number","doc":"Its luminous intensity at full burn, candela, in the visible: what the eye gets of it."},"flameSizeM":{"type":"number","doc":"How wide its burning plume looks, m: the size of the glowing sphere drawn."},"color":{"type":"string","doc":"The colour of its light, CSS."}}},"wind":{"type":"object","doc":"The wind at the flares' height: where it blows TOWARDS, degrees, and its speed.","fields":{"towardDeg":{"type":"number"},"speedMS":{"type":"number"}}}}},"variant":{"type":"object","doc":"When this interpretation is a VARIANT of the account: what the observer would have to have erred by for the account to be a misperception of a candidate, as the candidate search found it (see CandidateSearch, docs/witness-error-model.md).","fields":{"candidate":{"type":"string","doc":"Which candidate: a body of the sky (\\"Venus\\"), a satellite, a re-entry, an aircraft, or another interpretation."},"label":{"type":"string","doc":"What the candidate is called, as it was when the variant was kept."},"phenomenon":{"type":"string","doc":"The known phenomenon of Poher's matrix it is an instance of, when it is one."},"parameters":{"type":"object","doc":"The witness parameters applied: a clock offset in minutes, a rotation of the heading in degrees, factors on elevation and size.","fields":{"clockMinutes":{"type":"number"},"headingDeg":{"type":"number"},"elevationFactor":{"type":"number"},"sizeFactor":{"type":"number"},"durationFactor":{"type":"number"}}},"moved":{"type":"array","doc":"The values it moves: what the account says, what the variant reads, the bias and its status.","fields":{"field":{"type":"enum","values":["time","heading","elevation","size","duration","sweep"]},"stated":{"type":"number"},"variant":{"type":"number"},"parameter":{"type":"string"},"amount":{"type":"number"},"z":{"type":"number"},"status":{"type":"string"}}},"readings":{"type":"array","doc":"The structural readings it uses (see CandidateSearch's StructuralReading), each with what the studies say of it.","fields":{"reading":{"type":"string"},"status":{"type":"string"}}},"cost":{"type":"number","doc":"What it costs, in units of the laws' spread (σ), and the verdict it got."},"verdict":{"type":"enum","values":["consolidated","contested","incompatible"]},"modelId":{"type":"string","doc":"The error model it was computed with (see WitnessError.MODELS)."}}}}}`),Y={type:"object",doc:"What happened in the sky while it runs, as facts (a laser shot, a launch, a re-entry) — see SkyEventsJson.",fields:{beams:{type:"array",doc:"Beams of light sent through the air from the ground: a laser, a searchlight — see BeamJson.",fields:{id:{type:"string"},title:{type:"string",loose:!0},explains:{type:"array",doc:"The phenomena of the account (their `sourceId`s) this beam claims to be — see BodyJson.explains."},path:{type:"array",doc:"Where the light goes, in order: where it leaves the emitter, then where it is turned (a mirror) if it is, and where it ends (what it hits, or where it has gone far enough for nothing more of it to matter).",fields:{lat:{type:"number"},lng:{type:"number"},altitudeM:{type:"number",doc:"Altitude above the sea, metres."}}},wavelengthNm:{type:"number",doc:"The wavelength of the light, nm: 532 for the green of a frequency-doubled Nd:YAG laser, 520 for a green diode, 638 for a red one."},powerW:{type:"number",doc:"The optical power it carries, watts: its mean power for a pulsed laser, whose pulses an eye cannot tell apart."},divergenceMrad:{type:"number",doc:"How fast it widens, milliradians, full angle."},apertureM:{type:"number",doc:"How wide it leaves the emitter, metres."},fromT:{type:"number",doc:"When it is on, ms of the recording: from its start and until its end when absent."},untilT:{type:"number"}}},launches:{type:"array",doc:"Rocket launches, whose exhaust shows lit by a Sun the ground no longer sees — see LaunchJson.",fields:{id:{type:"string"},title:{type:"string",loose:!0},explains:{type:"array",doc:"The phenomena of the account (their `sourceId`s) this launch claims to be — see BodyJson.explains."},vehicle:{type:"object",doc:"What flew, as its operator names it.",fields:{name:{type:"string",doc:'"Falcon 9".'},mission:{type:"string",doc:'"Starlink Group 10-49".'},engine:{type:"object",doc:"The engine whose exhaust it is.",fields:{thrustKN:{type:"number",doc:"Thrust in vacuum, kN: a Merlin Vacuum's 934, an RL10's 110, a solid booster's thousands."},specificImpulseS:{type:"number",doc:"Specific impulse in vacuum, seconds: 348 for a Merlin Vacuum, 465 for an RL10, 285 for a solid."},propellant:{type:"enum",values:["rp1-lox","methane-lox","hydrogen-lox","hypergolic","solid"]}}}}},track:{type:"array",doc:"Where the stage stood at instants of the recording, ms.",fields:{t:{type:"number"},lat:{type:"number"},lng:{type:"number"},altitudeKm:{type:"number",doc:"Height above the ellipsoid, km."}}},burnFromT:{type:"number",doc:"When the engine started and stopped, ms of the recording."},burnUntilT:{type:"number"},plume:{type:"object",fields:{exhaustKgPerS:{type:"number",doc:"Mass of exhaust leaving the engine, kg per second."},condensedShare:{type:"number",doc:"The share of that exhaust that ends up as particles that scatter light, 0 to 1."},particleRadiusUm:{type:"number",doc:"Radius of those particles, micrometres."},expansionKmPerS:{type:"number",doc:"How fast a puff's radius grows, km per second, from `initialRadiusKm`."},initialRadiusKm:{type:"number"},lifetimeS:{type:"number",doc:"How long a puff lasts before the particles have gone, seconds."},collarShare:{type:"number",doc:"The share of the particles that is the collar: exhaust just let go, which leaves the nozzle at its exhaust velocity (3.4 km/s for a Merlin Vacuum) and, with almost no air to stop it, spreads into a wide, short-lived shell round the stage, where the rest stays behind as the narrow trail."},collarExpansionKmPerS:{type:"number",doc:"How fast the collar's radius grows, km per second, and how long it lasts, seconds."},collarLifetimeS:{type:"number"}}},jets:{type:"array",doc:"What else the stage lets go, each while some seconds of the recording.",fields:{id:{type:"string"},kind:{type:"enum",doc:`"burn": the engine burns, and what leaves it is its exhaust, with the vehicle's plume (its flow, its velocity, its collar and trail).`,values:["burn","vent"]},fromT:{type:"number",doc:"When it starts and stops, ms of the recording."},untilT:{type:"number"},angleDeg:{type:"number",doc:"Which way it leaves the stage, degrees from straight back along the stage's motion: 0 behind it (an engine pushing it on), 90 sideways, 180 ahead of it (an engine braking it)."},azimuthDeg:{type:"number",doc:"Round the stage's line of motion, which side it leaves on at the start, degrees from the side facing away from the Earth, towards the right of the motion."},spinPeriodS:{type:"number",doc:"How long the stage takes to turn once round its line of motion, seconds; the jet turns with it."},speedKmPerS:{type:"number",doc:"How fast it leaves the stage, km/s."},massKgPerS:{type:"number",doc:"Mass let go each second, kg."},condensedShare:{type:"number",doc:"What of it ends up as particles, their size, how fast a puff spreads and how long it lasts: as for the plume."},particleRadiusUm:{type:"number"},expansionKmPerS:{type:"number"},initialRadiusKm:{type:"number"},lifetimeS:{type:"number"}}}}},reentries:{type:"array",doc:"Re-entries: placed on the Earth, not round the observer — see ReentryJson.",fields:{id:{type:"string"},title:{type:"string",loose:!0},explains:{type:"array",doc:"The phenomena of the account (their `sourceId`s) this re-entry claims to be — see BodyJson.explains."},object:{type:"object",doc:"The object that came down, when it is known — the catalogue says which piece it was.",fields:{name:{type:"string",doc:'Its catalogue name — "SL-12 R/B".'},cosparId:{type:"string",doc:'International designator — "1968-013C".'},norad:{type:"number",doc:"US Space Surveillance Network catalogue number — 3136."}}},track:{type:"array",doc:"Where the leading point of the break-up was, at instants of the recording.",fields:{t:{type:"number"},lat:{type:"number"},lng:{type:"number"},altitudeKm:{type:"number",doc:"Height above the ellipsoid, km."}}},fragments:{type:"array",doc:"The pieces seen, each following the same path some seconds behind the leading point.",fields:{id:{type:"string"},lagS:{type:"number",doc:"Seconds behind the leading point, along the same path."},fromT:{type:"number",doc:"When it is first seen, ms of the recording — when it broke off, or when it began to glow."},untilT:{type:"number",doc:"When it goes out, ms — burned up, or slowed below glowing."},absoluteMagnitude:{type:"number",doc:"Its brightness as a magnitude at 100 km — the meteor astronomers' absolute magnitude, which a fireball report gives — from which the brightness at the observer's actual distance follows."},color:{type:"string",doc:"Its colour, as a CSS hex colour."},trainS:{type:"number",doc:"How long a stretch of its own past path still glows behind it, seconds: the tail observers describe."}}}}}}},F={type:"object",doc:"Where the data its scene would otherwise ask third parties for is kept, beside this file — see FrozenData.",fields:{live:{type:"boolean",doc:"Every group asked of its source at every play, and nothing taken at save: off (absent) by default."},relief:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},imagery:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},roads:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},water:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},railways:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},upperAir:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},map:{type:"object",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},weather:{type:"object",doc:"Where the weather is kept, when it is not inside the recording (its `weatherTrack` and `weatherSource`): an annex holding them, read as the recording is loaded (see RecordingSections).",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},place:{type:"object",doc:"The place's name and coordinates (`place`), read from the recording's file rather than looked up by a geocoder when present: inside the recording, or in the annex `src` names.",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},lightPollution:{type:"object",doc:"The night sky's figure (`lightPollution`), read from the recording's file rather than looked up in the atlas when present: inside the recording, or in the annex `src` names.",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}},decor:{type:"object",doc:"The scenery (`decor`), read from the recording's file rather than taken again from a survey of the place as the observer moves, when present: inside the recording, or in the annex `src` names.",fields:{src:{type:"string",doc:"The annex holding it, relative to the recording's own file (as a model's `url` is), or a `data:` URL for an annex kept inside the recording itself."},live:{type:"boolean",doc:"Asked of its source at every play, and never taken at save: what an author chooses by picking a source rather than the file."},takenOn:{type:"string",doc:"The day it was taken from its source, YYYY-MM-DD: what the data is a picture of (a road network, a survey) is that day's."},attribution:{type:"string",doc:"Whom it was taken from, as their licence asks it to be said."},refresh:{type:"boolean",doc:"Asked of its source again, once: the scene does not read the annex, and the editor takes it again at the next save and drops this."}}}}},B={$schema:e,version:t,id:o,time:r,endTime:a,durationSeconds:n,durationText:s,utcOffsetHours:i,timeZone:d,place:h,observer:c,account:l,sway:p,vehicle:u,description:y,image:m,summary:g,summaryTime:b,tags:f,timeline:w,observerTrack:v,weatherTrack:k,weather:S,soundTrack:M,decor:T,roads:D,traces:j,weatherSource:x,instrument:W,exposureSeconds:H,iso:A,lightPollution:R,observerMap:z,milestones:E,references:I,sources:O,interpretation:C,variants:L,skyEvents:Y,frozen:F};export{e as $schema,l as account,T as decor,B as default,y as description,n as durationSeconds,s as durationText,a as endTime,H as exposureSeconds,F as frozen,o as id,m as image,W as instrument,C as interpretation,A as iso,R as lightPollution,E as milestones,c as observer,z as observerMap,v as observerTrack,h as place,I as references,D as roads,Y as skyEvents,M as soundTrack,O as sources,g as summary,b as summaryTime,p as sway,f as tags,r as time,d as timeZone,w as timeline,j as traces,i as utcOffsetHours,L as variants,u as vehicle,t as version,S as weather,x as weatherSource,k as weatherTrack};
@@ -0,0 +1,2 @@
1
+ import{A as ut,L as dt,T as z,R as L,a as F,r as pt,l as $}from"./rr0-sighting.mjs";import{C as Z}from"./ContrailGrowth-DwHS8If9.js";class E{static OCTAVES=[63,125,250,500,1e3,2e3,4e3,8e3];static STANDARD={temperatureC:15,relativeHumidity:.7,pressureKPa:101.325};static REFERENCE_PRESSURE_KPA=101.325;static REFERENCE_TEMPERATURE_K=293.15;static TRIPLE_POINT_K=273.16;static speedOfSound(t){return 20.0468*Math.sqrt(t+273.15)}static absorptionDbPerKm(t,e=E.STANDARD){const a=e.temperatureC+273.15,n=E.REFERENCE_TEMPERATURE_K,i=e.pressureKPa/E.REFERENCE_PRESSURE_KPA,o=10**(-6.8346*(E.TRIPLE_POINT_K/a)**1.261+4.6151),s=e.relativeHumidity*100*o/i,r=i*(24+40400*s*(.02+s)/(.391+s)),h=i*(a/n)**-.5*(9+280*s*Math.exp(-4.17*((a/n)**(-1/3)-1))),c=t*t;return 8.686*c*(184e-13*(1/i)*Math.sqrt(a/n)+(a/n)**-2.5*(.01275*Math.exp(-2239.1/a)/(r+c/r)+.1068*Math.exp(-3352/a)/(h+c/h)))*1e3}static aWeightingDb(t){const e=t*t,a=12194**2*e*e/((e+20.6**2)*Math.sqrt((e+107.7**2)*(e+737.9**2))*(e+12194**2));return 20*Math.log10(a)+2}static sumDb(t){return 10*Math.log10(t.reduce((e,a)=>e+10**(a/10),0))}}const mt=.25,x=.4,Q=6,K=12,gt=3,tt=.2,Et=.2,Mt=24,At=3,ft=12,et=1,bt=.7,St=1.5,Dt=.8,Rt=1.3,at=.9,nt=5,Ot=25,vt=.5,It=.12,it=[.13,.31],Tt=.16,Bt=1.6,_t=3.6,st=1.1,Nt=.3,Ct=1e3,rt=.7;class q{context;noiseBuffer;roughBuffers=new Map;voices=new Map;bus;reverb;reverbOut;level=1;master;paused=!0;requested=[];headingDeg=0;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(t){console.warn("AircraftAudio: Web Audio unavailable, aircraft stay silent:",t);return}}ut.unlock(this.context),this.apply()}setLevel(t){this.level=t,this.master&&(this.master.gain.value=t)}setPaused(t){t!==this.paused&&(this.paused=t,this.apply())}setVoices(t,e=0){this.requested=t,this.headingDeg=e,this.apply()}dispose(){for(const t of[...this.voices.keys()])this.release(t);this.context?.close(),this.context=void 0,this.master=void 0,this.bus=void 0}apply(){const t=this.context;if(!t)return;const e=this.paused?[]:this.requested,a=new Set(e.map(n=>n.id));for(const n of[...this.voices.keys()])a.has(n)||this.release(n);for(const n of e){const i=this.voices.get(n.id)??this.build(t,n);this.retarget(t,i,n)}}build(t,e){const a=t.createGain();a.gain.value=0;let n;const i=this.busOf(t);typeof t.createStereoPanner=="function"?(n=t.createStereoPanner(),a.connect(n).connect(i)):a.connect(i);const o=t.createGain(),s=e.character?.modulationDepth??0;o.gain.value=1-s/2;const r=t.createOscillator();r.type="sine";const h=t.createGain();h.gain.value=s/2,r.connect(h).connect(o.gain),o.connect(a);const c=t.createBufferSource();c.buffer=this.noise(t),c.loop=!0,c.loopStart=Math.random()*Q*.5;const d=[],l=[],u=[];for(const v of E.OCTAVES){const T=t.createBiquadFilter();T.type="bandpass",T.frequency.value=v,T.Q.value=et;const N=t.createBiquadFilter();N.type="bandpass",N.frequency.value=v,N.Q.value=et;const k=t.createGain();k.gain.value=0,c.connect(T).connect(N).connect(k).connect(o),d.push(T),l.push(N),u.push(k)}const p=[];for(const[v,T]of[[e.character?.toneHz,e.character?.toneShare],[e.character?.humHz,e.character?.humShare]])if(v!==void 0)for(let N=1;N<=At;N++){const k=t.createOscillator();k.type="sine",k.frequency.value=v*N;const X=t.createGain();X.gain.value=0,k.connect(X).connect(a),p.push({oscillator:k,gain:X,harmonic:N,baseHz:v,share:T??0})}const m=t.createGain();m.gain.value=0,m.connect(a.gain);const A=it.map(v=>{const T=t.createOscillator();return T.type="sine",T.frequency.value=v,T.connect(m),T}),b=t.createGain();b.gain.value=e.character?.roughness??tt;const R=t.createBufferSource();R.buffer=this.roughTable(t,gt),R.loop=!0,R.loopStart=Math.random()*K*.5,R.connect(b).connect(o.gain);const S=t.createGain();S.gain.value=(e.character?.roughness??tt)*Et;const f=t.createBufferSource();f.buffer=this.roughTable(t,Mt),f.loop=!0,f.loopStart=Math.random()*K*.5,f.connect(S).connect(o.gain);let B;const j=this.busOf(t)&&this.reverb;j&&(B=t.createGain(),B.gain.value=0,a.connect(B).connect(j));const V=[c,R,f,r,...p.map(v=>v.oscillator),...A];for(const v of V)v.start();const J={nodes:[a,o,h,b,S,m,...p.map(v=>v.gain),...d,...l,...u,...B?[B]:[],...n?[n]:[],...V],sources:V,filters:d,seconds:l,gains:u,tones:p,lfo:r,lfoDepth:h,wander:A,wanderDepth:m,roughDepth:b,crackleDepth:S,beat:o,output:a,panner:n,send:B};return this.voices.set(e.id,J),J}retarget(t,e,a){const n=t.currentTime,i=(p,m)=>p.setTargetAtTime(m,n,mt),o=Math.min(St,Math.max(bt,a.dopplerRatio)),s=Math.min(Rt,Math.max(Dt,Math.sqrt(a.dopplerRatio))),r=t.sampleRate*.45,h=a.bandAmplitudes.map((p,m)=>p*q.unweighting(E.OCTAVES[m])),c=Math.sqrt(h.reduce((p,m)=>p+m*m,0)),d=Math.min(1,Math.max(0,(a.distanceKm-nt)/(Ot-nt)));E.OCTAVES.forEach((p,m)=>{i(e.filters[m].frequency,Math.min(r,p*s)),i(e.seconds[m].frequency,Math.min(r,p*s)),i(e.gains[m].gain,h[m]*Math.sqrt(Ct/p))});const l=a.character;for(const{oscillator:p,gain:m,harmonic:A,baseHz:b,share:R}of e.tones){const S=b*A;i(p.frequency,S*o);const f=S*o;i(m.gain,f<r?R*Tt*h[q.bandOf(f)]/A:0)}l?.modulationHz!==void 0&&i(e.lfo.frequency,l.modulationHz*o);const u=c>0?rt*(1-Math.exp(-c/rt))/c:1;if(i(e.output.gain,u*(1-Nt*d)),i(e.wanderDepth.gain,u*It*d/it.length),e.send&&i(e.send.gain,Bt*d),e.panner){const p=(a.azimuthDeg-this.headingDeg)*Math.PI/180;i(e.panner.pan,Math.max(-at,Math.min(at,Math.sin(p)))*(1-vt*d))}}static unweighting(t){return 10**(Math.min(ft,Math.max(0,-E.aWeightingDb(t)))/20)}static bandOf(t){let e=0;return E.OCTAVES.forEach((a,n)=>{Math.abs(Math.log(t/a))<Math.abs(Math.log(t/E.OCTAVES[e]))&&(e=n)}),e}roughTable(t,e){const a=this.roughBuffers.get(e);if(a)return a;const n=Math.floor(t.sampleRate*K),i=t.createBuffer(1,n,t.sampleRate),o=i.getChannelData(0),s=Array.from({length:K*e},()=>(Math.random()*2-1)**3),r=t.sampleRate/e;for(let h=0;h<o.length;h++){const c=h/r,d=Math.floor(c),l=c-d,u=l*l*(3-2*l);o[h]=s[d%s.length]*(1-u)+s[(d+1)%s.length]*u}return this.roughBuffers.set(e,i),i}roomImpulse(t){const e=Math.floor(t.sampleRate*_t),a=t.createBuffer(2,e,t.sampleRate);for(let n=0;n<2;n++){const i=a.getChannelData(n);let o=0;for(let s=0;s<e;s++){const r=s/t.sampleRate,h=.6*Math.exp(-r/(st*2))+.04;o+=h*(Math.random()*2-1-o),i[s]=o*Math.exp(-r/st)}}return a}busOf(t){if(this.bus)return this.bus;const e=t.createGain();if(e.gain.value=this.level,e.connect(t.destination),this.master=e,typeof t.createConvolver=="function"){const a=t.createConvolver();a.buffer=this.roomImpulse(t);const n=t.createGain();n.gain.value=.9,a.connect(n),this.reverb=a,this.reverbOut=n}if(typeof t.createDynamicsCompressor=="function"){const a=t.createDynamicsCompressor();a.threshold.value=-6,a.knee.value=3,a.ratio.value=4,a.attack.value=.005,a.release.value=.25,a.connect(e),this.bus=a}else this.bus=e;return this.reverbOut?.connect(this.bus),this.bus}release(t){const e=this.voices.get(t);if(!e)return;this.voices.delete(t);const a=this.context;a&&(e.output.gain.setTargetAtTime(0,a.currentTime,x/3),setTimeout(()=>{for(const n of e.sources)try{n.stop()}catch{}for(const n of e.nodes)n.disconnect()},x*1e3*3))}noise(t){if(this.noiseBuffer)return this.noiseBuffer;const e=t.createBuffer(1,t.sampleRate*Q,t.sampleRate),a=e.getChannelData(0);for(let n=0;n<a.length;n++)a[n]=Math.random()*2-1;return this.noiseBuffer=e,e}}class _{static BASE_DB_A=30;static WIND_DB_A=[[0,10],[1,28],[2,36],[3,40],[5,46],[8,52],[12,58],[20,66]];static RAIN_DB_A=[[0,0],[.1,42],[.3,50],[.6,58],[1,65]];static BED_RMS=.2;static MINIMUM_REFERENCE=.008;static WIND_BED_MIN_MS=1;static WIND_BED_FULL_MS=20;static dbA(t){const e=[_.BASE_DB_A,_.along(_.WIND_DB_A,t.windSpeed)];return(t.precipitationType==="rain"||t.precipitationType==="hail")&&e.push(_.along(_.RAIN_DB_A,t.precipitationIntensity)),10*Math.log10(e.filter(a=>a>0).reduce((a,n)=>a+10**(n/10),0))}static bedVolume(t){const e=t.windSpeed>_.WIND_BED_MIN_MS?Math.min(t.windSpeed/_.WIND_BED_FULL_MS,1):0,a=t.precipitationType==="hail"?Math.min(1,t.precipitationIntensity*1.3+.2):t.precipitationType==="rain"?t.precipitationIntensity:0;return Math.hypot(e,a)}static referenceAmplitude(t){return Math.max(_.BED_RMS*_.bedVolume(t),_.MINIMUM_REFERENCE)}static along(t,e){if(e<=t[0][0])return t[0][1];for(let a=1;a<t.length;a++)if(e<=t[a][0]){const[n,i]=t[a-1],[o,s]=t[a];return i+(s-i)*(e-n)/(o-n)}return t[t.length-1][1]}}class y{static TYPES={A318:["Airbus A318","airliner-narrow",34.1,31.4],A319:["Airbus A319","airliner-narrow",34.1,33.8],A320:["Airbus A320","airliner-narrow",34.1,37.6],A321:["Airbus A321","airliner-narrow",34.1,44.5],A19N:["Airbus A319neo","airliner-narrow",35.8,33.8],A20N:["Airbus A320neo","airliner-narrow",35.8,37.6],A21N:["Airbus A321neo","airliner-narrow",35.8,44.5],B737:["Boeing 737-700","airliner-narrow",34.3,33.6],B738:["Boeing 737-800","airliner-narrow",35.8,39.5],B739:["Boeing 737-900","airliner-narrow",35.8,42.1],B38M:["Boeing 737 MAX 8","airliner-narrow",35.9,39.5],B39M:["Boeing 737 MAX 9","airliner-narrow",35.9,42.2],B752:["Boeing 757-200","airliner-narrow",38,47.3],B753:["Boeing 757-300","airliner-narrow",38,54.4],B762:["Boeing 767-200","airliner-wide",47.6,48.5],B763:["Boeing 767-300","airliner-wide",47.6,54.9],B764:["Boeing 767-400","airliner-wide",51.9,61.4],B772:["Boeing 777-200","airliner-wide",60.9,63.7],B77L:["Boeing 777-200LR","airliner-wide",64.8,63.7],B77W:["Boeing 777-300ER","airliner-wide",64.8,73.9],B788:["Boeing 787-8","airliner-wide",60.1,56.7],B789:["Boeing 787-9","airliner-wide",60.1,62.8],B78X:["Boeing 787-10","airliner-wide",60.1,68.3],B744:["Boeing 747-400","airliner-wide",64.4,70.7],B748:["Boeing 747-8","airliner-wide",68.4,76.3],A306:["Airbus A300-600","airliner-wide",44.8,54.1],A310:["Airbus A310","airliner-wide",43.9,46.7],A332:["Airbus A330-200","airliner-wide",60.3,58.8],A333:["Airbus A330-300","airliner-wide",60.3,63.7],A338:["Airbus A330-800","airliner-wide",64,58.8],A339:["Airbus A330-900","airliner-wide",64,63.7],A343:["Airbus A340-300","airliner-wide",60.3,63.7],A346:["Airbus A340-600","airliner-wide",63.5,75.4],A359:["Airbus A350-900","airliner-wide",64.8,66.8],A35K:["Airbus A350-1000","airliner-wide",64.8,73.8],A388:["Airbus A380-800","airliner-wide",79.8,72.7],E170:["Embraer 170","regional-jet",26,29.9],E75L:["Embraer 175","regional-jet",26,31.7],E75S:["Embraer 175","regional-jet",26,31.7],E190:["Embraer 190","regional-jet",28.7,36.2],E195:["Embraer 195","regional-jet",28.7,38.7],E290:["Embraer 190-E2","regional-jet",33.7,36.2],E295:["Embraer 195-E2","regional-jet",33.7,41.5],CRJ2:["Bombardier CRJ200","regional-jet",21.2,26.8],CRJ7:["Bombardier CRJ700","regional-jet",23.2,32.3],CRJ9:["Bombardier CRJ900","regional-jet",24.9,36.2],CRJX:["Bombardier CRJ1000","regional-jet",26.2,39.1],AT72:["ATR 72","regional-turboprop",27.1,27.2],AT76:["ATR 72-600","regional-turboprop",27.1,27.2],AT45:["ATR 42","regional-turboprop",24.6,22.7],DH8D:["Dash 8 Q400","regional-turboprop",28.4,32.8],DH8C:["Dash 8-300","regional-turboprop",27.4,25.7],SF34:["Saab 340","regional-turboprop",21.4,19.7],B190:["Beechcraft 1900","regional-turboprop",17.7,17.6],C25A:["Cessna CitationJet CJ2","business-jet",14.3,14.4],C56X:["Cessna Citation Excel","business-jet",17.2,16],C68A:["Cessna Citation Latitude","business-jet",22,18.9],C550:["Cessna Citation II","business-jet",15.8,14.4],E55P:["Embraer Phenom 300","business-jet",15.9,15.6],E50P:["Embraer Phenom 100","business-jet",12.3,12.8],GLF5:["Gulfstream G550","business-jet",28.5,29.4],FA7X:["Dassault Falcon 7X","business-jet",26.2,23.4],CL60:["Bombardier Challenger 600","business-jet",19.6,20.9],LJ45:["Learjet 45","business-jet",14.6,17.7],H25B:["Hawker 800","business-jet",15.7,15.6],C172:["Cessna 172","light-piston",11,8.3],C152:["Cessna 152","light-piston",10.2,7.3],C182:["Cessna 182","light-piston",10.9,8.8],P28A:["Piper Cherokee","light-piston",10.7,7.2],PA34:["Piper Seneca","light-piston",11.8,8.7],SR20:["Cirrus SR20","light-piston",10.9,7.9],SR22:["Cirrus SR22","light-piston",11.7,7.9],S22T:["Cirrus SR22T","light-piston",11.7,7.9],DA40:["Diamond DA40","light-piston",11.6,8.1],DA42:["Diamond DA42","light-piston",13.4,8.6],BE20:["Beechcraft King Air 200","turboprop-light",16.6,13.3],BE58:["Beechcraft Baron","light-piston",11.5,9.1],C208:["Cessna Caravan","turboprop-light",15.9,11.5],PC12:["Pilatus PC-12","turboprop-light",16.3,14.4],TBM9:["Daher TBM 900","turboprop-light",12.8,10.7],EC35:["Airbus H135","helicopter-light",10.2,12.1],EC45:["Airbus H145","helicopter-medium",11,13.6],AS50:["Airbus H125","helicopter-light",10.7,12.9],EC30:["Airbus H130","helicopter-light",10.7,12.6],B407:["Bell 407","helicopter-light",10.7,12.7],B06:["Bell 206","helicopter-light",10.2,12],B429:["Bell 429","helicopter-medium",10.9,13],B505:["Bell 505","helicopter-light",10.4,11.9],R44:["Robinson R44","helicopter-light",10.1,11.7],R22:["Robinson R22","helicopter-light",7.7,8.8],A109:["Agusta A109","helicopter-medium",11,13],A139:["Leonardo AW139","helicopter-medium",13.8,16.7],S92:["Sikorsky S-92","helicopter-heavy",17.2,20.9],H60:["Sikorsky UH-60 Black Hawk","helicopter-heavy",16.4,19.8],T38:["Northrop T-38 Talon","military-jet",7.7,14.1],HAWK:["BAE Hawk","military-jet",9.4,11.9],GLID:["Glider","glider",17,7],BALL:["Balloon","balloon",15,20],ULAC:["Ultralight","ultralight",9.5,6],C42:["Ikarus C42","ultralight",9.4,6.3],DRON:["Drone","unmanned",1,.6]};static CATEGORIES={A1:["Light aircraft","light-piston",11,8.3],A2:["Small aircraft","business-jet",15,15],A3:["Large aircraft","airliner-narrow",34.1,37.6],A4:["High-vortex large aircraft","airliner-narrow",38,47.3],A5:["Heavy aircraft","airliner-wide",60.3,63.7],A6:["High-performance aircraft","military-jet",8,14],A7:["Rotorcraft","helicopter-medium",10.7,12.7],B1:["Glider","glider",17,7],B2:["Lighter-than-air","balloon",15,20],B4:["Ultralight","ultralight",9.5,6],B6:["Unmanned aircraft","unmanned",1.5,1]};static GENERIC={name:"Airliner",kind:"airliner-narrow",spanM:34.1,lengthM:37.6,basis:"nothing"};static of(t){const e=t?.type?.toUpperCase(),a=e?y.TYPES[e]:void 0;if(a)return{code:e,name:a[0],kind:a[1],spanM:a[2],lengthM:a[3],basis:"type"};const n=t?.category?y.CATEGORIES[t.category]:void 0;return n?{name:n[0],kind:n[1],spanM:n[2],lengthM:n[3],basis:"category"}:{...y.GENERIC}}static isHelicopter(t){return t.kind.startsWith("helicopter")}}class P{static DUSK_SUN_ELEVATION_DEG=-.83;static ALWAYS=new Set(["airliner-narrow","airliner-wide","regional-turboprop","regional-jet","business-jet"]);static NONE=new Set(["glider","balloon"]);static BRIGHTNESS={"light-piston":[.5,.2,.5],"turboprop-light":[.7,.4,.8],ultralight:[.4,.2,.4],unmanned:[0,.05,0],"helicopter-light":[1,1,1],"helicopter-medium":[1,1,1],"helicopter-heavy":[1,1,1],"military-jet":[1,1,1]};static lampsOn(t,e){if(P.NONE.has(t))return{navigation:!1,beacon:!1,strobes:!1};if(P.ALWAYS.has(t))return{navigation:!0,beacon:!0,strobes:!0};const a=e<P.DUSK_SUN_ELEVATION_DEG;return t==="unmanned"?{navigation:!1,beacon:!1,strobes:a}:{navigation:a,beacon:a,strobes:a}}static lights(t,e){const a=P.lampsOn(t.kind,e),n=t.kind.startsWith("helicopter"),i=dt.find(u=>u.id===(n?"helicopter":"airliner")).create(),[o,s]=n?[5,7]:[17,18],r=t.spanM/2/o,h=t.lengthM/2/s,[c,d,l]=P.BRIGHTNESS[t.kind]??[1,1,1];return i.filter(u=>u.id.startsWith("nav")?a.navigation:u.id.startsWith("strobe")?a.strobes:u.id.startsWith("beacon")?a.beacon:!0).filter(u=>!(u.id.startsWith("beacon")&&!a.beacon)).map(u=>{const p=u.id.startsWith("nav")?c:u.id.startsWith("strobe")?d:l;return{...u,offsetM:{x:u.offsetM.x*r,y:u.offsetM.y,z:u.offsetM.z*h},intensity:(u.intensity??1)*p}}).filter(u=>(u.intensity??1)>0)}static bodyColor(t){return t.kind==="military-jet"?"#8a8e94":t.kind.startsWith("helicopter")?"#c8ccd1":t.kind==="unmanned"?"#2c2e33":"#dde0e4"}}class U{static FAMILIES={"amvlab-a320":["A318","A319","A320","A321","A19N","A20N","A21N"],"amvlab-b737":["B732","B733","B734","B735","B736","B737","B738","B739","B37M","B38M","B39M","B3XM"],"amvlab-a350":["A359","A35K"],"amvlab-a380":["A388"],"amvlab-b787":["B788","B789","B78X"]};static BY_TYPE=new Map(Object.entries(U.FAMILIES).flatMap(([t,e])=>e.map(a=>[a,t])));static idOf(t){return t?U.BY_TYPE.get(t.toUpperCase()):void 0}}class D{static ID_PREFIX=z.ID_PREFIX;static MAX_OBJECTS=40;static FT_TO_M=.3048;static DEG=Math.PI/180;static from(t,e,a,n=D.MAX_OBJECTS,i={}){return new wt(t,e,a,n,i).build()}static idOf(t,e){return z.idOf(t,e)}static keyOf(t){return z.keyOf(t)}static HEIGHT_OVER_LENGTH=.29;static sizeOf(t){return{widthM:t.spanM,lengthM:t.lengthM,heightM:t.lengthM*D.HEIGHT_OVER_LENGTH}}static offsetOf(t,e){const a=L.seenFrom(t,{lat:e.lat,lng:e.lng,heightM:e.altitudeFt*D.FT_TO_M}),n=a.distanceKm*1e3,i=n*Math.cos(a.altitudeDeg*D.DEG);return{eastM:i*Math.sin(a.azimuthDeg*D.DEG),northM:i*Math.cos(a.azimuthDeg*D.DEG),upM:n*Math.sin(a.altitudeDeg*D.DEG),altitudeDeg:a.altitudeDeg,distanceKm:a.distanceKm}}static segments(t){const e=[];let a=[];for(const n of t.points){const i=a[a.length-1];i&&n.t-i.t>F.MAX_GAP_S*1e3&&(e.push(a),a=[]),a.push(n)}return e.push(a),e.filter(n=>n.length>=2)}static bearingDeg(t,e){return(Math.atan2(e.eastM-t.eastM,e.northM-t.northM)/D.DEG+360)%360}}class wt{constructor(t,e,a,n,i){this.tracks=t,this.observer=e,this.startMs=a,this.maxObjects=n,this.options=i}build(){const t=[];for(const a of this.tracks){const n=D.keyOf(a),i=this.options.models?.get(n)??y.of(void 0);D.segments(a).forEach((o,s)=>{const r=o.map(u=>({point:u,at:D.offsetOf(this.observer,u)}));if(!r.some(u=>u.at.altitudeDeg>=0))return;const h=r.map((u,p)=>({t:u.point.t-this.startMs,eastM:u.at.eastM,northM:u.at.northM,altitudeM:u.at.upM,headingDeg:u.point.trackDeg??(p+1<r.length?D.bearingDeg(u.at,r[p+1].at):D.bearingDeg(r[p-1].at,u.at))})),c=D.idOf(a,s),d=r[Math.floor(r.length/2)].point,l=this.options.sunElevationAt?.(d)??90;t.push({object:{id:c,kind:"aircraft",eastM:h[0].eastM,northM:h[0].northM,headingDeg:h[0].headingDeg,sizeM:D.sizeOf(i),model:U.idOf(i.code)?{id:U.idOf(i.code)}:void 0,color:P.bodyColor(i),lights:P.lights(i,l),track:h},presence:{fromMs:h[0].t,untilMs:h[h.length-1].t},flight:{icao:a.icao,nonIcao:a.nonIcao,points:o},closestKm:Math.min(...r.map(u=>u.at.distanceKm))})})}t.sort((a,n)=>a.closestKm-n.closestKm);const e=t.slice(0,this.maxObjects);return{objects:e.map(a=>a.object),presence:new Map(e.map(a=>[a.object.id,a.presence])),flights:new Map(e.map(a=>[a.object.id,a.flight])),total:t.length,shown:e.length}}}const ht=[{id:"turbofan-narrow-body",name:"Turbofan airliner, narrow-body",kinds:["airliner-narrow"],totalDbA:148,shapeDb:[-32,-17,-9,-6,-5,-7,-12,-22],character:{toneHz:1800,toneShare:.3,humHz:90,humShare:.7}},{id:"turbofan-wide-body",name:"Turbofan airliner, wide-body",kinds:["airliner-wide"],totalDbA:151,shapeDb:[-28,-14,-7,-4,-3,-6,-12,-22],character:{toneHz:1200,toneShare:.3,humHz:70,humShare:.7}},{id:"regional-jet",name:"Regional jet",kinds:["regional-jet"],totalDbA:144,shapeDb:[-32,-17,-9,-5,-4,-6,-11,-21],character:{toneHz:1900,toneShare:.3,humHz:95,humShare:.7}},{id:"business-jet",name:"Business jet",kinds:["business-jet"],totalDbA:141,shapeDb:[-30,-15,-8,-4,-3,-4,-9,-19],character:{toneHz:2500,toneShare:.25,humHz:110,humShare:.7}},{id:"turboprop",name:"Turboprop",kinds:["regional-turboprop","turboprop-light"],totalDbA:138,shapeDb:[-14,-9,-8,-6,-8,-13,-20,-28],character:{toneHz:80,toneShare:.5}},{id:"light-piston",name:"Light piston aircraft",kinds:["light-piston"],totalDbA:130,shapeDb:[-18,-9,-5,-4,-8,-14,-22,-30],character:{toneHz:80,toneShare:.4}},{id:"ultralight",name:"Ultralight",kinds:["ultralight"],totalDbA:124,shapeDb:[-16,-8,-5,-4,-9,-15,-23,-31],character:{toneHz:140,toneShare:.4}},{id:"helicopter-light",name:"Light helicopter",kinds:["helicopter-light"],totalDbA:134,shapeDb:[-15,-8,-4,-3,-6,-10,-16,-24],character:{modulationHz:20,modulationDepth:.5}},{id:"helicopter-medium",name:"Medium helicopter",kinds:["helicopter-medium"],totalDbA:138,shapeDb:[-14,-7,-4,-3,-6,-10,-16,-24],character:{modulationHz:22,modulationDepth:.5}},{id:"helicopter-heavy",name:"Heavy helicopter",kinds:["helicopter-heavy"],totalDbA:144,shapeDb:[-12,-6,-4,-4,-7,-11,-17,-25],character:{modulationHz:17,modulationDepth:.5}},{id:"military-jet",name:"Military jet",kinds:["military-jet"],totalDbA:152,shapeDb:[-26,-12,-6,-3,-2,-3,-7,-15]},{id:"glider",name:"Glider",kinds:["glider"],totalDbA:85,shapeDb:[-30,-22,-15,-10,-8,-8,-14,-22]},{id:"balloon",name:"Hot-air balloon",kinds:["balloon"],totalDbA:100,shapeDb:[-25,-15,-9,-5,-3,-6,-12,-20]},{id:"unmanned",name:"Unmanned aircraft",kinds:["unmanned"],totalDbA:88,shapeDb:[-40,-30,-20,-12,-6,-3,-3,-8],character:{toneHz:450,toneShare:1}}],ot=ht.map(C=>({id:C.id,name:C.name,soundPowerA:C.shapeDb.map(t=>t+C.totalDbA-E.sumDb(C.shapeDb)),character:C.character}));class g{static DEFAULT_AMBIENT_DB_A=35;static DETECTION_MARGIN_DB=3;static AMBIENT_SHAPE_DB=[-12,-7,-5,-5,-7,-11,-17,-25];static HEARING_FLOOR_DB_A=0;static ITERATIONS=30;static CONVERGED_MS=.5;static VELOCITY_STEP_MS=500;static noiseOfKind(t){const e=ht.find(a=>a.kinds.includes(t));return ot.find(a=>a.id===(e?.id??"turbofan-narrow-body"))}static bandsAt(t,e,a=E.STANDARD){const n=20*Math.log10(Math.max(e*1e3,1))+11;return E.OCTAVES.map((i,o)=>t.soundPowerA[o]-n-E.absorptionDbPerKm(i,a)*e)}static couldBeHeard(t,e,a=g.DEFAULT_AMBIENT_DB_A,n=E.STANDARD){const i=g.ambientBands(a);return g.bandsAt(t,e,n).some((o,s)=>o>=Math.max(i[s]+g.DETECTION_MARGIN_DB,g.HEARING_FLOOR_DB_A))}static ambientBands(t){const e=g.AMBIENT_SHAPE_DB,a=E.sumDb(e);return e.map(n=>t+n-a)}static heardAt(t,e,a,n={}){const i=n.air??E.STANDARD,o=E.speedOfSound(i.temperatureC),s=t.points[t.points.length-1]?.t;if(s===void 0)return;let r=Math.min(a,s),h=F.positionAt(t,r);if(!h)return;let c=L.seenFrom(e,h.geo);for(let f=0;f<g.ITERATIONS;f++){const B=a-c.distanceKm*1e3*1e3/o,j=Math.abs(B-r);if(r=B,h=F.positionAt(t,r),!h)return;if(c=L.seenFrom(e,h.geo),j<g.CONVERGED_MS)break}const d=F.positionAt(t,a),l=d?L.seenFrom(e,d.geo):c,u=n.noise??ot[0],p=n.ambientDbA??g.DEFAULT_AMBIENT_DB_A,m=g.ambientBands(p),A=g.bandsAt(u,c.distanceKm,i),b=A.map((f,B)=>f>=Math.max(m[B]+g.DETECTION_MARGIN_DB,g.HEARING_FLOOR_DB_A)),R=A.indexOf(Math.max(...A)),S=b.lastIndexOf(!0);return{track:t,audible:b.some(Boolean),levelDbA:E.sumDb(A),bandsDbA:A,ambientDbA:p,delayS:(a-r)/1e3,emittedAtMs:r,sound:c,seen:l,lagDeg:g.angleBetween(c,l),dopplerRatio:g.doppler(t,e,r,o),dominantHz:E.OCTAVES[R],cutoffHz:S<0?void 0:E.OCTAVES[S]}}static profile(t,e,a,n,i=2e3,o={}){const s=[];let r,h,c=a;for(let d=a;d<=n;d+=i){const l=g.heardAt(t,e,d,o);l&&(!h||l.levelDbA>h.levelDbA)&&(h=l,c=d),l?.audible?r?r.untilMs=d:s.push(r={fromMs:d,untilMs:d}):r=void 0}return{track:t,audible:s,peakDbA:h?.levelDbA??-1/0,peakAtMs:c,peakDelayS:h?.delayS??0}}static angleBetween(t,e){const a=c=>{const d=c.altitudeDeg*Math.PI/180,l=c.azimuthDeg*Math.PI/180;return[Math.cos(d)*Math.sin(l),Math.cos(d)*Math.cos(l),Math.sin(d)]},[n,i,o]=a(t),[s,r,h]=a(e);return Math.acos(Math.min(1,Math.max(-1,n*s+i*r+o*h)))*180/Math.PI}static doppler(t,e,a,n){const i=g.VELOCITY_STEP_MS,o=F.positionAt(t,a-i),s=F.positionAt(t,a+i);if(!o||!s)return 1;const r=L.seenFrom(e,o.geo).distanceKm*1e3,h=L.seenFrom(e,s.geo).distanceKm*1e3;return 1/(1-(r-h)/(2*i/1e3)/n)}}class Ft{static at(t,e,a,n,i={}){const o=F.seenFrom(t,a,n);if(!o)return;const s=y.of(e),r=g.heardAt(t,a,n,{noise:g.noiseOfKind(s.kind),...i});return{hex:`${t.nonIcao?"~":""}${t.icao.toString(16).padStart(6,"0")}`,registration:e?.registration,typeCode:s.code??e?.type,name:s.basis==="nothing"?void 0:s.name,kind:s.kind,basis:s.basis,military:e?.military===!0,restricted:e?.restricted===!0,altitudeFt:o.altitudeFt,groundSpeedKt:o.groundSpeedKt,trackDeg:o.trackDeg,sky:o.sky,angularRateDegPerS:o.angularRateDegPerS,hearing:r&&{audible:r.audible,levelDbA:r.levelDbA,ambientDbA:r.ambientDbA,delayS:r.delayS,lagDeg:r.lagDeg,dopplerRatio:r.dopplerRatio,cutoffHz:r.cutoffHz,dominantHz:r.dominantHz}}}}class O{static REFERENCE_AMPLITUDE=.02;static MASKING_RANGE_DB=6;static KNEE_DB=20;static COMPRESSION_RATIO=1.5;static EXPANSION_RATIO=1.8;static DEFAULT_MAX_VOICES=4;static candidates(t,e,a,n,i={}){const o=new Set;for(const[s,r]of t){const h=g.noiseOfKind(y.of(e.get(a(r))).kind);let c=1/0;for(const d of r.points)c=Math.min(c,L.seenFrom(n,{lat:d.lat,lng:d.lng,heightM:d.altitudeFt*.3048}).distanceKm);g.couldBeHeard(h,c,i.ambientDbA,i.air)&&o.add(s)}return o}static voices(t,e,a,n,i,o,s={}){const r=[];for(const h of t){const c=e.get(h);if(!c||s.only?.(h)===!1)continue;const d=s.kindOf?.(h)??y.of(a.get(n(c))).kind,l=s.noiseOf?.(h)??g.noiseOfKind(d),u=g.heardAt(c,i,o,{...s,noise:l});u?.audible&&r.push({id:h,kind:d,bandAmplitudes:O.bandAmplitudes(u.bandsDbA,u.ambientDbA,s.referenceAmplitude),levelDbA:u.levelDbA,distanceKm:u.sound.distanceKm,dopplerRatio:u.dopplerRatio,azimuthDeg:u.sound.azimuthDeg,elevationDeg:u.sound.altitudeDeg,character:l.character})}return r.sort((h,c)=>c.levelDbA-h.levelDbA).slice(0,s.maxVoices??O.DEFAULT_MAX_VOICES)}static bandAmplitudes(t,e,a=O.REFERENCE_AMPLITUDE){const n=g.ambientBands(e),i=g.DETECTION_MARGIN_DB,o=E.sumDb(t),s=o-e,r=s>=O.KNEE_DB?O.KNEE_DB+(s-O.KNEE_DB)/O.COMPRESSION_RATIO:O.KNEE_DB-(O.KNEE_DB-s)*O.EXPANSION_RATIO,h=a*10**(r/20);return t.map((c,d)=>{const l=Math.min(1,Math.max(0,(c-n[d]-i+O.MASKING_RANGE_DB)/(2*O.MASKING_RANGE_DB)));return l*l*(3-2*l)*h*10**((c-o)/20)})}static BANDS_HZ=E.OCTAVES}class W{static ENGLISH={unnamed:"Aircraft {hex}",altitude:"{value} ft",speed:"{value} kt",heading:"heading {value}°",position:"{distance} km away, {elevation}° above the horizon, {bearing}",heard:"Heard at {level} dB(A), left the aircraft {delay} s ago, {lag}° behind it: {character}{pitch}",rumble:"a low rumble, nothing above {cutoff} Hz",muffled:"a dull noise, nothing above {cutoff} Hz",broad:"a broad roar",pitchHigher:", pitch {percent} % higher",pitchLower:", pitch {percent} % lower",inaudible:"Not audible here: {level} dB(A) against {ambient} dB(A) of ambient noise",soundUnknown:"Its sound cannot be worked out: the aircraft was not recorded yet when it left",contrailPersistent:"Leaves a trail that lasts: the air is supersaturated over ice ({rhi} % over ice)",contrailShort:"Leaves a short-lived trail: the air is dry over ice ({rhi} % over ice)",military:"military",restricted:"identity withheld by its owner",candidate:"A compatible candidate, not an identification"};static RUMBLE_BELOW_HZ=500;static DULL_BELOW_HZ=2e3;static PITCH_NOTICED=.03;static text(t,e,a,n){const i=(l,u=0)=>l.toLocaleString(n,{maximumFractionDigits:u}),o=(l,u)=>l.replace(/\{(\w+)\}/g,(p,m)=>u[m]??`{${m}}`),s=[],r=[t.registration,t.name].filter(Boolean).join(" · ");s.push(r||o(e.unnamed,{hex:t.hex}));const h=[o(e.altitude,{value:i(Math.round(t.altitudeFt/100)*100)}),t.groundSpeedKt===void 0?void 0:o(e.speed,{value:i(Math.round(t.groundSpeedKt))}),t.trackDeg===void 0?void 0:o(e.heading,{value:i(Math.round(t.trackDeg))})].filter(Boolean);s.push(h.join(" · ")),s.push(o(e.position,{distance:i(t.sky.distanceKm,t.sky.distanceKm<10?1:0),elevation:i(Math.round(t.sky.altitudeDeg)),bearing:a(t.sky.azimuthDeg)}));const c=t.hearing;if(!c)s.push(e.soundUnknown);else if(!c.audible)s.push(o(e.inaudible,{level:i(Math.round(c.levelDbA)),ambient:i(Math.round(c.ambientDbA))}));else{const l=c.cutoffHz??0,u=l<=W.RUMBLE_BELOW_HZ?e.rumble:l<=W.DULL_BELOW_HZ?e.muffled:e.broad,p=c.dopplerRatio-1,m=Math.abs(p)<W.PITCH_NOTICED?"":o(p>0?e.pitchHigher:e.pitchLower,{percent:i(Math.round(Math.abs(p)*100))});s.push(o(e.heard,{level:i(Math.round(c.levelDbA)),delay:i(Math.round(c.delayS)),lag:i(Math.round(c.lagDeg)),character:o(u,{cutoff:i(l)}),pitch:m}))}t.contrail&&s.push(o(t.contrail.persistent?e.contrailPersistent:e.contrailShort,{rhi:i(Math.round(t.contrail.iceRelativeHumidity*100))}));const d=[t.military?e.military:void 0,t.restricted?e.restricted:void 0].filter(Boolean);return d.length>0&&s.push(d.join(" · ")),s.push(e.candidate),s.join(`
2
+ `)}}class Y{static EFFICIENCY={"airliner-narrow":.34,"airliner-wide":.38,"regional-jet":.32,"business-jet":.3,"military-jet":.22,unknown:.34};static efficiencyOf(t){return Y.EFFICIENCY[t]}}class M{static EMISSION_INDEX_WATER=1.25;static COMBUSTION_HEAT=432e5;static AIR_HEAT_CAPACITY=1004;static MOLAR_MASS_RATIO=.622;static FT_TO_M=.3048;static TROPOPAUSE_M=11e3;static TROPOPAUSE_HPA=226.32;static ISOTHERMAL_SCALE_HEIGHT_M=6341.62;static pressureAtAltitudeFt(t){const e=t*M.FT_TO_M;return e<=M.TROPOPAUSE_M?1013.25*(1-225577e-10*e)**5.25588:M.TROPOPAUSE_HPA*Math.exp(-(e-M.TROPOPAUSE_M)/M.ISOTHERMAL_SCALE_HEIGHT_M)}static saturationOverWaterPa(t){const e=t+273.15;return Math.exp(54.842763-6763.22/e-4.21*Math.log(e)+367e-6*e+Math.tanh(.0415*(e-218.8))*(53.878-1331.22/e-9.44523*Math.log(e)+.014025*e))}static saturationOverIcePa(t){const e=t+273.15;return Math.exp(9.550426-5723.265/e+3.53068*Math.log(e)-.00728332*e)}static iceRelativeHumidity(t,e){return e*M.saturationOverWaterPa(t)/M.saturationOverIcePa(t)}static mixingSlope(t,e){return M.EMISSION_INDEX_WATER*M.AIR_HEAT_CAPACITY*t*100/(M.MOLAR_MASS_RATIO*M.COMBUSTION_HEAT*(1-e))}static criticalTemperatureC(t,e){const a=Math.log(M.mixingSlope(t,e)-.053);return-46.46+9.43*a+.72*a*a}static requiredHumidity(t,e,a){const n=M.criticalTemperatureC(t,e);return(M.mixingSlope(t,e)*(a-n)+M.saturationOverWaterPa(n))/M.saturationOverWaterPa(a)}static evaluate(t,e){const a=M.criticalTemperatureC(t.pressureHpa,e),n=M.requiredHumidity(t.pressureHpa,e,t.temperatureC),i=M.iceRelativeHumidity(t.temperatureC,t.relativeHumidity),o=t.temperatureC<a&&t.relativeHumidity>=n;return{forms:o,persistent:o&&i>=1,criticalTemperatureC:a,requiredHumidity:n,iceRelativeHumidity:i}}}class w{static HOLD_MS=36e5;static DEG=Math.PI/180;static GAS_CONSTANT_OVER_GRAVITY=287.05/9.80665;static at(t,e,a){if(t.length===0)return;const n=t[0],i=t[t.length-1];if(e<n.t-w.HOLD_MS||e>i.t+w.HOLD_MS)return;const o=t.findIndex(u=>u.t>e),s=o===0?t[0]:t[(o===-1?t.length:o)-1],r=o===-1||o===0?s:t[o],h=w.atLevel(s,a),c=w.atLevel(r,a);if(!h||!c)return;const d=r.t>s.t?Math.min(1,Math.max(0,(e-s.t)/(r.t-s.t))):0,l=(u,p)=>u+(p-u)*d;return{pressureHpa:a,temperatureC:l(h.temperatureC,c.temperatureC),relativeHumidity:l(h.relativeHumidity,c.relativeHumidity),driftEastMs:l(h.driftEastMs,c.driftEastMs),driftNorthMs:l(h.driftNorthMs,c.driftNorthMs),shearPerS:l(h.shearPerS,c.shearPerS)}}static atLevel(t,e){const a=t.levels;for(let n=0;n+1<a.length;n++){const i=a[n],o=a[n+1];if(e>i.pressureHpa||e<o.pressureHpa)continue;const s=(Math.log(i.pressureHpa)-Math.log(e))/(Math.log(i.pressureHpa)-Math.log(o.pressureHpa)),r=(u,p)=>u+(p-u)*s,h=u=>{const p=u.windFromDeg*w.DEG;return{east:-u.windSpeedMs*Math.sin(p),north:-u.windSpeedMs*Math.cos(p)}},c=h(i),d=h(o),l=w.GAS_CONSTANT_OVER_GRAVITY*((i.temperatureC+o.temperatureC)/2+273.15)*Math.log(i.pressureHpa/o.pressureHpa);return{pressureHpa:e,temperatureC:r(i.temperatureC,o.temperatureC),relativeHumidity:r(i.relativeHumidity,o.relativeHumidity),driftEastMs:r(c.east,d.east),driftNorthMs:r(c.north,d.north),shearPerS:Math.hypot(d.east-c.east,d.north-c.north)/l}}}}class H{static MIN_ALTITUDE_FT=2e4;static DENSE_MS=3e5;static SPARSE_STEP_MS=6e4;static SATURATION_HORIZON_MS=3*36e5;static SATURATION_STEP_MS=6e5;static PRESSURE_STEP_HPA=5;static plan(t,e,a,n){if(a.length===0)return[];const i=[],o=new Map,s=(r,h,c)=>{const d=Math.round(h/H.PRESSURE_STEP_HPA)*H.PRESSURE_STEP_HPA,l=H.SATURATION_STEP_MS,u=Math.floor(r/l)*l,p=`${d}|${u}`,m=o.get(p);if(m!==void 0)return m+(u-r)/1e3;let A=1/0;for(let b=l;b<=H.SATURATION_HORIZON_MS;b+=l){const R=w.at(a,u+b,d);if(!R)break;if(M.evaluate(R,c).iceRelativeHumidity<1){A=b/1e3;break}}return o.set(p,A),A+(u-r)/1e3};for(const r of t.objects){const h=t.flights.get(r.id),c=r.track;if(!h||!c||c.length!==h.points.length)continue;const d=e?.get(z.keyOf(h))??y.of(void 0),l=Y.efficiencyOf(d.kind);if(l===void 0)continue;const u=[];let p=-1/0;c.forEach((m,A)=>{if(m.t<-H.DENSE_MS&&A+1<c.length&&m.t-p<H.SPARSE_STEP_MS)return;p=m.t;const b=h.points[A],R=M.pressureAtAltitudeFt(b.altitudeFt),S=w.at(a,n+m.t,R),f=S&&M.evaluate(S,l);u.push({tMs:m.t,eastM:m.eastM,northM:m.northM,upM:m.altitudeM??0,forms:f?.forms??!1,persistent:f?.persistent??!1,lifetimeS:f?Z.lifetimeS(f.iceRelativeHumidity):0,driftEastMs:S?.driftEastMs??0,driftNorthMs:S?.driftNorthMs??0,shearPerS:S?.shearPerS??Z.TYPICAL_SHEAR_PER_S,iceRelativeHumidity:f?.iceRelativeHumidity??0,saturatedForS:f?.persistent?s(n+m.t,R,l):0})}),u.some((m,A)=>m.forms&&u[A+1]?.forms)&&i.push({id:r.id,spanM:d.spanM,points:u})}return i}}const ct=[500,400,300,250,200,150],yt=["temperature","relative_humidity","wind_speed","wind_direction"],kt="2022-01-01",lt=.25;class Pt{fetchImpl;baseUrl;cache=new Map;constructor(t={}){this.fetchImpl=t.fetchImpl??fetch.bind(globalThis),this.baseUrl=t.baseUrl??"https://historical-forecast-api.open-meteo.com/v1/forecast"}between(t,e,a){const n=new Date(e).toISOString().slice(0,10);if(n<kt)return Promise.resolve({status:"outside"});const i=new Date(a).toISOString().slice(0,10),s=`${`${Math.round(t.lat/lt)},${Math.round(t.lng/lt)}`}|${n}|${i}`;let r=this.cache.get(s);return r||(r=this.read(t,n,i),this.cache.set(s,r),r.then(h=>{h.status==="failed"&&this.cache.delete(s)})),r}async read(t,e,a){const n=ct.flatMap(s=>yt.map(r=>`${r}_${s}hPa`)),i=new URLSearchParams({latitude:String(t.lat),longitude:String(t.lng),start_date:e,end_date:a,hourly:n.join(","),wind_speed_unit:"ms",timezone:"UTC"}),o=`${this.baseUrl}?${i}`;try{const s=await this.fetchImpl(o);if(!s.ok)return{status:"failed"};const r=await s.json();return this.decode(r,{id:"open-meteo-forecast",name:"Open-Meteo (historical forecast)",url:o})}catch{return{status:"failed"}}}decode(t,e){const a=t.hourly;if(!a?.time)return{status:"outside"};const n=[];return a.time.forEach((i,o)=>{const s=[];for(const r of ct){const h=p=>a[`${p}_${r}hPa`]?.[o],c=h("temperature"),d=h("relative_humidity"),l=h("wind_speed"),u=h("wind_direction");if([c,d,l,u].some(p=>p==null))return;s.push({pressureHpa:r,temperatureC:c,relativeHumidity:d/100,windSpeedMs:l,windFromDeg:u})}n.push({t:Date.parse(`${i}Z`),levels:s})}),n.length>0?{status:"found",samples:n,source:e}:{status:"outside"}}}const Gt=[{id:"open-meteo-forecast",name:"Open-Meteo (historical forecast)",credit:"Weather data by Open-Meteo.com, CC BY 4.0",creditUrl:"https://open-meteo.com/en/docs/historical-forecast-api",create:()=>new Pt}];class I{static DEFAULT_KIND="airliner-narrow";static STEP_MS=1e3;static LEAD_MS=12e4;static FT_PER_M=1/.3048;static ID_PREFIX="decor:";tracks=new Map;static kindOf(t,e){if(t.kind!=="aircraft"||!I.flies(t))return;const a=t.aircraftSound??e??I.DEFAULT_KIND;return a==="silent"?void 0:a}static flies(t){return(t.track??[]).some(e=>(e.altitudeM??0)>0)}voices(t,e,a,n={},i=()=>{}){const o=t.event.place?.[0];if(!o)return[];const s=new Map,r=new Map,h=Math.max(t.observerTrack.duration,(t.event.durationSeconds??0)*1e3);for(const c of t.decor){const d=I.kindOf(c,i(c));if(!d)continue;const l=`${I.ID_PREFIX}${c.id}`;s.set(l,this.trackOf(c,l,o,h)),r.set(l,d)}for(const c of this.tracks.keys())s.has(c)||this.tracks.delete(c);return s.size===0?[]:O.voices(new Set(s.keys()),s,new Map,c=>String(c.icao),a,e,{...n,kindOf:c=>r.get(c)})}trackOf(t,e,a,n){const i=JSON.stringify([t.track,t.eastM,t.northM,t.headingDeg,a.lat,a.lng,n]),o=this.tracks.get(e);if(o?.key===i)return o.track;const s=[];for(let l=0;l<=n;l+=I.STEP_MS)s.push(I.pointAt(t,l,a));(s.length===0||s[s.length-1].t<n)&&s.push(I.pointAt(t,n,a));const r=s[0],h=s[1]??r,c=l=>(h[l]-r[l])/(h.t-r.t||1);for(let l=I.STEP_MS*10;l<=I.LEAD_MS;l+=I.STEP_MS*10)s.unshift({t:-l,lat:r.lat-c("lat")*l,lng:r.lng-c("lng")*l,altitudeFt:Math.max(0,r.altitudeFt-c("altitudeFt")*l)});const d={icao:I.hash(e),nonIcao:!0,points:s};return this.tracks.set(e,{key:i,track:d}),d}static pointAt(t,e,a){const n=pt(t,e),i=$(n.eastM,-n.northM,a.lat,a.lng);return{t:e,lat:i.lat,lng:i.lng,altitudeFt:n.altitudeM*I.FT_PER_M}}static hash(t){let e=0;for(let a=0;a<t.length;a++)e=e*31+t.charCodeAt(a)>>>0;return e}}class G{static STEP_MS=250;static FT_PER_M=1/.3048;static ID_PREFIX="body:";tracks=new Map;static noiseOf(t){const e=g.noiseOfKind(t.kind),a=t.quieterDb??0;return a===0?e:{...e,id:`${e.id}-${a}dB`,soundPowerA:e.soundPowerA.map(n=>n-a)}}voices(t,e,a,n,i,o,s={}){const r=new Map,h=new Map,c=new Map;for(const l of t){if(!l.sound)continue;const u=`${G.ID_PREFIX}${l.id}`,p=this.trackOf(l,u,e,n,o);p&&(r.set(u,p),h.set(u,l.sound.kind),c.set(u,G.noiseOf(l.sound)))}for(const l of this.tracks.keys())r.has(l)||this.tracks.delete(l);if(r.size===0)return[];const d=$(a.eastM,-a.northM,n.lat,n.lng);return O.voices(new Set(r.keys()),r,new Map,l=>String(l.icao),{lat:d.lat,lng:d.lng,heightM:a.upM},i,{...s,kindOf:l=>h.get(l),noiseOf:l=>c.get(l)})}trackOf(t,e,a,n,i){const o=`${n.lat},${n.lng},${i}`,s=this.tracks.get(e);if(s&&s.body===t&&s.origin===o)return s.track;const r=[];for(let c=-12e4;c<=i;c+=G.STEP_MS){const d=a(t,c);if(!d)continue;const l=$(d.eastM,-d.northM,n.lat,n.lng);r.push({t:c,lat:l.lat,lng:l.lng,altitudeFt:d.upM*G.FT_PER_M})}if(r.length<2)return;const h={icao:G.hash(e),nonIcao:!0,points:r};return this.tracks.set(e,{body:t,origin:o,track:h}),h}static hash(t){let e=0;for(let a=0;a<t.length;a++)e=e*31+t.charCodeAt(a)>>>0;return e}}export{q as AircraftAudio,H as AircraftContrails,y as AircraftModels,_ as AmbientNoise,G as BodyHearing,I as DecorAircraftHearing,D as TrafficDecor,Ft as TrafficInfos,O as TrafficSound,W as TrafficTooltip,Gt as UPPER_AIR_SOURCES};
@@ -0,0 +1 @@
1
+ import{Y as P,aj as V,al as H,a0 as W,aF as X,aG as D,aH as F}from"./rr0-sighting-editor.mjs";import{L as f}from"./LaserBeam-B6u7y0-E.js";class M{static MAX_SEGMENTS=12e3;static MIN_PIXELS=2;static VERTICES_PER_SEGMENT=24;static HALO_RAD=.002;static HALO_SHARE=.15;static BRIGHTEST_RELATIVE=6e4;group=new P;mesh;positions=new Float32Array(M.MAX_SEGMENTS*M.VERTICES_PER_SEGMENT*3);colors=new Float32Array(M.MAX_SEGMENTS*M.VERTICES_PER_SEGMENT*3);segments=0;drawn=[];cut;constructor(){const s=new V;s.setAttribute("position",new H(this.positions,3)),s.setAttribute("color",new H(this.colors,3)),s.setDrawRange(0,0),this.mesh=new W(s,new X({vertexColors:!0,transparent:!0,blending:F,depthWrite:!1,fog:!1,side:D})),this.mesh.frustumCulled=!1,this.mesh.renderOrder=8,this.mesh.name="beams",this.group.name="beams",this.group.add(this.mesh)}get segmentCount(){return this.segments}update(s,t,o){const c=this.viewsOf(s,t,o);let r=0;this.segments=0,this.drawn=[];for(const i of c){const l=f.colourOf(i.wavelengthNm),h=this.place(i,o);this.drawn.push({id:i.id,points:h});for(let u=0;u+1<h.length&&!(this.segments>=M.MAX_SEGMENTS);u++){const A=this.pushSegment(r,h[u],h[u+1],i.wavelengthNm,l,o);A>r&&this.segments++,r=A}}const n=this.mesh.geometry;n.setDrawRange(0,r);for(const i of["position","color"]){const l=n.getAttribute(i);l.clearUpdateRanges(),r>0&&l.addUpdateRange(0,r*3),l.needsUpdate=!0}}viewsOf(s,t,o){const{lat:c,lng:r}=o.reference;(!this.cut||this.cut.lat!==c||this.cut.lng!==r||this.cut.air!==o.air)&&(this.cut={lat:c,lng:r,air:o.air,views:new Map});const n=this.cut.views;return f.onAt(s,t).map(i=>{let l=n.get(i);return l||n.set(i,l={id:i.id,wavelengthNm:i.wavelengthNm,samples:f.samplesOf(i,o.reference,o.air)}),l})}place(s,t){const o=[];for(const c of s.samples){const r=t.origin.x+c.eastM*t.eastScale,n=t.origin.z-c.northM,i=c.upM-t.siteElevationM;if(o.length>0&&i<t.groundYAt(r,n))break;o.push({x:r,y:i,z:n,altitudeM:c.altitudeM,widthM:c.widthM,powerW:c.powerW})}return o}static SMOOTH_RATIO=1.08;static MAX_HALVINGS=6;pushSegment(s,t,o,c,r,n,i=0){const l={x:o.x-t.x,y:o.y-t.y,z:o.z-t.z},h=Math.hypot(l.x,l.y,l.z);if(h<1e-6)return s;const u={x:l.x/h,y:l.y/h,z:l.z/h};if(Math.hypot((t.x+o.x)/2-n.eye.x,(t.y+o.y)/2-n.eye.y,(t.z+o.z)/2-n.eye.z)<.001)return s;const A=e=>{const y={x:n.eye.x-e.x,y:n.eye.y-e.y,z:n.eye.z-e.z},z=Math.hypot(y.x,y.y,y.z)||.001,p=(u.x*y.x+u.y*y.y+u.z*y.z)/z,a=Math.sqrt(Math.max(0,1-p*p)),w=f.phasedScattering(f.coefficientsAt(n.air,c,e.altitudeM),p);return{sinTurn:a,scattered:w,through:n.transmittanceTo(e.x,e.y,e.z)}},T=e=>M.MIN_PIXELS*n.pixelRad*Math.hypot(e.x-n.eye.x,e.y-n.eye.y,e.z-n.eye.z),m=e=>Math.hypot(e.x-n.eye.x,e.y-n.eye.y,e.z-n.eye.z),R=e=>Math.max(e.widthM,M.HALO_RAD*m(e)),g=e=>{const y=R(e)/T(e),z=Math.min(1,Math.max(0,y-1));return M.HALO_SHARE*z*z*(3-2*z)},I=new Map([t,o].map(e=>[e,A(e)])),x=(e,y,z)=>{const p=Math.max(y,T(e)),{sinTurn:a,scattered:w,through:E}=I.get(e),O=2*z*f.luminanceCdM2(e.powerW,c,w,p,a),N=r.map((v,C)=>Math.min(M.BRIGHTEST_RELATIVE,O*n.relativeScale*v*E[C])),L={x:n.eye.x-e.x,y:n.eye.y-e.y,z:n.eye.z-e.z},_=this.normalise(this.cross(u,L));return{point:e,half:p/2,level:N,across:_}},d=[[x(t,t.widthM,1-g(t)),x(o,o.widthM,1-g(o))],[x(t,R(t),g(t)),x(o,R(o),g(o))]],[G,S]=d[0].map(e=>Math.max(...e.level));if(i<M.MAX_HALVINGS&&Math.max(G,S)>M.SMOOTH_RATIO*Math.max(Math.min(G,S),1e-12)){const e={x:(t.x+o.x)/2,y:(t.y+o.y)/2,z:(t.z+o.z)/2,altitudeM:(t.altitudeM+o.altitudeM)/2,widthM:(t.widthM+o.widthM)/2,powerW:Math.sqrt(t.powerW*o.powerW)};return this.pushSegment(this.pushSegment(s,t,e,c,r,n,i+1),e,o,c,r,n,i+1)}if((s+M.VERTICES_PER_SEGMENT)*3>this.positions.length)return s;for(const e of d){if(e.every(a=>Math.max(...a.level)<=0))continue;const y=(a,w)=>{const E=s*3;this.positions[E]=a.point.x+a.across.x*a.half*w,this.positions[E+1]=a.point.y+a.across.y*a.half*w,this.positions[E+2]=a.point.z+a.across.z*a.half*w;const O=w===0?1:0;this.colors[E]=a.level[0]*O,this.colors[E+1]=a.level[1]*O,this.colors[E+2]=a.level[2]*O,s++},[z,p]=e;for(const a of[-1,1])y(z,a),y(z,0),y(p,a),y(z,0),y(p,0),y(p,a)}return s}pickAt(s,t,o){let c;for(const r of this.drawn)for(let n=0;n+1<r.points.length;n++){const i=r.points[n],l=r.points[n+1],h={x:l.x-i.x,y:l.y-i.y,z:l.z-i.z},u={x:i.x-s.x,y:i.y-s.y,z:i.z-s.z},A=h.x*h.x+h.y*h.y+h.z*h.z,T=h.x*t.x+h.y*t.y+h.z*t.z,m=h.x*u.x+h.y*u.y+h.z*u.z,R=t.x*u.x+t.y*u.y+t.z*u.z,g=A-T*T,I=g>1e-9?Math.min(1,Math.max(0,(T*R-m)/g)):0,x={x:i.x+h.x*I,y:i.y+h.y*I,z:i.z+h.z*I},d=(x.x-s.x)*t.x+(x.y-s.y)*t.y+(x.z-s.z)*t.z;if(d<=0)continue;const G=Math.hypot(x.x-s.x-t.x*d,x.y-s.y-t.y*d,x.z-s.z-t.z*d),S=Math.atan2(G,d);S<=o&&(!c||S<c.angle)&&(c={id:r.id,distanceM:Math.hypot(x.x-s.x,x.y-s.y,x.z-s.z),angle:S})}return c&&{id:c.id,distanceM:c.distanceM}}cross(s,t){return{x:s.y*t.z-s.z*t.y,y:s.z*t.x-s.x*t.z,z:s.x*t.y-s.y*t.x}}normalise(s){const t=Math.hypot(s.x,s.y,s.z)||1;return{x:s.x/t,y:s.y/t,z:s.z/t}}dispose(){this.mesh.geometry.dispose(),this.mesh.material.dispose(),this.group.removeFromParent()}}export{M as BeamSystem};
@@ -0,0 +1 @@
1
+ class t{static FORMATION_S=.3;static VORTEX_SPREAD=2;static VORTEX_S=150;static RICHARDSON_M_PER_S15=800/3600**1.5;static DIFFUSING_S=600;static TYPICAL_SHEAR_PER_S=.003;static INITIAL_DEPTH_M=150;static DEEPENING_M_PER_S=.1;static YOUNG_OPTICAL_DEPTH=.5;static DILUTION_EXPONENT=.5;static SUBLIMATION_S=600;static INVISIBLE=.006;static MIN_LIFETIME_S=15;static LIFETIME_SPAN_S=120;static widthM(I,s,_=t.TYPICAL_SHEAR_PER_S){const E=Math.max(0,s),S=I*(1+(t.VORTEX_SPREAD-1)*(1-Math.exp(-((E/t.VORTEX_S)**2)))),c=_*(t.INITIAL_DEPTH_M*E+t.DEEPENING_M_PER_S*E*E/2),e=t.RICHARDSON_M_PER_S15*E**1.5;return Math.hypot(S+c,e)}static profileExponent(I){return 2+6*Math.exp(-((Math.max(0,I)/t.DIFFUSING_S)**2))}static lifetimeS(I){const s=Math.min(1,Math.max(0,I));return t.MIN_LIFETIME_S+t.LIFETIME_SPAN_S*s*s}static opacity(I,s){if(I<t.FORMATION_S)return 0;const _=s.spanM*t.VORTEX_SPREAD,E=e=>Math.max(_,t.widthM(s.spanM,e,s.shearPerS));let S;if(s.persistent){const e=Math.min(I,s.saturatedForS??1/0);S=t.YOUNG_OPTICAL_DEPTH*(_/E(e))**t.DILUTION_EXPONENT;const M=I-e;M>0&&(S*=E(e)/E(I)*Math.max(0,1-M/t.SUBLIMATION_S)**2)}else{if(I>=s.lifetimeS)return 0;S=t.YOUNG_OPTICAL_DEPTH*(_/E(I))*(1-I/s.lifetimeS)**2}const c=1-Math.exp(-S);return c<t.INVISIBLE?0:c}}export{t as C};
@@ -0,0 +1 @@
1
+ import{Y as F,aj as m,al as I,a0 as X,aF as D,aG as L}from"./rr0-sighting-editor.mjs";import{C as x}from"./ContrailGrowth-DwHS8If9.js";class h{static MAX_SEGMENTS=8e3;static MIN_PIXELS=2;static UNRESOLVED_CONTRAST=.5;static STATIONS=[0,.5,1,1.5,2.25];static VERTICES_PER_SEGMENT=12*(h.STATIONS.length-1);static ICE_ASYMMETRY=.75;static phase(t){const s=h.ICE_ASYMMETRY;return(1-s*s)/(1+s*s-2*s*t)**1.5}object;group=new F;trails=[];positions=new Float32Array(h.MAX_SEGMENTS*h.VERTICES_PER_SEGMENT*3);colors=new Float32Array(h.MAX_SEGMENTS*h.VERTICES_PER_SEGMENT*4);segments=0;constructor(){const t=new m;t.setAttribute("position",new I(this.positions,3)),t.setAttribute("color",new I(this.colors,4)),t.setDrawRange(0,0),this.object=new X(t,new D({vertexColors:!0,transparent:!0,depthWrite:!1,side:L})),this.object.frustumCulled=!1,this.object.name="contrails",this.group.name="contrails",this.group.add(this.object)}get segmentCount(){return this.segments}set(t){this.trails=t}update(t,s,c){let p=0;this.segments=0;for(const o of this.trails){const u=this.samplesOf(o,t,s),i=c(o.id);for(let a=0;a+1<u.length;a++){const n=u[a],M=u[a+1];if(!n.forms||!M.forms)continue;if(this.segments>=h.MAX_SEGMENTS)break;const d=this.pushSegment(p,n,M,o.spanM,i,s);d>p&&this.segments++,p=d}}const y=this.object.geometry;y.setDrawRange(0,p);for(const[o,u]of[["position",3],["color",4]]){const i=y.getAttribute(o);i.clearUpdateRanges(),p>0&&i.addUpdateRange(0,p*u),i.needsUpdate=!0}}samplesOf(t,s,c){const p=s-x.FORMATION_S*1e3,y=[],o=(n,M,d,T,l,N)=>({x:M+n.driftEastMs*l+c.origin.x,y:T,z:-(d+n.driftNorthMs*l)+c.origin.z,age:l,forms:N,point:n});let u=-1;t.points.forEach((n,M)=>{n.tMs>p||(u=M,y.push(o(n,n.eastM,n.northM,n.upM,(s-n.tMs)/1e3,n.forms)))});const i=t.points[u],a=t.points[u+1];if(i&&a){const n=(p-i.tMs)/(a.tMs-i.tMs),M=(d,T)=>d+(T-d)*n;y.push(o(i,M(i.eastM,a.eastM),M(i.northM,a.northM),M(i.upM,a.upM),x.FORMATION_S,i.forms&&a.forms))}return y}pushSegment(t,s,c,p,y,o){const u=e=>({persistent:e.point.persistent,lifetimeS:e.point.lifetimeS,spanM:p,shearPerS:e.point.shearPerS,saturatedForS:e.point.saturatedForS}),i=x.opacity(s.age,u(s)),a=x.opacity(c.age,u(c));if(i<=0&&a<=0)return t;const n={x:c.x-s.x,y:c.y-s.y,z:c.z-s.z};if(Math.hypot(n.x,n.y,n.z)<1e-6)return t;const M={x:(s.x+c.x)/2-o.eye.x,y:(s.y+c.y)/2-o.eye.y,z:(s.z+c.z)/2-o.eye.z},d=this.normalise(this.cross(n,M)),T=e=>{if(!o.sun)return y;const r={x:e.x-o.eye.x,y:e.y-o.eye.y,z:e.z-o.eye.z},E=h.phase((o.sun.x*r.x+o.sun.y*r.y+o.sun.z*r.z)/(Math.hypot(r.x,r.y,r.z)||1));return[y[0]*E,y[1]*E,y[2]*E]},l=(e,r)=>{const E=Math.hypot(e.x-o.eye.x,e.y-o.eye.y,e.z-o.eye.z),A=x.widthM(p,e.age,e.point.shearPerS),g=Math.max(A,h.MIN_PIXELS*o.pixelRad*E),z=r*(A/g)**h.UNRESOLVED_CONTRAST,S=x.profileExponent(e.age),G=h.STATIONS.map((j,P)=>P===h.STATIONS.length-1?0:z*Math.exp(-Math.LN2*j**S));return{half:g/2,profile:G,scattered:T(e)}},N=[{sample:s,...l(s,i)},{sample:c,...l(c,a)}],w=h.STATIONS.length-1,f=(e,r,E)=>{const A=h.STATIONS[r],g=e.half*A*E,z=t*3;this.positions[z]=e.sample.x+d.x*g,this.positions[z+1]=e.sample.y+d.y*g,this.positions[z+2]=e.sample.z+d.z*g;const S=t*4;this.colors[S]=e.scattered[0],this.colors[S+1]=e.scattered[1],this.colors[S+2]=e.scattered[2],this.colors[S+3]=e.profile[r],t++},[R,O]=N;for(const e of[-1,1])for(let r=0;r<w;r++)f(R,r+1,e),f(R,r,e),f(O,r+1,e),f(R,r,e),f(O,r,e),f(O,r+1,e);return t}cross(t,s){return{x:t.y*s.z-t.z*s.y,y:t.z*s.x-t.x*s.z,z:t.x*s.y-t.y*s.x}}normalise(t){const s=Math.hypot(t.x,t.y,t.z)||1;return{x:t.x/s,y:t.y/s,z:t.z/s}}dispose(){this.object.geometry.dispose(),this.object.material.dispose(),this.group.removeFromParent()}}export{h as ContrailSystem};
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./SightingFileEditor-DcOCyimm.js","./index-DSNA74v6.js","./sightingSchema-CNCtrneh.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./SightingFileEditor-CJX1-U4q.js","./index-DSNA74v6.js","./sightingSchema-Dn9Nacua.js"])))=>i.map(i=>d[i]);
2
2
  import{S as L,a as T,H as j,s as P,U as H,l as I,I as W,P as z,b as y,W as O,r as J,c as D,E as C,A as U,d as E,M as q,_ as A,e as V}from"./rr0-sighting-editor.mjs";import{interviewMessages_en as S}from"./InterviewMessages_en-CRvWVmlL.js";import{InterviewMedia as N}from"./InterviewMedia-CvAxC62V.js";import{InterviewFile as g}from"./interviewJson-DXD8FSBX.js";const F="rr0-interview",G=`
3
3
  rr0-interview { display: block; --rr0-interview-line: color-mix(in srgb, currentColor 35%, transparent); }
4
4
  rr0-interview [hidden] { display: none !important; }
@@ -226,4 +226,4 @@ rr0-interview .turn .ref svg { width: 1.1em; height: 1.1em; }
226
226
  </section>
227
227
  </div>
228
228
  `;class m extends HTMLElement{shadow=this.attachShadow({mode:"open"});get importInput(){return this.shadow.getElementById("import")}messages=S;said=new L(["en"]);language="en";localeToken=0;current;options=[];group="summary";base;text;fileEditor;fileEditorLoading;shownFor;player;drawn;takenOut=new Map;constructor(){super(),this.shadow.innerHTML=K,customElements.upgrade(this.shadow),this.importInput.build();for(const e of["title","time","by","url","summary","witness","medium"])this.shadow.getElementById(e).addEventListener(e==="witness"||e==="medium"?"change":"input",()=>this.write(e));for(const e of this.shadow.querySelectorAll("#tabs .group-tab"))e.addEventListener("click",()=>this.openGroup(m.GROUPS[e.getAttribute("aria-controls")]??"summary"));this.shadow.getElementById("url").addEventListener("change",()=>{this.shownFor=void 0,this.text===void 0&&this.group==="file"&&this.showTranscript()}),this.importInput.addEventListener("filepick",e=>{const t=e.detail;t.file?this.importFromFile(t.file):this.importFromUrl(t.url)}),this.shadow.getElementById("save").addEventListener("click",()=>this.save()),this.player=new B(this.shadow.getElementById("player"),{timeChanged:e=>this.markCurrentTurn(e),recordingsChanged:e=>void this.editTranscript(t=>{e.length===0?delete t.recordings:t.recordings=e})},this.messages),this.shadow.getElementById("redact-selection").addEventListener("click",()=>void this.redactSelection()),this.shadow.getElementById("exchanges").addEventListener("click",e=>{const t=e.target.closest(".bar[data-restorable]");t&&this.restore(t.dataset.restorable)})}static GROUPS={"group-summary":"summary","group-exchanges":"exchanges","group-transcript":"file"};static get observedAttributes(){return["readonly"]}attributeChangedCallback(){this.applyReadonly()}connectedCallback(){this.loadLocale()}disconnectedCallback(){queueMicrotask(()=>{this.isConnected||this.releaseFileEditor()})}releaseFileEditor(){this.fileEditor&&(this.text=this.fileEditor.text,this.fileEditor.destroy(),this.fileEditor=void 0,this.fileEditorLoading=void 0,this.shownFor=void 0)}get event(){return this.current}set event(e){e!==this.current&&(this.shownFor=void 0),this.current=e,this.render(),this.takenOut.clear(),this.group==="file"&&this.showTranscript(),queueMicrotask(()=>{this.current===e&&this.showExchanges()})}set witnesses(e){this.options=e,this.render()}set documentUrl(e){this.base=e}get transcriptText(){return this.text}set transcriptText(e){this.text=e,this.shownFor=void 0}field(e){return this.shadow.getElementById(e)}async loadLocale(){const e=++this.localeToken,t=j.preferencesFor(this),r=P(t,H),s=r==="en"?S:await I(r);e===this.localeToken&&(this.language=r,this.said=new L(t),this.messages=s,this.render())}applyReadonly(){const e=this.hasAttribute("readonly");for(const t of["title","time","by","url","summary"])this.field(t).readOnly=e;for(const t of this.shadow.querySelectorAll("#witness input"))t.disabled=e;this.field("medium").disabled=e,this.importInput.disabled=e}static byText(e){return(e??[]).map(t=>"people"in t?t.people:"org"in t?t.org:t.title??"").filter(t=>t!=="").join(", ")}render(){const e=this.messages,t=(o,d)=>{this.shadow.getElementById(o).textContent=d};t("label-summary-group",e.summaryTab),t("label-file-group",e.fileTab),t("label-exchanges-group",e.exchangesTab),t("label-title",e.title),t("label-witness",e.witness),t("label-medium",e.medium),t("label-time",e.time),t("label-by",e.by),t("label-summary",e.summary),t("label-url",e.transcript),t("label-import-file",e.importFile),t("redact-selection",e.redactSelection),this.player?.setMessages(e),this.importInput.messages=e,t("save",e.saveButton),this.field("url").title=e.transcriptHint,this.field("by").title=e.byHint;const r=this.current,s=this.shadow.activeElement,i=(o,d)=>{this.field(o)!==s&&(this.field(o).value=d)};i("title",this.said.read(r?.title)??""),i("time",r?.time??""),i("by",m.byText(r?.by)),i("url",r?.url??""),i("summary",this.said.read(r?.summary)??"");const a=[r?.interviewOf].flat().filter(o=>typeof o=="string"),n=this.shadow.getElementById("witness"),l=[...this.options,...a.filter(o=>!this.options.some(d=>d.id===o)).map(o=>({id:o,label:o}))];n.replaceChildren(n.querySelector("legend"),...l.map(o=>{const d=document.createElement("label"),h=document.createElement("input");return h.type="checkbox",h.value=o.id,h.checked=a.includes(o.id),d.append(h,o.label),d}));const c=this.shadow.getElementById("medium");if(c.replaceChildren(...N.ALL.map(o=>new Option(N.label(o,this.language),o))),r?.medium!==void 0&&!N.ALL.includes(r.medium)&&c.append(new Option(String(r.medium),String(r.medium))),r?.medium===void 0){const o=new Option(e.witnessNone,"");o.disabled=!0,o.hidden=!0,c.prepend(o)}c.value=r?.medium??"",this.applyReadonly()}write(e){const t=this.current;if(!t)return;const r=e==="witness"?"":this.field(e).value;switch(e){case"title":{const s=this.said.write(t.title,r,this.language);s===void 0?delete t.title:t.title=s;break}case"summary":{const s=this.said.write(t.summary,r,this.language);s===void 0?delete t.summary:t.summary=s;break}case"time":r.trim()===""?delete t.time:t.time=r.trim();break;case"url":r.trim()===""?delete t.url:t.url=r.trim();break;case"by":{const s=r.split(",").map(i=>i.trim()).filter(i=>i!=="");s.length===0?delete t.by:t.by=s.map(i=>({people:i}));break}case"witness":{const s=[...this.shadow.querySelectorAll("#witness input:checked")].map(i=>i.value);if(s.length===0){this.render();return}t.interviewOf=s.length===1?s[0]:s;break}case"medium":r===""?delete t.medium:t.medium=r;break}this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:e,event:t}})),e==="medium"&&this.render(),e!=="url"&&this.syncFileFromEvent()}openGroup(e){this.group=e;const t={summary:"group-summary",exchanges:"group-exchanges",file:"group-transcript"};for(const r of this.shadow.querySelectorAll("#tabs .group-tab"))r.setAttribute("aria-expanded",String(r.getAttribute("aria-controls")===t[e]));for(const[r,s]of Object.entries(t))this.shadow.getElementById(s).hidden=r!==e;e==="file"&&this.showTranscript(),e==="exchanges"&&this.showExchanges()}exchangesToken=0;async transcriptJson(e){try{const t=this.fileEditor?.text??this.text;if(t!==void 0)return g.read(JSON.parse(t));const r=this.transcriptUrl(e);if(r!==void 0)return g.read(await T.json(r))}catch{}}async showExchanges(){const e=this.current;if(!e)return;const t=++this.exchangesToken,r=await this.transcriptJson(e);t!==this.exchangesToken||this.current!==e||this.drawExchanges(r)}drawExchanges(e){const t=this.shadow.getElementById("exchanges");t.documentUrl=this.current?this.transcriptUrl(this.current):void 0,t.interview=e,this.drawn=e,this.player.show(e,this.current?this.transcriptUrl(this.current):void 0),this.renderReview(e),this.decorateExchanges(e)}async editTranscript(e){const t=this.current;if(!t||this.hasAttribute("readonly"))return;const r=await this.transcriptJson(t)??JSON.parse(this.template(t));Array.isArray(r.segments)||(r.segments=[]),e(r);const s=m.pretty(m.withEventFields(r,t));this.fileEditor?.replaceText(s,!1),this.onEdited(s)}get redactReason(){return this.said.write(void 0,this.messages.redactReason,this.language)??this.messages.redactReason}speakerLabel(e,t){const r=g.speakerName(e,t),s=e.speakers?.[t]?.role;return r===s&&s in this.messages.roles?this.messages.roles[s]:r}renderReview(e){const t=this.shadow.getElementById("review"),r=Object.keys(e?.speakers??{});if(t.hidden=!e||r.length===0,!e||r.length===0)return;const s=l=>["approved","covered"].includes(e.proofreading?.[l]?.status??""),i=r.filter(l=>!s(l)),a=document.createElement("span");a.textContent=i.length===0?this.messages.proofreadAll:this.messages.proofreadPending.replace("{names}",i.map(l=>this.speakerLabel(e,l)).join(", ")),t.toggleAttribute("data-done",i.length===0);const n=document.createElement("span");n.className="review-boxes";for(const l of r){const c=document.createElement("label"),o=document.createElement("input");o.type="checkbox",o.checked=s(l),o.disabled=this.hasAttribute("readonly"),o.addEventListener("change",()=>void this.editTranscript(d=>{d.proofreading??={},o.checked?d.proofreading[l]={status:"approved",date:new Date().toISOString().slice(0,10)}:d.proofreading[l]={status:"pending"}})),c.append(o,this.messages.reviewedBy.replace("{name}",this.speakerLabel(e,l))),n.append(c)}t.replaceChildren(a,n)}momentsOf(e,t){const r=e.speakers?.[t]?.of??g.sightingsOf(e)[0];return this.options.find(s=>s.id===r)?.moments??this.options.flatMap(s=>s.moments??[])}decorateExchanges(e){if(!e)return;const t=this.messages,r=this.hasAttribute("readonly");for(const s of this.shadow.querySelectorAll("#exchanges .turn[data-segment]")){const i=Number(s.dataset.segment),a=e.segments[i];if(!a)continue;if(a.start!==void 0){const c=document.createElement("button");c.type="button",c.className="turn-time",c.textContent=g.clock(a.start),c.addEventListener("click",()=>this.player.seek(a.start)),s.prepend(c)}const n=a.redacted?[a.redacted]:f.partsOf(a).flatMap(c=>"redacted"in c?[c.redacted]:[]);s.querySelectorAll(".bar").forEach((c,o)=>{const d=n[o]?.words;c.textContent=d!==void 0?t.redactedWords.replace("{n}",String(d)):t.redacted,c.classList.add("redacted-label");const h=a.redacted?`${i}:all`:`${i}:${o}`;this.takenOut.has(h)&&(c.dataset.restorable=h)});const l=this.momentsOf(e,a.speaker);s.querySelectorAll(".ref").forEach((c,o)=>{const d=a.refs?.[o];if(!d)return;const h=l.find(p=>Math.abs(p.at-d.at)<.05);if(h){const p=document.createElement("span");p.className="moment-letter",p.textContent=h.label.slice(0,2),c.replaceChildren(p,t.momentLabel.replace("{k}",h.label).replace("{text}",h.note??g.clock(d.at)))}if(r)return;const u=document.createElement("button");u.type="button",u.className="ref-unlink",u.textContent="×",u.title=t.unlinkMoment,u.setAttribute("aria-label",t.unlinkMoment),u.addEventListener("click",()=>void this.editTranscript(p=>{p.segments[i].refs?.splice(o,1),p.segments[i].refs?.length===0&&delete p.segments[i].refs})),c.after(u)}),r||s.append(this.turnTools(e,i,a,l))}}turnTools(e,t,r,s){const i=this.messages,a=document.createElement("div");a.className="turn-tools";const n=(o,d)=>{const h=document.createElement("button");return h.type="button",h.textContent=o,h.addEventListener("click",d),h};if(e.speakers?.[r.speaker]?.role==="witness"&&!r.redacted){const o=n(i.linkMoment,()=>{});a.append(o),new U(o,`moment-menu-${t}`,h=>void this.editTranscript(u=>{const p=u.segments[t].refs??[];p.some(b=>Math.abs(b.at-Number(h))<.05)||p.push({at:Number(h)}),u.segments[t].refs=p}),h=>`moment-${t}-${h}`).setKinds(s.length>0?s.map(h=>({value:String(h.at),label:i.momentLabel.replace("{k}",h.label).replace("{text}",h.note??g.clock(h.at))})):[{value:"",label:i.noMoments,disabledReason:i.noMoments}])}r.redacted||a.append(n(i.editTurn,()=>this.editTurn(t)));const l=this.takenOut.has(`${t}:all`)||[...this.takenOut.keys()].some(o=>o.startsWith(`${t}:`));if(r.redacted||r.parts&&l){const o=n(i.restoreAll,()=>void this.restoreAll(t));o.disabled=!l,a.append(o)}else a.append(n(i.redactAll,()=>void this.editTranscript(o=>{this.forget(t),this.takenOut.set(`${t}:all`,f.redactWhole(o.segments[t],this.redactReason))})));const c=n("",()=>void this.editTranscript(o=>{o.segments.splice(t,1),this.shiftTakenOut(t)}));return c.className="icon-btn",c.innerHTML=y.TRASH,c.title=i.deleteTurn,c.setAttribute("aria-label",i.deleteTurn),a.append(c),a}async redactSelection(){const e=this.messages,t=this.shadow.getElementById("redact-note"),r=(h,u=!1)=>{t.textContent=h,t.classList.toggle("error",u)},s=this.shadow.getSelection?.()??document.getSelection(),i=s&&s.rangeCount>0?s.getRangeAt(0):void 0;if(!i||i.collapsed){r(e.selectionNone);return}const a=h=>h instanceof Element?h:h.parentElement,n=a(i.startContainer)?.closest(".said");if(!n||a(i.endContainer)?.closest(".said")!==n||a(i.startContainer)?.closest(".bar")||a(i.endContainer)?.closest(".bar")){r(e.selectionOnePassage,!0);return}const l=Number(n.closest(".turn").dataset.segment),c=m.offsetIn(n,i.startContainer,i.startOffset),o=m.offsetIn(n,i.endContainer,i.endOffset);let d=!1;await this.editTranscript(h=>{const u=f.redact(h.segments[l],Math.min(c,o),Math.max(c,o),this.redactReason);u&&(d=!0,this.shiftParts(l,u.nth),this.takenOut.set(`${l}:${u.nth}`,u.text))}),s?.removeAllRanges(),r(d?"":e.selectionOnePassage,!d)}static offsetIn(e,t,r){let s=0;const i=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode:a=>a.parentElement?.closest(".bar")?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT});for(let a=i.nextNode();a;a=i.nextNode()){if(a===t)return s+r;if(t.contains(a)&&t!==a){const l=[...t.childNodes].slice(0,r);return s+l.reduce((c,o)=>c+(o instanceof Element&&o.closest(".bar")?0:(o.textContent??"").length),0)}s+=(a.textContent??"").length}return s}async restore(e){const t=this.takenOut.get(e);if(t===void 0)return;const[r,s]=e.split(":"),i=Number(r);await this.editTranscript(a=>{s==="all"?f.restoreWhole(a.segments[i],t):(f.restorePart(a.segments[i],Number(s),t),this.shiftParts(i,Number(s),-1)),this.takenOut.delete(e)})}async restoreAll(e){if(this.takenOut.has(`${e}:all`)){await this.restore(`${e}:all`);return}const t=[...this.takenOut.keys()].filter(r=>r.startsWith(`${e}:`)).map(r=>Number(r.split(":")[1])).sort((r,s)=>s-r);for(const r of t)await this.restore(`${e}:${r}`)}editTurn(e){const r=this.drawn?.segments[e],s=this.shadow.querySelector(`#exchanges .turn[data-segment="${e}"] .said`);if(!r||!s)return;const i=this.messages,a=document.createElement("div");a.className="turn-edit";const n=document.createElement("textarea");n.value=f.raw(r,h=>this.takenOut.get(`${e}:${h}`));const l=document.createElement("span");l.className="hint",l.textContent=i.turnEditHint;const c=document.createElement("div");c.className="buttons";const o=document.createElement("button");o.type="button",o.className="pill-btn primary",o.textContent=i.saveTurn,o.addEventListener("click",()=>void this.editTranscript(h=>{const u=f.fromRaw(h.segments[e],n.value,this.redactReason);this.forget(e);for(const[p,b]of u)this.takenOut.set(`${e}:${p}`,b)}));const d=document.createElement("button");d.type="button",d.className="pill-btn",d.textContent=i.cancelTurn,d.addEventListener("click",()=>this.drawExchanges(this.drawn)),c.append(o,d),a.append(n,l,c),s.replaceWith(a),n.focus()}forget(e){for(const t of[...this.takenOut.keys()])t.startsWith(`${e}:`)&&this.takenOut.delete(t)}shiftParts(e,t,r=1){const s=[...this.takenOut].filter(([i])=>i.startsWith(`${e}:`)&&i!==`${e}:all`&&Number(i.split(":")[1])>=t+(r<0?1:0));for(const[i]of s)this.takenOut.delete(i);for(const[i,a]of s)this.takenOut.set(`${e}:${Number(i.split(":")[1])+r}`,a)}shiftTakenOut(e){const t=[...this.takenOut];this.takenOut.clear();for(const[r,s]of t){const[i,a]=r.split(":"),n=Number(i);n<e?this.takenOut.set(r,s):n>e&&this.takenOut.set(`${n-1}:${a}`,s)}}markCurrentTurn(e){const t=this.drawn?.segments??[];let r=-1;t.forEach((a,n)=>{a.start!==void 0&&a.start<=e&&(r=n)});for(const a of this.shadow.querySelectorAll("#exchanges .turn.current"))a.classList.remove("current");const s=this.shadow.querySelector(`#exchanges .turn[data-segment="${r}"]`);if(!s)return;s.classList.add("current");const i=s.closest(".rr0-interview-turns");i&&(s.offsetTop<i.scrollTop||s.offsetTop>i.scrollTop+i.clientHeight-s.offsetHeight)&&i.scrollTo({top:s.offsetTop-i.clientHeight/3,behavior:"smooth"})}async revealSegment(e){this.openGroup("exchanges"),await this.showExchanges(),this.shadow.getElementById("exchanges").revealSegment(e)}note(e,t="info"){q.show(this.shadow.getElementById("file-note"),e,t)}transcriptUrl(e){return e.url?new URL(e.url,this.base??location.href).href:void 0}static pretty(e){return JSON.stringify(e,null,2)+`
229
- `}static SHARED=["title","time","medium","by","interviewOf","summary"];static ORDER=["version","id","language","title","time","medium","by","interviewOf","summary","speakers","transcription","proofreading","anonymization","recordings","segments"];static ordered(e){const t={};for(const r of m.ORDER)r in e&&(t[r]=e[r]);for(const r of Object.keys(e))r in t||(t[r]=e[r]);return t}static witnessesOf(e){return[e].flat().filter(t=>typeof t=="string")}static withEventFields(e,t){const r={...e};for(const i of m.SHARED)t[i]===void 0?delete r[i]:r[i]=structuredClone(t[i]);const s=r.speakers;if(typeof s=="object"&&s!==null){const i={...s},a=(n,l,c)=>{c.forEach((o,d)=>{const h=d===0?n:`${n}${d+1}`;i[h]?i[h]={...i[h],[l]:o}:d>0&&(i[h]={role:n,[l]:o})})};a("interviewer","by",t.by??[]),a("witness","of",m.witnessesOf(t.interviewOf)),r.speakers=i}return m.ordered(r)}applyFileToEvent(e,t){const r=this.current;if(!r)return!1;const s=JSON.stringify(m.SHARED.map(n=>r[n])),i=n=>typeof n=="string"||typeof n=="object"&&n!==null&&!Array.isArray(n),a={title:i,summary:i,time:n=>typeof n=="string",medium:n=>typeof n=="string",by:n=>Array.isArray(n),interviewOf:n=>typeof n=="string"||Array.isArray(n)};for(const n of m.SHARED){const l=e[n];l!==void 0&&a[n](l)?r[n]=structuredClone(l):t&&delete r[n]}return JSON.stringify(m.SHARED.map(n=>r[n]))!==s}syncFileFromEvent(){const e=this.current,t=this.fileEditor?.text??this.text;if(!e||t===void 0)return;let r;try{r=JSON.parse(t)}catch{return}if(typeof r!="object"||r===null||Array.isArray(r))return;const s=m.pretty(m.withEventFields(r,e));s!==t&&(this.text=s,this.fileEditor?.replaceText(s,!1),this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"transcript",event:e,text:s}})))}template(e){return m.pretty(m.withEventFields({version:1,language:this.language,speakers:{interviewer:{role:"interviewer"},witness:{role:"witness"}},segments:[]},e))}loadFileEditor(e){return this.fileEditorLoading??=(async()=>{try{const[{SightingFileEditor:t},{default:r},s]=await Promise.all([A(()=>import("./SightingFileEditor-DcOCyimm.js"),__vite__mapDeps([0,1,2]),import.meta.url),A(()=>import("./interviewSchema-D82l9P1r.js"),[],import.meta.url),V(this.language)]);return this.fileEditor=new t(this.shadow.getElementById("file-editor"),e,i=>this.onEdited(i),this.messages.editorLabel,{baseline:e,phrases:s.fileSearchPhrases,schema:r}),this.fileEditor}catch(t){throw this.fileEditorLoading=void 0,t}})(),this.fileEditorLoading}async showTranscript(){const e=this.current;if(!e||this.shownFor===e)return;this.note(this.messages.loading);let t=this.text;if(t===void 0){const r=this.transcriptUrl(e);if(r!==void 0)try{const s=await T.json(r);this.current===e&&typeof s=="object"&&s!==null&&!Array.isArray(s)?(this.applyFileToEvent(s,!1)&&this.fileChangedEvent(),t=m.pretty(m.withEventFields(s,e))):t=m.pretty(s)}catch{this.note(this.messages.loadFailed,"error")}t??=this.template(e)}if(this.current===e)try{const r=await this.loadFileEditor(t);if(this.current!==e)return;r.replaceText(t,!1),this.shownFor=e,this.text!==void 0||this.transcriptUrl(e)===void 0?this.note(void 0):this.shadow.getElementById("file-note").textContent===this.messages.loading&&this.note(void 0)}catch{this.note(this.messages.loadFailed,"error")}}onEdited(e){this.text=e;let t,r=!0;try{t=JSON.parse(e)}catch{r=!1}this.note(r?void 0:this.messages.invalid,"error");const s=this.current;if(r&&s&&typeof t=="object"&&t!==null&&!Array.isArray(t)&&this.applyFileToEvent(t,!0)&&this.fileChangedEvent(),s&&!s.url&&e.trim()!==""){const i=s.id??"1";s.url=`${i.startsWith("interview")?i:`interview-${i}`}.json`,this.field("url").value=s.url,this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"url",event:s}}))}this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"transcript",event:s,text:e}})),this.showExchanges()}fileChangedEvent(){this.render(),this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"file",event:this.current}}))}async take(e){if(typeof e!="object"||e===null||!Array.isArray(e.segments)){this.note(this.messages.notATranscript,"error");return}const t=this.current;t&&this.applyFileToEvent(e,!1)&&this.fileChangedEvent();const r=m.pretty(t?m.withEventFields(e,t):e);await this.loadFileEditor(r).then(s=>s.replaceText(r,!1),()=>{}),this.shownFor=this.current,this.note(void 0),this.onEdited(r)}async importFromFile(e){try{const t=await e.text();let r;try{r=JSON.parse(t)}catch{r=void 0}if(typeof r!="object"||r===null){const{InterviewImport:s}=await A(async()=>{const{InterviewImport:l}=await import("./interviewImport-C-TiARLK.js");return{InterviewImport:l}},[],import.meta.url),i=this.current,a=[i?.interviewOf].flat().filter(l=>typeof l=="string"),n=s.parse(t,{by:(i?.by??[]).map(l=>m.byText([l])).filter(l=>l!==""),witnesses:this.options.filter(l=>a.length===0||a.includes(l.id))});r=n&&{...n,transcription:{note:`Imported from ${e.name}: who speaks and when are as the file gave them, to be checked.`}}}r===void 0?this.note(this.messages.notATranscript,"error"):await this.take(r)}catch{this.note(this.messages.loadFailed,"error")}}async importFromUrl(e){try{await this.take(await T.json(new URL(e,this.base??location.href).href))}catch{this.note(this.messages.loadFailed,"error")}}save(){const e=this.fileEditor?.text??this.text;if(e===void 0)return;const t=(this.current?.url??"").split("/").pop()||`${this.current?.id??"interview"}.json`,r=URL.createObjectURL(new Blob([e],{type:"application/json"})),s=document.createElement("a");s.href=r,s.download=t,s.click(),URL.revokeObjectURL(r)}}function te(){Y(),J(),customElements.get($)||customElements.define($,m)}export{E as INTERVIEW_CHANGE_EVENT,$ as INTERVIEW_EDITOR_ELEMENT_NAME,W as INTERVIEW_SEEK_EVENT,m as InterviewEditorElement,te as register};
229
+ `}static SHARED=["title","time","medium","by","interviewOf","summary"];static ORDER=["version","id","language","title","time","medium","by","interviewOf","summary","speakers","transcription","proofreading","anonymization","recordings","segments"];static ordered(e){const t={};for(const r of m.ORDER)r in e&&(t[r]=e[r]);for(const r of Object.keys(e))r in t||(t[r]=e[r]);return t}static witnessesOf(e){return[e].flat().filter(t=>typeof t=="string")}static withEventFields(e,t){const r={...e};for(const i of m.SHARED)t[i]===void 0?delete r[i]:r[i]=structuredClone(t[i]);const s=r.speakers;if(typeof s=="object"&&s!==null){const i={...s},a=(n,l,c)=>{c.forEach((o,d)=>{const h=d===0?n:`${n}${d+1}`;i[h]?i[h]={...i[h],[l]:o}:d>0&&(i[h]={role:n,[l]:o})})};a("interviewer","by",t.by??[]),a("witness","of",m.witnessesOf(t.interviewOf)),r.speakers=i}return m.ordered(r)}applyFileToEvent(e,t){const r=this.current;if(!r)return!1;const s=JSON.stringify(m.SHARED.map(n=>r[n])),i=n=>typeof n=="string"||typeof n=="object"&&n!==null&&!Array.isArray(n),a={title:i,summary:i,time:n=>typeof n=="string",medium:n=>typeof n=="string",by:n=>Array.isArray(n),interviewOf:n=>typeof n=="string"||Array.isArray(n)};for(const n of m.SHARED){const l=e[n];l!==void 0&&a[n](l)?r[n]=structuredClone(l):t&&delete r[n]}return JSON.stringify(m.SHARED.map(n=>r[n]))!==s}syncFileFromEvent(){const e=this.current,t=this.fileEditor?.text??this.text;if(!e||t===void 0)return;let r;try{r=JSON.parse(t)}catch{return}if(typeof r!="object"||r===null||Array.isArray(r))return;const s=m.pretty(m.withEventFields(r,e));s!==t&&(this.text=s,this.fileEditor?.replaceText(s,!1),this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"transcript",event:e,text:s}})))}template(e){return m.pretty(m.withEventFields({version:1,language:this.language,speakers:{interviewer:{role:"interviewer"},witness:{role:"witness"}},segments:[]},e))}loadFileEditor(e){return this.fileEditorLoading??=(async()=>{try{const[{SightingFileEditor:t},{default:r},s]=await Promise.all([A(()=>import("./SightingFileEditor-CJX1-U4q.js"),__vite__mapDeps([0,1,2]),import.meta.url),A(()=>import("./interviewSchema-D82l9P1r.js"),[],import.meta.url),V(this.language)]);return this.fileEditor=new t(this.shadow.getElementById("file-editor"),e,i=>this.onEdited(i),this.messages.editorLabel,{baseline:e,phrases:s.fileSearchPhrases,schema:r}),this.fileEditor}catch(t){throw this.fileEditorLoading=void 0,t}})(),this.fileEditorLoading}async showTranscript(){const e=this.current;if(!e||this.shownFor===e)return;this.note(this.messages.loading);let t=this.text;if(t===void 0){const r=this.transcriptUrl(e);if(r!==void 0)try{const s=await T.json(r);this.current===e&&typeof s=="object"&&s!==null&&!Array.isArray(s)?(this.applyFileToEvent(s,!1)&&this.fileChangedEvent(),t=m.pretty(m.withEventFields(s,e))):t=m.pretty(s)}catch{this.note(this.messages.loadFailed,"error")}t??=this.template(e)}if(this.current===e)try{const r=await this.loadFileEditor(t);if(this.current!==e)return;r.replaceText(t,!1),this.shownFor=e,this.text!==void 0||this.transcriptUrl(e)===void 0?this.note(void 0):this.shadow.getElementById("file-note").textContent===this.messages.loading&&this.note(void 0)}catch{this.note(this.messages.loadFailed,"error")}}onEdited(e){this.text=e;let t,r=!0;try{t=JSON.parse(e)}catch{r=!1}this.note(r?void 0:this.messages.invalid,"error");const s=this.current;if(r&&s&&typeof t=="object"&&t!==null&&!Array.isArray(t)&&this.applyFileToEvent(t,!0)&&this.fileChangedEvent(),s&&!s.url&&e.trim()!==""){const i=s.id??"1";s.url=`${i.startsWith("interview")?i:`interview-${i}`}.json`,this.field("url").value=s.url,this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"url",event:s}}))}this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"transcript",event:s,text:e}})),this.showExchanges()}fileChangedEvent(){this.render(),this.dispatchEvent(new CustomEvent(E,{bubbles:!0,composed:!0,detail:{field:"file",event:this.current}}))}async take(e){if(typeof e!="object"||e===null||!Array.isArray(e.segments)){this.note(this.messages.notATranscript,"error");return}const t=this.current;t&&this.applyFileToEvent(e,!1)&&this.fileChangedEvent();const r=m.pretty(t?m.withEventFields(e,t):e);await this.loadFileEditor(r).then(s=>s.replaceText(r,!1),()=>{}),this.shownFor=this.current,this.note(void 0),this.onEdited(r)}async importFromFile(e){try{const t=await e.text();let r;try{r=JSON.parse(t)}catch{r=void 0}if(typeof r!="object"||r===null){const{InterviewImport:s}=await A(async()=>{const{InterviewImport:l}=await import("./interviewImport-C-TiARLK.js");return{InterviewImport:l}},[],import.meta.url),i=this.current,a=[i?.interviewOf].flat().filter(l=>typeof l=="string"),n=s.parse(t,{by:(i?.by??[]).map(l=>m.byText([l])).filter(l=>l!==""),witnesses:this.options.filter(l=>a.length===0||a.includes(l.id))});r=n&&{...n,transcription:{note:`Imported from ${e.name}: who speaks and when are as the file gave them, to be checked.`}}}r===void 0?this.note(this.messages.notATranscript,"error"):await this.take(r)}catch{this.note(this.messages.loadFailed,"error")}}async importFromUrl(e){try{await this.take(await T.json(new URL(e,this.base??location.href).href))}catch{this.note(this.messages.loadFailed,"error")}}save(){const e=this.fileEditor?.text??this.text;if(e===void 0)return;const t=(this.current?.url??"").split("/").pop()||`${this.current?.id??"interview"}.json`,r=URL.createObjectURL(new Blob([e],{type:"application/json"})),s=document.createElement("a");s.href=r,s.download=t,s.click(),URL.revokeObjectURL(r)}}function te(){Y(),J(),customElements.get($)||customElements.define($,m)}export{E as INTERVIEW_CHANGE_EVENT,$ as INTERVIEW_EDITOR_ELEMENT_NAME,W as INTERVIEW_SEEK_EVENT,m as InterviewEditorElement,te as register};
@@ -0,0 +1,18 @@
1
+ import{L as h,q as c,U as g,v as f,o as d,n as u,p,u as v}from"./rr0-sighting-editor.mjs";import{launchEditorMessages_en as m}from"./LaunchEditorMessages_en-DH8gI1vo.js";class s{static plumeValue(e){return t=>h.plumeOf(t)[e]}static plume(e,t){return{type:"number",key:`plume.${e}`,effective:s.plumeValue(e),...t}}static burn(e,t){return{type:"number",key:e,min:-3600,max:36e3,step:.1,unit:"s",scale:.001,label:t.label,hint:t.hint,group:{en:"Engine",fr:"Moteur",es:"Motor",it:"Motore"},effective:i=>t.ends(i.track??[])?.t}}static nextPlace(e){const t=e[e.length-1]??{t:0,lat:0,lng:0,altitudeKm:0},i=e[e.length-2]??t,a=(n,l=0)=>e.length>1?t[n]-i[n]:l;return{t:t.t+(e.length>1?a("t"):1e4),lat:t.lat+a("lat"),lng:t.lng+a("lng"),altitudeKm:Math.max(0,t.altitudeKm+a("altitudeKm"))}}static SET={kind:"launch",title:{en:"Launch",fr:"Lancement",es:"Lanzamiento",it:"Lancio"},parameters:[{type:"said",key:"title",label:{en:"Name",fr:"Nom",es:"Nombre",it:"Nome"},group:{en:"Vehicle",fr:"Lanceur",es:"Lanzador",it:"Lanciatore"}},{type:"text",key:"vehicle.mission",label:{en:"Mission",fr:"Mission",es:"Misión",it:"Missione"},placeholder:"Starlink Group 10-49",group:{en:"Vehicle",fr:"Lanceur",es:"Lanzador",it:"Lanciatore"}},{type:"number",key:"vehicle.engine.thrustKN",min:1,max:1e5,step:1,unit:"kN",label:{en:"Thrust (vacuum)",fr:"Poussée (dans le vide)",es:"Empuje (en el vacío)",it:"Spinta (nel vuoto)"},hint:{en:"Merlin Vacuum 934, RL10 110, a solid booster several thousand. With the specific impulse, it sets how much exhaust leaves each second.",fr:"Merlin Vacuum 934, RL10 110, un booster à poudre plusieurs milliers. Avec l'impulsion spécifique, elle fixe le débit d'échappement.",es:"Merlin Vacuum 934, RL10 110, un propulsor sólido varios miles. Con el impulso específico, fija el caudal de escape.",it:"Merlin Vacuum 934, RL10 110, un booster solido diverse migliaia. Con l'impulso specifico, fissa la portata dello scarico."},group:{en:"Engine",fr:"Moteur",es:"Motor",it:"Motore"}},{type:"number",key:"vehicle.engine.specificImpulseS",min:100,max:500,step:1,unit:"s",label:{en:"Specific impulse (vacuum)",fr:"Impulsion spécifique (dans le vide)",es:"Impulso específico (en el vacío)",it:"Impulso specifico (nel vuoto)"},hint:{en:"Times 9.81 m/s² it is the exhaust velocity, which sets how fast the exhaust spreads round the stage: 348 s is 3.4 km/s.",fr:"Multipliée par 9,81 m/s², c'est la vitesse d'éjection, qui fixe la vitesse à laquelle l'échappement s'étale autour de l'étage : 348 s font 3,4 km/s.",es:"Multiplicado por 9,81 m/s² es la velocidad de escape, que fija la rapidez con que el escape se extiende alrededor de la etapa: 348 s son 3,4 km/s.",it:"Moltiplicato per 9,81 m/s² è la velocità di scarico, che fissa quanto in fretta lo scarico si allarga attorno allo stadio: 348 s sono 3,4 km/s."},group:{en:"Engine",fr:"Moteur",es:"Motor",it:"Motore"}},{type:"choice",key:"vehicle.engine.propellant",label:{en:"Propellant",fr:"Ergols",es:"Propelentes",it:"Propellenti"},hint:{en:"What the exhaust is made of decides how much of it turns into particles that scatter light.",fr:"Ce dont l'échappement est fait décide quelle part devient des particules qui diffusent la lumière.",es:"De qué está hecho el escape decide qué parte se vuelve partículas que difunden la luz.",it:"Di cosa è fatto lo scarico decide quanta parte diventa particelle che diffondono la luce."},choices:[{value:"rp1-lox",label:{en:"Kerosene and oxygen",fr:"Kérosène et oxygène",es:"Queroseno y oxígeno",it:"Cherosene e ossigeno"}},{value:"methane-lox",label:{en:"Methane and oxygen",fr:"Méthane et oxygène",es:"Metano y oxígeno",it:"Metano e ossigeno"}},{value:"hydrogen-lox",label:{en:"Hydrogen and oxygen",fr:"Hydrogène et oxygène",es:"Hidrógeno y oxígeno",it:"Idrogeno e ossigeno"}},{value:"hypergolic",label:{en:"Hypergolic",fr:"Hypergolique",es:"Hipergólico",it:"Ipergolico"}},{value:"solid",label:{en:"Solid",fr:"Poudre",es:"Sólido",it:"Solido"}}],group:{en:"Engine",fr:"Moteur",es:"Motor",it:"Motore"}},s.burn("burnFromT",{label:{en:"Engine start",fr:"Allumage du moteur",es:"Encendido del motor",it:"Accensione del motore"},hint:{en:"When the engine started, in seconds of the recording; it may be before it. Empty: the first point of the path.",fr:"Quand le moteur s'est allumé, en secondes de l'enregistrement ; avant lui si besoin. Vide : le premier point du chemin.",es:"Cuándo se encendió el motor, en segundos de la grabación; puede ser antes. Vacío: el primer punto de la trayectoria.",it:"Quando si è acceso il motore, in secondi della registrazione; può essere prima. Vuoto: il primo punto del percorso."},ends:e=>e[0]}),s.burn("burnUntilT",{label:{en:"Engine cut-off",fr:"Extinction du moteur",es:"Apagado del motor",it:"Spegnimento del motore"},hint:{en:"When the engine stopped, in seconds of the recording. Empty: the last point of the path. Nothing is let go after it.",fr:"Quand le moteur s'est éteint, en secondes de l'enregistrement. Vide : le dernier point du chemin. Rien n'est lâché après.",es:"Cuándo se apagó el motor, en segundos de la grabación. Vacío: el último punto de la trayectoria. Nada se suelta después.",it:"Quando si è spento il motore, in secondi della registrazione. Vuoto: l'ultimo punto del percorso. Dopo non si rilascia nulla."},ends:e=>e[e.length-1]}),s.plume("exhaustKgPerS",{min:.1,max:1e5,step:.1,unit:"kg/s",label:{en:"Exhaust flow",fr:"Débit d'échappement",es:"Caudal de escape",it:"Portata di scarico"},hint:{en:"How much exhaust leaves the engine each second: how bright the plume is. Thrust over exhaust velocity when the engine is stated.",fr:"La masse d'échappement qui sort du moteur chaque seconde : la luminosité du panache. La poussée sur la vitesse d'éjection quand le moteur est donné.",es:"La masa de escape que sale del motor cada segundo: el brillo de la estela. El empuje sobre la velocidad de escape cuando se da el motor.",it:"La massa di scarico che esce dal motore ogni secondo: la luminosità della scia. La spinta sulla velocità di scarico quando il motore è dato."},group:{en:"Plume",fr:"Panache",es:"Estela",it:"Scia"}}),s.plume("condensedShare",{min:0,max:1,step:.01,label:{en:"Share that scatters",fr:"Part qui diffuse",es:"Parte que difunde",it:"Parte che diffonde"},hint:{en:"The share of the exhaust that ends up as particles that scatter the Sun's light. Nobody has measured it for a launch: the one number to fit against a photograph.",fr:"La part de l'échappement qui devient des particules diffusant la lumière du Soleil. Personne ne l'a mesurée pour un lancement : le seul nombre à caler sur une photographie.",es:"La parte del escape que acaba como partículas que difunden la luz del Sol. Nadie la ha medido para un lanzamiento: el único número que ajustar con una fotografía.",it:"La parte dello scarico che diventa particelle che diffondono la luce del Sole. Nessuno l'ha misurata per un lancio: l'unico numero da tarare su una fotografia."},group:{en:"Plume",fr:"Panache",es:"Estela",it:"Scia"}}),s.plume("particleRadiusUm",{min:.05,max:5,step:.05,unit:"µm",label:{en:"Particle radius",fr:"Rayon des particules",es:"Radio de las partículas",it:"Raggio delle particelle"},group:{en:"Plume",fr:"Panache",es:"Estela",it:"Scia"}}),s.plume("lifetimeS",{min:10,max:3600,step:10,unit:"s",label:{en:"Trail lifetime",fr:"Durée de la traînée",es:"Duración de la estela",it:"Durata della scia"},hint:{en:"How long the narrow trail lasts before the particles have gone.",fr:"Combien de temps la traînée étroite dure avant que les particules aient disparu.",es:"Cuánto dura la estela estrecha antes de que las partículas hayan desaparecido.",it:"Quanto dura la scia stretta prima che le particelle siano sparite."},group:{en:"Plume",fr:"Panache",es:"Estela",it:"Scia"}}),s.plume("expansionKmPerS",{min:0,max:5,step:.01,unit:"km/s",label:{en:"Trail spreading",fr:"Étalement de la traînée",es:"Expansión de la estela",it:"Espansione della scia"},group:{en:"Plume",fr:"Panache",es:"Estela",it:"Scia"}}),s.plume("initialRadiusKm",{min:0,max:50,step:.1,unit:"km",label:{en:"Radius at release",fr:"Rayon au lâcher",es:"Radio al soltar",it:"Raggio al rilascio"},group:{en:"Plume",fr:"Panache",es:"Estela",it:"Scia"}}),s.plume("collarShare",{min:0,max:1,step:.01,label:{en:"Share in the collar",fr:"Part dans la collerette",es:"Parte en el collar",it:"Parte nel collare"},hint:{en:"The collar is the exhaust just let go, which spreads fast into a wide short-lived shell round the stage; the rest stays behind as the narrow trail.",fr:"La collerette est l'échappement qui vient d'être lâché, qui s'étale vite en une coque large et brève autour de l'étage ; le reste demeure derrière en traînée étroite.",es:"El collar es el escape recién soltado, que se extiende rápido en una envoltura ancha y breve alrededor de la etapa; el resto queda detrás como estela estrecha.",it:"Il collare è lo scarico appena rilasciato, che si allarga in fretta in un guscio largo e breve attorno allo stadio; il resto resta dietro come scia stretta."},group:{en:"Collar",fr:"Collerette",es:"Collar",it:"Collare"}}),s.plume("collarExpansionKmPerS",{min:0,max:10,step:.05,unit:"km/s",label:{en:"Collar spreading",fr:"Étalement de la collerette",es:"Expansión del collar",it:"Espansione del collare"},hint:{en:"How fast the collar widens: three quarters of the exhaust velocity when the engine is stated.",fr:"La vitesse à laquelle la collerette s'élargit : les trois quarts de la vitesse d'éjection quand le moteur est donné.",es:"La rapidez con que el collar se ensancha: tres cuartos de la velocidad de escape cuando se da el motor.",it:"Quanto in fretta il collare si allarga: tre quarti della velocità di scarico quando il motore è dato."},group:{en:"Collar",fr:"Collerette",es:"Collar",it:"Collare"}}),s.plume("collarLifetimeS",{min:1,max:300,step:1,unit:"s",label:{en:"Collar lifetime",fr:"Durée de la collerette",es:"Duración del collar",it:"Durata del collare"},group:{en:"Collar",fr:"Collerette",es:"Collar",it:"Collare"}})]};static PLACE={kind:"launch-place",title:{en:"Place of the path",fr:"Point du chemin",es:"Punto de la trayectoria",it:"Punto del percorso"},parameters:[{type:"number",key:"t",min:-3600,max:36e3,step:.1,unit:"s",scale:.001,required:!0,label:{en:"Time",fr:"Instant",es:"Instante",it:"Istante"},hint:{en:"Seconds of the recording; negative before it began: a stage seen minutes after its launch was already high when the recording started.",fr:"Secondes de l'enregistrement ; négatives avant son début : un étage vu des minutes après son lancement était déjà haut quand l'enregistrement a commencé.",es:"Segundos de la grabación; negativos antes de que empezara: una etapa vista minutos después de su lanzamiento ya estaba alta cuando empezó la grabación.",it:"Secondi della registrazione; negativi prima del suo inizio: uno stadio visto minuti dopo il lancio era già alto quando la registrazione è iniziata."}},{type:"number",key:"lat",min:-90,max:90,step:1e-4,unit:"°",required:!0,label:{en:"Latitude",fr:"Latitude",es:"Latitud",it:"Latitudine"}},{type:"number",key:"lng",min:-180,max:180,step:1e-4,unit:"°",required:!0,label:{en:"Longitude",fr:"Longitude",es:"Longitud",it:"Longitudine"}},{type:"number",key:"altitudeKm",min:0,max:2e3,step:.1,unit:"km",required:!0,label:{en:"Altitude",fr:"Altitude",es:"Altitud",it:"Altitudine"}}]};static jetContext(e,t){return{launch:e,index:t}}static jetValue(e){return(t,i)=>{const{launch:a,index:n}=i??{};return a?e(h.releaseOf(a,t,n)):void 0}}static JET_GROUP={en:"Jet",fr:"Jet",es:"Chorro",it:"Getto"};static JET={kind:"launch-jet",title:{en:"Jet",fr:"Jet",es:"Chorro",it:"Getto"},parameters:[{type:"choice",key:"kind",required:!0,label:{en:"What",fr:"Quoi",es:"Qué",it:"Cosa"},choices:[{value:"burn",label:{en:"Engine burn",fr:"Allumage du moteur",es:"Encendido del motor",it:"Accensione del motore"}},{value:"vent",label:{en:"Propellant vented",fr:"Ergols vidangés",es:"Propelentes purgados",it:"Propellenti scaricati"}}],group:s.JET_GROUP},{type:"number",key:"fromT",min:-36e3,max:36e3,step:.1,unit:"s",scale:.001,required:!0,label:{en:"From",fr:"Début",es:"Desde",it:"Da"},hint:{en:"Seconds of the recording; negative before it began.",fr:"Secondes de l'enregistrement ; négatives avant son début.",es:"Segundos de la grabación; negativos antes de que empezara.",it:"Secondi della registrazione; negativi prima del suo inizio."},group:s.JET_GROUP},{type:"number",key:"untilT",min:-36e3,max:36e3,step:.1,unit:"s",scale:.001,required:!0,label:{en:"Until",fr:"Fin",es:"Hasta",it:"Fino a"},group:s.JET_GROUP},{type:"number",key:"angleDeg",min:0,max:180,step:1,unit:"°",effective:()=>0,label:{en:"Direction from aft",fr:"Direction depuis l'arrière",es:"Dirección desde atrás",it:"Direzione da dietro"},hint:{en:"0 straight back along the stage's motion (an engine pushing it on), 90 sideways, 180 straight ahead (an engine braking it).",fr:"0 droit vers l'arrière du mouvement de l'étage (un moteur qui le pousse), 90 sur le côté, 180 droit devant (un moteur qui le freine).",es:"0 hacia atrás del movimiento de la etapa (un motor que la empuja), 90 de lado, 180 hacia delante (un motor que la frena).",it:"0 dritto all'indietro rispetto al moto dello stadio (un motore che lo spinge), 90 di lato, 180 dritto in avanti (un motore che lo frena)."},group:s.JET_GROUP},{type:"number",key:"azimuthDeg",min:-360,max:360,step:1,unit:"°",effective:()=>0,label:{en:"Side",fr:"Côté",es:"Lado",it:"Lato"},hint:{en:"Round the line of motion, which side it leaves on at the start: 0 away from the Earth, 90 to the right of the motion.",fr:"Autour de la ligne du mouvement, le côté par lequel il part au début : 0 à l'opposé de la Terre, 90 à droite du mouvement.",es:"Alrededor de la línea del movimiento, el lado por el que sale al principio: 0 opuesto a la Tierra, 90 a la derecha del movimiento.",it:"Attorno alla linea del moto, il lato da cui esce all'inizio: 0 opposto alla Terra, 90 a destra del moto."},group:s.JET_GROUP},{type:"number",key:"spinPeriodS",min:-3600,max:3600,step:.1,unit:"s",label:{en:"Spin period",fr:"Période de rotation",es:"Período de giro",it:"Periodo di rotazione"},hint:{en:"How long the stage takes to turn once round its line of motion; the jet turns with it and vented propellant draws a spiral. Negative turns the other way. Empty: it does not turn.",fr:"Le temps que met l'étage à faire un tour autour de sa ligne de mouvement ; le jet tourne avec lui et les ergols vidangés dessinent une spirale. Négative : dans l'autre sens. Vide : il ne tourne pas.",es:"Lo que tarda la etapa en dar una vuelta alrededor de su línea de movimiento; el chorro gira con ella y los propelentes purgados dibujan una espiral. Negativo: en el otro sentido. Vacío: no gira.",it:"Il tempo che lo stadio impiega a fare un giro attorno alla sua linea di moto; il getto gira con lui e i propellenti scaricati disegnano una spirale. Negativo: nell'altro verso. Vuoto: non gira."},group:s.JET_GROUP},{type:"number",key:"speedKmPerS",min:0,max:10,step:.01,unit:"km/s",effective:s.jetValue(e=>e.speedKmPerS),label:{en:"Speed",fr:"Vitesse",es:"Velocidad",it:"Velocità"},hint:{en:"How fast it leaves the stage. A burn: the engine's exhaust velocity.",fr:"La vitesse à laquelle il quitte l'étage. Un allumage : la vitesse d'éjection du moteur.",es:"La velocidad con que sale de la etapa. Un encendido: la velocidad de escape del motor.",it:"La velocità con cui lascia lo stadio. Un'accensione: la velocità di scarico del motore."},group:s.JET_GROUP},{type:"number",key:"massKgPerS",min:0,max:1e5,step:.1,unit:"kg/s",effective:s.jetValue(e=>e.massKgPerS),label:{en:"Flow",fr:"Débit",es:"Caudal",it:"Portata"},group:s.JET_GROUP},{type:"number",key:"condensedShare",min:0,max:1,step:.01,effective:s.jetValue(e=>e.condensedShare),label:{en:"Share that scatters",fr:"Part qui diffuse",es:"Parte que difunde",it:"Parte che diffonde"},group:s.JET_GROUP},{type:"number",key:"particleRadiusUm",min:.05,max:5,step:.05,unit:"µm",effective:s.jetValue(e=>e.particleRadiusUm),label:{en:"Particle radius",fr:"Rayon des particules",es:"Radio de las partículas",it:"Raggio delle particelle"},group:s.JET_GROUP},{type:"number",key:"expansionKmPerS",min:0,max:5,step:.01,unit:"km/s",effective:s.jetValue(e=>e.populations[0].expansionKmPerS),label:{en:"Spreading",fr:"Étalement",es:"Expansión",it:"Espansione"},hint:{en:"How fast each puff of it widens: a narrow jet keeps the arms of a spiral apart.",fr:"La vitesse à laquelle chaque bouffée s'élargit : un jet étroit garde les bras d'une spirale séparés.",es:"La rapidez con que se ensancha cada bocanada: un chorro estrecho mantiene separados los brazos de una espiral.",it:"Quanto in fretta si allarga ogni sbuffo: un getto stretto tiene separati i bracci di una spirale."},group:s.JET_GROUP},{type:"number",key:"initialRadiusKm",min:0,max:50,step:.1,unit:"km",effective:s.jetValue(e=>e.initialRadiusKm),label:{en:"Radius at release",fr:"Rayon au lâcher",es:"Radio al soltar",it:"Raggio al rilascio"},group:s.JET_GROUP},{type:"number",key:"lifetimeS",min:1,max:7200,step:1,unit:"s",effective:s.jetValue(e=>e.populations[0].lifetimeS),label:{en:"Lifetime",fr:"Durée",es:"Duración",it:"Durata"},group:s.JET_GROUP}]};static publish(){c.publish(s.SET)}}class r{constructor(e,t,i){this.container=e,this.host=t,this.language=i,c.of("launch")||s.publish(),this.render(),this.setLanguage(i)}messages=m;languageToken=0;currentId;form;list;places;placeForm;placeIndex=0;jets;jetForm;jetIndex=0;language;shown=[];async setLanguage(e){const t=++this.languageToken;this.language=e;const i=g.includes(e)?e:"en",a=i==="en"?m:await f(i);t===this.languageToken&&(this.messages=a,this.render(),this.sync())}sync(){const e=this.launches;this.shown=[...e],e.some(a=>a.id===this.currentId)||(this.currentId=e[0]?.id),this.list?.setItems(e.map(a=>({id:a.id,label:this.labelOf(a)})),this.currentId),this.syncAddState();const t=this.current;if(this.element("launch-fields").hidden=t===void 0,!t)return;this.syncPlaces(t),this.syncJets(t);const i=c.of("launch");this.form??=new d(this.element("launch-parameters"),this.language,"",{said:()=>this.host.said(),writingLanguage:()=>this.host.writingLanguage()}),this.form.show(i,t,()=>this.written())}refresh(){this.syncAddState();const e=this.launches;e.length===this.shown.length&&e.every((t,i)=>t===this.shown[i])||this.sync()}syncAddState(){const e=this.startOfNew()!==void 0;this.list?.setAddState(e,e?void 0:this.messages.addLaunchNowhere)}get current(){return this.launches.find(e=>e.id===this.currentId)}show(e){this.currentId=e,this.placeIndex=0,this.sync()}render(){const e=this.messages;this.form=void 0,this.container.innerHTML=`<div class="launch-editor">
2
+ <p class="launch-intro">${e.intro}</p>
3
+ <div id="launch-list"></div>
4
+ <div id="launch-fields" class="launch-fields">
5
+ <fieldset id="launch-path" class="launch-path">
6
+ <legend>${e.path}</legend>
7
+ <div id="launch-places"></div>
8
+ <div id="launch-place-parameters" class="launch-parameters"></div>
9
+ </fieldset>
10
+ <fieldset id="launch-jets" class="launch-path">
11
+ <legend>${e.jets}</legend>
12
+ <div id="launch-jet-list"></div>
13
+ <div id="launch-jet-parameters" class="launch-parameters"></div>
14
+ </fieldset>
15
+ <div id="launch-parameters" class="launch-parameters"></div>
16
+ <p class="launch-intro">${e.derivedNote}</p>
17
+ </div>
18
+ </div>`;const t=this.element("launch-list");this.list=new u({header:t,body:this.element("launch-fields"),ids:{list:"launch-select",remove:"launch-delete",add:"launch-add",kinds:"launch-add-kind",kind:a=>`launch-add-${a}`},onSelect:a=>{this.currentId=a,this.placeIndex=0,this.sync()},onAdd:a=>this.addLaunch(a??r.VEHICLES[0].id),onRemove:()=>this.deleteCurrent()}),this.list.setLabels({list:e.launch,remove:e.deleteLaunch,add:e.addLaunch,empty:e.none}),this.list.setKinds(r.VEHICLES.map(a=>({value:a.id,label:a.vehicle.name??a.id})));const i=this.element("launch-places");this.places=new u({header:i,body:this.element("launch-place-parameters"),ids:{list:"launch-place",remove:"launch-place-delete",add:"launch-place-add"},navigation:{scope:this.container,onStep:a=>this.selectPlace(this.placeIndex+a)},onSelect:a=>this.selectPlace(Number(a)),onAdd:()=>this.addPlace(),onRemove:()=>this.removePlace()}),this.places.setLabels({list:e.place,remove:e.deletePlace,add:e.addPlace,previous:e.previousPlace,next:e.nextPlace}),this.placeForm=void 0,this.jets=new u({header:this.element("launch-jet-list"),body:this.element("launch-jet-parameters"),ids:{list:"launch-jet",remove:"launch-jet-delete",add:"launch-jet-add",kinds:"launch-jet-add-kind",kind:a=>`launch-jet-add-${a}`},onSelect:a=>{this.jetIndex=Number(a),this.syncJets(this.current,!0)},onAdd:a=>this.addJet(a==="vent"?"vent":"burn"),onRemove:()=>this.removeJet()}),this.jets.setLabels({list:e.jet,remove:e.deleteJet,add:e.addJet,empty:e.noJet}),this.jets.setKinds([{value:"burn",label:e.jetBurn},{value:"vent",label:e.jetVent}]),this.jetForm=void 0}written(){this.host.changed();const e=this.current;e&&this.list?.rename(e.id,this.labelOf(e))}syncPlaces(e,t=!1){const i=e.track??[];this.placeIndex=Math.max(0,Math.min(this.placeIndex,i.length-1)),this.places?.setItems(i.map((a,n)=>({id:String(n),label:this.seconds(a.t)})),String(this.placeIndex)),this.places?.setStepState(this.placeIndex>0,this.placeIndex<i.length-1),this.places?.setRemoveEnabled(i.length>r.MIN_PLACES),this.placeForm?this.placeForm.sync(t):(this.placeForm=new d(this.element("launch-place-parameters"),this.language,"launchPlace."),this.placeForm.show(s.PLACE,new p(()=>this.current?.track[this.placeIndex],a=>this.editPlace(a)),()=>{}))}syncJets(e,t=!1){const i=e.jets??[];this.jetIndex=Math.max(0,Math.min(this.jetIndex,i.length-1));const a=this.messages;this.jets?.setItems(i.map((n,l)=>({id:String(l),label:`${n.kind==="vent"?a.jetVent:a.jetBurn} ${this.seconds(n.fromT)}`})),i.length?String(this.jetIndex):void 0),this.jetForm?this.jetForm.sync(t):(this.jetForm=new d(this.element("launch-jet-parameters"),this.language,"launchJet."),this.jetForm.show(s.JET,new p(()=>this.current?.jets?.[this.jetIndex],n=>this.editJet(n),()=>this.current?s.jetContext(this.current,this.jetIndex):void 0),()=>{}))}editJet(e){const t=this.current;if(!t?.jets)return;const i={...t.jets[this.jetIndex]};e(i),t.jets=t.jets.map((n,l)=>l===this.jetIndex?i:n);const a=this.messages;this.jets?.rename(String(this.jetIndex),`${i.kind==="vent"?a.jetVent:a.jetBurn} ${this.seconds(i.fromT)}`),this.written()}addJet(e){const t=this.current;if(!t)return;const i=t.track,a=Math.round((i[0].t+i[i.length-1].t)/2),n=e==="vent"?{kind:e,fromT:a,untilT:a+3e4,angleDeg:90}:{kind:e,fromT:a,untilT:a+3e4};!t.jets?.length&&t.burnFromT===void 0&&t.burnUntilT===void 0&&(t.burnFromT=i[0].t,t.burnUntilT=i[i.length-1].t),t.jets=[...t.jets??[],n],this.jetIndex=t.jets.length-1,this.syncJets(t,!0),this.form?.sync(!0),this.host.changed()}removeJet(){const e=this.current;if(!e?.jets?.length)return;const t=e.jets.filter((i,a)=>a!==this.jetIndex);t.length?e.jets=t:delete e.jets,this.syncJets(e,!0),this.host.changed()}static MIN_PLACES=2;seconds(e){return`${Math.round(e/100)/10} s`}selectPlace(e){const t=this.current?.track??[];e<0||e>=t.length||(this.placeIndex=e,this.syncPlaces(this.current,!0))}editPlace(e){const t=this.current;if(!t)return;const i={...t.track[this.placeIndex]};e(i);const a=t.track.map((n,l)=>l===this.placeIndex?i:n).sort((n,l)=>n.t-l.t);this.replaceTrack(t,a),this.placeIndex=a.indexOf(i),this.syncPlaces(this.current),this.written()}addPlace(){const e=this.current;if(!e)return;const t=e.track,i=s.nextPlace(t),a=[...e.track,i].sort((n,l)=>n.t-l.t);this.replaceTrack(e,a),this.placeIndex=a.indexOf(i),this.syncPlaces(this.current),this.host.changed()}removePlace(){const e=this.current;!e||e.track.length<=r.MIN_PLACES||(this.replaceTrack(e,e.track.filter((t,i)=>i!==this.placeIndex)),this.syncPlaces(this.current),this.host.changed())}replaceTrack(e,t){e.track=t}startOfNew(){const e=this.host.sighting(),t=e.event?.place?.[0];if(t?.lat===void 0||t.lng===void 0)return;const i=(e.observerTrack?.getInterpolatedPoseAt(0)?.headingDeg??0)*Math.PI/180,a=n=>({lat:t.lat+n*Math.cos(i)/111.32,lng:t.lng+n*Math.sin(i)/(111.32*Math.max(.05,Math.cos(t.lat*Math.PI/180)))});return[{t:0,...a(300),altitudeKm:80},{t:6e4,...a(450),altitudeKm:160}]}static VEHICLES=[{id:"falcon-9",vehicle:{name:"Falcon 9"}}];addLaunch(e){const t=this.startOfNew();if(!t)return;const i=r.VEHICLES.find(o=>o.id===e)??r.VEHICLES[0];let a=this.launches.length+1;for(;this.launches.some(o=>o.id===`launch-${a}`);)a++;const n={id:`launch-${a}`,vehicle:{...i.vehicle},track:t},l=this.host.sighting();l.skyEvents={...l.skyEvents,launches:[...this.launches,n]},this.currentId=n.id,this.host.changed(),this.sync()}deleteCurrent(){const e=this.current;if(!e)return;this.currentId=void 0;const t=this.host.sighting();t.skyEvents=v.written({...t.skyEvents,launches:this.launches.filter(i=>i!==e)}),this.host.changed(),this.sync()}labelOf(e){const t=this.host.said().read(e.title)??e.vehicle?.mission??e.vehicle?.name;return t?`${t} (${e.id})`:e.id}get launches(){return this.host.sighting().skyEvents?.launches??[]}element(e){return this.container.querySelector(`#${e}`)}}export{r as LaunchEditor};
@@ -0,0 +1 @@
1
+ const e={intro:"A rocket launch while the recording runs, a fact of the sky: its exhaust shows lit by a Sun the ground no longer sees. Its path, its engine and how its exhaust spreads are set below.",none:"No launch in this recording",launch:"Launch",deleteLaunch:"Delete launch",addLaunch:"Add a launch",path:"Path",place:"Place of the path",addPlace:"Add a place to the path, going on as it was going",deletePlace:"Remove this place",previousPlace:"Previous place",nextPlace:"Next place",jets:"Jets",jet:"Jet",noJet:"None: the engine burns along the path",addJet:"Add a jet",deleteJet:"Remove this jet",jetBurn:"Engine burn",jetVent:"Propellant vented",addLaunchNowhere:"Give the recording a place first: a launch is put in front of the observer",derivedNote:"An empty field shows, greyed, the value used: the default, or what the engine implies. Clear a field to go back to it."};export{e as launchEditorMessages_en};
@@ -0,0 +1 @@
1
+ const e={intro:"Un lanzamiento de cohete durante la grabación, un hecho del cielo: su escape se ve iluminado por un Sol que el suelo ya no ve. Su trayectoria, su motor y la expansión del escape se ajustan abajo.",none:"Ningún lanzamiento en esta grabación",launch:"Lanzamiento",deleteLaunch:"Eliminar el lanzamiento",addLaunch:"Añadir un lanzamiento",path:"Trayectoria",place:"Punto de la trayectoria",addPlace:"Añadir un punto a la trayectoria, en su continuidad",deletePlace:"Quitar este punto",previousPlace:"Punto anterior",nextPlace:"Punto siguiente",jets:"Chorros",jet:"Chorro",noJet:"Ninguno: el motor arde a lo largo de la trayectoria",addJet:"Añadir un chorro",deleteJet:"Quitar este chorro",jetBurn:"Encendido del motor",jetVent:"Propelentes purgados",addLaunchNowhere:"Dé primero un lugar a la grabación: un lanzamiento se pone delante del observador",derivedNote:"Un campo vacío muestra, en gris, el valor usado: el predeterminado, o lo que implica el motor. Vacíe un campo para volver a él."};export{e as launchEditorMessages_es};
@@ -0,0 +1 @@
1
+ const e={intro:"Un lancement de fusée pendant l'enregistrement, un fait du ciel : son échappement se voit éclairé par un Soleil que le sol ne voit plus. Son chemin, son moteur et l'étalement de l'échappement se règlent ci-dessous.",none:"Aucun lancement dans cet enregistrement",launch:"Lancement",deleteLaunch:"Supprimer le lancement",addLaunch:"Ajouter un lancement",path:"Chemin",place:"Point du chemin",addPlace:"Ajouter un point au chemin, dans la continuité",deletePlace:"Retirer ce point",previousPlace:"Point précédent",nextPlace:"Point suivant",jets:"Jets",jet:"Jet",noJet:"Aucun : le moteur brûle le long du chemin",addJet:"Ajouter un jet",deleteJet:"Retirer ce jet",jetBurn:"Allumage du moteur",jetVent:"Ergols vidangés",addLaunchNowhere:"Donnez d'abord un lieu à l'enregistrement : un lancement est placé devant l'observateur",derivedNote:"Un champ vide montre, en grisé, la valeur utilisée : le défaut, ou ce que le moteur implique. Videz un champ pour y revenir."};export{e as launchEditorMessages_fr};
@@ -0,0 +1 @@
1
+ const e={intro:"Un lancio di razzo durante la registrazione, un fatto del cielo: il suo scarico si vede illuminato da un Sole che il suolo non vede più. Il suo percorso, il motore e l'espansione dello scarico si regolano qui sotto.",none:"Nessun lancio in questa registrazione",launch:"Lancio",deleteLaunch:"Elimina il lancio",addLaunch:"Aggiungi un lancio",path:"Percorso",place:"Punto del percorso",addPlace:"Aggiungi un punto al percorso, in continuità",deletePlace:"Rimuovi questo punto",previousPlace:"Punto precedente",nextPlace:"Punto successivo",jets:"Getti",jet:"Getto",noJet:"Nessuno: il motore brucia lungo il percorso",addJet:"Aggiungere un getto",deleteJet:"Togliere questo getto",jetBurn:"Accensione del motore",jetVent:"Propellenti scaricati",addLaunchNowhere:"Dia prima un luogo alla registrazione: un lancio è posto davanti all'osservatore",derivedNote:"Un campo vuoto mostra, in grigio, il valore usato: il predefinito, o ciò che implica il motore. Svuota un campo per tornarci."};export{e as launchEditorMessages_it};