@rr0/ufoathome 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +21 -8
- package/dist-embed-scene/rr0-scene.mjs +215 -214
- package/dist-embed-scene/stars-mag7.5-9-BLV2tFmZ.bin +0 -0
- package/dist-embed-scene/stars-mag7.5-B43R6Wvg.bin +0 -0
- package/dist-embed-sighting/rr0-sighting.mjs +220 -219
- package/dist-embed-sighting/stars-mag7.5-9-BLV2tFmZ.bin +0 -0
- package/dist-embed-sighting/stars-mag7.5-B43R6Wvg.bin +0 -0
- package/dist-embed-sighting-editor/{SightingEditorMessages_fr-dupjSir8.js → SightingEditorMessages_fr-h_TFOqJZ.js} +1 -1
- package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +218 -217
- package/dist-embed-sighting-editor/stars-mag7.5-9-BLV2tFmZ.bin +0 -0
- package/dist-embed-sighting-editor/stars-mag7.5-B43R6Wvg.bin +0 -0
- package/dist-embed-ufo/rr0-ufo.mjs +1 -1
- package/package.json +1 -1
- package/dist-embed-scene/stars-mag7.5-DDr9QasD.bin +0 -0
- package/dist-embed-sighting/stars-mag7.5-DDr9QasD.bin +0 -0
- package/dist-embed-sighting-editor/stars-mag7.5-DDr9QasD.bin +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -311,4 +311,4 @@ input[type=range] {
|
|
|
311
311
|
pointer-events: none;
|
|
312
312
|
white-space: nowrap;
|
|
313
313
|
}
|
|
314
|
-
`;class T extends Error{constructor(e,t,s){super(`${e}: ${t}${s===void 0?"":` (HTTP ${s})`}`),this.kind=e,this.url=t,this.status=s,this.name="SightingFetchError"}}class de{static async json(e){const t=new URL(e,location.href);if(location.protocol==="https:"&&t.protocol==="http:")throw new T("mixed-content",e);let s;try{s=await fetch(e)}catch{throw await this.diagnose(e,t)}if(!s.ok)throw new T("status",e,s.status);try{return await s.json()}catch{throw new T("malformed",e)}}static async diagnose(e,t){if(t.origin===location.origin)return new T("unreachable",e);try{return await fetch(e,{mode:"no-cors"}),new T("cors",e)}catch{return new T("unreachable",e)}}}const C=[{id:"eye",name:{en:"Naked eye",fr:"Œil nu"},flare:0,projection:"equidistant"},{id:"rectilinear-lens",name:{en:"Camera, unknown device",fr:"Appareil, modèle inconnu"},projection:"rectilinear",fNumber:8,fNumberRange:{min:2,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:8},apertureBlades:6},{id:"instamatic-126",name:{en:"Instamatic, 126 film",fr:"Instamatic, film 126"},projection:"rectilinear",frame:{widthMm:28,heightMm:28,focalLengthMm:43},fNumber:11,exposureSeconds:1/90,years:{from:1963,to:1988},apertureBlades:5},{id:"slr-35mm-50",name:{en:"35 mm SLR, 50 mm lens",fr:"Reflex 35 mm, objectif 50 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:50},fNumber:8,fNumberRange:{min:2,max:16},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},years:{from:1959},apertureBlades:6},{id:"slr-35mm-zoom",name:{en:"35 mm SLR, 70-210 mm zoom",fr:"Reflex 35 mm, zoom 70-210 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:135,focalRangeMm:{minMm:70,maxMm:210}},fNumber:8,fNumberRange:{min:4,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},apertureBlades:8,years:{from:1975}},{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:1/120,exposureRangeSeconds:{min:1/8e3,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:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0}];class v{static get default(){return C[0]}static byId(e){return C.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?C:C.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 ue(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 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 fe(n){return`#${n.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function me(n,e,t){const s=G(n),i=G(e);return!s||!i?t<1?n:e:fe([g(s[0],i[0],t),g(s[1],i[1],t),g(s[2],i[2],t)])}function pe(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),o=g(n.haloScale,e.haloScale,t),a=g(n.blur??0,e.blur??0,t),l=g(n.brightness??0,e.brightness??0,t),h=me(n.color,e.color,t),d=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:o,blur:a,brightness:l,color:h,angular:d,behindCloud:t<1?n.behindCloud:e.behindCloud,points:n.points.map((c,u)=>({x:g(c.x,e.points[u].x,t),y:g(c.y,e.points[u].y,t)}))}:{...t<1?n:e,bounds:s,angle:i,transparency:r,haloScale:o,blur:a,brightness:l,color:h,angular:d}}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?pe(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 o=r.length-1;o>=0;o--){if(i?.has(r[o]))continue;const a=this.getInterpolatedShapeAt(e,r[o]);if(a&&ue(a,t,s))return{sourceId:r[o],shape:a}}}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 ge(n,e,t){return Math.max(e,Math.min(t,n))}function ye(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function E(n,e,t){return n+(e-n)*t}function ve(n,e,t){return{lat:n.lat===void 0||e.lat===void 0?void 0:E(n.lat,e.lat,t),lng:n.lng===void 0||e.lng===void 0?void 0:E(n.lng,e.lng,t),elevationM:E(n.elevationM,e.elevationM,t),headingDeg:n.headingDeg===void 0||e.headingDeg===void 0?void 0:ye(n.headingDeg,e.headingDeg,t),pitchDeg:E(n.pitchDeg,e.pitchDeg,t),rollDeg:E(n.rollDeg??0,e.rollDeg??0,t),fovDeg:E(n.fovDeg,e.fovDeg,t),fNumber:n.fNumber,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?ve(s.pose,i.pose,ge((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 be(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function we(n,e,t){return Math.max(e,Math.min(t,n))}function xe(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:be(n.windDirectionDeg,e.windDirectionDeg,t),windSpeed:x(n.windSpeed,e.windSpeed,t),storm:t<1?n.storm:e.storm}}class A{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?xe(s.weather,i.weather,we((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 A;for(const s of e.keyframes)t.addKeyframe(s.t,s.weather);return t}}class D{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 D;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 ke=100,Se={kind:"none",volume:0,pitchHz:ke};function z(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 V(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const Te=["year","month","day","hour","minute"],Ee=["day","hour","minute"];function X(n,e){return e.filter(t=>n[t]!==void 0).join(",")}function Me(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?Te:Ee,s=X(n.time,t);if(!(s===""||s!==X(n.endTime,t)))return e?z(n.endTime)-z(n.time):V(n.endTime)-V(n.time)}class N{constructor(e,t,s,i,r,o,a,l,h=[],d,c,u,f=[]){this.event=e,this.timeline=t,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=r,this.witness=o,this.caseId=a,this.weather=l,this.decor=h,this.weatherSource=d,this.instrumentId=c,this.exposureSeconds=u,this.milestones=f}get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return v.byId(this.instrumentId)}static create(e,t,s){return new N({eventType:"sighting",time:e,place:t},new O,new I,new A,new D,s)}}const Pe=0,Ie=0;function ne(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:Pe,headingDeg:void 0,pitchDeg:Ie,fovDeg:v.fieldOfViewDeg(n.instrument)}}function Ae(n,e){return n.soundTrack.getInterpolatedSoundAt(e)??Se}class De{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;onEnded;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(e,s);i&&t.set(s,i)}this.onFrame(e,t)}}const re=["nw","n","ne","e","se","s","sw","w"],Ce=24,L=8,Le=3,Be=8;function B(n,e,t){const s=n.x-e.x,i=n.y-e.y,r=Math.cos(t),o=Math.sin(t);return{x:e.x+s*r-i*o,y:e.y+s*o+i*r}}function M(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const Fe={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,o={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-Ce}},a=M(e.bounds),l={};for(const h of Object.keys(o))l[h]=B(o[h],a,e.angle);return l}static hitTestHandle(e,t,s=8,i=[...re,"rotate"]){const r=y.handlePointsFor(e);for(const o of i)if(Math.hypot(t.x-r[o].x,t.y-r[o].y)<=s)return o}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=M(e),o=B(i,r,-t),{x:a,y:l,width:h,height:d}=e,c=Fe[s];let u=a,f=l,m=a+h,p=l+d;return c.left&&(u=Math.min(o.x,m-L)),c.right&&(m=Math.max(o.x,u+L)),c.top&&(f=Math.min(o.y,p-L)),c.bottom&&(p=Math.max(o.y,f+L)),{x:u,y:f,width:m-u,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,o=y.resizeBounds(e.bounds,e.angle,t,s);if(e.kind==="oval")return{...e,bounds:o};const a=i===0?1:o.width/i,l=r===0?1:o.height/r;return{...e,bounds:o,points:e.points.map(h=>({x:h.x*a,y:h.y*l}))}}static rotateShape(e,t){const s=M(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(o=>o.x)),s=Math.min(...e.map(o=>o.y)),i=Math.max(...e.map(o=>o.x+o.width)),r=Math.max(...e.map(o=>o.y+o.height));return{x:t,y:s,width:i-t,height:r-s}}static vertexPointsFor(e){const t=M(e.bounds);return e.points.map(s=>B({x:e.bounds.x+s.x,y:e.bounds.y+s.y},t,e.angle))}static hitTestVertex(e,t,s=Be){const i=y.vertexPointsFor(e);let r,o=s;return i.forEach((a,l)=>{const h=Math.hypot(t.x-a.x,t.y-a.y);h<=o&&(o=h,r=l)}),r}static toLocalPoint(e,t){const s=M(e.bounds),i=B(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((c,u)=>u===t?y.toLocalPoint(e,s):c),r=Math.min(...i.map(c=>c.x)),o=Math.min(...i.map(c=>c.y)),a=Math.max(...i.map(c=>c.x)),l=Math.max(...i.map(c=>c.y)),h={x:e.bounds.x+r,y:e.bounds.y+o,width:a-r,height:l-o},d=i.map(c=>({x:c.x-r,y:c.y-o}));return{...e,bounds:h,points:d}}static insertVertexNear(e,t){const s=y.toLocalPoint(e,t),{points:i}=e;let r=i.length-1,o=1/0;for(let l=0;l<i.length;l++){const h=i[l],d=i[(l+1)%i.length],c=y.distanceToSegment(s,h,d);c<o&&(o=c,r=l)}const a=[...i.slice(0,r+1),s,...i.slice(r+1)];return{...e,points:a}}static distanceToSegment(e,t,s){const i=s.x-t.x,r=s.y-t.y,o=i*i+r*r,a=o===0?0:Math.max(0,Math.min(1,((e.x-t.x)*i+(e.y-t.y)*r)/o)),l=t.x+a*i,h=t.y+a*r;return Math.hypot(e.x-l,e.y-h)}static deleteVertex(e,t){return e.points.length<=Le?e:{...e,points:e.points.filter((s,i)=>i!==t)}}}const U=6,F=U/2,Re=20,_e=24,Oe=9,q=6,Ne=7,He=6,K=4;class ze{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(${(_e*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:o}=e.bounds,a=s+r/2,l=i+o/2,h=Math.max(r,o)/2,d=Math.max(h*(1+Oe*t),He*t);if(d<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const c=this.ctx.createRadialGradient(a,l,0,a,l,d);for(let u=0;u<=q;u++){const f=u/q,m=1/(1+24*f*f);this.ctx.globalAlpha=1,c.addColorStop(f,this.withAlpha(e.color,m*t*.55))}if(this.ctx.fillStyle=c,this.ctx.beginPath(),this.ctx.arc(a,l,d,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const u=h*(1+Ne*t);this.ctx.lineWidth=Math.max(1,h*.12),this.ctx.lineCap="round";for(let f=0;f<this.starPoints;f++){const m=f/this.starPoints*Math.PI*2+this.roll,p=a+Math.cos(m)*u,b=l+Math.sin(m)*u,k=this.ctx.createLinearGradient(a,l,p,b);k.addColorStop(0,this.withAlpha(e.color,t*.55)),k.addColorStop(.35,this.withAlpha(e.color,t*.22)),k.addColorStop(1,this.withAlpha(e.color,0)),this.ctx.strokeStyle=k,this.ctx.beginPath(),this.ctx.moveTo(a,l),this.ctx.lineTo(p,b),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=o=>Math.round(o+(255-o)*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,o=i/2,a=r/2;this.ctx.ellipse(t+o,s+a,o,a,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((o,a)=>{a===0?this.ctx.moveTo(o.x,o.y):this.ctx.lineTo(o.x,o.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=Re*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,K,K,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 re){const i=e[s];this.ctx.fillRect(i.x-F,i.y-F,U,U)}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,F+1,F+1,0,0,2*Math.PI),this.ctx.fill())}}const W=.05,Z=.08,J=2,Y=3,Ue=14,$e=.02;class Ge{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:o}of e.pitch)r.setTargetAtTime(Math.min(t.pitchHz*o,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 o=e.createGain();return o.gain.value=0,s.connect(r),i.connect(r),r.connect(o).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,o],sources:[s,i],volumeParam:o.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(),o=e.createGain();return o.gain.value=0,i.connect(r).connect(s.frequency),s.connect(o).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,o],sources:[s,i],volumeParam:o.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 o=e.createBufferSource();o.buffer=this.crackleEnvelope(e),o.loop=!0;const a=e.createGain();return a.gain.value=0,o.connect(a).connect(r.gain),s.connect(i).connect(r).connect(e.destination),s.start(),o.start(),{key:t,nodes:[i,r,a],sources:[s,o],volumeParam:a.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(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);return}if(s!==this.voiceToken||!this.context)return;const o=i.createBufferSource();o.buffer=r,o.loop=!0;const a=i.createGain();a.gain.value=0,o.connect(a).connect(i.destination),o.start(),this.voice={key:t,nodes:[a],sources:[o],volumeParam:a.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let 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*J),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*J),e.sampleRate),s=t.getChannelData(0);let i=0,r=0;for(let o=0;o<s.length;o++)i=(i+.02*(Math.random()*2-1))/1.02,s[o]=i,r=Math.max(r,Math.abs(i));if(r>0)for(let o=0;o<s.length;o++)s[o]/=r;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*Y),e.sampleRate),s=t.getChannelData(0),i=Math.max(1,Math.floor($e*e.sampleRate)),r=Math.round(Y*Ue);for(let o=0;o<r;o++){const a=Math.floor(Math.random()*s.length),l=.4+Math.random()*.6;for(let h=0;h<i&&a+h<s.length;h++)s[a+h]=Math.max(s[a+h],l*(1-h/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,Z/3);const i=s+Z;for(const r of e.sources){r.onended=()=>{r.disconnect();for(const o of e.nodes)o.disconnect()};try{r.stop(i)}catch{r.disconnect();for(const o of e.nodes)o.disconnect()}}}}function $(n){return[...n].sort((e,t)=>e.t-t.t)}function Ve(n,e){let t;for(const s of $(n)){if(s.t>e)break;t=s}return t}const H=Math.PI/180,R=180/Math.PI;class P{constructor(e,t,s){this.kind=e,this.canvasHeightPx=t,this.fovDeg=s;const i=s*H/2;this.focalPx=e==="equidistant"?t/2/i:t/2/Math.tan(i)}focalPx;static of(e,t,s){return new P(e.projection,t,s)}degToPx(e){const t=e*H;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*R:2*Math.atan(e/(2*this.focalPx))*R}angleDegToRadiusPx(e){const t=e*H;return this.kind==="equidistant"?this.focalPx*t:this.focalPx*Math.tan(t)}radiusPxToAngleDeg(e){return this.kind==="equidistant"?e/this.focalPx*R:Math.atan(e/this.focalPx)*R}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 oe{static fovOf(e,t){return ne(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),o={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:o};const a=t.bounds.width===0?1:i/t.bounds.width,l=t.bounds.height===0?1:r/t.bounds.height;return{...t,bounds:o,points:t.points.map(h=>({x:h.x*a,y:h.y*l}))}})}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,o=w.CANVAS_HEIGHT_PX/2;for(const a of[...e.timeline.allKeyframes]){const l=this.fovOf(e,a.t),h=s?.get(a.t)??l,d=P.of(t,w.CANVAS_HEIGHT_PX,h),c=P.of(e.instrument,w.CANVAS_HEIGHT_PX,l);e.timeline.addKeyframe(a.t,a.shapes.map(u=>{const{bounds:f}=u.shape,m=f.x+f.width/2-i,p=f.y+f.height/2-o,b=Math.hypot(m,p),k=b===0?1:c.angleDegToRadiusPx(d.radiusPxToAngleDeg(b))/b,le={...f,x:r+m*k-f.width/2,y:o+p*k-f.height/2};return{...u,shape:{...u.shape,bounds:le}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:s}=e;for(const i of[...s.allKeyframes]){const r=this.fovOf(e,i.t),o=P.of(e.instrument,w.CANVAS_HEIGHT_PX,r);s.addKeyframe(i.t,i.shapes.map(a=>({...a,shape:t(a.shape,o)})))}}}function Xe(n){return oe.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,milestones:n.milestones.length>0?n.milestones:void 0,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function qe(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?I.fromJSON(n.witnessTrack):new I,n.weatherTrack?A.fromJSON(n.weatherTrack):new A,n.soundTrack?D.fromJSON(n.soundTrack):new D,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),$(n.milestones??[]));return oe.toBounds(e),e}function Ke(n,e){for(const t of n){const s=t.toLowerCase().split("-")[0];if(e.includes(s))return s}return"en"}class We{static preferencesFor(e){const t=navigator.languages??[],s=e.closest("[lang]")?.getAttribute("lang")?.trim();return s?[s,...t]:t}}const Ze="modulepreload",Je=function(n,e){return new URL(n,e).href},j={},Q=function(e,t,s){let i=Promise.resolve();if(t&&t.length>0){let o=function(d){return Promise.all(d.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const a=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),h=l?.nonce||l?.getAttribute("nonce");i=o(t.map(d=>{if(d=Je(d,s),d in j)return;j[d]=!0;const c=d.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!s)for(let p=a.length-1;p>=0;p--){const b=a[p];if(b.href===d&&(!c||b.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${d}"]${u}`))return;const m=document.createElement("link");if(m.rel=c?"stylesheet":Ze,c||(m.as="script"),m.crossOrigin="",m.href=d,h&&m.setAttribute("nonce",h),document.head.appendChild(m),c)return new Promise((p,b)=>{m.addEventListener("load",p),m.addEventListener("error",()=>b(new Error(`Unable to preload CSS for ${d}`)))})}))}function r(o){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=o,window.dispatchEvent(a),!a.defaultPrevented)throw o}return i.then(o=>{for(const a of o||[])a.status==="rejected"&&r(a.reason);return e().catch(r)})},Ye=["en","fr"],je={en:()=>Q(()=>Promise.resolve().then(()=>et),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>Q(()=>import("./UfoMessages_fr-Hq24iVzT.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function Qe(n){return je[n]()}const ae={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"},et=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:ae},Symbol.toStringTag,{value:"Module"})),ee=new Set;class S extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;currentSighting=N.create();sightingAudio=new Ge;soundPreview;player;loopEnabled=!0;playbackBeforeClick;highlightedSourceIds=new Set;occludedSourceIds=ee;enableClickToPlay=!0;fullscreenTarget;messages=ae;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>this.updateFullscreenButton();simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};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>${ce}</style>${he}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new ze(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.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");for(const 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",t=>{this.enableClickToPlay&&(t.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause())}),this.canvas.addEventListener("dblclick",t=>{this.enableClickToPlay&&(t.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),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.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,s){e==="src"&&s&&s!==t&&this.isConnected&&this.loadFromSrc(s)}async loadFromSrc(e){this.sightingData=await de.json(e)}get sightingData(){return Xe(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=qe(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}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(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.refreshMilestoneMarks(),this.player.seek(this.player.time)}refreshMilestoneMarks(){const e=this.player.seekableDuration,t=e>0?$(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(s=>{const i=document.createElement("button");i.type="button",i.className="milestone-mark",i.style.left=`${Math.min(Math.max(s.t/e,0),1)*100}%`;const r=s.note?`${s.label} — ${s.note}`:s.label;return i.title=r,i.setAttribute("aria-label",r),i.addEventListener("click",()=>this.player.seek(s.t)),i}))}showMilestoneAt(e){const t=this.sighting.milestones.length>0?Ve(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,!t)return;const s=document.createElement("b");s.textContent=t.label;const i=t.note?` — ${t.note}`:"";this.milestoneCaption.replaceChildren(s,document.createTextNode(i))}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<S.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const s=Math.min(S.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t/S.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(e,t)/S.EXPOSURE_STEP_PX),r=Math.min(S.MAX_TRAVEL_STEPS,Math.max(s,i)),o=[];for(let a=0;a<r;a++)o.push(e+t*a/r);return o}travelPxOver(e,t){const s=this.shapesAt(e),i=this.shapesAt(e+t);let r=0;for(const[o,a]of s){const l=i.get(o);if(!l)continue;const h=l.bounds.x+l.bounds.width/2-(a.bounds.x+a.bounds.width/2),d=l.bounds.y+l.bounds.height/2-(a.bounds.y+a.bounds.height/2);r=Math.max(r,Math.hypot(h,d))}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:ee;this.canvasRenderer.setStarPoints(v.starPointsOf(this.sighting.instrument)),this.canvasRenderer.setRoll((ne(this.sighting,e)?.rollDeg??0)*Math.PI/180);const i=this.exposureInstants(e);for(const r of i)for(const[o,a]of r.shapes){if(this.occludedSourceIds.has(o))continue;const l=r.share,h=l===1?a:{...a,transparency:1-(1-a.transparency)*l};this.canvasRenderer.paintShape(h)}for(const[r,o]of i[0].shapes)this.occludedSourceIds.has(r)||!s.has(r)||(s.size===1?this.canvasRenderer.paintShape({...o,selected:!0}):this.canvasRenderer.paintMemberOutline(o));if(s.size>1){const r=y.groupBoundsFor([...t].filter(([o])=>s.has(o)).map(([,o])=>o.bounds));this.canvasRenderer.paintGroupHandles(r)}this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Ae(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 De(this.currentSighting.timeline,(t,s)=>this.onFrame(t,s));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},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}restorePlayback(){const e=this.playbackBeforeClick;e&&(this.currentTime=e.time,e.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}enterSimulatedFullscreen(){const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=e.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const t=e.style;t.setProperty("position","fixed"),t.setProperty("inset","0"),t.setProperty("margin","0"),t.setProperty("max-width","none"),t.setProperty("max-height","none"),t.setProperty("aspect-ratio","auto"),t.setProperty("z-index","2147483647"),t.setProperty("background","#000"),t.setProperty("width","100vw"),t.setProperty("width","100dvw"),t.setProperty("height","100vh"),t.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?e.setAttribute("style",this.styleBeforeSimulatedFullscreen):e.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}updateFullscreenButton(){const e=this.simulatedFullscreen||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=Ke(We.preferencesFor(this),Ye);e!=="en"&&this.applyMessages(await Qe(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=Me(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?z(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?se(te(this.realStartMs+s)):_(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?_(this.currentSighting.timeline.duration):this.canShowClockTime?se(te(this.realStartMs+this.realDurationMs)):_(this.realDurationMs)}}function te(n){const e=new Date(n);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function se(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 ie="rr0-ufo";function tt(){customElements.get(ie)||customElements.define(ie,S)}tt();
|
|
314
|
+
`;class T extends Error{constructor(e,t,s){super(`${e}: ${t}${s===void 0?"":` (HTTP ${s})`}`),this.kind=e,this.url=t,this.status=s,this.name="SightingFetchError"}}class de{static async json(e){const t=new URL(e,location.href);if(location.protocol==="https:"&&t.protocol==="http:")throw new T("mixed-content",e);let s;try{s=await fetch(e)}catch{throw await this.diagnose(e,t)}if(!s.ok)throw new T("status",e,s.status);try{return await s.json()}catch{throw new T("malformed",e)}}static async diagnose(e,t){if(t.origin===location.origin)return new T("unreachable",e);try{return await fetch(e,{mode:"no-cors"}),new T("cors",e)}catch{return new T("unreachable",e)}}}const C=[{id:"eye",name:{en:"Naked eye",fr:"Œil nu"},flare:0,projection:"equidistant"},{id:"rectilinear-lens",name:{en:"Camera, unknown device",fr:"Appareil, modèle inconnu"},projection:"rectilinear",fNumber:8,fNumberRange:{min:2,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:8},apertureBlades:6},{id:"instamatic-126",name:{en:"Instamatic, 126 film",fr:"Instamatic, film 126"},projection:"rectilinear",frame:{widthMm:28,heightMm:28,focalLengthMm:43},fNumber:11,exposureSeconds:1/90,years:{from:1963,to:1988},apertureBlades:5,detailUm:20},{id:"slr-35mm-50",name:{en:"35 mm SLR, 50 mm lens",fr:"Reflex 35 mm, objectif 50 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:50},fNumber:8,fNumberRange:{min:2,max:16},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},years:{from:1959},apertureBlades:6,detailUm:20},{id:"slr-35mm-zoom",name:{en:"35 mm SLR, 70-210 mm zoom",fr:"Reflex 35 mm, zoom 70-210 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:135,focalRangeMm:{minMm:70,maxMm:210}},fNumber:8,fNumberRange:{min:4,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},apertureBlades:8,years:{from:1975},detailUm:20},{id:"phone-landscape",name:{en:"Phone, held sideways",fr:"Téléphone, tenu couché"},projection:"rectilinear",frame:{widthMm:7.6,heightMm:5.7,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4},{id:"phone-portrait",name:{en:"Phone, held upright",fr:"Téléphone, tenu debout"},projection:"rectilinear",frame:{widthMm:5.7,heightMm:7.6,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4}];class v{static get default(){return C[0]}static byId(e){return C.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?C:C.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 ue(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 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 fe(n){return`#${n.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function me(n,e,t){const s=G(n),i=G(e);return!s||!i?t<1?n:e:fe([g(s[0],i[0],t),g(s[1],i[1],t),g(s[2],i[2],t)])}function pe(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),o=g(n.haloScale,e.haloScale,t),a=g(n.blur??0,e.blur??0,t),l=g(n.brightness??0,e.brightness??0,t),h=me(n.color,e.color,t),d=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:o,blur:a,brightness:l,color:h,angular:d,behindCloud:t<1?n.behindCloud:e.behindCloud,points:n.points.map((c,u)=>({x:g(c.x,e.points[u].x,t),y:g(c.y,e.points[u].y,t)}))}:{...t<1?n:e,bounds:s,angle:i,transparency:r,haloScale:o,blur:a,brightness:l,color:h,angular:d}}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?pe(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 o=r.length-1;o>=0;o--){if(i?.has(r[o]))continue;const a=this.getInterpolatedShapeAt(e,r[o]);if(a&&ue(a,t,s))return{sourceId:r[o],shape:a}}}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 ge(n,e,t){return Math.max(e,Math.min(t,n))}function ye(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function E(n,e,t){return n+(e-n)*t}function ve(n,e,t){return{lat:n.lat===void 0||e.lat===void 0?void 0:E(n.lat,e.lat,t),lng:n.lng===void 0||e.lng===void 0?void 0:E(n.lng,e.lng,t),elevationM:E(n.elevationM,e.elevationM,t),headingDeg:n.headingDeg===void 0||e.headingDeg===void 0?void 0:ye(n.headingDeg,e.headingDeg,t),pitchDeg:E(n.pitchDeg,e.pitchDeg,t),rollDeg:E(n.rollDeg??0,e.rollDeg??0,t),fovDeg:E(n.fovDeg,e.fovDeg,t),fNumber:n.fNumber,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?ve(s.pose,i.pose,ge((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 be(n,e,t){const s=((e-n)%360+540)%360-180;return((n+s*t)%360+360)%360}function we(n,e,t){return Math.max(e,Math.min(t,n))}function xe(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:be(n.windDirectionDeg,e.windDirectionDeg,t),windSpeed:x(n.windSpeed,e.windSpeed,t),storm:t<1?n.storm:e.storm}}class A{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?xe(s.weather,i.weather,we((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 A;for(const s of e.keyframes)t.addKeyframe(s.t,s.weather);return t}}class D{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 D;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 ke=100,Se={kind:"none",volume:0,pitchHz:ke};function z(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 V(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const Te=["year","month","day","hour","minute"],Ee=["day","hour","minute"];function X(n,e){return e.filter(t=>n[t]!==void 0).join(",")}function Me(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?Te:Ee,s=X(n.time,t);if(!(s===""||s!==X(n.endTime,t)))return e?z(n.endTime)-z(n.time):V(n.endTime)-V(n.time)}class N{constructor(e,t,s,i,r,o,a,l,h=[],d,c,u,f=[]){this.event=e,this.timeline=t,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=r,this.witness=o,this.caseId=a,this.weather=l,this.decor=h,this.weatherSource=d,this.instrumentId=c,this.exposureSeconds=u,this.milestones=f}get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return v.byId(this.instrumentId)}static create(e,t,s){return new N({eventType:"sighting",time:e,place:t},new O,new I,new A,new D,s)}}const Pe=0,Ie=0;function ne(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:Pe,headingDeg:void 0,pitchDeg:Ie,fovDeg:v.fieldOfViewDeg(n.instrument)}}function Ae(n,e){return n.soundTrack.getInterpolatedSoundAt(e)??Se}class De{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;onEnded;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(e,s);i&&t.set(s,i)}this.onFrame(e,t)}}const re=["nw","n","ne","e","se","s","sw","w"],Ce=24,L=8,Le=3,Be=8;function B(n,e,t){const s=n.x-e.x,i=n.y-e.y,r=Math.cos(t),o=Math.sin(t);return{x:e.x+s*r-i*o,y:e.y+s*o+i*r}}function M(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const Fe={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,o={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-Ce}},a=M(e.bounds),l={};for(const h of Object.keys(o))l[h]=B(o[h],a,e.angle);return l}static hitTestHandle(e,t,s=8,i=[...re,"rotate"]){const r=y.handlePointsFor(e);for(const o of i)if(Math.hypot(t.x-r[o].x,t.y-r[o].y)<=s)return o}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=M(e),o=B(i,r,-t),{x:a,y:l,width:h,height:d}=e,c=Fe[s];let u=a,f=l,m=a+h,p=l+d;return c.left&&(u=Math.min(o.x,m-L)),c.right&&(m=Math.max(o.x,u+L)),c.top&&(f=Math.min(o.y,p-L)),c.bottom&&(p=Math.max(o.y,f+L)),{x:u,y:f,width:m-u,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,o=y.resizeBounds(e.bounds,e.angle,t,s);if(e.kind==="oval")return{...e,bounds:o};const a=i===0?1:o.width/i,l=r===0?1:o.height/r;return{...e,bounds:o,points:e.points.map(h=>({x:h.x*a,y:h.y*l}))}}static rotateShape(e,t){const s=M(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(o=>o.x)),s=Math.min(...e.map(o=>o.y)),i=Math.max(...e.map(o=>o.x+o.width)),r=Math.max(...e.map(o=>o.y+o.height));return{x:t,y:s,width:i-t,height:r-s}}static vertexPointsFor(e){const t=M(e.bounds);return e.points.map(s=>B({x:e.bounds.x+s.x,y:e.bounds.y+s.y},t,e.angle))}static hitTestVertex(e,t,s=Be){const i=y.vertexPointsFor(e);let r,o=s;return i.forEach((a,l)=>{const h=Math.hypot(t.x-a.x,t.y-a.y);h<=o&&(o=h,r=l)}),r}static toLocalPoint(e,t){const s=M(e.bounds),i=B(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((c,u)=>u===t?y.toLocalPoint(e,s):c),r=Math.min(...i.map(c=>c.x)),o=Math.min(...i.map(c=>c.y)),a=Math.max(...i.map(c=>c.x)),l=Math.max(...i.map(c=>c.y)),h={x:e.bounds.x+r,y:e.bounds.y+o,width:a-r,height:l-o},d=i.map(c=>({x:c.x-r,y:c.y-o}));return{...e,bounds:h,points:d}}static insertVertexNear(e,t){const s=y.toLocalPoint(e,t),{points:i}=e;let r=i.length-1,o=1/0;for(let l=0;l<i.length;l++){const h=i[l],d=i[(l+1)%i.length],c=y.distanceToSegment(s,h,d);c<o&&(o=c,r=l)}const a=[...i.slice(0,r+1),s,...i.slice(r+1)];return{...e,points:a}}static distanceToSegment(e,t,s){const i=s.x-t.x,r=s.y-t.y,o=i*i+r*r,a=o===0?0:Math.max(0,Math.min(1,((e.x-t.x)*i+(e.y-t.y)*r)/o)),l=t.x+a*i,h=t.y+a*r;return Math.hypot(e.x-l,e.y-h)}static deleteVertex(e,t){return e.points.length<=Le?e:{...e,points:e.points.filter((s,i)=>i!==t)}}}const U=6,F=U/2,Re=20,_e=24,Oe=9,q=6,Ne=7,He=6,K=4;class ze{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(${(_e*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:o}=e.bounds,a=s+r/2,l=i+o/2,h=Math.max(r,o)/2,d=Math.max(h*(1+Oe*t),He*t);if(d<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const c=this.ctx.createRadialGradient(a,l,0,a,l,d);for(let u=0;u<=q;u++){const f=u/q,m=1/(1+24*f*f);this.ctx.globalAlpha=1,c.addColorStop(f,this.withAlpha(e.color,m*t*.55))}if(this.ctx.fillStyle=c,this.ctx.beginPath(),this.ctx.arc(a,l,d,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const u=h*(1+Ne*t);this.ctx.lineWidth=Math.max(1,h*.12),this.ctx.lineCap="round";for(let f=0;f<this.starPoints;f++){const m=f/this.starPoints*Math.PI*2+this.roll,p=a+Math.cos(m)*u,b=l+Math.sin(m)*u,k=this.ctx.createLinearGradient(a,l,p,b);k.addColorStop(0,this.withAlpha(e.color,t*.55)),k.addColorStop(.35,this.withAlpha(e.color,t*.22)),k.addColorStop(1,this.withAlpha(e.color,0)),this.ctx.strokeStyle=k,this.ctx.beginPath(),this.ctx.moveTo(a,l),this.ctx.lineTo(p,b),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=o=>Math.round(o+(255-o)*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,o=i/2,a=r/2;this.ctx.ellipse(t+o,s+a,o,a,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((o,a)=>{a===0?this.ctx.moveTo(o.x,o.y):this.ctx.lineTo(o.x,o.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=Re*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,K,K,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 re){const i=e[s];this.ctx.fillRect(i.x-F,i.y-F,U,U)}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,F+1,F+1,0,0,2*Math.PI),this.ctx.fill())}}const W=.05,Z=.08,J=2,Y=3,Ue=14,$e=.02;class Ge{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:o}of e.pitch)r.setTargetAtTime(Math.min(t.pitchHz*o,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 o=e.createGain();return o.gain.value=0,s.connect(r),i.connect(r),r.connect(o).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,o],sources:[s,i],volumeParam:o.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(),o=e.createGain();return o.gain.value=0,i.connect(r).connect(s.frequency),s.connect(o).connect(e.destination),s.start(),i.start(),{key:t,nodes:[r,o],sources:[s,i],volumeParam:o.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 o=e.createBufferSource();o.buffer=this.crackleEnvelope(e),o.loop=!0;const a=e.createGain();return a.gain.value=0,o.connect(a).connect(r.gain),s.connect(i).connect(r).connect(e.destination),s.start(),o.start(),{key:t,nodes:[i,r,a],sources:[s,o],volumeParam:a.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(l){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",l);return}if(s!==this.voiceToken||!this.context)return;const o=i.createBufferSource();o.buffer=r,o.loop=!0;const a=i.createGain();a.gain.value=0,o.connect(a).connect(i.destination),o.start(),this.voice={key:t,nodes:[a],sources:[o],volumeParam:a.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(e){const t=this.context;if(!t)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let 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*J),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*J),e.sampleRate),s=t.getChannelData(0);let i=0,r=0;for(let o=0;o<s.length;o++)i=(i+.02*(Math.random()*2-1))/1.02,s[o]=i,r=Math.max(r,Math.abs(i));if(r>0)for(let o=0;o<s.length;o++)s[o]/=r;return t}crackleEnvelope(e){if(!this.crackleBuffer){const t=e.createBuffer(1,Math.floor(e.sampleRate*Y),e.sampleRate),s=t.getChannelData(0),i=Math.max(1,Math.floor($e*e.sampleRate)),r=Math.round(Y*Ue);for(let o=0;o<r;o++){const a=Math.floor(Math.random()*s.length),l=.4+Math.random()*.6;for(let h=0;h<i&&a+h<s.length;h++)s[a+h]=Math.max(s[a+h],l*(1-h/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,Z/3);const i=s+Z;for(const r of e.sources){r.onended=()=>{r.disconnect();for(const o of e.nodes)o.disconnect()};try{r.stop(i)}catch{r.disconnect();for(const o of e.nodes)o.disconnect()}}}}function $(n){return[...n].sort((e,t)=>e.t-t.t)}function Ve(n,e){let t;for(const s of $(n)){if(s.t>e)break;t=s}return t}const H=Math.PI/180,R=180/Math.PI;class P{constructor(e,t,s){this.kind=e,this.canvasHeightPx=t,this.fovDeg=s;const i=s*H/2;this.focalPx=e==="equidistant"?t/2/i:t/2/Math.tan(i)}focalPx;static of(e,t,s){return new P(e.projection,t,s)}degToPx(e){const t=e*H;return this.kind==="equidistant"?this.focalPx*t:2*this.focalPx*Math.tan(t/2)}pxToDeg(e){return this.kind==="equidistant"?e/this.focalPx*R:2*Math.atan(e/(2*this.focalPx))*R}angleDegToRadiusPx(e){const t=e*H;return this.kind==="equidistant"?this.focalPx*t:this.focalPx*Math.tan(t)}radiusPxToAngleDeg(e){return this.kind==="equidistant"?e/this.focalPx*R:Math.atan(e/this.focalPx)*R}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 oe{static fovOf(e,t){return ne(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),o={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:o};const a=t.bounds.width===0?1:i/t.bounds.width,l=t.bounds.height===0?1:r/t.bounds.height;return{...t,bounds:o,points:t.points.map(h=>({x:h.x*a,y:h.y*l}))}})}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,o=w.CANVAS_HEIGHT_PX/2;for(const a of[...e.timeline.allKeyframes]){const l=this.fovOf(e,a.t),h=s?.get(a.t)??l,d=P.of(t,w.CANVAS_HEIGHT_PX,h),c=P.of(e.instrument,w.CANVAS_HEIGHT_PX,l);e.timeline.addKeyframe(a.t,a.shapes.map(u=>{const{bounds:f}=u.shape,m=f.x+f.width/2-i,p=f.y+f.height/2-o,b=Math.hypot(m,p),k=b===0?1:c.angleDegToRadiusPx(d.radiusPxToAngleDeg(b))/b,le={...f,x:r+m*k-f.width/2,y:o+p*k-f.height/2};return{...u,shape:{...u.shape,bounds:le}}}))}this.toBounds(e)}static eachKeyframe(e,t){const{timeline:s}=e;for(const i of[...s.allKeyframes]){const r=this.fovOf(e,i.t),o=P.of(e.instrument,w.CANVAS_HEIGHT_PX,r);s.addKeyframe(i.t,i.shapes.map(a=>({...a,shape:t(a.shape,o)})))}}}function Xe(n){return oe.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,milestones:n.milestones.length>0?n.milestones:void 0,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function qe(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?I.fromJSON(n.witnessTrack):new I,n.weatherTrack?A.fromJSON(n.weatherTrack):new A,n.soundTrack?D.fromJSON(n.soundTrack):new D,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),$(n.milestones??[]));return oe.toBounds(e),e}function Ke(n,e){for(const t of n){const s=t.toLowerCase().split("-")[0];if(e.includes(s))return s}return"en"}class We{static preferencesFor(e){const t=navigator.languages??[],s=e.closest("[lang]")?.getAttribute("lang")?.trim();return s?[s,...t]:t}}const Ze="modulepreload",Je=function(n,e){return new URL(n,e).href},j={},Q=function(e,t,s){let i=Promise.resolve();if(t&&t.length>0){let o=function(d){return Promise.all(d.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const a=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),h=l?.nonce||l?.getAttribute("nonce");i=o(t.map(d=>{if(d=Je(d,s),d in j)return;j[d]=!0;const c=d.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!s)for(let p=a.length-1;p>=0;p--){const b=a[p];if(b.href===d&&(!c||b.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${d}"]${u}`))return;const m=document.createElement("link");if(m.rel=c?"stylesheet":Ze,c||(m.as="script"),m.crossOrigin="",m.href=d,h&&m.setAttribute("nonce",h),document.head.appendChild(m),c)return new Promise((p,b)=>{m.addEventListener("load",p),m.addEventListener("error",()=>b(new Error(`Unable to preload CSS for ${d}`)))})}))}function r(o){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=o,window.dispatchEvent(a),!a.defaultPrevented)throw o}return i.then(o=>{for(const a of o||[])a.status==="rejected"&&r(a.reason);return e().catch(r)})},Ye=["en","fr"],je={en:()=>Q(()=>Promise.resolve().then(()=>et),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>Q(()=>import("./UfoMessages_fr-Hq24iVzT.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function Qe(n){return je[n]()}const ae={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"},et=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:ae},Symbol.toStringTag,{value:"Module"})),ee=new Set;class S extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;currentSighting=N.create();sightingAudio=new Ge;soundPreview;player;loopEnabled=!0;playbackBeforeClick;highlightedSourceIds=new Set;occludedSourceIds=ee;enableClickToPlay=!0;fullscreenTarget;messages=ae;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>this.updateFullscreenButton();simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=e=>{e.key==="Escape"&&this.exitSimulatedFullscreen()};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>${ce}</style>${he}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new ze(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.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");for(const 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",t=>{this.enableClickToPlay&&(t.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause())}),this.canvas.addEventListener("dblclick",t=>{this.enableClickToPlay&&(t.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),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.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(e,t,s){e==="src"&&s&&s!==t&&this.isConnected&&this.loadFromSrc(s)}async loadFromSrc(e){this.sightingData=await de.json(e)}get sightingData(){return Xe(this.currentSighting)}set sightingData(e){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=qe(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}set autoReplayEnabled(e){e!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(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.refreshMilestoneMarks(),this.player.seek(this.player.time)}refreshMilestoneMarks(){const e=this.player.seekableDuration,t=e>0?$(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...t.map(s=>{const i=document.createElement("button");i.type="button",i.className="milestone-mark",i.style.left=`${Math.min(Math.max(s.t/e,0),1)*100}%`;const r=s.note?`${s.label} — ${s.note}`:s.label;return i.title=r,i.setAttribute("aria-label",r),i.addEventListener("click",()=>this.player.seek(s.t)),i}))}showMilestoneAt(e){const t=this.sighting.milestones.length>0?Ve(this.sighting.milestones,e):void 0;if(this.milestoneCaption.hidden=t===void 0,!t)return;const s=document.createElement("b");s.textContent=t.label;const i=t.note?` — ${t.note}`:"";this.milestoneCaption.replaceChildren(s,document.createTextNode(i))}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<S.SHORTEST_VISIBLE_EXPOSURE_MS)return[e];const s=Math.min(S.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(t/S.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(e,t)/S.EXPOSURE_STEP_PX),r=Math.min(S.MAX_TRAVEL_STEPS,Math.max(s,i)),o=[];for(let a=0;a<r;a++)o.push(e+t*a/r);return o}travelPxOver(e,t){const s=this.shapesAt(e),i=this.shapesAt(e+t);let r=0;for(const[o,a]of s){const l=i.get(o);if(!l)continue;const h=l.bounds.x+l.bounds.width/2-(a.bounds.x+a.bounds.width/2),d=l.bounds.y+l.bounds.height/2-(a.bounds.y+a.bounds.height/2);r=Math.max(r,Math.hypot(h,d))}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:ee;this.canvasRenderer.setStarPoints(v.starPointsOf(this.sighting.instrument)),this.canvasRenderer.setRoll((ne(this.sighting,e)?.rollDeg??0)*Math.PI/180);const i=this.exposureInstants(e);for(const r of i)for(const[o,a]of r.shapes){if(this.occludedSourceIds.has(o))continue;const l=r.share,h=l===1?a:{...a,transparency:1-(1-a.transparency)*l};this.canvasRenderer.paintShape(h)}for(const[r,o]of i[0].shapes)this.occludedSourceIds.has(r)||!s.has(r)||(s.size===1?this.canvasRenderer.paintShape({...o,selected:!0}):this.canvasRenderer.paintMemberOutline(o));if(s.size>1){const r=y.groupBoundsFor([...t].filter(([o])=>s.has(o)).map(([,o])=>o.bounds));this.canvasRenderer.paintGroupHandles(r)}this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.showMilestoneAt(e),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Ae(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 De(this.currentSighting.timeline,(t,s)=>this.onFrame(t,s));return e.loop=this.loopEnabled,e.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},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}restorePlayback(){const e=this.playbackBeforeClick;e&&(this.currentTime=e.time,e.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}enterSimulatedFullscreen(){const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=e.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const t=e.style;t.setProperty("position","fixed"),t.setProperty("inset","0"),t.setProperty("margin","0"),t.setProperty("max-width","none"),t.setProperty("max-height","none"),t.setProperty("aspect-ratio","auto"),t.setProperty("z-index","2147483647"),t.setProperty("background","#000"),t.setProperty("width","100vw"),t.setProperty("width","100dvw"),t.setProperty("height","100vh"),t.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const e=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?e.setAttribute("style",this.styleBeforeSimulatedFullscreen):e.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}updateFullscreenButton(){const e=this.simulatedFullscreen||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=Ke(We.preferencesFor(this),Ye);e!=="en"&&this.applyMessages(await Qe(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=Me(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?z(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?se(te(this.realStartMs+s)):_(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?_(this.currentSighting.timeline.duration):this.canShowClockTime?se(te(this.realStartMs+this.realDurationMs)):_(this.realDurationMs)}}function te(n){const e=new Date(n);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function se(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 ie="rr0-ufo";function tt(){customElements.get(ie)||customElements.define(ie,S)}tt();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rr0/ufoathome",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.45.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",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|