@rr0/ufoathome 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-embed-scene/{UfoMessages_fr-CYappnH6.js → UfoMessages_fr-BJPYHN17.js} +1 -1
- package/dist-embed-scene/rr0-scene.mjs +216 -212
- package/dist-embed-sighting/{UfoMessages_fr-CYappnH6.js → UfoMessages_fr-BJPYHN17.js} +1 -1
- package/dist-embed-sighting/rr0-sighting.mjs +218 -214
- package/dist-embed-sighting-editor/{UfoMessages_fr-CYappnH6.js → UfoMessages_fr-BJPYHN17.js} +1 -1
- package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +221 -217
- package/dist-embed-ufo/{UfoMessages_fr-CYappnH6.js → UfoMessages_fr-BJPYHN17.js} +1 -1
- package/dist-embed-ufo/rr0-ufo.mjs +11 -7
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const e={play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",autoReplay:"Lecture automatique",currentPosition:"Position actuelle",duration:"Durée",switchToElapsed:"cliquer pour afficher la durée écoulée",switchToClockTime:"cliquer pour afficher l'heure de l'observation",fullscreen:"Plein écran",exitFullscreen:"Quitter le plein écran",showWitnessMap:"Voir où était le témoin",hideWitnessMap:"Masquer où était le témoin",mapImageryUnavailable:"Vue aérienne indisponible",showMilestones:"Voir les moments du récit",hideMilestones:"Masquer les moments du récit"};export{e as ufoMessages_fr};
|
|
1
|
+
const e={play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",autoReplay:"Lecture automatique",currentPosition:"Position actuelle",duration:"Durée",switchToElapsed:"cliquer pour afficher la durée écoulée",switchToClockTime:"cliquer pour afficher l'heure de l'observation",fullscreen:"Plein écran",exitFullscreen:"Quitter le plein écran",showWitnessMap:"Voir où était le témoin",hideWitnessMap:"Masquer où était le témoin",mapImageryUnavailable:"Vue aérienne indisponible",showMilestones:"Voir les moments du récit",hideMilestones:"Masquer les moments du récit",witnessHere:"Le témoin, ici",decorHere:"Élément de décor"};export{e as ufoMessages_fr};
|
|
@@ -317,16 +317,20 @@ canvas[data-cursor="rotate"] {
|
|
|
317
317
|
border-radius: 3px;
|
|
318
318
|
overflow: hidden;
|
|
319
319
|
background: rgba(0, 0, 0, 0.55);
|
|
320
|
-
/*
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
the pointer. The recording underneath no longer takes that click, which is the trade: play/
|
|
324
|
-
pause is a button, a bar, a keypress and the rest of the canvas. */
|
|
325
|
-
cursor: pointer;
|
|
320
|
+
/* Its marks answer the pointer: hovering one names it, clicking one goes to it — a named moment
|
|
321
|
+
is an instant, everything else is a place to turn and look at. Closing it is the button that
|
|
322
|
+
opened it, which is where a reader looks for it anyway. */
|
|
326
323
|
}
|
|
327
324
|
.witness-map-panel[hidden] {
|
|
328
325
|
display: none;
|
|
329
326
|
}
|
|
327
|
+
/* Out of the phenomenon's way — see UfoElement.keepWitnessMapClear. The corner it normally sits in
|
|
328
|
+
is the emptiest part of most of these skies, which is exactly why it is there; when it is not,
|
|
329
|
+
the recording wins and the map moves. */
|
|
330
|
+
.witness-map-panel.on-the-left {
|
|
331
|
+
right: auto;
|
|
332
|
+
left: 0.4em;
|
|
333
|
+
}
|
|
330
334
|
.witness-map-panel canvas {
|
|
331
335
|
display: block;
|
|
332
336
|
width: 100%;
|
|
@@ -380,4 +384,4 @@ input[type=range] {
|
|
|
380
384
|
pointer-events: none;
|
|
381
385
|
white-space: nowrap;
|
|
382
386
|
}
|
|
383
|
-
`;class P extends Error{constructor(t,e,s){super(`${t}: ${e}${s===void 0?"":` (HTTP ${s})`}`),this.kind=t,this.url=e,this.status=s,this.name="SightingFetchError"}}class Dt{static async json(t){const e=new URL(t,location.href);if(location.protocol==="https:"&&e.protocol==="http:")throw new P("mixed-content",t);let s;try{s=await fetch(t)}catch{throw await this.diagnose(t,e)}if(!s.ok)throw new P("status",t,s.status);try{return await s.json()}catch{throw new P("malformed",t)}}static async diagnose(t,e){if(e.origin===location.origin)return new P("unreachable",t);try{return await fetch(t,{mode:"no-cors"}),new P("cors",t)}catch{return new P("unreachable",t)}}}const N=[{id:"eye",name:{en:"Naked eye",fr:"Œil nu"},flare:0,projection:"equidistant"},{id:"rectilinear-lens",name:{en:"Camera, unknown device",fr:"Appareil, modèle inconnu"},projection:"rectilinear",fNumber:8,fNumberRange:{min:2,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:8},apertureBlades:6},{id:"instamatic-126",name:{en:"Instamatic, 126 film",fr:"Instamatic, film 126"},projection:"rectilinear",frame:{widthMm:28,heightMm:28,focalLengthMm:43},fNumber:11,exposureSeconds:1/90,years:{from:1963,to:1988},apertureBlades:5,detailUm:20},{id:"slr-35mm-50",name:{en:"35 mm SLR, 50 mm lens",fr:"Reflex 35 mm, objectif 50 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:50},fNumber:8,fNumberRange:{min:2,max:16},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},years:{from:1959},apertureBlades:6,detailUm:20},{id:"slr-35mm-zoom",name:{en:"35 mm SLR, 70-210 mm zoom",fr:"Reflex 35 mm, zoom 70-210 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:135,focalRangeMm:{minMm:70,maxMm:210}},fNumber:8,fNumberRange:{min:4,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},apertureBlades:8,years:{from:1975},detailUm:20},{id:"phone-landscape",name:{en:"Phone, held sideways",fr:"Téléphone, tenu couché"},projection:"rectilinear",frame:{widthMm:7.6,heightMm:5.7,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4},{id:"phone-portrait",name:{en:"Phone, held upright",fr:"Téléphone, tenu debout"},projection:"rectilinear",frame:{widthMm:5.7,heightMm:7.6,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4}];class w{static get default(){return N[0]}static byId(t){return N.find(e=>e.id===t)??this.default}static UNAIDED_FIELD_DEG=60;static UNAIDED_ASPECT=16/9;static fieldOfViewDeg(t){const e=t.frame;return e?2*Math.atan(e.heightMm/(2*e.focalLengthMm))*180/Math.PI:w.UNAIDED_FIELD_DEG}static aspectOf(t){const e=t.frame;return e?e.widthMm/e.heightMm:w.UNAIDED_ASPECT}static frameWidthPx(t,e){return Math.round(e*w.aspectOf(t))}static availableAt(t){return t===void 0?N:N.filter(e=>!e.years||t>=e.years.from&&(e.years.to===void 0||t<=e.years.to))}static fieldOfViewDegAt(t,e){const s=t.frame;if(!(!s||e<=0))return 2*Math.atan(s.heightMm/(2*e))*180/Math.PI}static focalLengthMmFor(t,e){const s=t.frame;if(!(!s||e<=0||e>=180))return s.heightMm/(2*Math.tan(e*Math.PI/360))}static bladesShowing(t,e){const s=e??t.fNumber;if(s===void 0||t.apertureBlades===void 0)return 0;const i=t.fNumberRange;return!i||i.max<=i.min?1:Math.max(0,Math.min(1,(s-i.min)/(i.max-i.min)))}static LENS_FLARE_ARTIFACTS=1;static flareArtifactsOf(t){return t.flare??w.LENS_FLARE_ARTIFACTS}static starPointsOf(t){const e=t.apertureBlades;return e===void 0||e<3?0:e%2===0?e:e*2}}class v{static MOON_ANGULAR_WIDTH_DEG=.5237;static CANVAS_WIDTH_PX=640;static CANVAS_HEIGHT_PX=360;static angularWidthDeg(t){return 2*Math.atan(t.sizeM/(2*t.distanceM))*180/Math.PI}static inMoons(t){return t/v.MOON_ANGULAR_WIDTH_DEG}static lerpAngular(t,e,s){if(!(!t||!e))return{widthDeg:t.widthDeg+(e.widthDeg-t.widthDeg)*s,heightDeg:t.heightDeg+(e.heightDeg-t.heightDeg)*s}}static sizeMAt(t,e){return 2*t*Math.tan(e*Math.PI/360)}static distanceMAt(t,e){return t/(2*Math.tan(e*Math.PI/360))}}function Ct(n,t,e){if(!n||!t)return e<1?n:t;const s=((t.azimuthDeg-n.azimuthDeg)%360+540)%360-180;return{azimuthDeg:((n.azimuthDeg+s*e)%360+360)%360,altitudeDeg:n.altitudeDeg+(t.altitudeDeg-n.altitudeDeg)*e}}function Bt(n,t,e){const{bounds:s}=n;return t>=s.x&&t<=s.x+s.width&&e>=s.y&&e<=s.y+s.height}function y(n,t,e){return n+(t-n)*e}function it(n){const t=/^#([0-9a-f]{6})$/i.exec(n);if(!t)return;const e=parseInt(t[1],16);return[e>>16&255,e>>8&255,e&255]}function Rt(n){return`#${n.map(t=>Math.round(t).toString(16).padStart(2,"0")).join("")}`}function _t(n,t,e){const s=it(n),i=it(t);return!s||!i?e<1?n:t:Rt([y(s[0],i[0],e),y(s[1],i[1],e),y(s[2],i[2],e)])}function Ft(n,t,e){const s={x:y(n.bounds.x,t.bounds.x,e),y:y(n.bounds.y,t.bounds.y,e),width:y(n.bounds.width,t.bounds.width,e),height:y(n.bounds.height,t.bounds.height,e)},i=y(n.angle,t.angle,e),a=y(n.transparency,t.transparency,e),r=y(n.haloScale,t.haloScale,e),o=y(n.blur??0,t.blur??0,e),l=y(n.brightness??0,t.brightness??0,e),h=_t(n.color,t.color,e),d=v.lerpAngular(n.angular,t.angular,e),c=Ct(n.aim,t.aim,e);return n.kind==="polygon"&&t.kind==="polygon"&&n.points.length===t.points.length?{...n,bounds:s,angle:i,transparency:a,haloScale:r,blur:o,brightness:l,color:h,angular:d,aim:c,behindCloud:e<1?n.behindCloud:t.behindCloud,points:n.points.map((u,f)=>({x:y(u.x,t.points[f].x,e),y:y(u.y,t.points[f].y,e)}))}:{...e<1?n:t,bounds:s,angle:i,transparency:a,haloScale:r,blur:o,brightness:l,color:h,angular:d,aim:c}}class q{keyframes=[];order=[];groups=[];addKeyframe(t,e){const s=this.findInsertIndex(t);if(this.keyframes[s]?.t===t){const i=new Set(e.map(a=>a.sourceId));this.keyframes[s]={t,shapes:[...this.keyframes[s].shapes.filter(a=>!i.has(a.sourceId)),...e]}}else this.keyframes.splice(s,0,{t,shapes:[...e]});for(const i of e)this.order.includes(i.sourceId)||this.order.push(i.sourceId)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getKeyframeAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e];return s?.t===t?s:void 0}getShapeAt(t,e){return this.getKeyframeAt(t)?.shapes.find(s=>s.sourceId===e)?.shape}getLatestShapeAt(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(a=>a.sourceId===e);if(i)return i.shape}}getInterpolatedShapeAt(t,e){const s=this.findShapeAtOrBefore(t,e);if(s?.t===t)return s.shape;const i=this.findShapeAtOrAfter(t,e);return s?i?Ft(s.shape,i.shape,(t-s.t)/(i.t-s.t)):s.shape:i?.shape}findShapeAtOrBefore(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(a=>a.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}findShapeAtOrAfter(t,e){for(let s=this.findInsertIndex(t);s<this.keyframes.length;s++){const i=this.keyframes[s].shapes.find(a=>a.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}hitTest(t,e,s,i){const a=this.sourceIds;for(let r=a.length-1;r>=0;r--){if(i?.has(a[r]))continue;const o=this.getInterpolatedShapeAt(t,a[r]);if(o&&Bt(o,e,s))return{sourceId:a[r],shape:o}}}removeSource(t){for(let e=this.keyframes.length-1;e>=0;e--){const s=this.keyframes[e].shapes.filter(i=>i.sourceId!==t);s.length===0?this.keyframes.splice(e,1):s.length!==this.keyframes[e].shapes.length&&(this.keyframes[e]={t:this.keyframes[e].t,shapes:s})}this.order=this.order.filter(e=>e!==t),this.removeFromGroup(t)}group(t){if(!(t.length<2)){for(const e of t)this.removeFromGroup(e);this.groups.push([...t])}}ungroup(t){const e=this.groups.findIndex(s=>s.includes(t));e!==-1&&this.groups.splice(e,1)}groupMembers(t){return this.groups.find(e=>e.includes(t))}removeFromGroup(t){const e=this.groups.findIndex(i=>i.includes(t));if(e===-1)return;const s=this.groups[e].filter(i=>i!==t);s.length<2?this.groups.splice(e,1):this.groups[e]=s}bringToFront(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.push(t))}sendToBack(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.unshift(t))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){return[...this.order]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes,order:this.order,groups:this.groups}}static fromJSON(t){const e=new q;for(const s of t.keyframes)e.addKeyframe(s.t,s.shapes);if(t.order){const s=e.order.filter(i=>!t.order.includes(i));e.order=[...t.order,...s]}return t.groups&&(e.groups=t.groups.map(s=>s.filter(i=>e.order.includes(i))).filter(s=>s.length>=2)),e}}function Lt(n,t,e){return Math.max(t,Math.min(e,n))}function Ot(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function E(n,t,e){return n+(t-n)*e}function Nt(n,t,e){return{lat:n.lat===void 0||t.lat===void 0?void 0:E(n.lat,t.lat,e),lng:n.lng===void 0||t.lng===void 0?void 0:E(n.lng,t.lng,e),elevationM:E(n.elevationM,t.elevationM,e),headingDeg:n.headingDeg===void 0||t.headingDeg===void 0?void 0:Ot(n.headingDeg,t.headingDeg,e),pitchDeg:E(n.pitchDeg,t.pitchDeg,e),rollDeg:E(n.rollDeg??0,t.rollDeg??0,e),fovDeg:E(n.fovDeg,t.fovDeg,e),fNumber:n.fNumber,focusDistanceM:n.focusDistanceM}}class _{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,pose:e}:this.keyframes.splice(s,0,{t,pose:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestPoseAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].pose:void 0}getInterpolatedPoseAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.pose;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?Nt(s.pose,i.pose,Lt((t-s.t)/(i.t-s.t),0,1)):s.pose:i?.pose}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new _;for(const s of t.keyframes)e.addKeyframe(s.t,s.pose);return e}}function S(n,t,e){return n+(t-n)*e}function Wt(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function zt(n,t,e){return Math.max(t,Math.min(e,n))}function Ht(n,t,e){return{cloudCover:S(n.cloudCover,t.cloudCover,e),cloudDarkness:S(n.cloudDarkness,t.cloudDarkness,e),highCloudCover:n.highCloudCover===void 0||t.highCloudCover===void 0?void 0:S(n.highCloudCover,t.highCloudCover,e),iceCrystalAlignment:n.iceCrystalAlignment===void 0||t.iceCrystalAlignment===void 0?void 0:S(n.iceCrystalAlignment,t.iceCrystalAlignment,e),lowerCloudCover:n.lowerCloudCover===void 0||t.lowerCloudCover===void 0?void 0:S(n.lowerCloudCover,t.lowerCloudCover,e),cloudBaseM:n.cloudBaseM===void 0||t.cloudBaseM===void 0?void 0:S(n.cloudBaseM,t.cloudBaseM,e),precipitationType:e<1?n.precipitationType:t.precipitationType,precipitationIntensity:S(n.precipitationIntensity,t.precipitationIntensity,e),windDirectionDeg:Wt(n.windDirectionDeg,t.windDirectionDeg,e),windSpeed:S(n.windSpeed,t.windSpeed,e),storm:e<1?n.storm:t.storm}}class F{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,weather:e}:this.keyframes.splice(s,0,{t,weather:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestWeatherAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].weather:void 0}getInterpolatedWeatherAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.weather;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?Ht(s.weather,i.weather,zt((t-s.t)/(i.t-s.t),0,1)):s.weather:i?.weather}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new F;for(const s of t.keyframes)e.addKeyframe(s.t,s.weather);return e}}class L{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,sound:e}:this.keyframes.splice(s,0,{t,sound:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}getLatestSoundAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].sound:void 0}getInterpolatedSoundAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.sound;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];if(!s)return i?.sound;if(!i)return s.sound;const a=(t-s.t)/(i.t-s.t);return this.lerp(s.sound,i.sound,Math.max(0,Math.min(1,a)))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new L;for(const s of t.keyframes)e.addKeyframe(s.t,s.sound);return e}lerp(t,e,s){return{kind:s<1?t.kind:e.kind,volume:t.volume+(e.volume-t.volume)*s,pitchHz:t.pitchHz+(e.pitchHz-t.pitchHz)*s,src:s<1?t.src:e.src}}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}}const Gt=100,Ut={kind:"none",volume:0,pitchHz:Gt};function et(n){if(n.year!==void 0)return Date.UTC(n.year,(n.month??1)-1,n.day??1,n.hour??0,n.minute??0,n.second??0)}function nt(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const $t=["year","month","day","hour","minute"],Xt=["day","hour","minute"];function at(n,t){return t.filter(e=>n[e]!==void 0).join(",")}function Vt(n){if(n.durationSeconds!==void 0)return n.durationSeconds*1e3;if(!n.time||!n.endTime)return;const t=n.time.year!==void 0&&n.endTime.year!==void 0,e=t?$t:Xt,s=at(n.time,e);if(!(s===""||s!==at(n.endTime,e)))return t?et(n.endTime)-et(n.time):nt(n.endTime)-nt(n.time)}class Z{constructor(t,e,s,i,a,r,o,l,h=[],d,c,u,f=[]){this.event=t,this.timeline=e,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=a,this.witness=r,this.caseId=o,this.weather=l,this.decor=h,this.weatherSource=d,this.instrumentId=c,this.exposureSeconds=u,this.milestones=f}get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return w.byId(this.instrumentId)}static create(t,e,s){return new Z({eventType:"sighting",time:t,place:e},new q,new _,new F,new L,s)}}const Kt=0,qt=0;function I(n,t){const e=n.witnessTrack.getInterpolatedPoseAt(t);if(e)return e;const s=n.event.place?.[0];if(s)return{lat:s.lat,lng:s.lng,elevationM:Kt,headingDeg:void 0,pitchDeg:qt,fovDeg:w.fieldOfViewDeg(n.instrument)}}function Zt(n,t){return n.soundTrack.getInterpolatedSoundAt(t)??Ut}class Yt{constructor(t,e){this.timeline=t,this.onFrame=e}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 t=()=>{if(this.state!=="playing")return;const e=performance.now();if(this.currentT+=(e-this.lastWallTime)*this.playbackRate,this.lastWallTime=e,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t)};this.rafId=requestAnimationFrame(t)}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(t){this.currentT=Math.max(0,Math.min(t,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(t){const e=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}this.onFrame(t,e)}}const St=["nw","n","ne","e","se","s","sw","w"],Jt=24,W=8,jt=3,Qt=8;function z(n,t,e){const s=n.x-t.x,i=n.y-t.y,a=Math.cos(e),r=Math.sin(e);return{x:t.x+s*a-i*r,y:t.y+s*r+i*a}}function B(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const te={nw:{left:!0,top:!0},n:{top:!0},ne:{right:!0,top:!0},e:{right:!0},se:{right:!0,bottom:!0},s:{bottom:!0},sw:{left:!0,bottom:!0},w:{left:!0}};class b{static handlePointsFor(t){const{x:e,y:s,width:i,height:a}=t.bounds,r={nw:{x:e,y:s},n:{x:e+i/2,y:s},ne:{x:e+i,y:s},e:{x:e+i,y:s+a/2},se:{x:e+i,y:s+a},s:{x:e+i/2,y:s+a},sw:{x:e,y:s+a},w:{x:e,y:s+a/2},rotate:{x:e+i/2,y:s-Jt}},o=B(t.bounds),l={};for(const h of Object.keys(r))l[h]=z(r[h],o,t.angle);return l}static hitTestHandle(t,e,s=8,i=[...St,"rotate"]){const a=b.handlePointsFor(t);for(const r of i)if(Math.hypot(e.x-a[r].x,e.y-a[r].y)<=s)return r}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(t,e){const i=((b.HANDLE_DIRECTION_DEG[t]+e*180/Math.PI)%180+180)%180/45,a=["ew","nwse","ns","nesw"];return a[Math.round(i)%a.length]}static resizeBounds(t,e,s,i){const a=B(t),r=z(i,a,-e),{x:o,y:l,width:h,height:d}=t,c=te[s];let u=o,f=l,m=o+h,p=l+d;return c.left&&(u=Math.min(r.x,m-W)),c.right&&(m=Math.max(r.x,u+W)),c.top&&(f=Math.min(r.y,p-W)),c.bottom&&(p=Math.max(r.y,f+W)),{x:u,y:f,width:m-u,height:p-f}}static resizeShape(t,e,s){if(e==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:i,height:a}=t.bounds,r=b.resizeBounds(t.bounds,t.angle,e,s);if(t.kind==="oval")return{...t,bounds:r};const o=i===0?1:r.width/i,l=a===0?1:r.height/a;return{...t,bounds:r,points:t.points.map(h=>({x:h.x*o,y:h.y*l}))}}static rotateShape(t,e){const s=B(t.bounds),i=Math.atan2(e.y-s.y,e.x-s.x)+Math.PI/2;return{...t,angle:i}}static groupBoundsFor(t){const e=Math.min(...t.map(r=>r.x)),s=Math.min(...t.map(r=>r.y)),i=Math.max(...t.map(r=>r.x+r.width)),a=Math.max(...t.map(r=>r.y+r.height));return{x:e,y:s,width:i-e,height:a-s}}static vertexPointsFor(t){const e=B(t.bounds);return t.points.map(s=>z({x:t.bounds.x+s.x,y:t.bounds.y+s.y},e,t.angle))}static hitTestVertex(t,e,s=Qt){const i=b.vertexPointsFor(t);let a,r=s;return i.forEach((o,l)=>{const h=Math.hypot(e.x-o.x,e.y-o.y);h<=r&&(r=h,a=l)}),a}static toLocalPoint(t,e){const s=B(t.bounds),i=z(e,s,-t.angle);return{x:i.x-t.bounds.x,y:i.y-t.bounds.y}}static moveVertex(t,e,s){const i=t.points.map((c,u)=>u===e?b.toLocalPoint(t,s):c),a=Math.min(...i.map(c=>c.x)),r=Math.min(...i.map(c=>c.y)),o=Math.max(...i.map(c=>c.x)),l=Math.max(...i.map(c=>c.y)),h={x:t.bounds.x+a,y:t.bounds.y+r,width:o-a,height:l-r},d=i.map(c=>({x:c.x-a,y:c.y-r}));return{...t,bounds:h,points:d}}static insertVertexNear(t,e){const s=b.toLocalPoint(t,e),{points:i}=t;let a=i.length-1,r=1/0;for(let l=0;l<i.length;l++){const h=i[l],d=i[(l+1)%i.length],c=b.distanceToSegment(s,h,d);c<r&&(r=c,a=l)}const o=[...i.slice(0,a+1),s,...i.slice(a+1)];return{...t,points:o}}static distanceToSegment(t,e,s){const i=s.x-e.x,a=s.y-e.y,r=i*i+a*a,o=r===0?0:Math.max(0,Math.min(1,((t.x-e.x)*i+(t.y-e.y)*a)/r)),l=e.x+o*i,h=e.y+o*a;return Math.hypot(t.x-l,t.y-h)}static deleteVertex(t,e){return t.points.length<=jt?t:{...t,points:t.points.filter((s,i)=>i!==e)}}}const st=6,H=st/2,ee=20,se=24,ie=9,rt=6,ne=7,ae=6,ot=4;class re{constructor(t){this.ctx=t}starPoints=0;roll=0;clear(t,e){this.ctx.clearRect(0,0,t,e)}paintShape(t){this.ctx.save(),this.ctx.globalAlpha=1-t.transparency;const e=t.blur??0;e>0&&(this.ctx.filter=`blur(${(se*e).toFixed(2)}px)`);const s=t.brightness??0;s>0&&this.paintDazzle(t,s),t.haloScale>0&&this.paintHalo(t),this.paintBase(t),this.ctx.restore(),t.selected&&this.paintSelectionHandles(t)}setStarPoints(t){this.starPoints=Math.max(0,Math.floor(t))}setRoll(t){this.roll=t}paintDazzle(t,e){const{x:s,y:i,width:a,height:r}=t.bounds,o=s+a/2,l=i+r/2,h=Math.max(a,r)/2,d=Math.max(h*(1+ie*e),ae*e);if(d<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const c=this.ctx.createRadialGradient(o,l,0,o,l,d);for(let u=0;u<=rt;u++){const f=u/rt,m=1/(1+24*f*f);this.ctx.globalAlpha=1,c.addColorStop(f,this.withAlpha(t.color,m*e*.55))}if(this.ctx.fillStyle=c,this.ctx.beginPath(),this.ctx.arc(o,l,d,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const u=h*(1+ne*e);this.ctx.lineWidth=Math.max(1,h*.12),this.ctx.lineCap="round";for(let f=0;f<this.starPoints;f++){const m=f/this.starPoints*Math.PI*2+this.roll,p=o+Math.cos(m)*u,g=l+Math.sin(m)*u,x=this.ctx.createLinearGradient(o,l,p,g);x.addColorStop(0,this.withAlpha(t.color,e*.55)),x.addColorStop(.35,this.withAlpha(t.color,e*.22)),x.addColorStop(1,this.withAlpha(t.color,0)),this.ctx.strokeStyle=x,this.ctx.beginPath(),this.ctx.moveTo(o,l),this.ctx.lineTo(p,g),this.ctx.stroke()}}this.ctx.restore()}dazzledFill(t,e){if(e<=0)return t.color;const s=/^#([0-9a-f]{6})$/i.exec(t.color.trim());if(!s)return t.color;const i=Number.parseInt(s[1],16),a=r=>Math.round(r+(255-r)*e);return`rgb(${a(i>>16&255)}, ${a(i>>8&255)}, ${a(i&255)})`}withAlpha(t,e){const s=Math.max(0,Math.min(1,e)),i=/^#([0-9a-f]{6})$/i.exec(t.trim());if(!i)return t;const a=Number.parseInt(i[1],16);return`rgba(${a>>16&255}, ${a>>8&255}, ${a&255}, ${s.toFixed(3)})`}paintBase(t){if(this.ctx.fillStyle=this.dazzledFill(t,t.brightness??0),this.ctx.beginPath(),t.kind==="oval"){const{x:e,y:s,width:i,height:a}=t.bounds,r=i/2,o=a/2;this.ctx.ellipse(e+r,s+o,r,o,t.angle,0,2*Math.PI)}else{const{x:e,y:s,width:i,height:a}=t.bounds;this.ctx.save(),this.ctx.translate(e+i/2,s+a/2),this.ctx.rotate(t.angle),this.ctx.translate(-i/2,-a/2),t.points.forEach((r,o)=>{o===0?this.ctx.moveTo(r.x,r.y):this.ctx.lineTo(r.x,r.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(t){this.ctx.save(),this.ctx.shadowColor=t.color,this.ctx.shadowBlur=ee*t.haloScale,this.paintBase(t),this.ctx.restore()}paintSelectionHandles(t){this.paintHandleFrame(b.handlePointsFor(t),{includeRotate:!0}),t.kind==="polygon"&&this.paintVertexHandles(t)}paintVertexHandles(t){this.ctx.fillStyle="#39f";for(const e of b.vertexPointsFor(t))this.ctx.beginPath(),this.ctx.ellipse(e.x,e.y,ot,ot,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(t){this.paintOutline(b.handlePointsFor(t))}paintGroupHandles(t){this.paintHandleFrame(b.handlePointsFor({bounds:t,angle:0}),{includeRotate:!0})}paintOutline(t){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(t.nw.x,t.nw.y),this.ctx.lineTo(t.ne.x,t.ne.y),this.ctx.lineTo(t.se.x,t.se.y),this.ctx.lineTo(t.sw.x,t.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(t,{includeRotate:e}){this.paintOutline(t),this.ctx.fillStyle="lightgray";for(const s of St){const i=t[s];this.ctx.fillRect(i.x-H,i.y-H,st,st)}e&&(this.ctx.beginPath(),this.ctx.moveTo(t.n.x,t.n.y),this.ctx.lineTo(t.rotate.x,t.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(t.rotate.x,t.rotate.y,H+1,H+1,0,0,2*Math.PI),this.ctx.fill())}}const ht=.05,lt=.08,ct=2,dt=3,oe=14,he=.02;class le{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(t){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",t);return}}this.context.state==="suspended"&&this.context.resume()}setSound(t){if(this.requested=t,!this.context)return;const e=this.keyFor(t);if(!e){this.silence();return}if(this.voice?.key!==e){this.stopVoice();const s=++this.voiceToken;e.startsWith("src:")?this.buildRecordedVoice(t.src,e,s):this.voice=this.buildSynthesizedVoice(t,e)}this.applyTo(this.voice,t)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(t){if(!(t.volume<=0))return t.src?`src:${t.src}`:t.kind==="none"?void 0:t.kind}applyTo(t,e){const s=this.context;if(!t||!s)return;const i=s.currentTime;t.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,e.volume))*t.gainScale,i,ht);for(const{param:a,ratio:r}of t.pitch)a.setTargetAtTime(Math.min(e.pitchHz*r,s.sampleRate/2-1),i,ht)}buildSynthesizedVoice(t,e){const s=this.context;if(s)switch(t.kind){case"hum":return this.buildHum(s,e);case"whistle":return this.buildWhistle(s,e);case"rumble":return this.buildRumble(s,e);case"crackle":return this.buildCrackle(s,e);default:return}}buildHum(t,e){const s=t.createOscillator();s.type="sawtooth";const i=t.createOscillator();i.type="sawtooth";const a=t.createBiquadFilter();a.type="lowpass",a.Q.value=1;const r=t.createGain();return r.gain.value=0,s.connect(a),i.connect(a),a.connect(r).connect(t.destination),s.start(),i.start(),{key:e,nodes:[a,r],sources:[s,i],volumeParam:r.gain,gainScale:.16,pitch:[{param:s.frequency,ratio:1},{param:i.frequency,ratio:1.006},{param:a.frequency,ratio:6}]}}buildWhistle(t,e){const s=t.createOscillator();s.type="sine";const i=t.createOscillator();i.type="sine",i.frequency.value=5;const a=t.createGain(),r=t.createGain();return r.gain.value=0,i.connect(a).connect(s.frequency),s.connect(r).connect(t.destination),s.start(),i.start(),{key:e,nodes:[a,r],sources:[s,i],volumeParam:r.gain,gainScale:.22,pitch:[{param:s.frequency,ratio:1},{param:a.gain,ratio:.012}]}}buildRumble(t,e){const s=t.createBufferSource();s.buffer=this.brownNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="lowpass",i.Q.value=.8;const a=t.createGain();return a.gain.value=0,s.connect(i).connect(a).connect(t.destination),s.start(),{key:e,nodes:[i,a],sources:[s],volumeParam:a.gain,gainScale:1.4,pitch:[{param:i.frequency,ratio:1}]}}buildCrackle(t,e){const s=t.createBufferSource();s.buffer=this.whiteNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="bandpass",i.Q.value=1.2;const a=t.createGain();a.gain.value=0;const r=t.createBufferSource();r.buffer=this.crackleEnvelope(t),r.loop=!0;const o=t.createGain();return o.gain.value=0,r.connect(o).connect(a.gain),s.connect(i).connect(a).connect(t.destination),s.start(),r.start(),{key:e,nodes:[i,a,o],sources:[s,r],volumeParam:o.gain,gainScale:.9,pitch:[{param:i.frequency,ratio:4}]}}async buildRecordedVoice(t,e,s){const i=this.context;if(!i)return;let a;try{a=await this.getBuffer(t)}catch(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);return}if(s!==this.voiceToken||!this.context)return;const r=i.createBufferSource();r.buffer=a,r.loop=!0;const o=i.createGain();o.gain.value=0,r.connect(o).connect(i.destination),r.start(),this.voice={key:e,nodes:[o],sources:[r],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(t){const e=this.context;if(!e)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let s=this.buffers.get(t);return s||(s=fetch(t).then(i=>{if(!i.ok)throw new Error(`Audio fetch failed (${i.status}): ${t}`);return i.arrayBuffer()}).then(i=>e.decodeAudioData(i)),this.buffers.set(t,s)),s}whiteNoise(t){if(!this.noiseBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*ct),t.sampleRate),s=e.getChannelData(0);for(let i=0;i<s.length;i++)s[i]=Math.random()*2-1;this.noiseBuffer=e}return this.noiseBuffer}brownNoise(t){const e=t.createBuffer(1,Math.floor(t.sampleRate*ct),t.sampleRate),s=e.getChannelData(0);let i=0,a=0;for(let r=0;r<s.length;r++)i=(i+.02*(Math.random()*2-1))/1.02,s[r]=i,a=Math.max(a,Math.abs(i));if(a>0)for(let r=0;r<s.length;r++)s[r]/=a;return e}crackleEnvelope(t){if(!this.crackleBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*dt),t.sampleRate),s=e.getChannelData(0),i=Math.max(1,Math.floor(he*t.sampleRate)),a=Math.round(dt*oe);for(let r=0;r<a;r++){const o=Math.floor(Math.random()*s.length),l=.4+Math.random()*.6;for(let h=0;h<i&&o+h<s.length;h++)s[o+h]=Math.max(s[o+h],l*(1-h/i))}this.crackleBuffer=e}return this.crackleBuffer}stopVoice(){const t=this.voice,e=this.context;if(this.voice=void 0,!t||!e)return;const s=e.currentTime;t.volumeParam.cancelScheduledValues(s),t.volumeParam.setTargetAtTime(0,s,lt/3);const i=s+lt;for(const a of t.sources){a.onended=()=>{a.disconnect();for(const r of t.nodes)r.disconnect()};try{a.stop(i)}catch{a.disconnect();for(const r of t.nodes)r.disconnect()}}}}function X(n){return[...n].sort((t,e)=>t.t-e.t)}function ut(n,t){let e;for(const s of X(n)){if(s.t>t)break;e=s}return e}const J=Math.PI/180,G=180/Math.PI;class D{constructor(t,e,s){this.kind=t,this.canvasHeightPx=e,this.fovDeg=s;const i=s*J/2;this.focalPx=t==="equidistant"?e/2/i:e/2/Math.tan(i)}focalPx;static of(t,e,s){return new D(t.projection,e,s)}degToPx(t){const e=t*J;return this.kind==="equidistant"?this.focalPx*e:2*this.focalPx*Math.tan(e/2)}pxToDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:2*Math.atan(t/(2*this.focalPx))*G}angleDegToRadiusPx(t){const e=t*J;return this.kind==="equidistant"?this.focalPx*e:this.focalPx*Math.tan(e)}halfWidthAngleDeg(t){return this.radiusPxToAngleDeg(this.canvasHeightPx*t/2)}radiusPxToAngleDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:Math.atan(t/this.focalPx)*G}ofBounds(t){return{widthDeg:this.pxToDeg(t.width),heightDeg:this.pxToDeg(t.height)}}toBoundsSize(t){return{width:this.degToPx(t.widthDeg),height:this.degToPx(t.heightDeg)}}widthPx(t){return this.degToPx(v.angularWidthDeg(t))}}class T{static fovOf(t,e){return I(t,e)?.fovDeg??w.fieldOfViewDeg(t.instrument)}static toAngular(t){this.eachKeyframe(t,(e,s)=>({...e,angular:s.ofBounds(e.bounds)}))}static toBounds(t){this.eachKeyframe(t,(e,s)=>{if(!e.angular)return e;const{width:i,height:a}=s.toBoundsSize(e.angular),r={x:e.bounds.x+(e.bounds.width-i)/2,y:e.bounds.y+(e.bounds.height-a)/2,width:i,height:a};if(e.kind!=="polygon")return{...e,bounds:r};const o=e.bounds.width===0?1:i/e.bounds.width,l=e.bounds.height===0?1:a/e.bounds.height;return{...e,bounds:r,points:e.points.map(h=>({x:h.x*o,y:h.y*l}))}})}static toAim(t){this.eachKeyframe(t,(e,s,i)=>{if(i?.headingDeg===void 0)return e;const a=this.frameCentre(t),r=s.radiusPxToAngleDeg(e.bounds.x+e.bounds.width/2-a.x),o=s.radiusPxToAngleDeg(a.y-(e.bounds.y+e.bounds.height/2));return{...e,aim:{azimuthDeg:((i.headingDeg+r)%360+360)%360,altitudeDeg:i.pitchDeg+o}}})}static toPosition(t){this.eachKeyframe(t,(e,s,i)=>{if(!e.aim||i?.headingDeg===void 0)return e;const a=this.frameCentre(t),r=this.shortestArc(e.aim.azimuthDeg-i.headingDeg),o=e.aim.altitudeDeg-i.pitchDeg,l=a.x+this.offAxisPx(s,r)-e.bounds.width/2,h=a.y-this.offAxisPx(s,o)-e.bounds.height/2;return{...e,bounds:{...e.bounds,x:l,y:h}}})}static frameCentre(t){return{x:w.frameWidthPx(t.instrument,v.CANVAS_HEIGHT_PX)/2,y:v.CANVAS_HEIGHT_PX/2}}static shortestArc(t){return(t%360+540)%360-180}static offAxisPx(t,e){return Math.abs(e)>=T.OFF_CANVAS_DEG?Math.sign(e)*T.OFF_CANVAS_PX:t.angleDegToRadiusPx(e)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(t,e,s){const i=w.frameWidthPx(e,v.CANVAS_HEIGHT_PX)/2,a=w.frameWidthPx(t.instrument,v.CANVAS_HEIGHT_PX)/2,r=v.CANVAS_HEIGHT_PX/2;for(const o of[...t.timeline.allKeyframes]){const l=this.fovOf(t,o.t),h=s?.get(o.t)??l,d=D.of(e,v.CANVAS_HEIGHT_PX,h),c=D.of(t.instrument,v.CANVAS_HEIGHT_PX,l);t.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:f}=u.shape,m=f.x+f.width/2-i,p=f.y+f.height/2-r,g=Math.hypot(m,p),x=g===0?1:c.angleDegToRadiusPx(d.radiusPxToAngleDeg(g))/g,O={...f,x:a+m*x-f.width/2,y:r+p*x-f.height/2};return{...u,shape:{...u.shape,bounds:O}}}))}this.toBounds(t)}static eachKeyframe(t,e){const{timeline:s}=t;for(const i of[...s.allKeyframes]){const a=I(t,i.t),r=this.fovOf(t,i.t),o=D.of(t.instrument,v.CANVAS_HEIGHT_PX,r);s.addKeyframe(i.t,i.shapes.map(l=>({...l,shape:e(l.shape,o,a)})))}}}function ce(n){return T.toAngular(n),T.toAim(n),{version:1,time:n.event.time,endTime:n.event.endTime,durationSeconds:n.event.durationSeconds,utcOffsetHours:n.event.utcOffsetHours,timeZone:n.event.timeZone,place:n.event.place,witness:n.witness,caseId:n.caseId,description:n.event.description,tags:n.event.tags,timeline:n.timeline.toJSON(),witnessTrack:n.witnessTrack.toJSON(),weatherTrack:n.weatherTrack.toJSON(),soundTrack:n.soundTrack.toJSON(),weather:n.weather,decor:n.decor,milestones:n.milestones.length>0?n.milestones:void 0,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function de(n){const t=new Z({eventType:"sighting",time:n.time,endTime:n.endTime,durationSeconds:n.durationSeconds,utcOffsetHours:n.utcOffsetHours,timeZone:n.timeZone,place:n.place,description:n.description,tags:n.tags},q.fromJSON(n.timeline),n.witnessTrack?_.fromJSON(n.witnessTrack):new _,n.weatherTrack?F.fromJSON(n.weatherTrack):new F,n.soundTrack?L.fromJSON(n.soundTrack):new L,n.witness,n.caseId,n.weather,n.decor??[],n.weatherSource,n.instrument,n.exposureSeconds??n.witnessTrack?.keyframes.map(e=>e.pose.exposureSeconds).find(e=>e!==void 0),X(n.milestones??[]));return T.toBounds(t),T.toPosition(t),t}function ue(n,t){for(const e of n){const s=e.toLowerCase().split("-")[0];if(t.includes(s))return s}return"en"}class ft{static preferencesFor(t){const e=navigator.languages??[],s=this.declaredFor(t);return s?[s,...e]:e}static declaredFor(t){for(let e=t;e;){const s=e.closest("[lang]")?.getAttribute("lang")?.trim();if(s)return s;const i=e.getRootNode();e=i instanceof ShadowRoot?i.host:void 0}}}class k{constructor(t){this.preferences=t}read(t){if(t===void 0||typeof t=="string")return t===""?void 0:t;for(const s of this.preferences){const i=t[s]??t[k.base(s)];if(i)return i}const e=new Set(this.preferences.map(s=>k.base(s)));for(const[s,i]of Object.entries(t))if(i&&e.has(k.base(s)))return i;return Object.values(t).find(s=>s)??void 0}write(t,e,s){const i=e?.trim()?e.trim():void 0;if(t===void 0||typeof t=="string")return i;const a={...t},r=Object.keys(a).find(o=>o===s)??Object.keys(a).find(o=>k.base(o)===k.base(s))??s;return i===void 0?delete a[r]:a[r]=i,Object.keys(a).length===0?void 0:a}static base(t){return t.toLowerCase().split("-")[0]}}const fe="modulepreload",me=function(n,t){return new URL(n,t).href},mt={},pt=function(t,e,s){let i=Promise.resolve();if(e&&e.length>0){let r=function(d){return Promise.all(d.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),h=l?.nonce||l?.getAttribute("nonce");i=r(e.map(d=>{if(d=me(d,s),d in mt)return;mt[d]=!0;const c=d.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!s)for(let p=o.length-1;p>=0;p--){const g=o[p];if(g.href===d&&(!c||g.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${d}"]${u}`))return;const m=document.createElement("link");if(m.rel=c?"stylesheet":fe,c||(m.as="script"),m.crossOrigin="",m.href=d,h&&m.setAttribute("nonce",h),document.head.appendChild(m),c)return new Promise((p,g)=>{m.addEventListener("load",p),m.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${d}`)))})}))}function a(r){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=r,window.dispatchEvent(o),!o.defaultPrevented)throw r}return i.then(r=>{for(const o of r||[])o.status==="rejected"&&a(o.reason);return t().catch(a)})},pe=["en","fr"],ge={en:()=>pt(()=>Promise.resolve().then(()=>we),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>pt(()=>import("./UfoMessages_fr-CYappnH6.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function ye(n){return ge[n]()}const kt={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",showWitnessMap:"Show where the witness was",hideWitnessMap:"Hide where the witness was",mapImageryUnavailable:"Aerial imagery unavailable",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments"},we=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:kt},Symbol.toStringTag,{value:"Module"})),Tt=Math.PI/180,V=111320;function C(n,t,e,s){const i=V*Math.cos(e*Tt),a=(n-e)*V;return{x:(t-s)*i,z:-a}}function R(n,t,e,s){const i=V*Math.cos(e*Tt),a=-t,r=n;return{lat:e+a/V,lng:s+r/i}}class K{constructor(t){this.points=t}static STATIONARY_M=5;static of(t){const e=[];for(const s of t.witnessTrack.allKeyframes){const{lat:i,lng:a,headingDeg:r}=s.pose;i===void 0||a===void 0||e.push({t:s.t,lat:i,lng:a,headingDeg:r})}if(e.length===0){const s=t.event.place?.[0];if(!s)return;e.push({t:0,lat:s.lat,lng:s.lng})}return new K(e)}get moved(){return this.spanM>K.STATIONARY_M}get spanM(){const t=this.box,e=(t.south+t.north)/2,s=(t.west+t.east)/2;return Math.max(Math.abs(C(e,t.east,e,t.west).x),Math.abs(C(t.north,s,t.south,s).z))}get center(){const t=this.box;return{lat:(t.south+t.north)/2,lng:(t.west+t.east)/2}}boundsAround(t,e){const{lat:s,lng:i}=this.center,a=Math.max(this.spanM*(1+e),t)/2;return{north:R(0,-a,s,i).lat,south:R(0,a,s,i).lat,east:R(a,0,s,i).lng,west:R(-a,0,s,i).lng}}get box(){const t=this.points.map(s=>s.lat),e=this.points.map(s=>s.lng);return{south:Math.min(...t),north:Math.max(...t),west:Math.min(...e),east:Math.max(...e)}}}function be(n,t){const e=n.track,s={eastM:n.eastM,northM:n.northM,altitudeM:0,headingDeg:n.headingDeg};if(!e||e.length===0)return s;if(e.length===1||t<=e[0].t)return gt(e[0],n);const i=e[e.length-1];if(t>=i.t)return gt(i,n);for(let a=0;a<e.length-1;a++){const r=e[a],o=e[a+1];if(t<r.t||t>o.t)continue;const l=o.t-r.t,h=l===0?0:(t-r.t)/l;return{eastM:r.eastM+(o.eastM-r.eastM)*h,northM:r.northM+(o.northM-r.northM)*h,altitudeM:(r.altitudeM??0)+((o.altitudeM??0)-(r.altitudeM??0))*h,headingDeg:r.headingDeg??n.headingDeg}}return s}function gt(n,t){return{eastM:n.eastM,northM:n.northM,altitudeM:n.altitudeM??0,headingDeg:n.headingDeg??t.headingDeg}}const Pt=Math.PI/180,ve=180/Math.PI,xe=256,Me=19;function Se(n,t){return 156543.03392*Math.cos(n*Pt)/2**t}function ke(n,t){return Se(n,t)*xe}function Te(n,t){let e=0;for(;e<Me&&ke(n,e+1)>=t;)e++;return e}function $(n){const t=n*Pt;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}function Pe(n,t,e){const s=2**e;return{x:(n+180)/360*s,y:$(t)*s}}function Ee(n,t,e){const s=$(n.north),i=$(n.south);return{x:(t-n.west)/(n.east-n.west),y:($(e)-s)/(i-s)}}function Ie(n,t,e){const{x:s,y:i}=Pe(n,t,e);return{x:Math.floor(s),y:Math.floor(i),z:e}}function Ae(n){const t=2**n.z,e=n.x/t*360-180,s=(n.x+1)/t*360-180,i=yt(n.y,t);return{south:yt(n.y+1,t),north:i,west:e,east:s}}function yt(n,t){const e=n/t;return Math.atan(Math.sinh(Math.PI*(1-2*e)))*ve}function De(n,t,e,s){const i=Ie(n,t,e),a=Math.floor(s/2),r=[];for(let h=-a;h<=a;h++)for(let d=-a;d<=a;d++)r.push({x:i.x+d,y:i.y+h,z:e});const o=r.map(Ae),l={south:Math.min(...o.map(h=>h.south)),north:Math.max(...o.map(h=>h.north)),west:Math.min(...o.map(h=>h.west)),east:Math.max(...o.map(h=>h.east))};return{tiles:r,bounds:l}}class A{constructor(t){this.ctx=t}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;get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(t){const{ctx:e}=this;e.save(),e.clearRect(0,0,this.width,this.height),this.paintGround(t),this.paintNight(t.nightFraction),this.paintPath(t),t.position?(this.paintCone(t,t.position),this.paintDecor(t),this.paintMarkers(t),this.paintWitness(t,t.position)):(this.paintDecor(t),this.paintMarkers(t)),this.paintNorth(),this.paintFooter(t.bounds,t.attribution),e.restore()}toCanvas(t,e,s){const i=Ee(t,s,e);return{x:i.x*this.width,y:i.y*this.height}}paintGround(t){const{ctx:e}=this;e.fillStyle="#1d2321",e.fillRect(0,0,this.width,this.height);const s=t.imagery;if(!s)return;const i=this.toCanvas(t.bounds,s.bounds.north,s.bounds.west),a=this.toCanvas(t.bounds,s.bounds.south,s.bounds.east);e.drawImage(s.source,i.x,i.y,a.x-i.x,a.y-i.y)}paintNight(t=0){if(t<=0)return;const{ctx:e}=this;e.save(),e.fillStyle=`rgba(6, 12, 30, ${Math.min(t,1)*.78})`,e.fillRect(0,0,this.width,this.height),e.restore()}paintDecor(t){const{ctx:e}=this;for(const s of t.decor){const i=this.toCanvas(t.bounds,s.lat,s.lng);if(e.beginPath(),e.rect(i.x-3.5,i.y-3.5,7,7),e.fillStyle="rgba(120, 220, 255, 0.85)",e.fill(),e.lineWidth=1.5,e.strokeStyle="rgba(0, 0, 0, 0.7)",e.stroke(),s.headingDeg===void 0)continue;const a=(s.headingDeg-90)*Math.PI/180;e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+Math.cos(a)*10,i.y+Math.sin(a)*10),e.lineWidth=3,e.strokeStyle="rgba(0, 0, 0, 0.6)",e.stroke(),e.lineWidth=1.5,e.strokeStyle="rgba(120, 220, 255, 0.9)",e.stroke()}}paintPath(t){if(!t.path.moved)return;const{ctx:e}=this,s=t.path.points.map(i=>this.toCanvas(t.bounds,i.lat,i.lng));e.beginPath(),e.moveTo(s[0].x,s[0].y);for(const i of s.slice(1))e.lineTo(i.x,i.y);e.lineJoin="round",e.lineCap="round",e.strokeStyle="rgba(0, 0, 0, 0.65)",e.lineWidth=5,e.stroke(),e.strokeStyle="rgba(255, 255, 255, 0.9)",e.lineWidth=2,e.stroke()}paintCone(t,e){const{headingDeg:s,coneHalfAngleDeg:i}=t;if(s===void 0||i===void 0)return;const{ctx:a}=this,r=this.toCanvas(t.bounds,e.lat,e.lng),o=Math.hypot(this.width,this.height),l=(s-90)*Math.PI/180,h=i*Math.PI/180;a.save(),a.beginPath(),a.moveTo(r.x,r.y),a.arc(r.x,r.y,o,l-h,l+h),a.closePath();const d=a.createRadialGradient(r.x,r.y,0,r.x,r.y,o);d.addColorStop(0,"rgba(255, 224, 130, 0.55)"),d.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),d.addColorStop(1,"rgba(255, 224, 130, 0)"),a.fillStyle=d,a.fill(),a.restore()}paintMarkers(t){const{ctx:e}=this;e.textAlign="center",e.textBaseline="middle",e.font="bold 10px sans-serif";for(const s of[...t.markers].sort((i,a)=>Number(i.current)-Number(a.current))){const i=this.toCanvas(t.bounds,s.lat,s.lng);e.beginPath(),e.arc(i.x,i.y,A.MARKER_RADIUS_PX,0,Math.PI*2),e.fillStyle=s.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",e.fill(),e.lineWidth=2,e.strokeStyle="rgba(255, 255, 255, 0.9)",e.stroke(),e.fillStyle=s.current?"#1d2321":"#fff",e.fillText(s.label,i.x,i.y)}}paintWitness(t,e){const{ctx:s}=this,i=this.toCanvas(t.bounds,e.lat,e.lng),a=t.markers.some(r=>{if(!r.current)return!1;const o=C(r.lat,r.lng,e.lat,e.lng);return Math.hypot(o.x,o.z)<=A.ON_MARKER_M});s.beginPath(),s.arc(i.x,i.y,a?A.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),a||(s.fillStyle="#ff5a3c",s.fill()),s.lineWidth=4,s.strokeStyle="rgba(0, 0, 0, 0.5)",s.stroke(),s.lineWidth=a?3:2.5,s.strokeStyle=a?"#ff5a3c":"#fff",s.stroke()}paintNorth(){const{ctx:t}=this,e=this.width-14,s=16;t.beginPath(),t.moveTo(e,s-9),t.lineTo(e-4.5,s+5),t.lineTo(e,s+2),t.lineTo(e+4.5,s+5),t.closePath(),t.fillStyle="rgba(255, 255, 255, 0.92)",t.strokeStyle="rgba(0, 0, 0, 0.6)",t.lineWidth=1,t.fill(),t.stroke(),t.font="bold 9px sans-serif",t.textAlign="center",t.textBaseline="top",t.strokeText("N",e,s+6),t.fillText("N",e,s+6)}paintFooter(t,e){const{ctx:s}=this;s.font="9px sans-serif",s.textBaseline="bottom";const i=e?this.wrap(e,this.width-20):[];let a=this.height-4;s.textAlign="right";for(const f of i.slice().reverse())this.paintOutlinedText(f,this.width-6,a),a-=11;const r=(t.south+t.north)/2,l=Math.abs(C(r,t.east,r,t.west).x)/this.width,h=A.SCALE_STEPS_M.find(f=>f/l<=this.width/3);if(h===void 0)return;const d=h/l,c=8,u=a-3;s.lineWidth=3,s.strokeStyle="rgba(0, 0, 0, 0.6)",s.beginPath(),s.moveTo(c,u),s.lineTo(c+d,u),s.stroke(),s.lineWidth=1.5,s.strokeStyle="rgba(255, 255, 255, 0.95)",s.stroke(),s.textAlign="left",this.paintOutlinedText(h>=1e3?`${h/1e3} km`:`${h} m`,c,u-3)}paintOutlinedText(t,e,s){const{ctx:i}=this;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.75)",i.strokeText(t,e,s),i.fillStyle="#fff",i.fillText(t,e,s)}wrap(t,e){const s=[];let i="";for(const a of t.split(" ")){const r=i?`${i} ${a}`:a;i&&this.ctx.measureText(r).width>e?(s.push(i),i=a):i=r}return i&&s.push(i),s}}const j=256,wt=3;async function Ce(n,t,e,s){const i=(n.south+n.north)/2,a=(n.west+n.east)/2,r=Math.abs(C(i,n.east,i,n.west).x),o=Math.abs(C(n.north,a,n.south,a).z),l=Te(i,Math.max(r,o)/2),{tiles:h,bounds:d}=De(a,i,l,wt),c=wt*j,u=document.createElement("canvas");u.width=c,u.height=c;const f=u.getContext("2d");if(!f)throw new Error("2D canvas context unavailable");const m=h[0];await Promise.all(h.map(async x=>{const O=e(x),Y=await s(O);if(!Y.ok)throw new Error(`Imagery tile fetch failed (${Y.status}): ${O}`);const Et=await createImageBitmap(await Y.blob());f.drawImage(Et,(x.x-m.x)*j,(x.y-m.y)*j)}));const p=document.createElement("canvas");p.width=t.width,p.height=t.height;const g=p.getContext("2d");if(!g)throw new Error("2D canvas context unavailable");return g.drawImage(u,0,0,c,c,0,0,t.width,t.height),{source:p,width:t.width,height:t.height,bounds:d}}class Be{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(t={}){this.fetchImpl=t.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=t.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(t,e){return Ce(t,e,s=>this.tileUrlTemplate.replace("{z}",String(s.z)).replace("{y}",String(s.y)).replace("{x}",String(s.x)),this.fetchImpl)}}function Re(){return new Be}const bt=new Set,Q="show-witness-map",tt="hide-milestones";class M extends HTMLElement{static get observedAttributes(){return["src",Q,tt]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;static WITNESS_MAP_MIN_SPAN_M=400;static WITNESS_MAP_MARGIN=.6;static WITNESS_MAP_IMAGERY_PX=768;fullscreenButton;cornerButtons;witnessMapButton;milestonesButton;witnessMapPanel;witnessMapCanvas;witnessMapRenderer;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;witnessPath;witnessMapBounds;witnessMapImagery;witnessMapImageryRequested=!1;witnessMapImageryCredit;witnessMapImageryFailed=!1;creditShownExternally=!1;milestonesShown=!0;currentSighting=Z.create();sightingAudio=new le;soundPreview;player;loopEnabled=!0;playbackBeforeClick;highlightedSourceIds=new Set;occludedSourceIds=bt;enableClickToPlay=!0;fullscreenTarget;messages=kt;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeWitnessMap()};witnessMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeWitnessMap());resizeWitnessMap(){this.witnessMapPanel.hidden||(this.sizeWitnessMapCanvas(),this.paintWitnessMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=t=>{t.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=t=>{const e=this.canvasPointFromEvent(t),s=e&&this.shapeAt(e.x,e.y),i=this.said.read(s?.shape.title);if(!i){this.tooltip.hidden=!0;return}this.tooltip.textContent=i,this.tooltip.hidden=!1;const a=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-a.left+12}px`,this.tooltip.style.top=`${t.clientY-a.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const t=document.createElement("template");t.innerHTML=`<style>${At}</style>${It}`,this.shadow.appendChild(t.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new re(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.witnessMapButton=this.shadow.getElementById("witness-map"),this.milestonesButton=this.shadow.getElementById("milestones"),this.witnessMapPanel=this.shadow.getElementById("witness-map-panel"),this.witnessMapCanvas=this.shadow.getElementById("witness-map-canvas"),this.witnessMapRenderer=new A(this.witnessMapCanvas.getContext("2d")),this.seekInput=this.shadow.getElementById("seek"),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");for(const e of[this.timeStartLabel,this.timeEndLabel])e.addEventListener("click",()=>this.toggleTimeDisplay()),e.addEventListener("keydown",s=>{s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),this.toggleTimeDisplay())});this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.loopButton.addEventListener("click",()=>this.toggleLoop()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.witnessMapButton.addEventListener("click",()=>this.toggleWitnessMap()),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.witnessMapPanel.addEventListener("click",()=>this.setWitnessMapOpen(!1)),this.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",e=>{this.enableClickToPlay&&(e.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause())}),this.canvas.addEventListener("dblclick",e=>{this.enableClickToPlay&&(e.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.observe(this.witnessMapPanel),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){this.saidTexts=void 0;const t=this.getAttribute("src");t&&this.loadFromSrc(t)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(t,e,s){t==="src"&&s&&s!==e&&this.isConnected&&this.loadFromSrc(s),t===Q&&this.applyWitnessMapDefault(),t===tt&&this.setMilestonesShown(!this.hasAttribute(tt))}async loadFromSrc(t){this.sightingData=await Dt.json(t)}get sightingData(){return ce(this.currentSighting)}set sightingData(t){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=de(t),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyWitnessMapDefault()}get sighting(){return this.currentSighting}previewSound(t){this.soundPreview=t,this.sightingAudio.resume(),this.sightingAudio.setSound(t)}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(t){this.player.seek(t)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(t){t!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){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(t){this.toolbar.classList.toggle("hidden",!t)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(t){this.currentSighting.event.durationSeconds=t,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(t){const e=new Set(t);e.size===this.highlightedSourceIds.size&&[...e].every(i=>this.highlightedSourceIds.has(i))||(this.highlightedSourceIds=e,this.refresh())}setOccludedSourceIds(t){t.size===this.occludedSourceIds.size&&[...t].every(s=>this.occludedSourceIds.has(s))||(this.occludedSourceIds=t,this.refresh())}hasVisibleShapeAt(t,e){return this.shapeAt(t,e)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateWitnessMap(),this.player.seek(this.player.time)}refreshMilestoneMarks(){const t=this.player.seekableDuration,e=t>0?X(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...e.map(s=>{const i=document.createElement("button");i.type="button",i.className="milestone-mark",i.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`;const a=this.said.read(s.label)??"",r=this.said.read(s.note),o=r?`${a} — ${r}`:a;return i.title=o,i.setAttribute("aria-label",o),i.addEventListener("click",()=>this.player.seek(s.t)),i}))}showMilestoneAt(t){const e=this.milestonesShown&&this.sighting.milestones.length>0?ut(this.sighting.milestones,t):void 0;if(this.milestoneCaption.hidden=e===void 0,!e)return;const s=document.createElement("b");s.textContent=this.said.read(e.label)??"";const i=this.said.read(e.note),a=i?` — ${i}`:"";this.milestoneCaption.replaceChildren(s,document.createTextNode(a))}exposureInstants(t){const e=this.exposureTimes(t),s=1/e.length;return e.map(i=>({shapes:this.shapesAt(i),share:s}))}exposureTimes(t=this.currentTime){const e=(this.currentSighting.exposure??0)*1e3;if(e<M.SHORTEST_VISIBLE_EXPOSURE_MS)return[t];const s=Math.min(M.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(e/M.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(t,e)/M.EXPOSURE_STEP_PX),a=Math.min(M.MAX_TRAVEL_STEPS,Math.max(s,i)),r=[];for(let o=0;o<a;o++)r.push(t+e*o/a);return r}travelPxOver(t,e){const s=this.shapesAt(t),i=this.shapesAt(t+e);let a=0;for(const[r,o]of s){const l=i.get(r);if(!l)continue;const h=l.bounds.x+l.bounds.width/2-(o.bounds.x+o.bounds.width/2),d=l.bounds.y+l.bounds.height/2-(o.bounds.y+o.bounds.height/2);a=Math.max(a,Math.hypot(h,d))}return a}shapeAt(t,e,s=this.occludedSourceIds){for(const i of this.exposureTimes()){const a=this.currentSighting.timeline.hitTest(i,t,e,s);if(a)return a}}shapesAt(t){const e=new Map;for(const s of this.currentSighting.timeline.sourceIds){const i=this.currentSighting.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}return e}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const t=w.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===t)return;this.canvas.width=t,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${t} / ${this.canvas.height}`)}canvasPointFromEvent(t){const e=this.canvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return{x:(t.clientX-e.left)/e.width*this.canvas.width,y:(t.clientY-e.top)/e.height*this.canvas.height}}onFrame(t,e){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const s=this.playbackState!=="playing"?this.highlightedSourceIds:bt;this.canvasRenderer.setStarPoints(w.starPointsOf(this.sighting.instrument)),this.canvasRenderer.setRoll((I(this.sighting,t)?.rollDeg??0)*Math.PI/180);const i=this.exposureInstants(t);for(const a of i)for(const[r,o]of a.shapes){if(this.occludedSourceIds.has(r))continue;const l=a.share,h=l===1?o:{...o,transparency:1-(1-o.transparency)*l};this.canvasRenderer.paintShape(h)}for(const[a,r]of i[0].shapes)this.occludedSourceIds.has(a)||!s.has(a)||(s.size===1?this.canvasRenderer.paintShape({...r,selected:!0}):this.canvasRenderer.paintMemberOutline(r));if(s.size>1){const a=b.groupBoundsFor([...e].filter(([r])=>s.has(r)).map(([,r])=>r.bounds));this.canvasRenderer.paintGroupHandles(a)}this.seekInput.value=String(t),this.timeStartLabel.textContent=this.formatPosition(t),this.showMilestoneAt(t),this.paintWitnessMap(t),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Zt(this.currentSighting,t))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:t}}))}createPlayer(){const t=new Yt(this.currentSighting.timeline,(e,s)=>this.onFrame(e,s));return t.loop=this.loopEnabled,t.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},t}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton())}updatePlayPauseButton(){const t=this.player.playbackState==="playing";this.playPauseButton.textContent=t?"⏸":"▶";const e=this.player.seekableDuration>0;this.playPauseButton.disabled=!e;const s=e?t?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=s,this.playPauseButton.setAttribute("aria-label",s),this.toolbar.classList.toggle("auto-hide",t),this.cornerButtons.classList.toggle("auto-hide",t)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}restorePlayback(){const t=this.playbackBeforeClick;t&&(this.currentTime=t.time,t.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(t=>{console.error("<rr0-ufo>: requestFullscreen() failed —",t)})}enterSimulatedFullscreen(){const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=t.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const e=t.style;e.setProperty("position","fixed"),e.setProperty("inset","0"),e.setProperty("margin","0"),e.setProperty("max-width","none"),e.setProperty("max-height","none"),e.setProperty("aspect-ratio","auto"),e.setProperty("z-index","2147483647"),e.setProperty("background","#000"),e.setProperty("width","100vw"),e.setProperty("width","100dvw"),e.setProperty("height","100vh"),e.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?t.setAttribute("style",this.styleBeforeSimulatedFullscreen):t.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}toggleWitnessMap(){this.setWitnessMapOpen(this.witnessMapPanel.hidden)}applyWitnessMapDefault(){this.setWitnessMapOpen(this.hasAttribute(Q)&&this.witnessPath!==void 0)}setWitnessMapOpen(t){this.witnessMapPanel.hidden=!t,this.witnessMapPanel.title=this.messages.hideWitnessMap,this.witnessMapButton.setAttribute("aria-pressed",String(t)),this.updateWitnessMapButton(),t&&(this.sizeWitnessMapCanvas(),this.loadWitnessMapImagery(),this.paintWitnessMap(this.currentTime))}updateWitnessMapButton(){const e=!this.witnessMapPanel.hidden?this.messages.hideWitnessMap:this.messages.showWitnessMap;this.witnessMapButton.title=e,this.witnessMapButton.setAttribute("aria-label",e)}updateWitnessMap(){const t=this.witnessPath!==void 0;if(this.witnessPath=K.of(this.currentSighting),this.witnessMapButton.hidden=this.witnessPath===void 0,!t&&this.witnessPath&&this.applyWitnessMapDefault(),!this.witnessPath){this.setWitnessMapOpen(!1),this.witnessMapBounds=void 0;return}const e=this.witnessPath.boundsAround(M.WITNESS_MAP_MIN_SPAN_M,M.WITNESS_MAP_MARGIN);(!this.witnessMapBounds||!this.sameGround(this.witnessMapBounds,e))&&(this.witnessMapBounds=e,this.witnessMapImagery=void 0,this.witnessMapImageryCredit=void 0,this.witnessMapImageryFailed=!1,this.witnessMapImageryRequested=!1,this.witnessMapPanel.hidden||this.loadWitnessMapImagery())}sameGround(t,e){const s=Math.abs(t.east-t.west)/20;return Math.abs(t.north-e.north)<s&&Math.abs(t.south-e.south)<s&&Math.abs(t.west-e.west)<s&&Math.abs(t.east-e.east)<s}async loadWitnessMapImagery(){if(this.witnessMapImageryRequested||!this.witnessMapBounds)return;this.witnessMapImageryRequested=!0;const t=Re(),e=this.witnessMapBounds;try{const s=await t.getImageryTexture(e,{width:M.WITNESS_MAP_IMAGERY_PX,height:M.WITNESS_MAP_IMAGERY_PX});if(this.witnessMapBounds!==e)return;this.witnessMapImagery=s,this.witnessMapImageryCredit=t.attribution}catch{this.witnessMapImageryFailed=!0}this.paintWitnessMap(this.currentTime)}get witnessMapCredit(){return this.witnessMapImageryCredit}get witnessMapFooterLine(){return this.witnessMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.witnessMapImageryCredit}sizeWitnessMapCanvas(){const t=this.witnessMapPanel.clientWidth;if(t===0)return;const e=Math.round(t*(globalThis.devicePixelRatio??1));this.witnessMapCanvas.width!==e&&(this.witnessMapCanvas.width=e,this.witnessMapCanvas.height=e)}paintWitnessMap(t){if(this.witnessMapPanel.hidden||!this.witnessPath||!this.witnessMapBounds)return;const e=I(this.currentSighting,t),s=this.currentSighting.instrument,i=[],a=this.currentSighting.milestones.length>0?ut(this.currentSighting.milestones,t):void 0;for(const r of this.milestonesShown?X(this.currentSighting.milestones):[]){const o=I(this.currentSighting,r.t);o?.lat===void 0||o.lng===void 0||i.push({label:this.said.read(r.label)??"",lat:o.lat,lng:o.lng,current:r===a})}this.witnessMapRenderer.paint({bounds:this.witnessMapBounds,imagery:this.witnessMapImagery,path:this.witnessPath,position:e?.lat!==void 0&&e.lng!==void 0?{lat:e.lat,lng:e.lng}:void 0,headingDeg:e?.headingDeg,coneHalfAngleDeg:e?D.of(s,this.canvas.height,e.fovDeg).halfWidthAngleDeg(w.aspectOf(s)):void 0,markers:i,decor:this.witnessMapDecorAt(t),nightFraction:this.witnessMapNightFraction,attribution:this.witnessMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(t){this.milestonesShown=t,this.milestoneMarks.hidden=!t,this.milestonesButton.setAttribute("aria-pressed",String(t)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintWitnessMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const t=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=t,this.milestonesButton.setAttribute("aria-label",t)}witnessMapDecorAt(t){const e=I(this.currentSighting,0);return e?.lat===void 0||e.lng===void 0?[]:this.currentSighting.decor.map(s=>{const i=be(s,t),{lat:a,lng:r}=R(i.eastM,-i.northM,e.lat,e.lng);return{lat:a,lng:r,headingDeg:i.headingDeg}})}witnessMapNightFraction;setSunAltitude(t){const e=t===void 0?void 0:Math.max(0,Math.min(1,-t/12));e!==this.witnessMapNightFraction&&(this.witnessMapNightFraction=e,this.paintWitnessMap(this.currentTime))}updateFullscreenButton(){const t=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=t?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new k(ft.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const t=ue(ft.preferencesFor(this),pe);t!=="en"&&this.applyMessages(await ye(t))}updateTimeLabelTitles(){const t=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,e=this.canSwitchTimeDisplay?` — ${t}`:"";this.timeStartLabel.title=this.messages.currentPosition+e,this.timeEndLabel.title=this.messages.duration+e;for(const s of[this.timeStartLabel,this.timeEndLabel])s.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(s.setAttribute("role","button"),s.setAttribute("tabindex","0")):(s.removeAttribute("role"),s.removeAttribute("tabindex"))}applyMessages(t){this.messages=t,this.updateTimeLabelTitles(),this.loopButton.title=t.autoReplay,this.loopButton.setAttribute("aria-label",t.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton()}updateTimeLabels(){const t=this.currentSighting.event,e=Vt(t);this.realDurationMs=e!==void 0&&e>0?e:void 0,this.realStartMs=t.time?et(t.time):void 0;const s=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&s>0?s/this.realDurationMs:1,this.player.durationOverrideMs=s>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.updateTimeLabelTitles()}formatPosition(t){if(this.realDurationMs===void 0)return U(t);const e=this.currentSighting.timeline.duration,s=e>0&&t<=e?t/e*this.realDurationMs:t;return this.canShowClockTime?xt(vt(this.realStartMs+s)):U(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?U(this.currentSighting.timeline.duration):this.canShowClockTime?xt(vt(this.realStartMs+this.realDurationMs)):U(this.realDurationMs)}}function vt(n){const t=new Date(n);return{hour:t.getUTCHours(),minute:t.getUTCMinutes(),second:t.getUTCSeconds()}}function xt(n){if(n.hour===void 0)return"0:00";const t=e=>String(e).padStart(2,"0");return n.second?`${t(n.hour)}:${t(n.minute??0)}:${t(n.second)}`:`${t(n.hour)}:${t(n.minute??0)}`}function U(n){const t=Math.round(n/1e3),e=Math.floor(t/60),s=t%60;return`${e}:${String(s).padStart(2,"0")}`}const Mt="rr0-ufo";function _e(){customElements.get(Mt)||customElements.define(Mt,M)}_e();
|
|
387
|
+
`;class A extends Error{constructor(t,e,s){super(`${t}: ${e}${s===void 0?"":` (HTTP ${s})`}`),this.kind=t,this.url=e,this.status=s,this.name="SightingFetchError"}}class Dt{static async json(t){const e=new URL(t,location.href);if(location.protocol==="https:"&&e.protocol==="http:")throw new A("mixed-content",t);let s;try{s=await fetch(t)}catch{throw await this.diagnose(t,e)}if(!s.ok)throw new A("status",t,s.status);try{return await s.json()}catch{throw new A("malformed",t)}}static async diagnose(t,e){if(e.origin===location.origin)return new A("unreachable",t);try{return await fetch(t,{mode:"no-cors"}),new A("cors",t)}catch{return new A("unreachable",t)}}}const N=[{id:"eye",name:{en:"Naked eye",fr:"Œil nu"},flare:0,projection:"equidistant"},{id:"rectilinear-lens",name:{en:"Camera, unknown device",fr:"Appareil, modèle inconnu"},projection:"rectilinear",fNumber:8,fNumberRange:{min:2,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:8},apertureBlades:6},{id:"instamatic-126",name:{en:"Instamatic, 126 film",fr:"Instamatic, film 126"},projection:"rectilinear",frame:{widthMm:28,heightMm:28,focalLengthMm:43},fNumber:11,exposureSeconds:1/90,years:{from:1963,to:1988},apertureBlades:5,detailUm:20},{id:"slr-35mm-50",name:{en:"35 mm SLR, 50 mm lens",fr:"Reflex 35 mm, objectif 50 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:50},fNumber:8,fNumberRange:{min:2,max:16},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},years:{from:1959},apertureBlades:6,detailUm:20},{id:"slr-35mm-zoom",name:{en:"35 mm SLR, 70-210 mm zoom",fr:"Reflex 35 mm, zoom 70-210 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:135,focalRangeMm:{minMm:70,maxMm:210}},fNumber:8,fNumberRange:{min:4,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},apertureBlades:8,years:{from:1975},detailUm:20},{id:"phone-landscape",name:{en:"Phone, held sideways",fr:"Téléphone, tenu couché"},projection:"rectilinear",frame:{widthMm:7.6,heightMm:5.7,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4},{id:"phone-portrait",name:{en:"Phone, held upright",fr:"Téléphone, tenu debout"},projection:"rectilinear",frame:{widthMm:5.7,heightMm:7.6,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4}];class w{static get default(){return N[0]}static byId(t){return N.find(e=>e.id===t)??this.default}static UNAIDED_FIELD_DEG=60;static UNAIDED_ASPECT=16/9;static fieldOfViewDeg(t){const e=t.frame;return e?2*Math.atan(e.heightMm/(2*e.focalLengthMm))*180/Math.PI:w.UNAIDED_FIELD_DEG}static aspectOf(t){const e=t.frame;return e?e.widthMm/e.heightMm:w.UNAIDED_ASPECT}static frameWidthPx(t,e){return Math.round(e*w.aspectOf(t))}static availableAt(t){return t===void 0?N:N.filter(e=>!e.years||t>=e.years.from&&(e.years.to===void 0||t<=e.years.to))}static fieldOfViewDegAt(t,e){const s=t.frame;if(!(!s||e<=0))return 2*Math.atan(s.heightMm/(2*e))*180/Math.PI}static focalLengthMmFor(t,e){const s=t.frame;if(!(!s||e<=0||e>=180))return s.heightMm/(2*Math.tan(e*Math.PI/360))}static bladesShowing(t,e){const s=e??t.fNumber;if(s===void 0||t.apertureBlades===void 0)return 0;const i=t.fNumberRange;return!i||i.max<=i.min?1:Math.max(0,Math.min(1,(s-i.min)/(i.max-i.min)))}static LENS_FLARE_ARTIFACTS=1;static flareArtifactsOf(t){return t.flare??w.LENS_FLARE_ARTIFACTS}static starPointsOf(t){const e=t.apertureBlades;return e===void 0||e<3?0:e%2===0?e:e*2}}class x{static MOON_ANGULAR_WIDTH_DEG=.5237;static CANVAS_WIDTH_PX=640;static CANVAS_HEIGHT_PX=360;static angularWidthDeg(t){return 2*Math.atan(t.sizeM/(2*t.distanceM))*180/Math.PI}static inMoons(t){return t/x.MOON_ANGULAR_WIDTH_DEG}static lerpAngular(t,e,s){if(!(!t||!e))return{widthDeg:t.widthDeg+(e.widthDeg-t.widthDeg)*s,heightDeg:t.heightDeg+(e.heightDeg-t.heightDeg)*s}}static sizeMAt(t,e){return 2*t*Math.tan(e*Math.PI/360)}static distanceMAt(t,e){return t/(2*Math.tan(e*Math.PI/360))}}function Ct(n,t,e){if(!n||!t)return e<1?n:t;const s=((t.azimuthDeg-n.azimuthDeg)%360+540)%360-180;return{azimuthDeg:((n.azimuthDeg+s*e)%360+360)%360,altitudeDeg:n.altitudeDeg+(t.altitudeDeg-n.altitudeDeg)*e}}function Bt(n,t,e){const{bounds:s}=n;return t>=s.x&&t<=s.x+s.width&&e>=s.y&&e<=s.y+s.height}function y(n,t,e){return n+(t-n)*e}function it(n){const t=/^#([0-9a-f]{6})$/i.exec(n);if(!t)return;const e=parseInt(t[1],16);return[e>>16&255,e>>8&255,e&255]}function Rt(n){return`#${n.map(t=>Math.round(t).toString(16).padStart(2,"0")).join("")}`}function _t(n,t,e){const s=it(n),i=it(t);return!s||!i?e<1?n:t:Rt([y(s[0],i[0],e),y(s[1],i[1],e),y(s[2],i[2],e)])}function Lt(n,t,e){const s={x:y(n.bounds.x,t.bounds.x,e),y:y(n.bounds.y,t.bounds.y,e),width:y(n.bounds.width,t.bounds.width,e),height:y(n.bounds.height,t.bounds.height,e)},i=y(n.angle,t.angle,e),a=y(n.transparency,t.transparency,e),r=y(n.haloScale,t.haloScale,e),o=y(n.blur??0,t.blur??0,e),l=y(n.brightness??0,t.brightness??0,e),h=_t(n.color,t.color,e),d=x.lerpAngular(n.angular,t.angular,e),c=Ct(n.aim,t.aim,e);return n.kind==="polygon"&&t.kind==="polygon"&&n.points.length===t.points.length?{...n,bounds:s,angle:i,transparency:a,haloScale:r,blur:o,brightness:l,color:h,angular:d,aim:c,behindCloud:e<1?n.behindCloud:t.behindCloud,points:n.points.map((u,f)=>({x:y(u.x,t.points[f].x,e),y:y(u.y,t.points[f].y,e)}))}:{...e<1?n:t,bounds:s,angle:i,transparency:a,haloScale:r,blur:o,brightness:l,color:h,angular:d,aim:c}}class q{keyframes=[];order=[];groups=[];addKeyframe(t,e){const s=this.findInsertIndex(t);if(this.keyframes[s]?.t===t){const i=new Set(e.map(a=>a.sourceId));this.keyframes[s]={t,shapes:[...this.keyframes[s].shapes.filter(a=>!i.has(a.sourceId)),...e]}}else this.keyframes.splice(s,0,{t,shapes:[...e]});for(const i of e)this.order.includes(i.sourceId)||this.order.push(i.sourceId)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getKeyframeAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e];return s?.t===t?s:void 0}getShapeAt(t,e){return this.getKeyframeAt(t)?.shapes.find(s=>s.sourceId===e)?.shape}getLatestShapeAt(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(a=>a.sourceId===e);if(i)return i.shape}}getInterpolatedShapeAt(t,e){const s=this.findShapeAtOrBefore(t,e);if(s?.t===t)return s.shape;const i=this.findShapeAtOrAfter(t,e);return s?i?Lt(s.shape,i.shape,(t-s.t)/(i.t-s.t)):s.shape:i?.shape}findShapeAtOrBefore(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(a=>a.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}findShapeAtOrAfter(t,e){for(let s=this.findInsertIndex(t);s<this.keyframes.length;s++){const i=this.keyframes[s].shapes.find(a=>a.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}hitTest(t,e,s,i){const a=this.sourceIds;for(let r=a.length-1;r>=0;r--){if(i?.has(a[r]))continue;const o=this.getInterpolatedShapeAt(t,a[r]);if(o&&Bt(o,e,s))return{sourceId:a[r],shape:o}}}removeSource(t){for(let e=this.keyframes.length-1;e>=0;e--){const s=this.keyframes[e].shapes.filter(i=>i.sourceId!==t);s.length===0?this.keyframes.splice(e,1):s.length!==this.keyframes[e].shapes.length&&(this.keyframes[e]={t:this.keyframes[e].t,shapes:s})}this.order=this.order.filter(e=>e!==t),this.removeFromGroup(t)}group(t){if(!(t.length<2)){for(const e of t)this.removeFromGroup(e);this.groups.push([...t])}}ungroup(t){const e=this.groups.findIndex(s=>s.includes(t));e!==-1&&this.groups.splice(e,1)}groupMembers(t){return this.groups.find(e=>e.includes(t))}removeFromGroup(t){const e=this.groups.findIndex(i=>i.includes(t));if(e===-1)return;const s=this.groups[e].filter(i=>i!==t);s.length<2?this.groups.splice(e,1):this.groups[e]=s}bringToFront(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.push(t))}sendToBack(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.unshift(t))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){return[...this.order]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes,order:this.order,groups:this.groups}}static fromJSON(t){const e=new q;for(const s of t.keyframes)e.addKeyframe(s.t,s.shapes);if(t.order){const s=e.order.filter(i=>!t.order.includes(i));e.order=[...t.order,...s]}return t.groups&&(e.groups=t.groups.map(s=>s.filter(i=>e.order.includes(i))).filter(s=>s.length>=2)),e}}function Ft(n,t,e){return Math.max(t,Math.min(e,n))}function Ot(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function D(n,t,e){return n+(t-n)*e}function Nt(n,t,e){return{lat:n.lat===void 0||t.lat===void 0?void 0:D(n.lat,t.lat,e),lng:n.lng===void 0||t.lng===void 0?void 0:D(n.lng,t.lng,e),elevationM:D(n.elevationM,t.elevationM,e),headingDeg:n.headingDeg===void 0||t.headingDeg===void 0?void 0:Ot(n.headingDeg,t.headingDeg,e),pitchDeg:D(n.pitchDeg,t.pitchDeg,e),rollDeg:D(n.rollDeg??0,t.rollDeg??0,e),fovDeg:D(n.fovDeg,t.fovDeg,e),fNumber:n.fNumber,focusDistanceM:n.focusDistanceM}}class _{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,pose:e}:this.keyframes.splice(s,0,{t,pose:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestPoseAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].pose:void 0}getInterpolatedPoseAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.pose;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?Nt(s.pose,i.pose,Ft((t-s.t)/(i.t-s.t),0,1)):s.pose:i?.pose}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new _;for(const s of t.keyframes)e.addKeyframe(s.t,s.pose);return e}}function k(n,t,e){return n+(t-n)*e}function Wt(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function zt(n,t,e){return Math.max(t,Math.min(e,n))}function Ht(n,t,e){return{cloudCover:k(n.cloudCover,t.cloudCover,e),cloudDarkness:k(n.cloudDarkness,t.cloudDarkness,e),highCloudCover:n.highCloudCover===void 0||t.highCloudCover===void 0?void 0:k(n.highCloudCover,t.highCloudCover,e),iceCrystalAlignment:n.iceCrystalAlignment===void 0||t.iceCrystalAlignment===void 0?void 0:k(n.iceCrystalAlignment,t.iceCrystalAlignment,e),lowerCloudCover:n.lowerCloudCover===void 0||t.lowerCloudCover===void 0?void 0:k(n.lowerCloudCover,t.lowerCloudCover,e),cloudBaseM:n.cloudBaseM===void 0||t.cloudBaseM===void 0?void 0:k(n.cloudBaseM,t.cloudBaseM,e),precipitationType:e<1?n.precipitationType:t.precipitationType,precipitationIntensity:k(n.precipitationIntensity,t.precipitationIntensity,e),windDirectionDeg:Wt(n.windDirectionDeg,t.windDirectionDeg,e),windSpeed:k(n.windSpeed,t.windSpeed,e),storm:e<1?n.storm:t.storm}}class L{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,weather:e}:this.keyframes.splice(s,0,{t,weather:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestWeatherAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].weather:void 0}getInterpolatedWeatherAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.weather;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?Ht(s.weather,i.weather,zt((t-s.t)/(i.t-s.t),0,1)):s.weather:i?.weather}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new L;for(const s of t.keyframes)e.addKeyframe(s.t,s.weather);return e}}class F{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,sound:e}:this.keyframes.splice(s,0,{t,sound:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}getLatestSoundAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].sound:void 0}getInterpolatedSoundAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.sound;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];if(!s)return i?.sound;if(!i)return s.sound;const a=(t-s.t)/(i.t-s.t);return this.lerp(s.sound,i.sound,Math.max(0,Math.min(1,a)))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new F;for(const s of t.keyframes)e.addKeyframe(s.t,s.sound);return e}lerp(t,e,s){return{kind:s<1?t.kind:e.kind,volume:t.volume+(e.volume-t.volume)*s,pitchHz:t.pitchHz+(e.pitchHz-t.pitchHz)*s,src:s<1?t.src:e.src}}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}}const Gt=100,Ut={kind:"none",volume:0,pitchHz:Gt};function et(n){if(n.year!==void 0)return Date.UTC(n.year,(n.month??1)-1,n.day??1,n.hour??0,n.minute??0,n.second??0)}function nt(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const $t=["year","month","day","hour","minute"],Xt=["day","hour","minute"];function at(n,t){return t.filter(e=>n[e]!==void 0).join(",")}function Vt(n){if(n.durationSeconds!==void 0)return n.durationSeconds*1e3;if(!n.time||!n.endTime)return;const t=n.time.year!==void 0&&n.endTime.year!==void 0,e=t?$t:Xt,s=at(n.time,e);if(!(s===""||s!==at(n.endTime,e)))return t?et(n.endTime)-et(n.time):nt(n.endTime)-nt(n.time)}class Y{constructor(t,e,s,i,a,r,o,l,h=[],d,c,u,f=[]){this.event=t,this.timeline=e,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=a,this.witness=r,this.caseId=o,this.weather=l,this.decor=h,this.weatherSource=d,this.instrumentId=c,this.exposureSeconds=u,this.milestones=f}get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return w.byId(this.instrumentId)}static create(t,e,s){return new Y({eventType:"sighting",time:t,place:e},new q,new _,new L,new F,s)}}const Kt=0,qt=0;function S(n,t){const e=n.witnessTrack.getInterpolatedPoseAt(t);if(e)return e;const s=n.event.place?.[0];if(s)return{lat:s.lat,lng:s.lng,elevationM:Kt,headingDeg:void 0,pitchDeg:qt,fovDeg:w.fieldOfViewDeg(n.instrument)}}function Yt(n,t){return n.soundTrack.getInterpolatedSoundAt(t)??Ut}class Zt{constructor(t,e){this.timeline=t,this.onFrame=e}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 t=()=>{if(this.state!=="playing")return;const e=performance.now();if(this.currentT+=(e-this.lastWallTime)*this.playbackRate,this.lastWallTime=e,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t)};this.rafId=requestAnimationFrame(t)}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(t){this.currentT=Math.max(0,Math.min(t,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(t){const e=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}this.onFrame(t,e)}}const kt=["nw","n","ne","e","se","s","sw","w"],Jt=24,W=8,jt=3,Qt=8;function z(n,t,e){const s=n.x-t.x,i=n.y-t.y,a=Math.cos(e),r=Math.sin(e);return{x:t.x+s*a-i*r,y:t.y+s*r+i*a}}function B(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const te={nw:{left:!0,top:!0},n:{top:!0},ne:{right:!0,top:!0},e:{right:!0},se:{right:!0,bottom:!0},s:{bottom:!0},sw:{left:!0,bottom:!0},w:{left:!0}};class b{static handlePointsFor(t){const{x:e,y:s,width:i,height:a}=t.bounds,r={nw:{x:e,y:s},n:{x:e+i/2,y:s},ne:{x:e+i,y:s},e:{x:e+i,y:s+a/2},se:{x:e+i,y:s+a},s:{x:e+i/2,y:s+a},sw:{x:e,y:s+a},w:{x:e,y:s+a/2},rotate:{x:e+i/2,y:s-Jt}},o=B(t.bounds),l={};for(const h of Object.keys(r))l[h]=z(r[h],o,t.angle);return l}static hitTestHandle(t,e,s=8,i=[...kt,"rotate"]){const a=b.handlePointsFor(t);for(const r of i)if(Math.hypot(e.x-a[r].x,e.y-a[r].y)<=s)return r}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(t,e){const i=((b.HANDLE_DIRECTION_DEG[t]+e*180/Math.PI)%180+180)%180/45,a=["ew","nwse","ns","nesw"];return a[Math.round(i)%a.length]}static resizeBounds(t,e,s,i){const a=B(t),r=z(i,a,-e),{x:o,y:l,width:h,height:d}=t,c=te[s];let u=o,f=l,m=o+h,p=l+d;return c.left&&(u=Math.min(r.x,m-W)),c.right&&(m=Math.max(r.x,u+W)),c.top&&(f=Math.min(r.y,p-W)),c.bottom&&(p=Math.max(r.y,f+W)),{x:u,y:f,width:m-u,height:p-f}}static resizeShape(t,e,s){if(e==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:i,height:a}=t.bounds,r=b.resizeBounds(t.bounds,t.angle,e,s);if(t.kind==="oval")return{...t,bounds:r};const o=i===0?1:r.width/i,l=a===0?1:r.height/a;return{...t,bounds:r,points:t.points.map(h=>({x:h.x*o,y:h.y*l}))}}static rotateShape(t,e){const s=B(t.bounds),i=Math.atan2(e.y-s.y,e.x-s.x)+Math.PI/2;return{...t,angle:i}}static groupBoundsFor(t){const e=Math.min(...t.map(r=>r.x)),s=Math.min(...t.map(r=>r.y)),i=Math.max(...t.map(r=>r.x+r.width)),a=Math.max(...t.map(r=>r.y+r.height));return{x:e,y:s,width:i-e,height:a-s}}static vertexPointsFor(t){const e=B(t.bounds);return t.points.map(s=>z({x:t.bounds.x+s.x,y:t.bounds.y+s.y},e,t.angle))}static hitTestVertex(t,e,s=Qt){const i=b.vertexPointsFor(t);let a,r=s;return i.forEach((o,l)=>{const h=Math.hypot(e.x-o.x,e.y-o.y);h<=r&&(r=h,a=l)}),a}static toLocalPoint(t,e){const s=B(t.bounds),i=z(e,s,-t.angle);return{x:i.x-t.bounds.x,y:i.y-t.bounds.y}}static moveVertex(t,e,s){const i=t.points.map((c,u)=>u===e?b.toLocalPoint(t,s):c),a=Math.min(...i.map(c=>c.x)),r=Math.min(...i.map(c=>c.y)),o=Math.max(...i.map(c=>c.x)),l=Math.max(...i.map(c=>c.y)),h={x:t.bounds.x+a,y:t.bounds.y+r,width:o-a,height:l-r},d=i.map(c=>({x:c.x-a,y:c.y-r}));return{...t,bounds:h,points:d}}static insertVertexNear(t,e){const s=b.toLocalPoint(t,e),{points:i}=t;let a=i.length-1,r=1/0;for(let l=0;l<i.length;l++){const h=i[l],d=i[(l+1)%i.length],c=b.distanceToSegment(s,h,d);c<r&&(r=c,a=l)}const o=[...i.slice(0,a+1),s,...i.slice(a+1)];return{...t,points:o}}static distanceToSegment(t,e,s){const i=s.x-e.x,a=s.y-e.y,r=i*i+a*a,o=r===0?0:Math.max(0,Math.min(1,((t.x-e.x)*i+(t.y-e.y)*a)/r)),l=e.x+o*i,h=e.y+o*a;return Math.hypot(t.x-l,t.y-h)}static deleteVertex(t,e){return t.points.length<=jt?t:{...t,points:t.points.filter((s,i)=>i!==e)}}}const st=6,H=st/2,ee=20,se=24,ie=9,rt=6,ne=7,ae=6,ot=4;class re{constructor(t){this.ctx=t}starPoints=0;roll=0;clear(t,e){this.ctx.clearRect(0,0,t,e)}paintShape(t){this.ctx.save(),this.ctx.globalAlpha=1-t.transparency;const e=t.blur??0;e>0&&(this.ctx.filter=`blur(${(se*e).toFixed(2)}px)`);const s=t.brightness??0;s>0&&this.paintDazzle(t,s),t.haloScale>0&&this.paintHalo(t),this.paintBase(t),this.ctx.restore(),t.selected&&this.paintSelectionHandles(t)}setStarPoints(t){this.starPoints=Math.max(0,Math.floor(t))}setRoll(t){this.roll=t}paintDazzle(t,e){const{x:s,y:i,width:a,height:r}=t.bounds,o=s+a/2,l=i+r/2,h=Math.max(a,r)/2,d=Math.max(h*(1+ie*e),ae*e);if(d<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const c=this.ctx.createRadialGradient(o,l,0,o,l,d);for(let u=0;u<=rt;u++){const f=u/rt,m=1/(1+24*f*f);this.ctx.globalAlpha=1,c.addColorStop(f,this.withAlpha(t.color,m*e*.55))}if(this.ctx.fillStyle=c,this.ctx.beginPath(),this.ctx.arc(o,l,d,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const u=h*(1+ne*e);this.ctx.lineWidth=Math.max(1,h*.12),this.ctx.lineCap="round";for(let f=0;f<this.starPoints;f++){const m=f/this.starPoints*Math.PI*2+this.roll,p=o+Math.cos(m)*u,g=l+Math.sin(m)*u,M=this.ctx.createLinearGradient(o,l,p,g);M.addColorStop(0,this.withAlpha(t.color,e*.55)),M.addColorStop(.35,this.withAlpha(t.color,e*.22)),M.addColorStop(1,this.withAlpha(t.color,0)),this.ctx.strokeStyle=M,this.ctx.beginPath(),this.ctx.moveTo(o,l),this.ctx.lineTo(p,g),this.ctx.stroke()}}this.ctx.restore()}dazzledFill(t,e){if(e<=0)return t.color;const s=/^#([0-9a-f]{6})$/i.exec(t.color.trim());if(!s)return t.color;const i=Number.parseInt(s[1],16),a=r=>Math.round(r+(255-r)*e);return`rgb(${a(i>>16&255)}, ${a(i>>8&255)}, ${a(i&255)})`}withAlpha(t,e){const s=Math.max(0,Math.min(1,e)),i=/^#([0-9a-f]{6})$/i.exec(t.trim());if(!i)return t;const a=Number.parseInt(i[1],16);return`rgba(${a>>16&255}, ${a>>8&255}, ${a&255}, ${s.toFixed(3)})`}paintBase(t){if(this.ctx.fillStyle=this.dazzledFill(t,t.brightness??0),this.ctx.beginPath(),t.kind==="oval"){const{x:e,y:s,width:i,height:a}=t.bounds,r=i/2,o=a/2;this.ctx.ellipse(e+r,s+o,r,o,t.angle,0,2*Math.PI)}else{const{x:e,y:s,width:i,height:a}=t.bounds;this.ctx.save(),this.ctx.translate(e+i/2,s+a/2),this.ctx.rotate(t.angle),this.ctx.translate(-i/2,-a/2),t.points.forEach((r,o)=>{o===0?this.ctx.moveTo(r.x,r.y):this.ctx.lineTo(r.x,r.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(t){this.ctx.save(),this.ctx.shadowColor=t.color,this.ctx.shadowBlur=ee*t.haloScale,this.paintBase(t),this.ctx.restore()}paintSelectionHandles(t){this.paintHandleFrame(b.handlePointsFor(t),{includeRotate:!0}),t.kind==="polygon"&&this.paintVertexHandles(t)}paintVertexHandles(t){this.ctx.fillStyle="#39f";for(const e of b.vertexPointsFor(t))this.ctx.beginPath(),this.ctx.ellipse(e.x,e.y,ot,ot,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(t){this.paintOutline(b.handlePointsFor(t))}paintGroupHandles(t){this.paintHandleFrame(b.handlePointsFor({bounds:t,angle:0}),{includeRotate:!0})}paintOutline(t){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(t.nw.x,t.nw.y),this.ctx.lineTo(t.ne.x,t.ne.y),this.ctx.lineTo(t.se.x,t.se.y),this.ctx.lineTo(t.sw.x,t.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(t,{includeRotate:e}){this.paintOutline(t),this.ctx.fillStyle="lightgray";for(const s of kt){const i=t[s];this.ctx.fillRect(i.x-H,i.y-H,st,st)}e&&(this.ctx.beginPath(),this.ctx.moveTo(t.n.x,t.n.y),this.ctx.lineTo(t.rotate.x,t.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(t.rotate.x,t.rotate.y,H+1,H+1,0,0,2*Math.PI),this.ctx.fill())}}const ht=.05,lt=.08,ct=2,dt=3,oe=14,he=.02;class le{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(t){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",t);return}}this.context.state==="suspended"&&this.context.resume()}setSound(t){if(this.requested=t,!this.context)return;const e=this.keyFor(t);if(!e){this.silence();return}if(this.voice?.key!==e){this.stopVoice();const s=++this.voiceToken;e.startsWith("src:")?this.buildRecordedVoice(t.src,e,s):this.voice=this.buildSynthesizedVoice(t,e)}this.applyTo(this.voice,t)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(t){if(!(t.volume<=0))return t.src?`src:${t.src}`:t.kind==="none"?void 0:t.kind}applyTo(t,e){const s=this.context;if(!t||!s)return;const i=s.currentTime;t.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,e.volume))*t.gainScale,i,ht);for(const{param:a,ratio:r}of t.pitch)a.setTargetAtTime(Math.min(e.pitchHz*r,s.sampleRate/2-1),i,ht)}buildSynthesizedVoice(t,e){const s=this.context;if(s)switch(t.kind){case"hum":return this.buildHum(s,e);case"whistle":return this.buildWhistle(s,e);case"rumble":return this.buildRumble(s,e);case"crackle":return this.buildCrackle(s,e);default:return}}buildHum(t,e){const s=t.createOscillator();s.type="sawtooth";const i=t.createOscillator();i.type="sawtooth";const a=t.createBiquadFilter();a.type="lowpass",a.Q.value=1;const r=t.createGain();return r.gain.value=0,s.connect(a),i.connect(a),a.connect(r).connect(t.destination),s.start(),i.start(),{key:e,nodes:[a,r],sources:[s,i],volumeParam:r.gain,gainScale:.16,pitch:[{param:s.frequency,ratio:1},{param:i.frequency,ratio:1.006},{param:a.frequency,ratio:6}]}}buildWhistle(t,e){const s=t.createOscillator();s.type="sine";const i=t.createOscillator();i.type="sine",i.frequency.value=5;const a=t.createGain(),r=t.createGain();return r.gain.value=0,i.connect(a).connect(s.frequency),s.connect(r).connect(t.destination),s.start(),i.start(),{key:e,nodes:[a,r],sources:[s,i],volumeParam:r.gain,gainScale:.22,pitch:[{param:s.frequency,ratio:1},{param:a.gain,ratio:.012}]}}buildRumble(t,e){const s=t.createBufferSource();s.buffer=this.brownNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="lowpass",i.Q.value=.8;const a=t.createGain();return a.gain.value=0,s.connect(i).connect(a).connect(t.destination),s.start(),{key:e,nodes:[i,a],sources:[s],volumeParam:a.gain,gainScale:1.4,pitch:[{param:i.frequency,ratio:1}]}}buildCrackle(t,e){const s=t.createBufferSource();s.buffer=this.whiteNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="bandpass",i.Q.value=1.2;const a=t.createGain();a.gain.value=0;const r=t.createBufferSource();r.buffer=this.crackleEnvelope(t),r.loop=!0;const o=t.createGain();return o.gain.value=0,r.connect(o).connect(a.gain),s.connect(i).connect(a).connect(t.destination),s.start(),r.start(),{key:e,nodes:[i,a,o],sources:[s,r],volumeParam:o.gain,gainScale:.9,pitch:[{param:i.frequency,ratio:4}]}}async buildRecordedVoice(t,e,s){const i=this.context;if(!i)return;let a;try{a=await this.getBuffer(t)}catch(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);return}if(s!==this.voiceToken||!this.context)return;const r=i.createBufferSource();r.buffer=a,r.loop=!0;const o=i.createGain();o.gain.value=0,r.connect(o).connect(i.destination),r.start(),this.voice={key:e,nodes:[o],sources:[r],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(t){const e=this.context;if(!e)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let s=this.buffers.get(t);return s||(s=fetch(t).then(i=>{if(!i.ok)throw new Error(`Audio fetch failed (${i.status}): ${t}`);return i.arrayBuffer()}).then(i=>e.decodeAudioData(i)),this.buffers.set(t,s)),s}whiteNoise(t){if(!this.noiseBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*ct),t.sampleRate),s=e.getChannelData(0);for(let i=0;i<s.length;i++)s[i]=Math.random()*2-1;this.noiseBuffer=e}return this.noiseBuffer}brownNoise(t){const e=t.createBuffer(1,Math.floor(t.sampleRate*ct),t.sampleRate),s=e.getChannelData(0);let i=0,a=0;for(let r=0;r<s.length;r++)i=(i+.02*(Math.random()*2-1))/1.02,s[r]=i,a=Math.max(a,Math.abs(i));if(a>0)for(let r=0;r<s.length;r++)s[r]/=a;return e}crackleEnvelope(t){if(!this.crackleBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*dt),t.sampleRate),s=e.getChannelData(0),i=Math.max(1,Math.floor(he*t.sampleRate)),a=Math.round(dt*oe);for(let r=0;r<a;r++){const o=Math.floor(Math.random()*s.length),l=.4+Math.random()*.6;for(let h=0;h<i&&o+h<s.length;h++)s[o+h]=Math.max(s[o+h],l*(1-h/i))}this.crackleBuffer=e}return this.crackleBuffer}stopVoice(){const t=this.voice,e=this.context;if(this.voice=void 0,!t||!e)return;const s=e.currentTime;t.volumeParam.cancelScheduledValues(s),t.volumeParam.setTargetAtTime(0,s,lt/3);const i=s+lt;for(const a of t.sources){a.onended=()=>{a.disconnect();for(const r of t.nodes)r.disconnect()};try{a.stop(i)}catch{a.disconnect();for(const r of t.nodes)r.disconnect()}}}}function X(n){return[...n].sort((t,e)=>t.t-e.t)}function ut(n,t){let e;for(const s of X(n)){if(s.t>t)break;e=s}return e}const J=Math.PI/180,G=180/Math.PI;class E{constructor(t,e,s){this.kind=t,this.canvasHeightPx=e,this.fovDeg=s;const i=s*J/2;this.focalPx=t==="equidistant"?e/2/i:e/2/Math.tan(i)}focalPx;static of(t,e,s){return new E(t.projection,e,s)}degToPx(t){const e=t*J;return this.kind==="equidistant"?this.focalPx*e:2*this.focalPx*Math.tan(e/2)}pxToDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:2*Math.atan(t/(2*this.focalPx))*G}angleDegToRadiusPx(t){const e=t*J;return this.kind==="equidistant"?this.focalPx*e:this.focalPx*Math.tan(e)}halfWidthAngleDeg(t){return this.radiusPxToAngleDeg(this.canvasHeightPx*t/2)}radiusPxToAngleDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:Math.atan(t/this.focalPx)*G}ofBounds(t){return{widthDeg:this.pxToDeg(t.width),heightDeg:this.pxToDeg(t.height)}}toBoundsSize(t){return{width:this.degToPx(t.widthDeg),height:this.degToPx(t.heightDeg)}}widthPx(t){return this.degToPx(x.angularWidthDeg(t))}}class I{static fovOf(t,e){return S(t,e)?.fovDeg??w.fieldOfViewDeg(t.instrument)}static toAngular(t){this.eachKeyframe(t,(e,s)=>({...e,angular:s.ofBounds(e.bounds)}))}static toBounds(t){this.eachKeyframe(t,(e,s)=>{if(!e.angular)return e;const{width:i,height:a}=s.toBoundsSize(e.angular),r={x:e.bounds.x+(e.bounds.width-i)/2,y:e.bounds.y+(e.bounds.height-a)/2,width:i,height:a};if(e.kind!=="polygon")return{...e,bounds:r};const o=e.bounds.width===0?1:i/e.bounds.width,l=e.bounds.height===0?1:a/e.bounds.height;return{...e,bounds:r,points:e.points.map(h=>({x:h.x*o,y:h.y*l}))}})}static toAim(t){this.eachKeyframe(t,(e,s,i)=>{if(i?.headingDeg===void 0)return e;const a=this.frameCentre(t),r=s.radiusPxToAngleDeg(e.bounds.x+e.bounds.width/2-a.x),o=s.radiusPxToAngleDeg(a.y-(e.bounds.y+e.bounds.height/2));return{...e,aim:{azimuthDeg:((i.headingDeg+r)%360+360)%360,altitudeDeg:i.pitchDeg+o}}})}static toPosition(t){this.eachKeyframe(t,(e,s,i)=>{if(!e.aim||i?.headingDeg===void 0)return e;const a=this.frameCentre(t),r=this.shortestArc(e.aim.azimuthDeg-i.headingDeg),o=e.aim.altitudeDeg-i.pitchDeg,l=a.x+this.offAxisPx(s,r)-e.bounds.width/2,h=a.y-this.offAxisPx(s,o)-e.bounds.height/2;return{...e,bounds:{...e.bounds,x:l,y:h}}})}static frameCentre(t){return{x:w.frameWidthPx(t.instrument,x.CANVAS_HEIGHT_PX)/2,y:x.CANVAS_HEIGHT_PX/2}}static shortestArc(t){return(t%360+540)%360-180}static offAxisPx(t,e){return Math.abs(e)>=I.OFF_CANVAS_DEG?Math.sign(e)*I.OFF_CANVAS_PX:t.angleDegToRadiusPx(e)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(t,e,s){const i=w.frameWidthPx(e,x.CANVAS_HEIGHT_PX)/2,a=w.frameWidthPx(t.instrument,x.CANVAS_HEIGHT_PX)/2,r=x.CANVAS_HEIGHT_PX/2;for(const o of[...t.timeline.allKeyframes]){const l=this.fovOf(t,o.t),h=s?.get(o.t)??l,d=E.of(e,x.CANVAS_HEIGHT_PX,h),c=E.of(t.instrument,x.CANVAS_HEIGHT_PX,l);t.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:f}=u.shape,m=f.x+f.width/2-i,p=f.y+f.height/2-r,g=Math.hypot(m,p),M=g===0?1:c.angleDegToRadiusPx(d.radiusPxToAngleDeg(g))/g,O={...f,x:a+m*M-f.width/2,y:r+p*M-f.height/2};return{...u,shape:{...u.shape,bounds:O}}}))}this.toBounds(t)}static eachKeyframe(t,e){const{timeline:s}=t;for(const i of[...s.allKeyframes]){const a=S(t,i.t),r=this.fovOf(t,i.t),o=E.of(t.instrument,x.CANVAS_HEIGHT_PX,r);s.addKeyframe(i.t,i.shapes.map(l=>({...l,shape:e(l.shape,o,a)})))}}}function ce(n){return I.toAngular(n),I.toAim(n),{version:1,time:n.event.time,endTime:n.event.endTime,durationSeconds:n.event.durationSeconds,utcOffsetHours:n.event.utcOffsetHours,timeZone:n.event.timeZone,place:n.event.place,witness:n.witness,caseId:n.caseId,description:n.event.description,tags:n.event.tags,timeline:n.timeline.toJSON(),witnessTrack:n.witnessTrack.toJSON(),weatherTrack:n.weatherTrack.toJSON(),soundTrack:n.soundTrack.toJSON(),weather:n.weather,decor:n.decor,milestones:n.milestones.length>0?n.milestones:void 0,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function de(n){const t=new Y({eventType:"sighting",time:n.time,endTime:n.endTime,durationSeconds:n.durationSeconds,utcOffsetHours:n.utcOffsetHours,timeZone:n.timeZone,place:n.place,description:n.description,tags:n.tags},q.fromJSON(n.timeline),n.witnessTrack?_.fromJSON(n.witnessTrack):new _,n.weatherTrack?L.fromJSON(n.weatherTrack):new L,n.soundTrack?F.fromJSON(n.soundTrack):new F,n.witness,n.caseId,n.weather,n.decor??[],n.weatherSource,n.instrument,n.exposureSeconds??n.witnessTrack?.keyframes.map(e=>e.pose.exposureSeconds).find(e=>e!==void 0),X(n.milestones??[]));return I.toBounds(t),I.toPosition(t),t}function ue(n,t){for(const e of n){const s=e.toLowerCase().split("-")[0];if(t.includes(s))return s}return"en"}class ft{static preferencesFor(t){const e=navigator.languages??[],s=this.declaredFor(t);return s?[s,...e]:e}static declaredFor(t){for(let e=t;e;){const s=e.closest("[lang]")?.getAttribute("lang")?.trim();if(s)return s;const i=e.getRootNode();e=i instanceof ShadowRoot?i.host:void 0}}}class T{constructor(t){this.preferences=t}read(t){if(t===void 0||typeof t=="string")return t===""?void 0:t;for(const s of this.preferences){const i=t[s]??t[T.base(s)];if(i)return i}const e=new Set(this.preferences.map(s=>T.base(s)));for(const[s,i]of Object.entries(t))if(i&&e.has(T.base(s)))return i;return Object.values(t).find(s=>s)??void 0}write(t,e,s){const i=e?.trim()?e.trim():void 0;if(t===void 0||typeof t=="string")return i;const a={...t},r=Object.keys(a).find(o=>o===s)??Object.keys(a).find(o=>T.base(o)===T.base(s))??s;return i===void 0?delete a[r]:a[r]=i,Object.keys(a).length===0?void 0:a}static base(t){return t.toLowerCase().split("-")[0]}}const fe="modulepreload",me=function(n,t){return new URL(n,t).href},mt={},pt=function(t,e,s){let i=Promise.resolve();if(e&&e.length>0){let r=function(d){return Promise.all(d.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),h=l?.nonce||l?.getAttribute("nonce");i=r(e.map(d=>{if(d=me(d,s),d in mt)return;mt[d]=!0;const c=d.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!s)for(let p=o.length-1;p>=0;p--){const g=o[p];if(g.href===d&&(!c||g.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${d}"]${u}`))return;const m=document.createElement("link");if(m.rel=c?"stylesheet":fe,c||(m.as="script"),m.crossOrigin="",m.href=d,h&&m.setAttribute("nonce",h),document.head.appendChild(m),c)return new Promise((p,g)=>{m.addEventListener("load",p),m.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${d}`)))})}))}function a(r){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=r,window.dispatchEvent(o),!o.defaultPrevented)throw r}return i.then(r=>{for(const o of r||[])o.status==="rejected"&&a(o.reason);return t().catch(a)})},pe=["en","fr"],ge={en:()=>pt(()=>Promise.resolve().then(()=>we),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>pt(()=>import("./UfoMessages_fr-BJPYHN17.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function ye(n){return ge[n]()}const St={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",showWitnessMap:"Show where the witness was",hideWitnessMap:"Hide where the witness was",mapImageryUnavailable:"Aerial imagery unavailable",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",witnessHere:"The witness, here",decorHere:"Scenery"},we=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:St},Symbol.toStringTag,{value:"Module"})),Tt=Math.PI/180,V=111320;function C(n,t,e,s){const i=V*Math.cos(e*Tt),a=(n-e)*V;return{x:(t-s)*i,z:-a}}function R(n,t,e,s){const i=V*Math.cos(e*Tt),a=-t,r=n;return{lat:e+a/V,lng:s+r/i}}class K{constructor(t){this.points=t}static STATIONARY_M=5;static of(t){const e=[];for(const s of t.witnessTrack.allKeyframes){const{lat:i,lng:a,headingDeg:r}=s.pose;i===void 0||a===void 0||e.push({t:s.t,lat:i,lng:a,headingDeg:r})}if(e.length===0){const s=t.event.place?.[0];if(!s)return;e.push({t:0,lat:s.lat,lng:s.lng})}return new K(e)}get moved(){return this.spanM>K.STATIONARY_M}get spanM(){const t=this.box,e=(t.south+t.north)/2,s=(t.west+t.east)/2;return Math.max(Math.abs(C(e,t.east,e,t.west).x),Math.abs(C(t.north,s,t.south,s).z))}get center(){const t=this.box;return{lat:(t.south+t.north)/2,lng:(t.west+t.east)/2}}boundsAround(t,e){const{lat:s,lng:i}=this.center,a=Math.max(this.spanM*(1+e),t)/2;return{north:R(0,-a,s,i).lat,south:R(0,a,s,i).lat,east:R(a,0,s,i).lng,west:R(-a,0,s,i).lng}}get box(){const t=this.points.map(s=>s.lat),e=this.points.map(s=>s.lng);return{south:Math.min(...t),north:Math.max(...t),west:Math.min(...e),east:Math.max(...e)}}}function be(n,t){const e=n.track,s={eastM:n.eastM,northM:n.northM,altitudeM:0,headingDeg:n.headingDeg};if(!e||e.length===0)return s;if(e.length===1||t<=e[0].t)return gt(e[0],n);const i=e[e.length-1];if(t>=i.t)return gt(i,n);for(let a=0;a<e.length-1;a++){const r=e[a],o=e[a+1];if(t<r.t||t>o.t)continue;const l=o.t-r.t,h=l===0?0:(t-r.t)/l;return{eastM:r.eastM+(o.eastM-r.eastM)*h,northM:r.northM+(o.northM-r.northM)*h,altitudeM:(r.altitudeM??0)+((o.altitudeM??0)-(r.altitudeM??0))*h,headingDeg:r.headingDeg??n.headingDeg}}return s}function gt(n,t){return{eastM:n.eastM,northM:n.northM,altitudeM:n.altitudeM??0,headingDeg:n.headingDeg??t.headingDeg}}const Pt=Math.PI/180,ve=180/Math.PI,xe=256,Me=19;function ke(n,t){return 156543.03392*Math.cos(n*Pt)/2**t}function Se(n,t){return ke(n,t)*xe}function Te(n,t){let e=0;for(;e<Me&&Se(n,e+1)>=t;)e++;return e}function $(n){const t=n*Pt;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}function Pe(n,t,e){const s=2**e;return{x:(n+180)/360*s,y:$(t)*s}}function Ee(n,t,e){const s=$(n.north),i=$(n.south);return{x:(t-n.west)/(n.east-n.west),y:($(e)-s)/(i-s)}}function Ie(n,t,e){const{x:s,y:i}=Pe(n,t,e);return{x:Math.floor(s),y:Math.floor(i),z:e}}function Ae(n){const t=2**n.z,e=n.x/t*360-180,s=(n.x+1)/t*360-180,i=yt(n.y,t);return{south:yt(n.y+1,t),north:i,west:e,east:s}}function yt(n,t){const e=n/t;return Math.atan(Math.sinh(Math.PI*(1-2*e)))*ve}function De(n,t,e,s){const i=Ie(n,t,e),a=Math.floor(s/2),r=[];for(let h=-a;h<=a;h++)for(let d=-a;d<=a;d++)r.push({x:i.x+d,y:i.y+h,z:e});const o=r.map(Ae),l={south:Math.min(...o.map(h=>h.south)),north:Math.max(...o.map(h=>h.north)),west:Math.min(...o.map(h=>h.west)),east:Math.max(...o.map(h=>h.east))};return{tiles:r,bounds:l}}class P{constructor(t){this.ctx=t}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(t,e){let s,i=1/0;for(const a of this.targets){const r=Math.hypot(a.x-t,a.y-e);r>a.radius||r>i||(s=a,i=r)}return s}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(t){const{ctx:e}=this;e.save(),this.targets=[],e.clearRect(0,0,this.width,this.height),this.paintGround(t),this.paintNight(t.nightFraction),this.paintPath(t),t.position?(this.paintCone(t,t.position),this.paintDecor(t),this.paintMarkers(t),this.paintWitness(t,t.position)):(this.paintDecor(t),this.paintMarkers(t)),this.paintNorth(),this.paintFooter(t.bounds,t.attribution),e.restore()}toCanvas(t,e,s){const i=Ee(t,s,e);return{x:i.x*this.width,y:i.y*this.height}}paintGround(t){const{ctx:e}=this;e.fillStyle="#1d2321",e.fillRect(0,0,this.width,this.height);const s=t.imagery;if(!s)return;const i=this.toCanvas(t.bounds,s.bounds.north,s.bounds.west),a=this.toCanvas(t.bounds,s.bounds.south,s.bounds.east);e.drawImage(s.source,i.x,i.y,a.x-i.x,a.y-i.y)}paintNight(t=0){if(t<=0)return;const{ctx:e}=this;e.save(),e.fillStyle=`rgba(6, 12, 30, ${Math.min(t,1)*.78})`,e.fillRect(0,0,this.width,this.height),e.restore()}paintDecor(t){const{ctx:e}=this;for(const s of t.decor){const i=this.toCanvas(t.bounds,s.lat,s.lng);if(s.label&&this.targets.push({kind:"decor",label:s.label,lat:s.lat,lng:s.lng,x:i.x,y:i.y,radius:7}),e.beginPath(),e.rect(i.x-3.5,i.y-3.5,7,7),e.fillStyle="rgba(120, 220, 255, 0.85)",e.fill(),e.lineWidth=1.5,e.strokeStyle="rgba(0, 0, 0, 0.7)",e.stroke(),s.headingDeg===void 0)continue;const a=(s.headingDeg-90)*Math.PI/180;e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+Math.cos(a)*10,i.y+Math.sin(a)*10),e.lineWidth=3,e.strokeStyle="rgba(0, 0, 0, 0.6)",e.stroke(),e.lineWidth=1.5,e.strokeStyle="rgba(120, 220, 255, 0.9)",e.stroke()}}paintPath(t){if(!t.path.moved)return;const{ctx:e}=this,s=t.path.points.map(i=>this.toCanvas(t.bounds,i.lat,i.lng));e.beginPath(),e.moveTo(s[0].x,s[0].y);for(const i of s.slice(1))e.lineTo(i.x,i.y);e.lineJoin="round",e.lineCap="round",e.strokeStyle="rgba(0, 0, 0, 0.65)",e.lineWidth=5,e.stroke(),e.strokeStyle="rgba(255, 255, 255, 0.9)",e.lineWidth=2,e.stroke()}paintCone(t,e){const{headingDeg:s,coneHalfAngleDeg:i}=t;if(s===void 0||i===void 0)return;const{ctx:a}=this,r=this.toCanvas(t.bounds,e.lat,e.lng),o=Math.hypot(this.width,this.height),l=(s-90)*Math.PI/180,h=i*Math.PI/180;a.save(),a.beginPath(),a.moveTo(r.x,r.y),a.arc(r.x,r.y,o,l-h,l+h),a.closePath();const d=a.createRadialGradient(r.x,r.y,0,r.x,r.y,o);d.addColorStop(0,"rgba(255, 224, 130, 0.55)"),d.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),d.addColorStop(1,"rgba(255, 224, 130, 0)"),a.fillStyle=d,a.fill(),a.restore()}paintMarkers(t){const{ctx:e}=this;e.textAlign="center",e.textBaseline="middle",e.font="bold 10px sans-serif";for(const s of[...t.markers].sort((i,a)=>Number(i.current)-Number(a.current))){const i=this.toCanvas(t.bounds,s.lat,s.lng);this.targets.push({kind:"milestone",label:s.note?`${s.label} — ${s.note}`:s.label,lat:s.lat,lng:s.lng,t:s.t,x:i.x,y:i.y,radius:P.MARKER_RADIUS_PX}),e.beginPath(),e.arc(i.x,i.y,P.MARKER_RADIUS_PX,0,Math.PI*2),e.fillStyle=s.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",e.fill(),e.lineWidth=2,e.strokeStyle="rgba(255, 255, 255, 0.9)",e.stroke(),e.fillStyle=s.current?"#1d2321":"#fff",e.fillText(s.label,i.x,i.y)}}paintWitness(t,e){const{ctx:s}=this,i=this.toCanvas(t.bounds,e.lat,e.lng);t.witnessLabel&&this.targets.push({kind:"witness",label:t.witnessLabel,lat:e.lat,lng:e.lng,x:i.x,y:i.y,radius:8});const a=t.markers.some(r=>{if(!r.current)return!1;const o=C(r.lat,r.lng,e.lat,e.lng);return Math.hypot(o.x,o.z)<=P.ON_MARKER_M});s.beginPath(),s.arc(i.x,i.y,a?P.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),a||(s.fillStyle="#ff5a3c",s.fill()),s.lineWidth=4,s.strokeStyle="rgba(0, 0, 0, 0.5)",s.stroke(),s.lineWidth=a?3:2.5,s.strokeStyle=a?"#ff5a3c":"#fff",s.stroke()}paintNorth(){const{ctx:t}=this,e=this.width-14,s=16;t.beginPath(),t.moveTo(e,s-9),t.lineTo(e-4.5,s+5),t.lineTo(e,s+2),t.lineTo(e+4.5,s+5),t.closePath(),t.fillStyle="rgba(255, 255, 255, 0.92)",t.strokeStyle="rgba(0, 0, 0, 0.6)",t.lineWidth=1,t.fill(),t.stroke(),t.font="bold 9px sans-serif",t.textAlign="center",t.textBaseline="top",t.strokeText("N",e,s+6),t.fillText("N",e,s+6)}paintFooter(t,e){const{ctx:s}=this;s.font="9px sans-serif",s.textBaseline="bottom";const i=e?this.wrap(e,this.width-20):[];let a=this.height-4;s.textAlign="right";for(const f of i.slice().reverse())this.paintOutlinedText(f,this.width-6,a),a-=11;const r=(t.south+t.north)/2,l=Math.abs(C(r,t.east,r,t.west).x)/this.width,h=P.SCALE_STEPS_M.find(f=>f/l<=this.width/3);if(h===void 0)return;const d=h/l,c=8,u=a-3;s.lineWidth=3,s.strokeStyle="rgba(0, 0, 0, 0.6)",s.beginPath(),s.moveTo(c,u),s.lineTo(c+d,u),s.stroke(),s.lineWidth=1.5,s.strokeStyle="rgba(255, 255, 255, 0.95)",s.stroke(),s.textAlign="left",this.paintOutlinedText(h>=1e3?`${h/1e3} km`:`${h} m`,c,u-3)}paintOutlinedText(t,e,s){const{ctx:i}=this;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.75)",i.strokeText(t,e,s),i.fillStyle="#fff",i.fillText(t,e,s)}wrap(t,e){const s=[];let i="";for(const a of t.split(" ")){const r=i?`${i} ${a}`:a;i&&this.ctx.measureText(r).width>e?(s.push(i),i=a):i=r}return i&&s.push(i),s}}const j=256,wt=3;async function Ce(n,t,e,s){const i=(n.south+n.north)/2,a=(n.west+n.east)/2,r=Math.abs(C(i,n.east,i,n.west).x),o=Math.abs(C(n.north,a,n.south,a).z),l=Te(i,Math.max(r,o)/2),{tiles:h,bounds:d}=De(a,i,l,wt),c=wt*j,u=document.createElement("canvas");u.width=c,u.height=c;const f=u.getContext("2d");if(!f)throw new Error("2D canvas context unavailable");const m=h[0];await Promise.all(h.map(async M=>{const O=e(M),Z=await s(O);if(!Z.ok)throw new Error(`Imagery tile fetch failed (${Z.status}): ${O}`);const Et=await createImageBitmap(await Z.blob());f.drawImage(Et,(M.x-m.x)*j,(M.y-m.y)*j)}));const p=document.createElement("canvas");p.width=t.width,p.height=t.height;const g=p.getContext("2d");if(!g)throw new Error("2D canvas context unavailable");return g.drawImage(u,0,0,c,c,0,0,t.width,t.height),{source:p,width:t.width,height:t.height,bounds:d}}class Be{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(t={}){this.fetchImpl=t.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=t.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(t,e){return Ce(t,e,s=>this.tileUrlTemplate.replace("{z}",String(s.z)).replace("{y}",String(s.y)).replace("{x}",String(s.x)),this.fetchImpl)}}function Re(){return new Be}const bt=new Set,Q="show-witness-map",tt="hide-milestones";class v extends HTMLElement{static get observedAttributes(){return["src",Q,tt]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;static WITNESS_MAP_MIN_SPAN_M=400;static WITNESS_MAP_MARGIN=.6;static WITNESS_MAP_IMAGERY_PX=768;fullscreenButton;cornerButtons;witnessMapButton;milestonesButton;witnessMapPanel;witnessMapCanvas;witnessMapRenderer;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;witnessPath;witnessMapBounds;witnessMapImagery;witnessMapImageryRequested=!1;witnessMapImageryCredit;witnessMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=Y.create();sightingAudio=new le;soundPreview;player;loopEnabled=!0;playbackBeforeClick;highlightedSourceIds=new Set;occludedSourceIds=bt;enableClickToPlay=!0;fullscreenTarget;messages=St;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeWitnessMap()};witnessMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeWitnessMap());resizeWitnessMap(){this.witnessMapPanel.hidden||(this.sizeWitnessMapCanvas(),this.paintWitnessMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=t=>{t.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=t=>{const e=this.canvasPointFromEvent(t),s=e&&this.shapeAt(e.x,e.y),i=this.said.read(s?.shape.title);if(!i){this.tooltip.hidden=!0;return}this.tooltip.textContent=i,this.tooltip.hidden=!1;const a=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-a.left+12}px`,this.tooltip.style.top=`${t.clientY-a.top+12}px`};handleWitnessMapPointerMove=t=>{const e=this.witnessMapTargetFrom(t);if(!e){this.tooltip.hidden=!0,this.witnessMapCanvas.style.cursor="default";return}this.witnessMapCanvas.style.cursor="pointer",this.tooltip.textContent=e.label,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-s.left+12}px`,this.tooltip.style.top=`${t.clientY-s.top+12}px`};handleWitnessMapClick=t=>{const e=this.witnessMapTargetFrom(t);if(e){if(e.kind==="milestone"&&e.t!==void 0){this.player.seek(e.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:e.lat,lng:e.lng,kind:e.kind},bubbles:!0,composed:!0}))}};witnessMapTargetFrom(t){const e=this.witnessMapCanvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return this.witnessMapRenderer.hitTest((t.clientX-e.left)/e.width*this.witnessMapCanvas.width,(t.clientY-e.top)/e.height*this.witnessMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const t=document.createElement("template");t.innerHTML=`<style>${At}</style>${It}`,this.shadow.appendChild(t.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new re(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.witnessMapButton=this.shadow.getElementById("witness-map"),this.milestonesButton=this.shadow.getElementById("milestones"),this.witnessMapPanel=this.shadow.getElementById("witness-map-panel"),this.witnessMapCanvas=this.shadow.getElementById("witness-map-canvas"),this.witnessMapRenderer=new P(this.witnessMapCanvas.getContext("2d")),this.seekInput=this.shadow.getElementById("seek"),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");for(const e of[this.timeStartLabel,this.timeEndLabel])e.addEventListener("click",()=>this.toggleTimeDisplay()),e.addEventListener("keydown",s=>{s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),this.toggleTimeDisplay())});this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.loopButton.addEventListener("click",()=>this.toggleLoop()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.witnessMapButton.addEventListener("click",()=>this.toggleWitnessMap()),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.witnessMapCanvas.addEventListener("pointermove",this.handleWitnessMapPointerMove),this.witnessMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.witnessMapCanvas.addEventListener("click",this.handleWitnessMapClick),this.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",e=>{this.enableClickToPlay&&(e.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause())}),this.canvas.addEventListener("dblclick",e=>{this.enableClickToPlay&&(e.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.observe(this.witnessMapPanel),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){this.saidTexts=void 0;const t=this.getAttribute("src");t&&this.loadFromSrc(t)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(t,e,s){t==="src"&&s&&s!==e&&this.isConnected&&this.loadFromSrc(s),t===Q&&this.applyWitnessMapDefault(),t===tt&&this.setMilestonesShown(!this.hasAttribute(tt))}async loadFromSrc(t){this.sightingData=await Dt.json(t)}get sightingData(){return ce(this.currentSighting)}set sightingData(t){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=de(t),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyWitnessMapDefault()}get sighting(){return this.currentSighting}previewSound(t){this.soundPreview=t,this.sightingAudio.resume(),this.sightingAudio.setSound(t)}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(t){this.player.seek(t)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(t){t!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){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(t){this.toolbar.classList.toggle("hidden",!t)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(t){this.currentSighting.event.durationSeconds=t,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(t){const e=new Set(t);e.size===this.highlightedSourceIds.size&&[...e].every(i=>this.highlightedSourceIds.has(i))||(this.highlightedSourceIds=e,this.refresh())}setOccludedSourceIds(t){t.size===this.occludedSourceIds.size&&[...t].every(s=>this.occludedSourceIds.has(s))||(this.occludedSourceIds=t,this.refresh())}hasVisibleShapeAt(t,e){return this.shapeAt(t,e)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateWitnessMap(),this.player.seek(this.player.time)}refreshMilestoneMarks(){const t=this.player.seekableDuration,e=t>0?X(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...e.map(s=>{const i=document.createElement("button");i.type="button",i.className="milestone-mark",i.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`;const a=this.said.read(s.label)??"",r=this.said.read(s.note),o=r?`${a} — ${r}`:a;return i.title=o,i.setAttribute("aria-label",o),i.addEventListener("click",()=>this.player.seek(s.t)),i}))}showMilestoneAt(t){const e=this.milestonesShown&&this.sighting.milestones.length>0?ut(this.sighting.milestones,t):void 0;if(this.milestoneCaption.hidden=e===void 0,!e)return;const s=document.createElement("b");s.textContent=this.said.read(e.label)??"";const i=this.said.read(e.note),a=i?` — ${i}`:"";this.milestoneCaption.replaceChildren(s,document.createTextNode(a))}exposureInstants(t){const e=this.exposureTimes(t),s=1/e.length;return e.map(i=>({shapes:this.shapesAt(i),share:s}))}exposureTimes(t=this.currentTime){const e=(this.currentSighting.exposure??0)*1e3;if(e<v.SHORTEST_VISIBLE_EXPOSURE_MS)return[t];const s=Math.min(v.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(e/v.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(t,e)/v.EXPOSURE_STEP_PX),a=Math.min(v.MAX_TRAVEL_STEPS,Math.max(s,i)),r=[];for(let o=0;o<a;o++)r.push(t+e*o/a);return r}travelPxOver(t,e){const s=this.shapesAt(t),i=this.shapesAt(t+e);let a=0;for(const[r,o]of s){const l=i.get(r);if(!l)continue;const h=l.bounds.x+l.bounds.width/2-(o.bounds.x+o.bounds.width/2),d=l.bounds.y+l.bounds.height/2-(o.bounds.y+o.bounds.height/2);a=Math.max(a,Math.hypot(h,d))}return a}shapeAt(t,e,s=this.occludedSourceIds){for(const i of this.exposureTimes()){const a=this.currentSighting.timeline.hitTest(i,t,e,s);if(a)return a}}shapesAt(t){const e=new Map;for(const s of this.currentSighting.timeline.sourceIds){const i=this.currentSighting.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}return e}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const t=w.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===t)return;this.canvas.width=t,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${t} / ${this.canvas.height}`)}canvasPointFromEvent(t){const e=this.canvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return{x:(t.clientX-e.left)/e.width*this.canvas.width,y:(t.clientY-e.top)/e.height*this.canvas.height}}onFrame(t,e){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const s=this.playbackState!=="playing"?this.highlightedSourceIds:bt;this.canvasRenderer.setStarPoints(w.starPointsOf(this.sighting.instrument)),this.canvasRenderer.setRoll((S(this.sighting,t)?.rollDeg??0)*Math.PI/180);const i=this.exposureInstants(t),a=this.lookShift;for(const r of i)for(const[o,l]of r.shapes){if(this.occludedSourceIds.has(o))continue;const h=r.share,d=h===1?l:{...l,transparency:1-(1-l.transparency)*h};this.canvasRenderer.paintShape(this.shifted(d,a))}for(const[r,o]of i[0].shapes)this.occludedSourceIds.has(r)||!s.has(r)||(s.size===1?this.canvasRenderer.paintShape({...o,selected:!0}):this.canvasRenderer.paintMemberOutline(o));if(s.size>1){const r=b.groupBoundsFor([...e].filter(([o])=>s.has(o)).map(([,o])=>o.bounds));this.canvasRenderer.paintGroupHandles(r)}this.keepWitnessMapClear(i[0].shapes),this.seekInput.value=String(t),this.timeStartLabel.textContent=this.formatPosition(t),this.showMilestoneAt(t),this.paintWitnessMap(t),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Yt(this.currentSighting,t))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:t}}))}shifted(t,e){return e.x===0&&e.y===0?t:{...t,bounds:{...t.bounds,x:t.bounds.x+e.x,y:t.bounds.y+e.y}}}createPlayer(){const t=new Zt(this.currentSighting.timeline,(e,s)=>this.onFrame(e,s));return t.loop=this.loopEnabled,t.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},t}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton())}updatePlayPauseButton(){const t=this.player.playbackState==="playing";this.playPauseButton.textContent=t?"⏸":"▶";const e=this.player.seekableDuration>0;this.playPauseButton.disabled=!e;const s=e?t?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=s,this.playPauseButton.setAttribute("aria-label",s),this.toolbar.classList.toggle("auto-hide",t),this.cornerButtons.classList.toggle("auto-hide",t)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}restorePlayback(){const t=this.playbackBeforeClick;t&&(this.currentTime=t.time,t.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(t=>{console.error("<rr0-ufo>: requestFullscreen() failed —",t)})}enterSimulatedFullscreen(){const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=t.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const e=t.style;e.setProperty("position","fixed"),e.setProperty("inset","0"),e.setProperty("margin","0"),e.setProperty("max-width","none"),e.setProperty("max-height","none"),e.setProperty("aspect-ratio","auto"),e.setProperty("z-index","2147483647"),e.setProperty("background","#000"),e.setProperty("width","100vw"),e.setProperty("width","100dvw"),e.setProperty("height","100vh"),e.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?t.setAttribute("style",this.styleBeforeSimulatedFullscreen):t.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}toggleWitnessMap(){this.setWitnessMapOpen(this.witnessMapPanel.hidden)}applyWitnessMapDefault(){this.setWitnessMapOpen(this.hasAttribute(Q)&&this.witnessPath!==void 0)}setWitnessMapOpen(t){this.witnessMapPanel.hidden=!t,this.witnessMapPanel.removeAttribute("title"),this.witnessMapButton.setAttribute("aria-pressed",String(t)),this.updateWitnessMapButton(),t&&(this.sizeWitnessMapCanvas(),this.loadWitnessMapImagery(),this.paintWitnessMap(this.currentTime))}updateWitnessMapButton(){const e=!this.witnessMapPanel.hidden?this.messages.hideWitnessMap:this.messages.showWitnessMap;this.witnessMapButton.title=e,this.witnessMapButton.setAttribute("aria-label",e)}updateWitnessMap(){const t=this.witnessPath!==void 0;if(this.witnessPath=K.of(this.currentSighting),this.witnessMapButton.hidden=this.witnessPath===void 0,!t&&this.witnessPath&&this.applyWitnessMapDefault(),!this.witnessPath){this.setWitnessMapOpen(!1),this.witnessMapBounds=void 0;return}const e=this.witnessPath.boundsAround(v.WITNESS_MAP_MIN_SPAN_M,v.WITNESS_MAP_MARGIN);(!this.witnessMapBounds||!this.sameGround(this.witnessMapBounds,e))&&(this.witnessMapBounds=e,this.witnessMapImagery=void 0,this.witnessMapImageryCredit=void 0,this.witnessMapImageryFailed=!1,this.witnessMapImageryRequested=!1,this.witnessMapPanel.hidden||this.loadWitnessMapImagery())}sameGround(t,e){const s=Math.abs(t.east-t.west)/20;return Math.abs(t.north-e.north)<s&&Math.abs(t.south-e.south)<s&&Math.abs(t.west-e.west)<s&&Math.abs(t.east-e.east)<s}async loadWitnessMapImagery(){if(this.witnessMapImageryRequested||!this.witnessMapBounds)return;this.witnessMapImageryRequested=!0;const t=Re(),e=this.witnessMapBounds;try{const s=await t.getImageryTexture(e,{width:v.WITNESS_MAP_IMAGERY_PX,height:v.WITNESS_MAP_IMAGERY_PX});if(this.witnessMapBounds!==e)return;this.witnessMapImagery=s,this.witnessMapImageryCredit=t.attribution}catch{this.witnessMapImageryFailed=!0}this.paintWitnessMap(this.currentTime)}get witnessMapCredit(){return this.witnessMapImageryCredit}get witnessMapFooterLine(){return this.witnessMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.witnessMapImageryCredit}keepWitnessMapClear(t){if(this.witnessMapPanel.hidden)return;const e=this.witnessMapBoxPx;if(!e)return;const s=(o,l,h)=>{for(const[d,c]of t)if(!(this.occludedSourceIds.has(d)||c.transparency>=1)&&!(c.bounds.x+c.bounds.width<o-h||c.bounds.x>l+h)&&!(c.bounds.y>e.bottom+h||c.bounds.y+c.bounds.height<e.top-h))return!0;return!1},i=[this.canvas.width-e.width,this.canvas.width],a=[0,e.width],r=v.WITNESS_MAP_CLEARANCE_PX;if(this.witnessMapPanel.classList.contains("on-the-left")){s(i[0],i[1],r)||this.witnessMapPanel.classList.remove("on-the-left");return}s(i[0],i[1],0)&&!s(a[0],a[1],0)&&this.witnessMapPanel.classList.add("on-the-left")}witnessMapBoxPx;static WITNESS_MAP_CLEARANCE_PX=24;sizeWitnessMapCanvas(){const t=this.witnessMapPanel.clientWidth;if(t===0)return;const e=Math.round(t*(globalThis.devicePixelRatio??1));this.measureWitnessMapBox(),this.witnessMapCanvas.width!==e&&(this.witnessMapCanvas.width=e,this.witnessMapCanvas.height=e)}measureWitnessMapBox(){const t=this.witnessMapPanel.getBoundingClientRect(),e=this.canvas.getBoundingClientRect();if(e.width===0||e.height===0)return;const s=this.canvas.width/e.width,i=this.canvas.height/e.height;this.witnessMapBoxPx={width:t.width*s,top:(t.top-e.top)*i,bottom:(t.bottom-e.top)*i}}paintWitnessMap(t){if(this.witnessMapPanel.hidden||!this.witnessPath||!this.witnessMapBounds)return;const e=S(this.currentSighting,t),s=this.currentSighting.instrument,i=[],a=this.currentSighting.milestones.length>0?ut(this.currentSighting.milestones,t):void 0;for(const r of this.milestonesShown?X(this.currentSighting.milestones):[]){const o=S(this.currentSighting,r.t);o?.lat===void 0||o.lng===void 0||i.push({label:this.said.read(r.label)??"",note:this.said.read(r.note),t:r.t,lat:o.lat,lng:o.lng,current:r===a})}this.witnessMapRenderer.paint({bounds:this.witnessMapBounds,imagery:this.witnessMapImagery,path:this.witnessPath,position:e?.lat!==void 0&&e.lng!==void 0?{lat:e.lat,lng:e.lng}:void 0,headingDeg:e?.headingDeg===void 0?void 0:e.headingDeg+this.lookYawDeg,coneHalfAngleDeg:e?E.of(s,this.canvas.height,e.fovDeg).halfWidthAngleDeg(w.aspectOf(s)):void 0,markers:i,decor:this.witnessMapDecorAt(t),witnessLabel:this.said.read(this.currentSighting.witness?.title)??this.messages.witnessHere,nightFraction:this.witnessMapNightFraction,attribution:this.witnessMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(t){this.milestonesShown=t,this.milestoneMarks.hidden=!t,this.milestonesButton.setAttribute("aria-pressed",String(t)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintWitnessMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const t=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=t,this.milestonesButton.setAttribute("aria-label",t)}witnessMapDecorAt(t){const e=S(this.currentSighting,0);return e?.lat===void 0||e.lng===void 0?[]:this.currentSighting.decor.map(s=>{const i=be(s,t),{lat:a,lng:r}=R(i.eastM,-i.northM,e.lat,e.lng);return{lat:a,lng:r,headingDeg:i.headingDeg,label:this.said.read(s.title)??this.messages.decorHere}})}witnessMapNightFraction;setSunAltitude(t){const e=t===void 0?void 0:Math.max(0,Math.min(1,-t/12));e!==this.witnessMapNightFraction&&(this.witnessMapNightFraction=e,this.paintWitnessMap(this.currentTime))}setLookOffset(t,e){t===this.lookYawDeg&&e===this.lookPitchDeg||(this.lookYawDeg=t,this.lookPitchDeg=e,this.refresh())}get lookShiftPx(){return this.lookShift}get lookShift(){if(this.lookYawDeg===0&&this.lookPitchDeg===0)return{x:0,y:0};const t=S(this.currentSighting,this.currentTime),e=E.of(this.currentSighting.instrument,this.canvas.height,t?.fovDeg??60);return{x:-e.angleDegToRadiusPx(this.lookYawDeg),y:e.angleDegToRadiusPx(this.lookPitchDeg)}}updateFullscreenButton(){const t=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=t?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new T(ft.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const t=ue(ft.preferencesFor(this),pe);t!=="en"&&this.applyMessages(await ye(t))}updateTimeLabelTitles(){const t=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,e=this.canSwitchTimeDisplay?` — ${t}`:"";this.timeStartLabel.title=this.messages.currentPosition+e,this.timeEndLabel.title=this.messages.duration+e;for(const s of[this.timeStartLabel,this.timeEndLabel])s.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(s.setAttribute("role","button"),s.setAttribute("tabindex","0")):(s.removeAttribute("role"),s.removeAttribute("tabindex"))}applyMessages(t){this.messages=t,this.updateTimeLabelTitles(),this.loopButton.title=t.autoReplay,this.loopButton.setAttribute("aria-label",t.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton()}updateTimeLabels(){const t=this.currentSighting.event,e=Vt(t);this.realDurationMs=e!==void 0&&e>0?e:void 0,this.realStartMs=t.time?et(t.time):void 0;const s=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&s>0?s/this.realDurationMs:1,this.player.durationOverrideMs=s>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.updateTimeLabelTitles()}formatPosition(t){if(this.realDurationMs===void 0)return U(t);const e=this.currentSighting.timeline.duration,s=e>0&&t<=e?t/e*this.realDurationMs:t;return this.canShowClockTime?xt(vt(this.realStartMs+s)):U(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?U(this.currentSighting.timeline.duration):this.canShowClockTime?xt(vt(this.realStartMs+this.realDurationMs)):U(this.realDurationMs)}}function vt(n){const t=new Date(n);return{hour:t.getUTCHours(),minute:t.getUTCMinutes(),second:t.getUTCSeconds()}}function xt(n){if(n.hour===void 0)return"0:00";const t=e=>String(e).padStart(2,"0");return n.second?`${t(n.hour)}:${t(n.minute??0)}:${t(n.second)}`:`${t(n.hour)}:${t(n.minute??0)}`}function U(n){const t=Math.round(n/1e3),e=Math.floor(t/60),s=t%60;return`${e}:${String(s).padStart(2,"0")}`}const Mt="rr0-ufo";function _e(){customElements.get(Mt)||customElements.define(Mt,v)}_e();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rr0/ufoathome",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.50.0",
|
|
5
5
|
"description": "UFO@home — record and replay a UFO sighting's shape, movement and appearance",
|
|
6
6
|
"author": "Jérôme Beau <rr0@rr0.org> (https://rr0.org)",
|
|
7
7
|
"license": "MIT",
|