@rr0/ufoathome 0.125.0 → 0.125.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.
@@ -2950,9 +2950,9 @@ canvas[data-cursor="rotate"] {
2950
2950
  `;class cc{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 cc(t)}static TRAVEL_M=50;get travels(){return this.spanM>cc.TRAVEL_M}get moved(){return this.spanM>cc.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(pt(t,e.east,t,e.west).x),Math.abs(pt(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:ws(0,-s,i,n).lat,south:ws(0,s,i,n).lat,east:ws(s,0,i,n).lng,west:ws(-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 Is{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)>Is.HAND_HELD_S,i=e.sway??(t?0:1);if(i>0)return new Is(Math.max(1-(e.instrument.stabilization??0),Is.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,l,c,h]=Is.HZ;return{eastM:i*Is.SWAY_M*(.7*n(s,.3)+.3*n(l,1.9)),northM:i*Is.SWAY_M*(.6*n(a,2.2)+.4*n(c,.8)),upM:i*Is.BREATH_M*n(r,0),yawDeg:i*this.rotationPassed*Is.DRIFT_YAW_DEG*(.6*n(h,1.1)+.4*n(l,2.7)),pitchDeg:i*this.rotationPassed*Is.DRIFT_PITCH_DEG*(.5*n(r,1.6)+.5*n(a,.4)),rollDeg:i*this.rotationPassed*Is.DRIFT_ROLL_DEG*n(s,2.9)}}}class jt{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=cc.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],l=(a.t-r.t)/1e3;if(l<=0)continue;const c=pt(a.lat,a.lng,r.lat,r.lng),h=c.x,d=-c.z,u=Math.hypot(h,d),p=u/l,f=jt.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*l}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 jt(i,1-(e.instrument.stabilization??0))}static bodyAt(e,t,i=!1){const n=jt.of(e)?.offsetAt(t)??jt.STILL;return i?n:jt.add(n,Is.of(e)?.offsetAt(t))}static stepHzFor(e){return e<jt.SLOWEST_WALK_M_PER_S||e>jt.FASTEST_WALK_M_PER_S?0:jt.REFERENCE_STEP_HZ*Math.pow(e/jt.REFERENCE_SPEED_M_PER_S,jt.CADENCE_EXPONENT)}offsetAt(e){return jt.add(this.cycleAt(e),this.joltAt(e))}joltAt(e){for(const t of this.stretches){if(t.stepHz===0)continue;const i=t.speedMPerS/jt.REFERENCE_SPEED_M_PER_S,n=(a,l,c,h)=>{if(a<0||a>jt.JOLT_LASTS_MS)return;const d=a/1e3,u=Math.exp(-d/jt.JOLT_DECAY_S)*Math.sin(2*Math.PI*jt.JOLT_HZ*d),p=h*l*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,jt.STOP_LURCH_M,jt.STOP_PITCH_DEG,1):void 0;if(s)return s;const r=t.rampsIn?n(e-t.startMs,jt.START_LURCH_M,jt.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(l=>e>=l.startMs&&e<=l.endMs);if(!t||t.stepHz===0)return jt.STILL;const i=t.stepsBefore+t.stepHz*(e-t.startMs)/1e3,n=2*Math.PI*i,s=this.amplitudeAt(t,e),r=jt.SWAY_M*s*Math.cos(n/2),a=s*this.rotationPassed*t.speedMPerS/jt.REFERENCE_SPEED_M_PER_S;return{eastM:r*t.northUnit,northM:-r*t.eastUnit,upM:jt.RISE_M_PER_M_PER_S*t.speedMPerS*s*Math.cos(n),rollDeg:jt.HEAD_ROLL_DEG*a*Math.cos(n/2),pitchDeg:-1.25*a*Math.cos(n),yawDeg:jt.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 OE{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 Wo{static sessionDeclared=!1;static unlock(e){if(Wo.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(!(Wo.sessionDeclared||typeof navigator>"u")){Wo.sessionDeclared=!0;try{const e=navigator.audioSession;e&&(e.type="playback")}catch{}}}}const T1=.05,_1=.08,I1=2,D1=3,PE=14,NE=.02;class FE{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}}Wo.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,T1);for(const{param:s,ratio:r}of e.pitch)s.setTargetAtTime(Math.min(t.pitchHz*r,i.sampleRate/2-1),n,T1)}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(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);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*I1),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*I1),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*D1),e.sampleRate),i=t.getChannelData(0),n=Math.max(1,Math.floor(NE*e.sampleRate)),s=Math.round(D1*PE);for(let r=0;r<s;r++){const a=Math.floor(Math.random()*i.length),l=.4+Math.random()*.6;for(let c=0;c<n&&a+c<i.length;c++)i[a+c]=Math.max(i[a+c],l*(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,_1/3);const n=i+_1;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 Xo(o){return[...o].sort((e,t)=>e.t-t.t)}function xf(o,e){let t;for(const i of Xo(o)){if(i.t>e)break;t=i}return t}const Sf=Math.PI/180,yd=180/Math.PI;class er{constructor(e,t,i){this.kind=e,this.canvasHeightPx=t,this.fovDeg=i;const n=i*Sf/2;this.focalPx=e==="equidistant"?t/2/n:t/2/Math.tan(n)}focalPx;static of(e,t,i){return new er(e.projection,t,i)}degToPx(e){const t=e*Sf;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*yd:2*Math.atan(e/(2*this.focalPx))*yd}angleDegToRadiusPx(e){const t=e*Sf;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*yd:Math.atan(e/this.focalPx)*yd}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(bt.angularWidthDeg(e))}}class rn{static fovOf(e,t){return ft(e,t)?.fovDeg??Ht.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,l=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*l}))}})}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)<rn.UNMOVED_PX&&Math.abs(s.y-t.bounds.y)<rn.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=er.of(e.instrument,bt.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),l=t.altitudeDeg-s.pitchDeg;return{x:r.x+this.offAxisPx(n,a)-i.width/2,y:r.y-this.offAxisPx(n,l)-i.height/2}}static frameCentre(e){return{x:Ht.frameWidthPx(e.instrument,bt.CANVAS_HEIGHT_PX)/2,y:bt.CANVAS_HEIGHT_PX/2}}static shortestArc(e){return(e%360+540)%360-180}static offAxisPx(e,t){return Math.abs(t)>=rn.OFF_CANVAS_DEG?Math.sign(t)*rn.OFF_CANVAS_PX:e.angleDegToRadiusPx(t)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(e,t,i){const n=Ht.frameWidthPx(t,bt.CANVAS_HEIGHT_PX)/2,s=Ht.frameWidthPx(e.instrument,bt.CANVAS_HEIGHT_PX)/2,r=bt.CANVAS_HEIGHT_PX/2;for(const a of[...e.timeline.allKeyframes]){const l=this.fovOf(e,a.t),c=i?.get(a.t)??l,h=er.of(t,bt.CANVAS_HEIGHT_PX,c),d=er.of(e.instrument,bt.CANVAS_HEIGHT_PX,l);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=er.of(e.instrument,bt.CANVAS_HEIGHT_PX,r);i.addKeyframe(n.t,n.shapes.map(l=>({...l,shape:t(l.shape,a,s)})))}}}function UE(o){return Xn.restore(a0(o),o.provenance)}function a0(o){return rn.toAngular(o),rn.toAim(o),{version:1,id:o.id,time:o.event.time,endTime:o.event.endTime,durationSeconds:o.event.durationSeconds,durationText:o.event.durationText,utcOffsetHours:o.event.utcOffsetHours,timeZone:o.event.timeZone,place:o.event.place,observer:o.observer,account:o.account,sources:o.sources?.length?o.sources:void 0,sway:o.sway,vehicle:o.vehicle,description:o.event.description,image:o.event.image,summary:o.event.summary,summaryTime:o.event.summaryTime,tags:o.event.tags,timeline:o.timeline.toJSON(),observerTrack:o.observerTrack.toJSON(),weatherTrack:o.weatherTrack.toJSON(),soundTrack:o.soundTrack.toJSON(),weather:o.weather,decor:o.decor,milestones:o.milestones.length>0?o.milestones:void 0,references:o.references.length>0?o.references:void 0,roads:o.roads.length>0?o.roads:void 0,traces:o.traces.length>0?o.traces:void 0,weatherSource:o.weatherSource,instrument:o.instrumentId,exposureSeconds:o.exposureSeconds,iso:o.iso,lightPollution:o.lightPollution,observerMap:o.observerMap,interpretation:o.interpretation}}function BE(o){const{recording:e,provenance:t}=Xn.strip(o),i=zE(e);return i.provenance=t,i}function zE(o){const e=[],t=new Qp({eventType:"sighting",time:o.time,endTime:o.endTime,durationSeconds:o.durationSeconds,durationText:o.durationText,utcOffsetHours:o.utcOffsetHours,timeZone:o.timeZone,place:o.place,description:o.description,image:o.image,summary:o.summary,summaryTime:o.summaryTime,tags:o.tags},qp.fromJSON(o.timeline,e),o.observerTrack?Bh.fromJSON(o.observerTrack):new Bh,o.weatherTrack?zh.fromJSON(o.weatherTrack):new zh,o.soundTrack?Hh.fromJSON(o.soundTrack):new Hh,o.observer&&{id:o.observer.id,title:o.observer.title,lastName:o.observer.lastName,firstNames:o.observer.firstNames},o.id,o.weather,o.decor??[],o.weatherSource,o.instrument,o.exposureSeconds??o.observerTrack?.keyframes.map(i=>i.pose.exposureSeconds).find(i=>i!==void 0),Xo(o.milestones??[]),o.references??[],o.roads??[],o.traces??[]);return t.account=o.account,t.sources=o.sources,t.sway=o.sway,t.iso=o.iso,t.lightPollution=o.lightPollution,t.observerMap=o.observerMap,t.vehicle=o.vehicle,t.interpretation=o.interpretation,t.loadIssues=e,rn.toBounds(t),rn.toPosition(t),t}class Rc{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=Rc.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=>` ${Rc.text(n)}`);console.warn(`UFO@home: ${e.length} problem(s) in ${t??"this recording"}:
2951
2951
  ${i.join(`
2952
2952
  `)}`)}}class Hs{constructor(e){this.format=e}issues(e){const t=[];return this.object(e,this.format,"",t),t}object(e,t,i,n){if(!Hs.isObject(e)){n.push({kind:"wrong-type",path:i||"(the file)",expected:"object",found:Hs.typeOf(e)});return}for(const[s,r]of Object.entries(e)){const a=i?`${i}.${s}`:s,l=t[s];l?this.field(r,l,a,n):n.push({kind:"unknown-key",path:a})}}field(e,t,i,n){if(Hs.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):Hs.isObject(e)||n.push({kind:"wrong-type",path:i,expected:"object",found:Hs.typeOf(e)});return;case"array":Array.isArray(e)?t.fields&&e.forEach((s,r)=>{const a=Hs.isStated(s)?s.value:s;this.object(a,t.fields,`${i}.${r}`,n)}):n.push({kind:"wrong-type",path:i,expected:"array",found:Hs.typeOf(e)});return;default:typeof e!==t.type&&n.push({kind:"wrong-type",path:i,expected:t.type,found:Hs.typeOf(e)})}}static isObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}static isStated(e){return Hs.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 Ys{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?Ys.travelDegOver(e,t,i,i+n*1e3)/s/Ys.PIXELS_PER_INSTANT:0,a=Ys.flashesOver(e,n)*Ys.INSTANTS_PER_FLASH,l=Math.ceil(Math.max(r,a));return l<2?1:Math.min(Ys.MAX_INSTANTS,l)}static travelDegOver(e,t,i,n){let s=0;for(const r of e){if(!r.track||r.track.length<2)continue;const a=Ys.directionAt(r,t,i),l=Ys.directionAt(r,t,n),c=Math.min(1,Math.max(-1,a.x*l.x+a.y*l.y+a.z*l.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=Qs(e,i),s=n.eastM,r=n.altitudeM-t,a=n.northM,l=Math.hypot(s,r,a);return l===0?{x:0,y:1,z:0}:{x:s/l,y:r/l,z:a/l}}}const Sw={play:"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:Rc.EN},HE=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Sw},Symbol.toStringTag,{value:"Module"})),Ew=Math.PI/180,GE=180/Math.PI,VE=256,Aw=19,WE=1.25;function Tw(o,e){return 156543.03392*Math.cos(o*Ew)/2**e}function _w(o,e){return Tw(o,e)*VE}function XE(o,e){let t=0;for(;t<Aw&&_w(o,t+1)>=e;)t++;return t}function Iw(o,e){return _w(o,e)}function $E(o,e){const t=e*WE;let i=0;for(;i<Aw&&Tw(o,i)>t;)i++;return i}function Tu(o){const e=o*Ew;return(1-Math.log(Math.tan(e)+1/Math.cos(e))/Math.PI)/2}function Dw(o,e,t){const i=2**t;return{x:(o+180)/360*i,y:Tu(e)*i}}function Rw(o,e,t){const i=Tu(o.north),n=Tu(o.south);return{x:(e-o.west)/(o.east-o.west),y:(Tu(t)-i)/(n-i)}}function YE(o,e,t){const{x:i,y:n}=Dw(o,e,t);return{x:Math.floor(i),y:Math.floor(n),z:t}}function qE(o){const e=2**o.z,t=o.x/e*360-180,i=(o.x+1)/e*360-180,n=R1(o.y,e);return{south:R1(o.y+1,e),north:n,west:t,east:i}}function R1(o,e){const t=o/e;return Math.atan(Math.sinh(Math.PI*(1-2*t)))*GE}function Cw(o,e,t,i){const n=YE(o,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(qE),l={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:l}}const KE="#2ec4ff";class mo{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=Rw(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??KE;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((l,c)=>Math.hypot(c.x-a.x,c.y-a.y)>Math.hypot(l.x-a.x,l.y-a.y)?c:l)}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),l=(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,l-c,l+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:mo.MARKER_RADIUS_PX}),t.beginPath(),t.arc(n.x,n.y,mo.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=pt(r.lat,r.lng,t.lat,t.lng);return Math.hypot(a.x,a.z)<=mo.ON_MARKER_M});i.beginPath(),i.arc(n.x,n.y,s?mo.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,l=Math.abs(pt(r,e.east,r,e.west).x)/this.width,c=mo.SCALE_STEPS_M.find(p=>p/l<=this.width/3);if(c===void 0)return;const h=c/l,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 Si{static MIN_ZOOM=.25;static MAX_ZOOM=64;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(Si.MAX_ZOOM,Math.max(Si.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=Si.mercatorY01(t.north),s=Si.mercatorY01(t.south),r=Si.mercatorY01(i.north),a=Si.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=Si.mercatorY01(n.north),r=Si.mercatorY01(n.south),a=(t.lng-n.west)/(n.east-n.west),l=(Si.mercatorY01(t.lat)-s)/(r-s);return a>=i&&a<=1-i&&l>=i&&l<=1-i?!1:(this.panBy(.5-a,.5-l),!0)}boundsWithin(e){if(!this.changed)return e;const t=.5/this.zoom,i=Si.mercatorY01(e.north),n=Si.mercatorY01(e.south),s=e.east-e.west;return{west:e.west+(this.centerX-t)*s,east:e.west+(this.centerX+t)*s,north:Si.latitudeAt(i+(this.centerY-t)*(n-i)),south:Si.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=pt(n,e.east,n,s).x;let a=r;for(const l of t){const{x:c,z:h}=pt(l.lat,l.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:ws(0,-a,n,s).lat,south:ws(0,a,n,s).lat,east:ws(a,0,n,s).lng,west:ws(-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=Si.mercatorY01(e.north),s=Si.mercatorY01(e.south);return{lat:Si.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 Ts=256,C1=3,ZE=32768;class Lw{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(pt(i,e.east,i,e.west).x),r=Math.abs(pt(e.north,n,e.south,n).z),a=XE(i,Math.max(s,r)),{tiles:l}=Cw(n,i,a,C1),c=C1*Ts,h=new Float32Array(c*c),d=l[0];await Promise.all(l.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()),l=document.createElement("canvas");l.width=Ts,l.height=Ts;const c=l.getContext("2d");if(!c)throw new Error("2D canvas context unavailable");c.drawImage(a,0,0);const{data:h}=c.getImageData(0,0,Ts,Ts),d=(e.x-t.x)*Ts,u=(e.y-t.y)*Ts;for(let p=0;p<Ts;p++)for(let f=0;f<Ts;f++){const g=(p*Ts+f)*4,m=h[g]*256+h[g+1]+h[g+2]/256-ZE,v=d+f,b=u+p;i[b*n+v]=m}}sampleAt(e,t,i,n,s,r){const{x:a,y:l}=Dw(t,e,i),c=(a-n.x)*Ts,h=(l-n.y)*Ts,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],M=s[f*r+d],y=s[f*r+p],w=v+(b-v)*g,S=M+(y-M)*g;return w+(S-w)*m}}class nf{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 M=s.get(v);if(M===void 0){if(b===void 0)throw new Error(`TIFF without tag ${v}`);return b}return M.type===3?M.value&65535:M.value},a=v=>{const b=s.get(v);if(b===void 0)throw new Error(`TIFF without tag ${v}`);const M=b.type===3?2:4,y=[];if(b.count*M<=4){const w=new DataView(new ArrayBuffer(4));w.setUint32(0,b.value,!0);for(let S=0;S<b.count;S++)y.push(M===2?w.getUint16(S*2,!0):w.getUint32(0,!0))}else for(let w=0;w<b.count;w++)y.push(M===2?t.getUint16(b.value+w*2,!0):t.getUint32(b.value+w*4,!0));return y};if(r(259,1)!==1)throw new Error("Compressed TIFF");if(s.has(322))throw new Error("Tiled TIFF");const l=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(l*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 nf(l,c,h,u?new Float32Array(g.buffer):g)}}class jE{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 l=this.structureAt(e+a*this.metresPerPixel,t+r*this.metresPerPixel);l!==void 0&&(n===void 0||l>n)&&(n=l)}return n}}class Ge{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 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)*Ge.METRES_PER_DEGREE*Math.cos(t.lat*Math.PI/180),north:(n-t.lat)*Ge.METRES_PER_DEGREE}}static path(e,t){return e.geometry.type!=="LineString"?[]:e.geometry.coordinates.map(i=>Ge.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(l=>({path:Ge.path(l,t),properties:l.properties})),r=(e.elevatedRailways??[]).map(l=>Ge.path(l,t)),a=[];for(const l of Ge.joined(e.bridges.map(c=>Ge.path(c,t)))){const c=l[0],h=l[l.length-1],d=Math.hypot(h.east-c.east,h.north-c.north);if(d<Ge.MIN_BRIDGE_M)continue;const u={east:(c.east+h.east)/2,north:(c.north+h.north)/2};Math.hypot(u.east,u.north)>i+d/2||Ge.carriesRailway(c,h,r)||a.push({eastM:Ge.tenth(u.east),northM:Ge.tenth(u.north),lengthM:Ge.tenth(d),widthM:Ge.roadOf(u,s).width,heightM:Ge.tenth(Ge.deckHeight(c,h,n)),headingDeg:Ge.tenth(Ge.bearing(c,h)%180),...Ge.roadOf(u,s).name?{name:Ge.roadOf(u,s).name}:{}})}return a.sort((l,c)=>Math.hypot(l.eastM,l.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=Ge.bearing(e,t)%180;for(const r of i)for(let a=0;a+1<r.length;a++){if(Ge.distanceToSegment(n,r[a],r[a+1])>Ge.RAIL_MATCH_M)continue;const l=Math.abs(Ge.bearing(r[a],r[a+1])%180-s);if(Math.min(l,180-l)<=Ge.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=Ge.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)>Ge.JOIN_M)continue;const a=Math.abs(Ge.bearing(i[0],s)-Ge.bearing(r,n[n.length-1]));if(Math.min(a,360-a)<=Ge.JOIN_DEG)return[...i,...n.slice(1)]}}static roadOf(e,t){let i;for(const a of t)for(let l=0;l+1<a.path.length;l++){const c=Ge.distanceToSegment(e,a.path[l],a.path[l+1]);(i===void 0||c<i.distance)&&(i={distance:c,properties:a.properties})}if(i===void 0||i.distance>Ge.ROAD_MATCH_M)return{width:Ge.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?Ge.tenth(s+Ge.DECK_MARGIN_M):i.properties.nature==="Sentier"?Ge.FOOTBRIDGE_DECK_M:Ge.DEFAULT_DECK_M;return n?{width:r,name:n}:{width:r}}static deckHeight(e,t,i){if(i===void 0)return Ge.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?Ge.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??""),l=Ge.kilovolts(a);if(l===void 0)continue;const c=Ge.BY_VOLTAGE.find(d=>l<=d.upToKv)??Ge.BY_VOLTAGE[Ge.BY_VOLTAGE.length-1],h=Ge.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>Ge.MAX_SPAN_M||Ge.distanceToSegment({east:0,north:0},u,p)>i)continue;const g=Ge.poleHeight(u,p,n)??c.poleM;s.push({eastM:Ge.tenth((u.east+p.east)/2),northM:Ge.tenth((u.north+p.north)/2),lengthM:Ge.tenth(f),headingDeg:Ge.tenth(Ge.bearing(u,p)),heightM:Ge.tenth(Math.max(4,g-c.dropM)),poleHeightM:Ge.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,Ge.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>=Ge.MIN_POLE_M&&s<=Ge.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 It{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??It.WMS,this.wfs=e.wfsEndpoint??It.WFS}static radius(e){return Math.min(e,It.MAX_RADIUS_M)}static lidarRadius(e){return Math.min(e,It.MAX_LIDAR_RADIUS_M)}async lidar(e,t){const i=It.lidarRadius(t),n=Math.ceil(2*i/It.METRES_PER_PIXEL),[s,r]=await Promise.all([this.raster(e,i,n,It.HEIGHT_LAYER),this.raster(e,i,n,It.CLASS_LAYER)]);if(s.samples!==1||r.samples<3)throw new Vs("unreachable","The survey answered in a layout this does not read");const a=s.values;if(a.every(d=>d===It.NO_DATA))throw new Vs("no-coverage","No LiDAR HD survey here yet");const l=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];l[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:l,building:c,structures:new jE(a,l,c,n,h)}}async laserDate(e){return It.latest(await this.dated("IGNF_LIDAR-HD_METADONNEE:metadata","geom","date_fin_acquisition",e))}async photoDate(e){return It.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=It.radius(t),n=Math.min(It.MAX_PHOTO_PIXELS,Math.ceil(2*i/It.METRES_PER_PIXEL)),s=await this.raster(e,i,n,It.PHOTO_LAYER);if(s.samples<3)throw new Vs("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=It.radius(t),s=n/It.METRES_PER_DEGREE,r=n/(It.METRES_PER_DEGREE*Math.cos(e.lat*Math.PI/180)),l=`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",`${l} AND nature='Pont'`,"nature,geometrie"),this.features("troncon_de_route",`${l} 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",`${l} AND position_par_rapport_au_sol>'0'`,"position_par_rapport_au_sol,geometrie").catch(()=>[]),this.features("ligne_electrique",l,"voltage,geometrie"),i?this.features("batiment",l,It.BUILDING_PROPERTIES,5e3):Promise.resolve([]),i?this.features("zone_d_activite_ou_d_interet",l,"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:It.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,l=It.MAX_WHOLE_M/It.METRES_PER_DEGREE;let c;for(const d of e){if(d.properties.categorie!=="Religieux")continue;const[u,p,f,g]=It.extent(d);if(u>=n&&p>=s&&f<=r&&g<=a)continue;const m=[Math.max(u,n-l),Math.max(p,s-l),Math.min(f,r+l),Math.min(g,a+l)];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')`,It.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,l=r==="LineString"?[a]:r==="MultiLineString"?a:[];for(const c of l)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/It.METRES_PER_DEGREE,r=t/(It.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 l;try{l=await this.fetchImpl(`${this.wms}?${a}`)}catch(c){throw new Vs("unreachable",c instanceof Error?c.message:String(c))}if(!l.ok)throw new Vs("unreachable",`The survey answered ${l.status}`);try{return nf.parse(await l.arrayBuffer())}catch(c){throw new Vs("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(l){throw new Vs("unreachable",l instanceof Error?l.message:String(l))}if(!r.ok)throw new Vs("unreachable",`The map answered ${r.status}`);return(await r.json()).features??[]}}class _a{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=>pt(a.lat,a.lng,i.lat,i.lng)),s=e.map(a=>pt(a.lat,a.lng,i.lat,i.lng));let r=0;for(let a=0;a<s.length-1;a++){const l=s[a],c=s[a+1],h=Math.hypot(c.x-l.x,c.z-l.z);if(h<1e-6)continue;const d=Math.max(1,Math.ceil(h/_a.SAMPLE_M)),u=Math.atan2(c.x-l.x,-(c.z-l.z));for(let p=0;p<=d;p++){const f=p/d,g=_a.nearest(n,l.x+(c.x-l.x)*f,l.z+(c.z-l.z)*f);g.distanceM>_a.NEAR_M||_a.angleBetween(u,g.heading)<=_a.ALIGNED_DEG&&(r+=h/d)}}return r>=_a.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],l=a.x-r.x,c=a.z-r.z,h=l*l+c*c,d=h<1e-9?0:Math.max(0,Math.min(1,((t-r.x)*l+(i-r.z)*c)/h)),u=Math.hypot(t-(r.x+l*d),i-(r.z+c*d));u<n.distanceM&&(n={distanceM:u,heading:Math.atan2(l,-c)})}return n}static angleBetween(e,t){const i=Math.abs((e-t)*180/Math.PI%180);return Math.min(i,180-i)}}class ni{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 MIN_PIXELS=8;static MAX_PIXELS=700;static NO_DATA_BELOW=-9e3;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??ni.WMS,this.timeoutMs=e.timeoutMs??ni.DEFAULT_TIMEOUT_MS,this.maxWindowM=e.maxWindowM??ni.MAX_WINDOW_M,this.geoplatform=new It({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(!It.covers(n.lat,n.lng))throw new Error("The IGN's relief covers France only");const s=Math.abs(pt(n.lat,e.east,n.lat,e.west).x),r=Math.abs(pt(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)},l={x:Math.min(a.x,this.maxWindowM),y:Math.min(a.y,this.maxWindowM)},c={south:e.south-l.y/ni.METRES_PER_DEGREE,north:e.north+l.y/ni.METRES_PER_DEGREE,west:e.west-l.x/(ni.METRES_PER_DEGREE*Math.cos(n.lat*Math.PI/180)),east:e.east+l.x/(ni.METRES_PER_DEGREE*Math.cos(n.lat*Math.PI/180))},h=f=>Math.min(ni.MAX_PIXELS,Math.max(ni.MIN_PIXELS,Math.ceil(f/ni.METRES_PER_PIXEL))),d={width:h(s+2*l.x),height:h(r+2*l.y)},u={x:(s+2*l.x)/d.width,y:(r+2*l.y)/d.height};let p=new Error("No layer of the IGN's relief answered");for(const f of ni.LAYERS)try{const g=await this.heights(c,d,f);f===ni.LAYERS[0]&&await this.layBridgeDecks(g,c,d,i?.route);const m=this.sample(g,t,e,c,u,a);if(m)return{heights:m,width:t.width,height:t.height,bounds:e};p=new Error(`${f} does not cover this place`)}catch(g){p=g}throw p}finer(e,t,i){if(e.width<ni.MESH_FROM_VERTICES||e.height<ni.MESH_FROM_VERTICES)return e;const n=(s,r)=>{const a=Math.ceil(s/ni.FINE_SPACING_M),l=Math.max(r-1,Math.min(ni.MAX_SEGMENTS,a));return l%2===0?l:l+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 nf.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>ni.NO_DATA_BELOW)))try{const s=(await this.geoplatform.elevatedRoadLines({west:t.west,south:t.south,east:t.east,north:t.north})).filter(v=>_a.follows(n,v));if(s.length===0)return;const r=await this.raster(t,i,ni.CLASS_LAYER);if(r.samples<3||r.width!==e.width||r.height!==e.height)return;const a=(v,[b,M,y])=>r.values[v*r.samples]===b&&r.values[v*r.samples+1]===M&&r.values[v*r.samples+2]===y,l=e.width,c=e.height,h=new Uint8Array(l*c);let d=!1;for(let v=0;v<h.length;v++)a(v,ni.DECK)&&(h[v]=1,d=!0);if(!d)return;const u=ni.DECK_END_PIXELS,p=[];for(let v=0;v<h.length;v++){if(h[v]!==1)continue;const b=v%l,M=(v-b)/l;for(let y=-u;y<=u;y++)for(let w=-u;w<=u;w++){const S=b+w,A=M+y;if(S<0||A<0||S>=l||A>=c)continue;const x=A*l+S;h[x]===0&&a(x,ni.NO_CLASS)&&p.push(x)}}for(const v of p)h[v]=1;const f=this.paint(s,t,l,c),g=this.touching(h,f,l,c);if(g.every(v=>v===0))return;const m=await this.heights(t,i,ni.SURFACE_LAYER);if(m.width!==l||m.height!==c)return;for(let v=0;v<g.length;v++){if(g[v]===0)continue;const b=m.values[v];Number.isFinite(b)&&b>ni.NO_DATA_BELOW&&b>e.values[v]&&(e.values[v]=b)}}catch{}}paint(e,t,i,n){const s=new Uint8Array(i*n),r=l=>(l-t.west)/(t.east-t.west)*i,a=l=>(t.north-l)/(t.north-t.south)*n;for(const l of e)for(let c=0;c<l.length-1;c++){const h=r(l[c].lng),d=a(l[c].lat),u=r(l[c+1].lng),p=a(l[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 M=-1;M<=1;M++){const y=m+M,w=v+b;y>=0&&w>=0&&y<i&&w<n&&(s[w*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 l=[],c=[a];r[a]=1;let h=!1;for(;c.length>0;){const d=c.pop();l.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 l)s[d]=1}return s}sample(e,t,i,n,s,r){const{width:a,height:l}=t,c=new Float32Array(a*l),h=new Uint8Array(a*l),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<l;f++){const g=i.north+(i.south-i.north)*(l===1?.5:f/(l-1)),m=(n.north-g)/(n.north-n.south)*e.height-.5;for(let v=0;v<a;v++){const M=(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,M,m,Math.max(1,d),Math.max(1,u)):this.bilinear(e,M,m),w=f*a+v;y===void 0?p++:(c[w]=y,h[w]=1)}}if(!(p===a*l||p/(a*l)>ni.MAX_HOLE_SHARE))return this.fill(c,h,a,l),c}tent(e,t,i,n,s){let r=0,a=0,l=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;l+=m;const v=e.at(g,p);!Number.isFinite(v)||v<ni.NO_DATA_BELOW||(r+=m*v,a+=m)}}return l>0&&a>=l*.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),l=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<ni.NO_DATA_BELOW)return;const f=h+(d-h)*l,g=u+(p-u)*l;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 l=0;l<i;l++){const c=a*i+l;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=l+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,l]of r)e[a]=l,t[a]=1;s-=r.length}}}class JE{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 _u=256,QE=5;async function kw(o,e,t,i){const n=(o.south+o.north)/2,s=(o.west+o.east)/2,r=Math.abs(pt(n,o.east,n,o.west).x),a=Math.abs(pt(o.north,s,o.south,s).z),l=Math.max(r,a),{zoom:c,gridSize:h}=eA(n,l,Math.max(e.width,e.height)),{tiles:d,bounds:u}=Cw(s,n,c,h),p=h*_u,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 w=t(y),S=await i(w);if(!S.ok)throw new Error(`Imagery tile fetch failed (${S.status}): ${w}`);const A=await createImageBitmap(await S.blob());g.drawImage(A,(y.x-m.x)*_u,(y.y-m.y)*_u)}));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 M=b.getContext("2d");if(!M)throw new Error("2D canvas context unavailable");return M.drawImage(f,0,0,p,p,0,0,v,v),{source:b,width:v,height:v,bounds:u}}function eA(o,e,t){let i=$E(o,e/Math.max(1,t)),n=L1(o,i,e);for(;n>QE&&i>0;)i--,n=L1(o,i,e);return{zoom:i,gridSize:n,mPerPixel:Iw(o,i)/_u}}function L1(o,e,t){const i=Math.ceil(t/2/Iw(o,e));return Math.max(3,2*i+1)}class Ow{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 kw(e,t,i=>this.tileUrlTemplate.replace("{z}",String(i.z)).replace("{y}",String(i.y)).replace("{x}",String(i.x)),this.fetchImpl)}}class ms{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=ms.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),l=(n+r)/2,c=(s+a)/2,h=ms.FRANCE;return l>=h.south&&l<=h.north&&c>=h.west&&c<=h.east?[ms.FRANCE_ENDPOINT,...t.filter(u=>u!==ms.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=ms.queue.then(e,e);return ms.queue=t.catch(()=>{}),t}async ask(e){let t;for(const i of this.endpoints??ms.endpointsFor(e))for(let n=0;n<2;n++){try{const s=await this.fetchImpl(`${i}?data=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(ms.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,ms.RETRY_MS))}throw t??new Error("No Overpass instance answered")}}class Ia{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 ms(e)}async getRoads(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:30];way["highway"~"^(${Ia.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:Ia.surfaceOf(s),widthM:Ia.widthOf(s),...Ia.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),l=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(l)&&l>0?{speedKmh:l}:{}}}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:Ia.WIDTH_M[e.highway]??Ia.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 Ef="https://ufoathome.org/roads/index.json";class Yl{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??Yl.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(`
2953
- `);let t=Yl.shared.get(e);return t||(t=this.fetchFirstAvailable(),Yl.shared.set(e,t),t.then(i=>{i.length===0&&Yl.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.125.0")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==Ef?[t,Ef]:[Ef]).map(i=>i+e)}}class La{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 It(e)}static covers(e){return It.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getRoads(e){return(await this.platform.roads(e)).flatMap((i,n)=>La.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=La.BY_NATURE[n]??La.DEFAULT,r=Number(e.properties.largeur_de_chaussee),a=Number(e.properties.nombre_de_voies),l=Number(e.properties.vitesse_moyenne_vl),c=La.DIRECTIONS[String(e.properties.sens_de_circulation??"")],h=La.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(l)&&l>0?{speedKmh:l}:{},points:i}]}}class tA{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(La.covers(e))try{const t=await this.regional.getRoads(e);if(t.length>0)return t}catch{}return this.worldwide.getRoads(e)}}class $h{attribution="Water © IGN, BD TOPO (Licence Ouverte 2.0)";platform;constructor(e={}){this.platform=new It(e)}static covers(e){return It.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getWater(e){return(await this.platform.waterSurfaces(e)).flatMap((i,n)=>$h.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,l)=>{const c=a.map(h=>$h.open(h.map(d=>({lat:d[1],lng:d[0]})))).filter(h=>h.length>=3);return c.length>0?[{id:s.length>1?`${r}-${l}`: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 go{attribution="Water © OpenStreetMap contributors";client;constructor(e={}){this.client=new ms(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 go.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))),l=go.rings(a("inner"));for(const[c,h]of go.rings(a("outer")).entries()){const d=l.filter(u=>go.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 go.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 l=i[a],c=s[s.length-1];if(t(c,l[0]))s=[...s,...l.slice(1)];else if(t(c,l[l.length-1]))s=[...s,...[...l].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 iA{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Water © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere";async getWater(e){if($h.covers(e))try{const t=await this.regional.getWater(e);if(t.length>0)return t}catch{}return this.worldwide.getWater(e)}}const Af="https://ufoathome.org/water/index.json";class ql{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??ql.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(`
2954
- `);let t=ql.shared.get(e);return t||(t=this.fetchFirstAvailable(),ql.shared.set(e,t),t.then(i=>{i.length===0&&ql.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.125.0")}`,t=typeof location>"u"?void 0:new URL("/water/index.json",location.href).href;return(t&&t!==Af?[t,Af]:[Af]).map(i=>i+e)}}class ts{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=ts.key(e);let i=ts.asked.get(t);return i===void 0&&(i=this.recalled(t,e),ts.asked.set(t,i),i.catch(()=>ts.asked.delete(t))),structuredClone(await i)}static forget(){ts.asked.clear()}static key(e){return`v${ts.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}async recalled(e,t){const i=await ts.openCache(),n=ts.request(e);if(i)try{const r=await i.match(n);if(r){const{savedAt:a,surfaces:l}=await r.json();if(Date.now()-a<ts.MAX_AGE_MS&&Array.isArray(l))return l;await i.delete(n)}}catch{}const s=await this.water.getWater(t);return i&&ts.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-ts.MAX_STORED)))await e.delete(r)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(ts.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 It(e)}static covers(e){return It.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}`),l=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(l)&&l>0?l: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 Kl{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 ms(e)}async getRailways(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:25];way["railway"~"^(${Kl.KINDS.join("|")})$"](${t});out tags geom;`;return this.linesFrom(await this.client.query(i))}linesFrom(e){const t=[];for(const i of e.elements??[]){const n=i.geometry,s=i.tags;if(!s||!n||n.length<2)continue;const r=Kl.kindOf(s),a=Number.parseInt((s.gauge??"").split(";")[0],10),l=Number.parseInt(s.tracks??"",10),c=Kl.electrificationOf(s.electrified),h=s.highspeed==="yes"||Number.parseInt(s.maxspeed??"",10)>=250;t.push({id:`osm-rail-${i.id??t.length}`,...s.name?{name:s.name}:{},kind:r,...s.service||s.usage==="industrial"?{service:!0}:{},...h?{highSpeed:!0}:{},gaugeMm:Number.isFinite(a)&&a>300?a:s.railway==="narrow_gauge"?1e3:1435,tracks:Number.isFinite(l)&&l>0?l:1,...c?{electrification:c}:{},level:Kl.levelOf(s),points:n.map(d=>({lat:d.lat,lng:d.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 nA{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 qi{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=qi.key(e);let i=qi.asked.get(t)??qi.containing(e);if(i===void 0){const n=this.recalled(t,e);i={bounds:e,answer:n},qi.asked.set(t,i),n.catch(()=>qi.asked.delete(t))}return structuredClone(await i.answer)}static forget(){qi.asked.clear(),qi.failedAt=-1/0}static key(e){return`v${qi.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}static containing(e){for(const t of qi.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 qi.openCache(),n=qi.request(e);let s;if(i)try{const a=await i.match(n);if(a){const{savedAt:l,lines:c}=await a.json();if(Array.isArray(c)){if(this.now()-l<qi.MAX_AGE_MS)return c;s=c}}}catch{}if(this.now()-qi.failedAt<qi.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(qi.failedAt=this.now(),s)return s;throw a}return i&&qi.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-qi.MAX_STORED)))await e.delete(s)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(qi.CACHE_NAME)}catch{return}}static request(e){const t=typeof location>"u"?"http://localhost":location.origin;return new Request(`${t}/__railways__/${encodeURIComponent(e)}`)}}const Tf="https://ufoathome.org/railways/index.json";class Zl{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??Zl.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(`
2955
- `);let t=Zl.shared.get(e);return t||(t=this.fetchFirstAvailable(),Zl.shared.set(e,t),t.then(i=>{i.length===0&&Zl.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.125.0")}`,t=typeof location>"u"?void 0:new URL("/railways/index.json",location.href).href;return(t&&t!==Tf?[t,Tf]:[Tf]).map(i=>i+e)}}function sA(){return{elevation:Pw(),imagery:Nw(),roads:rA(),water:aA(),railways:oA()}}function Pw(){return new JE(new ni,new Lw)}function Nw(){return new Ow}function rA(){return new Yl({fallback:new tA(new La,new Ia)})}function aA(){return new ql({fallback:new ts(new iA(new $h,new go))})}function oA(){return new Zl({fallback:new qi(new nA(new ka,new Kl))})}const lA=new Set,hc="show-observer-map",dc="hide-milestones";class _t extends HTMLElement{static get observedAttributes(){return["src",hc,dc]}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;static OBSERVER_MAP_MIN_SPAN_M=400;static OBSERVER_MAP_MARGIN=.6;static OBSERVER_MAP_IMAGERY_PX=768;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 Si;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=Qp.create();issues=[];issueCheck=0;sightingAudio=new FE;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>_t.LIVE_BODY_MAX_RATE}selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=Sw;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");_t.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)<_t.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>=Si.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=Si.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},_t.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=Si.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>${kE}</style>${LE}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new Dc(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",t=>{t.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 mo(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",t=>{t.key!=="Enter"&&t.key!==" "||(t.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.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",t=>{this.suspendForScrub(),this.seekSnapArmed=!0,this.nameMarkUnder(t),this.beginTouchScrub(t)}),this.seekInput.addEventListener("pointerup",t=>{this.seekSnapArmed=!1,this.endTouchScrub(t)}),this.seekInput.addEventListener("pointercancel",t=>this.endTouchScrub(t)),this.seekInput.addEventListener("pointermove",t=>{this.nameMarkUnder(t),this.hoverSeek(t),this.touchScrub(t)}),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 t of["touchend","click"])this.stageElement.addEventListener(t,()=>this.sightingAudio.resume(),!0);this.stageElement.addEventListener("pointermove",()=>this.touchedStage()),this.canvas.addEventListener("click",t=>{if(this.enableClickToPlay){if(this.tapRevealedOnly){this.tapRevealedOnly=!1;return}t.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause()}}),this.canvas.addEventListener("dblclick",t=>{this.enableClickToPlay&&(t.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){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===hc&&this.applyObserverMapDefault(),e===dc&&this.setMilestonesShown(!this.hasAttribute(dc))}async loadFromSrc(e){this.sightingData=await wo.json(e)}get sightingData(){return UE(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=BE(e),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.syncPlayerTitle(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault()}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 Ae(async()=>{const{default:r}=await import("./sightingSchema-v2l1p6Ld.js");return{default:r}},[],import.meta.url);n=new Hs(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],Rc.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(cA,{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=Rc.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.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 l=document.createElement("span");l.textContent=n,l.title=n,r.append(a,l);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"),_t.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)),_t.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?Xo(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 l=document.createElement("i");l.className="seek-hover";const c=document.createElement("i");c.className="seek-played",a.append(l,c),s.push(a),this.seekSegmentList.push({start:n[r],end:n[r+1],element:a,played:c,hover:l})}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}%`}paintSeekProgress(e){for(const i of this.seekSegmentList)i.played.style.width=_t.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=_t.fillOf(p,s),p.element.classList.toggle("hovered",s>=p.start&&(s<p.end||f===r))});const a=this.milestonesShown?this.sighting.milestones:[],l=a.length>0?xf(a,s):void 0,c=l?this.said.read(l.label)??"":"",h=l?this.said.read(l.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)),l=Math.max(1,Math.round(s*r));this.chromeCanvas.width!==a&&(this.chromeCanvas.width=a),this.chromeCanvas.height!==l&&(this.chromeCanvas.height=l),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,a,l),this.chromeDirty=!1,i&&(t.setTransform(a/this.canvas.width,0,0,l/this.canvas.height,0,0),this.chromeRenderer??=new Dc(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=_t.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?xf(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=Ys.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<_t.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(_t.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/_t.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/_t.EXPOSURE_STEP_PX),s=Math.min(_t.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 l=n.get(r);if(!l)continue;const c=l.bounds.x+l.bounds.width/2-(a.bounds.x+a.bounds.width/2),h=l.bounds.y+l.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=Ht.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:lA;this.canvasRenderer.setStarPoints(Ht.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 l of s)for(const[,c]of l.shapes){const h=l.share,d=h===1?c:{...c,transparency:1-(1-c.transparency)*h};this.canvasRenderer.paintShape(this.shifted(d,r))}for(const[l,c]of s[0].shapes)!this.selectionShown||!i.has(l)||(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?Nt.groupBoundsFor([...t].filter(([l])=>i.has(l)).map(([,l])=>l.bounds)):void 0;this.chromeDraw=l=>{a&&l.paintGroupHandles(a),this.overlayPainter?.(l)},this.paintChrome(this.chromeDraw),this.mapShapeBounds=s.flatMap(l=>[...l.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.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Hg(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 OE(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*_t.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){_t.icons.get(e)!==t&&(_t.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},_t.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,_t.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(hc),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;_t.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=cc.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=Si.including(this.observerPath.boundsAround(_t.OBSERVER_MAP_MIN_SPAN_M,_t.OBSERVER_MAP_MARGIN),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.observerMapImageryNeededFor(e))return;const t=this.observerMapView.changed,i=t?Si.expand(e,_t.OBSERVER_MAP_IMAGERY_AHEAD):e,n=_t.OBSERVER_MAP_IMAGERY_PX*(t?1+2*_t.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=Nw();try{const r=await s.getImageryTexture(i,{width:n,height:n});if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=r,this.observerMapImageryCredit=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?Si.expand(e,_t.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}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=_t.OBSERVER_MAP_CLEARANCE_PX,a=i(...n,0),l=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",a&&l&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!a&&(l||!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},_t.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?xf(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?Xo(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?er.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(Ht.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,Xo(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,_t.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=Qs(i,e),{lat:s,lng:r}=ws(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=er.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return jt.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=_t.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;if(this.muteButton.disabled=e,this.volumeInput.hidden=e,e){_t.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}_t.setIcon(this.muteButton,this.mutedState?qe.MUTED:qe.VOLUME);const t=this.mutedState?this.messages.unmute:this.messages.mute;this.muteButton.title=t,this.muteButton.setAttribute("aria-label",t),this.muteButton.setAttribute("aria-pressed",String(this.mutedState)),this.volumeInput.value=String(this.level)}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;_t.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 Yn(Ss.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=Uh(Ss.preferencesFor(this),Vh);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await sE(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=Tc(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?e0(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.updateTimeLabelTitles()}formatPosition(e){if(this.realDurationMs===void 0)return bd(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?O1(k1(this.realStartMs+i)):bd(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?bd(this.currentSighting.timeline.duration):this.canShowClockTime?O1(k1(this.realStartMs+this.realDurationMs)):bd(this.realDurationMs)}}function k1(o){const e=new Date(o);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function O1(o){if(o.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return o.second?`${e(o.hour)}:${e(o.minute??0)}:${e(o.second)}`:`${e(o.hour)}:${e(o.minute??0)}`}function bd(o){const e=Math.round(o/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const o0="rr0-ufo";function sf(){customElements.get(o0)||customElements.define(o0,_t)}const cA="recordingissues";class Eh{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+Eh.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-Eh.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=Eh.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 jl{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=pt(m.lat,m.lng,n.lat,n.lng);return{eastM:v.x,northM:-v.z,heading:m.headingDeg}},a=r(t-jl.HALF_SPAN_MS),l=r(t+jl.HALF_SPAN_MS),c=r(t);if(!a||!l||!c)return 0;const h=Math.hypot(l.eastM-a.eastM,l.northM-a.northM),d=2*jl.HALF_SPAN_MS/1e3;if(h/d<jl.MIN_SPEED_MS)return 0;const u=i.heightAt(l.eastM,l.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(l.eastM-a.eastM,l.northM-a.northM)*180/Math.PI;return p*Math.max(0,Math.cos((c.heading-f)*Math.PI/180))}}const hA=.08,P1=.15,N1=2,dA=2e3;class uA{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}}Wo.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<dA),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(),l=e.createBiquadFilter();l.type="lowpass",l.frequency.value=900,s.connect(l),r.connect(l),l.connect(a).connect(n);const c=()=>{const w=e.createBufferSource();return w.buffer=this.noise(e),w.loop=!0,w.loopStart=Math.random()*N1*.5,w},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 M=[s,r,h,p,m];for(const w of M)w.start();const y={nodes:[i,n,a,l,d,u,f,g,v,b,...M],sources:M,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:l}){const c=e.currentTime,h=(b,M)=>b.setTargetAtTime(M,c,hA),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+l/60);h(t.cabin.frequency,m);const v=r?.5:Math.min(1,8/Math.max(l,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,P1/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},P1*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*N1,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 Yh{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 Yh.BY_KIND[e??"generic"]??Yh.BY_KIND.generic}}class No{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=No.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 l=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<No.STANDING_MPS){l=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(;l<e.kmhPer1000Rpm.length&&p(l)>e.shiftUpRpm;)l++;for(;l>1&&p(l)<e.shiftDownRpm;)l--;const f=Math.max(p(l),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:l})}return new No(a,i)}at(e){const t=(e-this.startMs)/No.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 mp{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=pt(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=Yh.of(a.kind),h=this.drive(`observer:${a.kind}`,mp.trackKey(e),()=>No.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 l=s(t);for(const c of e.decor){if(!c.engine||!l)continue;const h=Yh.of(c.engine.kind),d=g=>{const m=Qs(c,g);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,mp.decorKey(c,r),()=>No.of(h,d,0,r));if(!u)continue;const p=Qs(c,t),f=Math.hypot(p.eastM-l.eastM,p.northM-l.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 pA="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==",fA=`
2953
+ `);let t=Yl.shared.get(e);return t||(t=this.fetchFirstAvailable(),Yl.shared.set(e,t),t.then(i=>{i.length===0&&Yl.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.125.1")}`,t=typeof location>"u"?void 0:new URL("/roads/index.json",location.href).href;return(t&&t!==Ef?[t,Ef]:[Ef]).map(i=>i+e)}}class La{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 It(e)}static covers(e){return It.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getRoads(e){return(await this.platform.roads(e)).flatMap((i,n)=>La.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=La.BY_NATURE[n]??La.DEFAULT,r=Number(e.properties.largeur_de_chaussee),a=Number(e.properties.nombre_de_voies),l=Number(e.properties.vitesse_moyenne_vl),c=La.DIRECTIONS[String(e.properties.sens_de_circulation??"")],h=La.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(l)&&l>0?{speedKmh:l}:{},points:i}]}}class tA{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(La.covers(e))try{const t=await this.regional.getRoads(e);if(t.length>0)return t}catch{}return this.worldwide.getRoads(e)}}class $h{attribution="Water © IGN, BD TOPO (Licence Ouverte 2.0)";platform;constructor(e={}){this.platform=new It(e)}static covers(e){return It.covers((e.south+e.north)/2,(e.west+e.east)/2)}async getWater(e){return(await this.platform.waterSurfaces(e)).flatMap((i,n)=>$h.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,l)=>{const c=a.map(h=>$h.open(h.map(d=>({lat:d[1],lng:d[0]})))).filter(h=>h.length>=3);return c.length>0?[{id:s.length>1?`${r}-${l}`: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 go{attribution="Water © OpenStreetMap contributors";client;constructor(e={}){this.client=new ms(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 go.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))),l=go.rings(a("inner"));for(const[c,h]of go.rings(a("outer")).entries()){const d=l.filter(u=>go.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 go.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 l=i[a],c=s[s.length-1];if(t(c,l[0]))s=[...s,...l.slice(1)];else if(t(c,l[l.length-1]))s=[...s,...[...l].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 iA{constructor(e,t){this.regional=e,this.worldwide=t}attribution="Water © IGN, BD TOPO, in France; © OpenStreetMap contributors elsewhere";async getWater(e){if($h.covers(e))try{const t=await this.regional.getWater(e);if(t.length>0)return t}catch{}return this.worldwide.getWater(e)}}const Af="https://ufoathome.org/water/index.json";class ql{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??ql.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(`
2954
+ `);let t=ql.shared.get(e);return t||(t=this.fetchFirstAvailable(),ql.shared.set(e,t),t.then(i=>{i.length===0&&ql.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.125.1")}`,t=typeof location>"u"?void 0:new URL("/water/index.json",location.href).href;return(t&&t!==Af?[t,Af]:[Af]).map(i=>i+e)}}class ts{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=ts.key(e);let i=ts.asked.get(t);return i===void 0&&(i=this.recalled(t,e),ts.asked.set(t,i),i.catch(()=>ts.asked.delete(t))),structuredClone(await i)}static forget(){ts.asked.clear()}static key(e){return`v${ts.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}async recalled(e,t){const i=await ts.openCache(),n=ts.request(e);if(i)try{const r=await i.match(n);if(r){const{savedAt:a,surfaces:l}=await r.json();if(Date.now()-a<ts.MAX_AGE_MS&&Array.isArray(l))return l;await i.delete(n)}}catch{}const s=await this.water.getWater(t);return i&&ts.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-ts.MAX_STORED)))await e.delete(r)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(ts.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 It(e)}static covers(e){return It.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}`),l=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(l)&&l>0?l: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 Kl{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 ms(e)}async getRailways(e){const t=`${e.south},${e.west},${e.north},${e.east}`,i=`[out:json][timeout:25];way["railway"~"^(${Kl.KINDS.join("|")})$"](${t});out tags geom;`;return this.linesFrom(await this.client.query(i))}linesFrom(e){const t=[];for(const i of e.elements??[]){const n=i.geometry,s=i.tags;if(!s||!n||n.length<2)continue;const r=Kl.kindOf(s),a=Number.parseInt((s.gauge??"").split(";")[0],10),l=Number.parseInt(s.tracks??"",10),c=Kl.electrificationOf(s.electrified),h=s.highspeed==="yes"||Number.parseInt(s.maxspeed??"",10)>=250;t.push({id:`osm-rail-${i.id??t.length}`,...s.name?{name:s.name}:{},kind:r,...s.service||s.usage==="industrial"?{service:!0}:{},...h?{highSpeed:!0}:{},gaugeMm:Number.isFinite(a)&&a>300?a:s.railway==="narrow_gauge"?1e3:1435,tracks:Number.isFinite(l)&&l>0?l:1,...c?{electrification:c}:{},level:Kl.levelOf(s),points:n.map(d=>({lat:d.lat,lng:d.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 nA{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 qi{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=qi.key(e);let i=qi.asked.get(t)??qi.containing(e);if(i===void 0){const n=this.recalled(t,e);i={bounds:e,answer:n},qi.asked.set(t,i),n.catch(()=>qi.asked.delete(t))}return structuredClone(await i.answer)}static forget(){qi.asked.clear(),qi.failedAt=-1/0}static key(e){return`v${qi.FORMAT}/${[e.north,e.south,e.east,e.west].map(t=>t.toFixed(5)).join("/")}`}static containing(e){for(const t of qi.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 qi.openCache(),n=qi.request(e);let s;if(i)try{const a=await i.match(n);if(a){const{savedAt:l,lines:c}=await a.json();if(Array.isArray(c)){if(this.now()-l<qi.MAX_AGE_MS)return c;s=c}}}catch{}if(this.now()-qi.failedAt<qi.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(qi.failedAt=this.now(),s)return s;throw a}return i&&qi.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-qi.MAX_STORED)))await e.delete(s)}catch{}}static async openCache(){try{return typeof caches>"u"?void 0:await caches.open(qi.CACHE_NAME)}catch{return}}static request(e){const t=typeof location>"u"?"http://localhost":location.origin;return new Request(`${t}/__railways__/${encodeURIComponent(e)}`)}}const Tf="https://ufoathome.org/railways/index.json";class Zl{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??Zl.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(`
2955
+ `);let t=Zl.shared.get(e);return t||(t=this.fetchFirstAvailable(),Zl.shared.set(e,t),t.then(i=>{i.length===0&&Zl.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.125.1")}`,t=typeof location>"u"?void 0:new URL("/railways/index.json",location.href).href;return(t&&t!==Tf?[t,Tf]:[Tf]).map(i=>i+e)}}function sA(){return{elevation:Pw(),imagery:Nw(),roads:rA(),water:aA(),railways:oA()}}function Pw(){return new JE(new ni,new Lw)}function Nw(){return new Ow}function rA(){return new Yl({fallback:new tA(new La,new Ia)})}function aA(){return new ql({fallback:new ts(new iA(new $h,new go))})}function oA(){return new Zl({fallback:new qi(new nA(new ka,new Kl))})}const lA=new Set,hc="show-observer-map",dc="hide-milestones";class _t extends HTMLElement{static get observedAttributes(){return["src",hc,dc]}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;static OBSERVER_MAP_MIN_SPAN_M=400;static OBSERVER_MAP_MARGIN=.6;static OBSERVER_MAP_IMAGERY_PX=768;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 Si;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=Qp.create();issues=[];issueCheck=0;sightingAudio=new FE;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>_t.LIVE_BODY_MAX_RATE}selectionShown=!0;overlayPainter;enableClickToPlay=!0;fullscreenTarget;messages=Sw;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");_t.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)<_t.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>=Si.MAX_ZOOM,this.mapZoomOutButton.disabled=this.observerMapView.zoomFactor<=Si.MIN_ZOOM,this.paintObserverMap(this.currentTime),this.scheduleObserverMapImagery()}scheduleObserverMapImagery(){this.observerMapImageryTimer===void 0&&(this.observerMapImageryTimer=setTimeout(()=>{this.observerMapImageryTimer=void 0,this.loadObserverMapImagery()},_t.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=Si.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>${kE}</style>${LE}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new Dc(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",t=>{t.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 mo(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",t=>{t.key!=="Enter"&&t.key!==" "||(t.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.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",t=>{this.suspendForScrub(),this.seekSnapArmed=!0,this.nameMarkUnder(t),this.beginTouchScrub(t)}),this.seekInput.addEventListener("pointerup",t=>{this.seekSnapArmed=!1,this.endTouchScrub(t)}),this.seekInput.addEventListener("pointercancel",t=>this.endTouchScrub(t)),this.seekInput.addEventListener("pointermove",t=>{this.nameMarkUnder(t),this.hoverSeek(t),this.touchScrub(t)}),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 t of["touchend","click"])this.stageElement.addEventListener(t,()=>this.sightingAudio.resume(),!0);this.stageElement.addEventListener("pointermove",()=>this.touchedStage()),this.canvas.addEventListener("click",t=>{if(this.enableClickToPlay){if(this.tapRevealedOnly){this.tapRevealedOnly=!1;return}t.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause()}}),this.canvas.addEventListener("dblclick",t=>{this.enableClickToPlay&&(t.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){e==="src"&&i&&i!==t&&this.isConnected&&this.loadFromSrc(i),e===hc&&this.applyObserverMapDefault(),e===dc&&this.setMilestonesShown(!this.hasAttribute(dc))}async loadFromSrc(e){this.sightingData=await wo.json(e)}get sightingData(){return UE(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=BE(e),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.syncPlayerTitle(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyObserverMapDefault()}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 Ae(async()=>{const{default:r}=await import("./sightingSchema-v2l1p6Ld.js");return{default:r}},[],import.meta.url);n=new Hs(s).issues(e)}catch{}i===this.issueCheck&&(this.issues=[...n,...t.loadIssues],Rc.warn(this.issues,t.id),this.renderIssues(),this.dispatchEvent(new CustomEvent(cA,{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=Rc.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.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 l=document.createElement("span");l.textContent=n,l.title=n,r.append(a,l);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"),_t.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)),_t.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?Xo(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 l=document.createElement("i");l.className="seek-hover";const c=document.createElement("i");c.className="seek-played",a.append(l,c),s.push(a),this.seekSegmentList.push({start:n[r],end:n[r+1],element:a,played:c,hover:l})}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}%`}paintSeekProgress(e){for(const i of this.seekSegmentList)i.played.style.width=_t.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=_t.fillOf(p,s),p.element.classList.toggle("hovered",s>=p.start&&(s<p.end||f===r))});const a=this.milestonesShown?this.sighting.milestones:[],l=a.length>0?xf(a,s):void 0,c=l?this.said.read(l.label)??"":"",h=l?this.said.read(l.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)),l=Math.max(1,Math.round(s*r));this.chromeCanvas.width!==a&&(this.chromeCanvas.width=a),this.chromeCanvas.height!==l&&(this.chromeCanvas.height=l),t.setTransform(1,0,0,1,0,0),t.clearRect(0,0,a,l),this.chromeDirty=!1,i&&(t.setTransform(a/this.canvas.width,0,0,l/this.canvas.height,0,0),this.chromeRenderer??=new Dc(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=_t.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?xf(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=Ys.windowEndingAt(e,this.currentSighting.exposure??0);if(t.ms<_t.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const i=Math.min(_t.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t.ms/_t.SHORTEST_VISIBLE_EXPOSURE_MS))),n=Math.ceil(this.travelPxOver(t.fromMs,t.ms)/_t.EXPOSURE_STEP_PX),s=Math.min(_t.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 l=n.get(r);if(!l)continue;const c=l.bounds.x+l.bounds.width/2-(a.bounds.x+a.bounds.width/2),h=l.bounds.y+l.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=Ht.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:lA;this.canvasRenderer.setStarPoints(Ht.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 l of s)for(const[,c]of l.shapes){const h=l.share,d=h===1?c:{...c,transparency:1-(1-c.transparency)*h};this.canvasRenderer.paintShape(this.shifted(d,r))}for(const[l,c]of s[0].shapes)!this.selectionShown||!i.has(l)||(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?Nt.groupBoundsFor([...t].filter(([l])=>i.has(l)).map(([,l])=>l.bounds)):void 0;this.chromeDraw=l=>{a&&l.paintGroupHandles(a),this.overlayPainter?.(l)},this.paintChrome(this.chromeDraw),this.mapShapeBounds=s.flatMap(l=>[...l.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.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.paintObserverMap(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Hg(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 OE(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*_t.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){_t.icons.get(e)!==t&&(_t.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},_t.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,_t.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(hc),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;_t.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=cc.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=Si.including(this.observerPath.boundsAround(_t.OBSERVER_MAP_MIN_SPAN_M,_t.OBSERVER_MAP_MARGIN),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.observerMapImageryNeededFor(e))return;const t=this.observerMapView.changed,i=t?Si.expand(e,_t.OBSERVER_MAP_IMAGERY_AHEAD):e,n=_t.OBSERVER_MAP_IMAGERY_PX*(t?1+2*_t.OBSERVER_MAP_IMAGERY_AHEAD:1);this.observerMapImageryBounds=i,this.observerMapImageryViewWidth=e.east-e.west,this.observerMapImageryLoading=!0;const s=Nw();try{const r=await s.getImageryTexture(i,{width:n,height:n});if(this.observerMapImageryBounds!==i)return;this.observerMapImagery=r,this.observerMapImageryCredit=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?Si.expand(e,_t.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}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=_t.OBSERVER_MAP_CLEARANCE_PX,a=i(...n,0),l=i(...s,0);if(this.observerMapPanel.classList.toggle("subject-overlap",a&&l&&!this.observerMapAsked),this.observerMapPanel.classList.contains("on-the-left")){!a&&(l||!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},_t.OBSERVER_MAP_FOLLOW_MARGIN)&&this.scheduleObserverMapImagery();const i=this.currentSighting.instrument,n=[],s=this.currentSighting.milestones.length>0?xf(this.currentSighting.milestones,e):void 0;for(const r of this.milestonesShown?Xo(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?er.of(i,this.canvas.height,t.fovDeg).halfWidthAngleDeg(Ht.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,Xo(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,_t.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=Qs(i,e),{lat:s,lng:r}=ws(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=er.of(this.currentSighting.instrument,this.canvas.height,n?.fovDeg??60);return{x:-s.angleDegToRadiusPx(t),y:s.angleDegToRadiusPx(i)}}gaitAt(e){return jt.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=_t.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;if(this.muteButton.disabled=e,this.volumeInput.hidden=e,e){_t.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}_t.setIcon(this.muteButton,this.mutedState?qe.MUTED:qe.VOLUME);const t=this.mutedState?this.messages.unmute:this.messages.mute;this.muteButton.title=t,this.muteButton.setAttribute("aria-label",t),this.muteButton.setAttribute("aria-pressed",String(this.mutedState)),this.volumeInput.value=String(this.level)}updateFullscreenButton(){const e=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;_t.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 Yn(Ss.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const e=Uh(Ss.preferencesFor(this),Vh);if(e===this.messagesLanguage||e==="en"&&this.messagesLanguage===void 0)return;this.messagesLanguage=e;const t=await sE(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=Tc(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?e0(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.updateTimeLabelTitles()}formatPosition(e){if(this.realDurationMs===void 0)return bd(e);const t=this.currentSighting.timeline.duration,i=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?O1(k1(this.realStartMs+i)):bd(i)}formatEndOfTimeline(){return this.realDurationMs===void 0?bd(this.currentSighting.timeline.duration):this.canShowClockTime?O1(k1(this.realStartMs+this.realDurationMs)):bd(this.realDurationMs)}}function k1(o){const e=new Date(o);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function O1(o){if(o.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return o.second?`${e(o.hour)}:${e(o.minute??0)}:${e(o.second)}`:`${e(o.hour)}:${e(o.minute??0)}`}function bd(o){const e=Math.round(o/1e3),t=Math.floor(e/60),i=e%60;return`${t}:${String(i).padStart(2,"0")}`}const o0="rr0-ufo";function sf(){customElements.get(o0)||customElements.define(o0,_t)}const cA="recordingissues";class Eh{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+Eh.SAME_INSTANT)?.t}previous(e){return[...this.marks].reverse().find(t=>t.t<e-Eh.SAME_INSTANT)?.t}static SAME_INSTANT=.5;markNear(e){const t=this.seekInput.getBoundingClientRect().width;if(this.duration<=0||t<=0)return;const i=Eh.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 jl{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=pt(m.lat,m.lng,n.lat,n.lng);return{eastM:v.x,northM:-v.z,heading:m.headingDeg}},a=r(t-jl.HALF_SPAN_MS),l=r(t+jl.HALF_SPAN_MS),c=r(t);if(!a||!l||!c)return 0;const h=Math.hypot(l.eastM-a.eastM,l.northM-a.northM),d=2*jl.HALF_SPAN_MS/1e3;if(h/d<jl.MIN_SPEED_MS)return 0;const u=i.heightAt(l.eastM,l.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(l.eastM-a.eastM,l.northM-a.northM)*180/Math.PI;return p*Math.max(0,Math.cos((c.heading-f)*Math.PI/180))}}const hA=.08,P1=.15,N1=2,dA=2e3;class uA{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}}Wo.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<dA),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(),l=e.createBiquadFilter();l.type="lowpass",l.frequency.value=900,s.connect(l),r.connect(l),l.connect(a).connect(n);const c=()=>{const w=e.createBufferSource();return w.buffer=this.noise(e),w.loop=!0,w.loopStart=Math.random()*N1*.5,w},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 M=[s,r,h,p,m];for(const w of M)w.start();const y={nodes:[i,n,a,l,d,u,f,g,v,b,...M],sources:M,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:l}){const c=e.currentTime,h=(b,M)=>b.setTargetAtTime(M,c,hA),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+l/60);h(t.cabin.frequency,m);const v=r?.5:Math.min(1,8/Math.max(l,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,P1/3),setTimeout(()=>{for(const n of t.sources)try{n.stop()}catch{}for(const n of t.nodes)n.disconnect()},P1*1e3*3))}noise(e){if(this.noiseBuffer)return this.noiseBuffer;const t=e.createBuffer(1,e.sampleRate*N1,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 Yh{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 Yh.BY_KIND[e??"generic"]??Yh.BY_KIND.generic}}class No{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=No.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 l=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<No.STANDING_MPS){l=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(;l<e.kmhPer1000Rpm.length&&p(l)>e.shiftUpRpm;)l++;for(;l>1&&p(l)<e.shiftDownRpm;)l--;const f=Math.max(p(l),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:l})}return new No(a,i)}at(e){const t=(e-this.startMs)/No.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 mp{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=pt(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=Yh.of(a.kind),h=this.drive(`observer:${a.kind}`,mp.trackKey(e),()=>No.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 l=s(t);for(const c of e.decor){if(!c.engine||!l)continue;const h=Yh.of(c.engine.kind),d=g=>{const m=Qs(c,g);return{eastM:m.eastM,northM:m.northM}},u=this.drive(`decor:${c.id}`,mp.decorKey(c,r),()=>No.of(h,d,0,r));if(!u)continue;const p=Qs(c,t),f=Math.hypot(p.eastM-l.eastM,p.northM-l.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 pA="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==",fA=`
2956
2956
  <div class="stage" id="stage">
2957
2957
  <div class="frame" id="frame">
2958
2958
  <canvas id="scene-canvas"></canvas>
@@ -9274,7 +9274,7 @@ uniform float uWindowLuminance;
9274
9274
  varying float vLitRank;
9275
9275
  ${l.fragmentShader.replace("#include <emissivemap_fragment>",`#include <emissivemap_fragment>
9276
9276
  totalEmissiveRadiance *= step(vLitRank, uLitShare) * uWindowLuminance * ${be.WINDOW_VARIETY_GLSL};`)}`},r.customProgramCacheKey=()=>"facade-lit-windows";const a=new we(s,r);return a.castShadow=!0,a.receiveShadow=!0,t.add(a),t}static litWindowShare={value:0};static WINDOW_LUMINANCE_CD_M2=50;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=xc.ofTemperature(be.ROOM_LIGHT_K),i=xc.ofTemperature(be.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){be.windowLuminance.value=be.WINDOW_LUMINANCE_CD_M2*e}static setLitWindowShare(e){be.litWindowShare.value=Math.max(0,Math.min(1,e))}static build(e,t){const i=be.hasFacade(e)?be.buildFacade(e):e.kind==="building"?Lb(e.floors??Ic,e.windows,e.observerSide,e.occupiedFloor,e.observerFacing):e.kind==="tree"?Ob():e.kind==="shrub"?kb():e.kind==="bridge"?vL(e):e.kind==="wire"?yL(e):e.kind==="wall"?Ki.build(e):e.kind==="crop"?Nb():e.kind==="terrace"?TL():e.kind==="mound"?Fb():e.kind==="streetlight"?Ub(t,e.lamp?.mount,e.lamp):e.kind==="trafficlight"?yt.trafficLight(it.isPedestrian(e.lights)):e.kind==="crossing"?yt.crossing(be.sizeOf(e).widthM,be.sizeOf(e).lengthM):e.kind==="roadsign"?yt.roadSign(e.sign):e.kind==="vehicle"?Bb(t,e.windows,e.observerSide,e.observerFacing):e.kind==="aircraft"?Xb():e.kind==="entity"?Vb():Rg();e.color&&this.paint(i,e.color);const n=this.scaleFor(e),s=new Lt;return e.kind==="building"&&e.roof&&this.addRoof(s,e),i.name=be.BODY_NAME,i.scale.copy(n),s.add(i),e.headingDeg!==void 0&&(s.rotation.y=-e.headingDeg*vm),this.addLights(s,e.lights),be.fitLights(s,e,e.sizeM??{}),s}static scaleFor(e){const t=e.sizeM;if(!t||be.hasFacade(e)||e.kind==="bridge"||e.kind==="wire"||e.kind==="terrace"||e.kind==="wall"||e.kind==="crossing")return new D(1,1,1);const i=this.naturalSize(e.kind,e.floors);return new D(t.widthM===void 0?1:Mm(t.widthM,i.widthM),t.heightM===void 0?1:Mm(e.kind==="building"?this.wallHeightOf(e,t.heightM):t.heightM,i.heightM),t.lengthM===void 0?1:Mm(t.lengthM,i.lengthM))}static naturalSize(e,t){const i=e==="building"?Math.max(1,(t??Ic)+1):0;if(e==="bridge")return ui.NATURAL_SIZE;if(e==="wire")return zo.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=zb.get(n);if(s)return s;const r=e==="building"?Lb(i-1,void 0,void 0,void 0):e==="tree"?Ob():e==="shrub"?kb():e==="crop"?Nb():e==="mound"?Fb():e==="streetlight"?Ub(!1):e==="trafficlight"?yt.trafficLight(!1):e==="roadsign"?yt.roadSign(void 0):e==="vehicle"?Bb(!1,void 0,void 0):e==="aircraft"?Xb():e==="entity"?Vb():Rg(),a=new gn().setFromObject(r),l={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),zb.set(n,l),l}static usesModel(e){return Xh(e)!==void 0&&!(e.observerSide!==void 0&&qa(e.kind))}static applyModel(e,t,i,n={}){const s=this.bodyOf(e),r=new Lt;r.name=be.BODY_NAME,i.rotation.y=-(n.headingOffsetDeg??0)*vm,i.updateMatrixWorld(!0);const a=new gn().setFromObject(i),l={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=Hb(c,l)??Hb(n.depictedSizeM,l)??1;i.scale.multiplyScalar(h);const d=a.getCenter(new D).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 we&&(u.castShadow=!0,u.receiveShadow=!0)}),r.add(i),t.kind==="tree"&&r.add(be.trunkShadow(l.heightM*h)),s.parent?(s.parent.add(r),s.removeFromParent()):e.add(r),this.dispose(s),be.fitLights(e,t,{widthM:l.widthM*h,heightM:l.heightM*h,lengthM:l.lengthM*h})}static fitLights(e,t,i){const n=_c.byId(Zs.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),l=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*l)})}static trunkShadow(e){be.trunkProxy??={geometry:new Nn(1,1.15,1,8),material:new Zn({colorWrite:!1,depthWrite:!1})};const t=new we(be.trunkProxy.geometry,be.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=lL(i.color),s=Gt(e,new Ui(i.radiusM??Db,8,6),n,i.offsetM.y,!0);s.position.set(i.offsetM.x,i.offsetM.y,i.offsetM.z),s.userData={emissive:!0,lightId:i.id,lightColor:n},s.visible=!1;const r=s.material;O2.apply(r),r.transparent=!0,r.depthWrite=!1,r.fog=!1}}static setLights(e,t,i,n,s=0,r=1,a=iL,l=[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(be.eyeScratch.copy(c)));for(const u of e.children){if(!(u instanceof we))continue;const p=u.userData.lightId;if(!p)continue;const f=h.get(p);if(!f)continue;const g=s>0?bw(f,i,i+s):wE(f,i)?1:0,m=d&&f.beamDeg!==void 0?be.beamShare(f.beamDeg,u.position,d):1;u.visible=g>0&&m>0;const v=f.radiusM??Db,b=Math.max(1,n*a/v),M=v*b,y=r/(Math.PI*M*M),w=u.material,S=u.userData.lightColor;if(S){const A=g*m*nL*(f.intensity??1)*y,x=[S[0]*A*l[0],S[1]*A*l[1],S[2]*A*l[2]],T=Math.min(1,Rb/Math.max(x[0],x[1],x[2],1e-30));w.color.setRGB(x[0]*T,x[1]*T,x[2]*T)}u.scale.setScalar(b)}}static eyeScratch=new D;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 l=-r/a;if(l<=0)return 0;const c=Math.log(.5)/Math.log(Math.cos(e*Math.PI/180));return Math.pow(l,c)}static setLit(e,t,i,n=1,s){const[r,a,l]=t==="streetlight"?[sL*be.lampShare(s),rL,be.lampHue(s)]:[aL,oL,ax],c=i?r*n/(Math.PI*a*a):0,h=Math.min(1,Rb/Math.max(l[0]*c,l[1]*c,l[2]*c,1e-30)),d=[l[0]*c*h,l[1]*c*h,l[2]*c*h];e.traverse(u=>{const p=u.userData;!(u instanceof we)||!p.emissive||p.lightId||(u.material instanceof Ns?u.material.emissive.setRGB(...d):u.material.color.setRGB(...d))})}static REFERENCE_LUMENS=1e4;static lampShare(e){return e?.lumens?e.lumens/be.REFERENCE_LUMENS:1}static lampHue(e){return e?.colorTemperatureK?xc.ofTemperature(e.colorTemperatureK):hL}static dispose(e){e.traverse(t=>{if(!(t instanceof we))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 xm="https://ufoathome.org/models/index.json";class kh{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??RL()}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(`
9277
- `);let t=kh.shared.get(e);return t||(t=this.fetchFirstAvailable(),kh.shared.set(e,t),t.then(i=>{i.length===0&&kh.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 RL(){const o=`?v=${encodeURIComponent("0.125.0")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==xm?[e,xm]:[xm]).map(t=>t+o)}const Uu=[{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 kh}];async function Sm(o){const{GLTFLoader:e}=await Ae(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-Bpggnw6G.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(o,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${o}: ${s instanceof Error?s.message:String(s)}`)))})}const px=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class ys{static MIN_ANGLE_RAD=.002;static FILL=(1-Math.exp(-4))/4;static MAX_SHARE=.3;mesh;constructor(e,t){this.mesh=new we(new jn(2,2),new kt({uniforms:{uColor:{value:new ne(0,0,0)},uRadius:{value:1}},vertexShader:ys.VERTEX,fragmentShader:ys.FRAGMENT,transparent:!0,depthWrite:!1,blending:an,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*ys.MIN_ANGLE_RAD),r=ys.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(ys.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(ys.MAX_SHARE*ys.FILL*Math.PI))}static VERTEX=`
9277
+ `);let t=kh.shared.get(e);return t||(t=this.fetchFirstAvailable(),kh.shared.set(e,t),t.then(i=>{i.length===0&&kh.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 RL(){const o=`?v=${encodeURIComponent("0.125.1")}`,e=typeof location>"u"?void 0:new URL("/models/index.json",location.href).href;return(e&&e!==xm?[e,xm]:[xm]).map(t=>t+o)}const Uu=[{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 kh}];async function Sm(o){const{GLTFLoader:e}=await Ae(async()=>{const{GLTFLoader:i}=await import("./GLTFLoader-Bpggnw6G.js");return{GLTFLoader:i}},[],import.meta.url),t=new e;return new Promise((i,n)=>{t.load(o,s=>i(Object.assign(s.scene,{animations:s.animations})),void 0,s=>n(new Error(`Could not load 3D model ${o}: ${s instanceof Error?s.message:String(s)}`)))})}const px=["ellipsoid","sphere","disc","cylinder","cone","box","torus","figure"];class ys{static MIN_ANGLE_RAD=.002;static FILL=(1-Math.exp(-4))/4;static MAX_SHARE=.3;mesh;constructor(e,t){this.mesh=new we(new jn(2,2),new kt({uniforms:{uColor:{value:new ne(0,0,0)},uRadius:{value:1}},vertexShader:ys.VERTEX,fragmentShader:ys.FRAGMENT,transparent:!0,depthWrite:!1,blending:an,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*ys.MIN_ANGLE_RAD),r=ys.FILL*Math.PI*s*s;return{radiusM:s,luminanceCdM2:i*Math.min(ys.MAX_SHARE,t/r)}}static conserving(e){return Math.sqrt(e/(ys.MAX_SHARE*ys.FILL*Math.PI))}static VERTEX=`
9278
9278
  uniform float uRadius;
9279
9279
  varying vec2 vOffset;
9280
9280
  void main() {