@rr0/ufoathome 0.129.0 → 0.129.1

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.
@@ -129,7 +129,7 @@ rr0-date-input [hidden] { display: none !important; }
129
129
  position are unknown is left out and listed in your report, not drawn as a pedestrian in the road.
130
130
  The eye is 1.6 m above the ground plus the pose's \`elevationM\`: for a high seat (a tractor cab, a
131
131
  lorry), raise \`elevationM\` by the difference. A seat that is not a car's (a central seat) is
132
- \`observerSide\` "front". Standing decor sits on the ground; only a \`track\` lifts it.`},{draft:"`observerTrack` is where the observer stood and which way they faced, over time. One pose is\n normally enough. `headingDeg` is the direction they were LOOKING, same convention as `aim`,\n and it is what makes a shape's azimuth mean anything — an observer driving towards a named village\n faces that bearing. `elevationM` is metres above the local ground (0 for someone standing on it;\n the eye's own height is added), `pitchDeg` 0 for someone looking level, `fovDeg` about 60 for\n the naked eye.",file:'`observerTrack` ({keyframes: [{t, pose}]}) is where the observer stood and which way they looked,\n over time: `lat`, `lng`, `elevationM` (metres above the local ground, 0 for someone standing\n on it: the eye\'s own height is added), `headingDeg` and `pitchDeg` (the direction they LOOKED,\n same conventions as `aim`), `fovDeg` (the vertical field shown, about 60 for the naked eye).\n A walk is a series of poses; a head turned towards the phenomenon is two poses a fraction of a\n second apart, the second where the look comes to rest: the player gives the turn its ease and the\n small swing past and back that ends a human look, so state no extra keyframe for them. An observer who follows a moving phenomenon looks 0.15 to 0.2 s behind it, so keep\n the gaze on the same path, sampled as densely, or it leaves the field. `instrument` is "eye" unless a camera was used.'},{draft:'`account` is about the OBSERVERS and how their account travelled, not about the phenomenon.\n An account naming who was there says it in `observerOccupation` ("boulanger"), in their own\n words and never as a level, and each other person present is a `decor` object of kind\n "observer". But `source` and `followedUp` say how the report was OBTAINED, which no account\n of a sighting can tell you: leave them out rather than guessing, unless the text itself says an\n investigator came, or that it is a press cutting.',file:'`account` is about the observer and how the account travelled: `observerAgeYears` (at the\n time), `observerOccupation` (in their words), `source` (how it reached whoever wrote the\n recording: on-site, interview, telephone, questionnaire, letter, social-media, press) and `followedUp`\n (whether they were gone back to). Where the account can be read goes in `sources`, in the\n shape of an RR0 source: [{type ("book" | "article", none for a web page or a post), title,\n authors, url, publication: {publisher, time}, index}]. Always give the documents you worked from. Each other person present is a `decor` object of kind\n "observer".'},{draft:'Silence is a statement. An observer who says the thing was silent is not an observer who said nothing\n about sound: write a `soundTrack` whose keyframe holds a sound of kind "none", and mark it\n "stated". An account that simply never mentions sound gets no soundTrack at all.',file:'Absent is not zero. An observer who says the thing was silent gets a `soundTrack` whose keyframe\n holds a sound of kind "none"; an account that never mentions sound gets no soundTrack. The same\n holds for the weather: `weatherTrack` states what the observer saw of the sky (cover, rain,\n wind); when it comes from a record (ERA5, a METAR, a station), `weatherSource` names it.'},{draft:`\`lightPollution\` is how bright the night sky of the place is, towns and all: the zenith of a
132
+ \`observerSide\` "front". Standing decor sits on the ground; only a \`track\` lifts it.`},{draft:"`observerTrack` is where the observer stood and which way they faced, over time. One pose is\n normally enough. `headingDeg` is the direction they were LOOKING, same convention as `aim`,\n and it is what makes a shape's azimuth mean anything — an observer driving towards a named village\n faces that bearing. `elevationM` is metres above the local ground (0 for someone standing on it;\n the eye's own height is added), `pitchDeg` 0 for someone looking level, `fovDeg` about 60 for\n the naked eye.",file:"`observerTrack` ({keyframes: [{t, pose}]}) is where the observer stood and which way they looked,\n over time: `lat`, `lng`, `elevationM` (metres above the local ground, 0 for someone standing\n on it: the eye's own height is added), `headingDeg` and `pitchDeg` (the direction they LOOKED,\n same conventions as `aim`), `fovDeg` (the vertical field shown, about 60 for the naked eye).\n A walk is a series of poses; a head turned towards the phenomenon is two poses a fraction of a\n second apart, the second where the look comes to rest: the player gives the turn its ease and the\n small swing past and back that ends a human look, so state no extra keyframe for them. An observer who follows a moving phenomenon looks 0.15 to 0.2 s behind it, so keep\n the gaze on the same path, sampled as densely, or it leaves the field. A recording made to show something (a sky test, a\n demonstration) and not to replay an account never holds still: from one keyframe to the next the look keeps moving, a slow pan\n along what is shown, a drift up or down, a few steps, and no stretch of more than about 3 s is the same view (a still view is a\n photograph, not a replay); an observer's own account keeps the stillness it states. `instrument` is \"eye\" unless a camera was used."},{draft:'`account` is about the OBSERVERS and how their account travelled, not about the phenomenon.\n An account naming who was there says it in `observerOccupation` ("boulanger"), in their own\n words and never as a level, and each other person present is a `decor` object of kind\n "observer". But `source` and `followedUp` say how the report was OBTAINED, which no account\n of a sighting can tell you: leave them out rather than guessing, unless the text itself says an\n investigator came, or that it is a press cutting.',file:'`account` is about the observer and how the account travelled: `observerAgeYears` (at the\n time), `observerOccupation` (in their words), `source` (how it reached whoever wrote the\n recording: on-site, interview, telephone, questionnaire, letter, social-media, press) and `followedUp`\n (whether they were gone back to). Where the account can be read goes in `sources`, in the\n shape of an RR0 source: [{type ("book" | "article", none for a web page or a post), title,\n authors, url, publication: {publisher, time}, index}]. Always give the documents you worked from. Each other person present is a `decor` object of kind\n "observer".'},{draft:'Silence is a statement. An observer who says the thing was silent is not an observer who said nothing\n about sound: write a `soundTrack` whose keyframe holds a sound of kind "none", and mark it\n "stated". An account that simply never mentions sound gets no soundTrack at all.',file:'Absent is not zero. An observer who says the thing was silent gets a `soundTrack` whose keyframe\n holds a sound of kind "none"; an account that never mentions sound gets no soundTrack. The same\n holds for the weather: `weatherTrack` states what the observer saw of the sky (cover, rain,\n wind); when it comes from a record (ERA5, a METAR, a station), `weatherSource` names it.'},{draft:`\`lightPollution\` is how bright the night sky of the place is, towns and all: the zenith of a
133
133
  moonless night in magnitudes per square arcsecond (22 a natural sky, about 19 a suburb, 17 a city
134
134
  centre). Write it only from a figure: a Sky Quality Meter reading the observer gives ("stated"),
135
135
  or the World Atlas's "SQM" value for the place (Falchi et al. 2016), marked "derived" with the
@@ -3288,9 +3288,9 @@ canvas[data-cursor="rotate"] {
3288
3288
  `;class sl{constructor(e){this.points=e}static STATIONARY_M=5;static of(e){const t=[];for(const i of e.observerTrack.allKeyframes){const{lat:n,lng:s,headingDeg:r}=i.pose;n===void 0||s===void 0||t.push({t:i.t,lat:n,lng:s,headingDeg:r})}if(t.length===0){const i=e.event.place?.[0];if(!i)return;t.push({t:0,lat:i.lat,lng:i.lng})}return new sl(t)}static TRAVEL_M=50;get travels(){return this.spanM>sl.TRAVEL_M}get moved(){return this.spanM>sl.STATIONARY_M}get spanM(){const e=this.box,t=(e.south+e.north)/2,i=(e.west+e.east)/2;return Math.max(Math.abs(rt(t,e.east,t,e.west).x),Math.abs(rt(e.north,i,e.south,i).z))}get center(){const e=this.box;return{lat:(e.south+e.north)/2,lng:(e.west+e.east)/2}}boundsAround(e,t){const{lat:i,lng:n}=this.center,s=Math.max(this.spanM*(1+t),e)/2;return{north:Ls(0,-s,i,n).lat,south:Ls(0,s,i,n).lat,east:Ls(s,0,i,n).lng,west:Ls(-s,0,i,n).lng}}get box(){const e=this.points.map(i=>i.lat),t=this.points.map(i=>i.lng);return{south:Math.min(...e),north:Math.max(...e),west:Math.min(...t),east:Math.max(...t)}}}class zs{constructor(e,t){this.rotationPassed=e,this.scale=t}static SWAY_M=.012;static BREATH_M=.003;static DRIFT_YAW_DEG=.5;static DRIFT_PITCH_DEG=.4;static DRIFT_ROLL_DEG=.3;static GAZE_DRIFT_SHARE=.35;static HAND_HELD_S=.5;static HZ=[.11,.23,.17,.29,.37,.07];static of(e){const t=e.event.tags?.includes("paralysis")||(e.exposure??0)>zs.HAND_HELD_S,i=e.sway??(t?0:1);if(i>0)return new zs(Math.max(1-(e.instrument.stabilization??0),zs.GAZE_DRIFT_SHARE),i)}offsetAt(e){const t=e/1e3,i=this.scale,n=(d,u)=>Math.sin(2*Math.PI*d*t+u),[s,r,a,o,c,h]=zs.HZ;return{eastM:i*zs.SWAY_M*(.7*n(s,.3)+.3*n(o,1.9)),northM:i*zs.SWAY_M*(.6*n(a,2.2)+.4*n(c,.8)),upM:i*zs.BREATH_M*n(r,0),yawDeg:i*this.rotationPassed*zs.DRIFT_YAW_DEG*(.6*n(h,1.1)+.4*n(o,2.7)),pitchDeg:i*this.rotationPassed*zs.DRIFT_PITCH_DEG*(.5*n(r,1.6)+.5*n(a,.4)),rollDeg:i*this.rotationPassed*zs.DRIFT_ROLL_DEG*n(s,2.9)}}}class ii{constructor(e,t){this.stretches=e,this.rotationPassed=t}static REFERENCE_SPEED_M_PER_S=1.4;static REFERENCE_STEP_HZ=1.9;static CADENCE_EXPONENT=.43;static RISE_M_PER_M_PER_S=.0175;static SWAY_M=.0175;static SLOWEST_WALK_M_PER_S=.2;static FASTEST_WALK_M_PER_S=2.2;static HEAD_ROLL_DEG=1.5;static HEAD_PITCH_DEG=1.25;static HEAD_YAW_DEG=1;static STOP_LURCH_M=.035;static STOP_PITCH_DEG=3;static START_LURCH_M=.015;static START_PITCH_DEG=1.2;static JOLT_HZ=1.4;static JOLT_DECAY_S=.35;static JOLT_LASTS_MS=1500;static STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(e){const t=sl.of(e);if(!t||t.points.length<2)return;const i=[];let n=0;for(let s=1;s<t.points.length;s++){const r=t.points[s-1],a=t.points[s],o=(a.t-r.t)/1e3;if(o<=0)continue;const c=rt(a.lat,a.lng,r.lat,r.lng),h=c.x,d=-c.z,u=Math.hypot(h,d),p=u/o,f=ii.stepHzFor(p);i.push({startMs:r.t,endMs:a.t,speedMPerS:p,eastUnit:u>0?h/u:0,northUnit:u>0?d/u:0,stepHz:f,stepsBefore:n,rampsIn:!0,rampsOut:!0}),n+=f*o}for(let s=0;s<i.length;s++){const r=i[s-1],a=i[s+1];i[s].rampsIn=r===void 0||r.stepHz===0,i[s].rampsOut=a===void 0||a.stepHz===0}if(i.some(s=>s.stepHz>0))return new ii(i,1-(e.instrument.stabilization??0))}static bodyAt(e,t,i=!1){const n=ii.of(e)?.offsetAt(t)??ii.STILL;return i?n:ii.add(n,zs.of(e)?.offsetAt(t))}static stepHzFor(e){return e<ii.SLOWEST_WALK_M_PER_S||e>ii.FASTEST_WALK_M_PER_S?0:ii.REFERENCE_STEP_HZ*Math.pow(e/ii.REFERENCE_SPEED_M_PER_S,ii.CADENCE_EXPONENT)}offsetAt(e){return ii.add(this.cycleAt(e),this.joltAt(e))}joltAt(e){for(const t of this.stretches){if(t.stepHz===0)continue;const i=t.speedMPerS/ii.REFERENCE_SPEED_M_PER_S,n=(a,o,c,h)=>{if(a<0||a>ii.JOLT_LASTS_MS)return;const d=a/1e3,u=Math.exp(-d/ii.JOLT_DECAY_S)*Math.sin(2*Math.PI*ii.JOLT_HZ*d),p=h*o*i*u;return{eastM:p*t.eastUnit,northM:p*t.northUnit,upM:-Math.abs(p)*.3,rollDeg:0,pitchDeg:-h*c*i*u*this.rotationPassed,yawDeg:0}},s=t.rampsOut?n(e-t.endMs,ii.STOP_LURCH_M,ii.STOP_PITCH_DEG,1):void 0;if(s)return s;const r=t.rampsIn?n(e-t.startMs,ii.START_LURCH_M,ii.START_PITCH_DEG,-1):void 0;if(r)return r}}static add(e,t){return t?{eastM:e.eastM+t.eastM,northM:e.northM+t.northM,upM:e.upM+t.upM,rollDeg:e.rollDeg+t.rollDeg,pitchDeg:e.pitchDeg+t.pitchDeg,yawDeg:e.yawDeg+t.yawDeg}:e}cycleAt(e){const t=this.stretches.find(o=>e>=o.startMs&&e<=o.endMs);if(!t||t.stepHz===0)return ii.STILL;const i=t.stepsBefore+t.stepHz*(e-t.startMs)/1e3,n=2*Math.PI*i,s=this.amplitudeAt(t,e),r=ii.SWAY_M*s*Math.cos(n/2),a=s*this.rotationPassed*t.speedMPerS/ii.REFERENCE_SPEED_M_PER_S;return{eastM:r*t.northUnit,northM:-r*t.eastUnit,upM:ii.RISE_M_PER_M_PER_S*t.speedMPerS*s*Math.cos(n),rollDeg:ii.HEAD_ROLL_DEG*a*Math.cos(n/2),pitchDeg:-1.25*a*Math.cos(n),yawDeg:ii.HEAD_YAW_DEG*a*Math.cos(n/2)}}amplitudeAt(e,t){const i=1e3/e.stepHz,n=Math.min(i,(e.endMs-e.startMs)/2);if(n<=0)return 1;const s=t-e.startMs,r=e.endMs-t,a=Math.min(e.rampsIn?s/n:1,e.rampsOut?r/n:1,1);return(1-Math.cos(Math.PI*Math.max(a,0)))/2}}class NA{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;onEnded;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const i of this.timeline.sourceIds){const n=this.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}this.onFrame(e,t)}}class rl{static sessionDeclared=!1;static unlock(e){if(rl.declarePlayback(),e.state!=="running")try{Promise.resolve(e.resume()).catch(()=>{});const t=e.createBufferSource();t.buffer=e.createBuffer(1,1,22050),t.connect(e.destination),t.start(0)}catch{}}static declarePlayback(){if(!(rl.sessionDeclared||typeof navigator>"u")){rl.sessionDeclared=!0;try{const e=navigator.audioSession;e&&(e.type="playback")}catch{}}}}const fy=.05,my=.08,gy=2,vy=3,FA=14,UA=.02;class BA{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;level=1;master;output(e){return this.master||(this.master=e.createGain(),this.master.gain.value=this.level,this.master.connect(e.destination)),this.master}setLevel(e){this.level=e,this.master&&(this.master.gain.value=e)}resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",e);return}}rl.unlock(this.context)}setSound(e){if(this.requested=e,!this.context)return;const t=this.keyFor(e);if(!t){this.silence();return}if(this.voice?.key!==t){this.stopVoice();const i=++this.voiceToken;t.startsWith("src:")?this.buildRecordedVoice(e.src,t,i):this.voice=this.buildSynthesizedVoice(e,t)}this.applyTo(this.voice,e)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.master=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(e){if(!(e.volume<=0))return e.src?`src:${e.src}`:e.kind==="none"?void 0:e.kind}applyTo(e,t){const i=this.context;if(!e||!i)return;const n=i.currentTime;e.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,t.volume))*e.gainScale,n,fy);for(const{param:s,ratio:r}of e.pitch)s.setTargetAtTime(Math.min(t.pitchHz*r,i.sampleRate/2-1),n,fy)}buildSynthesizedVoice(e,t){const i=this.context;if(i)switch(e.kind){case"hum":return this.buildHum(i,t);case"whistle":return this.buildWhistle(i,t);case"rumble":return this.buildRumble(i,t);case"crackle":return this.buildCrackle(i,t);default:return}}buildHum(e,t){const i=e.createOscillator();i.type="sawtooth";const n=e.createOscillator();n.type="sawtooth";const s=e.createBiquadFilter();s.type="lowpass",s.Q.value=1;const r=e.createGain();return r.gain.value=0,i.connect(s),n.connect(s),s.connect(r).connect(this.output(e)),i.start(),n.start(),{key:t,nodes:[s,r],sources:[i,n],volumeParam:r.gain,gainScale:.16,pitch:[{param:i.frequency,ratio:1},{param:n.frequency,ratio:1.006},{param:s.frequency,ratio:6}]}}buildWhistle(e,t){const i=e.createOscillator();i.type="sine";const n=e.createOscillator();n.type="sine",n.frequency.value=5;const s=e.createGain(),r=e.createGain();return r.gain.value=0,n.connect(s).connect(i.frequency),i.connect(r).connect(this.output(e)),i.start(),n.start(),{key:t,nodes:[s,r],sources:[i,n],volumeParam:r.gain,gainScale:.22,pitch:[{param:i.frequency,ratio:1},{param:s.gain,ratio:.012}]}}buildRumble(e,t){const i=e.createBufferSource();i.buffer=this.brownNoise(e),i.loop=!0;const n=e.createBiquadFilter();n.type="lowpass",n.Q.value=.8;const s=e.createGain();return s.gain.value=0,i.connect(n).connect(s).connect(this.output(e)),i.start(),{key:t,nodes:[n,s],sources:[i],volumeParam:s.gain,gainScale:1.4,pitch:[{param:n.frequency,ratio:1}]}}buildCrackle(e,t){const i=e.createBufferSource();i.buffer=this.whiteNoise(e),i.loop=!0;const n=e.createBiquadFilter();n.type="bandpass",n.Q.value=1.2;const s=e.createGain();s.gain.value=0;const r=e.createBufferSource();r.buffer=this.crackleEnvelope(e),r.loop=!0;const a=e.createGain();return a.gain.value=0,r.connect(a).connect(s.gain),i.connect(n).connect(s).connect(this.output(e)),i.start(),r.start(),{key:t,nodes:[n,s,a],sources:[i,r],volumeParam:a.gain,gainScale:.9,pitch:[{param:n.frequency,ratio:4}]}}async buildRecordedVoice(e,t,i){const n=this.context;if(!n)return;let s;try{s=await this.getBuffer(e)}catch(o){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",o);return}if(i!==this.voiceToken||!this.context)return;const r=n.createBufferSource();r.buffer=s,r.loop=!0;const a=n.createGain();a.gain.value=0,r.connect(a).connect(this.output(n)),r.start(),this.voice={key:t,nodes:[a],sources:[r],volumeParam:a.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let i=this.buffers.get(e);return i||(i=fetch(e).then(n=>{if(!n.ok)throw new Error(`Audio fetch failed (${n.status}): ${e}`);return n.arrayBuffer()}).then(n=>t.decodeAudioData(n)),this.buffers.set(e,i)),i}whiteNoise(e){if(!this.noiseBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*gy),e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;this.noiseBuffer=t}return this.noiseBuffer}brownNoise(e){const t=e.createBuffer(1,Math.floor(e.sampleRate*gy),e.sampleRate),i=t.getChannelData(0);let n=0,s=0;for(let r=0;r<i.length;r++)n=(n+.02*(Math.random()*2-1))/1.02,i[r]=n,s=Math.max(s,Math.abs(n));if(s>0)for(let r=0;r<i.length;r++)i[r]/=s;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*vy),e.sampleRate),i=t.getChannelData(0),n=Math.max(1,Math.floor(UA*e.sampleRate)),s=Math.round(vy*FA);for(let r=0;r<s;r++){const a=Math.floor(Math.random()*i.length),o=.4+Math.random()*.6;for(let c=0;c<n&&a+c<i.length;c++)i[a+c]=Math.max(i[a+c],o*(1-c/n))}this.crackleBuffer=t}return this.crackleBuffer}stopVoice(){const e=this.voice,t=this.context;if(this.voice=void 0,!e||!t)return;const i=t.currentTime;e.volumeParam.cancelScheduledValues(i),e.volumeParam.setTargetAtTime(0,i,my/3);const n=i+my;for(const s of e.sources){s.onended=()=>{s.disconnect();for(const r of e.nodes)r.disconnect()};try{s.stop(n)}catch{s.disconnect();for(const r of e.nodes)r.disconnect()}}}}class qe{static svg(e){return`<svg viewBox="0 0 24 24" width="1.5em" height="1.5em" aria-hidden="true" focusable="false"><path d="${e}" fill="currentColor"/></svg>`}static outline(e){return`<svg viewBox="0 0 24 24" width="1.5em" height="1.5em" aria-hidden="true" focusable="false"><path d="${e}" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" stroke-linecap="round"/></svg>`}static solid(e){return`<svg viewBox="0 0 24 24" width="1.5em" height="1.5em" aria-hidden="true" focusable="false"><path d="${e}" fill="currentColor" fill-rule="evenodd"/></svg>`}static BOOKMARK="M6 3.5h12a.5.5 0 0 1 .5.5v16.5L12 16.8l-6.5 3.7V4a.5.5 0 0 1 .5-.5z";static PICTURE="M5 4h14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z";static PICTURE_HILLS="M4.5 17.5l4.5-6 3 4 2.5-3 5 5z";static PIN="M12 21.5s-6.5-5.7-6.5-10.8a6.5 6.5 0 0 1 13 0c0 5.1-6.5 10.8-6.5 10.8z";static PIN_HOLE="M12 8.4a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8z";static MILESTONES_OFF=qe.outline(qe.BOOKMARK);static MILESTONES_ON=qe.solid(qe.BOOKMARK);static PICTURES_OFF=qe.outline(`${qe.PICTURE}${qe.PICTURE_HILLS}`);static PICTURES_ON=qe.solid(`${qe.PICTURE}${qe.PICTURE_HILLS}`);static TRACE_LINE="M3 19.5l2.2-2.2 1.4 1.4L4.4 20.9zM8 14.5l2.2-2.2 1.4 1.4-2.2 2.2zM13 9.5l2.2-2.2 1.4 1.4-2.2 2.2z";static TRACE_MARK="M18.5 2.5l3.5 3.5-3.5 3.5-3.5-3.5z";static TRACES_OFF=qe.outline(`${qe.TRACE_LINE}${qe.TRACE_MARK}`);static TRACES_ON=qe.solid(`${qe.TRACE_LINE}${qe.TRACE_MARK}`);static MAP_OFF=qe.outline(`${qe.PIN}${qe.PIN_HOLE}`);static MAP_ON=qe.solid(`${qe.PIN}${qe.PIN_HOLE}`);static SHARE=qe.outline("M14 4l7 7-7 7v-4.2C8.6 13.8 5.4 15.6 3.5 19c.6-5.6 3.4-9.6 10.5-10.8V4z");static FOLDED=qe.svg("M15.4 7.4 14 6l-6 6 6 6 1.4-1.4L10.8 12z");static UNFOLDED=qe.svg("M8.6 16.6 10 18l6-6-6-6-1.4 1.4 4.6 4.6z");static PLAY=qe.svg("M7.4 5v14l11-7z");static PAUSE=qe.svg("M6 5h4v14H6zM14 5h4v14h-4z");static VOLUME=qe.svg("M2 9.5v5h3.5l4.5 4.5V5L5.5 9.5H2zm12.5 2.5A4 4 0 0 0 12.5 8.5v7A4 4 0 0 0 14.5 12zM12.5 4.2v2.1a6.5 6.5 0 0 1 0 11.4v2.1a8.6 8.6 0 0 0 0-15.6z");static MUTED=qe.svg("M2 9.5v5h3.5l4.5 4.5V5L5.5 9.5H2zM15 9.8l1.4-1.4 2.6 2.6 2.6-2.6 1.4 1.4-2.6 2.6 2.6 2.6-1.4 1.4-2.6-2.6-2.6 2.6-1.4-1.4 2.6-2.6z");static ENTER_FULLSCREEN=qe.svg("M5 5h5v2H7v3H5V5zm9 0h5v5h-2V7h-3V5zM5 14h2v3h3v2H5v-5zm12 0h2v5h-5v-2h3v-3z");static EXIT_FULLSCREEN=qe.svg("M8 5h2v5H5V8h3V5zm6 0h2v3h3v2h-5V5zM5 14h5v5H8v-3H5v-2zm9 0h5v2h-3v3h-2v-5z")}function al(l){return[...l].sort((e,t)=>e.t-t.t)}function em(l,e){let t;for(const i of al(l)){if(i.t>e)break;t=i}return t}const tm=Math.PI/180,zd=180/Math.PI;class fr{constructor(e,t,i){this.kind=e,this.canvasHeightPx=t,this.fovDeg=i;const n=i*tm/2;this.focalPx=e==="equidistant"?t/2/n:t/2/Math.tan(n)}focalPx;static of(e,t,i){return new fr(e.projection,t,i)}degToPx(e){const t=e*tm;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*zd:2*Math.atan(e/(2*this.focalPx))*zd}angleDegToRadiusPx(e){const t=e*tm;return this.kind==="equidistant"?this.focalPx*t:this.focalPx*Math.tan(t)}halfWidthAngleDeg(e){return this.radiusPxToAngleDeg(this.canvasHeightPx*e/2)}radiusPxToAngleDeg(e){return this.kind==="equidistant"?e/this.focalPx*zd:Math.atan(e/this.focalPx)*zd}ofBounds(e){return{widthDeg:this.pxToDeg(e.width),heightDeg:this.pxToDeg(e.height)}}toBoundsSize(e){return{width:this.degToPx(e.widthDeg),height:this.degToPx(e.heightDeg)}}widthPx(e){return this.degToPx(Mt.angularWidthDeg(e))}}class pn{static fovOf(e,t){return ft(e,t)?.fovDeg??Xt.fieldOfViewDeg(e.instrument)}static toAngular(e){this.eachKeyframe(e,(t,i)=>({...t,angular:i.ofBounds(t.bounds)}))}static toBounds(e){this.eachKeyframe(e,(t,i)=>{if(!t.angular)return t;const{width:n,height:s}=i.toBoundsSize(t.angular),r={x:t.bounds.x+(t.bounds.width-n)/2,y:t.bounds.y+(t.bounds.height-s)/2,width:n,height:s};if(t.kind!=="polygon")return{...t,bounds:r};const a=t.bounds.width===0?1:n/t.bounds.width,o=t.bounds.height===0?1:s/t.bounds.height;return{...t,bounds:r,points:t.points.map(c=>({x:c.x*a,y:c.y*o}))}})}static toAim(e){this.eachKeyframe(e,(t,i,n)=>{const s=t.aim&&this.positionOf(e,t.aim,t.bounds,i,n);if(s&&Math.abs(s.x-t.bounds.x)<pn.UNMOVED_PX&&Math.abs(s.y-t.bounds.y)<pn.UNMOVED_PX)return t;const r=this.aimFrom(e,i,n,t.bounds);return r?{...t,aim:r}:t})}static UNMOVED_PX=.01;static aimOf(e,t,i){const n=fr.of(e.instrument,Mt.CANVAS_HEIGHT_PX,this.fovOf(e,t));return this.aimFrom(e,n,ft(e,t),i)}static aimFrom(e,t,i,n){if(i?.headingDeg===void 0)return;const s=this.frameCentre(e),r=t.radiusPxToAngleDeg(n.x+n.width/2-s.x),a=t.radiusPxToAngleDeg(s.y-(n.y+n.height/2));return{azimuthDeg:((i.headingDeg+r)%360+360)%360,altitudeDeg:i.pitchDeg+a}}static toPosition(e){this.eachKeyframe(e,(t,i,n)=>{const s=t.aim&&this.positionOf(e,t.aim,t.bounds,i,n);return s?{...t,bounds:{...t.bounds,...s}}:t})}static positionOf(e,t,i,n,s){if(s?.headingDeg===void 0)return;const r=this.frameCentre(e),a=this.shortestArc(t.azimuthDeg-s.headingDeg),o=t.altitudeDeg-s.pitchDeg;return{x:r.x+this.offAxisPx(n,a)-i.width/2,y:r.y-this.offAxisPx(n,o)-i.height/2}}static frameCentre(e){return{x:Xt.frameWidthPx(e.instrument,Mt.CANVAS_HEIGHT_PX)/2,y:Mt.CANVAS_HEIGHT_PX/2}}static shortestArc(e){return(e%360+540)%360-180}static offAxisPx(e,t){return Math.abs(t)>=pn.OFF_CANVAS_DEG?Math.sign(t)*pn.OFF_CANVAS_PX:e.angleDegToRadiusPx(t)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(e,t,i){const n=Xt.frameWidthPx(t,Mt.CANVAS_HEIGHT_PX)/2,s=Xt.frameWidthPx(e.instrument,Mt.CANVAS_HEIGHT_PX)/2,r=Mt.CANVAS_HEIGHT_PX/2;for(const a of[...e.timeline.allKeyframes]){const o=this.fovOf(e,a.t),c=i?.get(a.t)??o,h=fr.of(t,Mt.CANVAS_HEIGHT_PX,c),d=fr.of(e.instrument,Mt.CANVAS_HEIGHT_PX,o);e.timeline.addKeyframe(a.t,a.shapes.map(u=>{const{bounds:p}=u.shape,f=p.x+p.width/2-n,g=p.y+p.height/2-r,m=Math.hypot(f,g),v=m===0?1:d.angleDegToRadiusPx(h.radiusPxToAngleDeg(m))/m,b={...p,x:s+f*v-p.width/2,y:r+g*v-p.height/2};return{...u,shape:{...u.shape,bounds:b}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:i}=e;for(const n of[...i.allKeyframes]){const s=ft(e,n.t),r=this.fovOf(e,n.t),a=fr.of(e.instrument,Mt.CANVAS_HEIGHT_PX,r);i.addKeyframe(n.t,n.shapes.map(o=>({...o,shape:t(o.shape,a,s)})))}}}function H0(l){return ts.restore(G0(l),l.provenance)}function G0(l){return pn.toAngular(l),pn.toAim(l),{version:1,id:l.id,time:l.event.time,endTime:l.event.endTime,durationSeconds:l.event.durationSeconds,durationText:l.event.durationText,utcOffsetHours:l.event.utcOffsetHours,timeZone:l.event.timeZone,place:l.event.place,observer:l.observer,account:l.account,sources:l.sources?.length?l.sources:void 0,sway:l.sway,vehicle:l.vehicle,description:l.event.description,image:l.event.image,summary:l.event.summary,summaryTime:l.event.summaryTime,tags:l.event.tags,timeline:l.timeline.toJSON(),observerTrack:l.observerTrack.toJSON(),weatherTrack:l.weatherTrack.toJSON(),soundTrack:l.soundTrack.toJSON(),weather:l.weather,decor:l.decor,milestones:l.milestones.length>0?l.milestones:void 0,references:l.references.length>0?l.references:void 0,roads:l.roads.length>0?l.roads:void 0,traces:l.traces.length>0?l.traces:void 0,weatherSource:l.weatherSource,instrument:l.instrumentId,exposureSeconds:l.exposureSeconds,iso:l.iso,lightPollution:l.lightPollution,observerMap:l.observerMap,interpretation:l.interpretation,frozen:l.frozen}}function p2(l){const{recording:e,provenance:t}=ts.strip(l),i=zA(e);return i.provenance=t,i}function zA(l){const e=[],t=new Cf({eventType:"sighting",time:l.time,endTime:l.endTime,durationSeconds:l.durationSeconds,durationText:l.durationText,utcOffsetHours:l.utcOffsetHours,timeZone:l.timeZone,place:l.place,description:l.description,image:l.image,summary:l.summary,summaryTime:l.summaryTime,tags:l.tags},Tf.fromJSON(l.timeline,e),l.observerTrack?rd.fromJSON(l.observerTrack):new rd,l.weatherTrack?ad.fromJSON(l.weatherTrack):new ad,l.soundTrack?od.fromJSON(l.soundTrack):new od,l.observer&&{id:l.observer.id,title:l.observer.title,lastName:l.observer.lastName,firstNames:l.observer.firstNames},l.id,l.weather,l.decor??[],l.weatherSource,l.instrument,l.exposureSeconds??l.observerTrack?.keyframes.map(i=>i.pose.exposureSeconds).find(i=>i!==void 0),al(l.milestones??[]),l.references??[],l.roads??[],l.traces??[]);return t.account=l.account,t.sources=l.sources,t.sway=l.sway,t.iso=l.iso,t.lightPollution=l.lightPollution,t.observerMap=l.observerMap,t.vehicle=l.vehicle,t.interpretation=l.interpretation,t.frozen=l.frozen,t.loadIssues=e,pn.toBounds(t),pn.toPosition(t),t}class Xc{static EN={"unknown-key":"{path}: unknown key, ignored","wrong-type":"{path}: expected {expected}, found {found}","not-in-list":"{path}: {found} is not one of {expected}",defaulted:"{path}: missing, {expected} used",unplaced:"{path}: no aim (and no bounds), drawn at the middle of the view",unsized:"{path}: no angular size (and no bounds), drawn one pixel wide","pixels-over-angles":"{path}: bounds restated without aim or angular, so the shape follows the pixels"};static text(e,t=Xc.EN){return t[e.kind].replace("{path}",e.path).replace("{expected}",e.expected??"").replace("{found}",e.found??"")}static warn(e,t){if(e.length===0)return;const i=e.map(n=>` ${Xc.text(n)}`);console.warn(`UFO@home: ${e.length} problem(s) in ${t??"this recording"}:
3289
3289
  ${i.join(`
3290
3290
  `)}`)}}class er{constructor(e){this.format=e}issues(e){const t=[];return this.object(e,this.format,"",t),t}object(e,t,i,n){if(!er.isObject(e)){n.push({kind:"wrong-type",path:i||"(the file)",expected:"object",found:er.typeOf(e)});return}for(const[s,r]of Object.entries(e)){const a=i?`${i}.${s}`:s,o=t[s];o?this.field(r,o,a,n):n.push({kind:"unknown-key",path:a})}}field(e,t,i,n){if(er.isStated(e)){this.field(e.value,t,i,n);return}if(!(t.loose||e===void 0||e===null))switch(t.type){case"enum":(typeof e!="string"||!t.values?.includes(e))&&n.push({kind:"not-in-list",path:i,expected:(t.values??[]).map(s=>`"${s}"`).join(", "),found:JSON.stringify(e)});return;case"object":t.fields?this.object(e,t.fields,i,n):er.isObject(e)||n.push({kind:"wrong-type",path:i,expected:"object",found:er.typeOf(e)});return;case"array":Array.isArray(e)?t.fields&&e.forEach((s,r)=>{const a=er.isStated(s)?s.value:s;this.object(a,t.fields,`${i}.${r}`,n)}):n.push({kind:"wrong-type",path:i,expected:"array",found:er.typeOf(e)});return;default:typeof e!==t.type&&n.push({kind:"wrong-type",path:i,expected:t.type,found:er.typeOf(e)})}}static isObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}static isStated(e){return er.isObject(e)&&"value"in e&&Object.keys(e).every(t=>t==="value"||t==="basis"||t==="rationale")}static typeOf(e){return Array.isArray(e)?"array":e===null?"null":typeof e}}class ar{static PIXELS_PER_INSTANT=2;static INSTANTS_PER_FLASH=2;static MAX_INSTANTS=512;static windowEndingAt(e,t){const i=Number.isFinite(t)?Math.max(0,t):0,n=Math.max(0,e-i*1e3),s=Math.max(0,e-n);return{fromMs:n,toMs:e,ms:s,seconds:s/1e3}}static instants(e,t,i,n,s){if(!(n>0))return 1;const r=s>0?ar.travelDegOver(e,t,i,i+n*1e3)/s/ar.PIXELS_PER_INSTANT:0,a=ar.flashesOver(e,n)*ar.INSTANTS_PER_FLASH,o=Math.ceil(Math.max(r,a));return o<2?1:Math.min(ar.MAX_INSTANTS,o)}static travelDegOver(e,t,i,n){let s=0;for(const r of e){if(!r.track||r.track.length<2)continue;const a=ar.directionAt(r,t,i),o=ar.directionAt(r,t,n),c=Math.min(1,Math.max(-1,a.x*o.x+a.y*o.y+a.z*o.z));s=Math.max(s,Math.acos(c)*180/Math.PI)}return s}static flashesOver(e,t){let i=0;for(const n of e)for(const s of n.lights??[])s.pattern.kind==="flash"&&(i=Math.max(i,s.pattern.perMinute));return i/60*t}static directionAt(e,t,i){const n=pr(e,i),s=n.eastM,r=n.altitudeM-t,a=n.northM,o=Math.hypot(s,r,a);return o===0?{x:0,y:1,z:0}:{x:s/o,y:r/o,z:a/o}}}const f2={play:"Play",clickToPlay:"Click to play",pause:"Pause",noDuration:"No observation duration",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",mute:"Mute",unmute:"Unmute",noSound:"No sound recorded",moments:"Moments",moreControls:"More controls",fewerControls:"Fewer controls",showObserverMap:"Show where the observer was",hideObserverMap:"Hide where the observer was",mapImageryUnavailable:"Aerial imagery unavailable",zoomMapIn:"Zoom in",zoomMapOut:"Zoom out",fitMap:"Back to the whole path",placeObserverHere:"Put the observer here",showReferences:"Show the pictures of the place",hideReferences:"Hide the pictures of the place",showTraces:"Show the investigator's lines",hideTraces:"Hide the investigator's lines",referenceOpacity:"Picture opacity",referencesMenu:"Pictures of the place",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",observerHere:"The observer, here",decorHere:"Scenery",recordingIssues:"{count} problem(s) in this recording: it may not play as its author meant",issueTemplates:Xc.EN},HA=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:f2},Symbol.toStringTag,{value:"Module"})),m2=Math.PI/180,GA=180/Math.PI,VA=256,g2=19,WA=1.25;function v2(l,e){return 156543.03392*Math.cos(l*m2)/2**e}function y2(l,e){return v2(l,e)*VA}function $A(l,e){let t=0;for(;t<g2&&y2(l,t+1)>=e;)t++;return t}function b2(l,e){return y2(l,e)}function XA(l,e){const t=e*WA;let i=0;for(;i<g2&&v2(l,i)>t;)i++;return i}function Ju(l){const e=l*m2;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function w2(l,e,t){const i=2**t;return{x:(l+180)/360*i,y:Ju(e)*i}}function M2(l,e,t){const i=Ju(l.north),n=Ju(l.south);return{x:(e-l.west)/(l.east-l.west),y:(Ju(t)-i)/(n-i)}}function YA(l,e,t){const{x:i,y:n}=w2(l,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function KA(l){const e=2**l.z,t=l.x/e*360-180,i=(l.x+1)/e*360-180,n=yy(l.y,e);return{south:yy(l.y+1,e),north:n,west:t,east:i}}function yy(l,e){const t=l/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*GA}function x2(l,e,t,i){const n=YA(l,e,t),s=Math.floor(i/2),r=[];for(let c=-s;c<=s;c++)for(let h=-s;h<=s;h++)r.push({x:n.x+h,y:n.y+c,z:t});const a=r.map(KA),o={south:Math.min(...a.map(c=>c.south)),north:Math.max(...a.map(c=>c.north)),west:Math.min(...a.map(c=>c.west)),east:Math.max(...a.map(c=>c.east))};return{tiles:r,bounds:o}}const qA="#2ec4ff";class Do{constructor(e){this.ctx=e}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(e,t){let i,n=1/0;for(const s of this.targets){const r=Math.hypot(s.x-e,s.y-t);r>s.radius||r>n||(i=s,n=r)}return i}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(e){const{ctx:t}=this;t.save(),this.targets=[],t.clearRect(0,0,this.width,this.height),this.paintGround(e),this.paintNight(e.nightFraction),this.paintPath(e),this.paintTraces(e),e.position?(this.paintCone(e,e.position),this.paintDecor(e),this.paintMarkers(e),this.paintObserver(e,e.position)):(this.paintDecor(e),this.paintMarkers(e)),this.paintNorth(),this.paintFooter(e.bounds,e.attribution),t.restore()}toCanvas(e,t,i){const n=M2(e,i,t);return{x:n.x*this.width,y:n.y*this.height}}paintGround(e){const{ctx:t}=this;t.fillStyle="#1d2321",t.fillRect(0,0,this.width,this.height);const i=e.imagery;if(!i)return;const n=this.toCanvas(e.bounds,i.bounds.north,i.bounds.west),s=this.toCanvas(e.bounds,i.bounds.south,i.bounds.east);t.drawImage(i.source,n.x,n.y,s.x-n.x,s.y-n.y)}paintNight(e=0){if(e<=0)return;const{ctx:t}=this;t.save(),t.fillStyle=`rgba(6, 12, 30, ${Math.min(e,1)*.78})`,t.fillRect(0,0,this.width,this.height),t.restore()}paintDecor(e){const{ctx:t}=this;for(const i of e.decor){const n=this.toCanvas(e.bounds,i.lat,i.lng);if(i.label&&this.targets.push({kind:"decor",label:i.label,lat:i.lat,lng:i.lng,x:n.x,y:n.y,radius:7}),t.beginPath(),t.rect(n.x-3.5,n.y-3.5,7,7),t.fillStyle="rgba(120, 220, 255, 0.85)",t.fill(),t.lineWidth=1.5,t.strokeStyle="rgba(0, 0, 0, 0.7)",t.stroke(),i.headingDeg===void 0)continue;const s=(i.headingDeg-90)*Math.PI/180;t.beginPath(),t.moveTo(n.x,n.y),t.lineTo(n.x+Math.cos(s)*10,n.y+Math.sin(s)*10),t.lineWidth=3,t.strokeStyle="rgba(0, 0, 0, 0.6)",t.stroke(),t.lineWidth=1.5,t.strokeStyle="rgba(120, 220, 255, 0.9)",t.stroke()}}paintPath(e){if(!e.path.moved)return;const{ctx:t}=this,i=e.path.points.map(n=>this.toCanvas(e.bounds,n.lat,n.lng));t.beginPath(),t.moveTo(i[0].x,i[0].y);for(const n of i.slice(1))t.lineTo(n.x,n.y);t.lineJoin="round",t.lineCap="round",t.strokeStyle="rgba(0, 0, 0, 0.65)",t.lineWidth=5,t.stroke(),t.strokeStyle="rgba(255, 255, 255, 0.9)",t.lineWidth=2,t.stroke()}paintTraces(e){const{ctx:t}=this;for(const i of e.traces??[]){const n=i.points.map(r=>this.toCanvas(e.bounds,r.lat,r.lng));if(n.length===0)continue;const s=i.color??qA;if(i.kind==="point")t.beginPath(),t.moveTo(n[0].x,n[0].y-6),t.lineTo(n[0].x+6,n[0].y),t.lineTo(n[0].x,n[0].y+6),t.lineTo(n[0].x-6,n[0].y),t.closePath(),t.fillStyle=s,t.fill(),t.lineWidth=1.5,t.strokeStyle="rgba(0, 0, 0, 0.8)",t.stroke();else{t.beginPath(),t.moveTo(n[0].x,n[0].y);for(const r of n.slice(1))t.lineTo(r.x,r.y);i.kind==="polygon"&&t.closePath(),t.lineJoin="round",t.setLineDash([]),t.strokeStyle="rgba(0, 0, 0, 0.65)",t.lineWidth=4.5,t.stroke(),t.setLineDash([9,5]),t.strokeStyle=s,t.lineWidth=2.5,t.stroke(),t.setLineDash([])}if(i.label){t.font="11px sans-serif",t.textAlign="left",t.textBaseline="middle";let r=n[0];if(i.kind==="line"&&e.position){const a=this.toCanvas(e.bounds,e.position.lat,e.position.lng);r=n.reduce((o,c)=>Math.hypot(c.x-a.x,c.y-a.y)>Math.hypot(o.x-a.x,o.y-a.y)?c:o)}this.paintOutlinedText(i.label,r.x+9,r.y-9)}}}paintCone(e,t){const{headingDeg:i,coneHalfAngleDeg:n}=e;if(i===void 0||n===void 0)return;const{ctx:s}=this,r=this.toCanvas(e.bounds,t.lat,t.lng),a=Math.hypot(this.width,this.height),o=(i-90)*Math.PI/180,c=n*Math.PI/180;s.save(),s.beginPath(),s.moveTo(r.x,r.y),s.arc(r.x,r.y,a,o-c,o+c),s.closePath();const h=s.createRadialGradient(r.x,r.y,0,r.x,r.y,a);h.addColorStop(0,"rgba(255, 224, 130, 0.55)"),h.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),h.addColorStop(1,"rgba(255, 224, 130, 0)"),s.fillStyle=h,s.fill(),s.restore()}paintMarkers(e){const{ctx:t}=this;t.textAlign="center",t.textBaseline="middle",t.font="bold 10px sans-serif";for(const i of[...e.markers].sort((n,s)=>Number(n.current)-Number(s.current))){const n=this.toCanvas(e.bounds,i.lat,i.lng);this.targets.push({kind:"milestone",label:i.note?`${i.label} — ${i.note}`:i.label,lat:i.lat,lng:i.lng,t:i.t,x:n.x,y:n.y,radius:Do.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,Do.MARKER_RADIUS_PX,0,Math.PI*2),t.fillStyle=i.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",t.fill(),t.lineWidth=2,t.strokeStyle="rgba(255, 255, 255, 0.9)",t.stroke(),t.fillStyle=i.current?"#1d2321":"#fff",t.fillText(i.label,n.x,n.y)}}paintObserver(e,t){const{ctx:i}=this,n=this.toCanvas(e.bounds,t.lat,t.lng);e.observerLabel&&this.targets.push({kind:"observer",label:e.observerLabel,lat:t.lat,lng:t.lng,x:n.x,y:n.y,radius:8});const s=e.markers.some(r=>{if(!r.current)return!1;const a=rt(r.lat,r.lng,t.lat,t.lng);return Math.hypot(a.x,a.z)<=Do.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?Do.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),s||(i.fillStyle="#ff5a3c",i.fill()),i.lineWidth=4,i.strokeStyle="rgba(0, 0, 0, 0.5)",i.stroke(),i.lineWidth=s?3:2.5,i.strokeStyle=s?"#ff5a3c":"#fff",i.stroke()}paintNorth(){const{ctx:e}=this,t=this.width-14,i=16;e.beginPath(),e.moveTo(t,i-9),e.lineTo(t-4.5,i+5),e.lineTo(t,i+2),e.lineTo(t+4.5,i+5),e.closePath(),e.fillStyle="rgba(255, 255, 255, 0.92)",e.strokeStyle="rgba(0, 0, 0, 0.6)",e.lineWidth=1,e.fill(),e.stroke(),e.font="bold 9px sans-serif",e.textAlign="center",e.textBaseline="top",e.strokeText("N",t,i+6),e.fillText("N",t,i+6)}paintFooter(e,t){const{ctx:i}=this;i.font="9px sans-serif",i.textBaseline="bottom";const n=t?this.wrap(t,this.width-20):[];let s=this.height-4;i.textAlign="right";for(const p of n.slice().reverse())this.paintOutlinedText(p,this.width-6,s),s-=11;const r=(e.south+e.north)/2,o=Math.abs(rt(r,e.east,r,e.west).x)/this.width,c=Do.SCALE_STEPS_M.find(p=>p/o<=this.width/3);if(c===void 0)return;const h=c/o,d=8,u=s-3;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.6)",i.beginPath(),i.moveTo(d,u),i.lineTo(d+h,u),i.stroke(),i.lineWidth=1.5,i.strokeStyle="rgba(255, 255, 255, 0.95)",i.stroke(),i.textAlign="left",this.paintOutlinedText(c>=1e3?`${c/1e3} km`:`${c} m`,d,u-3)}paintOutlinedText(e,t,i){const{ctx:n}=this;n.lineWidth=3,n.strokeStyle="rgba(0, 0, 0, 0.75)",n.strokeText(e,t,i),n.fillStyle="#fff",n.fillText(e,t,i)}wrap(e,t){const i=[];let n="";for(const s of e.split(" ")){const r=n?`${n} ${s}`:s;n&&this.ctx.measureText(r).width>t?(i.push(n),n=s):n=r}return n&&i.push(n),i}}class qt{static MIN_ZOOM=.25;static MAX_ZOOM=64;static MIN_SPAN_M=400;static MARGIN=.6;static IMAGERY_PX=768;static fitted(e,t){return qt.including(e.boundsAround(qt.MIN_SPAN_M,qt.MARGIN),t)}zoom=1;centerX=.5;centerY=.5;get changed(){return this.zoom!==1||this.centerX!==.5||this.centerY!==.5}get zoomFactor(){return this.zoom}reset(){this.zoom=1,this.centerX=.5,this.centerY=.5}zoomAt(e,t,i){const n=Math.min(qt.MAX_ZOOM,Math.max(qt.MIN_ZOOM,this.zoom*i)),s=this.centerX+(e-.5)/this.zoom,r=this.centerY+(t-.5)/this.zoom;this.centerX=s-(e-.5)/n,this.centerY=r-(t-.5)/n,this.zoom=n}panBy(e,t){this.centerX-=e/this.zoom,this.centerY-=t/this.zoom}refit(e,t){if(!this.changed)return;const i=this.boundsWithin(e),n=qt.mercatorY01(t.north),s=qt.mercatorY01(t.south),r=qt.mercatorY01(i.north),a=qt.mercatorY01(i.south);this.zoom=(t.east-t.west)/(i.east-i.west),this.centerX=((i.west+i.east)/2-t.west)/(t.east-t.west),this.centerY=((r+a)/2-n)/(s-n)}keepInView(e,t,i){if(!this.changed)return!1;const n=this.boundsWithin(e),s=qt.mercatorY01(n.north),r=qt.mercatorY01(n.south),a=(t.lng-n.west)/(n.east-n.west),o=(qt.mercatorY01(t.lat)-s)/(r-s);return a>=i&&a<=1-i&&o>=i&&o<=1-i?!1:(this.panBy(.5-a,.5-o),!0)}boundsWithin(e){if(!this.changed)return e;const t=.5/this.zoom,i=qt.mercatorY01(e.north),n=qt.mercatorY01(e.south),s=e.east-e.west;return{west:e.west+(this.centerX-t)*s,east:e.west+(this.centerX+t)*s,north:qt.latitudeAt(i+(this.centerY-t)*(n-i)),south:qt.latitudeAt(i+(this.centerY+t)*(n-i))}}static including(e,t,i=3){const n=(e.north+e.south)/2,s=(e.east+e.west)/2,r=rt(n,e.east,n,s).x;let a=r;for(const o of t){const{x:c,z:h}=rt(o.lat,o.lng,n,s),d=Math.max(Math.abs(c),Math.abs(h));d<=r*i&&(a=Math.max(a,d*1.15))}return a<=r?e:{north:Ls(0,-a,n,s).lat,south:Ls(0,a,n,s).lat,east:Ls(a,0,n,s).lng,west:Ls(-a,0,n,s).lng}}static expand(e,t){const i=e.east-e.west,n=e.north-e.south;return{west:e.west-i*t,east:e.east+i*t,south:e.south-n*t,north:e.north+n*t}}static pointAt(e,t,i){const n=qt.mercatorY01(e.north),s=qt.mercatorY01(e.south);return{lat:qt.latitudeAt(n+i*(s-n)),lng:e.west+t*(e.east-e.west)}}static mercatorY01(e){const t=e*Math.PI/180;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}static latitudeAt(e){return Math.atan(Math.sinh(Math.PI*(1-2*e)))*180/Math.PI}}const Us=256,by=3,ZA=32768;class S2{fetchImpl;tileUrlTemplate;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=e.tileUrlTemplate??"https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"}async getElevationGrid(e,t){const i=(e.south+e.north)/2,n=(e.west+e.east)/2,s=Math.abs(rt(i,e.east,i,e.west).x),r=Math.abs(rt(e.north,n,e.south,n).z),a=$A(i,Math.max(s,r)),{tiles:o}=x2(n,i,a,by),c=by*Us,h=new Float32Array(c*c),d=o[0];await Promise.all(o.map(p=>this.decodeTileInto(p,d,h,c)));const u=new Float32Array(t.width*t.height);for(let p=0;p<t.height;p++){const f=e.north+(e.south-e.north)*(p/(t.height-1));for(let g=0;g<t.width;g++){const m=e.west+(e.east-e.west)*(g/(t.width-1));u[p*t.width+g]=this.sampleAt(f,m,a,d,h,c)}}return{heights:u,width:t.width,height:t.height,bounds:e}}async decodeTileInto(e,t,i,n){const s=this.tileUrlTemplate.replace("{z}",String(e.z)).replace("{x}",String(e.x)).replace("{y}",String(e.y)),r=await this.fetchImpl(s);if(!r.ok)throw new Error(`Elevation tile fetch failed (${r.status}): ${s}`);const a=await createImageBitmap(await r.blob()),o=document.createElement("canvas");o.width=Us,o.height=Us;const c=o.getContext("2d");if(!c)throw new Error("2D canvas context unavailable");c.drawImage(a,0,0);const{data:h}=c.getImageData(0,0,Us,Us),d=(e.x-t.x)*Us,u=(e.y-t.y)*Us;for(let p=0;p<Us;p++)for(let f=0;f<Us;f++){const g=(p*Us+f)*4,m=h[g]*256+h[g+1]+h[g+2]/256-ZA,v=d+f,b=u+p;i[b*n+v]=m}}sampleAt(e,t,i,n,s,r){const{x:a,y:o}=w2(t,e,i),c=(a-n.x)*Us,h=(o-n.y)*Us,d=Math.max(0,Math.min(r-1,Math.floor(c))),u=Math.max(0,Math.min(r-1,Math.floor(h))),p=Math.min(r-1,d+1),f=Math.min(r-1,u+1),g=c-d,m=h-u,v=s[u*r+d],b=s[u*r+p],w=s[f*r+d],y=s[f*r+p],M=v+(b-v)*g,S=w+(y-w)*g;return M+(S-M)*m}}class Of{constructor(e,t,i,n){this.width=e,this.height=t,this.samples=i,this.values=n}at(e,t,i=0){return this.values[(t*this.width+e)*this.samples+i]}static parse(e){const t=new DataView(e);if(e.byteLength<8||t.getUint16(0)!==18761||t.getUint16(2,!0)!==42)throw new Error("Not a little-endian TIFF");const i=t.getUint32(4,!0),n=t.getUint16(i,!0),s=new Map;for(let v=0;v<n;v++){const b=i+2+v*12;s.set(t.getUint16(b,!0),{type:t.getUint16(b+2,!0),count:t.getUint32(b+4,!0),value:t.getUint32(b+8,!0)})}const r=(v,b)=>{const w=s.get(v);if(w===void 0){if(b===void 0)throw new Error(`TIFF without tag ${v}`);return b}return w.type===3?w.value&65535:w.value},a=v=>{const b=s.get(v);if(b===void 0)throw new Error(`TIFF without tag ${v}`);const w=b.type===3?2:4,y=[];if(b.count*w<=4){const M=new DataView(new ArrayBuffer(4));M.setUint32(0,b.value,!0);for(let S=0;S<b.count;S++)y.push(w===2?M.getUint16(S*2,!0):M.getUint32(0,!0))}else for(let M=0;M<b.count;M++)y.push(w===2?t.getUint16(b.value+M*2,!0):t.getUint32(b.value+M*4,!0));return y};if(r(259,1)!==1)throw new Error("Compressed TIFF");if(s.has(322))throw new Error("Tiled TIFF");const o=r(256),c=r(257),h=r(277,1),d=a(258)[0],u=r(339,1)===3;if(!(u&&d===32||!u&&d===8))throw new Error(`Unsupported TIFF samples: ${d} bits`);const p=a(273),f=a(279),g=new Uint8Array(o*c*h*(d/8));let m=0;for(let v=0;v<p.length&&m<g.length;v++){const b=Math.min(f[v],g.length-m);g.set(new Uint8Array(e,p[v],b),m),m+=b}if(m<g.length)throw new Error("Truncated TIFF");return new Of(o,c,h,u?new Float32Array(g.buffer):g)}}class jA{constructor(e,t,i,n,s){this.heights=e,this.plants=t,this.buildings=i,this.size=n,this.metresPerPixel=s}structureAt(e,t){const i=Math.floor(e/this.metresPerPixel+this.size/2),n=Math.floor(this.size/2-t/this.metresPerPixel);if(i<0||n<0||i>=this.size||n>=this.size)return;const s=n*this.size+i;if(!(this.plants[s]||this.buildings[s]))return this.heights[s]}tallestStructureNear(e,t,i){let n;const s=Math.ceil(i/this.metresPerPixel);for(let r=-s;r<=s;r++)for(let a=-s;a<=s;a++){if(Math.hypot(a,r)*this.metresPerPixel>i)continue;const o=this.structureAt(e+a*this.metresPerPixel,t+r*this.metresPerPixel);o!==void 0&&(n===void 0||o>n)&&(n=o)}return n}}class _e{static METRES_PER_DEGREE=111320;static MIN_BRIDGE_M=4;static DEFAULT_DECK_M=6;static FOOTBRIDGE_DECK_M=2.5;static DECK_MARGIN_M=2.4;static JOIN_M=1;static JOIN_DEG=25;static RAIL_MATCH_M=6;static ROAD_MATCH_M=12;static DECK_SEEN_M=1.5;static DECK_STEP_M=.5;static DECK_GAP_M=2;static DECK_REACH_M=20;static DECK_MEETS_M=.3;static DECK_BEARING_M=4;static DEFAULT_DECK_HEIGHT_M=4;static MIN_POLE_M=6;static MAX_POLE_M=70;static MAX_SPAN_M=800;static MAX_LINES=16;static BY_VOLTAGE=[{upToKv:25,poleM:11,dropM:1.2,spreadM:.8},{upToKv:70,poleM:20,dropM:3,spreadM:3},{upToKv:100,poleM:22,dropM:3.5,spreadM:3.5},{upToKv:170,poleM:27,dropM:4.5,spreadM:4.5},{upToKv:250,poleM:32,dropM:5.5,spreadM:5.5},{upToKv:1e3,poleM:42,dropM:7,spreadM:7.5}];static toMetres(e,t){const[i,n]=e;return{east:(i-t.lng)*_e.METRES_PER_DEGREE*Math.cos(t.lat*Math.PI/180),north:(n-t.lat)*_e.METRES_PER_DEGREE}}static path(e,t){return e.geometry.type!=="LineString"?[]:e.geometry.coordinates.map(i=>_e.toMetres(i,t))}static bearing(e,t){return(Math.atan2(t.east-e.east,t.north-e.north)*180/Math.PI+360)%360}static distanceToSegment(e,t,i){const n=i.east-t.east,s=i.north-t.north,r=n*n+s*s,a=r===0?0:Math.max(0,Math.min(1,((e.east-t.east)*n+(e.north-t.north)*s)/r));return Math.hypot(e.east-(t.east+a*n),e.north-(t.north+a*s))}static bridges(e,t,i,n){const s=e.elevatedRoads.map(o=>({path:_e.path(o,t),properties:o.properties})),r=(e.elevatedRailways??[]).map(o=>_e.path(o,t)),a=[];for(const o of _e.joined(e.bridges.map(c=>_e.path(c,t)))){const c=o[0],h=o[o.length-1],d=Math.hypot(h.east-c.east,h.north-c.north);if(d<_e.MIN_BRIDGE_M)continue;const u={east:(c.east+h.east)/2,north:(c.north+h.north)/2};if(Math.hypot(u.east,u.north)>i+d/2||_e.carriesRailway(c,h,r))continue;const[p,f]=_e.deckReach(c,h,n);a.push({eastM:_e.tenth((p.east+f.east)/2),northM:_e.tenth((p.north+f.north)/2),lengthM:_e.tenth(Math.hypot(f.east-p.east,f.north-p.north)),widthM:_e.roadOf(u,s).width,heightM:_e.tenth(_e.deckHeight(c,h,n)),headingDeg:_e.tenth(_e.bearing(c,h)%180),..._e.roadOf(u,s).name?{name:_e.roadOf(u,s).name}:{}})}return a.sort((o,c)=>Math.hypot(o.eastM,o.northM)-Math.hypot(c.eastM,c.northM)),a}static carriesRailway(e,t,i){const n={east:(e.east+t.east)/2,north:(e.north+t.north)/2},s=_e.bearing(e,t)%180;for(const r of i)for(let a=0;a+1<r.length;a++){if(_e.distanceToSegment(n,r[a],r[a+1])>_e.RAIL_MATCH_M)continue;const o=Math.abs(_e.bearing(r[a],r[a+1])%180-s);if(Math.min(o,180-o)<=_e.JOIN_DEG)return!0}return!1}static joined(e){const t=e.filter(i=>i.length>=2);for(let i=0;i<t.length;i++)for(let n=i+1;n<t.length;n++){const s=_e.join(t[i],t[n]);s!==void 0&&(t[i]=s,t.splice(n,1),n=i)}return t}static join(e,t){for(const i of[e,[...e].reverse()])for(const n of[t,[...t].reverse()]){const s=i[i.length-1],r=n[0];if(Math.hypot(s.east-r.east,s.north-r.north)>_e.JOIN_M)continue;const a=Math.abs(_e.bearing(i[0],s)-_e.bearing(r,n[n.length-1]));if(Math.min(a,360-a)<=_e.JOIN_DEG)return[...i,...n.slice(1)]}}static roadOf(e,t){let i;for(const a of t)for(let o=0;o+1<a.path.length;o++){const c=_e.distanceToSegment(e,a.path[o],a.path[o+1]);(i===void 0||c<i.distance)&&(i={distance:c,properties:a.properties})}if(i===void 0||i.distance>_e.ROAD_MATCH_M)return{width:_e.DEFAULT_DECK_M};const n=[i.properties.nom_voie_ban_gauche,i.properties.nom_voie_ban_droite,i.properties.cpx_toponyme_route_nommee].find(a=>typeof a=="string"&&a.length>0),s=Number(i.properties.largeur_de_chaussee),r=Number.isFinite(s)&&s>0?_e.tenth(s+_e.DECK_MARGIN_M):i.properties.nature==="Sentier"?_e.FOOTBRIDGE_DECK_M:_e.DEFAULT_DECK_M;return n?{width:r,name:n}:{width:r}}static deckReach(e,t,i){const n=Math.hypot(t.east-e.east,t.north-e.north);if(i===void 0||n===0)return[e,t];const s={east:(e.east+t.east)/2,north:(e.north+t.north)/2},r={east:(t.east-e.east)/n,north:(t.north-e.north)/n},a=u=>(i.structureAt(s.east+r.east*u,s.north+r.north*u)??0)>=_e.DECK_SEEN_M,o=u=>i.structureAt(s.east+r.east*u,s.north+r.north*u)??0,c=u=>{let p;for(let g=0;g<=n/2+_e.DECK_REACH_M;g+=_e.DECK_STEP_M)if(a(u*g))p=g;else if(p!==void 0&&g-p>_e.DECK_GAP_M)break;if(p===void 0)return n/2;let f=p;for(;f-p<_e.DECK_REACH_M&&o(u*(f+_e.DECK_STEP_M))>=_e.DECK_MEETS_M;)f+=_e.DECK_STEP_M;return Math.max(n/2,f+_e.DECK_BEARING_M)},h=c(-1),d=c(1);return[{east:s.east-r.east*h,north:s.north-r.north*h},{east:s.east+r.east*d,north:s.north+r.north*d}]}static deckHeight(e,t,i){if(i===void 0)return _e.DEFAULT_DECK_HEIGHT_M;const n=[],s=20;for(let r=4;r<=s-4;r++){const a=i.structureAt(e.east+(t.east-e.east)*r/s,e.north+(t.north-e.north)*r/s);a!==void 0&&a>=1.5&&n.push(a)}return n.length===0?_e.DEFAULT_DECK_HEIGHT_M:(n.sort((r,a)=>r-a),n[Math.floor(n.length/2)])}static lines(e,t,i,n){const s=[];for(const r of e.lines){const a=String(r.properties.voltage??""),o=_e.kilovolts(a);if(o===void 0)continue;const c=_e.BY_VOLTAGE.find(d=>o<=d.upToKv)??_e.BY_VOLTAGE[_e.BY_VOLTAGE.length-1],h=_e.path(r,t);for(let d=0;d+1<h.length;d++){const u=h[d],p=h[d+1],f=Math.hypot(p.east-u.east,p.north-u.north);if(f<5||f>_e.MAX_SPAN_M||_e.distanceToSegment({east:0,north:0},u,p)>i)continue;const g=_e.poleHeight(u,p,n)??c.poleM;s.push({eastM:_e.tenth((u.east+p.east)/2),northM:_e.tenth((u.north+p.north)/2),lengthM:_e.tenth(f),headingDeg:_e.tenth(_e.bearing(u,p)),heightM:_e.tenth(Math.max(4,g-c.dropM)),poleHeightM:_e.tenth(g),acrossM:[-c.spreadM,0,c.spreadM],voltage:a})}}return s.sort((r,a)=>Math.hypot(r.eastM,r.northM)-Math.hypot(a.eastM,a.northM)),s.slice(0,_e.MAX_LINES)}static kilovolts(e){const t=/([\d.,]+)\s*kV/i.exec(e);if(t===null)return;const i=Number(t[1].replace(",","."));return Number.isFinite(i)&&i>0?i:void 0}static poleHeight(e,t,i){if(i===void 0)return;const n=[e,t].map(s=>i.tallestStructureNear(s.east,s.north,3)).filter(s=>s!==void 0&&s>=_e.MIN_POLE_M&&s<=_e.MAX_POLE_M);return n.length===0?void 0:n.reduce((s,r)=>s+r,0)/n.length}static tenth(e){return Math.round(e*10)/10}}class Dt{static covers(e,t){return e>=41.2&&e<=51.3&&t>=-5.4&&t<=9.8}static WMS="https://data.geopf.fr/wms-r/wms";static WFS="https://data.geopf.fr/wfs/ows";static HEIGHT_LAYER="IGNF_LIDAR-HD_MNH_ELEVATION.ELEVATIONGRIDCOVERAGE.LAMB93";static CLASS_LAYER="IGNF_LIDAR-HD_CLASS";static PHOTO_LAYER="ORTHOIMAGERY.ORTHOPHOTOS";static MAX_PHOTO_PIXELS=1e3;static METRES_PER_PIXEL=.5;static MAX_RADIUS_M=3e3;static MAX_LIDAR_RADIUS_M=150;static NO_DATA=-9999;static METRES_PER_DEGREE=111320;fetchImpl;wms;wfs;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.wms=e.wmsEndpoint??Dt.WMS,this.wfs=e.wfsEndpoint??Dt.WFS}static radius(e){return Math.min(e,Dt.MAX_RADIUS_M)}static lidarRadius(e){return Math.min(e,Dt.MAX_LIDAR_RADIUS_M)}async lidar(e,t){const i=Dt.lidarRadius(t),n=Math.ceil(2*i/Dt.METRES_PER_PIXEL),[s,r]=await Promise.all([this.raster(e,i,n,Dt.HEIGHT_LAYER),this.raster(e,i,n,Dt.CLASS_LAYER)]);if(s.samples!==1||r.samples<3)throw new ir("unreachable","The survey answered in a layout this does not read");const a=s.values;if(a.every(d=>d===Dt.NO_DATA))throw new ir("no-coverage","No LiDAR HD survey here yet");const o=new Uint8Array(n*n),c=new Uint8Array(n*n);for(let d=0;d<n*n;d++){const u=r.values[d*r.samples],p=r.values[d*r.samples+1],f=r.values[d*r.samples+2];o[d]=p>u&&p>f?1:0,c[d]=u>120&&p<90&&f<90?1:0}const h=2*i/n;return{size:n,metresPerPixel:h,heights:a,vegetation:o,building:c,structures:new jA(a,o,c,n,h)}}async laserDate(e){return Dt.latest(await this.dated("IGNF_LIDAR-HD_METADONNEE:metadata","geom","date_fin_acquisition",e))}async photoDate(e){return Dt.latest(await this.dated("ORTHOIMAGERY.ORTHOPHOTOS.GRAPHE-MOSAIQUAGE:graphe_bdortho","geom","date_vol",e))}static latest(e){return e.map(i=>/^\d{4}-\d{2}-\d{2}/.exec(String(i??""))?.[0]).filter(i=>i!==void 0).sort().at(-1)}async dated(e,t,i,n){const r=[n.lng-1e-4,n.lat-1e-4,n.lng+1e-4,n.lat+1e-4].map(a=>a.toFixed(6)).join(",");try{return(await this.features(e,`BBOX(${t},${r},'EPSG:4326')`,i,20)).map(a=>a.properties[i])}catch{return[]}}async photo(e,t){const i=Dt.radius(t),n=Math.min(Dt.MAX_PHOTO_PIXELS,Math.ceil(2*i/Dt.METRES_PER_PIXEL)),s=await this.raster(e,i,n,Dt.PHOTO_LAYER);if(s.samples<3)throw new ir("unreachable","The photograph came in a layout this does not read");return{size:n,metresPerPixel:2*i/n,samples:s.samples,values:s.values}}async vectors(e,t,i){const n=Dt.radius(t),s=n/Dt.METRES_PER_DEGREE,r=n/(Dt.METRES_PER_DEGREE*Math.cos(e.lat*Math.PI/180)),o=`BBOX(geometrie,${[e.lng-r,e.lat-s,e.lng+r,e.lat+s].map(v=>v.toFixed(6)).join(",")},'EPSG:4326')`,[c,h,d,u,p,f]=await Promise.all([this.features("construction_lineaire",`${o} AND nature='Pont'`,"nature,geometrie"),this.features("troncon_de_route",`${o} AND position_par_rapport_au_sol>'0'`,"nature,largeur_de_chaussee,position_par_rapport_au_sol,nom_voie_ban_gauche,nom_voie_ban_droite,cpx_toponyme_route_nommee,geometrie"),this.features("troncon_de_voie_ferree",`${o} AND position_par_rapport_au_sol>'0'`,"position_par_rapport_au_sol,geometrie").catch(()=>[]),this.features("ligne_electrique",o,"voltage,geometrie"),i?this.features("batiment",o,Dt.BUILDING_PROPERTIES,5e3):Promise.resolve([]),i?this.features("zone_d_activite_ou_d_interet",o,"toponyme,categorie,geometrie",100).catch(()=>[]):Promise.resolve([])]),g=i?await this.wholeWorship(f,p,[e.lng-r,e.lat-s,e.lng+r,e.lat+s]):[],m=[...p,...g];return{bridges:c,elevatedRoads:h,elevatedRailways:d,lines:u,buildings:m,zones:f,mapDate:Dt.latest(m.map(v=>v.properties.date_modification))}}static BUILDING_PROPERTIES="cleabs,nature,usage_1,hauteur,nombre_d_etages,materiaux_des_murs,materiaux_de_la_toiture,date_d_apparition,date_modification,geometrie";static MAX_WHOLE_M=300;async wholeWorship(e,t,i){const[n,s,r,a]=i,o=Dt.MAX_WHOLE_M/Dt.METRES_PER_DEGREE;let c;for(const d of e){if(d.properties.categorie!=="Religieux")continue;const[u,p,f,g]=Dt.extent(d);if(u>=n&&p>=s&&f<=r&&g<=a)continue;const m=[Math.max(u,n-o),Math.max(p,s-o),Math.min(f,r+o),Math.min(g,a+o)];c=c?[Math.min(c[0],m[0]),Math.min(c[1],m[1]),Math.max(c[2],m[2]),Math.max(c[3],m[3])]:m}if(!c)return[];const h=new Set(t.map(d=>d.properties.cleabs));try{return(await this.features("batiment",`BBOX(geometrie,${c.map(u=>u.toFixed(6)).join(",")},'EPSG:4326')`,Dt.BUILDING_PROPERTIES,1e3)).filter(u=>!h.has(u.properties.cleabs))}catch{return[]}}static extent(e){let t=1/0,i=1/0,n=-1/0,s=-1/0;const r=a=>{Array.isArray(a)&&typeof a[0]=="number"?(t=Math.min(t,a[0]),n=Math.max(n,a[0]),i=Math.min(i,a[1]),s=Math.max(s,a[1])):Array.isArray(a)&&a.forEach(r)};return r(e.geometry.coordinates),[t,i,n,s]}async elevatedRoadLines(e){const t=`BBOX(geometrie,${[e.west,e.south,e.east,e.north].map(s=>s.toFixed(6)).join(",")},'EPSG:4326')`,i=await this.features("troncon_de_route",`${t} AND position_par_rapport_au_sol>'0'`,"geometrie",300),n=[];for(const s of i){const{type:r,coordinates:a}=s.geometry,o=r==="LineString"?[a]:r==="MultiLineString"?a:[];for(const c of o)n.push(c.map(h=>({lat:h[1],lng:h[0]})))}return n}async waterSurfaces(e){const t=`BBOX(geometrie,${[e.west,e.south,e.east,e.north].map(i=>i.toFixed(6)).join(",")},'EPSG:4326')`;return this.features("surface_hydrographique",`${t} AND position_par_rapport_au_sol='0' AND persistance='Permanent'`,"cleabs,nature,geometrie",500)}async roads(e){const t=`BBOX(geometrie,${[e.west,e.south,e.east,e.north].map(i=>i.toFixed(6)).join(",")},'EPSG:4326')`;return this.features("troncon_de_route",`${t} AND nature<>'Sentier' AND nature<>'Escalier' AND position_par_rapport_au_sol>='0'`,"cleabs,nature,largeur_de_chaussee,nombre_de_voies,sens_de_circulation,vitesse_moyenne_vl,urbain,nom_voie_ban_gauche,nom_voie_ban_droite,cpx_toponyme_route_nommee,geometrie",3e3)}async railways(e){const t=`BBOX(geometrie,${[e.west,e.south,e.east,e.north].map(i=>i.toFixed(6)).join(",")},'EPSG:4326')`;return this.features("troncon_de_voie_ferree",`${t} AND etat_de_l_objet='En service'`,"cleabs,nature,electrifie,largeur,nombre_de_voies,position_par_rapport_au_sol,cpx_toponyme,geometrie",3e3)}async raster(e,t,i,n){const s=t/Dt.METRES_PER_DEGREE,r=t/(Dt.METRES_PER_DEGREE*Math.cos(e.lat*Math.PI/180)),a=new URLSearchParams({SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",LAYERS:n,STYLES:"",CRS:"EPSG:4326",BBOX:[e.lat-s,e.lng-r,e.lat+s,e.lng+r].map(c=>c.toFixed(6)).join(","),WIDTH:String(i),HEIGHT:String(i),FORMAT:"image/geotiff"});let o;try{o=await this.fetchImpl(`${this.wms}?${a}`)}catch(c){throw new ir("unreachable",c instanceof Error?c.message:String(c))}if(!o.ok)throw new ir("unreachable",`The survey answered ${o.status}`);try{return Of.parse(await o.arrayBuffer())}catch(c){throw new ir("unreachable",c instanceof Error?c.message:String(c))}}async features(e,t,i,n=200){const s=new URLSearchParams({SERVICE:"WFS",VERSION:"2.0.0",REQUEST:"GetFeature",TYPENAMES:e.includes(":")?e:`BDTOPO_V3:${e}`,SRSNAME:"EPSG:4326",OUTPUTFORMAT:"application/json",COUNT:String(n),CQL_FILTER:t,PROPERTYNAME:i});let r;try{r=await this.fetchImpl(`${this.wfs}?${s}`)}catch(o){throw new ir("unreachable",o instanceof Error?o.message:String(o))}if(!r.ok)throw new ir("unreachable",`The map answered ${r.status}`);return(await r.json()).features??[]}}class Ga{static NEAR_M=15;static ALIGNED_DEG=30;static LONG_ENOUGH_M=15;static SAMPLE_M=5;static follows(e,t){if(e.length<2||t.length<2)return!1;const i=t[0],n=t.map(a=>rt(a.lat,a.lng,i.lat,i.lng)),s=e.map(a=>rt(a.lat,a.lng,i.lat,i.lng));let r=0;for(let a=0;a<s.length-1;a++){const o=s[a],c=s[a+1],h=Math.hypot(c.x-o.x,c.z-o.z);if(h<1e-6)continue;const d=Math.max(1,Math.ceil(h/Ga.SAMPLE_M)),u=Math.atan2(c.x-o.x,-(c.z-o.z));for(let p=0;p<=d;p++){const f=p/d,g=Ga.nearest(n,o.x+(c.x-o.x)*f,o.z+(c.z-o.z)*f);g.distanceM>Ga.NEAR_M||Ga.angleBetween(u,g.heading)<=Ga.ALIGNED_DEG&&(r+=h/d)}}return r>=Ga.LONG_ENOUGH_M}static nearest(e,t,i){let n={distanceM:1/0,heading:0};for(let s=0;s<e.length-1;s++){const r=e[s],a=e[s+1],o=a.x-r.x,c=a.z-r.z,h=o*o+c*c,d=h<1e-9?0:Math.max(0,Math.min(1,((t-r.x)*o+(i-r.z)*c)/h)),u=Math.hypot(t-(r.x+o*d),i-(r.z+c*d));u<n.distanceM&&(n={distanceM:u,heading:Math.atan2(o,-c)})}return n}static angleBetween(e,t){const i=Math.abs((e-t)*180/Math.PI%180);return Math.min(i,180-i)}}class Et{static WMS="https://data.geopf.fr/wms-r/wms";static LAYERS=["IGNF_LIDAR-HD_MNT_ELEVATION.ELEVATIONGRIDCOVERAGE.LAMB93","ELEVATION.ELEVATIONGRIDCOVERAGE.HIGHRES"];static METRES_PER_PIXEL=4;static METRES_PER_DEGREE=111320;static CLASS_LAYER="IGNF_LIDAR-HD_CLASS";static SURFACE_LAYER="IGNF_LIDAR-HD_MNS_ELEVATION.ELEVATIONGRIDCOVERAGE.LAMB93";static DECK=[240,240,0];static NO_CLASS=[0,0,0];static DECK_END_PIXELS=3;static DECK_HALF_WIDTH_M=8;static DECK_APPROACH_M=10;static DECK_STRAIGHT_MAX_M=200;static MIN_PIXELS=8;static MAX_PIXELS=700;static NO_DATA_BELOW=-100;static COARSE_METRES_PER_PIXEL=20;static MESH_FROM_VERTICES=33;static FINE_SPACING_M=7;static MAX_SEGMENTS=256;static MAX_HOLE_SHARE=.05;static DEFAULT_TIMEOUT_MS=12e3;static MAX_WINDOW_M=50;fetchImpl;wms;timeoutMs;maxWindowM;geoplatform;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.wms=e.wmsEndpoint??Et.WMS,this.timeoutMs=e.timeoutMs??Et.DEFAULT_TIMEOUT_MS,this.maxWindowM=e.maxWindowM??Et.MAX_WINDOW_M,this.geoplatform=new Dt({fetchImpl:this.fetchImpl,wmsEndpoint:this.wms,wfsEndpoint:e.wfsEndpoint})}async getElevationGrid(e,t,i){const n={lat:(e.south+e.north)/2,lng:(e.west+e.east)/2};if(!Dt.covers(n.lat,n.lng))throw new Error("The IGN's relief covers France only");const s=Math.abs(rt(n.lat,e.east,n.lat,e.west).x),r=Math.abs(rt(e.north,n.lng,e.south,n.lng).z);t=this.finer(t,s,r);const a={x:s/Math.max(1,t.width-1),y:r/Math.max(1,t.height-1)},o={x:Math.min(a.x,this.maxWindowM),y:Math.min(a.y,this.maxWindowM)},c={south:e.south-o.y/Et.METRES_PER_DEGREE,north:e.north+o.y/Et.METRES_PER_DEGREE,west:e.west-o.x/(Et.METRES_PER_DEGREE*Math.cos(n.lat*Math.PI/180)),east:e.east+o.x/(Et.METRES_PER_DEGREE*Math.cos(n.lat*Math.PI/180))},h=g=>Math.min(Et.MAX_PIXELS,Math.max(Et.MIN_PIXELS,Math.ceil(g/Et.METRES_PER_PIXEL))),d={width:h(s+2*o.x),height:h(r+2*o.y)},u={x:(s+2*o.x)/d.width,y:(r+2*o.y)/d.height};let p=new Error("No layer of the IGN's relief answered");const f=Math.max(u.x,u.y)>Et.COARSE_METRES_PER_PIXEL;for(const g of f?[...Et.LAYERS].reverse():Et.LAYERS)try{const m=await this.heights(c,d,g);g===Et.LAYERS[0]&&await this.layBridgeDecks(m,c,d,i?.route);const v=this.sample(m,t,e,c,u,a);if(v)return{heights:v,width:t.width,height:t.height,bounds:e};p=new Error(`${g} does not cover this place`)}catch(m){p=m}throw p}finer(e,t,i){if(e.width<Et.MESH_FROM_VERTICES||e.height<Et.MESH_FROM_VERTICES)return e;const n=(s,r)=>{const a=Math.ceil(s/Et.FINE_SPACING_M),o=Math.max(r-1,Math.min(Et.MAX_SEGMENTS,a));return o%2===0?o:o+1};return{width:n(t,e.width)+1,height:n(i,e.height)+1}}async raster(e,t,i){const n=new URLSearchParams({SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",LAYERS:i,STYLES:"",CRS:"EPSG:4326",BBOX:[e.south,e.west,e.north,e.east].map(r=>r.toFixed(6)).join(","),WIDTH:String(t.width),HEIGHT:String(t.height),FORMAT:"image/geotiff"}),s=await this.fetchImpl(`${this.wms}?${n}`,{signal:AbortSignal.timeout(this.timeoutMs)});if(!s.ok)throw new Error(`The IGN's relief answered ${s.status}`);return Of.parse(await s.arrayBuffer())}async heights(e,t,i){const n=await this.raster(e,t,i);if(n.samples!==1||!(n.values instanceof Float32Array))throw new Error("The IGN's relief came in a layout this does not read");return n}async layBridgeDecks(e,t,i,n){if(!(!n||n.length<2)&&!(!(e.values instanceof Float32Array)||!e.values.some(s=>s>Et.NO_DATA_BELOW)))try{const s=(await this.geoplatform.elevatedRoadLines({west:t.west,south:t.south,east:t.east,north:t.north})).filter(a=>Ga.follows(n,a));if(s.length===0)return;const r=Float32Array.from(e.values);await this.layClassifiedDecks(e,t,i,s),this.layRouteDecks(e,r,t,s)}catch{}}async layClassifiedDecks(e,t,i,n){try{const s=await this.raster(t,i,Et.CLASS_LAYER);if(s.samples<3||s.width!==e.width||s.height!==e.height)return;const r=(m,[v,b,w])=>s.values[m*s.samples]===v&&s.values[m*s.samples+1]===b&&s.values[m*s.samples+2]===w,a=e.width,o=e.height,c=new Uint8Array(a*o);let h=!1;for(let m=0;m<c.length;m++)r(m,Et.DECK)&&(c[m]=1,h=!0);if(!h)return;const d=Et.DECK_END_PIXELS,u=[];for(let m=0;m<c.length;m++){if(c[m]!==1)continue;const v=m%a,b=(m-v)/a;for(let w=-d;w<=d;w++)for(let y=-d;y<=d;y++){const M=v+y,S=b+w;if(M<0||S<0||M>=a||S>=o)continue;const E=S*a+M;c[E]===0&&r(E,Et.NO_CLASS)&&u.push(E)}}for(const m of u)c[m]=1;const p=this.paint(n,t,a,o),f=this.touching(c,p,a,o);if(f.every(m=>m===0))return;const g=await this.heights(t,i,Et.SURFACE_LAYER);if(g.width!==a||g.height!==o)return;for(let m=0;m<f.length;m++){if(f[m]===0)continue;const v=g.values[m];Number.isFinite(v)&&v>Et.NO_DATA_BELOW&&v>e.values[m]&&(e.values[m]=v)}}catch{}}layRouteDecks(e,t,i,n){const{width:s,height:r}=e,a=(i.north+i.south)/2,o=(i.east-i.west)*Et.METRES_PER_DEGREE*Math.cos(a*Math.PI/180),c=(i.north-i.south)*Et.METRES_PER_DEGREE,h=o/s,d=c/r,u=v=>({x:(v.lng-i.west)/(i.east-i.west)*o,y:(i.north-v.lat)/(i.north-i.south)*c}),p=(v,b)=>{const w=Math.floor(v/h),y=Math.floor(b/d);if(w<0||y<0||w>=s||y>=r)return;const M=t[y*s+w];return Number.isFinite(M)&&M>Et.NO_DATA_BELOW?M:void 0},f=Et.DECK_HALF_WIDTH_M,g=Et.DECK_APPROACH_M,m=(v,b)=>{const w=Math.hypot(v.x-b.x,v.y-b.y)||1,y={x:(v.x-b.x)/w,y:(v.y-b.y)/w},M=[0,g/2,g].map(S=>p(v.x+y.x*S,v.y+y.y*S)).filter(S=>S!==void 0);return M.length>0?Math.max(...M):void 0};for(const v of Et.chains(n.map(b=>b.map(u)))){const b=[0];for(let R=1;R<v.length;R++)b.push(b[R-1]+Math.hypot(v[R].x-v[R-1].x,v[R].y-v[R-1].y));const w=b[b.length-1];if(w<1||w>Et.DECK_STRAIGHT_MAX_M)continue;const y=m(v[0],v[1]),M=m(v[v.length-1],v[v.length-2]);if(y===void 0||M===void 0)continue;const S=f+g,E=Math.max(0,Math.floor((Math.min(...v.map(R=>R.x))-S)/h)),x=Math.min(s-1,Math.ceil((Math.max(...v.map(R=>R.x))+S)/h)),T=Math.max(0,Math.floor((Math.min(...v.map(R=>R.y))-S)/d)),D=Math.min(r-1,Math.ceil((Math.max(...v.map(R=>R.y))+S)/d));for(let R=T;R<=D;R++)for(let O=E;O<=x;O++){const C=Et.nearestAlong(v,b,{x:(O+.5)*h,y:(R+.5)*d});if(C.distance>f||C.s<-g||C.s>w+g)continue;const P=y+(M-y)*Math.min(1,Math.max(0,C.s/w)),L=R*s+O;t[L]>=P||(e.values[L]=P)}}}static chains(e){const t=e.filter(s=>s.length>=2).map(s=>[...s]),i=[],n=(s,r)=>Math.hypot(s.x-r.x,s.y-r.y)<=1;for(;t.length>0;){let s=t.shift(),r=!0;for(;r;){r=!1;for(let a=0;a<t.length;a++){const o=t[a],c=s[0],h=s[s.length-1];if(n(h,o[0]))s=[...s,...o.slice(1)];else if(n(h,o[o.length-1]))s=[...s,...[...o].reverse().slice(1)];else if(n(c,o[o.length-1]))s=[...o.slice(0,-1),...s];else if(n(c,o[0]))s=[...[...o].reverse().slice(0,-1),...s];else continue;t.splice(a,1),r=!0;break}}i.push(s)}return i}static nearestAlong(e,t,i){let n={s:0,distance:1/0};for(let s=0;s<e.length-1;s++){const r=e[s],a=e[s+1],o=a.x-r.x,c=a.y-r.y,h=o*o+c*c;if(h<1e-9)continue;let d=((i.x-r.x)*o+(i.y-r.y)*c)/h;s>0&&(d=Math.max(0,d)),s<e.length-2&&(d=Math.min(1,d));const u=Math.hypot(i.x-(r.x+o*d),i.y-(r.y+c*d));u<n.distance&&(n={s:t[s]+d*Math.sqrt(h),distance:u})}return n}paint(e,t,i,n){const s=new Uint8Array(i*n),r=o=>(o-t.west)/(t.east-t.west)*i,a=o=>(t.north-o)/(t.north-t.south)*n;for(const o of e)for(let c=0;c<o.length-1;c++){const h=r(o[c].lng),d=a(o[c].lat),u=r(o[c+1].lng),p=a(o[c+1].lat),f=Math.max(1,Math.ceil(Math.hypot(u-h,p-d)*2));for(let g=0;g<=f;g++){const m=Math.floor(h+(u-h)*g/f),v=Math.floor(d+(p-d)*g/f);for(let b=-1;b<=1;b++)for(let w=-1;w<=1;w++){const y=m+w,M=v+b;y>=0&&M>=0&&y<i&&M<n&&(s[M*i+y]=1)}}}return s}touching(e,t,i,n){const s=new Uint8Array(e.length),r=new Uint8Array(e.length);for(let a=0;a<e.length;a++){if(e[a]===0||r[a]===1)continue;const o=[],c=[a];r[a]=1;let h=!1;for(;c.length>0;){const d=c.pop();o.push(d),t[d]===1&&(h=!0);const u=d%i,p=(d-u)/i;for(let f=-1;f<=1;f++)for(let g=-1;g<=1;g++){const m=u+g,v=p+f;if(m<0||v<0||m>=i||v>=n)continue;const b=v*i+m;e[b]===1&&r[b]===0&&(r[b]=1,c.push(b))}}if(h)for(const d of o)s[d]=1}return s}sample(e,t,i,n,s,r){const{width:a,height:o}=t,c=new Float32Array(a*o),h=new Uint8Array(a*o),d=Math.min(r.x,this.maxWindowM)/s.x,u=Math.min(r.y,this.maxWindowM)/s.y;let p=0;for(let f=0;f<o;f++){const g=i.north+(i.south-i.north)*(o===1?.5:f/(o-1)),m=(n.north-g)/(n.north-n.south)*e.height-.5;for(let v=0;v<a;v++){const w=(i.west+(i.east-i.west)*(a===1?.5:v/(a-1))-n.west)/(n.east-n.west)*e.width-.5,y=d>1||u>1?this.tent(e,w,m,Math.max(1,d),Math.max(1,u)):this.bilinear(e,w,m),M=f*a+v;y===void 0?p++:(c[M]=y,h[M]=1)}}if(!(p===a*o||p/(a*o)>Et.MAX_HOLE_SHARE))return this.fill(c,h,a,o),c}tent(e,t,i,n,s){let r=0,a=0,o=0;const c=Math.max(0,Math.ceil(i-s)),h=Math.min(e.height-1,Math.floor(i+s)),d=Math.max(0,Math.ceil(t-n)),u=Math.min(e.width-1,Math.floor(t+n));for(let p=c;p<=h;p++){const f=1-Math.abs(p-i)/s;if(!(f<=0))for(let g=d;g<=u;g++){const m=f*(1-Math.abs(g-t)/n);if(m<=0)continue;o+=m;const v=e.at(g,p);!Number.isFinite(v)||v<Et.NO_DATA_BELOW||(r+=m*v,a+=m)}}return o>0&&a>=o*.5?r/a:void 0}bilinear(e,t,i){const n=Math.max(0,Math.min(e.width-1,Math.floor(t))),s=Math.max(0,Math.min(e.height-1,Math.floor(i))),r=Math.min(e.width-1,n+1),a=Math.min(e.height-1,s+1),o=Math.max(0,Math.min(1,t-n)),c=Math.max(0,Math.min(1,i-s)),h=e.at(n,s),d=e.at(r,s),u=e.at(n,a),p=e.at(r,a);for(const m of[h,d,u,p])if(!Number.isFinite(m)||m<Et.NO_DATA_BELOW)return;const f=h+(d-h)*o,g=u+(p-u)*o;return f+(g-f)*c}fill(e,t,i,n){let s=t.reduce((r,a)=>r+(a?0:1),0);for(;s>0;){const r=[];for(let a=0;a<n;a++)for(let o=0;o<i;o++){const c=a*i+o;if(t[c])continue;let h=0,d=0;for(let u=-1;u<=1;u++)for(let p=-1;p<=1;p++){const f=a+u,g=o+p;f<0||g<0||f>=n||g>=i||!t[f*i+g]||(h+=e[f*i+g],d++)}d>0&&r.push([c,h/d])}if(r.length===0)return;for(const[a,o]of r)e[a]=o,t[a]=1;s-=r.length}}}class JA{providers;constructor(...e){this.providers=e}async getElevationGrid(e,t,i){let n=new Error("No source of relief to ask");for(const s of this.providers)try{return await s.getElevationGrid(e,t,i)}catch(r){n=r}throw n}}const Qu=256,QA=5;async function E2(l,e,t,i){const n=(l.south+l.north)/2,s=(l.west+l.east)/2,r=Math.abs(rt(n,l.east,n,l.west).x),a=Math.abs(rt(l.north,s,l.south,s).z),o=Math.max(r,a),{zoom:c,gridSize:h}=e3(n,o,Math.max(e.width,e.height)),{tiles:d,bounds:u}=x2(s,n,c,h),p=h*Qu,f=document.createElement("canvas");f.width=p,f.height=p;const g=f.getContext("2d");if(!g)throw new Error("2D canvas context unavailable");const m=d[0];await Promise.all(d.map(async y=>{const M=t(y),S=await i(M);if(!S.ok)throw new Error(`Imagery tile fetch failed (${S.status}): ${M}`);const E=await createImageBitmap(await S.blob());g.drawImage(E,(y.x-m.x)*Qu,(y.y-m.y)*Qu)}));const v=Math.min(Math.max(e.width,e.height),p);if(v===p)return{source:f,width:p,height:p,bounds:u};const b=document.createElement("canvas");b.width=v,b.height=v;const w=b.getContext("2d");if(!w)throw new Error("2D canvas context unavailable");return w.drawImage(f,0,0,p,p,0,0,v,v),{source:b,width:v,height:v,bounds:u}}function e3(l,e,t){let i=XA(l,e/Math.max(1,t)),n=wy(l,i,e);for(;n>QA&&i>0;)i--,n=wy(l,i,e);return{zoom:i,gridSize:n,mPerPixel:b2(l,i)/Qu}}function wy(l,e,t){const i=Math.ceil(t/2/b2(l,e));return Math.max(3,2*i+1)}class A2{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=e.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(e,t){return E2(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class ys{static ENDPOINTS=["https://overpass-api.de/api/interpreter","https://overpass.kumi.systems/api/interpreter","https://overpass.private.coffee/api/interpreter"];static FRANCE_ENDPOINT="https://overpass.openstreetmap.fr/api/interpreter";static FRANCE={south:41.3,west:-5.2,north:51.1,east:9.6};static endpointsFor(e,t=ys.ENDPOINTS){const i=/\((-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)\)/.exec(e);if(!i)return[...t];const[n,s,r,a]=i.slice(1).map(Number),o=(n+r)/2,c=(s+a)/2,h=ys.FRANCE;return o>=h.south&&o<=h.north&&c>=h.west&&c<=h.east?[ys.FRANCE_ENDPOINT,...t.filter(u=>u!==ys.FRANCE_ENDPOINT)]:[...t]}fetchImpl;endpoints;constructor(e={}){this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.endpoints=e.endpoints}static queue=Promise.resolve();static HANG_MS=4e4;static RETRY_MS=4e3;query(e){return this.queued(()=>this.ask(e))}queued(e){const t=ys.queue.then(e,e);return ys.queue=t.catch(()=>{}),t}async ask(e){let t;for(const i of this.endpoints??ys.endpointsFor(e))for(let n=0;n<2;n++){try{const s=await this.fetchImpl(`${i}?data=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(ys.HANG_MS)});if(s.ok)return await s.json();if(t=new Error(`${new URL(i).host} refused (${s.status})`),s.status!==429&&s.status!==504)break}catch(s){t=new Error(`${new URL(i).host} unreachable (${String(s)})`);break}await new Promise(s=>setTimeout(s,ys.RETRY_MS))}throw t??new Error("No Overpass instance answered")}}class Va{attribution="Roads © OpenStreetMap contributors, as surveyed today";contemporary=!0;static DRIVABLE=["motorway","trunk","primary","secondary","tertiary","unclassified","residential","service","track","motorway_link","trunk_link","primary_link","secondary_link","tertiary_link"];static WIDTH_M={motorway:11,trunk:9,primary:8,secondary:7,tertiary:6.5,unclassified:5.5,residential:5.5,service:4,track:3.5};static DEFAULT_WIDTH_M=5;client;constructor(e={}){this.client=new ys(e)}async getRoads(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:30];way["highway"~"^(${Va.DRIVABLE.join("|")})$"](${t});out geom;`;return this.waysFrom(await this.client.query(i))}waysFrom(e){const t=[];for(const i of e.elements??[]){const n=i.geometry,s=i.tags;!s||!n||n.length<2||t.push({id:`osm-${i.id??t.length}`,name:s.name,surface:Va.surfaceOf(s),widthM:Va.widthOf(s),...Va.paintOf(s),points:n.map(r=>({lat:r.lat,lng:r.lon}))})}return t}static paintOf(e){const t=e.highway??"",i=e.junction==="roundabout"||e.junction==="circular",n=e.oneway,s=n==="yes"||n==="1"||n==="true"||n===void 0&&(t==="motorway"||i),r=n==="-1"||n==="reverse",a=Number.parseInt(e.lanes??"",10),o=Number.parseInt(e.maxspeed??"",10),c=i?"roundabout":t==="motorway"?"motorway":t.endsWith("_link")?"slip":(t==="trunk"||t==="primary")&&(s||r)?"dual":t?"road":void 0;return{...c?{kind:c}:{},...Number.isFinite(a)&&a>0?{lanes:a}:{},...n!==void 0||t==="motorway"||i?{direction:r?"backward":s?"forward":"both"}:{},...Number.isFinite(o)&&o>0?{speedKmh:o}:{}}}static widthOf(e){const t=Number.parseFloat(e.width??e.est_width??"");if(Number.isFinite(t)&&t>0)return t;const i=Number.parseInt(e.lanes??"",10);return Number.isFinite(i)&&i>0?i*3.5:Va.WIDTH_M[e.highway]??Va.DEFAULT_WIDTH_M}static surfaceOf(e){const t=e.surface;return t==="dirt"||t==="earth"||t==="mud"?"dirt":t==="gravel"||t==="fine_gravel"||t==="compacted"||t==="ground"||t==="sand"||t==="unpaved"?"gravel":t?"paved":e.highway==="track"?"gravel":"paved"}}const im="https://ufoathome.org/roads/index.json";class cc{attributionRead;contemporaryRead;fetchImpl;indexUrls;fallback;index;static shared=new Map;sharing;constructor(e={}){this.sharing=!e.fetchImpl,this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??cc.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Roads © OpenStreetMap contributors"}get contemporary(){return this.contemporaryRead??this.fallback?.contemporary??!0}async getRoads(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getRoads(e);return this.attributionRead=this.fallback.attribution,this.contemporaryRead=this.fallback.contemporary,i}async held(e){let t;try{t=await this.load()}catch{return}const i=t.find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.ways)?(this.attributionRead=s.attribution,this.contemporaryRead=s.contemporary,s.ways):void 0}catch{return}}load(){if(this.index)return this.index;if(!this.sharing)return this.index=this.fetchFirstAvailable();const e=this.indexUrls.join(`
3291
- `);let t=cc.shared.get(e);return t||(t=this.fetchFirstAvailable(),cc.shared.set(e,t),t.then(i=>{i.length===0&&cc.shared.delete(e)})),this.index=t}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.129.0")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==im?[t,im]:[im]).map(i=>i+e)}}class Ya{attribution="Roads © IGN, BD TOPO (Licence Ouverte 2.0), as drawn today";contemporary=!0;static BY_NATURE={"Type autoroutier":{surface:"paved",widthM:11},"Route à 2 chaussées":{surface:"paved",widthM:7},"Route à 1 chaussée":{surface:"paved",widthM:5.5},"Rond-point":{surface:"paved",widthM:6},Bretelle:{surface:"paved",widthM:5},"Route empierrée":{surface:"gravel",widthM:3.5},Chemin:{surface:"dirt",widthM:3},"Piste cyclable":{surface:"paved",widthM:2.5}};static DEFAULT={surface:"paved",widthM:5};static KINDS={"Type autoroutier":"motorway","Route à 2 chaussées":"dual","Route à 1 chaussée":"road","Rond-point":"roundabout",Bretelle:"slip"};static DIRECTIONS={"Double sens":"both","Sens direct":"forward","Sens inverse":"backward"};platform;constructor(e={}){this.platform=new Dt(e)}static covers(e){return Dt.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getRoads(e){return(await this.platform.roads(e)).flatMap((i,n)=>Ya.wayOf(i,n))}static wayOf(e,t){if(e.geometry.type!=="LineString")return[];const i=e.geometry.coordinates.map(([u,p])=>({lat:p,lng:u}));if(i.length<2)return[];const n=String(e.properties.nature??""),s=Ya.BY_NATURE[n]??Ya.DEFAULT,r=Number(e.properties.largeur_de_chaussee),a=Number(e.properties.nombre_de_voies),o=Number(e.properties.vitesse_moyenne_vl),c=Ya.DIRECTIONS[String(e.properties.sens_de_circulation??"")],h=Ya.KINDS[n],d=[e.properties.cpx_toponyme_route_nommee,e.properties.nom_voie_ban_gauche,e.properties.nom_voie_ban_droite].find(u=>typeof u=="string"&&u.length>0);return[{id:String(e.properties.cleabs??`ign-${t}`),...d?{name:d}:{},surface:s.surface,widthM:Number.isFinite(r)&&r>0?r:s.widthM,...e.properties.urbain===!0?{urban:!0}:{},...h?{kind:h}:{},...Number.isInteger(a)&&a>0?{lanes:a}:{},...c?{direction:c}:{},...Number.isFinite(o)&&o>0?{speedKmh:o}:{},points:i}]}}class t3{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Roads © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere, as drawn today";contemporary=!0;async getRoads(e){if(Ya.covers(e))try{const t=await this.regional.getRoads(e);if(t.length>0)return t}catch{}return this.worldwide.getRoads(e)}}class ud{attribution="Water © IGN, BD TOPO (Licence Ouverte 2.0)";platform;constructor(e={}){this.platform=new Dt(e)}static covers(e){return Dt.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getWater(e){return(await this.platform.waterSurfaces(e)).flatMap((i,n)=>ud.surfacesOf(i,n))}static surfacesOf(e,t){const{type:i,coordinates:n}=e.geometry,s=i==="Polygon"?[n]:i==="MultiPolygon"?n:[],r=String(e.properties.cleabs??`ign-water-${t}`);return s.flatMap((a,o)=>{const c=a.map(h=>ud.open(h.map(d=>({lat:d[1],lng:d[0]})))).filter(h=>h.length>=3);return c.length>0?[{id:s.length>1?`${r}-${o}`:r,rings:c}]:[]})}static open(e){const t=e[0],i=e[e.length-1];return e.length>1&&t.lat===i.lat&&t.lng===i.lng?e.slice(0,-1):e}}class Co{attribution="Water © OpenStreetMap contributors";client;constructor(e={}){this.client=new ys(e)}async getWater(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:30];(way["natural"="water"](${t});way["waterway"="riverbank"](${t});)->.w;(relation["natural"="water"](${t});relation["waterway"="riverbank"](${t});)->.r;.w out geom;.r out body;way(r.r)(${t});out geom;`;return Co.surfacesFrom(await this.client.query(i))}static surfacesFrom(e){const t=e.elements??[],i=new Map;for(const r of t)r.type==="way"&&r.id!==void 0&&r.geometry&&i.set(r.id,r.geometry.map(a=>({lat:a.lat,lng:a.lon})));const n=[],s=new Set;for(const r of t){if(r.type!=="relation")continue;const a=c=>(r.members??[]).filter(h=>h.type==="way"&&h.role===c&&h.ref!==void 0&&i.has(h.ref)).map(h=>(s.add(h.ref),i.get(h.ref))),o=Co.rings(a("inner"));for(const[c,h]of Co.rings(a("outer")).entries()){const d=o.filter(u=>Co.contains(h,u[0]));n.push({id:`osm-r${r.id}-${c}`,rings:[h,...d]})}}for(const r of t)if(!(r.type!=="way"||!r.tags||r.id===void 0||s.has(r.id))&&!(r.tags.natural!=="water"&&r.tags.waterway!=="riverbank"))for(const a of Co.rings([i.get(r.id)??[]]))n.push({id:`osm-w${r.id}`,rings:[a]});return n}static rings(e){const t=(s,r)=>s.lat===r.lat&&s.lng===r.lng,i=e.filter(s=>s.length>=2).map(s=>[...s]),n=[];for(;i.length>0;){let s=i.pop(),r=!0;for(;!t(s[0],s[s.length-1])&&r;){r=!1;for(let a=0;a<i.length;a++){const o=i[a],c=s[s.length-1];if(t(c,o[0]))s=[...s,...o.slice(1)];else if(t(c,o[o.length-1]))s=[...s,...[...o].reverse().slice(1)];else continue;i.splice(a,1),r=!0;break}}s.length>=4&&t(s[0],s[s.length-1])&&n.push(s.slice(0,-1))}return n}static contains(e,t){let i=!1;for(let n=0,s=e.length-1;n<e.length;s=n++){const r=e[n],a=e[s];r.lat>t.lat!=a.lat>t.lat&&t.lng<(a.lng-r.lng)*(t.lat-r.lat)/(a.lat-r.lat)+r.lng&&(i=!i)}return i}}class i3{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Water © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere";async getWater(e){if(ud.covers(e))try{const t=await this.regional.getWater(e);if(t.length>0)return t}catch{}return this.worldwide.getWater(e)}}const nm="https://ufoathome.org/water/index.json";class hc{attributionRead;fetchImpl;indexUrls;fallback;index;static shared=new Map;sharing;constructor(e={}){this.sharing=!e.fetchImpl,this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??hc.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Water © OpenStreetMap contributors"}async getWater(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getWater(e);return this.attributionRead=this.fallback.attribution,i}async held(e){const i=(await this.load().catch(()=>[])).find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.surfaces)?(this.attributionRead=s.attribution,s.surfaces):void 0}catch{return}}load(){if(this.index)return this.index;if(!this.sharing)return this.index=this.fetchFirstAvailable();const e=this.indexUrls.join(`
3292
- `);let t=hc.shared.get(e);return t||(t=this.fetchFirstAvailable(),hc.shared.set(e,t),t.then(i=>{i.length===0&&hc.shared.delete(e)})),this.index=t}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.129.0")}`,t=typeof location>"u"?void 0:new URL("/water/index.json",location.href).href;return(t&&t!==nm?[t,nm]:[nm]).map(i=>i+e)}}class ps{constructor(e){this.water=e}static CACHE_NAME="rr0-water";static MAX_AGE_MS=720*3600*1e3;static FORMAT=1;static MAX_STORED=400;static asked=new Map;get attribution(){return this.water.attribution}async getWater(e){const t=ps.key(e);let i=ps.asked.get(t);return i===void 0&&(i=this.recalled(t,e),ps.asked.set(t,i),i.catch(()=>ps.asked.delete(t))),structuredClone(await i)}static forget(){ps.asked.clear()}static key(e){return`v${ps.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}async recalled(e,t){const i=await ps.openCache(),n=ps.request(e);if(i)try{const r=await i.match(n);if(r){const{savedAt:a,surfaces:o}=await r.json();if(Date.now()-a<ps.MAX_AGE_MS&&Array.isArray(o))return o;await i.delete(n)}}catch{}const s=await this.water.getWater(t);return i&&ps.store(i,n,s),s}static async store(e,t,i){try{const n={savedAt:Date.now(),surfaces:i};await e.put(t,new Response(JSON.stringify(n),{headers:{"Content-Type":"application/json"}}));const s=await e.keys();for(const r of s.slice(0,Math.max(0,s.length-ps.MAX_STORED)))await e.delete(r)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(ps.CACHE_NAME)}catch{return}}static request(e){const t=typeof location>"u"?"http://localhost":location.origin;return new Request(`${t}/__water__/${encodeURIComponent(e)}`)}}class Ka{attribution="Railways © IGN, BD TOPO (Licence Ouverte 2.0), as drawn today";contemporary=!0;static BY_NATURE={"Voie ferrée principale":{kind:"rail"},LGV:{kind:"rail",highSpeed:!0},"Voie de service":{kind:"rail",service:!0},Métro:{kind:"subway"},Tramway:{kind:"tram"},"Transport urbain":{kind:"light_rail"},"Funiculaire ou crémaillère":{kind:"funicular"}};static GAUGE_MM={Normale:1435,Etroite:1e3,Étroite:1e3,Large:1668};static NO_ALTITUDE=-1e3;platform;constructor(e={}){this.platform=new Dt(e)}static covers(e){return Dt.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getRailways(e){return(await this.platform.railways(e)).flatMap((i,n)=>Ka.linesOf(i,n))}static linesOf(e,t){const{type:i,coordinates:n}=e.geometry,s=i==="LineString"?[n]:i==="MultiLineString"?n:[],r=Ka.BY_NATURE[String(e.properties.nature??"")];if(!r)return[];const a=String(e.properties.cleabs??`ign-rail-${t}`),o=Number(e.properties.nombre_de_voies),c=Number(e.properties.position_par_rapport_au_sol),h=e.properties.cpx_toponyme,d=Ka.GAUGE_MM[String(e.properties.largeur??"")]??1435,u=Ka.electrificationOf(r.kind,d,e.properties.electrifie);return s.flatMap((p,f)=>{const g=p.map(m=>({lat:m[1],lng:m[0],...m.length>2&&Number.isFinite(m[2])&&m[2]>Ka.NO_ALTITUDE?{altitudeM:m[2]}:{}}));return g.length<2?[]:[{id:s.length>1?`${a}-${f}`:a,...typeof h=="string"&&h.length>0?{name:h}:{},kind:r.kind,...r.service?{service:!0}:{},...r.highSpeed?{highSpeed:!0}:{},gaugeMm:d,tracks:Number.isInteger(o)&&o>0?o:1,...u?{electrification:u}:{},level:Number.isFinite(c)?c:0,points:g}]})}static electrificationOf(e,t,i){if(i===!1)return"none";if(i===!0){if(e==="subway")return"third_rail";if(!(e==="funicular"||t<1435))return"overhead"}}}class In{static NEAR_M=15;static ALIGNED_DEG=30;static SAMPLE_M=10;static ROUTES="train|subway|light_rail|tram|monorail";static KINDS="rail|light_rail|subway|tram|narrow_gauge|funicular|preserved";client;constructor(e={}){this.client=new ys(e)}async named(e,t){if(e.length===0)return[...e];const i=`${t.south},${t.west},${t.north},${t.east}`,n=`[out:json][timeout:120];way["railway"~"^(${In.KINDS})$"](${i})->.w;.w out tags geom;rel(bw.w)["route"~"^(${In.ROUTES})$"];out body;`;return In.apply(e,await this.client.query(n),t)}static apply(e,t,i){const n={lat:(i.north+i.south)/2,lng:(i.east+i.west)/2},s=In.ways(t,n);return s.length===0?[...e]:e.map(r=>{const a=r.points.map(v=>rt(v.lat,v.lng,n.lat,n.lng)),o=new Map,c=new Map,h=new Map;let d=0;const u=s.filter(v=>v.service===!!r.service);for(const v of In.samples(a)){d++;const b=In.waysAt(u,v);if(b.length===0)continue;const[w]=b;w.name&&o.set(w.name,(o.get(w.name)??0)+1),w.voltageV&&c.set(w.voltageV,(c.get(w.voltageV)??0)+1);for(const y of new Set(b.flatMap(M=>M.services)))h.set(y,(h.get(y)??0)+1)}const p=v=>[...v.entries()].sort((b,w)=>w[1]-b[1])[0]?.[0],f=r.name??p(o),g=r.voltageV??(r.electrification==="none"?void 0:p(c)),m=[...h.entries()].filter(([,v])=>v*4>=d).sort((v,b)=>b[1]-v[1]||v[0].localeCompare(b[0])).map(([v])=>v);return{...r,...f?{name:f}:{},...g?{voltageV:g}:{},...m.length>0?{services:m}:{}}})}static serviceOf(e){const t=e.name?.split(" : ")[0].replace(/\s*\([^)]*\)\s*$/,"").trim();return t||[e.network,e.ref].filter(Boolean).join(" ")||void 0}static servicesByWay(e){const t=new Map;for(const i of e.elements??[]){if(i.type!=="relation"||!i.tags)continue;const n=In.serviceOf(i.tags);if(n)for(const s of i.members??[]){if(s.type!=="way"||s.ref===void 0)continue;const r=t.get(s.ref)??new Set;r.add(n),t.set(s.ref,r)}}return t}static voltageOf(e){const t=Number.parseInt((e.voltage??"").split(";")[0],10);return Number.isFinite(t)&&t>0?t:void 0}static ways(e,t){const i=In.servicesByWay(e),n=[];for(const s of e.elements??[]){if(s.type!=="way"||!s.geometry||s.geometry.length<2)continue;const r=s.tags??{},a=In.voltageOf(r);n.push({service:!!r.service||r.usage==="industrial",...r.name?{name:r.name}:{},...a?{voltageV:a}:{},services:[...i.get(s.id??-1)??[]],path:s.geometry.map(o=>rt(o.lat,o.lon,t.lat,t.lng))})}return n}static samples(e){const t=[];for(let i=0;i<e.length-1;i++){const n=Math.hypot(e[i+1].x-e[i].x,e[i+1].z-e[i].z);if(n<1e-6)continue;const s=(e[i+1].x-e[i].x)/n,r=(e[i+1].z-e[i].z)/n;for(let a=In.SAMPLE_M/2;a<n;a+=In.SAMPLE_M)t.push({x:e[i].x+s*a,z:e[i].z+r*a,dx:s,dz:r})}return t}static waysAt(e,t){const i=Math.cos(In.ALIGNED_DEG*Math.PI/180),n=[];for(const s of e){let r;for(let a=0;a<s.path.length-1;a++){const o=s.path[a],c=s.path[a+1],h=c.x-o.x,d=c.z-o.z,u=Math.hypot(h,d);if(u<1e-6||Math.abs((h*t.dx+d*t.dz)/u)<i)continue;const p=Math.max(0,Math.min(1,((t.x-o.x)*h+(t.z-o.z)*d)/(u*u))),f=Math.hypot(t.x-(o.x+h*p),t.z-(o.z+d*p));f<=In.NEAR_M&&(r===void 0||f<r)&&(r=f)}r!==void 0&&n.push({way:s,distance:r})}return n.sort((s,r)=>s.distance-r.distance).map(({way:s})=>s)}}class dc{attribution="Railways © OpenStreetMap contributors, as surveyed today";contemporary=!0;static KINDS=["rail","light_rail","tram","subway","narrow_gauge","funicular","preserved"];client;constructor(e={}){this.client=new ys(e)}async getRailways(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:25];way["railway"~"^(${dc.KINDS.join("|")})$"](${t})->.w;.w out tags geom;rel(bw.w)["route"~"^(${In.ROUTES})$"];out body;`;return this.linesFrom(await this.client.query(i))}linesFrom(e){const t=[],i=In.servicesByWay(e);for(const n of e.elements??[]){const s=n.geometry,r=n.tags;if(n.type==="relation"||!r||!s||s.length<2)continue;const a=[...i.get(n.id??-1)??[]].sort(),o=In.voltageOf(r),c=dc.kindOf(r),h=Number.parseInt((r.gauge??"").split(";")[0],10),d=Number.parseInt(r.tracks??"",10),u=dc.electrificationOf(r.electrified),p=r.highspeed==="yes"||Number.parseInt(r.maxspeed??"",10)>=250;t.push({id:`osm-rail-${n.id??t.length}`,...r.name?{name:r.name}:{},...a.length>0?{services:a}:{},...o?{voltageV:o}:{},kind:c,...r.service||r.usage==="industrial"?{service:!0}:{},...p?{highSpeed:!0}:{},gaugeMm:Number.isFinite(h)&&h>300?h:r.railway==="narrow_gauge"?1e3:1435,tracks:Number.isFinite(d)&&d>0?d:1,...u?{electrification:u}:{},level:dc.levelOf(r),points:s.map(f=>({lat:f.lat,lng:f.lon}))})}return t}static kindOf(e){switch(e.railway){case"tram":return"tram";case"subway":return"subway";case"light_rail":return"light_rail";case"funicular":return"funicular";default:return"rail"}}static electrificationOf(e){if(e!==void 0){if(e==="no")return"none";if(e==="contact_line"||e==="yes")return"overhead";if(e==="rail"||e==="4th_rail"||e==="ground-level_power_supply")return"third_rail"}}static levelOf(e){const t=Number.parseInt(e.layer??"",10);return e.tunnel&&e.tunnel!=="no"?Number.isFinite(t)&&t<0?t:-1:e.covered==="yes"&&e.location==="underground"?-1:e.bridge&&e.bridge!=="no"?Number.isFinite(t)&&t>0?t:1:0}}class n3{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Railways © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere, as drawn today";contemporary=!0;async getRailways(e){if(!Ka.covers(e))return this.worldwide.getRailways(e);let t;try{if(t=await this.regional.getRailways(e),t.length>0)return t}catch{}try{return await this.worldwide.getRailways(e)}catch(i){if(t)return t;throw i}}}class tn{constructor(e,t=Date.now){this.railways=e,this.now=t}static CACHE_NAME="rr0-railways";static MAX_AGE_MS=720*3600*1e3;static FORMAT=1;static MAX_STORED=400;static RETRY_AFTER_MS=300*1e3;static asked=new Map;static failedAt=-1/0;get attribution(){return this.railways.attribution}get contemporary(){return this.railways.contemporary}async getRailways(e){const t=tn.key(e);let i=tn.asked.get(t)??tn.containing(e);if(i===void 0){const n=this.recalled(t,e);i={bounds:e,answer:n},tn.asked.set(t,i),n.catch(()=>tn.asked.delete(t))}return structuredClone(await i.answer)}static forget(){tn.asked.clear(),tn.failedAt=-1/0}static key(e){return`v${tn.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}static containing(e){for(const t of tn.asked.values()){const i=t.bounds;if(i.north>=e.north&&i.south<=e.south&&i.east>=e.east&&i.west<=e.west)return t}}async recalled(e,t){const i=await tn.openCache(),n=tn.request(e);let s;if(i)try{const a=await i.match(n);if(a){const{savedAt:o,lines:c}=await a.json();if(Array.isArray(c)){if(this.now()-o<tn.MAX_AGE_MS)return c;s=c}}}catch{}if(this.now()-tn.failedAt<tn.RETRY_AFTER_MS){if(s)return s;throw new Error("The railway survey failed a moment ago: not asked again yet")}let r;try{r=await this.railways.getRailways(t)}catch(a){if(tn.failedAt=this.now(),s)return s;throw a}return i&&tn.store(i,n,{savedAt:this.now(),lines:r}),r}static async store(e,t,i){try{await e.delete(t),await e.put(t,new Response(JSON.stringify(i),{headers:{"Content-Type":"application/json"}}));const n=await e.keys();for(const s of n.slice(0,Math.max(0,n.length-tn.MAX_STORED)))await e.delete(s)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(tn.CACHE_NAME)}catch{return}}static request(e){const t=typeof location>"u"?"http://localhost":location.origin;return new Request(`${t}/__railways__/${encodeURIComponent(e)}`)}}const sm="https://ufoathome.org/railways/index.json";class uc{contemporary=!0;attributionRead;fetchImpl;indexUrls;fallback;index;static shared=new Map;sharing;constructor(e={}){this.sharing=!e.fetchImpl,this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??uc.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Railways © OpenStreetMap contributors"}async getRailways(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getRailways(e);return this.attributionRead=this.fallback.attribution,i}async held(e){const i=(await this.load().catch(()=>[])).find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.lines)?(this.attributionRead=s.attribution,s.lines):void 0}catch{return}}load(){if(this.index)return this.index;if(!this.sharing)return this.index=this.fetchFirstAvailable();const e=this.indexUrls.join(`
3293
- `);let t=uc.shared.get(e);return t||(t=this.fetchFirstAvailable(),uc.shared.set(e,t),t.then(i=>{i.length===0&&uc.shared.delete(e)})),this.index=t}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.129.0")}`,t=typeof location>"u"?void 0:new URL("/railways/index.json",location.href).href;return(t&&t!==sm?[t,sm]:[sm]).map(i=>i+e)}}function T2(){return{elevation:_2(),imagery:I2(),roads:R2(),water:D2(),railways:C2()}}function _2(){return new JA(new Et,new S2)}function I2(){return new A2}function R2(){return new cc({fallback:new t3(new Ya,new Va)})}function D2(){return new hc({fallback:new ps(new i3(new ud,new Co))})}function C2(){return new uc({fallback:new tn(new n3(new Ka,new dc))})}const s3=new Set,Tc="show-observer-map",_c="hide-milestones",Zo="minified";class Ft extends HTMLElement{static get observedAttributes(){return["src",Tc,_c,Zo]}shadow;stageElement;canvas;canvasRenderer;chromeCanvas;chromeRenderer;chromeResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>{this.chromeDirty&&this.refresh()});chromeDirty=!1;tooltip;toolbar;issuesBox;issuesButton;issuesPanel;playPauseButton;fullscreenButton;cornerButtons;observerMapButton;referencesButton;tracesButton;tracesShownState=!0;referenceOpacityInput;referencesShownState=!0;accountHidden=!1;referenceViews=new Map;referencesMenu;referenceOpacityTouched=!1;milestonesButton;observerMapPanel;observerMapCanvas;observerMapRenderer;mapZoomInButton;mapZoomOutButton;mapFitButton;observerMapView=new qt;observerMapDrag;suppressObserverMapClick=!1;observerMapImageryTimer;observerPlacing=!1;seekInput;playbackFlash;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;timePill;muteButton;volumeInput;controlsRow;momentsBox;momentsButton;momentsCurrent;momentsMenu;moreButton;controlsRight;seekSegments;seekDot;playerTitleBox;seekPreview;seekPreviewCanvas;seekPreviewTitle;seekPreviewTime;observerPath;observerMapBounds;observerMapImagery;observerMapImageryBounds;observerMapImageryViewWidth=0;observerMapImageryLoading=!1;observerMapImageryCredit;observerMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Cf.create();issues=[];issueCheck=0;sightingAudio=new BA;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;steadyObserver=!1;static LIVE_BODY_MAX_RATE=2;get steadyBody(){return this.steadyObserver||this.player.playbackState!=="playing"||this.player.playbackRate>Ft.LIVE_BODY_MAX_RATE}selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=f2;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.updateMuteButton(),this.resizeObserverMap()};controlsFitObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.fitControls());controlsMutations;fitControls(){const e=this.controlsRow,t=e.classList.contains("more-open"),i=()=>e.scrollWidth>e.clientWidth+1,n=!this.momentsMenu.hidden;if(this.momentsMenu.hidden=!0,e.classList.remove("narrow","more-open","nomoments","compact","tiny"),e.clientWidth>0&&i()){e.classList.add("narrow");for(const s of["nomoments","compact","tiny"]){if(!i())break;e.classList.add(s)}t&&e.classList.add("more-open")}this.momentsMenu.hidden=!n,this.updateMoreButton()}setControlsFolded(e){this.controlsRow.classList.toggle("more-open",!e),this.updateMoreButton()}updateMoreButton(){const e=this.controlsRow.classList.contains("more-open");Ft.setIcon(this.moreButton,e?qe.UNFOLDED:qe.FOLDED);const t=e?this.messages.fewerControls:this.messages.moreControls;this.moreButton.title=t,this.moreButton.setAttribute("aria-label",t),this.moreButton.setAttribute("aria-expanded",String(e))}addControl(e){this.controlsRight.insertBefore(e,this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null),this.fitControls()}observerMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeObserverMap());resizeObserverMap(){this.observerMapPanel.hidden||(this.sizeObserverMapCanvas(),this.paintObserverMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),i=t&&this.shapeAt(t.x,t.y),n=this.said.read(i?.shape.title);if(!n){this.tooltip.hidden=!0;return}this.tooltip.textContent=n,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handleObserverMapPointerMove=e=>{if(this.dragObserverMap(e))return;const t=this.observerMapTargetFrom(e);if(!t){if(!this.observerPlacing){this.tooltip.hidden=!0,this.observerMapCanvas.style.cursor="grab";return}this.observerMapCanvas.style.cursor="crosshair",this.showObserverMapTooltip(e,this.messages.placeObserverHere);return}this.observerMapCanvas.style.cursor="pointer",this.showObserverMapTooltip(e,t.label)};showObserverMapTooltip(e,t){this.tooltip.textContent=t,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`}handleObserverMapClick=e=>{if(this.suppressObserverMapClick){this.suppressObserverMapClick=!1;return}const t=this.observerMapTargetFrom(e);if(!t){this.observerPlacing&&this.placeObserverFrom(e);return}if(t.kind==="milestone"&&t.t!==void 0){this.player.seek(t.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:t.lat,lng:t.lng,kind:t.kind},bubbles:!0,composed:!0}))};observerMapTargetFrom(e){const t=this.observerMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.observerMapRenderer.hitTest((e.clientX-t.left)/t.width*this.observerMapCanvas.width,(e.clientY-t.top)/t.height*this.observerMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};handleObserverMapPointerDown=e=>{e.button===0&&(this.suppressObserverMapClick=!1,this.observerMapDrag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,moved:!1})};handleObserverMapPointerUp=e=>{const t=this.observerMapDrag;!t||t.pointerId!==e.pointerId||(this.observerMapDrag=void 0,this.observerMapCanvas.classList.remove("dragging"),t.moved&&(this.suppressObserverMapClick=!0,this.paintObserverMap(this.currentTime),this.observerMapCanvas.hasPointerCapture?.(e.pointerId)&&this.observerMapCanvas.releasePointerCapture(e.pointerId)))};dragObserverMap(e){const t=this.observerMapDrag;if(!t||t.pointerId!==e.pointerId)return!1;if(!t.moved){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<Ft.OBSERVER_MAP_DRAG_PX)return!1;t.moved=!0,this.tooltip.hidden=!0,this.observerMapCanvas.classList.add("dragging"),this.observerMapCanvas.setPointerCapture?.(e.pointerId)}const i=this.observerMapCanvas.getBoundingClientRect();return i.width>0&&i.height>0&&(this.observerMapView.panBy((e.clientX-t.lastX)/i.width,(e.clientY-t.lastY)/i.height),this.observerMapViewChanged()),t.lastX=e.clientX,t.lastY=e.clientY,!0}static OBSERVER_MAP_DRAG_PX=4;handleObserverMapWheel=e=>{e.preventDefault();const t=this.observerMapCanvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=e.deltaMode===0?e.deltaY:e.deltaY*40;this.zoomObserverMap((e.clientX-t.left)/t.width,(e.clientY-t.top)/t.height,Math.exp(-i*.002))};zoomObserverMap(e,t,i){this.observerMapView.zoomAt(e,t,i),this.observerMapViewChanged()}observerMapViewChanged(){this.mapFitButton.hidden=!this.observerMapView.changed,this.mapZoomInButton.disabled=this.observerMapView.zoomFactor>=qt.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=qt.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},Ft.OBSERVER_MAP_IMAGERY_DELAY_MS))}static OBSERVER_MAP_FOLLOW_MARGIN=.1;static OBSERVER_MAP_IMAGERY_DELAY_MS=300;get observerMapViewBounds(){return this.observerMapBounds&&this.observerMapView.boundsWithin(this.observerMapBounds)}placeObserverFrom(e){const t=this.observerMapViewBounds,i=this.observerMapCanvas.getBoundingClientRect();if(!t||i.width===0||i.height===0)return;const n=qt.pointAt(t,(e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height);this.dispatchEvent(new CustomEvent("observerplace",{detail:n,bubbles:!0,composed:!0}))}constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${PA}</style>${OA}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new $c(this.canvas.getContext("2d")),this.chromeCanvas=this.shadow.getElementById("chrome"),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.issuesBox=this.shadow.getElementById("issues"),this.issuesButton=this.shadow.getElementById("issues-button"),this.issuesPanel=this.shadow.getElementById("issues-panel"),this.issuesButton.addEventListener("click",a=>{a.stopPropagation(),this.issuesPanel.hidden=!this.issuesPanel.hidden,this.issuesButton.setAttribute("aria-expanded",String(!this.issuesPanel.hidden))}),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.observerMapButton=this.shadow.getElementById("observer-map"),this.referencesButton=this.shadow.getElementById("references"),this.tracesButton=this.shadow.getElementById("traces"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.referencesMenu=this.shadow.getElementById("references-menu"),this.milestonesButton=this.shadow.getElementById("milestones"),this.observerMapPanel=this.shadow.getElementById("observer-map-panel"),this.observerMapCanvas=this.shadow.getElementById("observer-map-canvas"),this.observerMapRenderer=new Do(this.observerMapCanvas.getContext("2d")),this.mapZoomInButton=this.shadow.getElementById("map-zoom-in"),this.mapZoomOutButton=this.shadow.getElementById("map-zoom-out"),this.mapFitButton=this.shadow.getElementById("map-fit"),this.seekInput=this.shadow.getElementById("seek"),this.playbackFlash=this.shadow.getElementById("playback-flash"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.timePill=this.shadow.getElementById("time"),this.muteButton=this.shadow.getElementById("mute"),this.controlsRight=this.shadow.getElementById("controls-right"),this.seekSegments=this.shadow.getElementById("seek-segments"),this.seekDot=this.shadow.getElementById("seek-dot"),this.playerTitleBox=this.shadow.getElementById("player-title"),this.seekPreview=this.shadow.getElementById("seek-preview"),this.seekPreviewCanvas=this.shadow.getElementById("seek-preview-canvas"),this.seekPreviewTitle=this.shadow.getElementById("seek-preview-title"),this.seekPreviewTime=this.shadow.getElementById("seek-preview-time"),this.timePill.addEventListener("click",()=>this.toggleTimeDisplay()),this.timePill.addEventListener("keydown",a=>{a.key!=="Enter"&&a.key!==" "||(a.preventDefault(),this.toggleTimeDisplay())}),this.volumeInput=this.shadow.getElementById("volume"),this.controlsRow=this.shadow.querySelector(".controls"),this.momentsBox=this.shadow.getElementById("moments"),this.momentsButton=this.shadow.getElementById("moments-button"),this.momentsCurrent=this.shadow.getElementById("moments-current"),this.momentsMenu=this.shadow.getElementById("moments-menu"),this.momentsButton.addEventListener("click",()=>this.toggleMomentsMenu()),this.moreButton=this.shadow.getElementById("controls-more"),this.moreButton.addEventListener("click",()=>this.setControlsFolded(this.controlsRow.classList.contains("more-open"))),this.muteButton.addEventListener("click",()=>this.toggleMuted()),this.miniPlayed=this.shadow.getElementById("mini-played"),this.miniRemaining=this.shadow.getElementById("mini-remaining"),this.miniMuteButton=this.shadow.getElementById("mini-mute"),this.miniDot=this.shadow.getElementById("mini-dot"),this.miniScrubTime=this.shadow.getElementById("mini-scrub-time");const t=this.shadow.getElementById("mini-bar"),i=t.querySelector(".mini-track"),n=a=>{const o=i.getBoundingClientRect(),c=this.player.seekableDuration;if(o.width<=0||c<=0)return;const h=Math.min(Math.max((a.clientX-o.left)/o.width,0),1)*c;this.player.seek(h),this.miniScrubTime.textContent=this.formatElapsedAt(h),this.miniScrubTime.style.left=this.miniDot.style.left};let s=!1;t.addEventListener("pointerdown",a=>{a.button===0&&(a.stopPropagation(),t.setPointerCapture(a.pointerId),t.classList.add("scrubbing"),s=this.playRequested,this.pause(),this.miniScrubTime.hidden=!1,n(a))}),t.addEventListener("pointermove",a=>{t.hasPointerCapture(a.pointerId)&&n(a)});const r=a=>{t.classList.contains("scrubbing")&&(t.classList.remove("scrubbing"),t.hasPointerCapture(a.pointerId)&&t.releasePointerCapture(a.pointerId),this.miniScrubTime.hidden=!0,s&&this.play())};t.addEventListener("pointerup",r),t.addEventListener("pointercancel",r),t.addEventListener("click",a=>a.stopPropagation()),this.miniMuteButton.addEventListener("click",a=>{a.stopPropagation(),this.toggleMuted()}),this.stageElement.addEventListener("pointerenter",this.handleMinifiedEnter),this.stageElement.addEventListener("pointerleave",this.handleMinifiedLeave),this.volumeInput.addEventListener("input",()=>{this.setVolume(Number(this.volumeInput.value))}),this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.addEventListener("keydown",this.handleFullscreenKey),this.observerMapButton.addEventListener("click",()=>this.toggleObserverMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.tracesButton.addEventListener("click",()=>this.toggleTraces()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.observerMapCanvas.addEventListener("pointermove",this.handleObserverMapPointerMove),this.observerMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.observerMapCanvas.addEventListener("click",this.handleObserverMapClick),this.observerMapCanvas.addEventListener("pointerdown",this.handleObserverMapPointerDown),this.observerMapCanvas.addEventListener("pointerup",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("pointercancel",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("wheel",this.handleObserverMapWheel,{passive:!1}),this.mapZoomInButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,2)),this.mapZoomOutButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,.5)),this.mapFitButton.addEventListener("click",()=>{this.observerMapView.reset(),this.observerMapViewChanged()}),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",a=>{this.suspendForScrub(),this.seekSnapArmed=!0,this.nameMarkUnder(a),this.beginTouchScrub(a)}),this.seekInput.addEventListener("pointerup",a=>{this.seekSnapArmed=!1,this.endTouchScrub(a)}),this.seekInput.addEventListener("pointercancel",a=>this.endTouchScrub(a)),this.seekInput.addEventListener("pointermove",a=>{this.nameMarkUnder(a),this.hoverSeek(a),this.touchScrub(a)}),this.seekInput.addEventListener("pointerleave",()=>{this.seekInput.removeAttribute("title"),this.leaveSeek()}),this.seekInput.addEventListener("keydown",this.handleSeekKey),this.stageElement.addEventListener("pointerdown",()=>{this.handlePointerDown(),this.touchedStage()},!0);for(const a of["touchend","click"])this.stageElement.addEventListener(a,()=>this.sightingAudio.resume(),!0);this.stageElement.addEventListener("pointermove",()=>this.touchedStage()),this.canvas.addEventListener("click",a=>{if(!(!this.enableClickToPlay||this.minified)){if(this.tapRevealedOnly){this.tapRevealedOnly=!1;return}a.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause()}}),this.canvas.addEventListener("dblclick",a=>{!this.enableClickToPlay||this.minified||(a.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),document.addEventListener("pointerdown",this.handleMomentsDismiss,!0),document.addEventListener("keydown",this.handleMomentsDismiss,!0),document.addEventListener("visibilitychange",this.handleVisibility),window.addEventListener("pagehide",this.handleHidden),document.addEventListener("pointerup",this.handlePointerRelease,!0),document.addEventListener("pointercancel",this.handlePointerRelease,!0),this.observerMapResizeObserver?.observe(this.observerMapPanel),this.chromeResizeObserver?.observe(this.chromeCanvas),this.controlsFitObserver?.observe(this.controlsRow),this.controlsFitObserver?.observe(this.controlsRow.querySelector(".controls-left")),typeof MutationObserver<"u"&&(this.controlsMutations=new MutationObserver(()=>this.fitControls()),this.controlsMutations.observe(this.controlsRight,{attributes:!0,attributeFilter:["hidden"],childList:!0,subtree:!0})),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateTracesButton(),this.updateMilestonesButton(),this.refresh()}connectedCallback(){this.saidTexts=void 0,this.loadLocaleMessages();const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),document.removeEventListener("pointerdown",this.handleMomentsDismiss,!0),document.removeEventListener("keydown",this.handleMomentsDismiss,!0),document.removeEventListener("visibilitychange",this.handleVisibility),window.removeEventListener("pagehide",this.handleHidden),document.removeEventListener("pointerup",this.handlePointerRelease,!0),document.removeEventListener("pointercancel",this.handlePointerRelease,!0),window.clearTimeout(this.controlsIdleTimer),this.observerMapResizeObserver?.disconnect(),this.chromeResizeObserver?.disconnect(),this.controlsFitObserver?.disconnect(),this.controlsMutations?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){if(e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===Tc&&this.applyObserverMapDefault(),e===_c&&this.setMilestonesShown(!this.hasAttribute(_c)),e===Zo){const n=this.minified;this.stageElement.classList.toggle("minified",n),n&&t===null&&(this.muted=!0),this.updateStartState()}}get minified(){return this.hasAttribute(Zo)}miniPlayed;miniRemaining;miniMuteButton;handleMinifiedEnter=e=>{!this.minified||e.pointerType==="touch"||(this.minifiedHovered=!0,this.play())};minifiedHovered=!1;handleMinifiedLeave=e=>{!this.minified||e.pointerType==="touch"||(this.minifiedHovered=!1,this.pause(),this.dispatchEvent(new CustomEvent(k2)))};started=!1;updateStartState(){this.playRequested&&(this.started=!0);const e=!this.started&&this.enableClickToPlay;this.stageElement.classList.toggle("unstarted",e),e&&!this.minified?this.canvas.title=this.messages.clickToPlay:this.canvas.removeAttribute("title")}async loadFromSrc(e){this.sightingData=await Vr.recording(e)}get sightingData(){return H0(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=p2(e),this.observerMapFrozenSource&&(this.observerMapFrozen=this.observerMapFrozenSource(this.currentSighting.frozen)),this.issuesPanel.hidden=!0,this.issuesButton.setAttribute("aria-expanded","false"),this.reportIssues(e,this.currentSighting),this.observerMapView.reset(),this.mapFitButton.hidden=!0,this.observerMapAsked=!1,this.player=this.createPlayer(),this.started=!1,this.syncPlayerTitle(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault(),this.minified&&this.minifiedHovered&&this.play()}set playerTitle(e){this.titleOverride=e,this.syncPlayerTitle()}titleOverride;syncPlayerTitle(){const e=this.said.read(this.titleOverride)??this.currentSighting.observer?.title??"";this.playerTitleBox.textContent=e,this.playerTitleBox.hidden=e===""}get recordingIssues(){return this.issues}async reportIssues(e,t){const i=++this.issueCheck;let n=[];try{const{default:s}=await fe(async()=>{const{default:r}=await import("./sightingSchema-Bj3LiRR4.js");return{default:r}},[],import.meta.url);n=new er(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],Xc.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(r3,{detail:{issues:this.issues},bubbles:!0,composed:!0})))}renderIssues(){const e=this.issues.length;this.issuesBox.hidden=e===0,this.shadow.getElementById("issues-count").textContent=String(e);const t=this.messages.recordingIssues.replace("{count}",String(e));this.issuesButton.title=t,this.issuesButton.setAttribute("aria-label",t),this.shadow.getElementById("issues-title").textContent=t,this.shadow.getElementById("issues-list").replaceChildren(...this.issues.map(i=>{const n=document.createElement("li");return n.textContent=Xc.text(i,this.messages.issueTemplates),n}))}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){if(!(this.player.seekableDuration<=0||this.playbackState==="playing")){if(this.clockHeld){this.playAfterHold=!0,this.announcePlayRequest();return}this.togglePlayPause()}}get playRequested(){return this.playbackState==="playing"||this.playAfterHold}announcePlayRequest(){this.started=!0,this.updateStartState(),this.dispatchEvent(new CustomEvent("playrequest"))}clockHeld=!1;playAfterHold=!1;holdClock(e){if(e!==this.clockHeld){if(this.clockHeld=e,e){if(this.playbackState!=="playing")return;this.playAfterHold=!0,this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh(),this.updatePlayPauseButton();return}this.playAfterHold&&(this.playAfterHold=!1,!(this.player.seekableDuration<=0||this.playbackState==="playing")&&(this.sightingAudio.resume(),this.player.play(),this.updatePlayPauseButton()))}}pause(){this.playAfterHold=!1,this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e),e?this.controlsRight.appendChild(this.fullscreenButton):this.cornerButtons.appendChild(this.fullscreenButton)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateMuteButton(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateObserverMap(),this.updateReferences(),this.updateTracesButton(),this.player.seek(this.player.time)}repaintOverlay(){this.quietFrame=!0;try{this.player.seek(this.player.time)}finally{this.quietFrame=!1}}quietFrame=!1;hostControls(e){const t=[this.milestonesButton,this.referencesButton,this.referenceOpacityInput,this.tracesButton,this.observerMapButton];if(e)for(const i of t)e.appendChild(i);else{const i=this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null;for(const n of t)this.controlsRight.insertBefore(n,i)}}get referencesShown(){return this.referencesShownState}get referenceOpacity(){return Number(this.referenceOpacityInput.value)}get severalReferences(){return this.currentSighting.references.length>1}toggleReferences(){if(this.severalReferences){this.toggleReferencesMenu();return}this.referencesShownState=!this.referencesShownState,this.updateReferencesButton(),this.dispatchReferenceView()}updateReferences(){const e=this.currentSighting.references;!this.referenceOpacityTouched&&e.length>0&&(this.referenceOpacityInput.value=String(e[0].opacity));for(const t of[...this.referenceViews.keys()])e.some(i=>i.id===t)||this.referenceViews.delete(t);this.severalReferences?this.referencesMenu.hidden||this.renderReferencesMenu():this.closeReferencesMenu(),this.updateReferencesButton(),this.severalReferences&&this.dispatchReferenceView()}viewOfReference(e){let t=this.referenceViews.get(e.id);return t||(t={shown:!0,opacity:e.opacity},this.referenceViews.set(e.id,t)),t}toggleReferencesMenu(){this.referencesMenu.hidden?this.openReferencesMenu():this.closeReferencesMenu()}openReferencesMenu(){this.renderReferencesMenu(),this.referencesMenu.hidden=!1,this.placeReferencesMenu(),this.referencesButton.setAttribute("aria-expanded","true"),document.addEventListener("pointerdown",this.handleReferencesMenuOutside,!0),document.addEventListener("keydown",this.handleReferencesMenuKey,!0)}closeReferencesMenu(){this.referencesMenu.hidden||(this.referencesMenu.hidden=!0,this.referencesButton.setAttribute("aria-expanded","false"),document.removeEventListener("pointerdown",this.handleReferencesMenuOutside,!0),document.removeEventListener("keydown",this.handleReferencesMenuKey,!0))}handleReferencesMenuOutside=e=>{const t=e.composedPath();t.includes(this.referencesMenu)||t.includes(this.referencesButton)||this.closeReferencesMenu()};handleReferencesMenuKey=e=>{e.key==="Escape"&&(this.closeReferencesMenu(),this.referencesButton.focus())};placeReferencesMenu(){const e=this.referencesButton.getBoundingClientRect(),t=this.referencesMenu,i=4;t.style.bottom=`${Math.max(i,window.innerHeight-e.top+i)}px`,t.style.top="auto";const n=t.getBoundingClientRect().width;t.style.left=`${Math.max(i,Math.min(e.left,window.innerWidth-n-i))}px`}renderReferencesMenu(){const e=this.currentSighting.references.map(t=>{const i=this.viewOfReference(t),n=this.said.read(t.title)??t.id,s=document.createElement("div");s.className="reference-row";const r=document.createElement("label"),a=document.createElement("input");a.type="checkbox",a.checked=i.shown;const o=document.createElement("span");o.textContent=n,o.title=n,r.append(a,o);const c=document.createElement("input");return c.type="range",c.min="0",c.max="1",c.step="0.05",c.value=String(i.opacity),c.title=this.messages.referenceOpacity,c.setAttribute("aria-label",`${this.messages.referenceOpacity}: ${n}`),c.disabled=!i.shown,a.addEventListener("change",()=>{i.shown=a.checked,c.disabled=!i.shown,this.updateReferencesButton(),this.dispatchReferenceView()}),c.addEventListener("input",()=>{i.opacity=Number(c.value),this.dispatchReferenceView()}),s.append(r,c),s});this.referencesMenu.setAttribute("aria-label",this.messages.referencesMenu),this.referencesMenu.replaceChildren(...e)}updateReferencesButton(){const e=this.currentSighting.references.length>0,t=this.severalReferences,i=t?this.currentSighting.references.some(s=>this.viewOfReference(s).shown):this.referencesShownState;this.referencesButton.hidden=!e,this.referenceOpacityInput.hidden=!e||!this.referencesShownState||t,this.referencesButton.setAttribute("aria-pressed",String(i)),t?this.referencesButton.setAttribute("aria-haspopup","true"):this.referencesButton.removeAttribute("aria-haspopup"),Ft.setIcon(this.referencesButton,i?qe.PICTURES_ON:qe.PICTURES_OFF);const n=t?this.messages.referencesMenu:this.referencesShownState?this.messages.hideReferences:this.messages.showReferences;this.referencesButton.title=n,this.referencesButton.setAttribute("aria-label",n),this.referenceOpacityInput.title=this.messages.referenceOpacity,this.referenceOpacityInput.setAttribute("aria-label",this.messages.referenceOpacity)}get tracesShown(){return this.tracesShownState}toggleTraces(){this.tracesShownState=!this.tracesShownState,this.updateTracesButton(),this.updateObserverMap(),this.dispatchEvent(new CustomEvent("traceview",{detail:{shown:this.tracesShown}}))}updateTracesButton(){this.tracesButton.hidden=this.currentSighting.traces.length===0,this.tracesButton.setAttribute("aria-pressed",String(this.tracesShownState)),Ft.setIcon(this.tracesButton,this.tracesShownState?qe.TRACES_ON:qe.TRACES_OFF);const e=this.tracesShownState?this.messages.hideTraces:this.messages.showTraces;this.tracesButton.title=e,this.tracesButton.setAttribute("aria-label",e)}dispatchReferenceView(){const e=this.severalReferences?Object.fromEntries(this.currentSighting.references.map(t=>[t.id,{...this.viewOfReference(t)}])):void 0;this.dispatchEvent(new CustomEvent("referenceview",{detail:{shown:this.referencesShown,opacity:this.referenceOpacity,views:e}}))}refreshMilestoneMarks(){const e=this.player.seekableDuration,t=e>0?al(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(i=>{const n=document.createElement("button");n.type="button",n.className="milestone-mark",n.style.left=`${Math.min(Math.max(i.t/e,0),1)*100}%`;const s=this.said.read(i.label)??"",r=this.said.read(i.note),a=r?`${s} — ${r}`:s;return n.title=a,n.setAttribute("aria-label",a),n.dataset.t=String(i.t),n.addEventListener("click",()=>this.player.seek(i.t)),n})),this.refreshSeekSegments(e,t.map(i=>i.t)),this.refreshMomentsPicker(t)}refreshMomentsPicker(e){const t=this.milestonesShown&&e.length>0;this.momentsBox.hidden=!t,this.closeMomentsMenu(),this.momentsMenu.replaceChildren(...e.map(i=>{const n=document.createElement("li"),s=document.createElement("button");s.type="button",s.dataset.t=String(i.t);const r=this.said.read(i.label)??"",a=this.said.read(i.note);return s.textContent=a?`${r} — ${a}`:r,s.addEventListener("click",()=>{this.player.seek(i.t),this.closeMomentsMenu()}),n.appendChild(s),n})),this.momentsButton.title=this.messages.moments,this.showCurrentMoment(void 0)}showCurrentMoment(e){const i=(e?this.said.read(e.label)??"":"")||this.messages.moments;this.momentsCurrent.textContent!==i&&(this.momentsCurrent.textContent=i);for(const n of this.momentsMenu.querySelectorAll("button")){const s=e!==void 0&&Number(n.dataset.t)===e.t;n.getAttribute("aria-current")==="true"!==s&&n.setAttribute("aria-current",String(s))}}toggleMomentsMenu(){this.momentsMenu.hidden?(this.momentsMenu.hidden=!1,this.momentsButton.setAttribute("aria-expanded","true")):this.closeMomentsMenu()}closeMomentsMenu(){this.momentsMenu.hidden=!0,this.momentsButton.setAttribute("aria-expanded","false")}handleMomentsDismiss=e=>{if(!this.momentsMenu.hidden){if(e instanceof KeyboardEvent){e.key==="Escape"&&this.closeMomentsMenu();return}e.composedPath().some(t=>t===this.momentsMenu||t===this.momentsButton)||this.closeMomentsMenu()}};seekSegmentList=[];refreshSeekSegments(e,t){const n=[0,...this.milestonesShown&&e>0?[...new Set(t.filter(r=>r>0&&r<e))].sort((r,a)=>r-a):[],Math.max(e,0)];this.seekSegmentList=[];const s=[];for(let r=0;r<n.length-1;r++){const a=document.createElement("div");a.className="seek-segment",a.style.flexGrow=String(Math.max(n[r+1]-n[r],1));const o=document.createElement("i");o.className="seek-hover";const c=document.createElement("i");c.className="seek-played",a.append(o,c),s.push(a),this.seekSegmentList.push({start:n[r],end:n[r+1],element:a,played:c,hover:o})}this.seekSegments.replaceChildren(...s),this.paintSeekProgress(this.player.time)}static fillOf(e,t){const i=e.end-e.start,n=i>0?(t-e.start)/i:t>=e.end?1:0;return`${Math.min(Math.max(n,0),1)*100}%`}paintMiniProgress(e){const t=this.player.seekableDuration,i=`${t>0?Math.min(Math.max(e/t,0),1)*100:0}%`;this.miniPlayed.style.width=i,this.miniDot.style.left=i,this.miniRemaining.textContent=this.formatRemaining(e)}miniDot;miniScrubTime;paintSeekProgress(e){for(const i of this.seekSegmentList)i.played.style.width=Ft.fillOf(i,e);const t=this.player.seekableDuration;this.seekDot.style.left=`${t>0?Math.min(Math.max(e/t,0),1)*100:0}%`}hoverSeek(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1),s=n*i,r=this.seekSegmentList.length-1;this.seekSegmentList.forEach((p,f)=>{p.hover.style.width=Ft.fillOf(p,s),p.element.classList.toggle("hovered",s>=p.start&&(s<p.end||f===r))});const a=this.milestonesShown?this.sighting.milestones:[],o=a.length>0?em(a,s):void 0,c=o?this.said.read(o.label)??"":"",h=o?this.said.read(o.note):void 0,d=h?`${c} — ${h}`:c;this.seekPreviewTitle.textContent=d,this.seekPreviewTime.textContent=this.formatPosition(s),this.seekPreview.hidden=!1;const u=this.seekPreview.offsetWidth;this.seekPreview.style.left=`${Math.min(Math.max(n*t.width-u/2,0),Math.max(t.width-u,0))}px`,this.paintSeekPreview(s)}leaveSeek(){for(const e of this.seekSegmentList)e.hover.style.width="0",e.element.classList.remove("hovered");this.seekPreview.hidden=!0}seekPreviewPainter;paintSeekPreview(e){const t=this.seekPreviewPainter;this.seekPreviewCanvas.hidden=t===void 0,t?.(e,this.seekPreviewCanvas)}paintChrome(e){const t=this.chromeCanvas.getContext("2d"),i=this.overlayPainter!==void 0||this.selectionShown;if(!i&&!this.chromeDirty)return;const n=this.chromeCanvas.clientWidth,s=this.chromeCanvas.clientHeight;if(!t||typeof t.setTransform!="function"||n===0||s===0){e(this.canvasRenderer);return}const r=window.devicePixelRatio||1,a=Math.max(1,Math.round(n*r)),o=Math.max(1,Math.round(s*r));this.chromeCanvas.width!==a&&(this.chromeCanvas.width=a),this.chromeCanvas.height!==o&&(this.chromeCanvas.height=o),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,a,o),this.chromeDirty=!1,i&&(t.setTransform(a/this.canvas.width,0,0,o/this.canvas.height,0,0),this.chromeRenderer??=new $c(t),this.chromeDirty=!0,e(this.chromeRenderer))}chromeDraw;repaintChrome(){this.chromeDraw&&this.overlayPainter&&this.paintChrome(this.chromeDraw)}scrubbingPointer;beginTouchScrub(e){if(e.pointerType!=="mouse"){this.scrubbingPointer=e.pointerId;try{this.seekInput.setPointerCapture(e.pointerId)}catch{}this.seekToPointer(e)}}touchScrub(e){e.pointerId===this.scrubbingPointer&&this.seekToPointer(e)}endTouchScrub(e){e.pointerId===this.scrubbingPointer&&(this.scrubbingPointer=void 0,this.seekToPointer(e))}seekToPointer(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1);this.player.seek(this.snapSeekToMark(n*i))}seekSnapArmed=!1;static MARK_SNAP_PX=6;markNear(e){const t=this.player.seekableDuration,i=this.seekInput.getBoundingClientRect().width;if(t<=0||i<=0)return;const n=Ft.MARK_SNAP_PX/i*t;let s;for(const r of this.milestoneMarks.children){const a=Number(r.dataset.t);Math.abs(a-e)>n||(!s||Math.abs(a-e)<Math.abs(s.t-e))&&(s={t:a,name:r.title})}return s}snapSeekToMark(e){return this.seekSnapArmed?(this.seekSnapArmed=!1,this.markNear(e)?.t??e):e}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=(e.clientX-t.left)/t.width*i,s=this.markNear(n);s?this.seekInput.title=s.name:this.seekInput.removeAttribute("title")}showMilestoneAt(e){const t=this.milestonesShown&&this.sighting.milestones.length>0?em(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,this.showCurrentMoment(t),!t)return;const i=document.createElement("b");i.textContent=this.said.read(t.label)??"";const n=this.said.read(t.note),s=n?` — ${n}`:"";this.milestoneCaption.replaceChildren(i,document.createTextNode(s))}exposureInstants(e){const t=this.exposureTimes(e),i=1/t.length;return t.map(n=>({shapes:this.shapesAt(n),share:i}))}exposureTimes(e=this.currentTime){const t=ar.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<Ft.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(Ft.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/Ft.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/Ft.EXPOSURE_STEP_PX),s=Math.min(Ft.MAX_TRAVEL_STEPS,Math.max(i,n)),r=[];for(let a=0;a<s;a++)r.push(t.fromMs+t.ms*a/(s-1));return r}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[r,a]of i){const o=n.get(r);if(!o)continue;const c=o.bounds.x+o.bounds.width/2-(a.bounds.x+a.bounds.width/2),h=o.bounds.y+o.bounds.height/2-(a.bounds.y+a.bounds.height/2);s=Math.max(s,Math.hypot(c,h))}return s}shapeAt(e,t,i){if(!this.accountHidden)for(const n of this.exposureTimes()){const s=this.currentSighting.timeline.hitTest(n,e,t,i);if(s)return s}}shapesAt(e){const t=new Map;for(const i of this.currentSighting.timeline.sourceIds){const n=this.currentSighting.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=Xt.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===e)return;this.canvas.width=e,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${e} / ${this.canvas.height}`)}canvasPointFromEvent(e){const t=this.canvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return{x:(e.clientX-t.left)/t.width*this.canvas.width,y:(e.clientY-t.top)/t.height*this.canvas.height}}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const i=this.playbackState!=="playing"?this.highlightedSourceIds:s3;this.canvasRenderer.setStarPoints(Xt.starPointsOf(this.sighting.instrument));const n=(ft(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),r=this.frameShift;if(this.paintsShapes)for(const o of s)for(const[,c]of o.shapes){const h=o.share,d=h===1?c:{...c,transparency:1-(1-c.transparency)*h};this.canvasRenderer.paintShape(this.shifted(d,r))}for(const[o,c]of s[0].shapes)!this.selectionShown||!i.has(o)||(this.paintsShapes?i.size===1?this.canvasRenderer.paintShape({...c,selected:!0}):this.canvasRenderer.paintMemberOutline(c):this.canvasRenderer.paintSelectionOnly(this.shifted(c,r)));const a=this.selectionShown&&i.size>1?Bt.groupBoundsFor([...t].filter(([o])=>i.has(o)).map(([,o])=>o.bounds)):void 0;this.chromeDraw=o=>{a&&o.paintGroupHandles(a),this.overlayPainter?.(o)},this.paintChrome(this.chromeDraw),this.mapShapeBounds=s.flatMap(o=>[...o.shapes.values()].filter(c=>c.transparency<1).map(c=>this.shifted(c,r).bounds)),this.keepObserverMapClear(),this.seekInput.value=String(e),this.paintSeekProgress(e),this.minified&&this.paintMiniProgress(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Sv(this.currentSighting,e))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),!this.quietFrame&&this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}shifted(e,t){return t.x===0&&t.y===0?e:{...e,bounds:{...e.bounds,x:e.bounds.x+t.x,y:e.bounds.y+t.y}}}createPlayer(){const e=new NA(this.currentSighting.timeline,(t,i)=>this.onFrame(t,i));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},e}static SEEK_KEY_STEP_MS=5e3;handleFullscreenKey=e=>{e.key.toLowerCase()!=="f"||e.altKey||e.ctrlKey||e.metaKey||!this.enableClickToPlay||e.composedPath().some(i=>i instanceof HTMLElement&&(i.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(i.tagName)&&i.type!=="range"))||(e.preventDefault(),this.toggleFullscreen())};handleSeekKey=e=>{if(e.altKey||e.ctrlKey||e.metaKey)return;const t=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0;if(t){e.preventDefault();const i=this.player.seekableDuration;this.player.seek(Math.min(i,Math.max(0,this.player.time+t*Ft.SEEK_KEY_STEP_MS)))}else e.key===" "&&(e.preventDefault(),this.togglePlayPause())};togglePlayPause(){if(!(this.player.seekableDuration<=0)){if(this.player.playbackState==="playing")this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh();else if(this.clockHeld){this.playAfterHold=!this.playAfterHold,this.playAfterHold&&this.announcePlayRequest();return}else this.announcePlayRequest(),this.sightingAudio.resume(),this.player.play();this.updatePlayPauseButton(),this.flashPlayback()}}flashPlayback(){const e=this.playbackFlash;e.innerHTML=this.player.playbackState==="playing"?qe.PLAY:qe.PAUSE,e.classList.remove("flashing"),e.offsetWidth,e.classList.add("flashing")}static icons=new WeakMap;static setIcon(e,t){Ft.icons.get(e)!==t&&(Ft.icons.set(e,t),e.innerHTML=t)}set captionHeld(e){this.stageElement.classList.toggle("caption-held",e)}set controlsHeld(e){this.stageElement.classList.toggle("controls-held",e)}static CONTROLS_IDLE_MS=3e3;controlsIdleTimer;wasPlaying=!1;quietControlsValue=!1;get quietControls(){return this.quietControlsValue}set quietControls(e){this.quietControlsValue=e,this.stageElement.classList.toggle("quiet",e)}tapRevealedOnly=!1;get hoverless(){return typeof matchMedia=="function"&&matchMedia("(hover: none)").matches}touchedStage(){if(!this.hoverless)return;this.player.playbackState==="playing"&&!this.stageElement.classList.contains("touched")&&(this.tapRevealedOnly=!0),this.revealControls()}pointerHeld=!1;handlePointerDown=()=>{this.pointerHeld=!0};resumeAfterScrub=!1;suspendForScrub(){this.player.playbackState==="playing"&&(this.resumeAfterScrub=!0,this.stopPlaying())}stopPlaying(){this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh(),this.updatePlayPauseButton()}resumeFromScrub(){this.resumeAfterScrub&&(this.resumeAfterScrub=!1,this.sightingAudio.resume(),this.player.play(),this.updatePlayPauseButton())}handleHidden=()=>{this.player.playbackState==="playing"&&this.stopPlaying(),this.resumeAfterScrub=!1};handleVisibility=()=>{document.visibilityState==="hidden"&&this.handleHidden()};handlePointerRelease=()=>{this.resumeAfterScrub&&window.setTimeout(()=>this.resumeFromScrub(),0),this.pointerHeld&&(this.pointerHeld=!1,this.hoverless&&this.stageElement.classList.contains("touched")&&this.revealControls())};revealControls(){this.stageElement.classList.add("touched"),window.clearTimeout(this.controlsIdleTimer),this.player.playbackState==="playing"&&(this.pointerHeld||!this.momentsMenu.hidden||(this.controlsIdleTimer=window.setTimeout(()=>{this.stageElement.classList.remove("touched"),this.tapRevealedOnly=!1},Ft.CONTROLS_IDLE_MS)))}updatePlayPauseButton(){const e=this.player.playbackState==="playing";this.stageElement.classList.toggle("paused",!e),e!==this.wasPlaying&&e&&this.hoverless&&(!this.quietControlsValue||this.stageElement.classList.contains("touched"))&&this.revealControls(),this.wasPlaying=e,this.updateStartState(),Ft.setIcon(this.playPauseButton,e?qe.PAUSE:qe.PLAY);const t=this.player.seekableDuration>0;this.playPauseButton.disabled=!t;const i=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=i,this.playPauseButton.setAttribute("aria-label",i)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.player.loop=this.loopEnabled}restorePlayback(){const e=this.playbackBeforeClick;e&&(this.currentTime=e.time,e.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}enterSimulatedFullscreen(){const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=e.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const t=e.style;t.setProperty("position","fixed"),t.setProperty("inset","0"),t.setProperty("margin","0"),t.setProperty("max-width","none"),t.setProperty("max-height","none"),t.setProperty("aspect-ratio","auto"),t.setProperty("z-index","2147483647"),t.setProperty("background","#000"),t.setProperty("width","100vw"),t.setProperty("width","100dvw"),t.setProperty("height","100vh"),t.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?e.setAttribute("style",this.styleBeforeSimulatedFullscreen):e.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}toggleObserverMap(){const e=this.observerMapPanel.hidden;this.observerMapAsked=e,this.setObserverMapOpen(e),this.keepObserverMapClear(),this.dispatchEvent(new CustomEvent("observermapchange",{bubbles:!0,composed:!0,detail:{open:e}}))}get observerMapOpen(){return!this.observerMapPanel.hidden}get milestonesVisible(){return this.milestonesShown}observerMapAsked=!1;applyObserverMapDefault(){const e=this.getAttribute(Tc),t=e===null?this.sighting.observerMap??this.observerPath?.travels===!0:e!=="false";this.setObserverMapOpen(t&&this.observerPath!==void 0)}setObserverMapOpen(e){this.observerMapPanel.hidden=!e,this.observerMapPanel.removeAttribute("title"),this.observerMapButton.setAttribute("aria-pressed",String(e)),this.updateObserverMapButton(),e&&(this.sizeObserverMapCanvas(),this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime))}updateObserverMapButton(){const e=!this.observerMapPanel.hidden;Ft.setIcon(this.observerMapButton,e?qe.MAP_ON:qe.MAP_OFF);const t=e?this.messages.hideObserverMap:this.messages.showObserverMap;this.observerMapButton.title=t,this.observerMapButton.setAttribute("aria-label",t);const i=[[this.mapZoomInButton,this.messages.zoomMapIn],[this.mapZoomOutButton,this.messages.zoomMapOut],[this.mapFitButton,this.messages.fitMap]];for(const[n,s]of i)n.title=s,n.setAttribute("aria-label",s)}updateObserverMap(){const e=this.observerPath!==void 0;if(this.observerPath=sl.of(this.currentSighting),this.observerMapButton.hidden=this.observerPath===void 0,!e&&this.observerPath&&this.applyObserverMapDefault(),!this.observerPath){this.setObserverMapOpen(!1),this.observerMapBounds=void 0;return}const t=this.tracesShownState?this.currentSighting.traces.flatMap(n=>n.points):[],i=qt.fitted(this.observerPath,t);(!this.observerMapBounds||!this.sameGround(this.observerMapBounds,i))&&(this.observerMapBounds&&this.observerMapView.refit(this.observerMapBounds,i),this.observerMapBounds=i,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||this.loadObserverMapImagery())}sameGround(e,t){const i=Math.abs(e.east-e.west)/20;return Math.abs(e.north-t.north)<i&&Math.abs(e.south-t.south)<i&&Math.abs(e.west-t.west)<i&&Math.abs(e.east-t.east)<i}async loadObserverMapImagery(){const e=this.observerMapViewBounds;if(!e||this.observerMapPanel.hidden||this.observerMapImageryLoading||(this.observerMapFrozenImagery?this.observerMapImageryBounds!==void 0:!this.observerMapImageryNeededFor(e)))return;const t=this.observerMapView.changed,i=t?qt.expand(e,Ft.OBSERVER_MAP_IMAGERY_AHEAD):e,n=qt.IMAGERY_PX*(t?1+2*Ft.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=this.observerMapFrozenImagery,r=s?void 0:I2();try{const a=r?await r.getImageryTexture(i,{width:n,height:n}):await s.getImagery();if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=a,this.observerMapImageryCredit=(r??s).attribution,this.observerMapImageryFailed=!1}catch{if(this.observerMapImageryBounds!==i)return;this.observerMapImageryFailed=this.observerMapImagery===void 0}finally{this.observerMapImageryBounds===i&&(this.observerMapImageryLoading=!1)}this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}static OBSERVER_MAP_IMAGERY_AHEAD=.5;static OBSERVER_MAP_IMAGERY_LOOKAHEAD=.25;observerMapImageryNeededFor(e){const t=this.observerMapImageryBounds;if(!t)return!0;const i=this.observerMapImagery?.bounds??t,n=this.observerMapView.changed?qt.expand(e,Ft.OBSERVER_MAP_IMAGERY_LOOKAHEAD):e,s=n.west>=i.west&&n.east<=i.east&&n.south>=i.south&&n.north<=i.north,r=e.east-e.west>this.observerMapImageryViewWidth/2;return!s||!r}get observerMapCredit(){return this.observerMapImageryCredit}set observerMapFrozen(e){e!==this.observerMapFrozenImagery&&(this.observerMapFrozenImagery=e,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||(this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime)))}get observerMapFrozen(){return this.observerMapFrozenImagery}observerMapFrozenImagery;observerMapFrozenSource;get observerMapFooterLine(){return this.observerMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.observerMapImageryCredit}mapShapeBounds=[];mapSceneBounds=[];setMapSubjectBounds(e){this.mapSceneBounds=e.map(t=>({x:t.x*this.canvas.width,y:t.y*this.canvas.height,width:t.width*this.canvas.width,height:t.height*this.canvas.height})),this.keepObserverMapClear()}keepObserverMapClear(){if(this.observerMapPanel.hidden)return;const e=this.observerMapBoxPx;if(!e)return;const t=[...this.mapShapeBounds,...this.mapSceneBounds],i=(c,h,d)=>{for(const u of t)if(!(u.x+u.width<c-d||u.x>h+d)&&!(u.y>e.bottom+d||u.y+u.height<e.top-d))return!0;return!1},n=[this.canvas.width-e.width,this.canvas.width],s=[0,e.width],r=Ft.OBSERVER_MAP_CLEARANCE_PX,a=i(...n,0),o=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",a&&o&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!a&&(o||!i(n[0],n[1],r))&&this.observerMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.observerMapPanel.classList.add("on-the-left")}observerMapBoxPx;static OBSERVER_MAP_CLEARANCE_PX=24;sizeObserverMapCanvas(){const e=this.observerMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureObserverMapBox(),this.observerMapCanvas.width!==t&&(this.observerMapCanvas.width=t,this.observerMapCanvas.height=t)}measureObserverMapBox(){const e=this.observerMapPanel.getBoundingClientRect(),t=this.canvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=this.canvas.width/t.width,n=this.canvas.height/t.height;this.observerMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintObserverMap(e){if(this.observerMapPanel.hidden||!this.observerPath||!this.observerMapBounds)return;const t=ft(this.currentSighting,e);t?.lat!==void 0&&t.lng!==void 0&&!this.observerMapDrag?.moved&&this.observerMapView.keepInView(this.observerMapBounds,{lat:t.lat,lng:t.lng},Ft.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?em(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?al(this.currentSighting.milestones):[]){const a=ft(this.currentSighting,r.t);a?.lat===void 0||a.lng===void 0||n.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:a.lat,lng:a.lng,current:r===s})}this.observerMapRenderer.paint({bounds:this.observerMapViewBounds,imagery:this.observerMapImagery,path:this.observerPath,position:t?.lat!==void 0&&t.lng!==void 0?{lat:t.lat,lng:t.lng}:void 0,headingDeg:t?.headingDeg===void 0?void 0:t.headingDeg+this.lookYawDeg,coneHalfAngleDeg:t?fr.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(Xt.aspectOf(i)):void 0,markers:n,traces:(this.tracesShownState?this.currentSighting.traces:[]).map(r=>({kind:r.kind,points:r.points,color:r.color,label:this.said.read(r.title)})),decor:this.observerMapDecorAt(e),observerLabel:this.said.read(this.currentSighting.observer?.title)??this.messages.observerHere,nightFraction:this.observerMapNightFraction,attribution:this.observerMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.momentsBox.hidden=!e||this.sighting.milestones.length===0,this.refreshSeekSegments(this.player.seekableDuration,al(this.sighting.milestones).map(t=>t.t)),this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintObserverMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0,this.momentsButton.title=this.messages.moments,Ft.setIcon(this.milestonesButton,this.milestonesShown?qe.MILESTONES_ON:qe.MILESTONES_OFF);const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}observerMapDecorAt(e){const t=ft(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=pr(i,e),{lat:s,lng:r}=Ls(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:r,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}observerMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.observerMapNightFraction&&(this.observerMapNightFraction=t,this.paintObserverMap(this.currentTime))}setLookOffset(e,t){e===this.lookYawDeg&&t===this.lookPitchDeg||(this.lookYawDeg=e,this.lookPitchDeg=t,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const e=this.gaitAt(this.currentTime),t=this.lookYawDeg+e.yawDeg,i=this.lookPitchDeg+e.pitchDeg;if(t===0&&i===0)return{x:0,y:0};const n=ft(this.currentSighting,this.currentTime),s=fr.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return ii.bodyAt(this.currentSighting,e,this.steadyBody)}mutedState=!1;get muted(){return this.mutedState}set muted(e){e!==this.mutedState&&(this.mutedState=e,!e&&this.volumeState===0&&(this.volumeState=Ft.RESTORED_VOLUME),this.applyLevel())}volumeState=1;static RESTORED_VOLUME=.5;get volume(){return this.volumeState}set volume(e){this.setVolume(e)}setVolume(e){this.volumeState=Math.min(Math.max(e,0),1),this.mutedState=this.volumeState===0,this.applyLevel()}get level(){return this.mutedState?0:this.volumeState}applyLevel(){this.sightingAudio.setLevel(this.level),this.updateMuteButton(),this.dispatchEvent(new CustomEvent("mutedchange",{bubbles:!0,composed:!0,detail:{muted:this.mutedState,volume:this.volumeState}}))}toggleMuted(){this.hasSound&&(this.muted=!this.mutedState)}ambientSound;get hasSound(){return this.currentSighting.soundTrack.allKeyframes.length>0||this.ambientSound?.(this.currentSighting)===!0}updateMuteButton(){const e=!this.hasSound;this.miniMuteButton.hidden=e,this.miniMuteButton.setAttribute("aria-pressed",String(this.mutedState)),Ft.setIcon(this.miniMuteButton,this.mutedState?qe.MUTED:qe.VOLUME);const t=this.mutedState?this.messages.unmute:this.messages.mute;if(this.miniMuteButton.title=t,this.miniMuteButton.setAttribute("aria-label",t),this.muteButton.disabled=e,this.volumeInput.hidden=e,e){Ft.setIcon(this.muteButton,qe.MUTED),this.muteButton.title=this.messages.noSound,this.muteButton.setAttribute("aria-label",this.messages.noSound),this.muteButton.setAttribute("aria-pressed","true");return}Ft.setIcon(this.muteButton,this.mutedState?qe.MUTED:qe.VOLUME);const i=this.mutedState?this.messages.unmute:this.messages.mute;this.muteButton.title=i,this.muteButton.setAttribute("aria-label",i),this.muteButton.setAttribute("aria-pressed",String(this.mutedState)),this.volumeInput.value=String(this.level)}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;Ft.setIcon(this.fullscreenButton,e?qe.EXIT_FULLSCREEN:qe.ENTER_FULLSCREEN),this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new ss(Ps.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=sd(Ps.preferencesFor(this),cd);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await aA(e);this.messagesLanguage===e&&this.applyMessages(t)}messagesLanguage;updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t,this.timePill.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(this.timePill.setAttribute("role","button"),this.timePill.setAttribute("tabindex","0")):(this.timePill.removeAttribute("role"),this.timePill.removeAttribute("tabindex"))}applyMessages(e){this.messages=e,this.updateTimeLabelTitles(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateTracesButton(),this.updateMilestonesButton(),this.renderIssues()}updateTimeLabels(){const e=this.currentSighting.event,t=Gc(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?N0(e.time):void 0;const i=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&i>0?i/this.realDurationMs:1,this.player.durationOverrideMs=i>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.paintMiniProgress(this.player.time),this.updateTimeLabelTitles()}formatPosition(e){if(this.realDurationMs===void 0)return _a(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?xy(My(this.realStartMs+i)):_a(i)}formatElapsedAt(e){const t=this.currentSighting.timeline.duration;return this.realDurationMs===void 0?_a(e):_a(t>0&&e<=t?e/t*this.realDurationMs:e)}formatRemaining(e){const t=this.currentSighting.timeline.duration;if(this.realDurationMs===void 0)return _a(Math.max(0,t-e));const i=t>0&&e<=t?e/t*this.realDurationMs:e;return _a(Math.max(0,this.realDurationMs-i))}formatEndOfTimeline(){return this.realDurationMs===void 0?_a(this.currentSighting.timeline.duration):this.canShowClockTime?xy(My(this.realStartMs+this.realDurationMs)):_a(this.realDurationMs)}}function My(l){const e=new Date(l);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function xy(l){if(l.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return l.second?`${e(l.hour)}:${e(l.minute??0)}:${e(l.second)}`:`${e(l.hour)}:${e(l.minute??0)}`}function _a(l){const e=Math.round(l/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const V0="rr0-ufo";function Pf(){customElements.get(V0)||customElements.define(V0,Ft)}const r3="recordingissues",k2="minifiedreset";class zh{constructor(e,t){this.seekInput=e,this.layer=t,e.addEventListener("pointerdown",i=>{this.snapArmed=!0,this.nameMarkUnder(i)}),e.addEventListener("pointerup",()=>this.snapArmed=!1),e.addEventListener("pointermove",i=>this.nameMarkUnder(i)),e.addEventListener("pointerleave",()=>e.removeAttribute("title"))}static SNAP_PX=6;marks=[];duration=0;drawn="";snapArmed=!1;show(e,t){const i=t>0?[...e].sort((s,r)=>s.t-r.t):[],n=`${t}|${i.map(s=>`${s.kind}:${s.t}:${s.label}`).join("|")}`;n!==this.drawn&&(this.drawn=n,this.marks=i,this.duration=t,this.layer.replaceChildren(...i.map(s=>{const r=document.createElement("span");return r.className=`timeline-mark ${s.kind}`,r.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`,r})))}snap(e){return this.snapArmed?(this.snapArmed=!1,this.markNear(e)?.t??e):e}next(e){return this.marks.find(t=>t.t>e+zh.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-zh.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=zh.SNAP_PX/t*this.duration;let n;for(const s of this.marks)Math.abs(s.t-e)>i||(!n||Math.abs(s.t-e)<Math.abs(n.t-e)||s.t===n.t&&s.kind==="milestone")&&(n=s);return n}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect();if(t.width<=0||this.duration<=0)return;const i=this.marksNear((e.clientX-t.left)/t.width*this.duration);i?this.seekInput.title=i:this.seekInput.removeAttribute("title")}marksNear(e){const t=this.markNear(e);if(t)return[...new Set(this.marks.filter(i=>i.t===t.t).map(i=>i.label))].join(" · ")}}class pc{static HALF_SPAN_MS=2e3;static MIN_SPEED_MS=1;static at(e,t,i){const n=e.event.place?.[0],s=e.observerTrack;if(!n)return 0;const r=g=>{const m=s.getInterpolatedPoseAt(Math.max(0,g));if(!m||m.lat===void 0||m.lng===void 0)return;const v=rt(m.lat,m.lng,n.lat,n.lng);return{eastM:v.x,northM:-v.z,heading:m.headingDeg}},a=r(t-pc.HALF_SPAN_MS),o=r(t+pc.HALF_SPAN_MS),c=r(t);if(!a||!o||!c)return 0;const h=Math.hypot(o.eastM-a.eastM,o.northM-a.northM),d=2*pc.HALF_SPAN_MS/1e3;if(h/d<pc.MIN_SPEED_MS)return 0;const u=i.heightAt(o.eastM,o.northM)-i.heightAt(a.eastM,a.northM),p=Math.atan2(u,h)*180/Math.PI;if(c.heading===void 0)return p;const f=Math.atan2(o.eastM-a.eastM,o.northM-a.northM)*180/Math.PI;return p*Math.max(0,Math.cos((c.heading-f)*Math.PI/180))}}const a3=.08,Sy=.15,Ey=2,o3=2e3;class l3{context;noiseBuffer;voices=new Map;level=1;master;paused=!0;requested=[];resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("VehicleAudio: Web Audio unavailable, vehicles stay silent:",e);return}}rl.unlock(this.context),this.apply()}setLevel(e){this.level=e,this.master&&(this.master.gain.value=e)}setPaused(e){e!==this.paused&&(this.paused=e,this.apply())}setVoices(e){this.requested=e,this.apply()}dispose(){for(const e of[...this.voices.keys()])this.release(e);this.context?.close(),this.context=void 0,this.master=void 0}masterOf(e){return this.master||(this.master=e.createGain(),this.master.gain.value=this.level,this.master.connect(e.destination)),this.master}apply(){const e=this.context;if(!e)return;const t=this.paused?[]:this.requested.filter(n=>n.inside||n.distanceM<o3),i=new Set(t.map(n=>n.id));for(const n of[...this.voices.keys()])i.has(n)||this.release(n);for(const n of t){const s=this.voices.get(n.id)??this.build(e,n.id);this.retarget(e,s,n)}}build(e,t){const i=e.createGain();i.gain.value=0;const n=e.createBiquadFilter();n.type="lowpass",n.connect(i).connect(this.masterOf(e));const s=e.createOscillator();s.type="sawtooth";const r=e.createOscillator();r.type="sine";const a=e.createGain(),o=e.createBiquadFilter();o.type="lowpass",o.frequency.value=900,s.connect(o),r.connect(o),o.connect(a).connect(n);const c=()=>{const M=e.createBufferSource();return M.buffer=this.noise(e),M.loop=!0,M.loopStart=Math.random()*Ey*.5,M},h=c(),d=e.createBiquadFilter();d.type="bandpass",d.Q.value=4;const u=e.createGain();h.connect(d).connect(u).connect(n);const p=c(),f=e.createBiquadFilter();f.type="lowpass",f.frequency.value=600;const g=e.createGain();p.connect(f).connect(g).connect(n);const m=c(),v=e.createBiquadFilter();v.type="bandpass",v.frequency.value=1400,v.Q.value=.7;const b=e.createGain();m.connect(v).connect(b).connect(n);const w=[s,r,h,p,m];for(const M of w)M.start();const y={nodes:[i,n,a,o,d,u,f,g,v,b,...w],sources:w,firing:s,crank:r,knock:d,engineGain:a,knockGain:u,roadGain:g,windGain:b,cabin:n,output:i};return this.voices.set(t,y),y}retarget(e,t,{profile:i,state:n,noise:s,inside:r,windowsOpen:a,distanceM:o}){const c=e.currentTime,h=(b,w)=>b.setTargetAtTime(w,c,a3),d=n.rpm/60,u=d*i.cylinders/2,p=Math.min(1,n.rpm/i.shiftUpRpm),f=Math.min(1.5,n.speedMps/30);h(t.firing.frequency,u),h(t.crank.frequency,d),h(t.knock.frequency,u*3),h(t.engineGain.gain,i.engineGain*(.25+.75*n.load)*(.5+.5*p)*.3),h(t.knockGain.gain,i.roughness*(.2+.8*n.load)*.4),h(t.roadGain.gain,i.roadGain*f**1.5*.8);const g=!r||a;h(t.windGain.gain,f**2*(g?.5:.06));const m=r?a?3500:700:8e3/(1+o/60);h(t.cabin.frequency,m);const v=r?.5:Math.min(1,8/Math.max(o,1));h(t.output.gain,v*s)}release(e){const t=this.voices.get(e);if(!t)return;this.voices.delete(e);const i=this.context;i&&(t.output.gain.setTargetAtTime(0,i.currentTime,Sy/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},Sy*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*Ey,e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;return this.noiseBuffer=t,t}}class pd{static BY_KIND={car:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},generic:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},van:{cylinders:4,idleRpm:750,kmhPer1000Rpm:[7,12,19,27,35,42],shiftUpRpm:2600,shiftDownRpm:1200,engineGain:.8,roadGain:.7,roughness:.45},truck:{cylinders:6,idleRpm:600,kmhPer1000Rpm:[5,8,12,17,23,30,38,48],shiftUpRpm:1900,shiftDownRpm:1e3,engineGain:1,roadGain:.9,roughness:.55},motorcycle:{cylinders:2,idleRpm:1100,kmhPer1000Rpm:[12,18,24,30,35,40],shiftUpRpm:6e3,shiftDownRpm:2500,engineGain:.9,roadGain:.3,roughness:.3}};static of(e){return pd.BY_KIND[e??"generic"]??pd.BY_KIND.generic}}class jo{constructor(e,t){this.samples=e,this.startMs=t}static SAMPLE_MS=100;static STANDING_MPS=.3;static of(e,t,i,n){const s=jo.SAMPLE_MS,r=[];for(let c=i;c<=n+s/2;c+=s){const h=t(c-s/2),d=t(c+s/2);if(!h||!d)return;r.push(Math.hypot(d.eastM-h.eastM,d.northM-h.northM)/(s/1e3))}const a=[];let o=1;for(let c=0;c<r.length;c++){const h=r[c],d=(r[Math.min(c+1,r.length-1)]-r[Math.max(c-1,0)])/(2*s/1e3);if(h<jo.STANDING_MPS){o=1,a.push({speedMps:h,rpm:e.idleRpm,load:.1,gear:0});continue}const u=h*3.6,p=v=>u/e.kmhPer1000Rpm[v-1]*1e3;for(;o<e.kmhPer1000Rpm.length&&p(o)>e.shiftUpRpm;)o++;for(;o>1&&p(o)<e.shiftDownRpm;)o--;const f=Math.max(p(o),e.idleRpm*(1+.3*Math.min(1,h/3))),g=.15+.5*Math.min(1,(h/35)**2),m=d>.15?Math.min(1,g+d/2.5):d<-.3?.05:g;a.push({speedMps:h,rpm:f,load:m,gear:o})}return new jo(a,i)}at(e){const t=(e-this.startMs)/jo.SAMPLE_MS,i=Math.min(Math.max(Math.floor(t),0),this.samples.length-1),n=this.samples[i],s=this.samples[Math.min(i+1,this.samples.length-1)],r=Math.min(Math.max(t-i,0),1);return{speedMps:n.speedMps+(s.speedMps-n.speedMps)*r,rpm:n.rpm+(s.rpm-n.rpm)*r,load:n.load+(s.load-n.load)*r,gear:r<.5?n.gear:s.gear}}}class zp{drives=new Map;at(e,t){const i=[],n=e.event.place?.[0],s=c=>{const h=e.observerTrack.getInterpolatedPoseAt(c),d=h?.lat??n?.lat,u=h?.lng??n?.lng;if(d===void 0||u===void 0||!n)return;const p=rt(d,u,n.lat,n.lng);return{eastM:p.x,northM:-p.z}},r=Math.max(e.observerTrack.duration,(e.event.durationSeconds??0)*1e3),a=e.vehicle;if(a){const c=pd.of(a.kind),h=this.drive(`observer:${a.kind}`,zp.trackKey(e),()=>jo.of(c,s,0,r));h&&i.push({id:"observer",profile:c,state:h.at(t),noise:a.noise??1,inside:!0,windowsOpen:a.windowsOpen===!0,distanceM:0})}const o=s(t);for(const c of e.decor){if(!c.engine||!o)continue;const h=pd.of(c.engine.kind),d=g=>{const m=pr(c,g);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,zp.decorKey(c,r),()=>jo.of(h,d,0,r));if(!u)continue;const p=pr(c,t),f=Math.hypot(p.eastM-o.eastM,p.northM-o.northM,p.altitudeM);i.push({id:`decor:${c.id}`,profile:h,state:u.at(t),noise:c.engine.noise??1,inside:!1,windowsOpen:!0,distanceM:f})}return i}drive(e,t,i){const n=this.drives.get(e);if(n&&n.key===t)return n.drive;const s=i();return this.drives.set(e,{key:t,drive:s}),s}static trackKey(e){return e.observerTrack.allKeyframes.map(i=>`${i.t}:${i.pose.lat}:${i.pose.lng}`).join(";")}static decorKey(e,t){return`${t}|${e.eastM},${e.northM}|`+(e.track??[]).map(i=>`${i.t}:${i.eastM}:${i.northM}`).join(";")}}const c3="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAARVBMVEX9sST/uyTfoCjNlSikfCqRcCx8Yy21hipTSi4TITEMHTEuMzBqWC3/wSRUX207SFj8/PyaoakmNUd5gYxBPy90fIfMz9RsB4kZAAACEUlEQVRo3u2XyXbDIAxFLeapjts6/f9PrQA3JymT25Cd3iY5xtYVIB6wLCQSiUQikUgk0nzBoVdFZ1xIpaTQbJnPACaNdYeskWwuAhaZoltnUemfnNkL4N49KEI8n0YAEXMvEWISAWQZPw2Xk1MIoF1dSNAzCMy6JsGyCR0wrkMwT3cBeDt+FH8aEHoA68LbM8HRHXQ3f0RE3/jXOOFX+s4djrKpIbzS7K+MmLrxlWCNR8mczjMw92Ar4dZLDXG4kzXinAECcOUPL3jMdN2294/GQGVG4EMHxORNJXoCfG6oT1trvDGM7iIA9N6aTOuuEbB9ra6FyCYr2gjge+fjA4CIi2uujYRoOBSw0A5/D0hT0X4PW0xtuotNpQPYtkt/fdtyM0LP6ZvOI2DtvoxtJWHvx38AxIkevLwXQ2TOA66DziYfLwC4r5wbIlwJo1ys8+VOBMy7/sxdjwIap49lVC3UePZp1mmyit4SuGUf11pjoTHVRVzXUaXFVt87kuEBsWZ055Q+2sXA76LbZYsvKR1ubvJqbKd5swl+GPI31p6L/sNYuDTe3jo+GBdr1J+P9PGawbRUw9VhleD/3ffT9uZ7DhtX1JNHL2CmNRXxYZhwSwBd3aJTyU85/GKOwhxBf5RnVk+75OSiuu+Ax4PQ3Jsg1hTeMUMwJgQp+PKCe2a+JuefFwQnkUgkEolEIpFerW8TL0hlNl2QVAAAAABJRU5ErkJggg==",h3=`
3291
+ `);let t=cc.shared.get(e);return t||(t=this.fetchFirstAvailable(),cc.shared.set(e,t),t.then(i=>{i.length===0&&cc.shared.delete(e)})),this.index=t}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.129.1")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==im?[t,im]:[im]).map(i=>i+e)}}class Ya{attribution="Roads © IGN, BD TOPO (Licence Ouverte 2.0), as drawn today";contemporary=!0;static BY_NATURE={"Type autoroutier":{surface:"paved",widthM:11},"Route à 2 chaussées":{surface:"paved",widthM:7},"Route à 1 chaussée":{surface:"paved",widthM:5.5},"Rond-point":{surface:"paved",widthM:6},Bretelle:{surface:"paved",widthM:5},"Route empierrée":{surface:"gravel",widthM:3.5},Chemin:{surface:"dirt",widthM:3},"Piste cyclable":{surface:"paved",widthM:2.5}};static DEFAULT={surface:"paved",widthM:5};static KINDS={"Type autoroutier":"motorway","Route à 2 chaussées":"dual","Route à 1 chaussée":"road","Rond-point":"roundabout",Bretelle:"slip"};static DIRECTIONS={"Double sens":"both","Sens direct":"forward","Sens inverse":"backward"};platform;constructor(e={}){this.platform=new Dt(e)}static covers(e){return Dt.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getRoads(e){return(await this.platform.roads(e)).flatMap((i,n)=>Ya.wayOf(i,n))}static wayOf(e,t){if(e.geometry.type!=="LineString")return[];const i=e.geometry.coordinates.map(([u,p])=>({lat:p,lng:u}));if(i.length<2)return[];const n=String(e.properties.nature??""),s=Ya.BY_NATURE[n]??Ya.DEFAULT,r=Number(e.properties.largeur_de_chaussee),a=Number(e.properties.nombre_de_voies),o=Number(e.properties.vitesse_moyenne_vl),c=Ya.DIRECTIONS[String(e.properties.sens_de_circulation??"")],h=Ya.KINDS[n],d=[e.properties.cpx_toponyme_route_nommee,e.properties.nom_voie_ban_gauche,e.properties.nom_voie_ban_droite].find(u=>typeof u=="string"&&u.length>0);return[{id:String(e.properties.cleabs??`ign-${t}`),...d?{name:d}:{},surface:s.surface,widthM:Number.isFinite(r)&&r>0?r:s.widthM,...e.properties.urbain===!0?{urban:!0}:{},...h?{kind:h}:{},...Number.isInteger(a)&&a>0?{lanes:a}:{},...c?{direction:c}:{},...Number.isFinite(o)&&o>0?{speedKmh:o}:{},points:i}]}}class t3{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Roads © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere, as drawn today";contemporary=!0;async getRoads(e){if(Ya.covers(e))try{const t=await this.regional.getRoads(e);if(t.length>0)return t}catch{}return this.worldwide.getRoads(e)}}class ud{attribution="Water © IGN, BD TOPO (Licence Ouverte 2.0)";platform;constructor(e={}){this.platform=new Dt(e)}static covers(e){return Dt.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getWater(e){return(await this.platform.waterSurfaces(e)).flatMap((i,n)=>ud.surfacesOf(i,n))}static surfacesOf(e,t){const{type:i,coordinates:n}=e.geometry,s=i==="Polygon"?[n]:i==="MultiPolygon"?n:[],r=String(e.properties.cleabs??`ign-water-${t}`);return s.flatMap((a,o)=>{const c=a.map(h=>ud.open(h.map(d=>({lat:d[1],lng:d[0]})))).filter(h=>h.length>=3);return c.length>0?[{id:s.length>1?`${r}-${o}`:r,rings:c}]:[]})}static open(e){const t=e[0],i=e[e.length-1];return e.length>1&&t.lat===i.lat&&t.lng===i.lng?e.slice(0,-1):e}}class Co{attribution="Water © OpenStreetMap contributors";client;constructor(e={}){this.client=new ys(e)}async getWater(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:30];(way["natural"="water"](${t});way["waterway"="riverbank"](${t});)->.w;(relation["natural"="water"](${t});relation["waterway"="riverbank"](${t});)->.r;.w out geom;.r out body;way(r.r)(${t});out geom;`;return Co.surfacesFrom(await this.client.query(i))}static surfacesFrom(e){const t=e.elements??[],i=new Map;for(const r of t)r.type==="way"&&r.id!==void 0&&r.geometry&&i.set(r.id,r.geometry.map(a=>({lat:a.lat,lng:a.lon})));const n=[],s=new Set;for(const r of t){if(r.type!=="relation")continue;const a=c=>(r.members??[]).filter(h=>h.type==="way"&&h.role===c&&h.ref!==void 0&&i.has(h.ref)).map(h=>(s.add(h.ref),i.get(h.ref))),o=Co.rings(a("inner"));for(const[c,h]of Co.rings(a("outer")).entries()){const d=o.filter(u=>Co.contains(h,u[0]));n.push({id:`osm-r${r.id}-${c}`,rings:[h,...d]})}}for(const r of t)if(!(r.type!=="way"||!r.tags||r.id===void 0||s.has(r.id))&&!(r.tags.natural!=="water"&&r.tags.waterway!=="riverbank"))for(const a of Co.rings([i.get(r.id)??[]]))n.push({id:`osm-w${r.id}`,rings:[a]});return n}static rings(e){const t=(s,r)=>s.lat===r.lat&&s.lng===r.lng,i=e.filter(s=>s.length>=2).map(s=>[...s]),n=[];for(;i.length>0;){let s=i.pop(),r=!0;for(;!t(s[0],s[s.length-1])&&r;){r=!1;for(let a=0;a<i.length;a++){const o=i[a],c=s[s.length-1];if(t(c,o[0]))s=[...s,...o.slice(1)];else if(t(c,o[o.length-1]))s=[...s,...[...o].reverse().slice(1)];else continue;i.splice(a,1),r=!0;break}}s.length>=4&&t(s[0],s[s.length-1])&&n.push(s.slice(0,-1))}return n}static contains(e,t){let i=!1;for(let n=0,s=e.length-1;n<e.length;s=n++){const r=e[n],a=e[s];r.lat>t.lat!=a.lat>t.lat&&t.lng<(a.lng-r.lng)*(t.lat-r.lat)/(a.lat-r.lat)+r.lng&&(i=!i)}return i}}class i3{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Water © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere";async getWater(e){if(ud.covers(e))try{const t=await this.regional.getWater(e);if(t.length>0)return t}catch{}return this.worldwide.getWater(e)}}const nm="https://ufoathome.org/water/index.json";class hc{attributionRead;fetchImpl;indexUrls;fallback;index;static shared=new Map;sharing;constructor(e={}){this.sharing=!e.fetchImpl,this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??hc.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Water © OpenStreetMap contributors"}async getWater(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getWater(e);return this.attributionRead=this.fallback.attribution,i}async held(e){const i=(await this.load().catch(()=>[])).find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.surfaces)?(this.attributionRead=s.attribution,s.surfaces):void 0}catch{return}}load(){if(this.index)return this.index;if(!this.sharing)return this.index=this.fetchFirstAvailable();const e=this.indexUrls.join(`
3292
+ `);let t=hc.shared.get(e);return t||(t=this.fetchFirstAvailable(),hc.shared.set(e,t),t.then(i=>{i.length===0&&hc.shared.delete(e)})),this.index=t}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.129.1")}`,t=typeof location>"u"?void 0:new URL("/water/index.json",location.href).href;return(t&&t!==nm?[t,nm]:[nm]).map(i=>i+e)}}class ps{constructor(e){this.water=e}static CACHE_NAME="rr0-water";static MAX_AGE_MS=720*3600*1e3;static FORMAT=1;static MAX_STORED=400;static asked=new Map;get attribution(){return this.water.attribution}async getWater(e){const t=ps.key(e);let i=ps.asked.get(t);return i===void 0&&(i=this.recalled(t,e),ps.asked.set(t,i),i.catch(()=>ps.asked.delete(t))),structuredClone(await i)}static forget(){ps.asked.clear()}static key(e){return`v${ps.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}async recalled(e,t){const i=await ps.openCache(),n=ps.request(e);if(i)try{const r=await i.match(n);if(r){const{savedAt:a,surfaces:o}=await r.json();if(Date.now()-a<ps.MAX_AGE_MS&&Array.isArray(o))return o;await i.delete(n)}}catch{}const s=await this.water.getWater(t);return i&&ps.store(i,n,s),s}static async store(e,t,i){try{const n={savedAt:Date.now(),surfaces:i};await e.put(t,new Response(JSON.stringify(n),{headers:{"Content-Type":"application/json"}}));const s=await e.keys();for(const r of s.slice(0,Math.max(0,s.length-ps.MAX_STORED)))await e.delete(r)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(ps.CACHE_NAME)}catch{return}}static request(e){const t=typeof location>"u"?"http://localhost":location.origin;return new Request(`${t}/__water__/${encodeURIComponent(e)}`)}}class Ka{attribution="Railways © IGN, BD TOPO (Licence Ouverte 2.0), as drawn today";contemporary=!0;static BY_NATURE={"Voie ferrée principale":{kind:"rail"},LGV:{kind:"rail",highSpeed:!0},"Voie de service":{kind:"rail",service:!0},Métro:{kind:"subway"},Tramway:{kind:"tram"},"Transport urbain":{kind:"light_rail"},"Funiculaire ou crémaillère":{kind:"funicular"}};static GAUGE_MM={Normale:1435,Etroite:1e3,Étroite:1e3,Large:1668};static NO_ALTITUDE=-1e3;platform;constructor(e={}){this.platform=new Dt(e)}static covers(e){return Dt.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getRailways(e){return(await this.platform.railways(e)).flatMap((i,n)=>Ka.linesOf(i,n))}static linesOf(e,t){const{type:i,coordinates:n}=e.geometry,s=i==="LineString"?[n]:i==="MultiLineString"?n:[],r=Ka.BY_NATURE[String(e.properties.nature??"")];if(!r)return[];const a=String(e.properties.cleabs??`ign-rail-${t}`),o=Number(e.properties.nombre_de_voies),c=Number(e.properties.position_par_rapport_au_sol),h=e.properties.cpx_toponyme,d=Ka.GAUGE_MM[String(e.properties.largeur??"")]??1435,u=Ka.electrificationOf(r.kind,d,e.properties.electrifie);return s.flatMap((p,f)=>{const g=p.map(m=>({lat:m[1],lng:m[0],...m.length>2&&Number.isFinite(m[2])&&m[2]>Ka.NO_ALTITUDE?{altitudeM:m[2]}:{}}));return g.length<2?[]:[{id:s.length>1?`${a}-${f}`:a,...typeof h=="string"&&h.length>0?{name:h}:{},kind:r.kind,...r.service?{service:!0}:{},...r.highSpeed?{highSpeed:!0}:{},gaugeMm:d,tracks:Number.isInteger(o)&&o>0?o:1,...u?{electrification:u}:{},level:Number.isFinite(c)?c:0,points:g}]})}static electrificationOf(e,t,i){if(i===!1)return"none";if(i===!0){if(e==="subway")return"third_rail";if(!(e==="funicular"||t<1435))return"overhead"}}}class In{static NEAR_M=15;static ALIGNED_DEG=30;static SAMPLE_M=10;static ROUTES="train|subway|light_rail|tram|monorail";static KINDS="rail|light_rail|subway|tram|narrow_gauge|funicular|preserved";client;constructor(e={}){this.client=new ys(e)}async named(e,t){if(e.length===0)return[...e];const i=`${t.south},${t.west},${t.north},${t.east}`,n=`[out:json][timeout:120];way["railway"~"^(${In.KINDS})$"](${i})->.w;.w out tags geom;rel(bw.w)["route"~"^(${In.ROUTES})$"];out body;`;return In.apply(e,await this.client.query(n),t)}static apply(e,t,i){const n={lat:(i.north+i.south)/2,lng:(i.east+i.west)/2},s=In.ways(t,n);return s.length===0?[...e]:e.map(r=>{const a=r.points.map(v=>rt(v.lat,v.lng,n.lat,n.lng)),o=new Map,c=new Map,h=new Map;let d=0;const u=s.filter(v=>v.service===!!r.service);for(const v of In.samples(a)){d++;const b=In.waysAt(u,v);if(b.length===0)continue;const[w]=b;w.name&&o.set(w.name,(o.get(w.name)??0)+1),w.voltageV&&c.set(w.voltageV,(c.get(w.voltageV)??0)+1);for(const y of new Set(b.flatMap(M=>M.services)))h.set(y,(h.get(y)??0)+1)}const p=v=>[...v.entries()].sort((b,w)=>w[1]-b[1])[0]?.[0],f=r.name??p(o),g=r.voltageV??(r.electrification==="none"?void 0:p(c)),m=[...h.entries()].filter(([,v])=>v*4>=d).sort((v,b)=>b[1]-v[1]||v[0].localeCompare(b[0])).map(([v])=>v);return{...r,...f?{name:f}:{},...g?{voltageV:g}:{},...m.length>0?{services:m}:{}}})}static serviceOf(e){const t=e.name?.split(" : ")[0].replace(/\s*\([^)]*\)\s*$/,"").trim();return t||[e.network,e.ref].filter(Boolean).join(" ")||void 0}static servicesByWay(e){const t=new Map;for(const i of e.elements??[]){if(i.type!=="relation"||!i.tags)continue;const n=In.serviceOf(i.tags);if(n)for(const s of i.members??[]){if(s.type!=="way"||s.ref===void 0)continue;const r=t.get(s.ref)??new Set;r.add(n),t.set(s.ref,r)}}return t}static voltageOf(e){const t=Number.parseInt((e.voltage??"").split(";")[0],10);return Number.isFinite(t)&&t>0?t:void 0}static ways(e,t){const i=In.servicesByWay(e),n=[];for(const s of e.elements??[]){if(s.type!=="way"||!s.geometry||s.geometry.length<2)continue;const r=s.tags??{},a=In.voltageOf(r);n.push({service:!!r.service||r.usage==="industrial",...r.name?{name:r.name}:{},...a?{voltageV:a}:{},services:[...i.get(s.id??-1)??[]],path:s.geometry.map(o=>rt(o.lat,o.lon,t.lat,t.lng))})}return n}static samples(e){const t=[];for(let i=0;i<e.length-1;i++){const n=Math.hypot(e[i+1].x-e[i].x,e[i+1].z-e[i].z);if(n<1e-6)continue;const s=(e[i+1].x-e[i].x)/n,r=(e[i+1].z-e[i].z)/n;for(let a=In.SAMPLE_M/2;a<n;a+=In.SAMPLE_M)t.push({x:e[i].x+s*a,z:e[i].z+r*a,dx:s,dz:r})}return t}static waysAt(e,t){const i=Math.cos(In.ALIGNED_DEG*Math.PI/180),n=[];for(const s of e){let r;for(let a=0;a<s.path.length-1;a++){const o=s.path[a],c=s.path[a+1],h=c.x-o.x,d=c.z-o.z,u=Math.hypot(h,d);if(u<1e-6||Math.abs((h*t.dx+d*t.dz)/u)<i)continue;const p=Math.max(0,Math.min(1,((t.x-o.x)*h+(t.z-o.z)*d)/(u*u))),f=Math.hypot(t.x-(o.x+h*p),t.z-(o.z+d*p));f<=In.NEAR_M&&(r===void 0||f<r)&&(r=f)}r!==void 0&&n.push({way:s,distance:r})}return n.sort((s,r)=>s.distance-r.distance).map(({way:s})=>s)}}class dc{attribution="Railways © OpenStreetMap contributors, as surveyed today";contemporary=!0;static KINDS=["rail","light_rail","tram","subway","narrow_gauge","funicular","preserved"];client;constructor(e={}){this.client=new ys(e)}async getRailways(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:25];way["railway"~"^(${dc.KINDS.join("|")})$"](${t})->.w;.w out tags geom;rel(bw.w)["route"~"^(${In.ROUTES})$"];out body;`;return this.linesFrom(await this.client.query(i))}linesFrom(e){const t=[],i=In.servicesByWay(e);for(const n of e.elements??[]){const s=n.geometry,r=n.tags;if(n.type==="relation"||!r||!s||s.length<2)continue;const a=[...i.get(n.id??-1)??[]].sort(),o=In.voltageOf(r),c=dc.kindOf(r),h=Number.parseInt((r.gauge??"").split(";")[0],10),d=Number.parseInt(r.tracks??"",10),u=dc.electrificationOf(r.electrified),p=r.highspeed==="yes"||Number.parseInt(r.maxspeed??"",10)>=250;t.push({id:`osm-rail-${n.id??t.length}`,...r.name?{name:r.name}:{},...a.length>0?{services:a}:{},...o?{voltageV:o}:{},kind:c,...r.service||r.usage==="industrial"?{service:!0}:{},...p?{highSpeed:!0}:{},gaugeMm:Number.isFinite(h)&&h>300?h:r.railway==="narrow_gauge"?1e3:1435,tracks:Number.isFinite(d)&&d>0?d:1,...u?{electrification:u}:{},level:dc.levelOf(r),points:s.map(f=>({lat:f.lat,lng:f.lon}))})}return t}static kindOf(e){switch(e.railway){case"tram":return"tram";case"subway":return"subway";case"light_rail":return"light_rail";case"funicular":return"funicular";default:return"rail"}}static electrificationOf(e){if(e!==void 0){if(e==="no")return"none";if(e==="contact_line"||e==="yes")return"overhead";if(e==="rail"||e==="4th_rail"||e==="ground-level_power_supply")return"third_rail"}}static levelOf(e){const t=Number.parseInt(e.layer??"",10);return e.tunnel&&e.tunnel!=="no"?Number.isFinite(t)&&t<0?t:-1:e.covered==="yes"&&e.location==="underground"?-1:e.bridge&&e.bridge!=="no"?Number.isFinite(t)&&t>0?t:1:0}}class n3{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Railways © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere, as drawn today";contemporary=!0;async getRailways(e){if(!Ka.covers(e))return this.worldwide.getRailways(e);let t;try{if(t=await this.regional.getRailways(e),t.length>0)return t}catch{}try{return await this.worldwide.getRailways(e)}catch(i){if(t)return t;throw i}}}class tn{constructor(e,t=Date.now){this.railways=e,this.now=t}static CACHE_NAME="rr0-railways";static MAX_AGE_MS=720*3600*1e3;static FORMAT=1;static MAX_STORED=400;static RETRY_AFTER_MS=300*1e3;static asked=new Map;static failedAt=-1/0;get attribution(){return this.railways.attribution}get contemporary(){return this.railways.contemporary}async getRailways(e){const t=tn.key(e);let i=tn.asked.get(t)??tn.containing(e);if(i===void 0){const n=this.recalled(t,e);i={bounds:e,answer:n},tn.asked.set(t,i),n.catch(()=>tn.asked.delete(t))}return structuredClone(await i.answer)}static forget(){tn.asked.clear(),tn.failedAt=-1/0}static key(e){return`v${tn.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}static containing(e){for(const t of tn.asked.values()){const i=t.bounds;if(i.north>=e.north&&i.south<=e.south&&i.east>=e.east&&i.west<=e.west)return t}}async recalled(e,t){const i=await tn.openCache(),n=tn.request(e);let s;if(i)try{const a=await i.match(n);if(a){const{savedAt:o,lines:c}=await a.json();if(Array.isArray(c)){if(this.now()-o<tn.MAX_AGE_MS)return c;s=c}}}catch{}if(this.now()-tn.failedAt<tn.RETRY_AFTER_MS){if(s)return s;throw new Error("The railway survey failed a moment ago: not asked again yet")}let r;try{r=await this.railways.getRailways(t)}catch(a){if(tn.failedAt=this.now(),s)return s;throw a}return i&&tn.store(i,n,{savedAt:this.now(),lines:r}),r}static async store(e,t,i){try{await e.delete(t),await e.put(t,new Response(JSON.stringify(i),{headers:{"Content-Type":"application/json"}}));const n=await e.keys();for(const s of n.slice(0,Math.max(0,n.length-tn.MAX_STORED)))await e.delete(s)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(tn.CACHE_NAME)}catch{return}}static request(e){const t=typeof location>"u"?"http://localhost":location.origin;return new Request(`${t}/__railways__/${encodeURIComponent(e)}`)}}const sm="https://ufoathome.org/railways/index.json";class uc{contemporary=!0;attributionRead;fetchImpl;indexUrls;fallback;index;static shared=new Map;sharing;constructor(e={}){this.sharing=!e.fetchImpl,this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??uc.defaultIndexUrls(),this.fallback=e.fallback}get attribution(){return this.attributionRead??this.fallback?.attribution??"Railways © OpenStreetMap contributors"}async getRailways(e){const t=await this.held(e);if(t)return t;if(!this.fallback)return[];const i=await this.fallback.getRailways(e);return this.attributionRead=this.fallback.attribution,i}async held(e){const i=(await this.load().catch(()=>[])).find(({entry:n})=>n.north>=e.north&&n.south<=e.south&&n.east>=e.east&&n.west<=e.west);if(i)try{const n=await this.fetchImpl(new URL(i.entry.file,i.indexUrl).href);if(!n.ok)return;const s=await n.json();return Array.isArray(s?.lines)?(this.attributionRead=s.attribution,s.lines):void 0}catch{return}}load(){if(this.index)return this.index;if(!this.sharing)return this.index=this.fetchFirstAvailable();const e=this.indexUrls.join(`
3293
+ `);let t=uc.shared.get(e);return t||(t=this.fetchFirstAvailable(),uc.shared.set(e,t),t.then(i=>{i.length===0&&uc.shared.delete(e)})),this.index=t}async fetchFirstAvailable(){for(const e of this.indexUrls)try{const t=await this.fetchImpl(e);if(!t.ok)continue;const i=await t.json();if(Array.isArray(i?.entries))return i.entries.map(n=>({entry:n,indexUrl:e}))}catch{}return[]}static defaultIndexUrls(){const e=`?v=${encodeURIComponent("0.129.1")}`,t=typeof location>"u"?void 0:new URL("/railways/index.json",location.href).href;return(t&&t!==sm?[t,sm]:[sm]).map(i=>i+e)}}function T2(){return{elevation:_2(),imagery:I2(),roads:R2(),water:D2(),railways:C2()}}function _2(){return new JA(new Et,new S2)}function I2(){return new A2}function R2(){return new cc({fallback:new t3(new Ya,new Va)})}function D2(){return new hc({fallback:new ps(new i3(new ud,new Co))})}function C2(){return new uc({fallback:new tn(new n3(new Ka,new dc))})}const s3=new Set,Tc="show-observer-map",_c="hide-milestones",Zo="minified";class Ft extends HTMLElement{static get observedAttributes(){return["src",Tc,_c,Zo]}shadow;stageElement;canvas;canvasRenderer;chromeCanvas;chromeRenderer;chromeResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>{this.chromeDirty&&this.refresh()});chromeDirty=!1;tooltip;toolbar;issuesBox;issuesButton;issuesPanel;playPauseButton;fullscreenButton;cornerButtons;observerMapButton;referencesButton;tracesButton;tracesShownState=!0;referenceOpacityInput;referencesShownState=!0;accountHidden=!1;referenceViews=new Map;referencesMenu;referenceOpacityTouched=!1;milestonesButton;observerMapPanel;observerMapCanvas;observerMapRenderer;mapZoomInButton;mapZoomOutButton;mapFitButton;observerMapView=new qt;observerMapDrag;suppressObserverMapClick=!1;observerMapImageryTimer;observerPlacing=!1;seekInput;playbackFlash;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;timePill;muteButton;volumeInput;controlsRow;momentsBox;momentsButton;momentsCurrent;momentsMenu;moreButton;controlsRight;seekSegments;seekDot;playerTitleBox;seekPreview;seekPreviewCanvas;seekPreviewTitle;seekPreviewTime;observerPath;observerMapBounds;observerMapImagery;observerMapImageryBounds;observerMapImageryViewWidth=0;observerMapImageryLoading=!1;observerMapImageryCredit;observerMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Cf.create();issues=[];issueCheck=0;sightingAudio=new BA;soundPreview;player;loopEnabled=!1;playbackBeforeClick;highlightedSourceIds=new Set;paintsShapes=!0;steadyObserver=!1;static LIVE_BODY_MAX_RATE=2;get steadyBody(){return this.steadyObserver||this.player.playbackState!=="playing"||this.player.playbackRate>Ft.LIVE_BODY_MAX_RATE}selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=f2;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.updateMuteButton(),this.resizeObserverMap()};controlsFitObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.fitControls());controlsMutations;fitControls(){const e=this.controlsRow,t=e.classList.contains("more-open"),i=()=>e.scrollWidth>e.clientWidth+1,n=!this.momentsMenu.hidden;if(this.momentsMenu.hidden=!0,e.classList.remove("narrow","more-open","nomoments","compact","tiny"),e.clientWidth>0&&i()){e.classList.add("narrow");for(const s of["nomoments","compact","tiny"]){if(!i())break;e.classList.add(s)}t&&e.classList.add("more-open")}this.momentsMenu.hidden=!n,this.updateMoreButton()}setControlsFolded(e){this.controlsRow.classList.toggle("more-open",!e),this.updateMoreButton()}updateMoreButton(){const e=this.controlsRow.classList.contains("more-open");Ft.setIcon(this.moreButton,e?qe.UNFOLDED:qe.FOLDED);const t=e?this.messages.fewerControls:this.messages.moreControls;this.moreButton.title=t,this.moreButton.setAttribute("aria-label",t),this.moreButton.setAttribute("aria-expanded",String(e))}addControl(e){this.controlsRight.insertBefore(e,this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null),this.fitControls()}observerMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeObserverMap());resizeObserverMap(){this.observerMapPanel.hidden||(this.sizeObserverMapCanvas(),this.paintObserverMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),i=t&&this.shapeAt(t.x,t.y),n=this.said.read(i?.shape.title);if(!n){this.tooltip.hidden=!0;return}this.tooltip.textContent=n,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handleObserverMapPointerMove=e=>{if(this.dragObserverMap(e))return;const t=this.observerMapTargetFrom(e);if(!t){if(!this.observerPlacing){this.tooltip.hidden=!0,this.observerMapCanvas.style.cursor="grab";return}this.observerMapCanvas.style.cursor="crosshair",this.showObserverMapTooltip(e,this.messages.placeObserverHere);return}this.observerMapCanvas.style.cursor="pointer",this.showObserverMapTooltip(e,t.label)};showObserverMapTooltip(e,t){this.tooltip.textContent=t,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`}handleObserverMapClick=e=>{if(this.suppressObserverMapClick){this.suppressObserverMapClick=!1;return}const t=this.observerMapTargetFrom(e);if(!t){this.observerPlacing&&this.placeObserverFrom(e);return}if(t.kind==="milestone"&&t.t!==void 0){this.player.seek(t.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:t.lat,lng:t.lng,kind:t.kind},bubbles:!0,composed:!0}))};observerMapTargetFrom(e){const t=this.observerMapCanvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return this.observerMapRenderer.hitTest((e.clientX-t.left)/t.width*this.observerMapCanvas.width,(e.clientY-t.top)/t.height*this.observerMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};handleObserverMapPointerDown=e=>{e.button===0&&(this.suppressObserverMapClick=!1,this.observerMapDrag={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,moved:!1})};handleObserverMapPointerUp=e=>{const t=this.observerMapDrag;!t||t.pointerId!==e.pointerId||(this.observerMapDrag=void 0,this.observerMapCanvas.classList.remove("dragging"),t.moved&&(this.suppressObserverMapClick=!0,this.paintObserverMap(this.currentTime),this.observerMapCanvas.hasPointerCapture?.(e.pointerId)&&this.observerMapCanvas.releasePointerCapture(e.pointerId)))};dragObserverMap(e){const t=this.observerMapDrag;if(!t||t.pointerId!==e.pointerId)return!1;if(!t.moved){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<Ft.OBSERVER_MAP_DRAG_PX)return!1;t.moved=!0,this.tooltip.hidden=!0,this.observerMapCanvas.classList.add("dragging"),this.observerMapCanvas.setPointerCapture?.(e.pointerId)}const i=this.observerMapCanvas.getBoundingClientRect();return i.width>0&&i.height>0&&(this.observerMapView.panBy((e.clientX-t.lastX)/i.width,(e.clientY-t.lastY)/i.height),this.observerMapViewChanged()),t.lastX=e.clientX,t.lastY=e.clientY,!0}static OBSERVER_MAP_DRAG_PX=4;handleObserverMapWheel=e=>{e.preventDefault();const t=this.observerMapCanvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=e.deltaMode===0?e.deltaY:e.deltaY*40;this.zoomObserverMap((e.clientX-t.left)/t.width,(e.clientY-t.top)/t.height,Math.exp(-i*.002))};zoomObserverMap(e,t,i){this.observerMapView.zoomAt(e,t,i),this.observerMapViewChanged()}observerMapViewChanged(){this.mapFitButton.hidden=!this.observerMapView.changed,this.mapZoomInButton.disabled=this.observerMapView.zoomFactor>=qt.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=qt.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},Ft.OBSERVER_MAP_IMAGERY_DELAY_MS))}static OBSERVER_MAP_FOLLOW_MARGIN=.1;static OBSERVER_MAP_IMAGERY_DELAY_MS=300;get observerMapViewBounds(){return this.observerMapBounds&&this.observerMapView.boundsWithin(this.observerMapBounds)}placeObserverFrom(e){const t=this.observerMapViewBounds,i=this.observerMapCanvas.getBoundingClientRect();if(!t||i.width===0||i.height===0)return;const n=qt.pointAt(t,(e.clientX-i.left)/i.width,(e.clientY-i.top)/i.height);this.dispatchEvent(new CustomEvent("observerplace",{detail:n,bubbles:!0,composed:!0}))}constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${PA}</style>${OA}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new $c(this.canvas.getContext("2d")),this.chromeCanvas=this.shadow.getElementById("chrome"),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.issuesBox=this.shadow.getElementById("issues"),this.issuesButton=this.shadow.getElementById("issues-button"),this.issuesPanel=this.shadow.getElementById("issues-panel"),this.issuesButton.addEventListener("click",a=>{a.stopPropagation(),this.issuesPanel.hidden=!this.issuesPanel.hidden,this.issuesButton.setAttribute("aria-expanded",String(!this.issuesPanel.hidden))}),this.playPauseButton=this.shadow.getElementById("play-pause"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.observerMapButton=this.shadow.getElementById("observer-map"),this.referencesButton=this.shadow.getElementById("references"),this.tracesButton=this.shadow.getElementById("traces"),this.referenceOpacityInput=this.shadow.getElementById("reference-opacity"),this.referencesMenu=this.shadow.getElementById("references-menu"),this.milestonesButton=this.shadow.getElementById("milestones"),this.observerMapPanel=this.shadow.getElementById("observer-map-panel"),this.observerMapCanvas=this.shadow.getElementById("observer-map-canvas"),this.observerMapRenderer=new Do(this.observerMapCanvas.getContext("2d")),this.mapZoomInButton=this.shadow.getElementById("map-zoom-in"),this.mapZoomOutButton=this.shadow.getElementById("map-zoom-out"),this.mapFitButton=this.shadow.getElementById("map-fit"),this.seekInput=this.shadow.getElementById("seek"),this.playbackFlash=this.shadow.getElementById("playback-flash"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.timePill=this.shadow.getElementById("time"),this.muteButton=this.shadow.getElementById("mute"),this.controlsRight=this.shadow.getElementById("controls-right"),this.seekSegments=this.shadow.getElementById("seek-segments"),this.seekDot=this.shadow.getElementById("seek-dot"),this.playerTitleBox=this.shadow.getElementById("player-title"),this.seekPreview=this.shadow.getElementById("seek-preview"),this.seekPreviewCanvas=this.shadow.getElementById("seek-preview-canvas"),this.seekPreviewTitle=this.shadow.getElementById("seek-preview-title"),this.seekPreviewTime=this.shadow.getElementById("seek-preview-time"),this.timePill.addEventListener("click",()=>this.toggleTimeDisplay()),this.timePill.addEventListener("keydown",a=>{a.key!=="Enter"&&a.key!==" "||(a.preventDefault(),this.toggleTimeDisplay())}),this.volumeInput=this.shadow.getElementById("volume"),this.controlsRow=this.shadow.querySelector(".controls"),this.momentsBox=this.shadow.getElementById("moments"),this.momentsButton=this.shadow.getElementById("moments-button"),this.momentsCurrent=this.shadow.getElementById("moments-current"),this.momentsMenu=this.shadow.getElementById("moments-menu"),this.momentsButton.addEventListener("click",()=>this.toggleMomentsMenu()),this.moreButton=this.shadow.getElementById("controls-more"),this.moreButton.addEventListener("click",()=>this.setControlsFolded(this.controlsRow.classList.contains("more-open"))),this.muteButton.addEventListener("click",()=>this.toggleMuted()),this.miniPlayed=this.shadow.getElementById("mini-played"),this.miniRemaining=this.shadow.getElementById("mini-remaining"),this.miniMuteButton=this.shadow.getElementById("mini-mute"),this.miniDot=this.shadow.getElementById("mini-dot"),this.miniScrubTime=this.shadow.getElementById("mini-scrub-time");const t=this.shadow.getElementById("mini-bar"),i=t.querySelector(".mini-track"),n=a=>{const o=i.getBoundingClientRect(),c=this.player.seekableDuration;if(o.width<=0||c<=0)return;const h=Math.min(Math.max((a.clientX-o.left)/o.width,0),1)*c;this.player.seek(h),this.miniScrubTime.textContent=this.formatElapsedAt(h),this.miniScrubTime.style.left=this.miniDot.style.left};let s=!1;t.addEventListener("pointerdown",a=>{a.button===0&&(a.stopPropagation(),t.setPointerCapture(a.pointerId),t.classList.add("scrubbing"),s=this.playRequested,this.pause(),this.miniScrubTime.hidden=!1,n(a))}),t.addEventListener("pointermove",a=>{t.hasPointerCapture(a.pointerId)&&n(a)});const r=a=>{t.classList.contains("scrubbing")&&(t.classList.remove("scrubbing"),t.hasPointerCapture(a.pointerId)&&t.releasePointerCapture(a.pointerId),this.miniScrubTime.hidden=!0,s&&this.play())};t.addEventListener("pointerup",r),t.addEventListener("pointercancel",r),t.addEventListener("click",a=>a.stopPropagation()),this.miniMuteButton.addEventListener("click",a=>{a.stopPropagation(),this.toggleMuted()}),this.stageElement.addEventListener("pointerenter",this.handleMinifiedEnter),this.stageElement.addEventListener("pointerleave",this.handleMinifiedLeave),this.volumeInput.addEventListener("input",()=>{this.setVolume(Number(this.volumeInput.value))}),this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.addEventListener("keydown",this.handleFullscreenKey),this.observerMapButton.addEventListener("click",()=>this.toggleObserverMap()),this.referencesButton.addEventListener("click",()=>this.toggleReferences()),this.tracesButton.addEventListener("click",()=>this.toggleTraces()),this.referenceOpacityInput.addEventListener("input",()=>{this.referenceOpacityTouched=!0,this.dispatchReferenceView()}),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.observerMapCanvas.addEventListener("pointermove",this.handleObserverMapPointerMove),this.observerMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.observerMapCanvas.addEventListener("click",this.handleObserverMapClick),this.observerMapCanvas.addEventListener("pointerdown",this.handleObserverMapPointerDown),this.observerMapCanvas.addEventListener("pointerup",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("pointercancel",this.handleObserverMapPointerUp),this.observerMapCanvas.addEventListener("wheel",this.handleObserverMapWheel,{passive:!1}),this.mapZoomInButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,2)),this.mapZoomOutButton.addEventListener("click",()=>this.zoomObserverMap(.5,.5,.5)),this.mapFitButton.addEventListener("click",()=>{this.observerMapView.reset(),this.observerMapViewChanged()}),this.seekInput.addEventListener("input",()=>this.player.seek(this.snapSeekToMark(Number(this.seekInput.value)))),this.seekInput.addEventListener("pointerdown",a=>{this.suspendForScrub(),this.seekSnapArmed=!0,this.nameMarkUnder(a),this.beginTouchScrub(a)}),this.seekInput.addEventListener("pointerup",a=>{this.seekSnapArmed=!1,this.endTouchScrub(a)}),this.seekInput.addEventListener("pointercancel",a=>this.endTouchScrub(a)),this.seekInput.addEventListener("pointermove",a=>{this.nameMarkUnder(a),this.hoverSeek(a),this.touchScrub(a)}),this.seekInput.addEventListener("pointerleave",()=>{this.seekInput.removeAttribute("title"),this.leaveSeek()}),this.seekInput.addEventListener("keydown",this.handleSeekKey),this.stageElement.addEventListener("pointerdown",()=>{this.handlePointerDown(),this.touchedStage()},!0);for(const a of["touchend","click"])this.stageElement.addEventListener(a,()=>this.sightingAudio.resume(),!0);this.stageElement.addEventListener("pointermove",()=>this.touchedStage()),this.canvas.addEventListener("click",a=>{if(!(!this.enableClickToPlay||this.minified)){if(this.tapRevealedOnly){this.tapRevealedOnly=!1;return}a.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause()}}),this.canvas.addEventListener("dblclick",a=>{!this.enableClickToPlay||this.minified||(a.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),document.addEventListener("pointerdown",this.handleMomentsDismiss,!0),document.addEventListener("keydown",this.handleMomentsDismiss,!0),document.addEventListener("visibilitychange",this.handleVisibility),window.addEventListener("pagehide",this.handleHidden),document.addEventListener("pointerup",this.handlePointerRelease,!0),document.addEventListener("pointercancel",this.handlePointerRelease,!0),this.observerMapResizeObserver?.observe(this.observerMapPanel),this.chromeResizeObserver?.observe(this.chromeCanvas),this.controlsFitObserver?.observe(this.controlsRow),this.controlsFitObserver?.observe(this.controlsRow.querySelector(".controls-left")),typeof MutationObserver<"u"&&(this.controlsMutations=new MutationObserver(()=>this.fitControls()),this.controlsMutations.observe(this.controlsRight,{attributes:!0,attributeFilter:["hidden"],childList:!0,subtree:!0})),this.hostControls(void 0),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateTracesButton(),this.updateMilestonesButton(),this.refresh()}connectedCallback(){this.saidTexts=void 0,this.loadLocaleMessages();const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),document.removeEventListener("pointerdown",this.handleMomentsDismiss,!0),document.removeEventListener("keydown",this.handleMomentsDismiss,!0),document.removeEventListener("visibilitychange",this.handleVisibility),window.removeEventListener("pagehide",this.handleHidden),document.removeEventListener("pointerup",this.handlePointerRelease,!0),document.removeEventListener("pointercancel",this.handlePointerRelease,!0),window.clearTimeout(this.controlsIdleTimer),this.observerMapResizeObserver?.disconnect(),this.chromeResizeObserver?.disconnect(),this.controlsFitObserver?.disconnect(),this.controlsMutations?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,i){if(e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===Tc&&this.applyObserverMapDefault(),e===_c&&this.setMilestonesShown(!this.hasAttribute(_c)),e===Zo){const n=this.minified;this.stageElement.classList.toggle("minified",n),n&&t===null&&(this.muted=!0),this.updateStartState()}}get minified(){return this.hasAttribute(Zo)}miniPlayed;miniRemaining;miniMuteButton;handleMinifiedEnter=e=>{!this.minified||e.pointerType==="touch"||(this.minifiedHovered=!0,this.play())};minifiedHovered=!1;handleMinifiedLeave=e=>{!this.minified||e.pointerType==="touch"||(this.minifiedHovered=!1,this.pause(),this.dispatchEvent(new CustomEvent(k2)))};started=!1;updateStartState(){this.playRequested&&(this.started=!0);const e=!this.started&&this.enableClickToPlay;this.stageElement.classList.toggle("unstarted",e),e&&!this.minified?this.canvas.title=this.messages.clickToPlay:this.canvas.removeAttribute("title")}async loadFromSrc(e){this.sightingData=await Vr.recording(e)}get sightingData(){return H0(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=p2(e),this.observerMapFrozenSource&&(this.observerMapFrozen=this.observerMapFrozenSource(this.currentSighting.frozen)),this.issuesPanel.hidden=!0,this.issuesButton.setAttribute("aria-expanded","false"),this.reportIssues(e,this.currentSighting),this.observerMapView.reset(),this.mapFitButton.hidden=!0,this.observerMapAsked=!1,this.player=this.createPlayer(),this.started=!1,this.syncPlayerTitle(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault(),this.minified&&this.minifiedHovered&&this.play()}set playerTitle(e){this.titleOverride=e,this.syncPlayerTitle()}titleOverride;syncPlayerTitle(){const e=this.said.read(this.titleOverride)??this.currentSighting.observer?.title??"";this.playerTitleBox.textContent=e,this.playerTitleBox.hidden=e===""}get recordingIssues(){return this.issues}async reportIssues(e,t){const i=++this.issueCheck;let n=[];try{const{default:s}=await fe(async()=>{const{default:r}=await import("./sightingSchema-Bj3LiRR4.js");return{default:r}},[],import.meta.url);n=new er(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],Xc.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(r3,{detail:{issues:this.issues},bubbles:!0,composed:!0})))}renderIssues(){const e=this.issues.length;this.issuesBox.hidden=e===0,this.shadow.getElementById("issues-count").textContent=String(e);const t=this.messages.recordingIssues.replace("{count}",String(e));this.issuesButton.title=t,this.issuesButton.setAttribute("aria-label",t),this.shadow.getElementById("issues-title").textContent=t,this.shadow.getElementById("issues-list").replaceChildren(...this.issues.map(i=>{const n=document.createElement("li");return n.textContent=Xc.text(i,this.messages.issueTemplates),n}))}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){if(!(this.player.seekableDuration<=0||this.playbackState==="playing")){if(this.clockHeld){this.playAfterHold=!0,this.announcePlayRequest();return}this.togglePlayPause()}}get playRequested(){return this.playbackState==="playing"||this.playAfterHold}announcePlayRequest(){this.started=!0,this.updateStartState(),this.dispatchEvent(new CustomEvent("playrequest"))}clockHeld=!1;playAfterHold=!1;holdClock(e){if(e!==this.clockHeld){if(this.clockHeld=e,e){if(this.playbackState!=="playing")return;this.playAfterHold=!0,this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh(),this.updatePlayPauseButton();return}this.playAfterHold&&(this.playAfterHold=!1,!(this.player.seekableDuration<=0||this.playbackState==="playing")&&(this.sightingAudio.resume(),this.player.play(),this.updatePlayPauseButton()))}}pause(){this.playAfterHold=!1,this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e),e?this.controlsRight.appendChild(this.fullscreenButton):this.cornerButtons.appendChild(this.fullscreenButton)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(n=>this.highlightedSourceIds.has(n))||(this.highlightedSourceIds=t,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateMuteButton(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateObserverMap(),this.updateReferences(),this.updateTracesButton(),this.player.seek(this.player.time)}repaintOverlay(){this.quietFrame=!0;try{this.player.seek(this.player.time)}finally{this.quietFrame=!1}}quietFrame=!1;hostControls(e){const t=[this.milestonesButton,this.referencesButton,this.referenceOpacityInput,this.tracesButton,this.observerMapButton];if(e)for(const i of t)e.appendChild(i);else{const i=this.fullscreenButton.parentElement===this.controlsRight?this.fullscreenButton:null;for(const n of t)this.controlsRight.insertBefore(n,i)}}get referencesShown(){return this.referencesShownState}get referenceOpacity(){return Number(this.referenceOpacityInput.value)}get severalReferences(){return this.currentSighting.references.length>1}toggleReferences(){if(this.severalReferences){this.toggleReferencesMenu();return}this.referencesShownState=!this.referencesShownState,this.updateReferencesButton(),this.dispatchReferenceView()}updateReferences(){const e=this.currentSighting.references;!this.referenceOpacityTouched&&e.length>0&&(this.referenceOpacityInput.value=String(e[0].opacity));for(const t of[...this.referenceViews.keys()])e.some(i=>i.id===t)||this.referenceViews.delete(t);this.severalReferences?this.referencesMenu.hidden||this.renderReferencesMenu():this.closeReferencesMenu(),this.updateReferencesButton(),this.severalReferences&&this.dispatchReferenceView()}viewOfReference(e){let t=this.referenceViews.get(e.id);return t||(t={shown:!0,opacity:e.opacity},this.referenceViews.set(e.id,t)),t}toggleReferencesMenu(){this.referencesMenu.hidden?this.openReferencesMenu():this.closeReferencesMenu()}openReferencesMenu(){this.renderReferencesMenu(),this.referencesMenu.hidden=!1,this.placeReferencesMenu(),this.referencesButton.setAttribute("aria-expanded","true"),document.addEventListener("pointerdown",this.handleReferencesMenuOutside,!0),document.addEventListener("keydown",this.handleReferencesMenuKey,!0)}closeReferencesMenu(){this.referencesMenu.hidden||(this.referencesMenu.hidden=!0,this.referencesButton.setAttribute("aria-expanded","false"),document.removeEventListener("pointerdown",this.handleReferencesMenuOutside,!0),document.removeEventListener("keydown",this.handleReferencesMenuKey,!0))}handleReferencesMenuOutside=e=>{const t=e.composedPath();t.includes(this.referencesMenu)||t.includes(this.referencesButton)||this.closeReferencesMenu()};handleReferencesMenuKey=e=>{e.key==="Escape"&&(this.closeReferencesMenu(),this.referencesButton.focus())};placeReferencesMenu(){const e=this.referencesButton.getBoundingClientRect(),t=this.referencesMenu,i=4;t.style.bottom=`${Math.max(i,window.innerHeight-e.top+i)}px`,t.style.top="auto";const n=t.getBoundingClientRect().width;t.style.left=`${Math.max(i,Math.min(e.left,window.innerWidth-n-i))}px`}renderReferencesMenu(){const e=this.currentSighting.references.map(t=>{const i=this.viewOfReference(t),n=this.said.read(t.title)??t.id,s=document.createElement("div");s.className="reference-row";const r=document.createElement("label"),a=document.createElement("input");a.type="checkbox",a.checked=i.shown;const o=document.createElement("span");o.textContent=n,o.title=n,r.append(a,o);const c=document.createElement("input");return c.type="range",c.min="0",c.max="1",c.step="0.05",c.value=String(i.opacity),c.title=this.messages.referenceOpacity,c.setAttribute("aria-label",`${this.messages.referenceOpacity}: ${n}`),c.disabled=!i.shown,a.addEventListener("change",()=>{i.shown=a.checked,c.disabled=!i.shown,this.updateReferencesButton(),this.dispatchReferenceView()}),c.addEventListener("input",()=>{i.opacity=Number(c.value),this.dispatchReferenceView()}),s.append(r,c),s});this.referencesMenu.setAttribute("aria-label",this.messages.referencesMenu),this.referencesMenu.replaceChildren(...e)}updateReferencesButton(){const e=this.currentSighting.references.length>0,t=this.severalReferences,i=t?this.currentSighting.references.some(s=>this.viewOfReference(s).shown):this.referencesShownState;this.referencesButton.hidden=!e,this.referenceOpacityInput.hidden=!e||!this.referencesShownState||t,this.referencesButton.setAttribute("aria-pressed",String(i)),t?this.referencesButton.setAttribute("aria-haspopup","true"):this.referencesButton.removeAttribute("aria-haspopup"),Ft.setIcon(this.referencesButton,i?qe.PICTURES_ON:qe.PICTURES_OFF);const n=t?this.messages.referencesMenu:this.referencesShownState?this.messages.hideReferences:this.messages.showReferences;this.referencesButton.title=n,this.referencesButton.setAttribute("aria-label",n),this.referenceOpacityInput.title=this.messages.referenceOpacity,this.referenceOpacityInput.setAttribute("aria-label",this.messages.referenceOpacity)}get tracesShown(){return this.tracesShownState}toggleTraces(){this.tracesShownState=!this.tracesShownState,this.updateTracesButton(),this.updateObserverMap(),this.dispatchEvent(new CustomEvent("traceview",{detail:{shown:this.tracesShown}}))}updateTracesButton(){this.tracesButton.hidden=this.currentSighting.traces.length===0,this.tracesButton.setAttribute("aria-pressed",String(this.tracesShownState)),Ft.setIcon(this.tracesButton,this.tracesShownState?qe.TRACES_ON:qe.TRACES_OFF);const e=this.tracesShownState?this.messages.hideTraces:this.messages.showTraces;this.tracesButton.title=e,this.tracesButton.setAttribute("aria-label",e)}dispatchReferenceView(){const e=this.severalReferences?Object.fromEntries(this.currentSighting.references.map(t=>[t.id,{...this.viewOfReference(t)}])):void 0;this.dispatchEvent(new CustomEvent("referenceview",{detail:{shown:this.referencesShown,opacity:this.referenceOpacity,views:e}}))}refreshMilestoneMarks(){const e=this.player.seekableDuration,t=e>0?al(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(i=>{const n=document.createElement("button");n.type="button",n.className="milestone-mark",n.style.left=`${Math.min(Math.max(i.t/e,0),1)*100}%`;const s=this.said.read(i.label)??"",r=this.said.read(i.note),a=r?`${s} — ${r}`:s;return n.title=a,n.setAttribute("aria-label",a),n.dataset.t=String(i.t),n.addEventListener("click",()=>this.player.seek(i.t)),n})),this.refreshSeekSegments(e,t.map(i=>i.t)),this.refreshMomentsPicker(t)}refreshMomentsPicker(e){const t=this.milestonesShown&&e.length>0;this.momentsBox.hidden=!t,this.closeMomentsMenu(),this.momentsMenu.replaceChildren(...e.map(i=>{const n=document.createElement("li"),s=document.createElement("button");s.type="button",s.dataset.t=String(i.t);const r=this.said.read(i.label)??"",a=this.said.read(i.note);return s.textContent=a?`${r} — ${a}`:r,s.addEventListener("click",()=>{this.player.seek(i.t),this.closeMomentsMenu()}),n.appendChild(s),n})),this.momentsButton.title=this.messages.moments,this.showCurrentMoment(void 0)}showCurrentMoment(e){const i=(e?this.said.read(e.label)??"":"")||this.messages.moments;this.momentsCurrent.textContent!==i&&(this.momentsCurrent.textContent=i);for(const n of this.momentsMenu.querySelectorAll("button")){const s=e!==void 0&&Number(n.dataset.t)===e.t;n.getAttribute("aria-current")==="true"!==s&&n.setAttribute("aria-current",String(s))}}toggleMomentsMenu(){this.momentsMenu.hidden?(this.momentsMenu.hidden=!1,this.momentsButton.setAttribute("aria-expanded","true")):this.closeMomentsMenu()}closeMomentsMenu(){this.momentsMenu.hidden=!0,this.momentsButton.setAttribute("aria-expanded","false")}handleMomentsDismiss=e=>{if(!this.momentsMenu.hidden){if(e instanceof KeyboardEvent){e.key==="Escape"&&this.closeMomentsMenu();return}e.composedPath().some(t=>t===this.momentsMenu||t===this.momentsButton)||this.closeMomentsMenu()}};seekSegmentList=[];refreshSeekSegments(e,t){const n=[0,...this.milestonesShown&&e>0?[...new Set(t.filter(r=>r>0&&r<e))].sort((r,a)=>r-a):[],Math.max(e,0)];this.seekSegmentList=[];const s=[];for(let r=0;r<n.length-1;r++){const a=document.createElement("div");a.className="seek-segment",a.style.flexGrow=String(Math.max(n[r+1]-n[r],1));const o=document.createElement("i");o.className="seek-hover";const c=document.createElement("i");c.className="seek-played",a.append(o,c),s.push(a),this.seekSegmentList.push({start:n[r],end:n[r+1],element:a,played:c,hover:o})}this.seekSegments.replaceChildren(...s),this.paintSeekProgress(this.player.time)}static fillOf(e,t){const i=e.end-e.start,n=i>0?(t-e.start)/i:t>=e.end?1:0;return`${Math.min(Math.max(n,0),1)*100}%`}paintMiniProgress(e){const t=this.player.seekableDuration,i=`${t>0?Math.min(Math.max(e/t,0),1)*100:0}%`;this.miniPlayed.style.width=i,this.miniDot.style.left=i,this.miniRemaining.textContent=this.formatRemaining(e)}miniDot;miniScrubTime;paintSeekProgress(e){for(const i of this.seekSegmentList)i.played.style.width=Ft.fillOf(i,e);const t=this.player.seekableDuration;this.seekDot.style.left=`${t>0?Math.min(Math.max(e/t,0),1)*100:0}%`}hoverSeek(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1),s=n*i,r=this.seekSegmentList.length-1;this.seekSegmentList.forEach((p,f)=>{p.hover.style.width=Ft.fillOf(p,s),p.element.classList.toggle("hovered",s>=p.start&&(s<p.end||f===r))});const a=this.milestonesShown?this.sighting.milestones:[],o=a.length>0?em(a,s):void 0,c=o?this.said.read(o.label)??"":"",h=o?this.said.read(o.note):void 0,d=h?`${c} — ${h}`:c;this.seekPreviewTitle.textContent=d,this.seekPreviewTime.textContent=this.formatPosition(s),this.seekPreview.hidden=!1;const u=this.seekPreview.offsetWidth;this.seekPreview.style.left=`${Math.min(Math.max(n*t.width-u/2,0),Math.max(t.width-u,0))}px`,this.paintSeekPreview(s)}leaveSeek(){for(const e of this.seekSegmentList)e.hover.style.width="0",e.element.classList.remove("hovered");this.seekPreview.hidden=!0}seekPreviewPainter;paintSeekPreview(e){const t=this.seekPreviewPainter;this.seekPreviewCanvas.hidden=t===void 0,t?.(e,this.seekPreviewCanvas)}paintChrome(e){const t=this.chromeCanvas.getContext("2d"),i=this.overlayPainter!==void 0||this.selectionShown;if(!i&&!this.chromeDirty)return;const n=this.chromeCanvas.clientWidth,s=this.chromeCanvas.clientHeight;if(!t||typeof t.setTransform!="function"||n===0||s===0){e(this.canvasRenderer);return}const r=window.devicePixelRatio||1,a=Math.max(1,Math.round(n*r)),o=Math.max(1,Math.round(s*r));this.chromeCanvas.width!==a&&(this.chromeCanvas.width=a),this.chromeCanvas.height!==o&&(this.chromeCanvas.height=o),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,a,o),this.chromeDirty=!1,i&&(t.setTransform(a/this.canvas.width,0,0,o/this.canvas.height,0,0),this.chromeRenderer??=new $c(t),this.chromeDirty=!0,e(this.chromeRenderer))}chromeDraw;repaintChrome(){this.chromeDraw&&this.overlayPainter&&this.paintChrome(this.chromeDraw)}scrubbingPointer;beginTouchScrub(e){if(e.pointerType!=="mouse"){this.scrubbingPointer=e.pointerId;try{this.seekInput.setPointerCapture(e.pointerId)}catch{}this.seekToPointer(e)}}touchScrub(e){e.pointerId===this.scrubbingPointer&&this.seekToPointer(e)}endTouchScrub(e){e.pointerId===this.scrubbingPointer&&(this.scrubbingPointer=void 0,this.seekToPointer(e))}seekToPointer(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=Math.min(Math.max((e.clientX-t.left)/t.width,0),1);this.player.seek(this.snapSeekToMark(n*i))}seekSnapArmed=!1;static MARK_SNAP_PX=6;markNear(e){const t=this.player.seekableDuration,i=this.seekInput.getBoundingClientRect().width;if(t<=0||i<=0)return;const n=Ft.MARK_SNAP_PX/i*t;let s;for(const r of this.milestoneMarks.children){const a=Number(r.dataset.t);Math.abs(a-e)>n||(!s||Math.abs(a-e)<Math.abs(s.t-e))&&(s={t:a,name:r.title})}return s}snapSeekToMark(e){return this.seekSnapArmed?(this.seekSnapArmed=!1,this.markNear(e)?.t??e):e}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect(),i=this.player.seekableDuration;if(t.width<=0||i<=0)return;const n=(e.clientX-t.left)/t.width*i,s=this.markNear(n);s?this.seekInput.title=s.name:this.seekInput.removeAttribute("title")}showMilestoneAt(e){const t=this.milestonesShown&&this.sighting.milestones.length>0?em(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,this.showCurrentMoment(t),!t)return;const i=document.createElement("b");i.textContent=this.said.read(t.label)??"";const n=this.said.read(t.note),s=n?` — ${n}`:"";this.milestoneCaption.replaceChildren(i,document.createTextNode(s))}exposureInstants(e){const t=this.exposureTimes(e),i=1/t.length;return t.map(n=>({shapes:this.shapesAt(n),share:i}))}exposureTimes(e=this.currentTime){const t=ar.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<Ft.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(Ft.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/Ft.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/Ft.EXPOSURE_STEP_PX),s=Math.min(Ft.MAX_TRAVEL_STEPS,Math.max(i,n)),r=[];for(let a=0;a<s;a++)r.push(t.fromMs+t.ms*a/(s-1));return r}travelPxOver(e,t){const i=this.shapesAt(e),n=this.shapesAt(e+t);let s=0;for(const[r,a]of i){const o=n.get(r);if(!o)continue;const c=o.bounds.x+o.bounds.width/2-(a.bounds.x+a.bounds.width/2),h=o.bounds.y+o.bounds.height/2-(a.bounds.y+a.bounds.height/2);s=Math.max(s,Math.hypot(c,h))}return s}shapeAt(e,t,i){if(!this.accountHidden)for(const n of this.exposureTimes()){const s=this.currentSighting.timeline.hitTest(n,e,t,i);if(s)return s}}shapesAt(e){const t=new Map;for(const i of this.currentSighting.timeline.sourceIds){const n=this.currentSighting.timeline.getInterpolatedShapeAt(e,i);n&&t.set(i,n)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=Xt.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===e)return;this.canvas.width=e,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${e} / ${this.canvas.height}`)}canvasPointFromEvent(e){const t=this.canvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return{x:(e.clientX-t.left)/t.width*this.canvas.width,y:(e.clientY-t.top)/t.height*this.canvas.height}}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const i=this.playbackState!=="playing"?this.highlightedSourceIds:s3;this.canvasRenderer.setStarPoints(Xt.starPointsOf(this.sighting.instrument));const n=(ft(this.sighting,e)?.rollDeg??0)+this.gaitAt(e).rollDeg;this.canvasRenderer.setRoll(n*Math.PI/180);const s=this.exposureInstants(e),r=this.frameShift;if(this.paintsShapes)for(const o of s)for(const[,c]of o.shapes){const h=o.share,d=h===1?c:{...c,transparency:1-(1-c.transparency)*h};this.canvasRenderer.paintShape(this.shifted(d,r))}for(const[o,c]of s[0].shapes)!this.selectionShown||!i.has(o)||(this.paintsShapes?i.size===1?this.canvasRenderer.paintShape({...c,selected:!0}):this.canvasRenderer.paintMemberOutline(c):this.canvasRenderer.paintSelectionOnly(this.shifted(c,r)));const a=this.selectionShown&&i.size>1?Bt.groupBoundsFor([...t].filter(([o])=>i.has(o)).map(([,o])=>o.bounds)):void 0;this.chromeDraw=o=>{a&&o.paintGroupHandles(a),this.overlayPainter?.(o)},this.paintChrome(this.chromeDraw),this.mapShapeBounds=s.flatMap(o=>[...o.shapes.values()].filter(c=>c.transparency<1).map(c=>this.shifted(c,r).bounds)),this.keepObserverMapClear(),this.seekInput.value=String(e),this.paintSeekProgress(e),this.minified&&this.paintMiniProgress(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Sv(this.currentSighting,e))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),!this.quietFrame&&this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}shifted(e,t){return t.x===0&&t.y===0?e:{...e,bounds:{...e.bounds,x:e.bounds.x+t.x,y:e.bounds.y+t.y}}}createPlayer(){const e=new NA(this.currentSighting.timeline,(t,i)=>this.onFrame(t,i));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},e}static SEEK_KEY_STEP_MS=5e3;handleFullscreenKey=e=>{e.key.toLowerCase()!=="f"||e.altKey||e.ctrlKey||e.metaKey||!this.enableClickToPlay||e.composedPath().some(i=>i instanceof HTMLElement&&(i.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(i.tagName)&&i.type!=="range"))||(e.preventDefault(),this.toggleFullscreen())};handleSeekKey=e=>{if(e.altKey||e.ctrlKey||e.metaKey)return;const t=e.key==="ArrowLeft"?-1:e.key==="ArrowRight"?1:0;if(t){e.preventDefault();const i=this.player.seekableDuration;this.player.seek(Math.min(i,Math.max(0,this.player.time+t*Ft.SEEK_KEY_STEP_MS)))}else e.key===" "&&(e.preventDefault(),this.togglePlayPause())};togglePlayPause(){if(!(this.player.seekableDuration<=0)){if(this.player.playbackState==="playing")this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh();else if(this.clockHeld){this.playAfterHold=!this.playAfterHold,this.playAfterHold&&this.announcePlayRequest();return}else this.announcePlayRequest(),this.sightingAudio.resume(),this.player.play();this.updatePlayPauseButton(),this.flashPlayback()}}flashPlayback(){const e=this.playbackFlash;e.innerHTML=this.player.playbackState==="playing"?qe.PLAY:qe.PAUSE,e.classList.remove("flashing"),e.offsetWidth,e.classList.add("flashing")}static icons=new WeakMap;static setIcon(e,t){Ft.icons.get(e)!==t&&(Ft.icons.set(e,t),e.innerHTML=t)}set captionHeld(e){this.stageElement.classList.toggle("caption-held",e)}set controlsHeld(e){this.stageElement.classList.toggle("controls-held",e)}static CONTROLS_IDLE_MS=3e3;controlsIdleTimer;wasPlaying=!1;quietControlsValue=!1;get quietControls(){return this.quietControlsValue}set quietControls(e){this.quietControlsValue=e,this.stageElement.classList.toggle("quiet",e)}tapRevealedOnly=!1;get hoverless(){return typeof matchMedia=="function"&&matchMedia("(hover: none)").matches}touchedStage(){if(!this.hoverless)return;this.player.playbackState==="playing"&&!this.stageElement.classList.contains("touched")&&(this.tapRevealedOnly=!0),this.revealControls()}pointerHeld=!1;handlePointerDown=()=>{this.pointerHeld=!0};resumeAfterScrub=!1;suspendForScrub(){this.player.playbackState==="playing"&&(this.resumeAfterScrub=!0,this.stopPlaying())}stopPlaying(){this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh(),this.updatePlayPauseButton()}resumeFromScrub(){this.resumeAfterScrub&&(this.resumeAfterScrub=!1,this.sightingAudio.resume(),this.player.play(),this.updatePlayPauseButton())}handleHidden=()=>{this.player.playbackState==="playing"&&this.stopPlaying(),this.resumeAfterScrub=!1};handleVisibility=()=>{document.visibilityState==="hidden"&&this.handleHidden()};handlePointerRelease=()=>{this.resumeAfterScrub&&window.setTimeout(()=>this.resumeFromScrub(),0),this.pointerHeld&&(this.pointerHeld=!1,this.hoverless&&this.stageElement.classList.contains("touched")&&this.revealControls())};revealControls(){this.stageElement.classList.add("touched"),window.clearTimeout(this.controlsIdleTimer),this.player.playbackState==="playing"&&(this.pointerHeld||!this.momentsMenu.hidden||(this.controlsIdleTimer=window.setTimeout(()=>{this.stageElement.classList.remove("touched"),this.tapRevealedOnly=!1},Ft.CONTROLS_IDLE_MS)))}updatePlayPauseButton(){const e=this.player.playbackState==="playing";this.stageElement.classList.toggle("paused",!e),e!==this.wasPlaying&&e&&this.hoverless&&(!this.quietControlsValue||this.stageElement.classList.contains("touched"))&&this.revealControls(),this.wasPlaying=e,this.updateStartState(),Ft.setIcon(this.playPauseButton,e?qe.PAUSE:qe.PLAY);const t=this.player.seekableDuration>0;this.playPauseButton.disabled=!t;const i=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=i,this.playPauseButton.setAttribute("aria-label",i)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.player.loop=this.loopEnabled}restorePlayback(){const e=this.playbackBeforeClick;e&&(this.currentTime=e.time,e.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}enterSimulatedFullscreen(){const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=e.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const t=e.style;t.setProperty("position","fixed"),t.setProperty("inset","0"),t.setProperty("margin","0"),t.setProperty("max-width","none"),t.setProperty("max-height","none"),t.setProperty("aspect-ratio","auto"),t.setProperty("z-index","2147483647"),t.setProperty("background","#000"),t.setProperty("width","100vw"),t.setProperty("width","100dvw"),t.setProperty("height","100vh"),t.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?e.setAttribute("style",this.styleBeforeSimulatedFullscreen):e.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton(),this.updateMuteButton()}toggleObserverMap(){const e=this.observerMapPanel.hidden;this.observerMapAsked=e,this.setObserverMapOpen(e),this.keepObserverMapClear(),this.dispatchEvent(new CustomEvent("observermapchange",{bubbles:!0,composed:!0,detail:{open:e}}))}get observerMapOpen(){return!this.observerMapPanel.hidden}get milestonesVisible(){return this.milestonesShown}observerMapAsked=!1;applyObserverMapDefault(){const e=this.getAttribute(Tc),t=e===null?this.sighting.observerMap??this.observerPath?.travels===!0:e!=="false";this.setObserverMapOpen(t&&this.observerPath!==void 0)}setObserverMapOpen(e){this.observerMapPanel.hidden=!e,this.observerMapPanel.removeAttribute("title"),this.observerMapButton.setAttribute("aria-pressed",String(e)),this.updateObserverMapButton(),e&&(this.sizeObserverMapCanvas(),this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime))}updateObserverMapButton(){const e=!this.observerMapPanel.hidden;Ft.setIcon(this.observerMapButton,e?qe.MAP_ON:qe.MAP_OFF);const t=e?this.messages.hideObserverMap:this.messages.showObserverMap;this.observerMapButton.title=t,this.observerMapButton.setAttribute("aria-label",t);const i=[[this.mapZoomInButton,this.messages.zoomMapIn],[this.mapZoomOutButton,this.messages.zoomMapOut],[this.mapFitButton,this.messages.fitMap]];for(const[n,s]of i)n.title=s,n.setAttribute("aria-label",s)}updateObserverMap(){const e=this.observerPath!==void 0;if(this.observerPath=sl.of(this.currentSighting),this.observerMapButton.hidden=this.observerPath===void 0,!e&&this.observerPath&&this.applyObserverMapDefault(),!this.observerPath){this.setObserverMapOpen(!1),this.observerMapBounds=void 0;return}const t=this.tracesShownState?this.currentSighting.traces.flatMap(n=>n.points):[],i=qt.fitted(this.observerPath,t);(!this.observerMapBounds||!this.sameGround(this.observerMapBounds,i))&&(this.observerMapBounds&&this.observerMapView.refit(this.observerMapBounds,i),this.observerMapBounds=i,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||this.loadObserverMapImagery())}sameGround(e,t){const i=Math.abs(e.east-e.west)/20;return Math.abs(e.north-t.north)<i&&Math.abs(e.south-t.south)<i&&Math.abs(e.west-t.west)<i&&Math.abs(e.east-t.east)<i}async loadObserverMapImagery(){const e=this.observerMapViewBounds;if(!e||this.observerMapPanel.hidden||this.observerMapImageryLoading||(this.observerMapFrozenImagery?this.observerMapImageryBounds!==void 0:!this.observerMapImageryNeededFor(e)))return;const t=this.observerMapView.changed,i=t?qt.expand(e,Ft.OBSERVER_MAP_IMAGERY_AHEAD):e,n=qt.IMAGERY_PX*(t?1+2*Ft.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=this.observerMapFrozenImagery,r=s?void 0:I2();try{const a=r?await r.getImageryTexture(i,{width:n,height:n}):await s.getImagery();if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=a,this.observerMapImageryCredit=(r??s).attribution,this.observerMapImageryFailed=!1}catch{if(this.observerMapImageryBounds!==i)return;this.observerMapImageryFailed=this.observerMapImagery===void 0}finally{this.observerMapImageryBounds===i&&(this.observerMapImageryLoading=!1)}this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}static OBSERVER_MAP_IMAGERY_AHEAD=.5;static OBSERVER_MAP_IMAGERY_LOOKAHEAD=.25;observerMapImageryNeededFor(e){const t=this.observerMapImageryBounds;if(!t)return!0;const i=this.observerMapImagery?.bounds??t,n=this.observerMapView.changed?qt.expand(e,Ft.OBSERVER_MAP_IMAGERY_LOOKAHEAD):e,s=n.west>=i.west&&n.east<=i.east&&n.south>=i.south&&n.north<=i.north,r=e.east-e.west>this.observerMapImageryViewWidth/2;return!s||!r}get observerMapCredit(){return this.observerMapImageryCredit}set observerMapFrozen(e){e!==this.observerMapFrozenImagery&&(this.observerMapFrozenImagery=e,this.observerMapImagery=void 0,this.observerMapImageryCredit=void 0,this.observerMapImageryFailed=!1,this.observerMapImageryBounds=void 0,this.observerMapImageryLoading=!1,this.observerMapPanel.hidden||(this.loadObserverMapImagery(),this.paintObserverMap(this.currentTime)))}get observerMapFrozen(){return this.observerMapFrozenImagery}observerMapFrozenImagery;observerMapFrozenSource;get observerMapFooterLine(){return this.observerMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.observerMapImageryCredit}mapShapeBounds=[];mapSceneBounds=[];setMapSubjectBounds(e){this.mapSceneBounds=e.map(t=>({x:t.x*this.canvas.width,y:t.y*this.canvas.height,width:t.width*this.canvas.width,height:t.height*this.canvas.height})),this.keepObserverMapClear()}keepObserverMapClear(){if(this.observerMapPanel.hidden)return;const e=this.observerMapBoxPx;if(!e)return;const t=[...this.mapShapeBounds,...this.mapSceneBounds],i=(c,h,d)=>{for(const u of t)if(!(u.x+u.width<c-d||u.x>h+d)&&!(u.y>e.bottom+d||u.y+u.height<e.top-d))return!0;return!1},n=[this.canvas.width-e.width,this.canvas.width],s=[0,e.width],r=Ft.OBSERVER_MAP_CLEARANCE_PX,a=i(...n,0),o=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",a&&o&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!a&&(o||!i(n[0],n[1],r))&&this.observerMapPanel.classList.remove("on-the-left");return}i(n[0],n[1],0)&&!i(s[0],s[1],0)&&this.observerMapPanel.classList.add("on-the-left")}observerMapBoxPx;static OBSERVER_MAP_CLEARANCE_PX=24;sizeObserverMapCanvas(){const e=this.observerMapPanel.clientWidth;if(e===0)return;const t=Math.round(e*(globalThis.devicePixelRatio??1));this.measureObserverMapBox(),this.observerMapCanvas.width!==t&&(this.observerMapCanvas.width=t,this.observerMapCanvas.height=t)}measureObserverMapBox(){const e=this.observerMapPanel.getBoundingClientRect(),t=this.canvas.getBoundingClientRect();if(t.width===0||t.height===0)return;const i=this.canvas.width/t.width,n=this.canvas.height/t.height;this.observerMapBoxPx={width:e.width*i,top:(e.top-t.top)*n,bottom:(e.bottom-t.top)*n}}paintObserverMap(e){if(this.observerMapPanel.hidden||!this.observerPath||!this.observerMapBounds)return;const t=ft(this.currentSighting,e);t?.lat!==void 0&&t.lng!==void 0&&!this.observerMapDrag?.moved&&this.observerMapView.keepInView(this.observerMapBounds,{lat:t.lat,lng:t.lng},Ft.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?em(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?al(this.currentSighting.milestones):[]){const a=ft(this.currentSighting,r.t);a?.lat===void 0||a.lng===void 0||n.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:a.lat,lng:a.lng,current:r===s})}this.observerMapRenderer.paint({bounds:this.observerMapViewBounds,imagery:this.observerMapImagery,path:this.observerPath,position:t?.lat!==void 0&&t.lng!==void 0?{lat:t.lat,lng:t.lng}:void 0,headingDeg:t?.headingDeg===void 0?void 0:t.headingDeg+this.lookYawDeg,coneHalfAngleDeg:t?fr.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(Xt.aspectOf(i)):void 0,markers:n,traces:(this.tracesShownState?this.currentSighting.traces:[]).map(r=>({kind:r.kind,points:r.points,color:r.color,label:this.said.read(r.title)})),decor:this.observerMapDecorAt(e),observerLabel:this.said.read(this.currentSighting.observer?.title)??this.messages.observerHere,nightFraction:this.observerMapNightFraction,attribution:this.observerMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(e){this.milestonesShown=e,this.milestoneMarks.hidden=!e,this.momentsBox.hidden=!e||this.sighting.milestones.length===0,this.refreshSeekSegments(this.player.seekableDuration,al(this.sighting.milestones).map(t=>t.t)),this.milestonesButton.setAttribute("aria-pressed",String(e)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintObserverMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0,this.momentsButton.title=this.messages.moments,Ft.setIcon(this.milestonesButton,this.milestonesShown?qe.MILESTONES_ON:qe.MILESTONES_OFF);const e=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=e,this.milestonesButton.setAttribute("aria-label",e)}observerMapDecorAt(e){const t=ft(this.currentSighting,0);return t?.lat===void 0||t.lng===void 0?[]:this.currentSighting.decor.filter(i=>i.kind!=="crop").map(i=>{const n=pr(i,e),{lat:s,lng:r}=Ls(n.eastM,-n.northM,t.lat,t.lng);return{lat:s,lng:r,headingDeg:n.headingDeg,label:this.said.read(i.title)??this.messages.decorHere}})}observerMapNightFraction;setSunAltitude(e){const t=e===void 0?void 0:Math.max(0,Math.min(1,-e/12));t!==this.observerMapNightFraction&&(this.observerMapNightFraction=t,this.paintObserverMap(this.currentTime))}setLookOffset(e,t){e===this.lookYawDeg&&t===this.lookPitchDeg||(this.lookYawDeg=e,this.lookPitchDeg=t,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const e=this.gaitAt(this.currentTime),t=this.lookYawDeg+e.yawDeg,i=this.lookPitchDeg+e.pitchDeg;if(t===0&&i===0)return{x:0,y:0};const n=ft(this.currentSighting,this.currentTime),s=fr.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return ii.bodyAt(this.currentSighting,e,this.steadyBody)}mutedState=!1;get muted(){return this.mutedState}set muted(e){e!==this.mutedState&&(this.mutedState=e,!e&&this.volumeState===0&&(this.volumeState=Ft.RESTORED_VOLUME),this.applyLevel())}volumeState=1;static RESTORED_VOLUME=.5;get volume(){return this.volumeState}set volume(e){this.setVolume(e)}setVolume(e){this.volumeState=Math.min(Math.max(e,0),1),this.mutedState=this.volumeState===0,this.applyLevel()}get level(){return this.mutedState?0:this.volumeState}applyLevel(){this.sightingAudio.setLevel(this.level),this.updateMuteButton(),this.dispatchEvent(new CustomEvent("mutedchange",{bubbles:!0,composed:!0,detail:{muted:this.mutedState,volume:this.volumeState}}))}toggleMuted(){this.hasSound&&(this.muted=!this.mutedState)}ambientSound;get hasSound(){return this.currentSighting.soundTrack.allKeyframes.length>0||this.ambientSound?.(this.currentSighting)===!0}updateMuteButton(){const e=!this.hasSound;this.miniMuteButton.hidden=e,this.miniMuteButton.setAttribute("aria-pressed",String(this.mutedState)),Ft.setIcon(this.miniMuteButton,this.mutedState?qe.MUTED:qe.VOLUME);const t=this.mutedState?this.messages.unmute:this.messages.mute;if(this.miniMuteButton.title=t,this.miniMuteButton.setAttribute("aria-label",t),this.muteButton.disabled=e,this.volumeInput.hidden=e,e){Ft.setIcon(this.muteButton,qe.MUTED),this.muteButton.title=this.messages.noSound,this.muteButton.setAttribute("aria-label",this.messages.noSound),this.muteButton.setAttribute("aria-pressed","true");return}Ft.setIcon(this.muteButton,this.mutedState?qe.MUTED:qe.VOLUME);const i=this.mutedState?this.messages.unmute:this.messages.mute;this.muteButton.title=i,this.muteButton.setAttribute("aria-label",i),this.muteButton.setAttribute("aria-pressed",String(this.mutedState)),this.volumeInput.value=String(this.level)}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;Ft.setIcon(this.fullscreenButton,e?qe.EXIT_FULLSCREEN:qe.ENTER_FULLSCREEN),this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new ss(Ps.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=sd(Ps.preferencesFor(this),cd);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await aA(e);this.messagesLanguage===e&&this.applyMessages(t)}messagesLanguage;updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t,this.timePill.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(this.timePill.setAttribute("role","button"),this.timePill.setAttribute("tabindex","0")):(this.timePill.removeAttribute("role"),this.timePill.removeAttribute("tabindex"))}applyMessages(e){this.messages=e,this.updateTimeLabelTitles(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateMuteButton(),this.updateObserverMapButton(),this.updateReferencesButton(),this.updateTracesButton(),this.updateMilestonesButton(),this.renderIssues()}updateTimeLabels(){const e=this.currentSighting.event,t=Gc(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?N0(e.time):void 0;const i=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&i>0?i/this.realDurationMs:1,this.player.durationOverrideMs=i>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.paintMiniProgress(this.player.time),this.updateTimeLabelTitles()}formatPosition(e){if(this.realDurationMs===void 0)return _a(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?xy(My(this.realStartMs+i)):_a(i)}formatElapsedAt(e){const t=this.currentSighting.timeline.duration;return this.realDurationMs===void 0?_a(e):_a(t>0&&e<=t?e/t*this.realDurationMs:e)}formatRemaining(e){const t=this.currentSighting.timeline.duration;if(this.realDurationMs===void 0)return _a(Math.max(0,t-e));const i=t>0&&e<=t?e/t*this.realDurationMs:e;return _a(Math.max(0,this.realDurationMs-i))}formatEndOfTimeline(){return this.realDurationMs===void 0?_a(this.currentSighting.timeline.duration):this.canShowClockTime?xy(My(this.realStartMs+this.realDurationMs)):_a(this.realDurationMs)}}function My(l){const e=new Date(l);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function xy(l){if(l.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return l.second?`${e(l.hour)}:${e(l.minute??0)}:${e(l.second)}`:`${e(l.hour)}:${e(l.minute??0)}`}function _a(l){const e=Math.round(l/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const V0="rr0-ufo";function Pf(){customElements.get(V0)||customElements.define(V0,Ft)}const r3="recordingissues",k2="minifiedreset";class zh{constructor(e,t){this.seekInput=e,this.layer=t,e.addEventListener("pointerdown",i=>{this.snapArmed=!0,this.nameMarkUnder(i)}),e.addEventListener("pointerup",()=>this.snapArmed=!1),e.addEventListener("pointermove",i=>this.nameMarkUnder(i)),e.addEventListener("pointerleave",()=>e.removeAttribute("title"))}static SNAP_PX=6;marks=[];duration=0;drawn="";snapArmed=!1;show(e,t){const i=t>0?[...e].sort((s,r)=>s.t-r.t):[],n=`${t}|${i.map(s=>`${s.kind}:${s.t}:${s.label}`).join("|")}`;n!==this.drawn&&(this.drawn=n,this.marks=i,this.duration=t,this.layer.replaceChildren(...i.map(s=>{const r=document.createElement("span");return r.className=`timeline-mark ${s.kind}`,r.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`,r})))}snap(e){return this.snapArmed?(this.snapArmed=!1,this.markNear(e)?.t??e):e}next(e){return this.marks.find(t=>t.t>e+zh.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-zh.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=zh.SNAP_PX/t*this.duration;let n;for(const s of this.marks)Math.abs(s.t-e)>i||(!n||Math.abs(s.t-e)<Math.abs(n.t-e)||s.t===n.t&&s.kind==="milestone")&&(n=s);return n}nameMarkUnder(e){const t=this.seekInput.getBoundingClientRect();if(t.width<=0||this.duration<=0)return;const i=this.marksNear((e.clientX-t.left)/t.width*this.duration);i?this.seekInput.title=i:this.seekInput.removeAttribute("title")}marksNear(e){const t=this.markNear(e);if(t)return[...new Set(this.marks.filter(i=>i.t===t.t).map(i=>i.label))].join(" · ")}}class pc{static HALF_SPAN_MS=2e3;static MIN_SPEED_MS=1;static at(e,t,i){const n=e.event.place?.[0],s=e.observerTrack;if(!n)return 0;const r=g=>{const m=s.getInterpolatedPoseAt(Math.max(0,g));if(!m||m.lat===void 0||m.lng===void 0)return;const v=rt(m.lat,m.lng,n.lat,n.lng);return{eastM:v.x,northM:-v.z,heading:m.headingDeg}},a=r(t-pc.HALF_SPAN_MS),o=r(t+pc.HALF_SPAN_MS),c=r(t);if(!a||!o||!c)return 0;const h=Math.hypot(o.eastM-a.eastM,o.northM-a.northM),d=2*pc.HALF_SPAN_MS/1e3;if(h/d<pc.MIN_SPEED_MS)return 0;const u=i.heightAt(o.eastM,o.northM)-i.heightAt(a.eastM,a.northM),p=Math.atan2(u,h)*180/Math.PI;if(c.heading===void 0)return p;const f=Math.atan2(o.eastM-a.eastM,o.northM-a.northM)*180/Math.PI;return p*Math.max(0,Math.cos((c.heading-f)*Math.PI/180))}}const a3=.08,Sy=.15,Ey=2,o3=2e3;class l3{context;noiseBuffer;voices=new Map;level=1;master;paused=!0;requested=[];resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("VehicleAudio: Web Audio unavailable, vehicles stay silent:",e);return}}rl.unlock(this.context),this.apply()}setLevel(e){this.level=e,this.master&&(this.master.gain.value=e)}setPaused(e){e!==this.paused&&(this.paused=e,this.apply())}setVoices(e){this.requested=e,this.apply()}dispose(){for(const e of[...this.voices.keys()])this.release(e);this.context?.close(),this.context=void 0,this.master=void 0}masterOf(e){return this.master||(this.master=e.createGain(),this.master.gain.value=this.level,this.master.connect(e.destination)),this.master}apply(){const e=this.context;if(!e)return;const t=this.paused?[]:this.requested.filter(n=>n.inside||n.distanceM<o3),i=new Set(t.map(n=>n.id));for(const n of[...this.voices.keys()])i.has(n)||this.release(n);for(const n of t){const s=this.voices.get(n.id)??this.build(e,n.id);this.retarget(e,s,n)}}build(e,t){const i=e.createGain();i.gain.value=0;const n=e.createBiquadFilter();n.type="lowpass",n.connect(i).connect(this.masterOf(e));const s=e.createOscillator();s.type="sawtooth";const r=e.createOscillator();r.type="sine";const a=e.createGain(),o=e.createBiquadFilter();o.type="lowpass",o.frequency.value=900,s.connect(o),r.connect(o),o.connect(a).connect(n);const c=()=>{const M=e.createBufferSource();return M.buffer=this.noise(e),M.loop=!0,M.loopStart=Math.random()*Ey*.5,M},h=c(),d=e.createBiquadFilter();d.type="bandpass",d.Q.value=4;const u=e.createGain();h.connect(d).connect(u).connect(n);const p=c(),f=e.createBiquadFilter();f.type="lowpass",f.frequency.value=600;const g=e.createGain();p.connect(f).connect(g).connect(n);const m=c(),v=e.createBiquadFilter();v.type="bandpass",v.frequency.value=1400,v.Q.value=.7;const b=e.createGain();m.connect(v).connect(b).connect(n);const w=[s,r,h,p,m];for(const M of w)M.start();const y={nodes:[i,n,a,o,d,u,f,g,v,b,...w],sources:w,firing:s,crank:r,knock:d,engineGain:a,knockGain:u,roadGain:g,windGain:b,cabin:n,output:i};return this.voices.set(t,y),y}retarget(e,t,{profile:i,state:n,noise:s,inside:r,windowsOpen:a,distanceM:o}){const c=e.currentTime,h=(b,w)=>b.setTargetAtTime(w,c,a3),d=n.rpm/60,u=d*i.cylinders/2,p=Math.min(1,n.rpm/i.shiftUpRpm),f=Math.min(1.5,n.speedMps/30);h(t.firing.frequency,u),h(t.crank.frequency,d),h(t.knock.frequency,u*3),h(t.engineGain.gain,i.engineGain*(.25+.75*n.load)*(.5+.5*p)*.3),h(t.knockGain.gain,i.roughness*(.2+.8*n.load)*.4),h(t.roadGain.gain,i.roadGain*f**1.5*.8);const g=!r||a;h(t.windGain.gain,f**2*(g?.5:.06));const m=r?a?3500:700:8e3/(1+o/60);h(t.cabin.frequency,m);const v=r?.5:Math.min(1,8/Math.max(o,1));h(t.output.gain,v*s)}release(e){const t=this.voices.get(e);if(!t)return;this.voices.delete(e);const i=this.context;i&&(t.output.gain.setTargetAtTime(0,i.currentTime,Sy/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},Sy*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*Ey,e.sampleRate),i=t.getChannelData(0);for(let n=0;n<i.length;n++)i[n]=Math.random()*2-1;return this.noiseBuffer=t,t}}class pd{static BY_KIND={car:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},generic:{cylinders:4,idleRpm:800,kmhPer1000Rpm:[8,14,21,29,37,45],shiftUpRpm:3e3,shiftDownRpm:1300,engineGain:.6,roadGain:.5,roughness:.15},van:{cylinders:4,idleRpm:750,kmhPer1000Rpm:[7,12,19,27,35,42],shiftUpRpm:2600,shiftDownRpm:1200,engineGain:.8,roadGain:.7,roughness:.45},truck:{cylinders:6,idleRpm:600,kmhPer1000Rpm:[5,8,12,17,23,30,38,48],shiftUpRpm:1900,shiftDownRpm:1e3,engineGain:1,roadGain:.9,roughness:.55},motorcycle:{cylinders:2,idleRpm:1100,kmhPer1000Rpm:[12,18,24,30,35,40],shiftUpRpm:6e3,shiftDownRpm:2500,engineGain:.9,roadGain:.3,roughness:.3}};static of(e){return pd.BY_KIND[e??"generic"]??pd.BY_KIND.generic}}class jo{constructor(e,t){this.samples=e,this.startMs=t}static SAMPLE_MS=100;static STANDING_MPS=.3;static of(e,t,i,n){const s=jo.SAMPLE_MS,r=[];for(let c=i;c<=n+s/2;c+=s){const h=t(c-s/2),d=t(c+s/2);if(!h||!d)return;r.push(Math.hypot(d.eastM-h.eastM,d.northM-h.northM)/(s/1e3))}const a=[];let o=1;for(let c=0;c<r.length;c++){const h=r[c],d=(r[Math.min(c+1,r.length-1)]-r[Math.max(c-1,0)])/(2*s/1e3);if(h<jo.STANDING_MPS){o=1,a.push({speedMps:h,rpm:e.idleRpm,load:.1,gear:0});continue}const u=h*3.6,p=v=>u/e.kmhPer1000Rpm[v-1]*1e3;for(;o<e.kmhPer1000Rpm.length&&p(o)>e.shiftUpRpm;)o++;for(;o>1&&p(o)<e.shiftDownRpm;)o--;const f=Math.max(p(o),e.idleRpm*(1+.3*Math.min(1,h/3))),g=.15+.5*Math.min(1,(h/35)**2),m=d>.15?Math.min(1,g+d/2.5):d<-.3?.05:g;a.push({speedMps:h,rpm:f,load:m,gear:o})}return new jo(a,i)}at(e){const t=(e-this.startMs)/jo.SAMPLE_MS,i=Math.min(Math.max(Math.floor(t),0),this.samples.length-1),n=this.samples[i],s=this.samples[Math.min(i+1,this.samples.length-1)],r=Math.min(Math.max(t-i,0),1);return{speedMps:n.speedMps+(s.speedMps-n.speedMps)*r,rpm:n.rpm+(s.rpm-n.rpm)*r,load:n.load+(s.load-n.load)*r,gear:r<.5?n.gear:s.gear}}}class zp{drives=new Map;at(e,t){const i=[],n=e.event.place?.[0],s=c=>{const h=e.observerTrack.getInterpolatedPoseAt(c),d=h?.lat??n?.lat,u=h?.lng??n?.lng;if(d===void 0||u===void 0||!n)return;const p=rt(d,u,n.lat,n.lng);return{eastM:p.x,northM:-p.z}},r=Math.max(e.observerTrack.duration,(e.event.durationSeconds??0)*1e3),a=e.vehicle;if(a){const c=pd.of(a.kind),h=this.drive(`observer:${a.kind}`,zp.trackKey(e),()=>jo.of(c,s,0,r));h&&i.push({id:"observer",profile:c,state:h.at(t),noise:a.noise??1,inside:!0,windowsOpen:a.windowsOpen===!0,distanceM:0})}const o=s(t);for(const c of e.decor){if(!c.engine||!o)continue;const h=pd.of(c.engine.kind),d=g=>{const m=pr(c,g);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,zp.decorKey(c,r),()=>jo.of(h,d,0,r));if(!u)continue;const p=pr(c,t),f=Math.hypot(p.eastM-o.eastM,p.northM-o.northM,p.altitudeM);i.push({id:`decor:${c.id}`,profile:h,state:u.at(t),noise:c.engine.noise??1,inside:!1,windowsOpen:!0,distanceM:f})}return i}drive(e,t,i){const n=this.drives.get(e);if(n&&n.key===t)return n.drive;const s=i();return this.drives.set(e,{key:t,drive:s}),s}static trackKey(e){return e.observerTrack.allKeyframes.map(i=>`${i.t}:${i.pose.lat}:${i.pose.lng}`).join(";")}static decorKey(e,t){return`${t}|${e.eastM},${e.northM}|`+(e.track??[]).map(i=>`${i.t}:${i.eastM}:${i.northM}`).join(";")}}const c3="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAARVBMVEX9sST/uyTfoCjNlSikfCqRcCx8Yy21hipTSi4TITEMHTEuMzBqWC3/wSRUX207SFj8/PyaoakmNUd5gYxBPy90fIfMz9RsB4kZAAACEUlEQVRo3u2XyXbDIAxFLeapjts6/f9PrQA3JymT25Cd3iY5xtYVIB6wLCQSiUQikUgk0nzBoVdFZ1xIpaTQbJnPACaNdYeskWwuAhaZoltnUemfnNkL4N49KEI8n0YAEXMvEWISAWQZPw2Xk1MIoF1dSNAzCMy6JsGyCR0wrkMwT3cBeDt+FH8aEHoA68LbM8HRHXQ3f0RE3/jXOOFX+s4djrKpIbzS7K+MmLrxlWCNR8mczjMw92Ar4dZLDXG4kzXinAECcOUPL3jMdN2294/GQGVG4EMHxORNJXoCfG6oT1trvDGM7iIA9N6aTOuuEbB9ra6FyCYr2gjge+fjA4CIi2uujYRoOBSw0A5/D0hT0X4PW0xtuotNpQPYtkt/fdtyM0LP6ZvOI2DtvoxtJWHvx38AxIkevLwXQ2TOA66DziYfLwC4r5wbIlwJo1ys8+VOBMy7/sxdjwIap49lVC3UePZp1mmyit4SuGUf11pjoTHVRVzXUaXFVt87kuEBsWZ055Q+2sXA76LbZYsvKR1ubvJqbKd5swl+GPI31p6L/sNYuDTe3jo+GBdr1J+P9PGawbRUw9VhleD/3ffT9uZ7DhtX1JNHL2CmNRXxYZhwSwBd3aJTyU85/GKOwhxBf5RnVk+75OSiuu+Ax4PQ3Jsg1hTeMUMwJgQp+PKCe2a+JuefFwQnkUgkEolEIpFerW8TL0hlNl2QVAAAAABJRU5ErkJggg==",h3=`
3294
3294
  <div class="stage" id="stage">
3295
3295
  <div class="frame" id="frame">
3296
3296
  <canvas id="scene-canvas"></canvas>
@@ -9617,7 +9617,7 @@ uniform float uWindowLuminance;
9617
9617
  varying float vLitRank;
9618
9618
  ${o.fragmentShader.replace("#include <emissivemap_fragment>",`#include <emissivemap_fragment>
9619
9619
  totalEmissiveRadiance *= step(vLitRank, uLitShare) * uWindowLuminance * ${Me.WINDOW_VARIETY_GLSL};`)}`},r.customProgramCacheKey=()=>"facade-lit-windows";const a=new Se(s,r);return a.castShadow=!0,a.receiveShadow=!0,t.add(a),t}static litWindowShare={value:0};static WINDOW_LUMINANCE_CD_M2=15;static ROOM_LIGHT_K=2900;static COLD_ROOM_LIGHT_K=4e3;static windowLuminance={value:0};static get WINDOW_VARIETY_GLSL(){const e=n=>`vec3(${n.map(s=>s.toFixed(4)).join(", ")})`,t=Uc.ofTemperature(Me.ROOM_LIGHT_K),i=Uc.ofTemperature(Me.COLD_ROOM_LIGHT_K);return`(mix(0.35, 2.0, fract(vLitRank * 97.13) * fract(vLitRank * 97.13)) * mix(${e(t)}, ${e(i)}, step(0.86, fract(vLitRank * 53.71))))`}static setWindowLuminance(e){Me.windowLuminance.value=Me.WINDOW_LUMINANCE_CD_M2*e}static setLitWindowShare(e){Me.litWindowShare.value=Math.max(0,Math.min(1,e))}static build(e,t){const i=Me.hasFacade(e)?Me.buildFacade(e):e.kind==="building"?Sw(e.floors??Wc,e.windows,e.observerSide,e.occupiedFloor,e.observerFacing):e.kind==="tree"?Aw():e.kind==="shrub"?Ew():e.kind==="bridge"?TL(e):e.kind==="wire"?_L(e):e.kind==="wall"?nn.build(e):e.kind==="crop"?_w():e.kind==="terrace"?PL():e.kind==="mound"?Iw():e.kind==="streetlight"?Rw(t,e.lamp?.mount,e.lamp):e.kind==="trafficlight"?yt.trafficLight(st.isPedestrian(e.lights)):e.kind==="crossing"?yt.crossing(Me.sizeOf(e).widthM,Me.sizeOf(e).lengthM):e.kind==="roadsign"?yt.roadSign(e.sign):e.kind==="vehicle"?Dw(t,e.windows,e.observerSide,e.observerFacing):e.kind==="aircraft"?Nw():e.kind==="entity"?Ow():pv();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Ot;return e.kind==="building"&&e.roof&&this.addRoof(s,e),i.name=Me.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*Jm),this.addLights(s,e.lights),Me.fitLights(s,e,e.sizeM??{}),s}static scaleFor(e){const t=e.sizeM;if(!t||Me.hasFacade(e)||e.kind==="bridge"||e.kind==="wire"||e.kind==="terrace"||e.kind==="wall"||e.kind==="crossing")return new I(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new I(t.widthM===void 0?1:i0(t.widthM,i.widthM),t.heightM===void 0?1:i0(e.kind==="building"?this.wallHeightOf(e,t.heightM):t.heightM,i.heightM),t.lengthM===void 0?1:i0(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Wc)+1):0;if(e==="bridge")return gi.NATURAL_SIZE;if(e==="wire")return tl.NATURAL_SIZE;if(e==="terrace"||e==="wall")return{widthM:1,lengthM:1,heightM:1};if(e==="crossing")return{widthM:6,lengthM:2.5,heightM:yt.CROSSING_LIFT_M};const n=`${e}:${i}`,s=Cw.get(n);if(s)return s;const r=e==="building"?Sw(i-1,void 0,void 0,void 0):e==="tree"?Aw():e==="shrub"?Ew():e==="crop"?_w():e==="mound"?Iw():e==="streetlight"?Rw(!1):e==="trafficlight"?yt.trafficLight(!1):e==="roadsign"?yt.roadSign(void 0):e==="vehicle"?Dw(!1,void 0,void 0):e==="aircraft"?Nw():e==="entity"?Ow():pv(),a=new En().setFromObject(r),o={widthM:a.max.x-a.min.x,heightM:a.max.y-a.min.y,lengthM:a.max.z-a.min.z};return this.dispose(r),Cw.set(n,o),o}static usesModel(e){return dd(e)!==void 0&&!(e.observerSide!==void 0&&co(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),r=new Ot;r.name=Me.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*Jm,i.updateMatrixWorld(!0);const a=new En().setFromObject(i),o={lengthM:a.max.z-a.min.z,heightM:a.max.y-a.min.y,widthM:a.max.x-a.min.x},c=t.kind==="tree"&&t.sizeM?.heightM!==void 0?{heightM:t.sizeM.heightM}:t.sizeM,h=kw(c,o)??kw(n.depictedSizeM,o)??1;i.scale.multiplyScalar(h);const d=a.getCenter(new I).multiplyScalar(h);i.position.x-=d.x,i.position.z-=d.z,i.position.y-=t.kind==="aircraft"?d.y:a.min.y*h,i.traverse(u=>{u instanceof Se&&(u.castShadow=!0,u.receiveShadow=!0)}),r.add(i),t.kind==="tree"&&r.add(Me.trunkShadow(o.heightM*h)),s.parent?(s.parent.add(r),s.removeFromParent()):e.add(r),this.dispose(s),Me.fitLights(e,t,{widthM:o.widthM*h,heightM:o.heightM*h,lengthM:o.lengthM*h})}static fitLights(e,t,i){const n=Vc.byId(hr.rigOf(t)||void 0)?.referenceSizeM;if(!n||!t.lights)return;const s=(h,d)=>h===void 0||d===0?1:h/d,r=s(i.widthM,n.widthM),a=s(i.heightM,n.heightM),o=s(i.lengthM,n.lengthM),c=new Map(t.lights.map(h=>[h.id,h.offsetM]));e.traverse(h=>{const d=h.userData.lightId,u=d===void 0?void 0:c.get(d);u&&h.position.set(u.x*r,u.y*a,u.z*o)})}static trunkShadow(e){Me.trunkProxy??={geometry:new Yn(1,1.15,1,8),material:new os({colorWrite:!1,depthWrite:!1})};const t=new Se(Me.trunkProxy.geometry,Me.trunkProxy.material),i=Math.min(.3,Math.max(.12,e*.035)),n=e*.45;return t.scale.set(i,n,i),t.position.y=n/2,t.castShadow=!0,t.receiveShadow=!1,t.userData.sharedModel=!0,t.name="trunk-shadow",t}static trunkProxy;static sizeOf(e){const t=this.naturalSize(e.kind,e.floors);return{widthM:e.sizeM?.widthM??t.widthM,lengthM:e.sizeM?.lengthM??t.lengthM,heightM:e.sizeM?.heightM??t.heightM}}static addLights(e,t){for(const i of t??[]){const n=vL(i.color),s=st.figureOf(i),r=s?yt.pedestrianFigure(s,1.8*(i.radiusM??Qm)):new $i(i.radiusM??Qm,8,6),a=Yt(e,r,n,i.offsetM.y,!0);a.position.set(i.offsetM.x,i.offsetM.y,i.offsetM.z),a.userData={emissive:!0,lightId:i.id,lightColor:n},a.visible=!1;const o=a.material;Lx.apply(o),o.transparent=!0,o.depthWrite=!1,o.fog=!1}}static setLights(e,t,i,n,s=0,r=1,a=dL,o=[1,1,1],c){if(!t||t.length===0)return;const h=new Map(t.map(u=>[u.id,u]));let d;c&&t.some(u=>u.beamDeg!==void 0)&&(e.updateWorldMatrix(!0,!1),d=e.worldToLocal(Me.eyeScratch.copy(c)));for(const u of e.children){if(!(u instanceof Se))continue;const p=u.userData.lightId;if(!p)continue;const f=h.get(p);if(!f)continue;const g=s>0?c2(f,i,i+s):SA(f,i)?1:0,m=d&&f.beamDeg!==void 0?Me.beamShare(f.beamDeg,u.position,d):1;u.visible=g>0&&m>0;const v=f.radiusM??Qm,b=Math.max(1,n*a/v),w=v*b,y=r/(Math.PI*w*w),M=u.material,S=u.userData.lightColor;if(S){const E=g*m*uL*(f.intensity??1)*y,x=[S[0]*E*o[0],S[1]*E*o[1],S[2]*E*o[2]],T=Math.min(1,Mw/Math.max(x[0],x[1],x[2],1e-30));M.color.setRGB(x[0]*T,x[1]*T,x[2]*T)}u.scale.setScalar(b)}}static eyeScratch=new I;static beamShare(e,t,i){const n=i.x-t.x,s=i.y-t.y,r=i.z-t.z,a=Math.hypot(n,s,r);if(a===0)return 1;const o=-r/a;if(o<=0)return 0;const c=Math.log(.5)/Math.log(Math.cos(e*Math.PI/180));return Math.pow(o,c)}static setLit(e,t,i,n=1,s){const[r,a,o]=t==="streetlight"?[pL*Me.lampShare(s),fL,Me.lampHue(s)]:[mL,gL,rS],c=i?r*n/(Math.PI*a*a):0,h=Math.min(1,Mw/Math.max(o[0]*c,o[1]*c,o[2]*c,1e-30)),d=[o[0]*c*h,o[1]*c*h,o[2]*c*h];e.traverse(u=>{const p=u.userData;!(u instanceof Se)||!p.emissive||p.lightId||(u.material instanceof qs?u.material.emissive.setRGB(...d):u.material.color.setRGB(...d))})}static REFERENCE_LUMENS=1e4;static lampShare(e){return e?.lumens?e.lumens/Me.REFERENCE_LUMENS:1}static lampHue(e){return e?.colorTemperatureK?Uc.ofTemperature(e.colorTemperatureK):bL}static dispose(e){e.traverse(t=>{if(!(t instanceof Se))return;if(t.userData.sharedModel){for(const n of t.userData.tintedMaterials??[])n.dispose();return}t.geometry.dispose();const i=t.material;for(const n of Array.isArray(i)?i:[i])n.dispose()})}}const s0="https://ufoathome.org/models/index.json";class Qh{attribution="3D models re-hosted by UFO@home, each under its own author's licence";fetchImpl;indexUrls;loading;static shared=new Map;sharing;constructor(e={}){this.sharing=!e.fetchImpl,this.fetchImpl=e.fetchImpl??fetch.bind(globalThis),this.indexUrls=e.indexUrls??BL()}async entries(e){const t=await this.load();return e?t.filter(i=>[i.kind].flat().includes(e)):t}async entry(e){return(await this.load()).find(t=>t.id===e)}load(){if(this.loading)return this.loading;if(!this.sharing)return this.loading=this.fetchFirstAvailable();const e=this.indexUrls.join(`
9620
- `);let t=Qh.shared.get(e);return t||(t=this.fetchFirstAvailable(),Qh.shared.set(e,t),t.then(i=>{i.length===0&&Qh.shared.delete(e)})),this.loading=t}async fetchFirstAvailable(){for(const e of this.indexUrls){const t=await this.fetchIndex(e);if(t)return t}return[]}async fetchIndex(e){try{const t=await this.fetchImpl(e);if(!t.ok)return;const i=await t.json();return Array.isArray(i?.models)?i.models.map(({file:n,...s})=>({...s,url:new URL(n,e).href})):void 0}catch{return}}}function BL(){const l=`?v=${encodeURIComponent("0.129.0")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==s0?[e,s0]:[s0]).map(t=>t+l)}const cp=[{id:"ufoathome",name:"UFO@home",credit:"3D models re-hosted by UFO@home, each under its own author's licence",creditUrl:"https://ufoathome.org/models/",create:()=>new Qh}];async function r0(l){const{GLTFLoader:e}=await fe(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-ZegnGcYO.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(l,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${l}: ${s instanceof Error?s.message:String(s)}`)))})}const uS=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class Ds{static MIN_ANGLE_RAD=.002;static FILL=(1-Math.exp(-4))/4;static MAX_SHARE=.3;mesh;constructor(e,t){this.mesh=new Se(new cs(2,2),new Pt({uniforms:{uColor:{value:new ne(0,0,0)},uRadius:{value:1}},vertexShader:Ds.VERTEX,fragmentShader:Ds.FRAGMENT,transparent:!0,depthWrite:!1,blending:fn,toneMapped:!1})),this.mesh.name=e,this.mesh.frustumCulled=!1,this.mesh.renderOrder=t,this.mesh.visible=!1}shine(e,t,i){this.mesh.visible=!0,this.mesh.position.set(e.x,e.y,e.z),this.mesh.material.uniforms.uRadius.value=t,this.mesh.material.uniforms.uColor.value.setRGB(i[0],i[1],i[2])}hide(){this.mesh.visible=!1}dispose(){this.mesh.geometry.dispose(),this.mesh.material.dispose()}static spread(e,t,i,n){const s=Math.max(e,n*Ds.MIN_ANGLE_RAD),r=Ds.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(Ds.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(Ds.MAX_SHARE*Ds.FILL*Math.PI))}static VERTEX=`
9620
+ `);let t=Qh.shared.get(e);return t||(t=this.fetchFirstAvailable(),Qh.shared.set(e,t),t.then(i=>{i.length===0&&Qh.shared.delete(e)})),this.loading=t}async fetchFirstAvailable(){for(const e of this.indexUrls){const t=await this.fetchIndex(e);if(t)return t}return[]}async fetchIndex(e){try{const t=await this.fetchImpl(e);if(!t.ok)return;const i=await t.json();return Array.isArray(i?.models)?i.models.map(({file:n,...s})=>({...s,url:new URL(n,e).href})):void 0}catch{return}}}function BL(){const l=`?v=${encodeURIComponent("0.129.1")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==s0?[e,s0]:[s0]).map(t=>t+l)}const cp=[{id:"ufoathome",name:"UFO@home",credit:"3D models re-hosted by UFO@home, each under its own author's licence",creditUrl:"https://ufoathome.org/models/",create:()=>new Qh}];async function r0(l){const{GLTFLoader:e}=await fe(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-ZegnGcYO.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(l,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${l}: ${s instanceof Error?s.message:String(s)}`)))})}const uS=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class Ds{static MIN_ANGLE_RAD=.002;static FILL=(1-Math.exp(-4))/4;static MAX_SHARE=.3;mesh;constructor(e,t){this.mesh=new Se(new cs(2,2),new Pt({uniforms:{uColor:{value:new ne(0,0,0)},uRadius:{value:1}},vertexShader:Ds.VERTEX,fragmentShader:Ds.FRAGMENT,transparent:!0,depthWrite:!1,blending:fn,toneMapped:!1})),this.mesh.name=e,this.mesh.frustumCulled=!1,this.mesh.renderOrder=t,this.mesh.visible=!1}shine(e,t,i){this.mesh.visible=!0,this.mesh.position.set(e.x,e.y,e.z),this.mesh.material.uniforms.uRadius.value=t,this.mesh.material.uniforms.uColor.value.setRGB(i[0],i[1],i[2])}hide(){this.mesh.visible=!1}dispose(){this.mesh.geometry.dispose(),this.mesh.material.dispose()}static spread(e,t,i,n){const s=Math.max(e,n*Ds.MIN_ANGLE_RAD),r=Ds.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(Ds.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(Ds.MAX_SHARE*Ds.FILL*Math.PI))}static VERTEX=`
9621
9621
  uniform float uRadius;
9622
9622
  varying vec2 vOffset;
9623
9623
  void main() {