@rr0/ufoathome 0.34.0 → 0.36.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.
@@ -1,4 +1,4 @@
1
- const ae=`
1
+ const oe=`
2
2
  <div class="stage" id="stage">
3
3
  <div class="frame" id="frame">
4
4
  <canvas id="canvas" width="640" height="360"></canvas>
@@ -13,7 +13,7 @@ const ae=`
13
13
  <button id="loop" type="button" title="Auto-replay" aria-label="Auto-replay" aria-pressed="true">↻</button>
14
14
  </div>
15
15
  </div>
16
- `,oe=`
16
+ `,he=`
17
17
  :host {
18
18
  display: block;
19
19
  font-family: sans-serif;
@@ -229,4 +229,4 @@ input[type=range] {
229
229
  pointer-events: none;
230
230
  white-space: nowrap;
231
231
  }
232
- `,P=[{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:.004,exposureRangeSeconds:{min:.001,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:.011111111111111112,years:{from:1963,to:1988},apertureBlades:5},{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:.004,exposureRangeSeconds:{min:.001,max:30},years:{from:1959},apertureBlades:6},{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:.004,exposureRangeSeconds:{min:.001,max:30},apertureBlades:8,years:{from:1975}},{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},exposureSeconds:.008333333333333333,exposureRangeSeconds:{min:125e-6,max:10},years:{from:2007},apertureBlades:void 0},{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},exposureSeconds:.008333333333333333,exposureRangeSeconds:{min:125e-6,max:10},years:{from:2007},apertureBlades:void 0}];class v{static get default(){return P[0]}static byId(e){return P.find(t=>t.id===e)??this.default}static UNAIDED_FIELD_DEG=60;static UNAIDED_ASPECT=16/9;static fieldOfViewDeg(e){const t=e.frame;return t?2*Math.atan(t.heightMm/(2*t.focalLengthMm))*180/Math.PI:v.UNAIDED_FIELD_DEG}static aspectOf(e){const t=e.frame;return t?t.widthMm/t.heightMm:v.UNAIDED_ASPECT}static frameWidthPx(e,t){return Math.round(t*v.aspectOf(e))}static availableAt(e){return e===void 0?P:P.filter(t=>!t.years||e>=t.years.from&&(t.years.to===void 0||e<=t.years.to))}static fieldOfViewDegAt(e,t){const s=e.frame;if(!(!s||t<=0))return 2*Math.atan(s.heightMm/(2*t))*180/Math.PI}static focalLengthMmFor(e,t){const s=e.frame;if(!(!s||t<=0||t>=180))return s.heightMm/(2*Math.tan(t*Math.PI/360))}static bladesShowing(e,t){const s=t??e.fNumber;if(s===void 0||e.apertureBlades===void 0)return 0;const i=e.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(e){return e.flare??v.LENS_FLARE_ARTIFACTS}static starPointsOf(e){const t=e.apertureBlades;return t===void 0||t<3?0:t%2===0?t:t*2}}class w{static MOON_ANGULAR_WIDTH_DEG=.5237;static CANVAS_WIDTH_PX=640;static CANVAS_HEIGHT_PX=360;static angularWidthDeg(e){return 2*Math.atan(e.sizeM/(2*e.distanceM))*180/Math.PI}static inMoons(e){return e/w.MOON_ANGULAR_WIDTH_DEG}static lerpAngular(e,t,s){if(!(!e||!t))return{widthDeg:e.widthDeg+(t.widthDeg-e.widthDeg)*s,heightDeg:e.heightDeg+(t.heightDeg-e.heightDeg)*s}}static sizeMAt(e,t){return 2*e*Math.tan(t*Math.PI/360)}static distanceMAt(e,t){return e/(2*Math.tan(t*Math.PI/360))}}function he(n,e,t){const{bounds:s}=n;return e>=s.x&&e<=s.x+s.width&&t>=s.y&&t<=s.y+s.height}function y(n,e,t){return n+(e-n)*t}function G(n){const e=/^#([0-9a-f]{6})$/i.exec(n);if(!e)return;const t=parseInt(e[1],16);return[t>>16&255,t>>8&255,t&255]}function ce(n){return`#${n.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function le(n,e,t){const s=G(n),i=G(e);return!s||!i?t<1?n:e:ce([y(s[0],i[0],t),y(s[1],i[1],t),y(s[2],i[2],t)])}function ue(n,e,t){const s={x:y(n.bounds.x,e.bounds.x,t),y:y(n.bounds.y,e.bounds.y,t),width:y(n.bounds.width,e.bounds.width,t),height:y(n.bounds.height,e.bounds.height,t)},i=y(n.angle,e.angle,t),r=y(n.transparency,e.transparency,t),a=y(n.haloScale,e.haloScale,t),o=le(n.color,e.color,t),h=w.lerpAngular(n.angular,e.angular,t);return n.kind==="polygon"&&e.kind==="polygon"&&n.points.length===e.points.length?{...n,bounds:s,angle:i,transparency:r,haloScale:a,color:o,angular:h,behindCloud:t<1?n.behindCloud:e.behindCloud,points:n.points.map((c,u)=>({x:y(c.x,e.points[u].x,t),y:y(c.y,e.points[u].y,t)}))}:{...t<1?n:e,bounds:s,angle:i,transparency:r,haloScale:a,color:o,angular:h}}class O{keyframes=[];order=[];groups=[];addKeyframe(e,t){const s=this.findInsertIndex(e);if(this.keyframes[s]?.t===e){const i=new Set(t.map(r=>r.sourceId));this.keyframes[s]={t:e,shapes:[...this.keyframes[s].shapes.filter(r=>!i.has(r.sourceId)),...t]}}else this.keyframes.splice(s,0,{t:e,shapes:[...t]});for(const i of t)this.order.includes(i.sourceId)||this.order.push(i.sourceId)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}getKeyframeAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t];return s?.t===e?s:void 0}getShapeAt(e,t){return this.getKeyframeAt(e)?.shapes.find(s=>s.sourceId===t)?.shape}getLatestShapeAt(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===t);if(i)return i.shape}}getInterpolatedShapeAt(e,t){const s=this.findShapeAtOrBefore(e,t);if(s?.t===e)return s.shape;const i=this.findShapeAtOrAfter(e,t);return s?i?ue(s.shape,i.shape,(e-s.t)/(i.t-s.t)):s.shape:i?.shape}findShapeAtOrBefore(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===t);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}findShapeAtOrAfter(e,t){for(let s=this.findInsertIndex(e);s<this.keyframes.length;s++){const i=this.keyframes[s].shapes.find(r=>r.sourceId===t);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}hitTest(e,t,s,i){const r=this.sourceIds;for(let a=r.length-1;a>=0;a--){if(i?.has(r[a]))continue;const o=this.getInterpolatedShapeAt(e,r[a]);if(o&&he(o,t,s))return{sourceId:r[a],shape:o}}}removeSource(e){for(let t=this.keyframes.length-1;t>=0;t--){const s=this.keyframes[t].shapes.filter(i=>i.sourceId!==e);s.length===0?this.keyframes.splice(t,1):s.length!==this.keyframes[t].shapes.length&&(this.keyframes[t]={t:this.keyframes[t].t,shapes:s})}this.order=this.order.filter(t=>t!==e),this.removeFromGroup(e)}group(e){if(!(e.length<2)){for(const t of e)this.removeFromGroup(t);this.groups.push([...e])}}ungroup(e){const t=this.groups.findIndex(s=>s.includes(e));t!==-1&&this.groups.splice(t,1)}groupMembers(e){return this.groups.find(t=>t.includes(e))}removeFromGroup(e){const t=this.groups.findIndex(i=>i.includes(e));if(t===-1)return;const s=this.groups[t].filter(i=>i!==e);s.length<2?this.groups.splice(t,1):this.groups[t]=s}bringToFront(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.push(e))}sendToBack(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.unshift(e))}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(e){const t=new O;for(const s of e.keyframes)t.addKeyframe(s.t,s.shapes);if(e.order){const s=t.order.filter(i=>!e.order.includes(i));t.order=[...e.order,...s]}return e.groups&&(t.groups=e.groups.map(s=>s.filter(i=>t.order.includes(i))).filter(s=>s.length>=2)),t}}function de(n,e,t){return Math.max(e,Math.min(t,n))}function fe(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function k(n,e,t){return n+(e-n)*t}function me(n,e,t){return{lat:n.lat===void 0||e.lat===void 0?void 0:k(n.lat,e.lat,t),lng:n.lng===void 0||e.lng===void 0?void 0:k(n.lng,e.lng,t),elevationM:k(n.elevationM,e.elevationM,t),headingDeg:n.headingDeg===void 0||e.headingDeg===void 0?void 0:fe(n.headingDeg,e.headingDeg,t),pitchDeg:k(n.pitchDeg,e.pitchDeg,t),fovDeg:k(n.fovDeg,e.fovDeg,t),fNumber:n.fNumber,exposureSeconds:n.exposureSeconds,focusDistanceM:n.focusDistanceM}}class I{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,pose:t}:this.keyframes.splice(s,0,{t:e,pose:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}getLatestPoseAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].pose:void 0}getInterpolatedPoseAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.pose;const i=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?i?me(s.pose,i.pose,de((e-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(e){const t=new I;for(const s of e.keyframes)t.addKeyframe(s.t,s.pose);return t}}function x(n,e,t){return n+(e-n)*t}function pe(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function ge(n,e,t){return Math.max(e,Math.min(t,n))}function ye(n,e,t){return{cloudCover:x(n.cloudCover,e.cloudCover,t),cloudDarkness:x(n.cloudDarkness,e.cloudDarkness,t),highCloudCover:n.highCloudCover===void 0||e.highCloudCover===void 0?void 0:x(n.highCloudCover,e.highCloudCover,t),iceCrystalAlignment:n.iceCrystalAlignment===void 0||e.iceCrystalAlignment===void 0?void 0:x(n.iceCrystalAlignment,e.iceCrystalAlignment,t),lowerCloudCover:n.lowerCloudCover===void 0||e.lowerCloudCover===void 0?void 0:x(n.lowerCloudCover,e.lowerCloudCover,t),cloudBaseM:n.cloudBaseM===void 0||e.cloudBaseM===void 0?void 0:x(n.cloudBaseM,e.cloudBaseM,t),precipitationType:t<1?n.precipitationType:e.precipitationType,precipitationIntensity:x(n.precipitationIntensity,e.precipitationIntensity,t),windDirectionDeg:pe(n.windDirectionDeg,e.windDirectionDeg,t),windSpeed:x(n.windSpeed,e.windSpeed,t),storm:t<1?n.storm:e.storm}}class M{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,weather:t}:this.keyframes.splice(s,0,{t:e,weather:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}getLatestWeatherAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].weather:void 0}getInterpolatedWeatherAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.weather;const i=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?i?ye(s.weather,i.weather,ge((e-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(e){const t=new M;for(const s of e.keyframes)t.addKeyframe(s.t,s.weather);return t}}class A{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,sound:t}:this.keyframes.splice(s,0,{t:e,sound:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}getLatestSoundAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].sound:void 0}getInterpolatedSoundAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.sound;const i=this.keyframes[t]?.t===e?void 0:this.keyframes[t];if(!s)return i?.sound;if(!i)return s.sound;const r=(e-s.t)/(i.t-s.t);return this.lerp(s.sound,i.sound,Math.max(0,Math.min(1,r)))}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(e){const t=new A;for(const s of e.keyframes)t.addKeyframe(s.t,s.sound);return t}lerp(e,t,s){return{kind:s<1?e.kind:t.kind,volume:e.volume+(t.volume-e.volume)*s,pitchHz:e.pitchHz+(t.pitchHz-e.pitchHz)*s,src:s<1?e.src:t.src}}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}}const ve=100,we={kind:"none",volume:0,pitchHz:ve};function F(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 U(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const be=["year","month","day","hour","minute"],xe=["day","hour","minute"];function V(n,e){return e.filter(t=>n[t]!==void 0).join(",")}function ke(n){if(n.durationSeconds!==void 0)return n.durationSeconds*1e3;if(!n.time||!n.endTime)return;const e=n.time.year!==void 0&&n.endTime.year!==void 0,t=e?be:xe,s=V(n.time,t);if(!(s===""||s!==V(n.endTime,t)))return e?F(n.endTime)-F(n.time):U(n.endTime)-U(n.time)}class _{constructor(e,t,s,i,r,a,o,h,c=[],u,l){this.event=e,this.timeline=t,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=r,this.witness=a,this.caseId=o,this.weather=h,this.decor=c,this.weatherSource=u,this.instrumentId=l}get instrument(){return v.byId(this.instrumentId)}static create(e,t,s){return new _({eventType:"sighting",time:e,place:t},new O,new I,new M,new A,s)}}const Se=0,Te=0;function te(n,e){const t=n.witnessTrack.getInterpolatedPoseAt(e);if(t)return t;const s=n.event.place?.[0];if(s)return{lat:s.lat,lng:s.lng,elevationM:Se,headingDeg:void 0,pitchDeg:Te,fovDeg:v.fieldOfViewDeg(n.instrument)}}function Ee(n,e){return n.soundTrack.getInterpolatedSoundAt(e)??we}class Ie{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT);return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(e,s);i&&t.set(s,i)}this.onFrame(e,t)}}const se=["nw","n","ne","e","se","s","sw","w"],Me=24,D=8,Ae=3,Pe=8;function C(n,e,t){const s=n.x-e.x,i=n.y-e.y,r=Math.cos(t),a=Math.sin(t);return{x:e.x+s*r-i*a,y:e.y+s*a+i*r}}function S(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const De={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 p{static handlePointsFor(e){const{x:t,y:s,width:i,height:r}=e.bounds,a={nw:{x:t,y:s},n:{x:t+i/2,y:s},ne:{x:t+i,y:s},e:{x:t+i,y:s+r/2},se:{x:t+i,y:s+r},s:{x:t+i/2,y:s+r},sw:{x:t,y:s+r},w:{x:t,y:s+r/2},rotate:{x:t+i/2,y:s-Me}},o=S(e.bounds),h={};for(const c of Object.keys(a))h[c]=C(a[c],o,e.angle);return h}static hitTestHandle(e,t,s=8,i=[...se,"rotate"]){const r=p.handlePointsFor(e);for(const a of i)if(Math.hypot(t.x-r[a].x,t.y-r[a].y)<=s)return a}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(e,t){const i=((p.HANDLE_DIRECTION_DEG[e]+t*180/Math.PI)%180+180)%180/45,r=["ew","nwse","ns","nesw"];return r[Math.round(i)%r.length]}static resizeBounds(e,t,s,i){const r=S(e),a=C(i,r,-t),{x:o,y:h,width:c,height:u}=e,l=De[s];let d=o,m=h,f=o+c,g=h+u;return l.left&&(d=Math.min(a.x,f-D)),l.right&&(f=Math.max(a.x,d+D)),l.top&&(m=Math.min(a.y,g-D)),l.bottom&&(g=Math.max(a.y,m+D)),{x:d,y:m,width:f-d,height:g-m}}static resizeShape(e,t,s){if(t==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:i,height:r}=e.bounds,a=p.resizeBounds(e.bounds,e.angle,t,s);if(e.kind==="oval")return{...e,bounds:a};const o=i===0?1:a.width/i,h=r===0?1:a.height/r;return{...e,bounds:a,points:e.points.map(c=>({x:c.x*o,y:c.y*h}))}}static rotateShape(e,t){const s=S(e.bounds),i=Math.atan2(t.y-s.y,t.x-s.x)+Math.PI/2;return{...e,angle:i}}static groupBoundsFor(e){const t=Math.min(...e.map(a=>a.x)),s=Math.min(...e.map(a=>a.y)),i=Math.max(...e.map(a=>a.x+a.width)),r=Math.max(...e.map(a=>a.y+a.height));return{x:t,y:s,width:i-t,height:r-s}}static vertexPointsFor(e){const t=S(e.bounds);return e.points.map(s=>C({x:e.bounds.x+s.x,y:e.bounds.y+s.y},t,e.angle))}static hitTestVertex(e,t,s=Pe){const i=p.vertexPointsFor(e);let r,a=s;return i.forEach((o,h)=>{const c=Math.hypot(t.x-o.x,t.y-o.y);c<=a&&(a=c,r=h)}),r}static toLocalPoint(e,t){const s=S(e.bounds),i=C(t,s,-e.angle);return{x:i.x-e.bounds.x,y:i.y-e.bounds.y}}static moveVertex(e,t,s){const i=e.points.map((l,d)=>d===t?p.toLocalPoint(e,s):l),r=Math.min(...i.map(l=>l.x)),a=Math.min(...i.map(l=>l.y)),o=Math.max(...i.map(l=>l.x)),h=Math.max(...i.map(l=>l.y)),c={x:e.bounds.x+r,y:e.bounds.y+a,width:o-r,height:h-a},u=i.map(l=>({x:l.x-r,y:l.y-a}));return{...e,bounds:c,points:u}}static insertVertexNear(e,t){const s=p.toLocalPoint(e,t),{points:i}=e;let r=i.length-1,a=1/0;for(let h=0;h<i.length;h++){const c=i[h],u=i[(h+1)%i.length],l=p.distanceToSegment(s,c,u);l<a&&(a=l,r=h)}const o=[...i.slice(0,r+1),s,...i.slice(r+1)];return{...e,points:o}}static distanceToSegment(e,t,s){const i=s.x-t.x,r=s.y-t.y,a=i*i+r*r,o=a===0?0:Math.max(0,Math.min(1,((e.x-t.x)*i+(e.y-t.y)*r)/a)),h=t.x+o*i,c=t.y+o*r;return Math.hypot(e.x-h,e.y-c)}static deleteVertex(e,t){return e.points.length<=Ae?e:{...e,points:e.points.filter((s,i)=>i!==t)}}}const H=6,B=H/2,Ce=20,q=4;class Be{constructor(e){this.ctx=e}clear(e,t){this.ctx.clearRect(0,0,e,t)}paintShape(e){this.ctx.save(),this.ctx.globalAlpha=1-e.transparency,e.haloScale>0&&this.paintHalo(e),this.paintBase(e),this.ctx.restore(),e.selected&&this.paintSelectionHandles(e)}paintBase(e){if(this.ctx.fillStyle=e.color,this.ctx.beginPath(),e.kind==="oval"){const{x:t,y:s,width:i,height:r}=e.bounds,a=i/2,o=r/2;this.ctx.ellipse(t+a,s+o,a,o,e.angle,0,2*Math.PI)}else{const{x:t,y:s,width:i,height:r}=e.bounds;this.ctx.save(),this.ctx.translate(t+i/2,s+r/2),this.ctx.rotate(e.angle),this.ctx.translate(-i/2,-r/2),e.points.forEach((a,o)=>{o===0?this.ctx.moveTo(a.x,a.y):this.ctx.lineTo(a.x,a.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=Ce*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){this.paintHandleFrame(p.handlePointsFor(e),{includeRotate:!0}),e.kind==="polygon"&&this.paintVertexHandles(e)}paintVertexHandles(e){this.ctx.fillStyle="#39f";for(const t of p.vertexPointsFor(e))this.ctx.beginPath(),this.ctx.ellipse(t.x,t.y,q,q,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(e){this.paintOutline(p.handlePointsFor(e))}paintGroupHandles(e){this.paintHandleFrame(p.handlePointsFor({bounds:e,angle:0}),{includeRotate:!0})}paintOutline(e){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(e.nw.x,e.nw.y),this.ctx.lineTo(e.ne.x,e.ne.y),this.ctx.lineTo(e.se.x,e.se.y),this.ctx.lineTo(e.sw.x,e.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(e,{includeRotate:t}){this.paintOutline(e),this.ctx.fillStyle="lightgray";for(const s of se){const i=e[s];this.ctx.fillRect(i.x-B,i.y-B,H,H)}t&&(this.ctx.beginPath(),this.ctx.moveTo(e.n.x,e.n.y),this.ctx.lineTo(e.rotate.x,e.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(e.rotate.x,e.rotate.y,B+1,B+1,0,0,2*Math.PI),this.ctx.fill())}}const W=.05,K=.08,$=2,X=3,Le=14,Re=.02;class Oe{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(e){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",e);return}}this.context.state==="suspended"&&this.context.resume()}setSound(e){if(this.requested=e,!this.context)return;const t=this.keyFor(e);if(!t){this.silence();return}if(this.voice?.key!==t){this.stopVoice();const s=++this.voiceToken;t.startsWith("src:")?this.buildRecordedVoice(e.src,t,s):this.voice=this.buildSynthesizedVoice(e,t)}this.applyTo(this.voice,e)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(e){if(!(e.volume<=0))return e.src?`src:${e.src}`:e.kind==="none"?void 0:e.kind}applyTo(e,t){const s=this.context;if(!e||!s)return;const i=s.currentTime;e.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,t.volume))*e.gainScale,i,W);for(const{param:r,ratio:a}of e.pitch)r.setTargetAtTime(Math.min(t.pitchHz*a,s.sampleRate/2-1),i,W)}buildSynthesizedVoice(e,t){const s=this.context;if(s)switch(e.kind){case"hum":return this.buildHum(s,t);case"whistle":return this.buildWhistle(s,t);case"rumble":return this.buildRumble(s,t);case"crackle":return this.buildCrackle(s,t);default:return}}buildHum(e,t){const s=e.createOscillator();s.type="sawtooth";const i=e.createOscillator();i.type="sawtooth";const r=e.createBiquadFilter();r.type="lowpass",r.Q.value=1;const a=e.createGain();return a.gain.value=0,s.connect(r),i.connect(r),r.connect(a).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.16,pitch:[{param:s.frequency,ratio:1},{param:i.frequency,ratio:1.006},{param:r.frequency,ratio:6}]}}buildWhistle(e,t){const s=e.createOscillator();s.type="sine";const i=e.createOscillator();i.type="sine",i.frequency.value=5;const r=e.createGain(),a=e.createGain();return a.gain.value=0,i.connect(r).connect(s.frequency),s.connect(a).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.22,pitch:[{param:s.frequency,ratio:1},{param:r.gain,ratio:.012}]}}buildRumble(e,t){const s=e.createBufferSource();s.buffer=this.brownNoise(e),s.loop=!0;const i=e.createBiquadFilter();i.type="lowpass",i.Q.value=.8;const r=e.createGain();return r.gain.value=0,s.connect(i).connect(r).connect(e.destination),s.start(),{key:t,nodes:[i,r],sources:[s],volumeParam:r.gain,gainScale:1.4,pitch:[{param:i.frequency,ratio:1}]}}buildCrackle(e,t){const s=e.createBufferSource();s.buffer=this.whiteNoise(e),s.loop=!0;const i=e.createBiquadFilter();i.type="bandpass",i.Q.value=1.2;const r=e.createGain();r.gain.value=0;const a=e.createBufferSource();a.buffer=this.crackleEnvelope(e),a.loop=!0;const o=e.createGain();return o.gain.value=0,a.connect(o).connect(r.gain),s.connect(i).connect(r).connect(e.destination),s.start(),a.start(),{key:t,nodes:[i,r,o],sources:[s,a],volumeParam:o.gain,gainScale:.9,pitch:[{param:i.frequency,ratio:4}]}}async buildRecordedVoice(e,t,s){const i=this.context;if(!i)return;let r;try{r=await this.getBuffer(e)}catch(h){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",h);return}if(s!==this.voiceToken||!this.context)return;const a=i.createBufferSource();a.buffer=r,a.loop=!0;const o=i.createGain();o.gain.value=0,a.connect(o).connect(i.destination),a.start(),this.voice={key:t,nodes:[o],sources:[a],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let s=this.buffers.get(e);return s||(s=fetch(e).then(i=>{if(!i.ok)throw new Error(`Audio fetch failed (${i.status}): ${e}`);return i.arrayBuffer()}).then(i=>t.decodeAudioData(i)),this.buffers.set(e,s)),s}whiteNoise(e){if(!this.noiseBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*$),e.sampleRate),s=t.getChannelData(0);for(let i=0;i<s.length;i++)s[i]=Math.random()*2-1;this.noiseBuffer=t}return this.noiseBuffer}brownNoise(e){const t=e.createBuffer(1,Math.floor(e.sampleRate*$),e.sampleRate),s=t.getChannelData(0);let i=0,r=0;for(let a=0;a<s.length;a++)i=(i+.02*(Math.random()*2-1))/1.02,s[a]=i,r=Math.max(r,Math.abs(i));if(r>0)for(let a=0;a<s.length;a++)s[a]/=r;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*X),e.sampleRate),s=t.getChannelData(0),i=Math.max(1,Math.floor(Re*e.sampleRate)),r=Math.round(X*Le);for(let a=0;a<r;a++){const o=Math.floor(Math.random()*s.length),h=.4+Math.random()*.6;for(let c=0;c<i&&o+c<s.length;c++)s[o+c]=Math.max(s[o+c],h*(1-c/i))}this.crackleBuffer=t}return this.crackleBuffer}stopVoice(){const e=this.voice,t=this.context;if(this.voice=void 0,!e||!t)return;const s=t.currentTime;e.volumeParam.cancelScheduledValues(s),e.volumeParam.setTargetAtTime(0,s,K/3);const i=s+K;for(const r of e.sources){r.onended=()=>{r.disconnect();for(const a of e.nodes)a.disconnect()};try{r.stop(i)}catch{r.disconnect();for(const a of e.nodes)a.disconnect()}}}}const N=Math.PI/180,L=180/Math.PI;class T{constructor(e,t,s){this.kind=e,this.canvasHeightPx=t,this.fovDeg=s;const i=s*N/2;this.focalPx=e==="equidistant"?t/2/i:t/2/Math.tan(i)}focalPx;static of(e,t,s){return new T(e.projection,t,s)}degToPx(e){const t=e*N;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*L:2*Math.atan(e/(2*this.focalPx))*L}angleDegToRadiusPx(e){const t=e*N;return this.kind==="equidistant"?this.focalPx*t:this.focalPx*Math.tan(t)}radiusPxToAngleDeg(e){return this.kind==="equidistant"?e/this.focalPx*L:Math.atan(e/this.focalPx)*L}ofBounds(e){return{widthDeg:this.pxToDeg(e.width),heightDeg:this.pxToDeg(e.height)}}toBoundsSize(e){return{width:this.degToPx(e.widthDeg),height:this.degToPx(e.heightDeg)}}widthPx(e){return this.degToPx(w.angularWidthDeg(e))}}class ie{static fovOf(e,t){return te(e,t)?.fovDeg??v.fieldOfViewDeg(e.instrument)}static toAngular(e){this.eachKeyframe(e,(t,s)=>({...t,angular:s.ofBounds(t.bounds)}))}static toBounds(e){this.eachKeyframe(e,(t,s)=>{if(!t.angular)return t;const{width:i,height:r}=s.toBoundsSize(t.angular),a={x:t.bounds.x+(t.bounds.width-i)/2,y:t.bounds.y+(t.bounds.height-r)/2,width:i,height:r};if(t.kind!=="polygon")return{...t,bounds:a};const o=t.bounds.width===0?1:i/t.bounds.width,h=t.bounds.height===0?1:r/t.bounds.height;return{...t,bounds:a,points:t.points.map(c=>({x:c.x*o,y:c.y*h}))}})}static reproject(e,t,s){const i=v.frameWidthPx(t,w.CANVAS_HEIGHT_PX)/2,r=v.frameWidthPx(e.instrument,w.CANVAS_HEIGHT_PX)/2,a=w.CANVAS_HEIGHT_PX/2;for(const o of[...e.timeline.allKeyframes]){const h=this.fovOf(e,o.t),c=s?.get(o.t)??h,u=T.of(t,w.CANVAS_HEIGHT_PX,c),l=T.of(e.instrument,w.CANVAS_HEIGHT_PX,h);e.timeline.addKeyframe(o.t,o.shapes.map(d=>{const{bounds:m}=d.shape,f=m.x+m.width/2-i,g=m.y+m.height/2-a,b=Math.hypot(f,g),z=b===0?1:l.angleDegToRadiusPx(u.radiusPxToAngleDeg(b))/b,re={...m,x:r+f*z-m.width/2,y:a+g*z-m.height/2};return{...d,shape:{...d.shape,bounds:re}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:s}=e;for(const i of[...s.allKeyframes]){const r=this.fovOf(e,i.t),a=T.of(e.instrument,w.CANVAS_HEIGHT_PX,r);s.addKeyframe(i.t,i.shapes.map(o=>({...o,shape:t(o.shape,a)})))}}}function _e(n){return ie.toAngular(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,weatherSource:n.weatherSource,instrument:n.instrumentId}}function Ne(n){const e=new _({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},O.fromJSON(n.timeline),n.witnessTrack?I.fromJSON(n.witnessTrack):new I,n.weatherTrack?M.fromJSON(n.weatherTrack):new M,n.soundTrack?A.fromJSON(n.soundTrack):new A,n.witness,n.caseId,n.weather,n.decor??[],n.weatherSource,n.instrument);return ie.toBounds(e),e}function Fe(n,e){for(const t of n){const s=t.toLowerCase().split("-")[0];if(e.includes(s))return s}return"en"}const He="modulepreload",ze=function(n,e){return new URL(n,e).href},J={},Y=function(e,t,s){let i=Promise.resolve();if(t&&t.length>0){let a=function(u){return Promise.all(u.map(l=>Promise.resolve(l).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const o=document.getElementsByTagName("link"),h=document.querySelector("meta[property=csp-nonce]"),c=h?.nonce||h?.getAttribute("nonce");i=a(t.map(u=>{if(u=ze(u,s),u in J)return;J[u]=!0;const l=u.endsWith(".css"),d=l?'[rel="stylesheet"]':"";if(!!s)for(let g=o.length-1;g>=0;g--){const b=o[g];if(b.href===u&&(!l||b.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${u}"]${d}`))return;const f=document.createElement("link");if(f.rel=l?"stylesheet":He,l||(f.as="script"),f.crossOrigin="",f.href=u,c&&f.setAttribute("nonce",c),document.head.appendChild(f),l)return new Promise((g,b)=>{f.addEventListener("load",g),f.addEventListener("error",()=>b(new Error(`Unable to preload CSS for ${u}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return i.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})},Ge=["en","fr"],Ue={en:()=>Y(()=>Promise.resolve().then(()=>qe),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>Y(()=>import("./UfoMessages_fr-Hq24iVzT.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function Ve(n){return Ue[n]()}const ne={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"},qe=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:ne},Symbol.toStringTag,{value:"Module"})),Z=new Set;class E extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=_.create();sightingAudio=new Oe;soundPreview;player;loopEnabled=!0;highlightedSourceIds=new Set;occludedSourceIds=Z;enableClickToPlay=!0;fullscreenTarget;messages=ne;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>this.updateFullscreenButton();handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),s=t&&this.currentSighting.timeline.hitTest(this.currentTime,t.x,t.y,this.occludedSourceIds);if(!s?.shape.title){this.tooltip.hidden=!0;return}this.tooltip.textContent=s.shape.title,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${oe}</style>${ae}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new Be(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.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");for(const t of[this.timeStartLabel,this.timeEndLabel])t.addEventListener("click",()=>this.toggleTimeDisplay()),t.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.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",()=>{this.enableClickToPlay&&this.togglePlayPause()}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.sightingAudio.dispose()}attributeChangedCallback(e,t,s){e==="src"&&s&&s!==t&&this.isConnected&&this.loadFromSrc(s)}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return _e(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=Ne(e),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh()}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(i=>this.highlightedSourceIds.has(i))||(this.highlightedSourceIds=t,this.refresh())}setOccludedSourceIds(e){e.size===this.occludedSourceIds.size&&[...e].every(s=>this.occludedSourceIds.has(s))||(this.occludedSourceIds=e,this.refresh())}hasVisibleShapeAt(e,t){return this.currentSighting.timeline.hitTest(this.currentTime,e,t,this.occludedSourceIds)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.player.seek(this.player.time)}exposureInstants(e){const t=(te(this.currentSighting,e)?.exposureSeconds??0)*1e3,s=()=>[{shapes:this.shapesAt(e),share:1}];if(t<E.SHORTEST_VISIBLE_EXPOSURE_MS)return s();const i=Math.min(E.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t/E.SHORTEST_VISIBLE_EXPOSURE_MS))),r=[];for(let a=0;a<i;a++)r.push({shapes:this.shapesAt(e+t*a/i),share:1/i});return r}shapesAt(e){const t=new Map;for(const s of this.currentSighting.timeline.sourceIds){const i=this.currentSighting.timeline.getInterpolatedShapeAt(e,s);i&&t.set(s,i)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;applyFrameFormat(){const e=v.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===e)return;this.canvas.width=e,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${e} / ${this.canvas.height}`)}canvasPointFromEvent(e){const t=this.canvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return{x:(e.clientX-t.left)/t.width*this.canvas.width,y:(e.clientY-t.top)/t.height*this.canvas.height}}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const s=this.playbackState!=="playing"?this.highlightedSourceIds:Z;for(const i of this.exposureInstants(e))for(const[r,a]of i.shapes){if(this.occludedSourceIds.has(r))continue;const o=i.share,h=o===1?a:{...a,transparency:1-(1-a.transparency)*o},c=s.has(r)&&o===1;c&&s.size===1?this.canvasRenderer.paintShape({...h,selected:!0}):(this.canvasRenderer.paintShape(h),c&&this.canvasRenderer.paintMemberOutline(h))}if(s.size>1){const i=p.groupBoundsFor([...t].filter(([r])=>s.has(r)).map(([,r])=>r.bounds));this.canvasRenderer.paintGroupHandles(i)}this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Ee(this.currentSighting,e))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}createPlayer(){const e=new Ie(this.currentSighting.timeline,(t,s)=>this.onFrame(t,s));return e.loop=this.loopEnabled,e}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 e=this.player.playbackState==="playing";this.playPauseButton.textContent=e?"⏸":"▶";const t=this.player.seekableDuration>0;this.playPauseButton.disabled=!t;const s=t?e?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",e),this.fullscreenButton.classList.toggle("auto-hide",e)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}updateFullscreenButton(){const e=document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}async loadLocaleMessages(){const e=Fe(navigator.languages,Ge);e!=="en"&&this.applyMessages(await Ve(e))}updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t;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(e){this.messages=e,this.updateTimeLabelTitles(),this.loopButton.title=e.autoReplay,this.loopButton.setAttribute("aria-label",e.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton()}updateTimeLabels(){const e=this.currentSighting.event,t=ke(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?F(e.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(e){if(this.realDurationMs===void 0)return R(e);const t=this.currentSighting.timeline.duration,s=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?Q(j(this.realStartMs+s)):R(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?R(this.currentSighting.timeline.duration):this.canShowClockTime?Q(j(this.realStartMs+this.realDurationMs)):R(this.realDurationMs)}}function j(n){const e=new Date(n);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function Q(n){if(n.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return n.second?`${e(n.hour)}:${e(n.minute??0)}:${e(n.second)}`:`${e(n.hour)}:${e(n.minute??0)}`}function R(n){const e=Math.round(n/1e3),t=Math.floor(e/60),s=e%60;return`${t}:${String(s).padStart(2,"0")}`}const ee="rr0-ufo";function We(){customElements.get(ee)||customElements.define(ee,E)}We();
232
+ `,D=[{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:.004,exposureRangeSeconds:{min:.001,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:.011111111111111112,years:{from:1963,to:1988},apertureBlades:5},{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:.004,exposureRangeSeconds:{min:.001,max:3600},years:{from:1959},apertureBlades:6},{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:.004,exposureRangeSeconds:{min:.001,max:3600},apertureBlades:8,years:{from:1975}},{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},exposureSeconds:.008333333333333333,exposureRangeSeconds:{min:125e-6,max:10},years:{from:2007},apertureBlades:void 0},{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},exposureSeconds:.008333333333333333,exposureRangeSeconds:{min:125e-6,max:10},years:{from:2007},apertureBlades:void 0}];class v{static get default(){return D[0]}static byId(e){return D.find(t=>t.id===e)??this.default}static UNAIDED_FIELD_DEG=60;static UNAIDED_ASPECT=16/9;static fieldOfViewDeg(e){const t=e.frame;return t?2*Math.atan(t.heightMm/(2*t.focalLengthMm))*180/Math.PI:v.UNAIDED_FIELD_DEG}static aspectOf(e){const t=e.frame;return t?t.widthMm/t.heightMm:v.UNAIDED_ASPECT}static frameWidthPx(e,t){return Math.round(t*v.aspectOf(e))}static availableAt(e){return e===void 0?D:D.filter(t=>!t.years||e>=t.years.from&&(t.years.to===void 0||e<=t.years.to))}static fieldOfViewDegAt(e,t){const s=e.frame;if(!(!s||t<=0))return 2*Math.atan(s.heightMm/(2*t))*180/Math.PI}static focalLengthMmFor(e,t){const s=e.frame;if(!(!s||t<=0||t>=180))return s.heightMm/(2*Math.tan(t*Math.PI/360))}static bladesShowing(e,t){const s=t??e.fNumber;if(s===void 0||e.apertureBlades===void 0)return 0;const i=e.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(e){return e.flare??v.LENS_FLARE_ARTIFACTS}static starPointsOf(e){const t=e.apertureBlades;return t===void 0||t<3?0:t%2===0?t:t*2}}class b{static MOON_ANGULAR_WIDTH_DEG=.5237;static CANVAS_WIDTH_PX=640;static CANVAS_HEIGHT_PX=360;static angularWidthDeg(e){return 2*Math.atan(e.sizeM/(2*e.distanceM))*180/Math.PI}static inMoons(e){return e/b.MOON_ANGULAR_WIDTH_DEG}static lerpAngular(e,t,s){if(!(!e||!t))return{widthDeg:e.widthDeg+(t.widthDeg-e.widthDeg)*s,heightDeg:e.heightDeg+(t.heightDeg-e.heightDeg)*s}}static sizeMAt(e,t){return 2*e*Math.tan(t*Math.PI/360)}static distanceMAt(e,t){return e/(2*Math.tan(t*Math.PI/360))}}function ce(n,e,t){const{bounds:s}=n;return e>=s.x&&e<=s.x+s.width&&t>=s.y&&t<=s.y+s.height}function g(n,e,t){return n+(e-n)*t}function U(n){const e=/^#([0-9a-f]{6})$/i.exec(n);if(!e)return;const t=parseInt(e[1],16);return[t>>16&255,t>>8&255,t&255]}function le(n){return`#${n.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function ue(n,e,t){const s=U(n),i=U(e);return!s||!i?t<1?n:e:le([g(s[0],i[0],t),g(s[1],i[1],t),g(s[2],i[2],t)])}function de(n,e,t){const s={x:g(n.bounds.x,e.bounds.x,t),y:g(n.bounds.y,e.bounds.y,t),width:g(n.bounds.width,e.bounds.width,t),height:g(n.bounds.height,e.bounds.height,t)},i=g(n.angle,e.angle,t),r=g(n.transparency,e.transparency,t),a=g(n.haloScale,e.haloScale,t),o=g(n.blur??0,e.blur??0,t),h=g(n.brightness??0,e.brightness??0,t),c=ue(n.color,e.color,t),u=b.lerpAngular(n.angular,e.angular,t);return n.kind==="polygon"&&e.kind==="polygon"&&n.points.length===e.points.length?{...n,bounds:s,angle:i,transparency:r,haloScale:a,blur:o,brightness:h,color:c,angular:u,behindCloud:t<1?n.behindCloud:e.behindCloud,points:n.points.map((l,d)=>({x:g(l.x,e.points[d].x,t),y:g(l.y,e.points[d].y,t)}))}:{...t<1?n:e,bounds:s,angle:i,transparency:r,haloScale:a,blur:o,brightness:h,color:c,angular:u}}class O{keyframes=[];order=[];groups=[];addKeyframe(e,t){const s=this.findInsertIndex(e);if(this.keyframes[s]?.t===e){const i=new Set(t.map(r=>r.sourceId));this.keyframes[s]={t:e,shapes:[...this.keyframes[s].shapes.filter(r=>!i.has(r.sourceId)),...t]}}else this.keyframes.splice(s,0,{t:e,shapes:[...t]});for(const i of t)this.order.includes(i.sourceId)||this.order.push(i.sourceId)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}getKeyframeAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t];return s?.t===e?s:void 0}getShapeAt(e,t){return this.getKeyframeAt(e)?.shapes.find(s=>s.sourceId===t)?.shape}getLatestShapeAt(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===t);if(i)return i.shape}}getInterpolatedShapeAt(e,t){const s=this.findShapeAtOrBefore(e,t);if(s?.t===e)return s.shape;const i=this.findShapeAtOrAfter(e,t);return s?i?de(s.shape,i.shape,(e-s.t)/(i.t-s.t)):s.shape:i?.shape}findShapeAtOrBefore(e,t){let s=this.findInsertIndex(e);for(this.keyframes[s]?.t!==e&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===t);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}findShapeAtOrAfter(e,t){for(let s=this.findInsertIndex(e);s<this.keyframes.length;s++){const i=this.keyframes[s].shapes.find(r=>r.sourceId===t);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}hitTest(e,t,s,i){const r=this.sourceIds;for(let a=r.length-1;a>=0;a--){if(i?.has(r[a]))continue;const o=this.getInterpolatedShapeAt(e,r[a]);if(o&&ce(o,t,s))return{sourceId:r[a],shape:o}}}removeSource(e){for(let t=this.keyframes.length-1;t>=0;t--){const s=this.keyframes[t].shapes.filter(i=>i.sourceId!==e);s.length===0?this.keyframes.splice(t,1):s.length!==this.keyframes[t].shapes.length&&(this.keyframes[t]={t:this.keyframes[t].t,shapes:s})}this.order=this.order.filter(t=>t!==e),this.removeFromGroup(e)}group(e){if(!(e.length<2)){for(const t of e)this.removeFromGroup(t);this.groups.push([...e])}}ungroup(e){const t=this.groups.findIndex(s=>s.includes(e));t!==-1&&this.groups.splice(t,1)}groupMembers(e){return this.groups.find(t=>t.includes(e))}removeFromGroup(e){const t=this.groups.findIndex(i=>i.includes(e));if(t===-1)return;const s=this.groups[t].filter(i=>i!==e);s.length<2?this.groups.splice(t,1):this.groups[t]=s}bringToFront(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.push(e))}sendToBack(e){const t=this.order.indexOf(e);t!==-1&&(this.order.splice(t,1),this.order.unshift(e))}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(e){const t=new O;for(const s of e.keyframes)t.addKeyframe(s.t,s.shapes);if(e.order){const s=t.order.filter(i=>!e.order.includes(i));t.order=[...e.order,...s]}return e.groups&&(t.groups=e.groups.map(s=>s.filter(i=>t.order.includes(i))).filter(s=>s.length>=2)),t}}function fe(n,e,t){return Math.max(e,Math.min(t,n))}function me(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function T(n,e,t){return n+(e-n)*t}function pe(n,e,t){return{lat:n.lat===void 0||e.lat===void 0?void 0:T(n.lat,e.lat,t),lng:n.lng===void 0||e.lng===void 0?void 0:T(n.lng,e.lng,t),elevationM:T(n.elevationM,e.elevationM,t),headingDeg:n.headingDeg===void 0||e.headingDeg===void 0?void 0:me(n.headingDeg,e.headingDeg,t),pitchDeg:T(n.pitchDeg,e.pitchDeg,t),rollDeg:T(n.rollDeg??0,e.rollDeg??0,t),fovDeg:T(n.fovDeg,e.fovDeg,t),fNumber:n.fNumber,focusDistanceM:n.focusDistanceM}}class M{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,pose:t}:this.keyframes.splice(s,0,{t:e,pose:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}getLatestPoseAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].pose:void 0}getInterpolatedPoseAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.pose;const i=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?i?pe(s.pose,i.pose,fe((e-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(e){const t=new M;for(const s of e.keyframes)t.addKeyframe(s.t,s.pose);return t}}function w(n,e,t){return n+(e-n)*t}function ge(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function ye(n,e,t){return Math.max(e,Math.min(t,n))}function ve(n,e,t){return{cloudCover:w(n.cloudCover,e.cloudCover,t),cloudDarkness:w(n.cloudDarkness,e.cloudDarkness,t),highCloudCover:n.highCloudCover===void 0||e.highCloudCover===void 0?void 0:w(n.highCloudCover,e.highCloudCover,t),iceCrystalAlignment:n.iceCrystalAlignment===void 0||e.iceCrystalAlignment===void 0?void 0:w(n.iceCrystalAlignment,e.iceCrystalAlignment,t),lowerCloudCover:n.lowerCloudCover===void 0||e.lowerCloudCover===void 0?void 0:w(n.lowerCloudCover,e.lowerCloudCover,t),cloudBaseM:n.cloudBaseM===void 0||e.cloudBaseM===void 0?void 0:w(n.cloudBaseM,e.cloudBaseM,t),precipitationType:t<1?n.precipitationType:e.precipitationType,precipitationIntensity:w(n.precipitationIntensity,e.precipitationIntensity,t),windDirectionDeg:ge(n.windDirectionDeg,e.windDirectionDeg,t),windSpeed:w(n.windSpeed,e.windSpeed,t),storm:t<1?n.storm:e.storm}}class P{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,weather:t}:this.keyframes.splice(s,0,{t:e,weather:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}getLatestWeatherAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].weather:void 0}getInterpolatedWeatherAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.weather;const i=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return s?i?ve(s.weather,i.weather,ye((e-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(e){const t=new P;for(const s of e.keyframes)t.addKeyframe(s.t,s.weather);return t}}class A{keyframes=[];addKeyframe(e,t){const s=this.findInsertIndex(e);this.keyframes[s]?.t===e?this.keyframes[s]={t:e,sound:t}:this.keyframes.splice(s,0,{t:e,sound:t})}clear(){this.keyframes.length=0}removeKeyframeAt(e){const t=this.findInsertIndex(e);this.keyframes[t]?.t===e&&this.keyframes.splice(t,1)}getLatestSoundAt(e){let t=this.findInsertIndex(e);return this.keyframes[t]?.t!==e&&(t-=1),t>=0?this.keyframes[t].sound:void 0}getInterpolatedSoundAt(e){const t=this.findInsertIndex(e),s=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(s?.t===e)return s.sound;const i=this.keyframes[t]?.t===e?void 0:this.keyframes[t];if(!s)return i?.sound;if(!i)return s.sound;const r=(e-s.t)/(i.t-s.t);return this.lerp(s.sound,i.sound,Math.max(0,Math.min(1,r)))}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(e){const t=new A;for(const s of e.keyframes)t.addKeyframe(s.t,s.sound);return t}lerp(e,t,s){return{kind:s<1?e.kind:t.kind,volume:e.volume+(t.volume-e.volume)*s,pitchHz:e.pitchHz+(t.pitchHz-e.pitchHz)*s,src:s<1?e.src:t.src}}findInsertIndex(e){let t=0,s=this.keyframes.length;for(;t<s;){const i=t+s>>>1;this.keyframes[i].t<e?t=i+1:s=i}return t}}const xe=100,be={kind:"none",volume:0,pitchHz:xe};function H(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 G(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const we=["year","month","day","hour","minute"],Se=["day","hour","minute"];function V(n,e){return e.filter(t=>n[t]!==void 0).join(",")}function ke(n){if(n.durationSeconds!==void 0)return n.durationSeconds*1e3;if(!n.time||!n.endTime)return;const e=n.time.year!==void 0&&n.endTime.year!==void 0,t=e?we:Se,s=V(n.time,t);if(!(s===""||s!==V(n.endTime,t)))return e?H(n.endTime)-H(n.time):G(n.endTime)-G(n.time)}class N{constructor(e,t,s,i,r,a,o,h,c=[],u,l,d){this.event=e,this.timeline=t,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=r,this.witness=a,this.caseId=o,this.weather=h,this.decor=c,this.weatherSource=u,this.instrumentId=l,this.exposureSeconds=d}get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return v.byId(this.instrumentId)}static create(e,t,s){return new N({eventType:"sighting",time:e,place:t},new O,new M,new P,new A,s)}}const Te=0,Ee=0;function se(n,e){const t=n.witnessTrack.getInterpolatedPoseAt(e);if(t)return t;const s=n.event.place?.[0];if(s)return{lat:s.lat,lng:s.lng,elevationM:Te,headingDeg:void 0,pitchDeg:Ee,fovDeg:v.fieldOfViewDeg(n.instrument)}}function Ie(n,e){return n.soundTrack.getInterpolatedSoundAt(e)??be}class Me{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT);return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(e,s);i&&t.set(s,i)}this.onFrame(e,t)}}const ie=["nw","n","ne","e","se","s","sw","w"],Pe=24,C=8,Ae=3,De=8;function L(n,e,t){const s=n.x-e.x,i=n.y-e.y,r=Math.cos(t),a=Math.sin(t);return{x:e.x+s*r-i*a,y:e.y+s*a+i*r}}function E(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const Ce={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 y{static handlePointsFor(e){const{x:t,y:s,width:i,height:r}=e.bounds,a={nw:{x:t,y:s},n:{x:t+i/2,y:s},ne:{x:t+i,y:s},e:{x:t+i,y:s+r/2},se:{x:t+i,y:s+r},s:{x:t+i/2,y:s+r},sw:{x:t,y:s+r},w:{x:t,y:s+r/2},rotate:{x:t+i/2,y:s-Pe}},o=E(e.bounds),h={};for(const c of Object.keys(a))h[c]=L(a[c],o,e.angle);return h}static hitTestHandle(e,t,s=8,i=[...ie,"rotate"]){const r=y.handlePointsFor(e);for(const a of i)if(Math.hypot(t.x-r[a].x,t.y-r[a].y)<=s)return a}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(e,t){const i=((y.HANDLE_DIRECTION_DEG[e]+t*180/Math.PI)%180+180)%180/45,r=["ew","nwse","ns","nesw"];return r[Math.round(i)%r.length]}static resizeBounds(e,t,s,i){const r=E(e),a=L(i,r,-t),{x:o,y:h,width:c,height:u}=e,l=Ce[s];let d=o,f=h,m=o+c,p=h+u;return l.left&&(d=Math.min(a.x,m-C)),l.right&&(m=Math.max(a.x,d+C)),l.top&&(f=Math.min(a.y,p-C)),l.bottom&&(p=Math.max(a.y,f+C)),{x:d,y:f,width:m-d,height:p-f}}static resizeShape(e,t,s){if(t==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:i,height:r}=e.bounds,a=y.resizeBounds(e.bounds,e.angle,t,s);if(e.kind==="oval")return{...e,bounds:a};const o=i===0?1:a.width/i,h=r===0?1:a.height/r;return{...e,bounds:a,points:e.points.map(c=>({x:c.x*o,y:c.y*h}))}}static rotateShape(e,t){const s=E(e.bounds),i=Math.atan2(t.y-s.y,t.x-s.x)+Math.PI/2;return{...e,angle:i}}static groupBoundsFor(e){const t=Math.min(...e.map(a=>a.x)),s=Math.min(...e.map(a=>a.y)),i=Math.max(...e.map(a=>a.x+a.width)),r=Math.max(...e.map(a=>a.y+a.height));return{x:t,y:s,width:i-t,height:r-s}}static vertexPointsFor(e){const t=E(e.bounds);return e.points.map(s=>L({x:e.bounds.x+s.x,y:e.bounds.y+s.y},t,e.angle))}static hitTestVertex(e,t,s=De){const i=y.vertexPointsFor(e);let r,a=s;return i.forEach((o,h)=>{const c=Math.hypot(t.x-o.x,t.y-o.y);c<=a&&(a=c,r=h)}),r}static toLocalPoint(e,t){const s=E(e.bounds),i=L(t,s,-e.angle);return{x:i.x-e.bounds.x,y:i.y-e.bounds.y}}static moveVertex(e,t,s){const i=e.points.map((l,d)=>d===t?y.toLocalPoint(e,s):l),r=Math.min(...i.map(l=>l.x)),a=Math.min(...i.map(l=>l.y)),o=Math.max(...i.map(l=>l.x)),h=Math.max(...i.map(l=>l.y)),c={x:e.bounds.x+r,y:e.bounds.y+a,width:o-r,height:h-a},u=i.map(l=>({x:l.x-r,y:l.y-a}));return{...e,bounds:c,points:u}}static insertVertexNear(e,t){const s=y.toLocalPoint(e,t),{points:i}=e;let r=i.length-1,a=1/0;for(let h=0;h<i.length;h++){const c=i[h],u=i[(h+1)%i.length],l=y.distanceToSegment(s,c,u);l<a&&(a=l,r=h)}const o=[...i.slice(0,r+1),s,...i.slice(r+1)];return{...e,points:o}}static distanceToSegment(e,t,s){const i=s.x-t.x,r=s.y-t.y,a=i*i+r*r,o=a===0?0:Math.max(0,Math.min(1,((e.x-t.x)*i+(e.y-t.y)*r)/a)),h=t.x+o*i,c=t.y+o*r;return Math.hypot(e.x-h,e.y-c)}static deleteVertex(e,t){return e.points.length<=Ae?e:{...e,points:e.points.filter((s,i)=>i!==t)}}}const z=6,R=z/2,Le=20,Re=24,Be=9,$=6,_e=7,X=4;class Oe{constructor(e){this.ctx=e}starPoints=0;roll=0;clear(e,t){this.ctx.clearRect(0,0,e,t)}paintShape(e){this.ctx.save(),this.ctx.globalAlpha=1-e.transparency;const t=e.blur??0;t>0&&(this.ctx.filter=`blur(${(Re*t).toFixed(2)}px)`);const s=e.brightness??0;s>0&&this.paintDazzle(e,s),e.haloScale>0&&this.paintHalo(e),this.paintBase(e),this.ctx.restore(),e.selected&&this.paintSelectionHandles(e)}setStarPoints(e){this.starPoints=Math.max(0,Math.floor(e))}setRoll(e){this.roll=e}paintDazzle(e,t){const{x:s,y:i,width:r,height:a}=e.bounds,o=s+r/2,h=i+a/2,c=Math.max(r,a)/2,u=c*(1+Be*t);if(u<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const l=this.ctx.createRadialGradient(o,h,0,o,h,u);for(let d=0;d<=$;d++){const f=d/$,m=1/(1+24*f*f);this.ctx.globalAlpha=1,l.addColorStop(f,this.withAlpha(e.color,m*t*.55))}if(this.ctx.fillStyle=l,this.ctx.beginPath(),this.ctx.arc(o,h,u,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const d=c*(1+_e*t);this.ctx.lineWidth=Math.max(1,c*.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)*d,x=h+Math.sin(m)*d,S=this.ctx.createLinearGradient(o,h,p,x);S.addColorStop(0,this.withAlpha(e.color,t*.55)),S.addColorStop(.35,this.withAlpha(e.color,t*.22)),S.addColorStop(1,this.withAlpha(e.color,0)),this.ctx.strokeStyle=S,this.ctx.beginPath(),this.ctx.moveTo(o,h),this.ctx.lineTo(p,x),this.ctx.stroke()}}this.ctx.restore()}dazzledFill(e,t){if(t<=0)return e.color;const s=/^#([0-9a-f]{6})$/i.exec(e.color.trim());if(!s)return e.color;const i=Number.parseInt(s[1],16),r=a=>Math.round(a+(255-a)*t);return`rgb(${r(i>>16&255)}, ${r(i>>8&255)}, ${r(i&255)})`}withAlpha(e,t){const s=Math.max(0,Math.min(1,t)),i=/^#([0-9a-f]{6})$/i.exec(e.trim());if(!i)return e;const r=Number.parseInt(i[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${s.toFixed(3)})`}paintBase(e){if(this.ctx.fillStyle=this.dazzledFill(e,e.brightness??0),this.ctx.beginPath(),e.kind==="oval"){const{x:t,y:s,width:i,height:r}=e.bounds,a=i/2,o=r/2;this.ctx.ellipse(t+a,s+o,a,o,e.angle,0,2*Math.PI)}else{const{x:t,y:s,width:i,height:r}=e.bounds;this.ctx.save(),this.ctx.translate(t+i/2,s+r/2),this.ctx.rotate(e.angle),this.ctx.translate(-i/2,-r/2),e.points.forEach((a,o)=>{o===0?this.ctx.moveTo(a.x,a.y):this.ctx.lineTo(a.x,a.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=Le*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){this.paintHandleFrame(y.handlePointsFor(e),{includeRotate:!0}),e.kind==="polygon"&&this.paintVertexHandles(e)}paintVertexHandles(e){this.ctx.fillStyle="#39f";for(const t of y.vertexPointsFor(e))this.ctx.beginPath(),this.ctx.ellipse(t.x,t.y,X,X,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(e){this.paintOutline(y.handlePointsFor(e))}paintGroupHandles(e){this.paintHandleFrame(y.handlePointsFor({bounds:e,angle:0}),{includeRotate:!0})}paintOutline(e){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(e.nw.x,e.nw.y),this.ctx.lineTo(e.ne.x,e.ne.y),this.ctx.lineTo(e.se.x,e.se.y),this.ctx.lineTo(e.sw.x,e.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(e,{includeRotate:t}){this.paintOutline(e),this.ctx.fillStyle="lightgray";for(const s of ie){const i=e[s];this.ctx.fillRect(i.x-R,i.y-R,z,z)}t&&(this.ctx.beginPath(),this.ctx.moveTo(e.n.x,e.n.y),this.ctx.lineTo(e.rotate.x,e.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(e.rotate.x,e.rotate.y,R+1,R+1,0,0,2*Math.PI),this.ctx.fill())}}const q=.05,W=.08,K=2,J=3,Ne=14,Fe=.02;class He{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(e){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",e);return}}this.context.state==="suspended"&&this.context.resume()}setSound(e){if(this.requested=e,!this.context)return;const t=this.keyFor(e);if(!t){this.silence();return}if(this.voice?.key!==t){this.stopVoice();const s=++this.voiceToken;t.startsWith("src:")?this.buildRecordedVoice(e.src,t,s):this.voice=this.buildSynthesizedVoice(e,t)}this.applyTo(this.voice,e)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(e){if(!(e.volume<=0))return e.src?`src:${e.src}`:e.kind==="none"?void 0:e.kind}applyTo(e,t){const s=this.context;if(!e||!s)return;const i=s.currentTime;e.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,t.volume))*e.gainScale,i,q);for(const{param:r,ratio:a}of e.pitch)r.setTargetAtTime(Math.min(t.pitchHz*a,s.sampleRate/2-1),i,q)}buildSynthesizedVoice(e,t){const s=this.context;if(s)switch(e.kind){case"hum":return this.buildHum(s,t);case"whistle":return this.buildWhistle(s,t);case"rumble":return this.buildRumble(s,t);case"crackle":return this.buildCrackle(s,t);default:return}}buildHum(e,t){const s=e.createOscillator();s.type="sawtooth";const i=e.createOscillator();i.type="sawtooth";const r=e.createBiquadFilter();r.type="lowpass",r.Q.value=1;const a=e.createGain();return a.gain.value=0,s.connect(r),i.connect(r),r.connect(a).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.16,pitch:[{param:s.frequency,ratio:1},{param:i.frequency,ratio:1.006},{param:r.frequency,ratio:6}]}}buildWhistle(e,t){const s=e.createOscillator();s.type="sine";const i=e.createOscillator();i.type="sine",i.frequency.value=5;const r=e.createGain(),a=e.createGain();return a.gain.value=0,i.connect(r).connect(s.frequency),s.connect(a).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.22,pitch:[{param:s.frequency,ratio:1},{param:r.gain,ratio:.012}]}}buildRumble(e,t){const s=e.createBufferSource();s.buffer=this.brownNoise(e),s.loop=!0;const i=e.createBiquadFilter();i.type="lowpass",i.Q.value=.8;const r=e.createGain();return r.gain.value=0,s.connect(i).connect(r).connect(e.destination),s.start(),{key:t,nodes:[i,r],sources:[s],volumeParam:r.gain,gainScale:1.4,pitch:[{param:i.frequency,ratio:1}]}}buildCrackle(e,t){const s=e.createBufferSource();s.buffer=this.whiteNoise(e),s.loop=!0;const i=e.createBiquadFilter();i.type="bandpass",i.Q.value=1.2;const r=e.createGain();r.gain.value=0;const a=e.createBufferSource();a.buffer=this.crackleEnvelope(e),a.loop=!0;const o=e.createGain();return o.gain.value=0,a.connect(o).connect(r.gain),s.connect(i).connect(r).connect(e.destination),s.start(),a.start(),{key:t,nodes:[i,r,o],sources:[s,a],volumeParam:o.gain,gainScale:.9,pitch:[{param:i.frequency,ratio:4}]}}async buildRecordedVoice(e,t,s){const i=this.context;if(!i)return;let r;try{r=await this.getBuffer(e)}catch(h){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",h);return}if(s!==this.voiceToken||!this.context)return;const a=i.createBufferSource();a.buffer=r,a.loop=!0;const o=i.createGain();o.gain.value=0,a.connect(o).connect(i.destination),a.start(),this.voice={key:t,nodes:[o],sources:[a],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let s=this.buffers.get(e);return s||(s=fetch(e).then(i=>{if(!i.ok)throw new Error(`Audio fetch failed (${i.status}): ${e}`);return i.arrayBuffer()}).then(i=>t.decodeAudioData(i)),this.buffers.set(e,s)),s}whiteNoise(e){if(!this.noiseBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*K),e.sampleRate),s=t.getChannelData(0);for(let i=0;i<s.length;i++)s[i]=Math.random()*2-1;this.noiseBuffer=t}return this.noiseBuffer}brownNoise(e){const t=e.createBuffer(1,Math.floor(e.sampleRate*K),e.sampleRate),s=t.getChannelData(0);let i=0,r=0;for(let a=0;a<s.length;a++)i=(i+.02*(Math.random()*2-1))/1.02,s[a]=i,r=Math.max(r,Math.abs(i));if(r>0)for(let a=0;a<s.length;a++)s[a]/=r;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*J),e.sampleRate),s=t.getChannelData(0),i=Math.max(1,Math.floor(Fe*e.sampleRate)),r=Math.round(J*Ne);for(let a=0;a<r;a++){const o=Math.floor(Math.random()*s.length),h=.4+Math.random()*.6;for(let c=0;c<i&&o+c<s.length;c++)s[o+c]=Math.max(s[o+c],h*(1-c/i))}this.crackleBuffer=t}return this.crackleBuffer}stopVoice(){const e=this.voice,t=this.context;if(this.voice=void 0,!e||!t)return;const s=t.currentTime;e.volumeParam.cancelScheduledValues(s),e.volumeParam.setTargetAtTime(0,s,W/3);const i=s+W;for(const r of e.sources){r.onended=()=>{r.disconnect();for(const a of e.nodes)a.disconnect()};try{r.stop(i)}catch{r.disconnect();for(const a of e.nodes)a.disconnect()}}}}const F=Math.PI/180,B=180/Math.PI;class I{constructor(e,t,s){this.kind=e,this.canvasHeightPx=t,this.fovDeg=s;const i=s*F/2;this.focalPx=e==="equidistant"?t/2/i:t/2/Math.tan(i)}focalPx;static of(e,t,s){return new I(e.projection,t,s)}degToPx(e){const t=e*F;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*B:2*Math.atan(e/(2*this.focalPx))*B}angleDegToRadiusPx(e){const t=e*F;return this.kind==="equidistant"?this.focalPx*t:this.focalPx*Math.tan(t)}radiusPxToAngleDeg(e){return this.kind==="equidistant"?e/this.focalPx*B:Math.atan(e/this.focalPx)*B}ofBounds(e){return{widthDeg:this.pxToDeg(e.width),heightDeg:this.pxToDeg(e.height)}}toBoundsSize(e){return{width:this.degToPx(e.widthDeg),height:this.degToPx(e.heightDeg)}}widthPx(e){return this.degToPx(b.angularWidthDeg(e))}}class ne{static fovOf(e,t){return se(e,t)?.fovDeg??v.fieldOfViewDeg(e.instrument)}static toAngular(e){this.eachKeyframe(e,(t,s)=>({...t,angular:s.ofBounds(t.bounds)}))}static toBounds(e){this.eachKeyframe(e,(t,s)=>{if(!t.angular)return t;const{width:i,height:r}=s.toBoundsSize(t.angular),a={x:t.bounds.x+(t.bounds.width-i)/2,y:t.bounds.y+(t.bounds.height-r)/2,width:i,height:r};if(t.kind!=="polygon")return{...t,bounds:a};const o=t.bounds.width===0?1:i/t.bounds.width,h=t.bounds.height===0?1:r/t.bounds.height;return{...t,bounds:a,points:t.points.map(c=>({x:c.x*o,y:c.y*h}))}})}static reproject(e,t,s){const i=v.frameWidthPx(t,b.CANVAS_HEIGHT_PX)/2,r=v.frameWidthPx(e.instrument,b.CANVAS_HEIGHT_PX)/2,a=b.CANVAS_HEIGHT_PX/2;for(const o of[...e.timeline.allKeyframes]){const h=this.fovOf(e,o.t),c=s?.get(o.t)??h,u=I.of(t,b.CANVAS_HEIGHT_PX,c),l=I.of(e.instrument,b.CANVAS_HEIGHT_PX,h);e.timeline.addKeyframe(o.t,o.shapes.map(d=>{const{bounds:f}=d.shape,m=f.x+f.width/2-i,p=f.y+f.height/2-a,x=Math.hypot(m,p),S=x===0?1:l.angleDegToRadiusPx(u.radiusPxToAngleDeg(x))/x,ae={...f,x:r+m*S-f.width/2,y:a+p*S-f.height/2};return{...d,shape:{...d.shape,bounds:ae}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:s}=e;for(const i of[...s.allKeyframes]){const r=this.fovOf(e,i.t),a=I.of(e.instrument,b.CANVAS_HEIGHT_PX,r);s.addKeyframe(i.t,i.shapes.map(o=>({...o,shape:t(o.shape,a)})))}}}function ze(n){return ne.toAngular(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,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function Ue(n){const e=new N({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},O.fromJSON(n.timeline),n.witnessTrack?M.fromJSON(n.witnessTrack):new M,n.weatherTrack?P.fromJSON(n.weatherTrack):new P,n.soundTrack?A.fromJSON(n.soundTrack):new A,n.witness,n.caseId,n.weather,n.decor??[],n.weatherSource,n.instrument,n.exposureSeconds??n.witnessTrack?.keyframes.map(t=>t.pose.exposureSeconds).find(t=>t!==void 0));return ne.toBounds(e),e}function Ge(n,e){for(const t of n){const s=t.toLowerCase().split("-")[0];if(e.includes(s))return s}return"en"}const Ve="modulepreload",$e=function(n,e){return new URL(n,e).href},Z={},Y=function(e,t,s){let i=Promise.resolve();if(t&&t.length>0){let a=function(u){return Promise.all(u.map(l=>Promise.resolve(l).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const o=document.getElementsByTagName("link"),h=document.querySelector("meta[property=csp-nonce]"),c=h?.nonce||h?.getAttribute("nonce");i=a(t.map(u=>{if(u=$e(u,s),u in Z)return;Z[u]=!0;const l=u.endsWith(".css"),d=l?'[rel="stylesheet"]':"";if(!!s)for(let p=o.length-1;p>=0;p--){const x=o[p];if(x.href===u&&(!l||x.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${u}"]${d}`))return;const m=document.createElement("link");if(m.rel=l?"stylesheet":Ve,l||(m.as="script"),m.crossOrigin="",m.href=u,c&&m.setAttribute("nonce",c),document.head.appendChild(m),l)return new Promise((p,x)=>{m.addEventListener("load",p),m.addEventListener("error",()=>x(new Error(`Unable to preload CSS for ${u}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return i.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})},Xe=["en","fr"],qe={en:()=>Y(()=>Promise.resolve().then(()=>Ke),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>Y(()=>import("./UfoMessages_fr-Hq24iVzT.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function We(n){return qe[n]()}const re={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"},Ke=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:re},Symbol.toStringTag,{value:"Module"})),j=new Set;class k extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=N.create();sightingAudio=new He;soundPreview;player;loopEnabled=!0;highlightedSourceIds=new Set;occludedSourceIds=j;enableClickToPlay=!0;fullscreenTarget;messages=re;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>this.updateFullscreenButton();handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),s=t&&this.shapeAt(t.x,t.y);if(!s?.shape.title){this.tooltip.hidden=!0;return}this.tooltip.textContent=s.shape.title,this.tooltip.hidden=!1;const i=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-i.left+12}px`,this.tooltip.style.top=`${e.clientY-i.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${he}</style>${oe}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new Oe(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.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");for(const t of[this.timeStartLabel,this.timeEndLabel])t.addEventListener("click",()=>this.toggleTimeDisplay()),t.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.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",()=>{this.enableClickToPlay&&this.togglePlayPause()}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.sightingAudio.dispose()}attributeChangedCallback(e,t,s){e==="src"&&s&&s!==t&&this.isConnected&&this.loadFromSrc(s)}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return ze(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=Ue(e),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh()}get sighting(){return this.currentSighting}previewSound(e){this.soundPreview=e,this.sightingAudio.resume(),this.sightingAudio.setSound(e)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(e){this.toolbar.classList.toggle("hidden",!e)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(e){const t=new Set(e);t.size===this.highlightedSourceIds.size&&[...t].every(i=>this.highlightedSourceIds.has(i))||(this.highlightedSourceIds=t,this.refresh())}setOccludedSourceIds(e){e.size===this.occludedSourceIds.size&&[...e].every(s=>this.occludedSourceIds.has(s))||(this.occludedSourceIds=e,this.refresh())}hasVisibleShapeAt(e,t){return this.shapeAt(e,t)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.player.seek(this.player.time)}exposureInstants(e){const t=this.exposureTimes(e),s=1/t.length;return t.map(i=>({shapes:this.shapesAt(i),share:s}))}exposureTimes(e=this.currentTime){const t=(this.currentSighting.exposure??0)*1e3;if(t<k.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const s=Math.min(k.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t/k.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(e,t)/k.EXPOSURE_STEP_PX),r=Math.min(k.MAX_TRAVEL_STEPS,Math.max(s,i)),a=[];for(let o=0;o<r;o++)a.push(e+t*o/r);return a}travelPxOver(e,t){const s=this.shapesAt(e),i=this.shapesAt(e+t);let r=0;for(const[a,o]of s){const h=i.get(a);if(!h)continue;const c=h.bounds.x+h.bounds.width/2-(o.bounds.x+o.bounds.width/2),u=h.bounds.y+h.bounds.height/2-(o.bounds.y+o.bounds.height/2);r=Math.max(r,Math.hypot(c,u))}return r}shapeAt(e,t,s=this.occludedSourceIds){for(const i of this.exposureTimes()){const r=this.currentSighting.timeline.hitTest(i,e,t,s);if(r)return r}}shapesAt(e){const t=new Map;for(const s of this.currentSighting.timeline.sourceIds){const i=this.currentSighting.timeline.getInterpolatedShapeAt(e,s);i&&t.set(s,i)}return t}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const e=v.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===e)return;this.canvas.width=e,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${e} / ${this.canvas.height}`)}canvasPointFromEvent(e){const t=this.canvas.getBoundingClientRect();if(!(t.width===0||t.height===0))return{x:(e.clientX-t.left)/t.width*this.canvas.width,y:(e.clientY-t.top)/t.height*this.canvas.height}}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const s=this.playbackState!=="playing"?this.highlightedSourceIds:j;this.canvasRenderer.setStarPoints(v.starPointsOf(this.sighting.instrument)),this.canvasRenderer.setRoll((se(this.sighting,e)?.rollDeg??0)*Math.PI/180);const i=this.exposureInstants(e);for(const r of i)for(const[a,o]of r.shapes){if(this.occludedSourceIds.has(a))continue;const h=r.share,c=h===1?o:{...o,transparency:1-(1-o.transparency)*h};this.canvasRenderer.paintShape(c)}for(const[r,a]of i[0].shapes)this.occludedSourceIds.has(r)||!s.has(r)||(s.size===1?this.canvasRenderer.paintShape({...a,selected:!0}):this.canvasRenderer.paintMemberOutline(a));if(s.size>1){const r=y.groupBoundsFor([...t].filter(([a])=>s.has(a)).map(([,a])=>a.bounds));this.canvasRenderer.paintGroupHandles(r)}this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Ie(this.currentSighting,e))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}createPlayer(){const e=new Me(this.currentSighting.timeline,(t,s)=>this.onFrame(t,s));return e.loop=this.loopEnabled,e}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 e=this.player.playbackState==="playing";this.playPauseButton.textContent=e?"⏸":"▶";const t=this.player.seekableDuration>0;this.playPauseButton.disabled=!t;const s=t?e?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",e),this.fullscreenButton.classList.toggle("auto-hide",e)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}updateFullscreenButton(){const e=document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}async loadLocaleMessages(){const e=Ge(navigator.languages,Xe);e!=="en"&&this.applyMessages(await We(e))}updateTimeLabelTitles(){const e=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,t=this.canSwitchTimeDisplay?` — ${e}`:"";this.timeStartLabel.title=this.messages.currentPosition+t,this.timeEndLabel.title=this.messages.duration+t;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(e){this.messages=e,this.updateTimeLabelTitles(),this.loopButton.title=e.autoReplay,this.loopButton.setAttribute("aria-label",e.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton()}updateTimeLabels(){const e=this.currentSighting.event,t=ke(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?H(e.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(e){if(this.realDurationMs===void 0)return _(e);const t=this.currentSighting.timeline.duration,s=t>0&&e<=t?e/t*this.realDurationMs:e;return this.canShowClockTime?ee(Q(this.realStartMs+s)):_(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?_(this.currentSighting.timeline.duration):this.canShowClockTime?ee(Q(this.realStartMs+this.realDurationMs)):_(this.realDurationMs)}}function Q(n){const e=new Date(n);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function ee(n){if(n.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return n.second?`${e(n.hour)}:${e(n.minute??0)}:${e(n.second)}`:`${e(n.hour)}:${e(n.minute??0)}`}function _(n){const e=Math.round(n/1e3),t=Math.floor(e/60),s=e%60;return`${t}:${String(s).padStart(2,"0")}`}const te="rr0-ufo";function Je(){customElements.get(te)||customElements.define(te,k)}Je();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rr0/ufoathome",
3
3
  "type": "module",
4
- "version": "0.34.0",
4
+ "version": "0.36.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",
@@ -1 +0,0 @@
1
- const e={color:"Couleur",transparency:"Transparence",halo:"Halo",shapeTitle:"Nom",utcOffset:"Fuseau horaire",cloudBase:"Base des nuages",elevation:"Altitude",heightAboveGround:"Hauteur au-dessus du sol",duration:"Durée",placeName:"Lieu",latitude:"Latitude",longitude:"Longitude",heading:"Orientation",pitch:"Inclinaison",observationTime:"Début de l'observation",observationEndTime:"Fin de l'observation",witnessId:"ID témoin",witnessTitle:"Titre du témoin",witnessLastName:"Nom de famille du témoin",witnessFirstNames:"Prénoms du témoin",caseId:"ID de l'affaire",tags:"Mots-clés",cloudCover:"Couverture nuageuse",highCloudCover:"Nuages de glace (cirrus)",focalLength:"Focale",fieldOfView:"Champ de vision",aperture:"Diaphragme",exposure:"Temps de pose",focusDistance:"Mise au point à",iceCrystalAlignment:"Alignement des cristaux",cloudDarkness:"Obscurité des nuages",precipitationType:"Précipitations",precipitationNone:"Aucune",precipitationRain:"Pluie",precipitationSnow:"Neige",precipitationHail:"Grêle",precipitationIntensity:"Intensité",windDirection:"Direction du vent",windSpeed:"Force du vent",storm:"Orage",soundKind:"Son",soundNone:"Aucun (silencieux)",soundHum:"Bourdonnement",soundWhistle:"Sifflement",soundRumble:"Grondement",soundCrackle:"Crépitement",soundVolume:"Intensité sonore",soundPitch:"Hauteur",soundSrc:"Enregistrement",instrument:"Observé avec",decorAircraft:"Aéronef",decorAltitude:"Altitude",decorLights:"Feux",decorLightsNone:"aucun",decor:"Décor",decorBuilding:"Bâtiment",decorTree:"Arbre",decorStreetlight:"Lampadaire",decorVehicle:"Véhicule",decorWitness:"Autre témoin",decorEast:"Distance à l'est",decorNorth:"Distance au nord",decorHeading:"Orientation",decorLit:"Allumé",decorTitle:"Nom",decorFloors:"Étages",decorOccupiedFloor:"Étage occupé",decorWitnessSide:"Emplacement du témoin",decorWitnessSideNone:"Non présent",decorWindows:"Fenêtres",decorSideFront:"Avant",decorSideBehind:"Arrière",decorSideLeft:"Gauche",decorSideRight:"Droite",decorSideFrontLeft:"Avant-gauche",decorSideFrontRight:"Avant-droit",decorSideBehindLeft:"Arrière-gauche",decorSideBehindRight:"Arrière-droit"},i={...e,oval:"Ovale",polygon:"Polygone",addVertex:"Ajouter un sommet",deleteVertex:"Supprimer le sommet",notAPolygon:"Sélectionnez une seule forme polygonale",tooFewVertices:"Une forme a besoin d'au moins 3 points",shape:"Forme",objectSize:"Essayer une taille",objectDistance:"à une distance de",apparentSize:"soit {deg}° — {moons}× la Lune",utcOffsetPlaceholder:"d'après la longitude",objectSizePlaceholder:"largeur supposée",objectDistancePlaceholder:"distance supposée",realSizeBetween:"Largeur réelle : entre {min} et {max} m, d'après ce qu'il croise",realSizeAtLeast:"Largeur réelle : au moins {min} m, d'après ce qu'il passe derrière",realSizeAtMost:"Largeur réelle : au plus {max} m, d'après ce qu'il passe devant",realSizeUnknown:"Largeur réelle indéterminée : rien dans la scène ne croise sa ligne de visée",realSizeContradiction:"Contradiction : les passages déclarés ne peuvent pas être vrais ensemble",realDistanceHere:" — soit ici entre {min} et {max} m",addShape:"Ajouter une forme",deleteShape:"Supprimer la forme",play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",autoReplay:"Lecture automatique",group:"Grouper",ungroup:"Dégrouper",bringToFront:"Placer devant",sendToBack:"Placer derrière",contextMenuDelete:"Supprimer",confirmDeleteShape:"Supprimer {name} ? Cette action est irréversible.",confirmDeleteShapes:"Supprimer {count} formes ? Cette action est irréversible.",onlyOneShape:"Il n'y a qu'une seule forme",alreadyAtFront:"Cette forme est déjà la plus en avant",alreadyAtBack:"Cette forme est déjà la plus en arrière",needTwoShapesToGroup:"Sélectionnez au moins deux formes pour les grouper",notGrouped:"Cette forme ne fait pas partie d'un groupe",multipleShapesSelected:"Plusieurs formes sélectionnées — sélectionnez-en une seule pour modifier ceci",samplingRate:"Échantillonnage",currentPosition:"Position actuelle",switchToElapsed:"cliquer pour afficher la durée écoulée",switchToClockTime:"cliquer pour afficher l'heure de l'observation",durationPlaceholder:"durée de l'observation",durationImprecise:"Ces dates ne permettent pas de déterminer précisément une durée — saisissez-la manuellement.",export:"Enregistrer",importFile:"Charger un fichier JSON",importUrl:"Ou charger depuis une URL",importUrlPlaceholder:"https://…/sighting.json",importButton:"Charger",importError:"Impossible de charger cet enregistrement — vérifiez le fichier ou l'URL et réessayez.",record:"Enregistrer",stop:"Arrêter",according:"selon",sourceElevation:"Relief",sourceImagery:"Imagerie",placeNamePlaceholder:"Valensole, France",searchPlace:"Localiser",placeMatch:"Correspondances",placeSearching:"Recherche du lieu…",placeMatchFound:"lieu trouvé",placeMatchesFound:"lieux trouvés",placeNotFound:"Aucun lieu de ce nom",placeSearchFailed:"Recherche de lieu momentanément indisponible",utcOffsetImplausible:"Ce fuseau ne correspond pas à la longitude saisie, dont l'heure solaire est UTC{solar} — la scène et le relevé météo obéissent pourtant à ce qui est déclaré ici. Videz le champ pour le déduire de la longitude.",timeZoneManual:"décalage saisi",altitudeAboveSeaLevel:"Altitude du témoin au-dessus du niveau de la mer — le sol du lieu saisi en fixe le minimum : un témoin dans les Alpes n'est pas à 0 m.",groundAt:"sol à {m} m",headingPlaceholder:"inconnu",edtfInvalid:"Date/heure EDTF invalide (ex. 1965-07-01T05:00, 2025-06?, 2025~).",edtfPlaceholder:"AAAA-MM-JJThh:mm[?~%] ou hh:mm",observationTimeHint:"EDTF — ex. 1965-07-01T05:00, 2025-06? (incertain), 2025~ (approximatif), ou juste 05:00 si la date est inconnue",observationEndTimeHint:"EDTF — ex. 1965-07-01T05:10, 2025-06? (incertain), 2025~ (approximatif), ou juste 05:10 si la date est inconnue",presetsGroupLabel:"Forme de l'ovni",witnessDirName:"Répertoire du témoin",description:"Description",tagsPlaceholder:"séparés par des virgules",weather:"Météo",shapeGroup:"Forme",soundGroup:"Son",temporalGroup:"Date et heure",locationGroup:"Lieu",observationGroup:"Observation",witnessGroup:"Témoin",weatherInferred:"D'après les relevés",weatherInferredTitle:"La météo n'est pas un témoignage : c'est un fait mesurable, relevé pour la date, l'heure et le lieu de l'observation. Décochez pour saisir les conditions rapportées par le témoin, qui priment alors sur le relevé.",weatherLookingUp:"Recherche du relevé…",weatherNeedsDateAndPlace:"Date complète et lieu requis pour retrouver le relevé",weatherNoRecord:"Aucun relevé pour cette date et ce lieu",weatherLookupFailed:"Relevés météo momentanément inaccessibles",instrumentOutOfPeriod:"{name} — hors de son époque",unitMillimetres:"mm",unitDegrees:"°",soundSrcPlaceholder:"URL d'un enregistrement réel",skyLine:"Ciel : {parts}",skyDetails:"Voir le détail des relevés",skyDetailsHide:"Masquer le détail des relevés",skyShowerActive:"{name} — radiant à {altitude}° de hauteur, {bearing} — environ {rate}/h sous ce ciel, sur un fond sporadique de {sporadic}/h",skyShowerBelowHorizon:"{name} est actif, mais son radiant est sous l'horizon — il n'a rien pu produire",skyNothingActive:"aucune pluie de météores active à cette date — un fond sporadique d'environ {sporadic}/h",showMeteor:"Montrer un météore",skyComet:"La {name}, magnitude {magnitude}, à {altitude}° de hauteur {bearing}, queue longue de {tail}°",skyCometNoTail:"La {name}, magnitude {magnitude}, à {altitude}° de hauteur {bearing}",skyCometBelowHorizon:"La {name} était là, magnitude {magnitude}, mais sous l'horizon — personne ici ne l'a vue",skyCometInDaylight:"La {name}, magnitude {magnitude}, à {altitude}° de hauteur {bearing} — mais à {elongation}° du Soleil seulement",showComet:"Montrer la comète",skyOpticsPossible:"nuages de glace suffisants, autour {source}, pour {forms}",skyOpticsAlignment:" — tout sauf les halos exige des cristaux tombant à plat, ici supposés alignés à {alignment} %, qu'aucun relevé ne donne",skyOpticsHalo22:"un halo à {angle}°",skyOpticsHalo46:"un halo à {angle}°",skyOpticsParhelia:"des parhélies à {angle}° de part et d'autre",skyOpticsTangentArc:"un arc tangent au sommet",skyOpticsParhelicCircle:"un cercle parhélique blanc faisant le tour du ciel à sa hauteur",skyOpticsCircumzenithal:"un arc circumzénithal à {angle}° de hauteur",skyOpticsCircumhorizontal:"un arc circumhorizontal très au-dessous",skyOpticsPillar:"un pilier dressé au-dessus",skyOpticsNoIce:"aucun nuage de glace : ni halo ni parhélie",skyOpticsHidden:"des nuages de glace en altitude, mais une couche plus basse les cache",skyOpticsSun:"du Soleil",skyOpticsMoon:"de la Lune",skyBowPossible:"pluie et soleil suffisants pour {forms}",skyBowMoon:"pluie sous une Lune éclairée à {lit} %, de quoi former {forms} — un arc-en-ciel lunaire, que l'œil voit blanc",skyBowPrimary:"un arc de {radius}° de rayon, dont le sommet montait à {top}°",skyBowSecondary:"un second, plus pâle, à {radius}°, aux couleurs inversées",skyBowSourceTooHigh:"pluie, mais l'astre à {altitude}° de hauteur : aucun arc ne pouvait dépasser l'horizon, ils se tiennent à {radius}° du point opposé",skyBowHidden:"pluie, mais une couche continue entre elle et la lumière : aucun arc",skyBowNoSource:"pluie, et rien d'allumé pour en tirer un arc",skySatellitesLit:"l'ombre de la Terre montait à {height} km au-dessus du témoin, l'orbite basse était donc encore éclairée — {count} objets suivis étaient en orbite ce mois-là",skySatellitesLitWith:"l'ombre de la Terre montait à {height} km au-dessus du témoin, l'orbite basse était donc encore éclairée — {count} objets suivis étaient en orbite ce mois-là, dont {eras}",skySatellitesShadowed:"l'ombre de la Terre montait à {height} km au-dessus du témoin : sur les {count} objets suivis en orbite ce mois-là, rien en orbite basse n'était éclairé",skySatellitesNotYet:"rien n'était encore en orbite",skySatellitesDaylight:"tout ce qui est en orbite était éclairé, comme toujours de jour, et {eras} pouvaient l'emporter même sur ce ciel",skySatellitesDaylightOne:"tout ce qui est en orbite était éclairé, comme toujours de jour, et {eras} pouvait l'emporter même sur ce ciel",skyUnknown:"indéterminable sans date ni lieu",lookAtDecor:"Regarder cet élément",addDecor:"Ajouter",deleteDecor:"Supprimer l'élément de décor",decorSightingUrl:"URL de l'enregistrement du témoin",viewTestimony:"Voir le témoignage",noWitnessRecording:"Aucune URL d'enregistrement définie pour ce témoin",masks:"Masque",addWitness:"Ajouter un témoin"};export{i as ufoRecorderMessages_fr};
@@ -1 +0,0 @@
1
- const e={color:"Couleur",transparency:"Transparence",halo:"Halo",shapeTitle:"Nom",utcOffset:"Fuseau horaire",cloudBase:"Base des nuages",elevation:"Altitude",heightAboveGround:"Hauteur au-dessus du sol",duration:"Durée",placeName:"Lieu",latitude:"Latitude",longitude:"Longitude",heading:"Orientation",pitch:"Inclinaison",observationTime:"Début de l'observation",observationEndTime:"Fin de l'observation",witnessId:"ID témoin",witnessTitle:"Titre du témoin",witnessLastName:"Nom de famille du témoin",witnessFirstNames:"Prénoms du témoin",caseId:"ID de l'affaire",tags:"Mots-clés",cloudCover:"Couverture nuageuse",highCloudCover:"Nuages de glace (cirrus)",focalLength:"Focale",fieldOfView:"Champ de vision",aperture:"Diaphragme",exposure:"Temps de pose",focusDistance:"Mise au point à",iceCrystalAlignment:"Alignement des cristaux",cloudDarkness:"Obscurité des nuages",precipitationType:"Précipitations",precipitationNone:"Aucune",precipitationRain:"Pluie",precipitationSnow:"Neige",precipitationHail:"Grêle",precipitationIntensity:"Intensité",windDirection:"Direction du vent",windSpeed:"Force du vent",storm:"Orage",soundKind:"Son",soundNone:"Aucun (silencieux)",soundHum:"Bourdonnement",soundWhistle:"Sifflement",soundRumble:"Grondement",soundCrackle:"Crépitement",soundVolume:"Intensité sonore",soundPitch:"Hauteur",soundSrc:"Enregistrement",instrument:"Observé avec",decorAircraft:"Aéronef",decorAltitude:"Altitude",decorLights:"Feux",decorLightsNone:"aucun",decor:"Décor",decorBuilding:"Bâtiment",decorTree:"Arbre",decorStreetlight:"Lampadaire",decorVehicle:"Véhicule",decorWitness:"Autre témoin",decorEast:"Distance à l'est",decorNorth:"Distance au nord",decorHeading:"Orientation",decorLit:"Allumé",decorTitle:"Nom",decorFloors:"Étages",decorOccupiedFloor:"Étage occupé",decorWitnessSide:"Emplacement du témoin",decorWitnessSideNone:"Non présent",decorWindows:"Fenêtres",decorSideFront:"Avant",decorSideBehind:"Arrière",decorSideLeft:"Gauche",decorSideRight:"Droite",decorSideFrontLeft:"Avant-gauche",decorSideFrontRight:"Avant-droit",decorSideBehindLeft:"Arrière-gauche",decorSideBehindRight:"Arrière-droit"},i={...e,testimonyBy:"Témoignage de",about:"À propos",close:"Fermer",observation:"Observation",date:"Date",location:"Lieu",case:"Dossier",description:"Description",credits:"Crédits",editThisObservation:"Éditer cette observation",embed:"Intégrer",embedReplay:"Relecture",embedEdit:"Édition",embedCopy:"Copier",embedCopied:"Copié",showLabels:"Afficher ce qu'elle indique",hideLabels:"Masquer ce qu'elle indique"};export{i as eyewitnessMessages_fr};