@rr0/ufoathome 0.16.0 → 0.17.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.
@@ -699,7 +699,7 @@ input[type=range] {
699
699
  pointer-events: none;
700
700
  white-space: nowrap;
701
701
  }
702
- `;class ps{static MOON_ANGULAR_WIDTH_DEG=.5237;static angularWidthDeg(e){return 2*Math.atan(e.sizeM/(2*e.distanceM))*180/Math.PI}static widthPx(e,t,n){return t*e.sizeM/(2*e.distanceM*Math.tan(n*Math.PI/360))}static pxToDeg(e,t,n){return 2*Math.atan(e*Math.tan(n*Math.PI/360)/t)*180/Math.PI}static inMoons(e){return e/ps.MOON_ANGULAR_WIDTH_DEG}static lerp(e,t,n){if(!(!e||!t))return{sizeM:e.sizeM+(t.sizeM-e.sizeM)*n,distanceM:e.distanceM+(t.distanceM-e.distanceM)*n}}}function P0(i,e="#39ff14"){return{kind:"oval",bounds:i,color:e,angle:0,transparency:0,haloScale:0,selected:!1}}function L0(i,e,t="#39ff14"){return{kind:"polygon",bounds:i,points:e,color:t,angle:0,transparency:0,haloScale:0,selected:!1}}function N0(i,e="#39ff14"){const{width:t,height:n}=i;return L0(i,[{x:0,y:0},{x:t,y:0},{x:t,y:n},{x:0,y:n}],e)}const U0={oval:P0,polygon:N0};function Wc(i,e){return{...U0[e.presetId](i,e.color),transparency:e.transparency,haloScale:e.haloScale}}function F0(i,e,t){const{bounds:n}=i;return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}function Kd(i,e,t){const n=i.bounds.width,s=i.bounds.height;return{...i,bounds:{x:e-n/2,y:t-s/2,width:n,height:s}}}function jt(i,e,t){return i+(e-i)*t}function Xc(i){const e=/^#([0-9a-f]{6})$/i.exec(i);if(!e)return;const t=parseInt(e[1],16);return[t>>16&255,t>>8&255,t&255]}function O0(i){return`#${i.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function B0(i,e,t){const n=Xc(i),s=Xc(e);return!n||!s?t<1?i:e:O0([jt(n[0],s[0],t),jt(n[1],s[1],t),jt(n[2],s[2],t)])}function z0(i,e,t){const n={x:jt(i.bounds.x,e.bounds.x,t),y:jt(i.bounds.y,e.bounds.y,t),width:jt(i.bounds.width,e.bounds.width,t),height:jt(i.bounds.height,e.bounds.height,t)},s=jt(i.angle,e.angle,t),r=jt(i.transparency,e.transparency,t),a=jt(i.haloScale,e.haloScale,t),o=B0(i.color,e.color,t),l=ps.lerp(i.physical,e.physical,t);return i.kind==="polygon"&&e.kind==="polygon"&&i.points.length===e.points.length?{...i,bounds:n,angle:s,transparency:r,haloScale:a,color:o,physical:l,points:i.points.map((c,h)=>({x:jt(c.x,e.points[h].x,t),y:jt(c.y,e.points[h].y,t)}))}:{...t<1?i:e,bounds:n,angle:s,transparency:r,haloScale:a,color:o,physical:l}}class Ta{keyframes=[];order=[];groups=[];addKeyframe(e,t){const n=this.findInsertIndex(e);if(this.keyframes[n]?.t===e){const s=new Set(t.map(r=>r.sourceId));this.keyframes[n]={t:e,shapes:[...this.keyframes[n].shapes.filter(r=>!s.has(r.sourceId)),...t]}}else this.keyframes.splice(n,0,{t:e,shapes:[...t]});for(const s of t)this.order.includes(s.sourceId)||this.order.push(s.sourceId)}findInsertIndex(e){let t=0,n=this.keyframes.length;for(;t<n;){const s=t+n>>>1;this.keyframes[s].t<e?t=s+1:n=s}return t}getKeyframeAt(e){const t=this.findInsertIndex(e),n=this.keyframes[t];return n?.t===e?n:void 0}getShapeAt(e,t){return this.getKeyframeAt(e)?.shapes.find(n=>n.sourceId===t)?.shape}getLatestShapeAt(e,t){let n=this.findInsertIndex(e);for(this.keyframes[n]?.t!==e&&(n-=1);n>=0;n--){const s=this.keyframes[n].shapes.find(r=>r.sourceId===t);if(s)return s.shape}}getInterpolatedShapeAt(e,t){const n=this.findShapeAtOrBefore(e,t);if(n?.t===e)return n.shape;const s=this.findShapeAtOrAfter(e,t);return n?s?z0(n.shape,s.shape,(e-n.t)/(s.t-n.t)):n.shape:s?.shape}findShapeAtOrBefore(e,t){let n=this.findInsertIndex(e);for(this.keyframes[n]?.t!==e&&(n-=1);n>=0;n--){const s=this.keyframes[n].shapes.find(r=>r.sourceId===t);if(s)return{t:this.keyframes[n].t,shape:s.shape}}}findShapeAtOrAfter(e,t){for(let n=this.findInsertIndex(e);n<this.keyframes.length;n++){const s=this.keyframes[n].shapes.find(r=>r.sourceId===t);if(s)return{t:this.keyframes[n].t,shape:s.shape}}}hitTest(e,t,n,s){const r=this.sourceIds;for(let a=r.length-1;a>=0;a--){if(s?.has(r[a]))continue;const o=this.getInterpolatedShapeAt(e,r[a]);if(o&&F0(o,t,n))return{sourceId:r[a],shape:o}}}removeSource(e){for(let t=this.keyframes.length-1;t>=0;t--){const n=this.keyframes[t].shapes.filter(s=>s.sourceId!==e);n.length===0?this.keyframes.splice(t,1):n.length!==this.keyframes[t].shapes.length&&(this.keyframes[t]={t:this.keyframes[t].t,shapes:n})}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(n=>n.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(s=>s.includes(e));if(t===-1)return;const n=this.groups[t].filter(s=>s!==e);n.length<2?this.groups.splice(t,1):this.groups[t]=n}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 Ta;for(const n of e.keyframes)t.addKeyframe(n.t,n.shapes);if(e.order){const n=t.order.filter(s=>!e.order.includes(s));t.order=[...e.order,...n]}return e.groups&&(t.groups=e.groups.map(n=>n.filter(s=>t.order.includes(s))).filter(n=>n.length>=2)),t}}function k0(i,e,t){return Math.max(e,Math.min(t,i))}function G0(i,e,t){const n=((e-i)%360+540)%360-180;return((i+n*t)%360+360)%360}function As(i,e,t){return i+(e-i)*t}function H0(i,e,t){return{lat:i.lat===void 0||e.lat===void 0?void 0:As(i.lat,e.lat,t),lng:i.lng===void 0||e.lng===void 0?void 0:As(i.lng,e.lng,t),elevationM:As(i.elevationM,e.elevationM,t),headingDeg:i.headingDeg===void 0||e.headingDeg===void 0?void 0:G0(i.headingDeg,e.headingDeg,t),pitchDeg:As(i.pitchDeg,e.pitchDeg,t),fovDeg:As(i.fovDeg,e.fovDeg,t)}}class er{keyframes=[];addKeyframe(e,t){const n=this.findInsertIndex(e);this.keyframes[n]?.t===e?this.keyframes[n]={t:e,pose:t}:this.keyframes.splice(n,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,n=this.keyframes.length;for(;t<n;){const s=t+n>>>1;this.keyframes[s].t<e?t=s+1:n=s}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),n=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(n?.t===e)return n.pose;const s=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return n?s?H0(n.pose,s.pose,k0((e-n.t)/(s.t-n.t),0,1)):n.pose:s?.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 er;for(const n of e.keyframes)t.addKeyframe(n.t,n.pose);return t}}function Is(i,e,t){return i+(e-i)*t}function V0(i,e,t){const n=((e-i)%360+540)%360-180;return((i+n*t)%360+360)%360}function W0(i,e,t){return Math.max(e,Math.min(t,i))}function X0(i,e,t){return{cloudCover:Is(i.cloudCover,e.cloudCover,t),cloudDarkness:Is(i.cloudDarkness,e.cloudDarkness,t),cloudBaseM:i.cloudBaseM===void 0||e.cloudBaseM===void 0?void 0:Is(i.cloudBaseM,e.cloudBaseM,t),precipitationType:t<1?i.precipitationType:e.precipitationType,precipitationIntensity:Is(i.precipitationIntensity,e.precipitationIntensity,t),windDirectionDeg:V0(i.windDirectionDeg,e.windDirectionDeg,t),windSpeed:Is(i.windSpeed,e.windSpeed,t),storm:t<1?i.storm:e.storm}}class tr{keyframes=[];addKeyframe(e,t){const n=this.findInsertIndex(e);this.keyframes[n]?.t===e?this.keyframes[n]={t:e,weather:t}:this.keyframes.splice(n,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,n=this.keyframes.length;for(;t<n;){const s=t+n>>>1;this.keyframes[s].t<e?t=s+1:n=s}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),n=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(n?.t===e)return n.weather;const s=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return n?s?X0(n.weather,s.weather,W0((e-n.t)/(s.t-n.t),0,1)):n.weather:s?.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 tr;for(const n of e.keyframes)t.addKeyframe(n.t,n.weather);return t}}const zo=1e3,Zd={cloudCover:0,cloudDarkness:0,precipitationType:"none",precipitationIntensity:0,windDirectionDeg:0,windSpeed:0,storm:!1},Y0=/^(?:(?<year>\d{4}|\d{3}X|\d{2}XX)(-(?<month>0[1-9]|1[0-2]))?(-(?<day>0[1-9]|[12]\d|3[01]))?(?:[T ](?<hour>[01]?\d|2[0-3]):(?<minute>[0-5]\d)(?::(?<second>[0-5]\d))?)?|(?<timeOnlyHour>[01]?\d|2[0-3]):(?<timeOnlyMinute>[0-5]\d)(?::(?<timeOnlySecond>[0-5]\d))?)[?~%]?$/;function Yc(i){const e=Y0.exec(i.trim());if(!e?.groups)return;const{year:t,month:n,day:s,hour:r,minute:a,second:o,timeOnlyHour:l,timeOnlyMinute:c,timeOnlySecond:h}=e.groups,u=r??l,d=a??c,p=o??h;return{year:t&&!t.includes("X")?Number(t):void 0,month:n?Number(n):void 0,day:s?Number(s):void 0,hour:u?Number(u):void 0,minute:d?Number(d):void 0,second:p?Number(p):void 0,raw:i.trim()}}function qc(i){if(i.raw!==void 0)return i.raw;if(i.year===void 0){if(i.hour===void 0)return"";let t=`${String(i.hour).padStart(2,"0")}:${String(i.minute??0).padStart(2,"0")}`;return i.second!==void 0&&(t+=`:${String(i.second).padStart(2,"0")}`),t}let e=String(i.year).padStart(4,"0");return i.month===void 0||(e+=`-${String(i.month).padStart(2,"0")}`,i.day===void 0)||(e+=`-${String(i.day).padStart(2,"0")}`,i.hour===void 0)||(e+=`T${String(i.hour).padStart(2,"0")}:${String(i.minute??0).padStart(2,"0")}`,i.second!==void 0&&(e+=`:${String(i.second).padStart(2,"0")}`)),e}function ko(i){if(i.year!==void 0)return Date.UTC(i.year,(i.month??1)-1,i.day??1,i.hour??0,i.minute??0,i.second??0)}function $c(i){return(((i.day??0)*24+(i.hour??0))*60+(i.minute??0))*6e4+(i.second??0)*1e3}const q0=["year","month","day","hour","minute"],$0=["day","hour","minute"];function Kc(i,e){return e.filter(t=>i[t]!==void 0).join(",")}function dc(i){if(i.durationSeconds!==void 0)return i.durationSeconds*1e3;if(!i.time||!i.endTime)return;const e=i.time.year!==void 0&&i.endTime.year!==void 0,t=e?q0:$0,n=Kc(i.time,t);if(!(n===""||n!==Kc(i.endTime,t)))return e?ko(i.endTime)-ko(i.time):$c(i.endTime)-$c(i.time)}function K0(i){if(i.durationSeconds===void 0&&!(!i.time||!i.endTime))return dc(i)===void 0?"imprecise":void 0}class Aa{constructor(e,t,n,s,r,a,o,l=[]){this.event=e,this.timeline=t,this.witnessTrack=n,this.weatherTrack=s,this.witness=r,this.caseId=a,this.weather=o,this.decor=l}static create(e,t,n){return new Aa({eventType:"sighting",time:e,place:t},new Ta,new er,new tr,n)}}const Z0=0,J0=0,Q0=60;function Go(i,e){const t=i.witnessTrack.getInterpolatedPoseAt(e);if(t)return t;const n=i.event.place?.[0];if(n)return{lat:n.lat,lng:n.lng,elevationM:Z0,headingDeg:void 0,pitchDeg:J0,fovDeg:Q0}}function Ho(i,e){return i.weatherTrack.getInterpolatedWeatherAt(e)??i.weather??Zd}class j0{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT);return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const n of this.timeline.sourceIds){const s=this.timeline.getInterpolatedShapeAt(e,n);s&&t.set(n,s)}this.onFrame(e,t)}}const Jd=["nw","n","ne","e","se","s","sw","w"],ef=24,ai=8,Qd=3,tf=8;function Ws(i,e,t){const n=i.x-e.x,s=i.y-e.y,r=Math.cos(t),a=Math.sin(t);return{x:e.x+n*r-s*a,y:e.y+n*a+s*r}}function yi(i){return{x:i.x+i.width/2,y:i.y+i.height/2}}const nf={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 Ke{static handlePointsFor(e){const{x:t,y:n,width:s,height:r}=e.bounds,a={nw:{x:t,y:n},n:{x:t+s/2,y:n},ne:{x:t+s,y:n},e:{x:t+s,y:n+r/2},se:{x:t+s,y:n+r},s:{x:t+s/2,y:n+r},sw:{x:t,y:n+r},w:{x:t,y:n+r/2},rotate:{x:t+s/2,y:n-ef}},o=yi(e.bounds),l={};for(const c of Object.keys(a))l[c]=Ws(a[c],o,e.angle);return l}static hitTestHandle(e,t,n=8,s=[...Jd,"rotate"]){const r=Ke.handlePointsFor(e);for(const a of s)if(Math.hypot(t.x-r[a].x,t.y-r[a].y)<=n)return a}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(e,t){const s=((Ke.HANDLE_DIRECTION_DEG[e]+t*180/Math.PI)%180+180)%180/45,r=["ew","nwse","ns","nesw"];return r[Math.round(s)%r.length]}static resizeBounds(e,t,n,s){const r=yi(e),a=Ws(s,r,-t),{x:o,y:l,width:c,height:h}=e,u=nf[n];let d=o,p=l,g=o+c,_=l+h;return u.left&&(d=Math.min(a.x,g-ai)),u.right&&(g=Math.max(a.x,d+ai)),u.top&&(p=Math.min(a.y,_-ai)),u.bottom&&(_=Math.max(a.y,p+ai)),{x:d,y:p,width:g-d,height:_-p}}static resizeShape(e,t,n){if(t==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:s,height:r}=e.bounds,a=Ke.resizeBounds(e.bounds,e.angle,t,n);if(e.kind==="oval")return{...e,bounds:a};const o=s===0?1:a.width/s,l=r===0?1:a.height/r;return{...e,bounds:a,points:e.points.map(c=>({x:c.x*o,y:c.y*l}))}}static rotateShape(e,t){const n=yi(e.bounds),s=Math.atan2(t.y-n.y,t.x-n.x)+Math.PI/2;return{...e,angle:s}}static groupBoundsFor(e){const t=Math.min(...e.map(a=>a.x)),n=Math.min(...e.map(a=>a.y)),s=Math.max(...e.map(a=>a.x+a.width)),r=Math.max(...e.map(a=>a.y+a.height));return{x:t,y:n,width:s-t,height:r-n}}static vertexPointsFor(e){const t=yi(e.bounds);return e.points.map(n=>Ws({x:e.bounds.x+n.x,y:e.bounds.y+n.y},t,e.angle))}static hitTestVertex(e,t,n=tf){const s=Ke.vertexPointsFor(e);let r,a=n;return s.forEach((o,l)=>{const c=Math.hypot(t.x-o.x,t.y-o.y);c<=a&&(a=c,r=l)}),r}static toLocalPoint(e,t){const n=yi(e.bounds),s=Ws(t,n,-e.angle);return{x:s.x-e.bounds.x,y:s.y-e.bounds.y}}static moveVertex(e,t,n){const s=e.points.map((u,d)=>d===t?Ke.toLocalPoint(e,n):u),r=Math.min(...s.map(u=>u.x)),a=Math.min(...s.map(u=>u.y)),o=Math.max(...s.map(u=>u.x)),l=Math.max(...s.map(u=>u.y)),c={x:e.bounds.x+r,y:e.bounds.y+a,width:o-r,height:l-a},h=s.map(u=>({x:u.x-r,y:u.y-a}));return{...e,bounds:c,points:h}}static insertVertexNear(e,t){const n=Ke.toLocalPoint(e,t),{points:s}=e;let r=s.length-1,a=1/0;for(let l=0;l<s.length;l++){const c=s[l],h=s[(l+1)%s.length],u=Ke.distanceToSegment(n,c,h);u<a&&(a=u,r=l)}const o=[...s.slice(0,r+1),n,...s.slice(r+1)];return{...e,points:o}}static distanceToSegment(e,t,n){const s=n.x-t.x,r=n.y-t.y,a=s*s+r*r,o=a===0?0:Math.max(0,Math.min(1,((e.x-t.x)*s+(e.y-t.y)*r)/a)),l=t.x+o*s,c=t.y+o*r;return Math.hypot(e.x-l,e.y-c)}static deleteVertex(e,t){return e.points.length<=Qd?e:{...e,points:e.points.filter((n,s)=>s!==t)}}}class za{constructor(e){this.members=e}bounds(){return Ke.groupBoundsFor(this.members.map(e=>e.shape.bounds))}scaleTo(e){const t=this.bounds(),n=t.width===0?1:e.width/t.width,s=t.height===0?1:e.height/t.height;return this.members.map(({sourceId:r,shape:a})=>{const o=Math.max(a.bounds.width*n,ai),l=Math.max(a.bounds.height*s,ai),c=e.x+(a.bounds.x-t.x)*n,h=e.y+(a.bounds.y-t.y)*s,u={x:c,y:h,width:o,height:l};return a.kind==="oval"?{sourceId:r,shape:{...a,bounds:u}}:{sourceId:r,shape:{...a,bounds:u,points:a.points.map(d=>({x:d.x*n,y:d.y*s}))}}})}resize(e,t){return this.scaleTo(Ke.resizeBounds(this.bounds(),0,e,t))}rotate(e,t){const n=yi(this.bounds()),s=a=>Math.atan2(a.y-n.y,a.x-n.x),r=s(e)-s(t);return this.members.map(({sourceId:a,shape:o})=>{const l=yi(o.bounds),c=Ws(l,n,r),h={x:c.x-o.bounds.width/2,y:c.y-o.bounds.height/2,width:o.bounds.width,height:o.bounds.height};return{sourceId:a,shape:{...o,bounds:h,angle:o.angle+r}}})}}const Vo=6,ur=Vo/2,sf=20,Zc=4;class rf{constructor(e){this.ctx=e}clear(e,t){this.ctx.clearRect(0,0,e,t)}paintShape(e){this.ctx.save(),this.ctx.globalAlpha=1-e.transparency,e.haloScale>0&&this.paintHalo(e),this.paintBase(e),this.ctx.restore(),e.selected&&this.paintSelectionHandles(e)}paintBase(e){if(this.ctx.fillStyle=e.color,this.ctx.beginPath(),e.kind==="oval"){const{x:t,y:n,width:s,height:r}=e.bounds,a=s/2,o=r/2;this.ctx.ellipse(t+a,n+o,a,o,e.angle,0,2*Math.PI)}else{const{x:t,y:n,width:s,height:r}=e.bounds;this.ctx.save(),this.ctx.translate(t+s/2,n+r/2),this.ctx.rotate(e.angle),this.ctx.translate(-s/2,-r/2),e.points.forEach((a,o)=>{o===0?this.ctx.moveTo(a.x,a.y):this.ctx.lineTo(a.x,a.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=sf*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){this.paintHandleFrame(Ke.handlePointsFor(e),{includeRotate:!0}),e.kind==="polygon"&&this.paintVertexHandles(e)}paintVertexHandles(e){this.ctx.fillStyle="#39f";for(const t of Ke.vertexPointsFor(e))this.ctx.beginPath(),this.ctx.ellipse(t.x,t.y,Zc,Zc,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(e){this.paintOutline(Ke.handlePointsFor(e))}paintGroupHandles(e){this.paintHandleFrame(Ke.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 n of Jd){const s=e[n];this.ctx.fillRect(s.x-ur,s.y-ur,Vo,Vo)}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,ur+1,ur+1,0,0,2*Math.PI),this.ctx.fill())}}function af(i){return{version:1,time:i.event.time,endTime:i.event.endTime,durationSeconds:i.event.durationSeconds,utcOffsetHours:i.event.utcOffsetHours,place:i.event.place,witness:i.witness,caseId:i.caseId,description:i.event.description,tags:i.event.tags,timeline:i.timeline.toJSON(),witnessTrack:i.witnessTrack.toJSON(),weatherTrack:i.weatherTrack.toJSON(),weather:i.weather,decor:i.decor}}function of(i){return new Aa({eventType:"sighting",time:i.time,endTime:i.endTime,durationSeconds:i.durationSeconds,utcOffsetHours:i.utcOffsetHours,place:i.place,description:i.description,tags:i.tags},Ta.fromJSON(i.timeline),i.witnessTrack?er.fromJSON(i.witnessTrack):new er,i.weatherTrack?tr.fromJSON(i.weatherTrack):new tr,i.witness,i.caseId,i.weather,i.decor??[])}function Ia(i,e){for(const t of i){const n=t.toLowerCase().split("-")[0];if(e.includes(n))return n}return"en"}const lf="modulepreload",cf=function(i,e){return new URL(i,e).href},Jc={},da=function(e,t,n){let s=Promise.resolve();if(t&&t.length>0){let a=function(h){return Promise.all(h.map(u=>Promise.resolve(u).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),c=l?.nonce||l?.getAttribute("nonce");s=a(t.map(h=>{if(h=cf(h,n),h in Jc)return;Jc[h]=!0;const u=h.endsWith(".css"),d=u?'[rel="stylesheet"]':"";if(!!n)for(let _=o.length-1;_>=0;_--){const m=o[_];if(m.href===h&&(!u||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${h}"]${d}`))return;const g=document.createElement("link");if(g.rel=u?"stylesheet":lf,u||(g.as="script"),g.crossOrigin="",g.href=h,c&&g.setAttribute("nonce",c),document.head.appendChild(g),u)return new Promise((_,m)=>{g.addEventListener("load",_),g.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${h}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return s.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})},jd=["en","fr"],hf={en:()=>da(()=>Promise.resolve().then(()=>pf),void 0,import.meta.url).then(i=>i.ufoMessages_en),fr:()=>da(()=>import("./UfoMessages_fr-NZrwNFCQ.js"),[],import.meta.url).then(i=>i.ufoMessages_fr)};function df(i){return hf[i]()}const uf={en:()=>da(()=>Promise.resolve().then(()=>vy),void 0,import.meta.url).then(i=>i.ufoRecorderMessages_en),fr:()=>da(()=>import("./UfoRecorderMessages_fr-CzIWyNRZ.js"),[],import.meta.url).then(i=>i.ufoRecorderMessages_fr)};function ff(i){return uf[i]()}const eu={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen"},pf=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:eu},Symbol.toStringTag,{value:"Module"})),Qc=new Set;class mf extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=Aa.create();player;loopEnabled=!0;highlightedSourceIds=new Set;occludedSourceIds=Qc;enableClickToPlay=!0;fullscreenTarget;messages=eu;realDurationMs;realStartMs;handleFullscreenChange=()=>this.updateFullscreenButton();handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),n=t&&this.currentSighting.timeline.hitTest(this.currentTime,t.x,t.y,this.occludedSourceIds);if(!n?.shape.title){this.tooltip.hidden=!0;return}this.tooltip.textContent=n.shape.title,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${D0}</style>${C0}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new rf(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.loopButton.addEventListener("click",()=>this.toggleLoop()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",()=>{this.enableClickToPlay&&this.togglePlayPause()}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange)}attributeChangedCallback(e,t,n){e==="src"&&n&&n!==t&&this.isConnected&&this.loadFromSrc(n)}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return af(this.currentSighting)}set sightingData(e){this.player.stop(),this.currentSighting=of(e),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh()}get sighting(){return this.currentSighting}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}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(s=>this.highlightedSourceIds.has(s))||(this.highlightedSourceIds=t,this.refresh())}setOccludedSourceIds(e){e.size===this.occludedSourceIds.size&&[...e].every(n=>this.occludedSourceIds.has(n))||(this.occludedSourceIds=e,this.refresh())}hasVisibleShapeAt(e,t){return this.currentSighting.timeline.hitTest(this.currentTime,e,t,this.occludedSourceIds)!==void 0}refresh(){this.seekInput.max=String(this.player.seekableDuration),this.player.seek(this.player.time)}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 n=this.playbackState!=="playing"?this.highlightedSourceIds:Qc;for(const[s,r]of t){if(this.occludedSourceIds.has(s))continue;const a=n.has(s);a&&n.size===1?this.canvasRenderer.paintShape({...r,selected:!0}):(this.canvasRenderer.paintShape(r),a&&this.canvasRenderer.paintMemberOutline(r))}if(n.size>1){const s=Ke.groupBoundsFor([...t].filter(([r])=>n.has(r)).map(([,r])=>r.bounds));this.canvasRenderer.paintGroupHandles(s)}this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}createPlayer(){const e=new j0(this.currentSighting.timeline,(t,n)=>this.onFrame(t,n));return e.loop=this.loopEnabled,e}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.refresh()):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 n=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=n,this.playPauseButton.setAttribute("aria-label",n),this.toolbar.classList.toggle("auto-hide",e),this.fullscreenButton.classList.toggle("auto-hide",e)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}updateFullscreenButton(){const e=document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}async loadLocaleMessages(){const e=Ia(navigator.languages,jd);e!=="en"&&this.applyMessages(await df(e))}applyMessages(e){this.messages=e,this.timeStartLabel.title=e.currentPosition,this.timeEndLabel.title=e.duration,this.loopButton.title=e.autoReplay,this.loopButton.setAttribute("aria-label",e.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton()}updateTimeLabels(){const e=this.currentSighting.event,t=dc(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?ko(e.time):void 0;const n=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&n>0?n/this.realDurationMs:1,this.player.durationOverrideMs=n>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time)}formatPosition(e){if(this.realDurationMs===void 0)return fr(e);const t=this.currentSighting.timeline.duration,n=t>0&&e<=t?e/t*this.realDurationMs:e;return this.realStartMs!==void 0?eh(jc(this.realStartMs+n)):fr(n)}formatEndOfTimeline(){return this.realDurationMs===void 0?fr(this.currentSighting.timeline.duration):this.realStartMs!==void 0?eh(jc(this.realStartMs+this.realDurationMs)):fr(this.realDurationMs)}}function jc(i){const e=new Date(i);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function eh(i){if(i.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return i.second?`${e(i.hour)}:${e(i.minute??0)}:${e(i.second)}`:`${e(i.hour)}:${e(i.minute??0)}`}function fr(i){const e=Math.round(i/1e3),t=Math.floor(e/60),n=e%60;return`${t}:${String(n).padStart(2,"0")}`}const Wo="rr0-ufo";function Ra(){customElements.get(Wo)||customElements.define(Wo,mf)}const gf=`
702
+ `;class ps{static MOON_ANGULAR_WIDTH_DEG=.5237;static angularWidthDeg(e){return 2*Math.atan(e.sizeM/(2*e.distanceM))*180/Math.PI}static widthPx(e,t,n){return t*e.sizeM/(2*e.distanceM*Math.tan(n*Math.PI/360))}static pxToDeg(e,t,n){return 2*Math.atan(e*Math.tan(n*Math.PI/360)/t)*180/Math.PI}static inMoons(e){return e/ps.MOON_ANGULAR_WIDTH_DEG}static lerp(e,t,n){if(!(!e||!t))return{sizeM:e.sizeM+(t.sizeM-e.sizeM)*n,distanceM:e.distanceM+(t.distanceM-e.distanceM)*n}}}function P0(i,e="#39ff14"){return{kind:"oval",bounds:i,color:e,angle:0,transparency:0,haloScale:0,selected:!1}}function L0(i,e,t="#39ff14"){return{kind:"polygon",bounds:i,points:e,color:t,angle:0,transparency:0,haloScale:0,selected:!1}}function N0(i,e="#39ff14"){const{width:t,height:n}=i;return L0(i,[{x:0,y:0},{x:t,y:0},{x:t,y:n},{x:0,y:n}],e)}const U0={oval:P0,polygon:N0};function Wc(i,e){return{...U0[e.presetId](i,e.color),transparency:e.transparency,haloScale:e.haloScale}}function F0(i,e,t){const{bounds:n}=i;return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}function Kd(i,e,t){const n=i.bounds.width,s=i.bounds.height;return{...i,bounds:{x:e-n/2,y:t-s/2,width:n,height:s}}}function jt(i,e,t){return i+(e-i)*t}function Xc(i){const e=/^#([0-9a-f]{6})$/i.exec(i);if(!e)return;const t=parseInt(e[1],16);return[t>>16&255,t>>8&255,t&255]}function O0(i){return`#${i.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function B0(i,e,t){const n=Xc(i),s=Xc(e);return!n||!s?t<1?i:e:O0([jt(n[0],s[0],t),jt(n[1],s[1],t),jt(n[2],s[2],t)])}function z0(i,e,t){const n={x:jt(i.bounds.x,e.bounds.x,t),y:jt(i.bounds.y,e.bounds.y,t),width:jt(i.bounds.width,e.bounds.width,t),height:jt(i.bounds.height,e.bounds.height,t)},s=jt(i.angle,e.angle,t),r=jt(i.transparency,e.transparency,t),a=jt(i.haloScale,e.haloScale,t),o=B0(i.color,e.color,t),l=ps.lerp(i.physical,e.physical,t);return i.kind==="polygon"&&e.kind==="polygon"&&i.points.length===e.points.length?{...i,bounds:n,angle:s,transparency:r,haloScale:a,color:o,physical:l,behindCloud:t<1?i.behindCloud:e.behindCloud,points:i.points.map((c,h)=>({x:jt(c.x,e.points[h].x,t),y:jt(c.y,e.points[h].y,t)}))}:{...t<1?i:e,bounds:n,angle:s,transparency:r,haloScale:a,color:o,physical:l}}class Ta{keyframes=[];order=[];groups=[];addKeyframe(e,t){const n=this.findInsertIndex(e);if(this.keyframes[n]?.t===e){const s=new Set(t.map(r=>r.sourceId));this.keyframes[n]={t:e,shapes:[...this.keyframes[n].shapes.filter(r=>!s.has(r.sourceId)),...t]}}else this.keyframes.splice(n,0,{t:e,shapes:[...t]});for(const s of t)this.order.includes(s.sourceId)||this.order.push(s.sourceId)}findInsertIndex(e){let t=0,n=this.keyframes.length;for(;t<n;){const s=t+n>>>1;this.keyframes[s].t<e?t=s+1:n=s}return t}getKeyframeAt(e){const t=this.findInsertIndex(e),n=this.keyframes[t];return n?.t===e?n:void 0}getShapeAt(e,t){return this.getKeyframeAt(e)?.shapes.find(n=>n.sourceId===t)?.shape}getLatestShapeAt(e,t){let n=this.findInsertIndex(e);for(this.keyframes[n]?.t!==e&&(n-=1);n>=0;n--){const s=this.keyframes[n].shapes.find(r=>r.sourceId===t);if(s)return s.shape}}getInterpolatedShapeAt(e,t){const n=this.findShapeAtOrBefore(e,t);if(n?.t===e)return n.shape;const s=this.findShapeAtOrAfter(e,t);return n?s?z0(n.shape,s.shape,(e-n.t)/(s.t-n.t)):n.shape:s?.shape}findShapeAtOrBefore(e,t){let n=this.findInsertIndex(e);for(this.keyframes[n]?.t!==e&&(n-=1);n>=0;n--){const s=this.keyframes[n].shapes.find(r=>r.sourceId===t);if(s)return{t:this.keyframes[n].t,shape:s.shape}}}findShapeAtOrAfter(e,t){for(let n=this.findInsertIndex(e);n<this.keyframes.length;n++){const s=this.keyframes[n].shapes.find(r=>r.sourceId===t);if(s)return{t:this.keyframes[n].t,shape:s.shape}}}hitTest(e,t,n,s){const r=this.sourceIds;for(let a=r.length-1;a>=0;a--){if(s?.has(r[a]))continue;const o=this.getInterpolatedShapeAt(e,r[a]);if(o&&F0(o,t,n))return{sourceId:r[a],shape:o}}}removeSource(e){for(let t=this.keyframes.length-1;t>=0;t--){const n=this.keyframes[t].shapes.filter(s=>s.sourceId!==e);n.length===0?this.keyframes.splice(t,1):n.length!==this.keyframes[t].shapes.length&&(this.keyframes[t]={t:this.keyframes[t].t,shapes:n})}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(n=>n.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(s=>s.includes(e));if(t===-1)return;const n=this.groups[t].filter(s=>s!==e);n.length<2?this.groups.splice(t,1):this.groups[t]=n}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 Ta;for(const n of e.keyframes)t.addKeyframe(n.t,n.shapes);if(e.order){const n=t.order.filter(s=>!e.order.includes(s));t.order=[...e.order,...n]}return e.groups&&(t.groups=e.groups.map(n=>n.filter(s=>t.order.includes(s))).filter(n=>n.length>=2)),t}}function k0(i,e,t){return Math.max(e,Math.min(t,i))}function G0(i,e,t){const n=((e-i)%360+540)%360-180;return((i+n*t)%360+360)%360}function As(i,e,t){return i+(e-i)*t}function H0(i,e,t){return{lat:i.lat===void 0||e.lat===void 0?void 0:As(i.lat,e.lat,t),lng:i.lng===void 0||e.lng===void 0?void 0:As(i.lng,e.lng,t),elevationM:As(i.elevationM,e.elevationM,t),headingDeg:i.headingDeg===void 0||e.headingDeg===void 0?void 0:G0(i.headingDeg,e.headingDeg,t),pitchDeg:As(i.pitchDeg,e.pitchDeg,t),fovDeg:As(i.fovDeg,e.fovDeg,t)}}class er{keyframes=[];addKeyframe(e,t){const n=this.findInsertIndex(e);this.keyframes[n]?.t===e?this.keyframes[n]={t:e,pose:t}:this.keyframes.splice(n,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,n=this.keyframes.length;for(;t<n;){const s=t+n>>>1;this.keyframes[s].t<e?t=s+1:n=s}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),n=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(n?.t===e)return n.pose;const s=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return n?s?H0(n.pose,s.pose,k0((e-n.t)/(s.t-n.t),0,1)):n.pose:s?.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 er;for(const n of e.keyframes)t.addKeyframe(n.t,n.pose);return t}}function Is(i,e,t){return i+(e-i)*t}function V0(i,e,t){const n=((e-i)%360+540)%360-180;return((i+n*t)%360+360)%360}function W0(i,e,t){return Math.max(e,Math.min(t,i))}function X0(i,e,t){return{cloudCover:Is(i.cloudCover,e.cloudCover,t),cloudDarkness:Is(i.cloudDarkness,e.cloudDarkness,t),cloudBaseM:i.cloudBaseM===void 0||e.cloudBaseM===void 0?void 0:Is(i.cloudBaseM,e.cloudBaseM,t),precipitationType:t<1?i.precipitationType:e.precipitationType,precipitationIntensity:Is(i.precipitationIntensity,e.precipitationIntensity,t),windDirectionDeg:V0(i.windDirectionDeg,e.windDirectionDeg,t),windSpeed:Is(i.windSpeed,e.windSpeed,t),storm:t<1?i.storm:e.storm}}class tr{keyframes=[];addKeyframe(e,t){const n=this.findInsertIndex(e);this.keyframes[n]?.t===e?this.keyframes[n]={t:e,weather:t}:this.keyframes.splice(n,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,n=this.keyframes.length;for(;t<n;){const s=t+n>>>1;this.keyframes[s].t<e?t=s+1:n=s}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),n=this.keyframes[t]?.t===e?this.keyframes[t]:this.keyframes[t-1];if(n?.t===e)return n.weather;const s=this.keyframes[t]?.t===e?void 0:this.keyframes[t];return n?s?X0(n.weather,s.weather,W0((e-n.t)/(s.t-n.t),0,1)):n.weather:s?.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 tr;for(const n of e.keyframes)t.addKeyframe(n.t,n.weather);return t}}const zo=1e3,Zd={cloudCover:0,cloudDarkness:0,precipitationType:"none",precipitationIntensity:0,windDirectionDeg:0,windSpeed:0,storm:!1},Y0=/^(?:(?<year>\d{4}|\d{3}X|\d{2}XX)(-(?<month>0[1-9]|1[0-2]))?(-(?<day>0[1-9]|[12]\d|3[01]))?(?:[T ](?<hour>[01]?\d|2[0-3]):(?<minute>[0-5]\d)(?::(?<second>[0-5]\d))?)?|(?<timeOnlyHour>[01]?\d|2[0-3]):(?<timeOnlyMinute>[0-5]\d)(?::(?<timeOnlySecond>[0-5]\d))?)[?~%]?$/;function Yc(i){const e=Y0.exec(i.trim());if(!e?.groups)return;const{year:t,month:n,day:s,hour:r,minute:a,second:o,timeOnlyHour:l,timeOnlyMinute:c,timeOnlySecond:h}=e.groups,u=r??l,d=a??c,p=o??h;return{year:t&&!t.includes("X")?Number(t):void 0,month:n?Number(n):void 0,day:s?Number(s):void 0,hour:u?Number(u):void 0,minute:d?Number(d):void 0,second:p?Number(p):void 0,raw:i.trim()}}function qc(i){if(i.raw!==void 0)return i.raw;if(i.year===void 0){if(i.hour===void 0)return"";let t=`${String(i.hour).padStart(2,"0")}:${String(i.minute??0).padStart(2,"0")}`;return i.second!==void 0&&(t+=`:${String(i.second).padStart(2,"0")}`),t}let e=String(i.year).padStart(4,"0");return i.month===void 0||(e+=`-${String(i.month).padStart(2,"0")}`,i.day===void 0)||(e+=`-${String(i.day).padStart(2,"0")}`,i.hour===void 0)||(e+=`T${String(i.hour).padStart(2,"0")}:${String(i.minute??0).padStart(2,"0")}`,i.second!==void 0&&(e+=`:${String(i.second).padStart(2,"0")}`)),e}function ko(i){if(i.year!==void 0)return Date.UTC(i.year,(i.month??1)-1,i.day??1,i.hour??0,i.minute??0,i.second??0)}function $c(i){return(((i.day??0)*24+(i.hour??0))*60+(i.minute??0))*6e4+(i.second??0)*1e3}const q0=["year","month","day","hour","minute"],$0=["day","hour","minute"];function Kc(i,e){return e.filter(t=>i[t]!==void 0).join(",")}function dc(i){if(i.durationSeconds!==void 0)return i.durationSeconds*1e3;if(!i.time||!i.endTime)return;const e=i.time.year!==void 0&&i.endTime.year!==void 0,t=e?q0:$0,n=Kc(i.time,t);if(!(n===""||n!==Kc(i.endTime,t)))return e?ko(i.endTime)-ko(i.time):$c(i.endTime)-$c(i.time)}function K0(i){if(i.durationSeconds===void 0&&!(!i.time||!i.endTime))return dc(i)===void 0?"imprecise":void 0}class Aa{constructor(e,t,n,s,r,a,o,l=[]){this.event=e,this.timeline=t,this.witnessTrack=n,this.weatherTrack=s,this.witness=r,this.caseId=a,this.weather=o,this.decor=l}static create(e,t,n){return new Aa({eventType:"sighting",time:e,place:t},new Ta,new er,new tr,n)}}const Z0=0,J0=0,Q0=60;function Go(i,e){const t=i.witnessTrack.getInterpolatedPoseAt(e);if(t)return t;const n=i.event.place?.[0];if(n)return{lat:n.lat,lng:n.lng,elevationM:Z0,headingDeg:void 0,pitchDeg:J0,fovDeg:Q0}}function Ho(i,e){return i.weatherTrack.getInterpolatedWeatherAt(e)??i.weather??Zd}class j0{constructor(e,t){this.timeline=e,this.onFrame=t}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const e=()=>{if(this.state!=="playing")return;const t=performance.now();if(this.currentT+=(t-this.lastWallTime)*this.playbackRate,this.lastWallTime=t,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT);return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(e){this.currentT=Math.max(0,Math.min(e,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(e){const t=new Map;for(const n of this.timeline.sourceIds){const s=this.timeline.getInterpolatedShapeAt(e,n);s&&t.set(n,s)}this.onFrame(e,t)}}const Jd=["nw","n","ne","e","se","s","sw","w"],ef=24,ai=8,Qd=3,tf=8;function Ws(i,e,t){const n=i.x-e.x,s=i.y-e.y,r=Math.cos(t),a=Math.sin(t);return{x:e.x+n*r-s*a,y:e.y+n*a+s*r}}function yi(i){return{x:i.x+i.width/2,y:i.y+i.height/2}}const nf={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 Ke{static handlePointsFor(e){const{x:t,y:n,width:s,height:r}=e.bounds,a={nw:{x:t,y:n},n:{x:t+s/2,y:n},ne:{x:t+s,y:n},e:{x:t+s,y:n+r/2},se:{x:t+s,y:n+r},s:{x:t+s/2,y:n+r},sw:{x:t,y:n+r},w:{x:t,y:n+r/2},rotate:{x:t+s/2,y:n-ef}},o=yi(e.bounds),l={};for(const c of Object.keys(a))l[c]=Ws(a[c],o,e.angle);return l}static hitTestHandle(e,t,n=8,s=[...Jd,"rotate"]){const r=Ke.handlePointsFor(e);for(const a of s)if(Math.hypot(t.x-r[a].x,t.y-r[a].y)<=n)return a}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(e,t){const s=((Ke.HANDLE_DIRECTION_DEG[e]+t*180/Math.PI)%180+180)%180/45,r=["ew","nwse","ns","nesw"];return r[Math.round(s)%r.length]}static resizeBounds(e,t,n,s){const r=yi(e),a=Ws(s,r,-t),{x:o,y:l,width:c,height:h}=e,u=nf[n];let d=o,p=l,g=o+c,_=l+h;return u.left&&(d=Math.min(a.x,g-ai)),u.right&&(g=Math.max(a.x,d+ai)),u.top&&(p=Math.min(a.y,_-ai)),u.bottom&&(_=Math.max(a.y,p+ai)),{x:d,y:p,width:g-d,height:_-p}}static resizeShape(e,t,n){if(t==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:s,height:r}=e.bounds,a=Ke.resizeBounds(e.bounds,e.angle,t,n);if(e.kind==="oval")return{...e,bounds:a};const o=s===0?1:a.width/s,l=r===0?1:a.height/r;return{...e,bounds:a,points:e.points.map(c=>({x:c.x*o,y:c.y*l}))}}static rotateShape(e,t){const n=yi(e.bounds),s=Math.atan2(t.y-n.y,t.x-n.x)+Math.PI/2;return{...e,angle:s}}static groupBoundsFor(e){const t=Math.min(...e.map(a=>a.x)),n=Math.min(...e.map(a=>a.y)),s=Math.max(...e.map(a=>a.x+a.width)),r=Math.max(...e.map(a=>a.y+a.height));return{x:t,y:n,width:s-t,height:r-n}}static vertexPointsFor(e){const t=yi(e.bounds);return e.points.map(n=>Ws({x:e.bounds.x+n.x,y:e.bounds.y+n.y},t,e.angle))}static hitTestVertex(e,t,n=tf){const s=Ke.vertexPointsFor(e);let r,a=n;return s.forEach((o,l)=>{const c=Math.hypot(t.x-o.x,t.y-o.y);c<=a&&(a=c,r=l)}),r}static toLocalPoint(e,t){const n=yi(e.bounds),s=Ws(t,n,-e.angle);return{x:s.x-e.bounds.x,y:s.y-e.bounds.y}}static moveVertex(e,t,n){const s=e.points.map((u,d)=>d===t?Ke.toLocalPoint(e,n):u),r=Math.min(...s.map(u=>u.x)),a=Math.min(...s.map(u=>u.y)),o=Math.max(...s.map(u=>u.x)),l=Math.max(...s.map(u=>u.y)),c={x:e.bounds.x+r,y:e.bounds.y+a,width:o-r,height:l-a},h=s.map(u=>({x:u.x-r,y:u.y-a}));return{...e,bounds:c,points:h}}static insertVertexNear(e,t){const n=Ke.toLocalPoint(e,t),{points:s}=e;let r=s.length-1,a=1/0;for(let l=0;l<s.length;l++){const c=s[l],h=s[(l+1)%s.length],u=Ke.distanceToSegment(n,c,h);u<a&&(a=u,r=l)}const o=[...s.slice(0,r+1),n,...s.slice(r+1)];return{...e,points:o}}static distanceToSegment(e,t,n){const s=n.x-t.x,r=n.y-t.y,a=s*s+r*r,o=a===0?0:Math.max(0,Math.min(1,((e.x-t.x)*s+(e.y-t.y)*r)/a)),l=t.x+o*s,c=t.y+o*r;return Math.hypot(e.x-l,e.y-c)}static deleteVertex(e,t){return e.points.length<=Qd?e:{...e,points:e.points.filter((n,s)=>s!==t)}}}class za{constructor(e){this.members=e}bounds(){return Ke.groupBoundsFor(this.members.map(e=>e.shape.bounds))}scaleTo(e){const t=this.bounds(),n=t.width===0?1:e.width/t.width,s=t.height===0?1:e.height/t.height;return this.members.map(({sourceId:r,shape:a})=>{const o=Math.max(a.bounds.width*n,ai),l=Math.max(a.bounds.height*s,ai),c=e.x+(a.bounds.x-t.x)*n,h=e.y+(a.bounds.y-t.y)*s,u={x:c,y:h,width:o,height:l};return a.kind==="oval"?{sourceId:r,shape:{...a,bounds:u}}:{sourceId:r,shape:{...a,bounds:u,points:a.points.map(d=>({x:d.x*n,y:d.y*s}))}}})}resize(e,t){return this.scaleTo(Ke.resizeBounds(this.bounds(),0,e,t))}rotate(e,t){const n=yi(this.bounds()),s=a=>Math.atan2(a.y-n.y,a.x-n.x),r=s(e)-s(t);return this.members.map(({sourceId:a,shape:o})=>{const l=yi(o.bounds),c=Ws(l,n,r),h={x:c.x-o.bounds.width/2,y:c.y-o.bounds.height/2,width:o.bounds.width,height:o.bounds.height};return{sourceId:a,shape:{...o,bounds:h,angle:o.angle+r}}})}}const Vo=6,ur=Vo/2,sf=20,Zc=4;class rf{constructor(e){this.ctx=e}clear(e,t){this.ctx.clearRect(0,0,e,t)}paintShape(e){this.ctx.save(),this.ctx.globalAlpha=1-e.transparency,e.haloScale>0&&this.paintHalo(e),this.paintBase(e),this.ctx.restore(),e.selected&&this.paintSelectionHandles(e)}paintBase(e){if(this.ctx.fillStyle=e.color,this.ctx.beginPath(),e.kind==="oval"){const{x:t,y:n,width:s,height:r}=e.bounds,a=s/2,o=r/2;this.ctx.ellipse(t+a,n+o,a,o,e.angle,0,2*Math.PI)}else{const{x:t,y:n,width:s,height:r}=e.bounds;this.ctx.save(),this.ctx.translate(t+s/2,n+r/2),this.ctx.rotate(e.angle),this.ctx.translate(-s/2,-r/2),e.points.forEach((a,o)=>{o===0?this.ctx.moveTo(a.x,a.y):this.ctx.lineTo(a.x,a.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(e){this.ctx.save(),this.ctx.shadowColor=e.color,this.ctx.shadowBlur=sf*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){this.paintHandleFrame(Ke.handlePointsFor(e),{includeRotate:!0}),e.kind==="polygon"&&this.paintVertexHandles(e)}paintVertexHandles(e){this.ctx.fillStyle="#39f";for(const t of Ke.vertexPointsFor(e))this.ctx.beginPath(),this.ctx.ellipse(t.x,t.y,Zc,Zc,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(e){this.paintOutline(Ke.handlePointsFor(e))}paintGroupHandles(e){this.paintHandleFrame(Ke.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 n of Jd){const s=e[n];this.ctx.fillRect(s.x-ur,s.y-ur,Vo,Vo)}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,ur+1,ur+1,0,0,2*Math.PI),this.ctx.fill())}}function af(i){return{version:1,time:i.event.time,endTime:i.event.endTime,durationSeconds:i.event.durationSeconds,utcOffsetHours:i.event.utcOffsetHours,place:i.event.place,witness:i.witness,caseId:i.caseId,description:i.event.description,tags:i.event.tags,timeline:i.timeline.toJSON(),witnessTrack:i.witnessTrack.toJSON(),weatherTrack:i.weatherTrack.toJSON(),weather:i.weather,decor:i.decor}}function of(i){return new Aa({eventType:"sighting",time:i.time,endTime:i.endTime,durationSeconds:i.durationSeconds,utcOffsetHours:i.utcOffsetHours,place:i.place,description:i.description,tags:i.tags},Ta.fromJSON(i.timeline),i.witnessTrack?er.fromJSON(i.witnessTrack):new er,i.weatherTrack?tr.fromJSON(i.weatherTrack):new tr,i.witness,i.caseId,i.weather,i.decor??[])}function Ia(i,e){for(const t of i){const n=t.toLowerCase().split("-")[0];if(e.includes(n))return n}return"en"}const lf="modulepreload",cf=function(i,e){return new URL(i,e).href},Jc={},da=function(e,t,n){let s=Promise.resolve();if(t&&t.length>0){let a=function(h){return Promise.all(h.map(u=>Promise.resolve(u).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),c=l?.nonce||l?.getAttribute("nonce");s=a(t.map(h=>{if(h=cf(h,n),h in Jc)return;Jc[h]=!0;const u=h.endsWith(".css"),d=u?'[rel="stylesheet"]':"";if(!!n)for(let _=o.length-1;_>=0;_--){const m=o[_];if(m.href===h&&(!u||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${h}"]${d}`))return;const g=document.createElement("link");if(g.rel=u?"stylesheet":lf,u||(g.as="script"),g.crossOrigin="",g.href=h,c&&g.setAttribute("nonce",c),document.head.appendChild(g),u)return new Promise((_,m)=>{g.addEventListener("load",_),g.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${h}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return s.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})},jd=["en","fr"],hf={en:()=>da(()=>Promise.resolve().then(()=>pf),void 0,import.meta.url).then(i=>i.ufoMessages_en),fr:()=>da(()=>import("./UfoMessages_fr-NZrwNFCQ.js"),[],import.meta.url).then(i=>i.ufoMessages_fr)};function df(i){return hf[i]()}const uf={en:()=>da(()=>Promise.resolve().then(()=>vy),void 0,import.meta.url).then(i=>i.ufoRecorderMessages_en),fr:()=>da(()=>import("./UfoRecorderMessages_fr-CzIWyNRZ.js"),[],import.meta.url).then(i=>i.ufoRecorderMessages_fr)};function ff(i){return uf[i]()}const eu={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen"},pf=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:eu},Symbol.toStringTag,{value:"Module"})),Qc=new Set;class mf extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=Aa.create();player;loopEnabled=!0;highlightedSourceIds=new Set;occludedSourceIds=Qc;enableClickToPlay=!0;fullscreenTarget;messages=eu;realDurationMs;realStartMs;handleFullscreenChange=()=>this.updateFullscreenButton();handlePointerMove=e=>{const t=this.canvasPointFromEvent(e),n=t&&this.currentSighting.timeline.hitTest(this.currentTime,t.x,t.y,this.occludedSourceIds);if(!n?.shape.title){this.tooltip.hidden=!0;return}this.tooltip.textContent=n.shape.title,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${e.clientX-s.left+12}px`,this.tooltip.style.top=`${e.clientY-s.top+12}px`};handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${D0}</style>${C0}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new rf(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.loopButton.addEventListener("click",()=>this.toggleLoop()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",()=>{this.enableClickToPlay&&this.togglePlayPause()}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange)}attributeChangedCallback(e,t,n){e==="src"&&n&&n!==t&&this.isConnected&&this.loadFromSrc(n)}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return af(this.currentSighting)}set sightingData(e){this.player.stop(),this.currentSighting=of(e),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh()}get sighting(){return this.currentSighting}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(e){this.player.seek(e)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}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(s=>this.highlightedSourceIds.has(s))||(this.highlightedSourceIds=t,this.refresh())}setOccludedSourceIds(e){e.size===this.occludedSourceIds.size&&[...e].every(n=>this.occludedSourceIds.has(n))||(this.occludedSourceIds=e,this.refresh())}hasVisibleShapeAt(e,t){return this.currentSighting.timeline.hitTest(this.currentTime,e,t,this.occludedSourceIds)!==void 0}refresh(){this.seekInput.max=String(this.player.seekableDuration),this.player.seek(this.player.time)}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 n=this.playbackState!=="playing"?this.highlightedSourceIds:Qc;for(const[s,r]of t){if(this.occludedSourceIds.has(s))continue;const a=n.has(s);a&&n.size===1?this.canvasRenderer.paintShape({...r,selected:!0}):(this.canvasRenderer.paintShape(r),a&&this.canvasRenderer.paintMemberOutline(r))}if(n.size>1){const s=Ke.groupBoundsFor([...t].filter(([r])=>n.has(r)).map(([,r])=>r.bounds));this.canvasRenderer.paintGroupHandles(s)}this.seekInput.value=String(e),this.timeStartLabel.textContent=this.formatPosition(e),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:e}}))}createPlayer(){const e=new j0(this.currentSighting.timeline,(t,n)=>this.onFrame(t,n));return e.loop=this.loopEnabled,e}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.refresh()):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 n=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=n,this.playPauseButton.setAttribute("aria-label",n),this.toolbar.classList.toggle("auto-hide",e),this.fullscreenButton.classList.toggle("auto-hide",e)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(e=>{console.error("<rr0-ufo>: requestFullscreen() failed —",e)})}updateFullscreenButton(){const e=document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=e?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}async loadLocaleMessages(){const e=Ia(navigator.languages,jd);e!=="en"&&this.applyMessages(await df(e))}applyMessages(e){this.messages=e,this.timeStartLabel.title=e.currentPosition,this.timeEndLabel.title=e.duration,this.loopButton.title=e.autoReplay,this.loopButton.setAttribute("aria-label",e.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton()}updateTimeLabels(){const e=this.currentSighting.event,t=dc(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?ko(e.time):void 0;const n=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&n>0?n/this.realDurationMs:1,this.player.durationOverrideMs=n>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time)}formatPosition(e){if(this.realDurationMs===void 0)return fr(e);const t=this.currentSighting.timeline.duration,n=t>0&&e<=t?e/t*this.realDurationMs:e;return this.realStartMs!==void 0?eh(jc(this.realStartMs+n)):fr(n)}formatEndOfTimeline(){return this.realDurationMs===void 0?fr(this.currentSighting.timeline.duration):this.realStartMs!==void 0?eh(jc(this.realStartMs+this.realDurationMs)):fr(this.realDurationMs)}}function jc(i){const e=new Date(i);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function eh(i){if(i.hour===void 0)return"0:00";const e=t=>String(t).padStart(2,"0");return i.second?`${e(i.hour)}:${e(i.minute??0)}:${e(i.second)}`:`${e(i.hour)}:${e(i.minute??0)}`}function fr(i){const e=Math.round(i/1e3),t=Math.floor(e/60),n=e%60;return`${t}:${String(n).padStart(2,"0")}`}const Wo="rr0-ufo";function Ra(){customElements.get(Wo)||customElements.define(Wo,mf)}const gf=`
703
703
  <div class="stage" id="stage">
704
704
  <div class="frame" id="frame">
705
705
  <canvas id="scene-canvas"></canvas>
@@ -5524,7 +5524,7 @@ void main()
5524
5524
 
5525
5525
  gl_FragColor = vec4(finalColor, max(dazzleAlpha, artifactsAlpha));
5526
5526
  }
5527
- `;let $r;function O2(){if($r)return $r;const i=256,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d");t.fillStyle="rgba(60,60,60,1)",t.fillRect(0,0,i,i);const n=[[.3,.35,.22,.5],[.7,.6,.16,.35],[.55,.8,.3,.4],[.15,.7,.12,.3],[.85,.2,.18,.3]];for(const[s,r,a,o]of n){const l=t.createRadialGradient(s*i,r*i,0,s*i,r*i,a*i);l.addColorStop(0,`rgba(200,200,200,${o})`),l.addColorStop(1,"rgba(200,200,200,0)"),t.fillStyle=l,t.fillRect(0,0,i,i)}return $r=new Wn(e),$r}function B2(){const i={uTime:{value:0},uResolution:{value:new Ue(1,1)},uLensPosition:{value:new Ue(0,0)},uColorGain:{value:new Se(55,55,51)},uOpacity:{value:.5},uFlareIntensity:{value:0},uStarPoints:{value:6},uGlareSize:{value:.55},uFlareSize:{value:.0025},uFlareSpeed:{value:.4},uFlareShape:{value:1.2},uHaloScale:{value:.5},uGhostScale:{value:.3},uAnimated:{value:!1},uAnamorphic:{value:!1},uSecondaryGhosts:{value:!0},uStarBurst:{value:!0},uAdditionalStreaks:{value:!0},uLensDirtTexture:{value:O2()}},e=new Ht({uniforms:i,vertexShader:U2,fragmentShader:F2,transparent:!0,depthWrite:!1,depthTest:!1,blending:ua,fog:!1,name:"LensFlareShader"}),t=new yt(new or(2,2),e);return t.frustumCulled=!1,t.renderOrder=20,{mesh:t,uniforms:i}}const ss=["front","behind","left","right","front-left","front-right","behind-left","behind-right"];function Nc(i){return i==="vehicle"?["front","behind","front-left","front-right","behind-left","behind-right"]:["front","behind","left","right"]}function z2(i){return i==="vehicle"?["front-left","front-right","behind-left","behind-right"]:Nc(i)}const ic=90,k2=50;function G2(i){if(!l0(i))return;const e={};for(const t of Nc(i))e[t]=sc(i,t)?k2:ic;return e}const ha=2;function l0(i){return i==="building"||i==="vehicle"}function sc(i,e){return i==="building"?!0:i==="vehicle"?e!=="front"&&e!=="behind":!1}function Zs(i){return i==="building"||i==="vehicle"}function rc(i,e){const t=i.litKeyframes;if(!t||t.length===0)return i.lit??!1;let n=t[0];for(const s of t){if(s.t>e)break;n=s}return n.lit}const yd=Math.PI/180;function nn(i,e,t,n,s=!1){const r=s?new rr({color:new Se(...t)}):new lr({color:new Se(...t)}),a=new yt(e,r);return a.position.y=n,a.userData={emissive:s},a.castShadow=!s,a.receiveShadow=!s,i.add(a),a}const c0=[.32,.32,.3],h0=[1,.85,.5],d0=[.25,.25,.25],u0=[1,.97,.85],H2=[.55,.68,.72],vn=.03,xn=.05,f0={front:0,behind:Math.PI,left:Math.PI/2,right:-Math.PI/2,"front-left":Math.PI/2,"behind-left":Math.PI/2,"front-right":-Math.PI/2,"behind-right":-Math.PI/2};function Gt(i,e){return i?.[e]}function _n(i,e,t,n,s,r,a,o){if(o===void 0)return;const l=new lr({color:new Se(...H2),transparent:!0,opacity:o/100}),c=new yt(new In(e,t,n),l);c.position.set(s,r,a),i.add(c)}const Qt=.05,Io=[.6,.58,.53],bd=[.5,.5,.48];function fs(i,e,t,n,s,r,a,o){const l=new lr({color:new Se(...o),side:Pt}),c=new yt(new In(e,t,n),l);c.position.set(s,r,a),i.add(c)}function V2(i,e,t,n,s,r,a,o,l){const c=(n-r)/2,h=(g,_,m,f)=>{g<=0||(e?fs(i,g,_,Qt,m,o+f,a,l):fs(i,Qt,_,g,a,o+f,m,l))};h(t,c,0,n/2-c/2),h(t,c,0,-(n/2-c/2));const u=[...s].sort((g,_)=>g.center-_.center);let d=-t/2;for(const g of u){const m=g.center-g.main/2-d;h(m,r,d+m/2,0),d=g.center+g.main/2}const p=t/2-d;h(p,r,d+p/2,0)}function p0(i,e,t,n,s,r,a,o,l,c){const h=(n+s)/2,u=s-n,d=(p,g,_,m)=>{p.length===0?g?fs(i,_,u,Qt,0,h,m,Io):fs(i,Qt,u,_,m,h,0,Io):V2(i,g,_,u,p,r,m,h,Io)};d(a,!0,e*2,-t+Qt),d(o,!0,e*2,t-Qt),d(l,!1,t*2,-e+Qt),d(c,!1,t*2,e-Qt),fs(i,e*2,Qt,t*2,0,n+Qt,0,bd),fs(i,e*2,Qt,t*2,0,s-Qt,0,bd)}function ac(i,e,t,n){switch(i){case"front":return{x:0,z:-(t-n)};case"behind":return{x:0,z:t-n};case"left":return{x:-(e-n),z:0};case"right":return{x:e-n,z:0};case"front-left":return{x:-(e-n),z:-Sn};case"front-right":return{x:e-n,z:-Sn};case"behind-left":return{x:-(e-n),z:Sn};case"behind-right":return{x:e-n,z:Sn}}}function m0(i,e,t,n,s,r,a){const o=x0();o.scale.setScalar(n),o.rotation.y=f0[e];const{x:l,z:c}=ac(e,s,r,a);o.position.set(l,t,c),i.add(o)}const oc=6,lc=6,si=3,ks=1.4,Gs=si*.5,g0=1.2,v0=.35,W2=.75,X2=1.6;function Y2(i,e,t,n){const s=new En,r=Math.max(1,i+1),a=r*si;nn(s,new In(oc,a,lc),[.55,.54,.5],a/2);const o=oc/2,l=lc/2;for(let c=0;c<r;c++){const h=c*si+si/2;_n(s,ks,Gs,xn,0,h,-l-vn,Gt(e,"front")),_n(s,ks,Gs,xn,0,h,l+vn,Gt(e,"behind")),_n(s,xn,Gs,ks,-o-vn,h,0,Gt(e,"left")),_n(s,xn,Gs,ks,o+vn,h,0,Gt(e,"right"))}if(t){const c=Math.min(Math.max(n??0,0),r-1);m0(s,t,c*si,.72,o,l,g0);const h=u=>Gt(e,u)!==void 0?[{main:ks,center:0}]:[];p0(s,o,l,c*si,(c+1)*si,Gs,h("front"),h("behind"),h("left"),h("right"))}return s}function q2(){const i=new En;return nn(i,new Ui(.15,.22,2,8),[.32,.22,.14],1),nn(i,new Pa(1.4,3,8),[.16,.32,.14],3.5),i}function $2(i){const e=new En;return nn(e,new Ui(.05,.08,5,8),[.28,.28,.3],2.5),nn(e,new fi(.25,12,8),i?h0:c0,5.1,!0),e}const K2=[.08,.08,.08],ii=.8,cs=1,mn=1.7,Ro=.6,_i=.4,Kr=1.2,rs=.7,Sn=.5,Z2=mn;function J2(i,e,t){const n=new En;nn(n,new In(1.8,1.4,4.2),[.45,.14,.14],.7),nn(n,new In(ii*2,Ro,cs*2),[.4,.12,.12],mn);for(const r of[-1.4,1.4])for(const a of[-.95,.95]){const o=nn(n,new Ui(.4,.4,.3,12),K2,.4);o.rotation.z=Math.PI/2,o.position.x=a,o.position.z=r}const s=i?u0:d0;for(const r of[-.7,.7])nn(n,new fi(.15,8,6),s,.7,!0).position.set(r,.7,-2.1);if(_n(n,Kr,_i,xn,0,mn,-cs-vn,Gt(e,"front")),_n(n,Kr,_i,xn,0,mn,cs+vn,Gt(e,"behind")),_n(n,xn,_i,rs,-ii-vn,mn,-Sn,Gt(e,"front-left")),_n(n,xn,_i,rs,-ii-vn,mn,Sn,Gt(e,"behind-left")),_n(n,xn,_i,rs,ii+vn,mn,-Sn,Gt(e,"front-right")),_n(n,xn,_i,rs,ii+vn,mn,Sn,Gt(e,"behind-right")),t){m0(n,t,W2,.5,ii,cs,v0);const r=(l,c)=>{const h=[];return Gt(e,l)!==void 0&&h.push({main:rs,center:-Sn}),Gt(e,c)!==void 0&&h.push({main:rs,center:Sn}),h},a=Gt(e,"front")!==void 0?[{main:Kr,center:0}]:[],o=Gt(e,"behind")!==void 0?[{main:Kr,center:0}]:[];p0(n,ii,cs,mn-Ro/2,mn+Ro/2,_i,a,o,r("front-left","behind-left"),r("front-right","behind-right"))}return n}const Q2=[.95,.9,.82];function x0(){const i=new En;nn(i,new Ui(.25,.3,1.5,10),[.3,.3,.35],.75),nn(i,new fi(.22,10,8),[.62,.52,.46],1.72);const e=nn(i,new Pa(.07,.16,8),Q2,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,i}class Hs{static occupantView(e){const t=e.witnessSide;if(!t)throw new Error("occupantView requires object.witnessSide to be set");const{x:n,z:s}=e.kind==="vehicle"?ac(t,ii,cs,v0):ac(t,oc/2,lc/2,g0),r=e.kind==="vehicle"?Z2:(e.occupiedFloor??0)*si+X2,a=(e.headingDeg??0)-f0[t]/yd;return{x:n,z:s,eyeY:r,headingDeg:a}}static build(e,t){const n=e.kind==="building"?Y2(e.floors??ha,e.windows,e.witnessSide,e.occupiedFloor):e.kind==="tree"?q2():e.kind==="streetlight"?$2(t):e.kind==="vehicle"?J2(t,e.windows,e.witnessSide):x0();return e.headingDeg!==void 0&&(n.rotation.y=-e.headingDeg*yd),n}static setLit(e,t,n){const s=t==="streetlight"?n?h0:c0:n?u0:d0;for(const r of e.children)!(r instanceof yt)||!r.userData.emissive||r.material.color.setRGB(...s)}static dispose(e){for(const t of e.children)t instanceof yt&&(t.geometry.dispose(),t.material.dispose())}}function j2(i,e){return i.cloudCover===e.cloudCover&&i.cloudDarkness===e.cloudDarkness&&i.cloudBaseM===e.cloudBaseM&&i.precipitationType===e.precipitationType&&i.precipitationIntensity===e.precipitationIntensity&&i.windDirectionDeg===e.windDirectionDeg&&i.windSpeed===e.windSpeed&&i.storm===e.storm}const Co=900,eS=.5,Js=900,tS=6371e3,nS=3e4;function Md(i){const e=Math.sqrt(2*tS*Math.max(i,0));return Math.max(Js,Math.min(e,nS))}const Do=.5,iS=850,Po=850,Lo=4,sS=1.5,rS=-1,aS=6,cc=-4,Ed=cc,oS=3,lS=.15,cS=.6,hS=40,dS=30,uS=[0,45,90,135,180,225,270,315],fS={en:["N","NE","E","SE","S","SW","W","NW"],fr:["N","NE","E","SE","S","SO","O","NO"]},pS=["en","fr"],mS=880,wd=40,gS=10,vS=1,xS=150,No=55,Td=.5,_S=700,SS=250/zo,yS=12,bS=.5,MS=5,as=[.92,.92,.95],Uo=[.15,.15,.19],Ad={snow:{fallSpeedMPerS:2,size:.3,color:new Se(1,1,1),driftSensitivity:1,radiusM:25,topYM:20,opacity:.75,poolSize:2400},hail:{fallSpeedMPerS:14,size:.5,color:new Se(.95,.98,1),driftSensitivity:.25,radiusM:25,topYM:10,opacity:.95,poolSize:1200}},hs=0,ES=0,Id=3e3,Vs=20,wS=15,Rd=hs,TS=40,Cd=1,AS=9;function IS(i){return Cd+(AS-Cd)*i}const RS=new Se(.75,.82,.92),CS=10,DS=.5;function PS(i){return _0(i/CS,DS,1)}const os=200,Dd=.18,Pd=.32,LS=.35,NS=new Se(.85,.9,.98),Ld=5,Nd=.85,Ud=.01;function Fo(i,e){return Math.round(i*(Ud+(1-Ud)*e))}const Fd=230,Od=.7,Bd=.05,US=.6,FS=.7,OS=.15,zd=.35,BS=3,zS=1/12,kS=.5,kd=8,Gd=25,Hd=.12,GS={Venus:new Se(1,.96,.85),Mars:new Se(1,.55,.4),Jupiter:new Se(.95,.87,.72),Saturn:new Se(.92,.86,.68)};class HS{renderer;scene=new pp;camera;celestialGroup=new En;skyMesh;groundMesh;terrainMesh;terrainProviders;terrainOrigin;terrainBuildToken=0;terrainAttribution;starTiers=[];bodyMeshes=new Map;hitAreas=new Map;glareSprites=new Map;decorObjects=[];decorGroups=new Map;celestialLight=new Op(16777215,0);celestialLightTarget=new bt;skyLight=new Lp(16777215,0,0);streetlightLights=new Map;compassSprites=[];showCompass=!1;compassHovered=!1;compassForced=!1;animationFrameId=null;raycaster=new go;weather=Zd;observerElevationM=0;groundRadius=Js;terrainRadius=Js;cloudMesh;cloudMaterial;cloudUniforms;precipitationPoints;precipitationPositions;precipitationPhase;precipitationSpeedJitter;precipitationWobbleFreqJitter;precipitationWobbleAmpJitter;precipitationBounceRemaining;precipitationBounceHeight;precipitationBounceDuration;precipitationBounceVelX;precipitationBounceVelZ;precipitationUniforms;rainSystem;rainSplashSystem;rainCameraDirection=new U;rainLastVerticalFacing=-1;lightningArmed=!1;nextLightningAtS=null;lightningFlashRemainingS=0;baseFogColor=[0,0,0];lastSunPosition;lensFlare;dazzleIntensity=1;lensFlareArtifactIntensity=0;sunWorldPosition=new U;sunVisible=!1;lensFlareScratch=new U;sunOcclusionRaycaster=new go;sunOcclusionDirectionScratch=new U;ufoOcclusionRaycaster=new go;onLightningFlash;constructor(e,t=p2(),n){this.renderer=new Ux({canvas:e,antialias:!0}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio||1,2)),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=Qs,this.camera=new qt(60,e.width/Math.max(e.height,1),.1,Co*1.2),this.camera.position.set(0,1.6,0),this.terrainProviders=t,this.onLightningFlash=n,this.celestialLight.shadow.mapSize.set(1024,1024),this.celestialLight.shadow.camera.left=-120,this.celestialLight.shadow.camera.right=120,this.celestialLight.shadow.camera.top=120,this.celestialLight.shadow.camera.bottom=-120,this.celestialLight.shadow.camera.near=1,this.celestialLight.shadow.camera.far=1e3,this.celestialLight.shadow.bias=-.0015,this.celestialLight.target=this.celestialLightTarget,this.scene.add(this.celestialLight,this.celestialLightTarget,this.skyLight),this.scene.add(this.celestialGroup)}get currentTerrainAttribution(){return this.terrainAttribution}resize(e,t){this.renderer.setSize(e,t,!1),this.camera.aspect=e/Math.max(t,1),this.camera.updateProjectionMatrix(),this.render()}setObserverPose(e){e.headingDeg!==void 0?this.camera.rotation.set(e.pitchDeg*Nn,-e.headingDeg*Nn,0,"YXZ"):this.camera.rotation.set(e.pitchDeg*Nn,this.camera.rotation.y,0,"YXZ"),e.elevationM!==this.observerElevationM&&(this.observerElevationM=e.elevationM,this.syncCloudLayer(),Math.abs(Md(e.elevationM)-this.groundRadius)>1&&(this.buildGround(),this.celestialGroup.scale.setScalar(this.groundRadius/Js),this.camera.far=Math.max(Co*1.2,this.groundRadius*2.5),this.camera.updateProjectionMatrix())),this.camera.position.y=1.6+e.elevationM,this.celestialGroup.position.y=this.camera.position.y,this.camera.fov!==e.fovDeg&&(this.camera.fov=e.fovDeg,this.camera.updateProjectionMatrix())}setTerrainOrigin(e,t,n){if(e===void 0||t===void 0)return;const s=this.groundRadius;if(this.terrainOrigin&&Math.abs(this.terrainRadius-s)<s*.1){const{x:a,z:o}=Pi(e,t,this.terrainOrigin.lat,this.terrainOrigin.lng);if(Math.sqrt(a*a+o*o)<xS*(s/Js))return}this.terrainOrigin={lat:e,lng:t},this.terrainRadius=s;const r=++this.terrainBuildToken;S2(e,t,this.terrainProviders,s).then(({mesh:a,attribution:o})=>{r===this.terrainBuildToken&&(this.disposeMesh(this.terrainMesh),a.renderOrder=vS,this.terrainMesh=a,this.terrainAttribution=o,this.scene.add(a),this.render(),n?.())}).catch(a=>{console.warn("Terrain build failed, keeping the flat ground fallback:",a),n?.()})}setShowCompass(e){this.showCompass!==e&&(this.showCompass=e,this.disposeCompassLabels(),e&&this.buildCompassLabels(),this.render())}setDazzleIntensity(e){this.dazzleIntensity!==e&&(this.dazzleIntensity=e,this.lensFlare&&(this.lensFlare.uniforms.uOpacity.value=Td*e,this.lastSunPosition&&this.applyLensFlareTint(Bs(this.lastSunPosition.altitudeDeg))),this.render())}setLensFlareArtifactIntensity(e){this.lensFlareArtifactIntensity!==e&&(this.lensFlareArtifactIntensity=e,this.lensFlare&&(this.lensFlare.uniforms.uFlareIntensity.value=e),this.render())}applyLensFlareTint(e){this.lensFlare&&this.lensFlare.uniforms.uColorGain.value.setRGB(No*this.dazzleIntensity*e[0],No*this.dazzleIntensity*e[1],No*this.dazzleIntensity*e[2])}setCompassHovered(e){this.compassHovered!==e&&(this.compassHovered=e,this.updateCompassVisibility())}setCompassForced(e){this.compassForced!==e&&(this.compassForced=e,this.updateCompassVisibility())}updateCompassVisibility(){const e=this.compassHovered||this.compassForced;for(const t of this.compassSprites)t.visible=e;this.render()}setWeather(e){j2(this.weather,e)||(this.weather=e,this.buildClouds(),this.buildPrecipitation(),this.lightningArmed=e.storm&&e.cloudDarkness>=kS,this.lightningArmed||(this.nextLightningAtS=null,this.lightningFlashRemainingS=0,this.scene.fog&&this.scene.fog.color.setRGB(...this.baseFogColor)),this.syncAnimationLoop(),this.render())}setDecor(e){if(this.decorObjects!==e){this.decorObjects=e;for(const t of this.decorGroups.values())t.removeFromParent(),Hs.dispose(t);this.decorGroups.clear(),this.streetlightLights.clear();for(const t of e){const n=Hs.build(t,t.lit??!1);this.scene.add(n),this.decorGroups.set(t.id,n),t.kind==="streetlight"&&this.addStreetlightLight(t.id,n)}this.celestialLight.castShadow=this.decorGroups.size>0}}updateDecorAnchoring(e,t){const n=e?.lat!==void 0&&e.lng!==void 0&&t?.lat!==void 0&&t?.lng!==void 0?Pi(e.lat,e.lng,t.lat,t.lng):{x:0,z:0},s=this.decorObjects.find(a=>a.witnessSide!==void 0&&Zs(a.kind)),r={x:0,z:0};if(s){const a=Hs.occupantView(s),o=-(s.headingDeg??0)*Nn,l=a.x*Math.cos(o)+a.z*Math.sin(o),c=-a.x*Math.sin(o)+a.z*Math.cos(o),h=s.eastM+n.x,u=-s.northM+n.z;r.x=-(h+l),r.z=-(u+c),this.camera.position.y=a.eyeY,this.camera.rotation.set(this.indoorLookPitchDeg*Nn,-(a.headingDeg+this.indoorLookYawDeg)*Nn,0,"YXZ")}for(const a of this.decorObjects){const o=this.decorGroups.get(a.id);o&&o.position.set(a.eastM+n.x+r.x,ES,-a.northM+n.z+r.z)}}indoorLookYawDeg=0;indoorLookPitchDeg=0;setIndoorLook(e,t){this.indoorLookYawDeg=e,this.indoorLookPitchDeg=t}updateDecorLitState(e){for(const t of this.decorObjects){if(t.kind!=="streetlight"&&t.kind!=="vehicle")continue;const n=this.decorGroups.get(t.id);if(!n)continue;const s=rc(t,e);Hs.setLit(n,t.kind,s);const r=this.streetlightLights.get(t.id);r&&(r.visible=s)}}addStreetlightLight(e,t){const n=t.children.find(r=>r.userData.emissive);if(!n)return;const s=new Up(16764006,hS,dS);s.position.copy(n.position),s.castShadow=!0,s.shadow.mapSize.set(512,512),s.shadow.bias=-.002,t.add(s),this.streetlightLights.set(e,s)}setAstronomy(e){const t=Ou(e.sun.altitudeDeg),n=t.horizon;this.baseFogColor=n,this.lastSunPosition=e.sun,this.buildSky(e.sun),this.buildGround(),this.updateCloudLighting(e.sun,n),this.updateCelestialLight(e,t.zenith,n),this.buildStars(e.stars,e.sun.altitudeDeg),this.setBodyMesh("sun",e.sun,Lo,new Se(1,.96,.88),e.sun.magnitude),this.setMoonMesh(e.moon),this.buildPlanets(e.planets),this.scene.fog=new Ec(new Se(...n),this.groundRadius*.2,this.groundRadius),this.syncAnimationLoop(),this.render()}updateCelestialLight(e,t,n){this.skyLight.color.setRGB(t[0],t[1],t[2]),this.skyLight.groundColor.setRGB(n[0],n[1],n[2]),this.skyLight.intensity=cS;const s=e.sun.altitudeDeg>=Ed,r=!s&&e.moon.altitudeDeg>=Ed;if(!s&&!r){this.celestialLight.intensity=0;return}const a=s?e.sun:e.moon,{x:o,y:l,z:c}=ns(a.altitudeDeg,a.azimuthDeg,Po);this.celestialLight.position.set(o,l,c);const h=Bs(a.altitudeDeg);this.celestialLight.color.setRGB(h[0],h[1],h[2]);const u=Math.max(0,Math.sin(Math.max(a.altitudeDeg,0)*Nn));this.celestialLight.intensity=(s?oS:lS)*u,this.celestialLight.castShadow=this.decorGroups.size>0}render(){this.updateLensFlarePosition(),this.renderer.render(this.scene,this.camera)}updateLensFlarePosition(){const e=this.lensFlare;if(!e)return;if(!this.sunVisible){e.mesh.visible=!1;return}if(this.camera.updateMatrixWorld(),this.lensFlareScratch.copy(this.sunWorldPosition).applyMatrix4(this.camera.matrixWorldInverse),this.lensFlareScratch.z>0){e.mesh.visible=!1;return}if(this.isSunOccluded()){e.mesh.visible=!1;return}e.mesh.visible=!0;const t=this.lensFlareScratch.copy(this.sunWorldPosition).project(this.camera);e.uniforms.uLensPosition.value.set(t.x,t.y),e.uniforms.uResolution.value.set(this.renderer.domElement.width,this.renderer.domElement.height)}isSunOccluded(){const e=[];this.groundMesh&&e.push(this.groundMesh),this.terrainMesh&&e.push(this.terrainMesh);for(const r of this.decorGroups.values())e.push(r);if(e.length===0)return!1;const t=this.sunOcclusionDirectionScratch.copy(this.sunWorldPosition).sub(this.camera.position),n=t.length();t.normalize(),this.sunOcclusionRaycaster.set(this.camera.position,t);const s=this.sunOcclusionRaycaster.intersectObjects(e,!0)[0];return s!==void 0&&s.distance<n}isScreenPointOccluded(e,t,n,s){if(this.isBehindCloudLayer(e,t,s))return!0;const r=[];for(const a of this.decorObjects){if(!a.occludesSourceIds?.includes(n))continue;const o=this.decorGroups.get(a.id);o&&r.push(o)}return r.length===0?!1:(this.ufoOcclusionRaycaster.setFromCamera(new Ue(e,t),this.camera),this.ufoOcclusionRaycaster.near=eS,this.ufoOcclusionRaycaster.intersectObjects(r,!0).length>0)}isBehindCloudLayer(e,t,n){if(n===void 0||!this.cloudMesh||this.weather.cloudCover<=0)return!1;const s=this.cloudLayerOffset();this.ufoOcclusionRaycaster.setFromCamera(new Ue(e,t),this.camera);const r=this.ufoOcclusionRaycaster.ray.direction;return Math.sign(r.y||1)!==Math.sign(s)||Math.abs(this.cloudLayerHeightM())/Math.max(Math.abs(r.y),.026)>=n?!1:Mt.alphaAt(r,Math.abs(s),this.weather.cloudCover)>=bS}cloudLayerHeightM(){return(this.weather.cloudBaseM??zo)-this.observerElevationM}pickBodyAt(e,t){this.raycaster.setFromCamera(new Ue(e,t),this.camera);const n=[...this.hitAreas.entries()],s=this.raycaster.intersectObjects(n.map(([,r])=>r))[0];if(s)return n.find(([,r])=>r===s.object)?.[0]}pickDecorAt(e,t){this.raycaster.setFromCamera(new Ue(e,t),this.camera);const n=[...this.decorGroups.entries()],s=this.raycaster.intersectObjects(n.map(([,a])=>a),!0)[0];if(!s)return;let r=s.object;for(;r&&!n.some(([,a])=>a===r);)r=r.parent;return n.find(([,a])=>a===r)?.[0]}dispose(){this.stopTwinkle(),this.terrainBuildToken++,this.disposeMesh(this.skyMesh),this.disposeMesh(this.groundMesh),this.disposeMesh(this.terrainMesh),this.disposeCloudSystem(),this.disposePrecipitationPoints(),this.disposeRain(),this.disposeStarTiers();for(const e of this.bodyMeshes.values())this.disposeMesh(e);this.bodyMeshes.clear();for(const e of[...this.hitAreas.keys()])this.disposeHitArea(e);for(const e of[...this.glareSprites.keys()])this.disposeGlare(e);this.disposeCompassLabels();for(const e of this.decorGroups.values())Hs.dispose(e);this.decorGroups.clear(),this.streetlightLights.clear(),this.lensFlare&&(this.lensFlare.mesh.removeFromParent(),this.lensFlare.mesh.geometry.dispose(),this.lensFlare.mesh.material.dispose(),this.lensFlare=void 0),this.renderer.dispose()}startTwinkle(){if(this.animationFrameId!==null||!this.needsAnimationLoop())return;let e;const t=n=>{const s=e===void 0?0:Math.min((n-e)/1e3,zS);e=n,this.updateTwinkle(n/1e3),this.updatePrecipitation(n/1e3,s),this.updateRain(s),this.updateLightning(n/1e3,s),this.lensFlare&&(this.lensFlare.uniforms.uTime.value=n/1e3),this.render(),this.animationFrameId=requestAnimationFrame(t)};this.animationFrameId=requestAnimationFrame(t)}stopTwinkle(){this.animationFrameId!==null&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}needsAnimationLoop(){return this.starTiers.length>0||this.precipitationPoints!==void 0||this.rainSystem!==void 0||this.lightningArmed||this.lensFlare!==void 0&&this.sunVisible}syncAnimationLoop(){this.needsAnimationLoop()?this.startTwinkle():this.stopTwinkle()}buildSky(e){this.disposeMesh(this.skyMesh);const t=new fi(Co,32,16),n=t.attributes.position,s=[];for(let a=0;a<n.count;a++){const{altitudeDeg:o,azimuthDeg:l}=zx(n.getX(a),n.getY(a),n.getZ(a)),c=Hx(o,l,e.azimuthDeg,e.altitudeDeg);s.push(c[0],c[1],c[2])}t.setAttribute("color",new Et(s,3));const r=new rr({vertexColors:!0,side:Pt,fog:!1});this.skyMesh=new yt(t,r),this.celestialGroup.add(this.skyMesh)}buildGround(){this.disposeMesh(this.groundMesh),this.groundRadius=Md(this.observerElevationM);const e=new Ac(this.groundRadius,48),t=new lr({color:new Se(Do,Do,Do),fog:!0});this.groundMesh=new yt(e,t),this.groundMesh.rotation.x=-Math.PI/2,this.groundMesh.position.y=0,this.groundMesh.receiveShadow=!0,this.scene.add(this.groundMesh)}setBodyMesh(e,t,n,s,r){if(this.disposeMesh(this.bodyMeshes.get(e)),this.disposeHitArea(e),this.disposeGlare(e),t.altitudeDeg<cc){this.bodyMeshes.delete(e),e==="sun"&&(this.sunVisible=!1);return}const{x:a,y:o,z:l}=ns(t.altitudeDeg,t.azimuthDeg,Po),c=Bs(t.altitudeDeg),h=new Se(s.r*c[0],s.g*c[1],s.b*c[2]),u=new fi(n,16,16),d=new rr({color:h,fog:!1}),p=new yt(u,d);if(p.position.set(a,o,l),this.celestialGroup.add(p),this.bodyMeshes.set(e,p),this.setHitArea(e,a,o,l,n),e!=="sun"&&this.setGlare(e,a,o,l,r,h),e==="sun"){this.sunVisible=!0;const g=this.celestialGroup.scale.x;this.sunWorldPosition.set(a*g,o*g+this.celestialGroup.position.y,l*g),this.lensFlare||(this.lensFlare=B2(),this.lensFlare.uniforms.uOpacity.value=Td*this.dazzleIntensity,this.lensFlare.uniforms.uFlareIntensity.value=this.lensFlareArtifactIntensity,this.scene.add(this.lensFlare.mesh)),this.applyLensFlareTint(c)}}setMoonMesh(e){const t="moon";if(this.disposeMesh(this.bodyMeshes.get(t)),this.disposeHitArea(t),this.disposeGlare(t),e.altitudeDeg<cc){this.bodyMeshes.delete(t);return}const{x:n,y:s,z:r}=ns(e.altitudeDeg,e.azimuthDeg,Po),a=Bs(e.altitudeDeg),o=new Se(a[0],a[1],a[2]),l=new Ys({map:QS(e.phase),color:o,fog:!1}),c=new Er(l);c.position.set(n,s,r);const h=Lo*2;c.scale.set(h,h,1),this.celestialGroup.add(c),this.bodyMeshes.set(t,c),this.setHitArea(t,n,s,r,Lo),this.setGlare(t,n,s,r,e.magnitude,o)}setHitArea(e,t,n,s,r){const a=new Ys({transparent:!0,opacity:0,depthTest:!1,depthWrite:!1,fog:!1}),o=new Er(a);o.position.set(t,n,s);const l=r*aS;o.scale.set(l,l,1),this.celestialGroup.add(o),this.hitAreas.set(e,o)}disposeHitArea(e){const t=this.hitAreas.get(e);t&&(t.removeFromParent(),t.material.dispose(),this.hitAreas.delete(e))}setGlare(e,t,n,s,r,a){this.disposeGlare(e);const o=Wx(r);if(o<=0)return;const l=qx(o),c=new Ys({map:JS(),color:a,transparent:!0,opacity:Zx(o),blending:ua,depthWrite:!1,fog:!1}),h=new Er(c);h.position.set(t,n,s),h.scale.set(l*2,l*2,1),this.celestialGroup.add(h),this.glareSprites.set(e,h)}disposeGlare(e){const t=this.glareSprites.get(e);t&&(t.removeFromParent(),t.material.dispose(),this.glareSprites.delete(e))}buildPlanets(e){const t=new Set(["sun","moon"]);for(const n of e){t.add(n.body);const s=sd(n.magnitude),r=rd(s),a=GS[n.body]??new Se(1,1,1),o=new Se(a.r*r,a.g*r,a.b*r),l=sS*(.5+.5*s);this.setBodyMesh(n.body,n.position,l,o,n.magnitude)}for(const n of[...this.bodyMeshes.keys()])t.has(n)||(this.disposeMesh(this.bodyMeshes.get(n)),this.bodyMeshes.delete(n),this.disposeHitArea(n),this.disposeGlare(n))}buildStars(e,t){if(this.disposeStarTiers(),!e||e.catalog.count===0){this.syncAnimationLoop();return}const{catalog:n,date:s,observer:r}=e,a=Fx(t),o=Oo(1337),l=[];for(let c=0;c<n.count;c++){const h=n.mag[c];if(h>a)continue;const{altitudeDeg:u,azimuthDeg:d}=J_(n.ra[c],n.dec[c],s,r);if(u<rS)continue;const{x:p,y:g,z:_}=ns(u,d,iS);l.push({x:p,y:g,z:_,brightness:sd(h),phase:o()*Math.PI*2,speedFactor:.7+o()*.6})}this.starTiers=zu.map((c,h)=>{const u=l.filter(S=>Qx(S.brightness)===h),d=new Float32Array(u.length*3),p=new Float32Array(u.length),g=new Float32Array(u.length),_=new Float32Array(u.length);u.forEach((S,b)=>{d[b*3]=S.x,d[b*3+1]=S.y,d[b*3+2]=S.z,p[b]=S.brightness,g[b]=S.phase,_[b]=S.speedFactor});const m=new wt;m.setAttribute("position",new mt(d,3));const f=new mt(new Float32Array(u.length*3),3);m.setAttribute("color",f);const T=new bu({vertexColors:!0,size:c.size,sizeAttenuation:!1,fog:!1}),A=new oa(m,T);return this.celestialGroup.add(A),{points:A,colorAttribute:f,brightness:p,phase:g,speedFactor:_}}),this.updateTwinkle(0),this.startTwinkle()}updateTwinkle(e){for(const t of this.starTiers){const n=t.colorAttribute.array;for(let s=0;s<t.brightness.length;s++){const r=t_(t.brightness[s],{phase:t.phase[s],speedFactor:t.speedFactor[s]},e),a=rd(t.brightness[s])*r;n[s*3]=a,n[s*3+1]=a,n[s*3+2]=a}t.colorAttribute.needsUpdate=!0}}buildClouds(){if(this.disposeCloudSystem(),this.weather.cloudCover<=0)return;const e=this.weather.cloudDarkness,t=new Se(as[0]+(Uo[0]-as[0])*e,as[1]+(Uo[1]-as[1])*e,as[2]+(Uo[2]-as[2])*e),{material:n,uniforms:s}=P2(t,this.weather.cloudCover,Math.abs(this.cloudLayerOffset()));this.cloudMaterial=n,this.cloudUniforms=s,this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);const r=N2(_S);this.cloudMesh=new yt(r,n),this.cloudLayerOffset()<0&&(this.cloudMesh.scale.y=-1),this.cloudMesh.renderOrder=MS,this.celestialGroup.add(this.cloudMesh)}syncCloudLayer(){const e=this.cloudLayerOffset();this.cloudUniforms&&(this.cloudUniforms.layerHeight.value=Math.abs(e)),this.cloudMesh&&(this.cloudMesh.scale.y=e<0?-1:1)}cloudLayerOffset(){const t=((this.weather.cloudBaseM??zo)-this.observerElevationM)*SS;return Math.sign(t||1)*Math.max(Math.abs(t),yS)}updateCloudLighting(e,t){if(!this.cloudUniforms)return;const{x:n,y:s,z:r}=ns(e.altitudeDeg,e.azimuthDeg,1),a=Bs(e.altitudeDeg);this.cloudUniforms.sunDir.value.set(n,s,r),this.cloudUniforms.sunColor.value.setRGB(a[0],.96*a[1],.88*a[2]),this.cloudUniforms.ambientColor.value.setRGB(t[0],t[1],t[2])}disposeCloudSystem(){this.cloudMesh&&(this.cloudMesh.removeFromParent(),this.cloudMesh.geometry.dispose()),this.cloudMaterial?.dispose(),this.cloudMesh=void 0,this.cloudMaterial=void 0,this.cloudUniforms=void 0}disposePrecipitationPoints(){this.precipitationPoints&&(this.precipitationPoints.removeFromParent(),this.precipitationPoints.geometry.dispose(),this.precipitationPoints.material.dispose()),this.precipitationPoints=void 0,this.precipitationPositions=void 0,this.precipitationPhase=void 0,this.precipitationSpeedJitter=void 0,this.precipitationWobbleFreqJitter=void 0,this.precipitationWobbleAmpJitter=void 0,this.precipitationBounceRemaining=void 0,this.precipitationBounceHeight=void 0,this.precipitationBounceDuration=void 0,this.precipitationBounceVelX=void 0,this.precipitationBounceVelZ=void 0,this.precipitationUniforms=void 0}buildPrecipitation(){this.disposePrecipitationPoints(),this.disposeRain();const e=this.weather.precipitationType;if(e==="none")return;if(e==="rain"){this.buildRain();return}const t=Ad[e],n=t.poolSize,s=Oo(9001),r=new Float32Array(n*3),a=new Float32Array(n),o=new Float32Array(n),l=new Float32Array(n),c=new Float32Array(n),h=new Float32Array(n),u=new Float32Array(n),d=new Float32Array(n),p=new Float32Array(n),g=new Float32Array(n);for(let A=0;A<n;A++){const S=s()*Math.PI*2,b=s()*t.radiusM;r[A*3]=Math.cos(S)*b,r[A*3+1]=hs+s()*(t.topYM-hs),r[A*3+2]=Math.sin(S)*b,a[A]=s()*Math.PI*2,o[A]=.7+s()*.6,l[A]=.6+s()*.8,c[A]=.4+s()*1.2}const _=new wt;_.setAttribute("position",new mt(r,3)),_.setDrawRange(0,Fo(n,this.weather.precipitationIntensity));const m={uSize:{value:t.size},uScale:{value:.5*this.renderer.domElement.height},uOpacity:{value:t.opacity},uColor:{value:t.color},uHazeColor:{value:new Se(...this.baseFogColor)},uTexture:{value:VS(e)},uNearFocusDistance:{value:Ld},uHazeDistance:{value:t.radiusM*Nd}},f=new Ht({uniforms:m,vertexShader:KS,fragmentShader:ZS,transparent:!0,depthWrite:!1});this.precipitationUniforms=m;const T=new oa(_,f);this.scene.add(T),this.precipitationPoints=T,this.precipitationPositions=r,this.precipitationPhase=a,this.precipitationSpeedJitter=o,this.precipitationWobbleFreqJitter=l,this.precipitationWobbleAmpJitter=c,this.precipitationBounceRemaining=h,this.precipitationBounceHeight=u,this.precipitationBounceDuration=d,this.precipitationBounceVelX=p,this.precipitationBounceVelZ=g}updatePrecipitation(e,t){if(!this.precipitationPoints||!this.precipitationPositions||!this.precipitationPhase||!this.precipitationSpeedJitter||(this.precipitationUniforms?.uHazeColor.value.setRGB(...this.baseFogColor),t<=0))return;const n=this.weather.precipitationType;if(n==="none"||n==="rain")return;const s=Ad[n],r=this.weather.windDirectionDeg*Nn,a=Math.sin(r)*this.weather.windSpeed*s.driftSensitivity,o=-Math.cos(r)*this.weather.windSpeed*s.driftSensitivity,l=this.precipitationPositions,c=this.precipitationPhase,h=this.precipitationSpeedJitter,u=this.precipitationWobbleFreqJitter,d=this.precipitationWobbleAmpJitter,p=this.precipitationBounceRemaining,g=this.precipitationBounceHeight,_=this.precipitationBounceDuration,m=this.precipitationBounceVelX,f=this.precipitationBounceVelZ,T=n==="snow"&&u&&d,A=n==="hail"&&p&&g&&_&&m&&f;for(let S=0,b=0;S<l.length;S+=3,b++){if(A&&p[b]>0){if(p[b]-=t,p[b]<=0){const x=Math.random()*Math.PI*2,M=Math.sqrt(Math.random())*s.radiusM;l[S]=Math.cos(x)*M,l[S+1]=s.topYM,l[S+2]=Math.sin(x)*M,p[b]=0}else{const x=1-p[b]/_[b];l[S+1]=hs+Math.sin(Math.PI*x)*g[b],l[S]=Jr(l[S]+(a+m[b])*t,s.radiusM),l[S+2]=Jr(l[S+2]+(o+f[b])*t,s.radiusM)}continue}let E=0,I=0;if(T){const x=US*u[b],M=FS*d[b];E=Math.sin(e*x*Math.PI*2+c[b])*M,I=Math.sin(e*x*Math.PI*2+c[b]+Math.PI/2)*M}if(l[S]+=(a+E)*t,l[S+1]-=s.fallSpeedMPerS*h[b]*t,l[S+2]+=(o+I)*t,l[S]=Jr(l[S],s.radiusM),l[S+2]=Jr(l[S+2],s.radiusM),l[S+1]<hs){if(A){g[b]=zd*(.2+Math.random()*1.6),_[b]=OS*(.5+Math.random()),p[b]=_[b];const C=Math.random()*Math.PI*2,R=BS*(g[b]/zd);m[b]=Math.cos(C)*R,f[b]=Math.sin(C)*R,l[S+1]=hs;continue}const x=Math.random()*Math.PI*2,M=Math.sqrt(Math.random())*s.radiusM;l[S]=Math.cos(x)*M,l[S+1]=s.topYM,l[S+2]=Math.sin(x)*M}}this.precipitationPoints.geometry.attributes.position.needsUpdate=!0}buildRain(){const e=IS(this.weather.precipitationIntensity);this.rainSystem=R2({count:Id,radiusM:Vs,heightM:wS,bottomYM:Rd,overallSpeed:TS,color:RS,opacity:.85,visibleCount:Fo(Id,this.weather.precipitationIntensity),speedStreak:PS(e),pixelSize:Fd,seed:9001,nearFocusDistanceM:Ld,hazeDistanceM:Vs*Nd,hazeColor:new Se(...this.baseFogColor)}),this.rainLastVerticalFacing=-1,this.scene.add(this.rainSystem.points),this.buildRainSplashes()}buildRainSplashes(){const e=Oo(4242),t=new Float32Array(os*3),n=new Float32Array(os),s=new Float32Array(os),r=new Float32Array(os);for(let h=0;h<os;h++){const u=e()*Math.PI*2,d=Math.sqrt(e())*Vs;t[h*3]=Math.cos(u)*d,t[h*3+1]=Rd,t[h*3+2]=Math.sin(u)*d,s[h]=Zr(Dd,Pd),n[h]=e()*s[h],r[h]=n[h]/s[h]}const a=new wt;a.setAttribute("position",new mt(t,3));const o=new mt(r,1);a.setAttribute("aLife",o),a.setDrawRange(0,Fo(os,this.weather.precipitationIntensity));const l=new Ht({uniforms:{uMaxSize:{value:LS},uScale:{value:.5*this.renderer.domElement.height},uColor:{value:NS},uTexture:{value:YS()}},vertexShader:qS,fragmentShader:$S,transparent:!0,depthWrite:!1}),c=new oa(a,l);this.scene.add(c),this.rainSplashSystem={points:c,positions:t,life:n,duration:s,lifeAttribute:o}}disposeRain(){this.rainSystem&&(this.rainSystem.points.removeFromParent(),this.rainSystem.points.geometry.dispose(),this.rainSystem.points.material.dispose()),this.rainSystem=void 0,this.disposeRainSplashes()}disposeRainSplashes(){this.rainSplashSystem&&(this.rainSplashSystem.points.removeFromParent(),this.rainSplashSystem.points.geometry.dispose(),this.rainSplashSystem.points.material.dispose()),this.rainSplashSystem=void 0}updateRain(e){const t=this.rainSystem;if(!t)return;t.uniforms.uTime.value+=e,t.uniforms.uHazeColor.value.setRGB(...this.baseFogColor);const n=this.weather.windDirectionDeg*Nn,s=Math.sin(n)*this.weather.windSpeed,r=-Math.cos(n)*this.weather.windSpeed,a=Vs,o=c=>(c%(a*2)+a*3)%(a*2)-a;t.uniforms.uWindOffset.value.set(o(t.uniforms.uWindOffset.value.x+s*e),o(t.uniforms.uWindOffset.value.y+r*e)),this.camera.getWorldDirection(this.rainCameraDirection);const l=Math.abs(this.rainCameraDirection.y);if(Math.abs(l-this.rainLastVerticalFacing)>.001){this.rainLastVerticalFacing=l;const c=Od+(1-Od)*(1-l),h=Bd+(1-Bd)*(1-l);t.uniforms.uUvSquash.value=h,t.uniforms.uPixelSize.value=Fd*c*(.5+.5*h)}this.updateRainSplashes(e)}updateRainSplashes(e){const t=this.rainSplashSystem;if(!t||e<=0)return;const{positions:n,life:s,duration:r,lifeAttribute:a}=t,o=a.array;for(let l=0,c=0;l<n.length;l+=3,c++){if(s[c]+=e,s[c]>=r[c]){const h=Math.random()*Math.PI*2,u=Math.sqrt(Math.random())*Vs;n[l]=Math.cos(h)*u,n[l+2]=Math.sin(h)*u,r[c]=Zr(Dd,Pd),s[c]=0}o[c]=s[c]/r[c]}t.points.geometry.attributes.position.needsUpdate=!0,a.needsUpdate=!0}updateLightning(e,t){if(this.lightningArmed){if(this.lightningFlashRemainingS>0){this.lightningFlashRemainingS=Math.max(0,this.lightningFlashRemainingS-t);const n=this.lightningFlashRemainingS/Hd;this.scene.fog&&this.scene.fog.color.setRGB(this.baseFogColor[0]+(1-this.baseFogColor[0])*n,this.baseFogColor[1]+(1-this.baseFogColor[1])*n,this.baseFogColor[2]+(1-this.baseFogColor[2])*n);return}if(this.nextLightningAtS===null){this.nextLightningAtS=e+Zr(kd,Gd);return}e>=this.nextLightningAtS&&(this.lightningFlashRemainingS=Hd,this.nextLightningAtS=e+Zr(kd,Gd),this.onLightningFlash?.())}}buildCompassLabels(){const e=Ia(navigator.languages,pS),t=fS[e];this.compassSprites=uS.map((n,s)=>{const r=t[s],a=new Ys({map:jS(r),depthTest:!1,fog:!1}),o=new Er(a),{x:l,y:c,z:h}=ns(0,n,mS);return o.position.set(l,c,h),o.scale.set(wd,wd,1),o.renderOrder=gS,o.visible=this.compassHovered||this.compassForced,this.celestialGroup.add(o),o})}disposeCompassLabels(){for(const e of this.compassSprites)e.removeFromParent(),e.material.map?.dispose(),e.material.dispose();this.compassSprites=[]}disposeStarTiers(){for(const e of this.starTiers)this.disposeMesh(e.points);this.starTiers=[]}disposeMesh(e){if(!e)return;e.removeFromParent(),"geometry"in e&&e.geometry.dispose();const t=e.material;for(const n of Array.isArray(t)?t:[t]){const s=n.map;s&&s.dispose(),n.dispose()}}}const Nn=Math.PI/180;function _0(i,e,t){return Math.max(e,Math.min(t,i))}function Zr(i,e){return i+Math.random()*(e-i)}function Jr(i,e){return(i%(e*2)+e*3)%(e*2)-e}let Qr,jr;function VS(i){switch(i){case"snow":return WS();case"hail":return XS()}}function WS(){if(Qr)return Qr;const i=64,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,0.95)"),n.addColorStop(.5,"rgba(255,255,255,0.55)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),Qr=new Wn(e),Qr}function XS(){if(jr)return jr;const i=64,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,1)"),n.addColorStop(.55,"rgba(255,255,255,0.95)"),n.addColorStop(.8,"rgba(255,255,255,0.4)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),jr=new Wn(e),jr}let ea;function YS(){if(ea)return ea;const i=64,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,0)"),n.addColorStop(.4,"rgba(255,255,255,0)"),n.addColorStop(.6,"rgba(255,255,255,0.9)"),n.addColorStop(.8,"rgba(255,255,255,0.25)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),ea=new Wn(e),ea}const qS=`
5527
+ `;let $r;function O2(){if($r)return $r;const i=256,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d");t.fillStyle="rgba(60,60,60,1)",t.fillRect(0,0,i,i);const n=[[.3,.35,.22,.5],[.7,.6,.16,.35],[.55,.8,.3,.4],[.15,.7,.12,.3],[.85,.2,.18,.3]];for(const[s,r,a,o]of n){const l=t.createRadialGradient(s*i,r*i,0,s*i,r*i,a*i);l.addColorStop(0,`rgba(200,200,200,${o})`),l.addColorStop(1,"rgba(200,200,200,0)"),t.fillStyle=l,t.fillRect(0,0,i,i)}return $r=new Wn(e),$r}function B2(){const i={uTime:{value:0},uResolution:{value:new Ue(1,1)},uLensPosition:{value:new Ue(0,0)},uColorGain:{value:new Se(55,55,51)},uOpacity:{value:.5},uFlareIntensity:{value:0},uStarPoints:{value:6},uGlareSize:{value:.55},uFlareSize:{value:.0025},uFlareSpeed:{value:.4},uFlareShape:{value:1.2},uHaloScale:{value:.5},uGhostScale:{value:.3},uAnimated:{value:!1},uAnamorphic:{value:!1},uSecondaryGhosts:{value:!0},uStarBurst:{value:!0},uAdditionalStreaks:{value:!0},uLensDirtTexture:{value:O2()}},e=new Ht({uniforms:i,vertexShader:U2,fragmentShader:F2,transparent:!0,depthWrite:!1,depthTest:!1,blending:ua,fog:!1,name:"LensFlareShader"}),t=new yt(new or(2,2),e);return t.frustumCulled=!1,t.renderOrder=20,{mesh:t,uniforms:i}}const ss=["front","behind","left","right","front-left","front-right","behind-left","behind-right"];function Nc(i){return i==="vehicle"?["front","behind","front-left","front-right","behind-left","behind-right"]:["front","behind","left","right"]}function z2(i){return i==="vehicle"?["front-left","front-right","behind-left","behind-right"]:Nc(i)}const ic=90,k2=50;function G2(i){if(!l0(i))return;const e={};for(const t of Nc(i))e[t]=sc(i,t)?k2:ic;return e}const ha=2;function l0(i){return i==="building"||i==="vehicle"}function sc(i,e){return i==="building"?!0:i==="vehicle"?e!=="front"&&e!=="behind":!1}function Zs(i){return i==="building"||i==="vehicle"}function rc(i,e){const t=i.litKeyframes;if(!t||t.length===0)return i.lit??!1;let n=t[0];for(const s of t){if(s.t>e)break;n=s}return n.lit}const yd=Math.PI/180;function nn(i,e,t,n,s=!1){const r=s?new rr({color:new Se(...t)}):new lr({color:new Se(...t)}),a=new yt(e,r);return a.position.y=n,a.userData={emissive:s},a.castShadow=!s,a.receiveShadow=!s,i.add(a),a}const c0=[.32,.32,.3],h0=[1,.85,.5],d0=[.25,.25,.25],u0=[1,.97,.85],H2=[.55,.68,.72],vn=.03,xn=.05,f0={front:0,behind:Math.PI,left:Math.PI/2,right:-Math.PI/2,"front-left":Math.PI/2,"behind-left":Math.PI/2,"front-right":-Math.PI/2,"behind-right":-Math.PI/2};function Gt(i,e){return i?.[e]}function _n(i,e,t,n,s,r,a,o){if(o===void 0)return;const l=new lr({color:new Se(...H2),transparent:!0,opacity:o/100}),c=new yt(new In(e,t,n),l);c.position.set(s,r,a),i.add(c)}const Qt=.05,Io=[.6,.58,.53],bd=[.5,.5,.48];function fs(i,e,t,n,s,r,a,o){const l=new lr({color:new Se(...o),side:Pt}),c=new yt(new In(e,t,n),l);c.position.set(s,r,a),i.add(c)}function V2(i,e,t,n,s,r,a,o,l){const c=(n-r)/2,h=(g,_,m,f)=>{g<=0||(e?fs(i,g,_,Qt,m,o+f,a,l):fs(i,Qt,_,g,a,o+f,m,l))};h(t,c,0,n/2-c/2),h(t,c,0,-(n/2-c/2));const u=[...s].sort((g,_)=>g.center-_.center);let d=-t/2;for(const g of u){const m=g.center-g.main/2-d;h(m,r,d+m/2,0),d=g.center+g.main/2}const p=t/2-d;h(p,r,d+p/2,0)}function p0(i,e,t,n,s,r,a,o,l,c){const h=(n+s)/2,u=s-n,d=(p,g,_,m)=>{p.length===0?g?fs(i,_,u,Qt,0,h,m,Io):fs(i,Qt,u,_,m,h,0,Io):V2(i,g,_,u,p,r,m,h,Io)};d(a,!0,e*2,-t+Qt),d(o,!0,e*2,t-Qt),d(l,!1,t*2,-e+Qt),d(c,!1,t*2,e-Qt),fs(i,e*2,Qt,t*2,0,n+Qt,0,bd),fs(i,e*2,Qt,t*2,0,s-Qt,0,bd)}function ac(i,e,t,n){switch(i){case"front":return{x:0,z:-(t-n)};case"behind":return{x:0,z:t-n};case"left":return{x:-(e-n),z:0};case"right":return{x:e-n,z:0};case"front-left":return{x:-(e-n),z:-Sn};case"front-right":return{x:e-n,z:-Sn};case"behind-left":return{x:-(e-n),z:Sn};case"behind-right":return{x:e-n,z:Sn}}}function m0(i,e,t,n,s,r,a){const o=x0();o.scale.setScalar(n),o.rotation.y=f0[e];const{x:l,z:c}=ac(e,s,r,a);o.position.set(l,t,c),i.add(o)}const oc=6,lc=6,si=3,ks=1.4,Gs=si*.5,g0=1.2,v0=.35,W2=.75,X2=1.6;function Y2(i,e,t,n){const s=new En,r=Math.max(1,i+1),a=r*si;nn(s,new In(oc,a,lc),[.55,.54,.5],a/2);const o=oc/2,l=lc/2;for(let c=0;c<r;c++){const h=c*si+si/2;_n(s,ks,Gs,xn,0,h,-l-vn,Gt(e,"front")),_n(s,ks,Gs,xn,0,h,l+vn,Gt(e,"behind")),_n(s,xn,Gs,ks,-o-vn,h,0,Gt(e,"left")),_n(s,xn,Gs,ks,o+vn,h,0,Gt(e,"right"))}if(t){const c=Math.min(Math.max(n??0,0),r-1);m0(s,t,c*si,.72,o,l,g0);const h=u=>Gt(e,u)!==void 0?[{main:ks,center:0}]:[];p0(s,o,l,c*si,(c+1)*si,Gs,h("front"),h("behind"),h("left"),h("right"))}return s}function q2(){const i=new En;return nn(i,new Ui(.15,.22,2,8),[.32,.22,.14],1),nn(i,new Pa(1.4,3,8),[.16,.32,.14],3.5),i}function $2(i){const e=new En;return nn(e,new Ui(.05,.08,5,8),[.28,.28,.3],2.5),nn(e,new fi(.25,12,8),i?h0:c0,5.1,!0),e}const K2=[.08,.08,.08],ii=.8,cs=1,mn=1.7,Ro=.6,_i=.4,Kr=1.2,rs=.7,Sn=.5,Z2=mn;function J2(i,e,t){const n=new En;nn(n,new In(1.8,1.4,4.2),[.45,.14,.14],.7),nn(n,new In(ii*2,Ro,cs*2),[.4,.12,.12],mn);for(const r of[-1.4,1.4])for(const a of[-.95,.95]){const o=nn(n,new Ui(.4,.4,.3,12),K2,.4);o.rotation.z=Math.PI/2,o.position.x=a,o.position.z=r}const s=i?u0:d0;for(const r of[-.7,.7])nn(n,new fi(.15,8,6),s,.7,!0).position.set(r,.7,-2.1);if(_n(n,Kr,_i,xn,0,mn,-cs-vn,Gt(e,"front")),_n(n,Kr,_i,xn,0,mn,cs+vn,Gt(e,"behind")),_n(n,xn,_i,rs,-ii-vn,mn,-Sn,Gt(e,"front-left")),_n(n,xn,_i,rs,-ii-vn,mn,Sn,Gt(e,"behind-left")),_n(n,xn,_i,rs,ii+vn,mn,-Sn,Gt(e,"front-right")),_n(n,xn,_i,rs,ii+vn,mn,Sn,Gt(e,"behind-right")),t){m0(n,t,W2,.5,ii,cs,v0);const r=(l,c)=>{const h=[];return Gt(e,l)!==void 0&&h.push({main:rs,center:-Sn}),Gt(e,c)!==void 0&&h.push({main:rs,center:Sn}),h},a=Gt(e,"front")!==void 0?[{main:Kr,center:0}]:[],o=Gt(e,"behind")!==void 0?[{main:Kr,center:0}]:[];p0(n,ii,cs,mn-Ro/2,mn+Ro/2,_i,a,o,r("front-left","behind-left"),r("front-right","behind-right"))}return n}const Q2=[.95,.9,.82];function x0(){const i=new En;nn(i,new Ui(.25,.3,1.5,10),[.3,.3,.35],.75),nn(i,new fi(.22,10,8),[.62,.52,.46],1.72);const e=nn(i,new Pa(.07,.16,8),Q2,1.72);return e.rotation.x=-Math.PI/2,e.position.z=-.2,i}class Hs{static occupantView(e){const t=e.witnessSide;if(!t)throw new Error("occupantView requires object.witnessSide to be set");const{x:n,z:s}=e.kind==="vehicle"?ac(t,ii,cs,v0):ac(t,oc/2,lc/2,g0),r=e.kind==="vehicle"?Z2:(e.occupiedFloor??0)*si+X2,a=(e.headingDeg??0)-f0[t]/yd;return{x:n,z:s,eyeY:r,headingDeg:a}}static build(e,t){const n=e.kind==="building"?Y2(e.floors??ha,e.windows,e.witnessSide,e.occupiedFloor):e.kind==="tree"?q2():e.kind==="streetlight"?$2(t):e.kind==="vehicle"?J2(t,e.windows,e.witnessSide):x0();return e.headingDeg!==void 0&&(n.rotation.y=-e.headingDeg*yd),n}static setLit(e,t,n){const s=t==="streetlight"?n?h0:c0:n?u0:d0;for(const r of e.children)!(r instanceof yt)||!r.userData.emissive||r.material.color.setRGB(...s)}static dispose(e){for(const t of e.children)t instanceof yt&&(t.geometry.dispose(),t.material.dispose())}}function j2(i,e){return i.cloudCover===e.cloudCover&&i.cloudDarkness===e.cloudDarkness&&i.cloudBaseM===e.cloudBaseM&&i.precipitationType===e.precipitationType&&i.precipitationIntensity===e.precipitationIntensity&&i.windDirectionDeg===e.windDirectionDeg&&i.windSpeed===e.windSpeed&&i.storm===e.storm}const Co=900,eS=.5,Js=900,tS=6371e3,nS=3e4;function Md(i){const e=Math.sqrt(2*tS*Math.max(i,0));return Math.max(Js,Math.min(e,nS))}const Do=.5,iS=850,Po=850,Lo=4,sS=1.5,rS=-1,aS=6,cc=-4,Ed=cc,oS=3,lS=.15,cS=.6,hS=40,dS=30,uS=[0,45,90,135,180,225,270,315],fS={en:["N","NE","E","SE","S","SW","W","NW"],fr:["N","NE","E","SE","S","SO","O","NO"]},pS=["en","fr"],mS=880,wd=40,gS=10,vS=1,xS=150,No=55,Td=.5,_S=700,SS=250/zo,yS=12,bS=.5,MS=5,as=[.92,.92,.95],Uo=[.15,.15,.19],Ad={snow:{fallSpeedMPerS:2,size:.3,color:new Se(1,1,1),driftSensitivity:1,radiusM:25,topYM:20,opacity:.75,poolSize:2400},hail:{fallSpeedMPerS:14,size:.5,color:new Se(.95,.98,1),driftSensitivity:.25,radiusM:25,topYM:10,opacity:.95,poolSize:1200}},hs=0,ES=0,Id=3e3,Vs=20,wS=15,Rd=hs,TS=40,Cd=1,AS=9;function IS(i){return Cd+(AS-Cd)*i}const RS=new Se(.75,.82,.92),CS=10,DS=.5;function PS(i){return _0(i/CS,DS,1)}const os=200,Dd=.18,Pd=.32,LS=.35,NS=new Se(.85,.9,.98),Ld=5,Nd=.85,Ud=.01;function Fo(i,e){return Math.round(i*(Ud+(1-Ud)*e))}const Fd=230,Od=.7,Bd=.05,US=.6,FS=.7,OS=.15,zd=.35,BS=3,zS=1/12,kS=.5,kd=8,Gd=25,Hd=.12,GS={Venus:new Se(1,.96,.85),Mars:new Se(1,.55,.4),Jupiter:new Se(.95,.87,.72),Saturn:new Se(.92,.86,.68)};class HS{renderer;scene=new pp;camera;celestialGroup=new En;skyMesh;groundMesh;terrainMesh;terrainProviders;terrainOrigin;terrainBuildToken=0;terrainAttribution;starTiers=[];bodyMeshes=new Map;hitAreas=new Map;glareSprites=new Map;decorObjects=[];decorGroups=new Map;celestialLight=new Op(16777215,0);celestialLightTarget=new bt;skyLight=new Lp(16777215,0,0);streetlightLights=new Map;compassSprites=[];showCompass=!1;compassHovered=!1;compassForced=!1;animationFrameId=null;raycaster=new go;weather=Zd;observerElevationM=0;groundRadius=Js;terrainRadius=Js;cloudMesh;cloudMaterial;cloudUniforms;precipitationPoints;precipitationPositions;precipitationPhase;precipitationSpeedJitter;precipitationWobbleFreqJitter;precipitationWobbleAmpJitter;precipitationBounceRemaining;precipitationBounceHeight;precipitationBounceDuration;precipitationBounceVelX;precipitationBounceVelZ;precipitationUniforms;rainSystem;rainSplashSystem;rainCameraDirection=new U;rainLastVerticalFacing=-1;lightningArmed=!1;nextLightningAtS=null;lightningFlashRemainingS=0;baseFogColor=[0,0,0];lastSunPosition;lensFlare;dazzleIntensity=1;lensFlareArtifactIntensity=0;sunWorldPosition=new U;sunVisible=!1;lensFlareScratch=new U;sunOcclusionRaycaster=new go;sunOcclusionDirectionScratch=new U;ufoOcclusionRaycaster=new go;onLightningFlash;constructor(e,t=p2(),n){this.renderer=new Ux({canvas:e,antialias:!0}),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio||1,2)),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=Qs,this.camera=new qt(60,e.width/Math.max(e.height,1),.1,Co*1.2),this.camera.position.set(0,1.6,0),this.terrainProviders=t,this.onLightningFlash=n,this.celestialLight.shadow.mapSize.set(1024,1024),this.celestialLight.shadow.camera.left=-120,this.celestialLight.shadow.camera.right=120,this.celestialLight.shadow.camera.top=120,this.celestialLight.shadow.camera.bottom=-120,this.celestialLight.shadow.camera.near=1,this.celestialLight.shadow.camera.far=1e3,this.celestialLight.shadow.bias=-.0015,this.celestialLight.target=this.celestialLightTarget,this.scene.add(this.celestialLight,this.celestialLightTarget,this.skyLight),this.scene.add(this.celestialGroup)}get currentTerrainAttribution(){return this.terrainAttribution}resize(e,t){this.renderer.setSize(e,t,!1),this.camera.aspect=e/Math.max(t,1),this.camera.updateProjectionMatrix(),this.render()}setObserverPose(e){e.headingDeg!==void 0?this.camera.rotation.set(e.pitchDeg*Nn,-e.headingDeg*Nn,0,"YXZ"):this.camera.rotation.set(e.pitchDeg*Nn,this.camera.rotation.y,0,"YXZ"),e.elevationM!==this.observerElevationM&&(this.observerElevationM=e.elevationM,this.syncCloudLayer(),Math.abs(Md(e.elevationM)-this.groundRadius)>1&&(this.buildGround(),this.celestialGroup.scale.setScalar(this.groundRadius/Js),this.camera.far=Math.max(Co*1.2,this.groundRadius*2.5),this.camera.updateProjectionMatrix())),this.camera.position.y=1.6+e.elevationM,this.celestialGroup.position.y=this.camera.position.y,this.camera.fov!==e.fovDeg&&(this.camera.fov=e.fovDeg,this.camera.updateProjectionMatrix())}setTerrainOrigin(e,t,n){if(e===void 0||t===void 0)return;const s=this.groundRadius;if(this.terrainOrigin&&Math.abs(this.terrainRadius-s)<s*.1){const{x:a,z:o}=Pi(e,t,this.terrainOrigin.lat,this.terrainOrigin.lng);if(Math.sqrt(a*a+o*o)<xS*(s/Js))return}this.terrainOrigin={lat:e,lng:t},this.terrainRadius=s;const r=++this.terrainBuildToken;S2(e,t,this.terrainProviders,s).then(({mesh:a,attribution:o})=>{r===this.terrainBuildToken&&(this.disposeMesh(this.terrainMesh),a.renderOrder=vS,this.terrainMesh=a,this.terrainAttribution=o,this.scene.add(a),this.render(),n?.())}).catch(a=>{console.warn("Terrain build failed, keeping the flat ground fallback:",a),n?.()})}setShowCompass(e){this.showCompass!==e&&(this.showCompass=e,this.disposeCompassLabels(),e&&this.buildCompassLabels(),this.render())}setDazzleIntensity(e){this.dazzleIntensity!==e&&(this.dazzleIntensity=e,this.lensFlare&&(this.lensFlare.uniforms.uOpacity.value=Td*e,this.lastSunPosition&&this.applyLensFlareTint(Bs(this.lastSunPosition.altitudeDeg))),this.render())}setLensFlareArtifactIntensity(e){this.lensFlareArtifactIntensity!==e&&(this.lensFlareArtifactIntensity=e,this.lensFlare&&(this.lensFlare.uniforms.uFlareIntensity.value=e),this.render())}applyLensFlareTint(e){this.lensFlare&&this.lensFlare.uniforms.uColorGain.value.setRGB(No*this.dazzleIntensity*e[0],No*this.dazzleIntensity*e[1],No*this.dazzleIntensity*e[2])}setCompassHovered(e){this.compassHovered!==e&&(this.compassHovered=e,this.updateCompassVisibility())}setCompassForced(e){this.compassForced!==e&&(this.compassForced=e,this.updateCompassVisibility())}updateCompassVisibility(){const e=this.compassHovered||this.compassForced;for(const t of this.compassSprites)t.visible=e;this.render()}setWeather(e){j2(this.weather,e)||(this.weather=e,this.buildClouds(),this.buildPrecipitation(),this.lightningArmed=e.storm&&e.cloudDarkness>=kS,this.lightningArmed||(this.nextLightningAtS=null,this.lightningFlashRemainingS=0,this.scene.fog&&this.scene.fog.color.setRGB(...this.baseFogColor)),this.syncAnimationLoop(),this.render())}setDecor(e){if(this.decorObjects!==e){this.decorObjects=e;for(const t of this.decorGroups.values())t.removeFromParent(),Hs.dispose(t);this.decorGroups.clear(),this.streetlightLights.clear();for(const t of e){const n=Hs.build(t,t.lit??!1);this.scene.add(n),this.decorGroups.set(t.id,n),t.kind==="streetlight"&&this.addStreetlightLight(t.id,n)}this.celestialLight.castShadow=this.decorGroups.size>0}}updateDecorAnchoring(e,t){const n=e?.lat!==void 0&&e.lng!==void 0&&t?.lat!==void 0&&t?.lng!==void 0?Pi(e.lat,e.lng,t.lat,t.lng):{x:0,z:0},s=this.decorObjects.find(a=>a.witnessSide!==void 0&&Zs(a.kind)),r={x:0,z:0};if(s){const a=Hs.occupantView(s),o=-(s.headingDeg??0)*Nn,l=a.x*Math.cos(o)+a.z*Math.sin(o),c=-a.x*Math.sin(o)+a.z*Math.cos(o),h=s.eastM+n.x,u=-s.northM+n.z;r.x=-(h+l),r.z=-(u+c),this.camera.position.y=a.eyeY,this.camera.rotation.set(this.indoorLookPitchDeg*Nn,-(a.headingDeg+this.indoorLookYawDeg)*Nn,0,"YXZ")}for(const a of this.decorObjects){const o=this.decorGroups.get(a.id);o&&o.position.set(a.eastM+n.x+r.x,ES,-a.northM+n.z+r.z)}}indoorLookYawDeg=0;indoorLookPitchDeg=0;setIndoorLook(e,t){this.indoorLookYawDeg=e,this.indoorLookPitchDeg=t}updateDecorLitState(e){for(const t of this.decorObjects){if(t.kind!=="streetlight"&&t.kind!=="vehicle")continue;const n=this.decorGroups.get(t.id);if(!n)continue;const s=rc(t,e);Hs.setLit(n,t.kind,s);const r=this.streetlightLights.get(t.id);r&&(r.visible=s)}}addStreetlightLight(e,t){const n=t.children.find(r=>r.userData.emissive);if(!n)return;const s=new Up(16764006,hS,dS);s.position.copy(n.position),s.castShadow=!0,s.shadow.mapSize.set(512,512),s.shadow.bias=-.002,t.add(s),this.streetlightLights.set(e,s)}setAstronomy(e){const t=Ou(e.sun.altitudeDeg),n=t.horizon;this.baseFogColor=n,this.lastSunPosition=e.sun,this.buildSky(e.sun),this.buildGround(),this.updateCloudLighting(e.sun,n),this.updateCelestialLight(e,t.zenith,n),this.buildStars(e.stars,e.sun.altitudeDeg),this.setBodyMesh("sun",e.sun,Lo,new Se(1,.96,.88),e.sun.magnitude),this.setMoonMesh(e.moon),this.buildPlanets(e.planets),this.scene.fog=new Ec(new Se(...n),this.groundRadius*.2,this.groundRadius),this.syncAnimationLoop(),this.render()}updateCelestialLight(e,t,n){this.skyLight.color.setRGB(t[0],t[1],t[2]),this.skyLight.groundColor.setRGB(n[0],n[1],n[2]),this.skyLight.intensity=cS;const s=e.sun.altitudeDeg>=Ed,r=!s&&e.moon.altitudeDeg>=Ed;if(!s&&!r){this.celestialLight.intensity=0;return}const a=s?e.sun:e.moon,{x:o,y:l,z:c}=ns(a.altitudeDeg,a.azimuthDeg,Po);this.celestialLight.position.set(o,l,c);const h=Bs(a.altitudeDeg);this.celestialLight.color.setRGB(h[0],h[1],h[2]);const u=Math.max(0,Math.sin(Math.max(a.altitudeDeg,0)*Nn));this.celestialLight.intensity=(s?oS:lS)*u,this.celestialLight.castShadow=this.decorGroups.size>0}render(){this.updateLensFlarePosition(),this.renderer.render(this.scene,this.camera)}updateLensFlarePosition(){const e=this.lensFlare;if(!e)return;if(!this.sunVisible){e.mesh.visible=!1;return}if(this.camera.updateMatrixWorld(),this.lensFlareScratch.copy(this.sunWorldPosition).applyMatrix4(this.camera.matrixWorldInverse),this.lensFlareScratch.z>0){e.mesh.visible=!1;return}if(this.isSunOccluded()){e.mesh.visible=!1;return}e.mesh.visible=!0;const t=this.lensFlareScratch.copy(this.sunWorldPosition).project(this.camera);e.uniforms.uLensPosition.value.set(t.x,t.y),e.uniforms.uResolution.value.set(this.renderer.domElement.width,this.renderer.domElement.height)}isSunOccluded(){const e=[];this.groundMesh&&e.push(this.groundMesh),this.terrainMesh&&e.push(this.terrainMesh);for(const r of this.decorGroups.values())e.push(r);if(e.length===0)return!1;const t=this.sunOcclusionDirectionScratch.copy(this.sunWorldPosition).sub(this.camera.position),n=t.length();t.normalize(),this.sunOcclusionRaycaster.set(this.camera.position,t);const s=this.sunOcclusionRaycaster.intersectObjects(e,!0)[0];return s!==void 0&&s.distance<n}isScreenPointOccluded(e,t,n,s){if(this.isBehindCloudLayer(e,t,s))return!0;const r=[];for(const a of this.decorObjects){if(!a.occludesSourceIds?.includes(n))continue;const o=this.decorGroups.get(a.id);o&&r.push(o)}return r.length===0?!1:(this.ufoOcclusionRaycaster.setFromCamera(new Ue(e,t),this.camera),this.ufoOcclusionRaycaster.near=eS,this.ufoOcclusionRaycaster.intersectObjects(r,!0).length>0)}isBehindCloudLayer(e,t,n){if(!n||!this.cloudMesh||this.weather.cloudCover<=0)return!1;if(n.declared)return!0;const s=n.distanceM;if(s===void 0)return!1;const r=this.cloudLayerOffset();this.ufoOcclusionRaycaster.setFromCamera(new Ue(e,t),this.camera);const a=this.ufoOcclusionRaycaster.ray.direction;return Math.sign(a.y||1)!==Math.sign(r)||Math.abs(this.cloudLayerHeightM())/Math.max(Math.abs(a.y),.026)>=s?!1:Mt.alphaAt(a,Math.abs(r),this.weather.cloudCover)>=bS}cloudLayerHeightM(){return(this.weather.cloudBaseM??zo)-this.observerElevationM}pickBodyAt(e,t){this.raycaster.setFromCamera(new Ue(e,t),this.camera);const n=[...this.hitAreas.entries()],s=this.raycaster.intersectObjects(n.map(([,r])=>r))[0];if(s)return n.find(([,r])=>r===s.object)?.[0]}pickDecorAt(e,t){this.raycaster.setFromCamera(new Ue(e,t),this.camera);const n=[...this.decorGroups.entries()],s=this.raycaster.intersectObjects(n.map(([,a])=>a),!0)[0];if(!s)return;let r=s.object;for(;r&&!n.some(([,a])=>a===r);)r=r.parent;return n.find(([,a])=>a===r)?.[0]}dispose(){this.stopTwinkle(),this.terrainBuildToken++,this.disposeMesh(this.skyMesh),this.disposeMesh(this.groundMesh),this.disposeMesh(this.terrainMesh),this.disposeCloudSystem(),this.disposePrecipitationPoints(),this.disposeRain(),this.disposeStarTiers();for(const e of this.bodyMeshes.values())this.disposeMesh(e);this.bodyMeshes.clear();for(const e of[...this.hitAreas.keys()])this.disposeHitArea(e);for(const e of[...this.glareSprites.keys()])this.disposeGlare(e);this.disposeCompassLabels();for(const e of this.decorGroups.values())Hs.dispose(e);this.decorGroups.clear(),this.streetlightLights.clear(),this.lensFlare&&(this.lensFlare.mesh.removeFromParent(),this.lensFlare.mesh.geometry.dispose(),this.lensFlare.mesh.material.dispose(),this.lensFlare=void 0),this.renderer.dispose()}startTwinkle(){if(this.animationFrameId!==null||!this.needsAnimationLoop())return;let e;const t=n=>{const s=e===void 0?0:Math.min((n-e)/1e3,zS);e=n,this.updateTwinkle(n/1e3),this.updatePrecipitation(n/1e3,s),this.updateRain(s),this.updateLightning(n/1e3,s),this.lensFlare&&(this.lensFlare.uniforms.uTime.value=n/1e3),this.render(),this.animationFrameId=requestAnimationFrame(t)};this.animationFrameId=requestAnimationFrame(t)}stopTwinkle(){this.animationFrameId!==null&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}needsAnimationLoop(){return this.starTiers.length>0||this.precipitationPoints!==void 0||this.rainSystem!==void 0||this.lightningArmed||this.lensFlare!==void 0&&this.sunVisible}syncAnimationLoop(){this.needsAnimationLoop()?this.startTwinkle():this.stopTwinkle()}buildSky(e){this.disposeMesh(this.skyMesh);const t=new fi(Co,32,16),n=t.attributes.position,s=[];for(let a=0;a<n.count;a++){const{altitudeDeg:o,azimuthDeg:l}=zx(n.getX(a),n.getY(a),n.getZ(a)),c=Hx(o,l,e.azimuthDeg,e.altitudeDeg);s.push(c[0],c[1],c[2])}t.setAttribute("color",new Et(s,3));const r=new rr({vertexColors:!0,side:Pt,fog:!1});this.skyMesh=new yt(t,r),this.celestialGroup.add(this.skyMesh)}buildGround(){this.disposeMesh(this.groundMesh),this.groundRadius=Md(this.observerElevationM);const e=new Ac(this.groundRadius,48),t=new lr({color:new Se(Do,Do,Do),fog:!0});this.groundMesh=new yt(e,t),this.groundMesh.rotation.x=-Math.PI/2,this.groundMesh.position.y=0,this.groundMesh.receiveShadow=!0,this.scene.add(this.groundMesh)}setBodyMesh(e,t,n,s,r){if(this.disposeMesh(this.bodyMeshes.get(e)),this.disposeHitArea(e),this.disposeGlare(e),t.altitudeDeg<cc){this.bodyMeshes.delete(e),e==="sun"&&(this.sunVisible=!1);return}const{x:a,y:o,z:l}=ns(t.altitudeDeg,t.azimuthDeg,Po),c=Bs(t.altitudeDeg),h=new Se(s.r*c[0],s.g*c[1],s.b*c[2]),u=new fi(n,16,16),d=new rr({color:h,fog:!1}),p=new yt(u,d);if(p.position.set(a,o,l),this.celestialGroup.add(p),this.bodyMeshes.set(e,p),this.setHitArea(e,a,o,l,n),e!=="sun"&&this.setGlare(e,a,o,l,r,h),e==="sun"){this.sunVisible=!0;const g=this.celestialGroup.scale.x;this.sunWorldPosition.set(a*g,o*g+this.celestialGroup.position.y,l*g),this.lensFlare||(this.lensFlare=B2(),this.lensFlare.uniforms.uOpacity.value=Td*this.dazzleIntensity,this.lensFlare.uniforms.uFlareIntensity.value=this.lensFlareArtifactIntensity,this.scene.add(this.lensFlare.mesh)),this.applyLensFlareTint(c)}}setMoonMesh(e){const t="moon";if(this.disposeMesh(this.bodyMeshes.get(t)),this.disposeHitArea(t),this.disposeGlare(t),e.altitudeDeg<cc){this.bodyMeshes.delete(t);return}const{x:n,y:s,z:r}=ns(e.altitudeDeg,e.azimuthDeg,Po),a=Bs(e.altitudeDeg),o=new Se(a[0],a[1],a[2]),l=new Ys({map:QS(e.phase),color:o,fog:!1}),c=new Er(l);c.position.set(n,s,r);const h=Lo*2;c.scale.set(h,h,1),this.celestialGroup.add(c),this.bodyMeshes.set(t,c),this.setHitArea(t,n,s,r,Lo),this.setGlare(t,n,s,r,e.magnitude,o)}setHitArea(e,t,n,s,r){const a=new Ys({transparent:!0,opacity:0,depthTest:!1,depthWrite:!1,fog:!1}),o=new Er(a);o.position.set(t,n,s);const l=r*aS;o.scale.set(l,l,1),this.celestialGroup.add(o),this.hitAreas.set(e,o)}disposeHitArea(e){const t=this.hitAreas.get(e);t&&(t.removeFromParent(),t.material.dispose(),this.hitAreas.delete(e))}setGlare(e,t,n,s,r,a){this.disposeGlare(e);const o=Wx(r);if(o<=0)return;const l=qx(o),c=new Ys({map:JS(),color:a,transparent:!0,opacity:Zx(o),blending:ua,depthWrite:!1,fog:!1}),h=new Er(c);h.position.set(t,n,s),h.scale.set(l*2,l*2,1),this.celestialGroup.add(h),this.glareSprites.set(e,h)}disposeGlare(e){const t=this.glareSprites.get(e);t&&(t.removeFromParent(),t.material.dispose(),this.glareSprites.delete(e))}buildPlanets(e){const t=new Set(["sun","moon"]);for(const n of e){t.add(n.body);const s=sd(n.magnitude),r=rd(s),a=GS[n.body]??new Se(1,1,1),o=new Se(a.r*r,a.g*r,a.b*r),l=sS*(.5+.5*s);this.setBodyMesh(n.body,n.position,l,o,n.magnitude)}for(const n of[...this.bodyMeshes.keys()])t.has(n)||(this.disposeMesh(this.bodyMeshes.get(n)),this.bodyMeshes.delete(n),this.disposeHitArea(n),this.disposeGlare(n))}buildStars(e,t){if(this.disposeStarTiers(),!e||e.catalog.count===0){this.syncAnimationLoop();return}const{catalog:n,date:s,observer:r}=e,a=Fx(t),o=Oo(1337),l=[];for(let c=0;c<n.count;c++){const h=n.mag[c];if(h>a)continue;const{altitudeDeg:u,azimuthDeg:d}=J_(n.ra[c],n.dec[c],s,r);if(u<rS)continue;const{x:p,y:g,z:_}=ns(u,d,iS);l.push({x:p,y:g,z:_,brightness:sd(h),phase:o()*Math.PI*2,speedFactor:.7+o()*.6})}this.starTiers=zu.map((c,h)=>{const u=l.filter(S=>Qx(S.brightness)===h),d=new Float32Array(u.length*3),p=new Float32Array(u.length),g=new Float32Array(u.length),_=new Float32Array(u.length);u.forEach((S,b)=>{d[b*3]=S.x,d[b*3+1]=S.y,d[b*3+2]=S.z,p[b]=S.brightness,g[b]=S.phase,_[b]=S.speedFactor});const m=new wt;m.setAttribute("position",new mt(d,3));const f=new mt(new Float32Array(u.length*3),3);m.setAttribute("color",f);const T=new bu({vertexColors:!0,size:c.size,sizeAttenuation:!1,fog:!1}),A=new oa(m,T);return this.celestialGroup.add(A),{points:A,colorAttribute:f,brightness:p,phase:g,speedFactor:_}}),this.updateTwinkle(0),this.startTwinkle()}updateTwinkle(e){for(const t of this.starTiers){const n=t.colorAttribute.array;for(let s=0;s<t.brightness.length;s++){const r=t_(t.brightness[s],{phase:t.phase[s],speedFactor:t.speedFactor[s]},e),a=rd(t.brightness[s])*r;n[s*3]=a,n[s*3+1]=a,n[s*3+2]=a}t.colorAttribute.needsUpdate=!0}}buildClouds(){if(this.disposeCloudSystem(),this.weather.cloudCover<=0)return;const e=this.weather.cloudDarkness,t=new Se(as[0]+(Uo[0]-as[0])*e,as[1]+(Uo[1]-as[1])*e,as[2]+(Uo[2]-as[2])*e),{material:n,uniforms:s}=P2(t,this.weather.cloudCover,Math.abs(this.cloudLayerOffset()));this.cloudMaterial=n,this.cloudUniforms=s,this.lastSunPosition&&this.updateCloudLighting(this.lastSunPosition,this.baseFogColor);const r=N2(_S);this.cloudMesh=new yt(r,n),this.cloudLayerOffset()<0&&(this.cloudMesh.scale.y=-1),this.cloudMesh.renderOrder=MS,this.celestialGroup.add(this.cloudMesh)}syncCloudLayer(){const e=this.cloudLayerOffset();this.cloudUniforms&&(this.cloudUniforms.layerHeight.value=Math.abs(e)),this.cloudMesh&&(this.cloudMesh.scale.y=e<0?-1:1)}cloudLayerOffset(){const t=((this.weather.cloudBaseM??zo)-this.observerElevationM)*SS;return Math.sign(t||1)*Math.max(Math.abs(t),yS)}updateCloudLighting(e,t){if(!this.cloudUniforms)return;const{x:n,y:s,z:r}=ns(e.altitudeDeg,e.azimuthDeg,1),a=Bs(e.altitudeDeg);this.cloudUniforms.sunDir.value.set(n,s,r),this.cloudUniforms.sunColor.value.setRGB(a[0],.96*a[1],.88*a[2]),this.cloudUniforms.ambientColor.value.setRGB(t[0],t[1],t[2])}disposeCloudSystem(){this.cloudMesh&&(this.cloudMesh.removeFromParent(),this.cloudMesh.geometry.dispose()),this.cloudMaterial?.dispose(),this.cloudMesh=void 0,this.cloudMaterial=void 0,this.cloudUniforms=void 0}disposePrecipitationPoints(){this.precipitationPoints&&(this.precipitationPoints.removeFromParent(),this.precipitationPoints.geometry.dispose(),this.precipitationPoints.material.dispose()),this.precipitationPoints=void 0,this.precipitationPositions=void 0,this.precipitationPhase=void 0,this.precipitationSpeedJitter=void 0,this.precipitationWobbleFreqJitter=void 0,this.precipitationWobbleAmpJitter=void 0,this.precipitationBounceRemaining=void 0,this.precipitationBounceHeight=void 0,this.precipitationBounceDuration=void 0,this.precipitationBounceVelX=void 0,this.precipitationBounceVelZ=void 0,this.precipitationUniforms=void 0}buildPrecipitation(){this.disposePrecipitationPoints(),this.disposeRain();const e=this.weather.precipitationType;if(e==="none")return;if(e==="rain"){this.buildRain();return}const t=Ad[e],n=t.poolSize,s=Oo(9001),r=new Float32Array(n*3),a=new Float32Array(n),o=new Float32Array(n),l=new Float32Array(n),c=new Float32Array(n),h=new Float32Array(n),u=new Float32Array(n),d=new Float32Array(n),p=new Float32Array(n),g=new Float32Array(n);for(let A=0;A<n;A++){const S=s()*Math.PI*2,b=s()*t.radiusM;r[A*3]=Math.cos(S)*b,r[A*3+1]=hs+s()*(t.topYM-hs),r[A*3+2]=Math.sin(S)*b,a[A]=s()*Math.PI*2,o[A]=.7+s()*.6,l[A]=.6+s()*.8,c[A]=.4+s()*1.2}const _=new wt;_.setAttribute("position",new mt(r,3)),_.setDrawRange(0,Fo(n,this.weather.precipitationIntensity));const m={uSize:{value:t.size},uScale:{value:.5*this.renderer.domElement.height},uOpacity:{value:t.opacity},uColor:{value:t.color},uHazeColor:{value:new Se(...this.baseFogColor)},uTexture:{value:VS(e)},uNearFocusDistance:{value:Ld},uHazeDistance:{value:t.radiusM*Nd}},f=new Ht({uniforms:m,vertexShader:KS,fragmentShader:ZS,transparent:!0,depthWrite:!1});this.precipitationUniforms=m;const T=new oa(_,f);this.scene.add(T),this.precipitationPoints=T,this.precipitationPositions=r,this.precipitationPhase=a,this.precipitationSpeedJitter=o,this.precipitationWobbleFreqJitter=l,this.precipitationWobbleAmpJitter=c,this.precipitationBounceRemaining=h,this.precipitationBounceHeight=u,this.precipitationBounceDuration=d,this.precipitationBounceVelX=p,this.precipitationBounceVelZ=g}updatePrecipitation(e,t){if(!this.precipitationPoints||!this.precipitationPositions||!this.precipitationPhase||!this.precipitationSpeedJitter||(this.precipitationUniforms?.uHazeColor.value.setRGB(...this.baseFogColor),t<=0))return;const n=this.weather.precipitationType;if(n==="none"||n==="rain")return;const s=Ad[n],r=this.weather.windDirectionDeg*Nn,a=Math.sin(r)*this.weather.windSpeed*s.driftSensitivity,o=-Math.cos(r)*this.weather.windSpeed*s.driftSensitivity,l=this.precipitationPositions,c=this.precipitationPhase,h=this.precipitationSpeedJitter,u=this.precipitationWobbleFreqJitter,d=this.precipitationWobbleAmpJitter,p=this.precipitationBounceRemaining,g=this.precipitationBounceHeight,_=this.precipitationBounceDuration,m=this.precipitationBounceVelX,f=this.precipitationBounceVelZ,T=n==="snow"&&u&&d,A=n==="hail"&&p&&g&&_&&m&&f;for(let S=0,b=0;S<l.length;S+=3,b++){if(A&&p[b]>0){if(p[b]-=t,p[b]<=0){const x=Math.random()*Math.PI*2,M=Math.sqrt(Math.random())*s.radiusM;l[S]=Math.cos(x)*M,l[S+1]=s.topYM,l[S+2]=Math.sin(x)*M,p[b]=0}else{const x=1-p[b]/_[b];l[S+1]=hs+Math.sin(Math.PI*x)*g[b],l[S]=Jr(l[S]+(a+m[b])*t,s.radiusM),l[S+2]=Jr(l[S+2]+(o+f[b])*t,s.radiusM)}continue}let E=0,I=0;if(T){const x=US*u[b],M=FS*d[b];E=Math.sin(e*x*Math.PI*2+c[b])*M,I=Math.sin(e*x*Math.PI*2+c[b]+Math.PI/2)*M}if(l[S]+=(a+E)*t,l[S+1]-=s.fallSpeedMPerS*h[b]*t,l[S+2]+=(o+I)*t,l[S]=Jr(l[S],s.radiusM),l[S+2]=Jr(l[S+2],s.radiusM),l[S+1]<hs){if(A){g[b]=zd*(.2+Math.random()*1.6),_[b]=OS*(.5+Math.random()),p[b]=_[b];const C=Math.random()*Math.PI*2,R=BS*(g[b]/zd);m[b]=Math.cos(C)*R,f[b]=Math.sin(C)*R,l[S+1]=hs;continue}const x=Math.random()*Math.PI*2,M=Math.sqrt(Math.random())*s.radiusM;l[S]=Math.cos(x)*M,l[S+1]=s.topYM,l[S+2]=Math.sin(x)*M}}this.precipitationPoints.geometry.attributes.position.needsUpdate=!0}buildRain(){const e=IS(this.weather.precipitationIntensity);this.rainSystem=R2({count:Id,radiusM:Vs,heightM:wS,bottomYM:Rd,overallSpeed:TS,color:RS,opacity:.85,visibleCount:Fo(Id,this.weather.precipitationIntensity),speedStreak:PS(e),pixelSize:Fd,seed:9001,nearFocusDistanceM:Ld,hazeDistanceM:Vs*Nd,hazeColor:new Se(...this.baseFogColor)}),this.rainLastVerticalFacing=-1,this.scene.add(this.rainSystem.points),this.buildRainSplashes()}buildRainSplashes(){const e=Oo(4242),t=new Float32Array(os*3),n=new Float32Array(os),s=new Float32Array(os),r=new Float32Array(os);for(let h=0;h<os;h++){const u=e()*Math.PI*2,d=Math.sqrt(e())*Vs;t[h*3]=Math.cos(u)*d,t[h*3+1]=Rd,t[h*3+2]=Math.sin(u)*d,s[h]=Zr(Dd,Pd),n[h]=e()*s[h],r[h]=n[h]/s[h]}const a=new wt;a.setAttribute("position",new mt(t,3));const o=new mt(r,1);a.setAttribute("aLife",o),a.setDrawRange(0,Fo(os,this.weather.precipitationIntensity));const l=new Ht({uniforms:{uMaxSize:{value:LS},uScale:{value:.5*this.renderer.domElement.height},uColor:{value:NS},uTexture:{value:YS()}},vertexShader:qS,fragmentShader:$S,transparent:!0,depthWrite:!1}),c=new oa(a,l);this.scene.add(c),this.rainSplashSystem={points:c,positions:t,life:n,duration:s,lifeAttribute:o}}disposeRain(){this.rainSystem&&(this.rainSystem.points.removeFromParent(),this.rainSystem.points.geometry.dispose(),this.rainSystem.points.material.dispose()),this.rainSystem=void 0,this.disposeRainSplashes()}disposeRainSplashes(){this.rainSplashSystem&&(this.rainSplashSystem.points.removeFromParent(),this.rainSplashSystem.points.geometry.dispose(),this.rainSplashSystem.points.material.dispose()),this.rainSplashSystem=void 0}updateRain(e){const t=this.rainSystem;if(!t)return;t.uniforms.uTime.value+=e,t.uniforms.uHazeColor.value.setRGB(...this.baseFogColor);const n=this.weather.windDirectionDeg*Nn,s=Math.sin(n)*this.weather.windSpeed,r=-Math.cos(n)*this.weather.windSpeed,a=Vs,o=c=>(c%(a*2)+a*3)%(a*2)-a;t.uniforms.uWindOffset.value.set(o(t.uniforms.uWindOffset.value.x+s*e),o(t.uniforms.uWindOffset.value.y+r*e)),this.camera.getWorldDirection(this.rainCameraDirection);const l=Math.abs(this.rainCameraDirection.y);if(Math.abs(l-this.rainLastVerticalFacing)>.001){this.rainLastVerticalFacing=l;const c=Od+(1-Od)*(1-l),h=Bd+(1-Bd)*(1-l);t.uniforms.uUvSquash.value=h,t.uniforms.uPixelSize.value=Fd*c*(.5+.5*h)}this.updateRainSplashes(e)}updateRainSplashes(e){const t=this.rainSplashSystem;if(!t||e<=0)return;const{positions:n,life:s,duration:r,lifeAttribute:a}=t,o=a.array;for(let l=0,c=0;l<n.length;l+=3,c++){if(s[c]+=e,s[c]>=r[c]){const h=Math.random()*Math.PI*2,u=Math.sqrt(Math.random())*Vs;n[l]=Math.cos(h)*u,n[l+2]=Math.sin(h)*u,r[c]=Zr(Dd,Pd),s[c]=0}o[c]=s[c]/r[c]}t.points.geometry.attributes.position.needsUpdate=!0,a.needsUpdate=!0}updateLightning(e,t){if(this.lightningArmed){if(this.lightningFlashRemainingS>0){this.lightningFlashRemainingS=Math.max(0,this.lightningFlashRemainingS-t);const n=this.lightningFlashRemainingS/Hd;this.scene.fog&&this.scene.fog.color.setRGB(this.baseFogColor[0]+(1-this.baseFogColor[0])*n,this.baseFogColor[1]+(1-this.baseFogColor[1])*n,this.baseFogColor[2]+(1-this.baseFogColor[2])*n);return}if(this.nextLightningAtS===null){this.nextLightningAtS=e+Zr(kd,Gd);return}e>=this.nextLightningAtS&&(this.lightningFlashRemainingS=Hd,this.nextLightningAtS=e+Zr(kd,Gd),this.onLightningFlash?.())}}buildCompassLabels(){const e=Ia(navigator.languages,pS),t=fS[e];this.compassSprites=uS.map((n,s)=>{const r=t[s],a=new Ys({map:jS(r),depthTest:!1,fog:!1}),o=new Er(a),{x:l,y:c,z:h}=ns(0,n,mS);return o.position.set(l,c,h),o.scale.set(wd,wd,1),o.renderOrder=gS,o.visible=this.compassHovered||this.compassForced,this.celestialGroup.add(o),o})}disposeCompassLabels(){for(const e of this.compassSprites)e.removeFromParent(),e.material.map?.dispose(),e.material.dispose();this.compassSprites=[]}disposeStarTiers(){for(const e of this.starTiers)this.disposeMesh(e.points);this.starTiers=[]}disposeMesh(e){if(!e)return;e.removeFromParent(),"geometry"in e&&e.geometry.dispose();const t=e.material;for(const n of Array.isArray(t)?t:[t]){const s=n.map;s&&s.dispose(),n.dispose()}}}const Nn=Math.PI/180;function _0(i,e,t){return Math.max(e,Math.min(t,i))}function Zr(i,e){return i+Math.random()*(e-i)}function Jr(i,e){return(i%(e*2)+e*3)%(e*2)-e}let Qr,jr;function VS(i){switch(i){case"snow":return WS();case"hail":return XS()}}function WS(){if(Qr)return Qr;const i=64,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,0.95)"),n.addColorStop(.5,"rgba(255,255,255,0.55)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),Qr=new Wn(e),Qr}function XS(){if(jr)return jr;const i=64,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,1)"),n.addColorStop(.55,"rgba(255,255,255,0.95)"),n.addColorStop(.8,"rgba(255,255,255,0.4)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),jr=new Wn(e),jr}let ea;function YS(){if(ea)return ea;const i=64,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,0)"),n.addColorStop(.4,"rgba(255,255,255,0)"),n.addColorStop(.6,"rgba(255,255,255,0.9)"),n.addColorStop(.8,"rgba(255,255,255,0.25)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),ea=new Wn(e),ea}const qS=`
5528
5528
  uniform float uMaxSize;
5529
5529
  uniform float uScale;
5530
5530
  attribute float aLife;
@@ -5589,4 +5589,4 @@ void main() {
5589
5589
  float blur = max(vNearBlur, vHaze);
5590
5590
  gl_FragColor = vec4(color, tex.a * uOpacity * (1.0 - blur * 0.5));
5591
5591
  }
5592
- `;let ta;function JS(){if(ta)return ta;const i=128,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,1)"),n.addColorStop(.4,"rgba(255,255,255,0.35)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),ta=new Wn(e),ta}function QS(i){const t=document.createElement("canvas");t.width=128,t.height=128;const n=t.getContext("2d"),s=128/2-2,r=128/2,a=128/2,o="#2e2b26",l="#f4f1e2";n.fillStyle=o,n.beginPath(),n.arc(r,a,s,0,Math.PI*2),n.fill();const c=_0(i.illuminatedFraction,0,1),h=i.phaseFraction<.5,u=s*Math.abs(1-2*c),d=c<.5;return n.save(),n.beginPath(),n.arc(r,a,s,0,Math.PI*2),n.clip(),n.fillStyle=l,n.beginPath(),h?(n.arc(r,a,s,-Math.PI/2,Math.PI/2,!1),n.ellipse(r,a,u,s,0,Math.PI/2,-Math.PI/2,d)):(n.arc(r,a,s,Math.PI/2,-Math.PI/2,!1),n.ellipse(r,a,u,s,0,-Math.PI/2,Math.PI/2,d)),n.fill(),n.restore(),new Wn(t)}function jS(i){const e=document.createElement("canvas");e.width=128,e.height=128;const t=e.getContext("2d");return t.fillStyle="rgba(0, 0, 0, 0.55)",t.beginPath(),t.arc(64,64,60,0,Math.PI*2),t.fill(),t.fillStyle="#ffffff",t.font="bold 52px sans-serif",t.textAlign="center",t.textBaseline="middle",t.fillText(i,64,68),new Wn(e)}function Oo(i){let e=i;return function(){e|=0,e=e+1831565813|0;let n=Math.imul(e^e>>>15,1|e);return n=n+Math.imul(n^n>>>7,61|n)^n,((n^n>>>14)>>>0)/4294967296}}const Vd=new Map;function ey(i){const e=i.byteLength/(4*Float32Array.BYTES_PER_ELEMENT),t=e,n=t*Float32Array.BYTES_PER_ELEMENT;return{count:e,ra:new Float32Array(i,0*n,t),dec:new Float32Array(i,1*n,t),mag:new Float32Array(i,2*n,t),ci:new Float32Array(i,3*n,t)}}function ty(i){let e=Vd.get(i);return e||(e=fetch(i).then(t=>t.arrayBuffer()).then(ey),Vd.set(i,e)),e}const ny=new URL(""+new URL("rain-BvWZrB9h.ogg",import.meta.url).href,import.meta.url).href,iy=new URL(""+new URL("wind-BjklexY7.ogg",import.meta.url).href,import.meta.url).href,sy=new URL(""+new URL("thunder-DA48qs_v.wav",import.meta.url).href,import.meta.url).href,ry=20,ay=1;class oy{context;buffers=new Map;ambientSource;ambientGain;ambientKey="none";ambientToken=0;ambientVolume=0;windSource;windGain;windActive=!1;windToken=0;windVolume=0;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("WeatherAudio: Web Audio unavailable, weather sounds disabled:",e);return}}this.context.state==="suspended"&&this.context.resume()}setAmbient(e,t,n){if(!this.context)return;const s=e==="rain"||e==="hail"?e:"none";if(this.ambientVolume=s==="hail"?Math.min(1,t*1.3+.2):s==="rain"?t:0,s!==this.ambientKey){this.ambientKey=s;const a=++this.ambientToken;this.stopSource(this.ambientSource,this.ambientGain),this.ambientSource=void 0,this.ambientGain=void 0,s!=="none"&&this.startLoop(ny,this.ambientVolume).then(o=>this.applyIfCurrent(o,a,()=>this.ambientToken,l=>{this.ambientSource=l?.source,this.ambientGain=l?.gain,l&&(l.gain.gain.value=this.ambientVolume)}))}else this.ambientGain&&(this.ambientGain.gain.value=this.ambientVolume);this.windVolume=Math.min(n/ry,1);const r=n>ay;if(r!==this.windActive){this.windActive=r;const a=++this.windToken;this.stopSource(this.windSource,this.windGain),this.windSource=void 0,this.windGain=void 0,r&&this.startLoop(iy,this.windVolume).then(o=>this.applyIfCurrent(o,a,()=>this.windToken,l=>{this.windSource=l?.source,this.windGain=l?.gain,l&&(l.gain.gain.value=this.windVolume)}))}else this.windGain&&(this.windGain.gain.value=this.windVolume)}playThunder(){this.context&&this.playOneShot(sy,.9)}dispose(){this.ambientToken++,this.windToken++,this.stopSource(this.ambientSource,this.ambientGain),this.stopSource(this.windSource,this.windGain),this.ambientSource=void 0,this.ambientGain=void 0,this.windSource=void 0,this.windGain=void 0,this.context?.close(),this.context=void 0,this.buffers.clear()}applyIfCurrent(e,t,n,s){if(t!==n()){e?.source.stop();return}s(e)}async startLoop(e,t){const n=this.context;if(n)try{const s=await this.getBuffer(e),r=n.createBufferSource();r.buffer=s,r.loop=!0;const a=n.createGain();return a.gain.value=t,r.connect(a).connect(n.destination),r.start(),{source:r,gain:a}}catch(s){console.warn("Weather ambient sound failed to load, staying silent:",s);return}}async playOneShot(e,t){const n=this.context;if(n)try{const s=await this.getBuffer(e),r=n.createBufferSource();r.buffer=s;const a=n.createGain();a.gain.value=t,r.connect(a).connect(n.destination),r.start()}catch(s){console.warn("Thunder sound failed to load:",s)}}async getBuffer(e){const t=this.context;if(!t)throw new Error("WeatherAudio: AudioContext not ready — resume() must be called first");let n=this.buffers.get(e);return n||(n=fetch(e).then(s=>{if(!s.ok)throw new Error(`Audio fetch failed (${s.status}): ${e}`);return s.arrayBuffer()}).then(s=>t.decodeAudioData(s)),this.buffers.set(e,n)),n}stopSource(e,t){try{e?.stop()}catch{}e?.disconnect(),t?.disconnect()}}Ra();const ly={sun:{en:"Sun",fr:"Soleil"},moon:{en:"Moon",fr:"Lune"},Venus:{en:"Venus",fr:"Vénus"},Mars:{en:"Mars",fr:"Mars"},Jupiter:{en:"Jupiter",fr:"Jupiter"},Saturn:{en:"Saturn",fr:"Saturne"}},cy=["en","fr"],hy={building:{en:"Building",fr:"Bâtiment"},tree:{en:"Tree",fr:"Arbre"},streetlight:{en:"Streetlight",fr:"Lampadaire"},vehicle:{en:"Vehicle",fr:"Véhicule"},witness:{en:"Witness",fr:"Témoin"}},dy=new URL(""+new URL("stars-mag7.5-DDr9QasD.bin",import.meta.url).href,import.meta.url).href,uy={sun:{altitudeDeg:-3,azimuthDeg:180,magnitude:-26.7},moon:{altitudeDeg:-90,azimuthDeg:0,phase:{phaseFraction:0,illuminatedFraction:0},magnitude:-12.7},planets:[]},Bo={lat:0,lng:0,elevationM:0,headingDeg:void 0,pitchDeg:0,fovDeg:60};class fy extends HTMLElement{static get observedAttributes(){return["src","star-catalog-src","show-compass","lens-flare-intensity","lens-flare-brightness"]}shadow;stageElement;frameElement;sceneCanvas;ufoElement;sceneRenderer;hoverTooltip;resizeObserver;lastTimeMs=0;starCatalog;weatherAudio=new oy;thunderTimeoutId;handleFullscreenChange=()=>this.resizeToStage();handlePointerMove=e=>{this.sceneRenderer.setCompassHovered(!0);const t=this.ufoElement.canvasElement,n=t.getBoundingClientRect();if(n.width===0||n.height===0)return;const s=(e.clientX-n.left)/n.width*t.width,r=(e.clientY-n.top)/n.height*t.height;if(this.ufoElement.hasVisibleShapeAt(s,r)){this.hoverTooltip.hidden=!0;return}const a=(e.clientX-n.left)/n.width*2-1,o=-((e.clientY-n.top)/n.height*2-1),l=Ia(navigator.languages,cy),c=this.sceneRenderer.pickBodyAt(a,o);if(c){this.showHoverTooltip(e,ly[c]?.[l]??c);return}const h=this.sceneRenderer.pickDecorAt(a,o),u=h?this.ufoElement.sighting.decor.find(d=>d.id===h):void 0;if(u){this.showHoverTooltip(e,u.title||hy[u.kind][l]);return}this.hoverTooltip.hidden=!0};showHoverTooltip(e,t){this.hoverTooltip.textContent=t,this.hoverTooltip.hidden=!1;const n=this.stageElement.getBoundingClientRect();this.hoverTooltip.style.left=`${e.clientX-n.left+12}px`,this.hoverTooltip.style.top=`${e.clientY-n.top+12}px`}handlePointerLeave=()=>{this.hoverTooltip.hidden=!0,this.sceneRenderer.setCompassHovered(!1)};handleLightningFlash=()=>{clearTimeout(this.thunderTimeoutId);const e=(.5+Math.random()*3.5)*1e3;this.thunderTimeoutId=window.setTimeout(()=>this.weatherAudio.playThunder(),e)};handleFirstInteraction=()=>{this.weatherAudio.resume(),this.setWeather(Ho(this.ufoElement.sighting,this.lastTimeMs))};handleTimeUpdate=e=>{this.lastTimeMs=e.detail.time,this.updateAstronomy(this.lastTimeMs),this.updateUfoOcclusion(this.lastTimeMs)};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${vf}</style>${gf}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.frameElement=this.shadow.getElementById("frame"),this.sceneCanvas=this.shadow.getElementById("scene-canvas"),this.sceneRenderer=new HS(this.sceneCanvas,void 0,this.handleLightningFlash),this.hoverTooltip=this.shadow.getElementById("hover-tooltip"),this.ufoElement=document.createElement(Wo),this.ufoElement.classList.add("ufo-overlay"),this.ufoElement.style.setProperty("--ufo-canvas-background","transparent"),this.ufoElement.style.setProperty("--ufo-canvas-border","none"),this.ufoElement.fullscreenTarget=this.stageElement,this.shadow.getElementById("ufo-slot").replaceWith(this.ufoElement),this.ufoElement.addEventListener("timeupdate",this.handleTimeUpdate),this.ufoElement.canvasElement.addEventListener("pointermove",this.handlePointerMove),this.ufoElement.canvasElement.addEventListener("pointerleave",this.handlePointerLeave),this.ufoElement.canvasElement.addEventListener("pointerdown",this.handleFirstInteraction,{once:!0})}connectedCallback(){this.resizeToStage(),this.updateAstronomy(this.lastTimeMs),this.loadStars(),this.resizeObserver=new ResizeObserver(()=>this.resizeToStage()),this.resizeObserver.observe(this.frameElement),document.addEventListener("fullscreenchange",this.handleFullscreenChange);const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){this.resizeObserver?.disconnect(),document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.sceneRenderer.stopTwinkle(),clearTimeout(this.thunderTimeoutId),this.weatherAudio.dispose()}attributeChangedCallback(e,t,n){if(e==="src"&&n&&n!==t&&this.isConnected&&this.loadFromSrc(n),e==="star-catalog-src"&&n!==t&&this.isConnected&&this.loadStars(),e==="show-compass"&&n!==t&&this.sceneRenderer.setShowCompass(this.hasAttribute("show-compass")),e==="lens-flare-intensity"&&n!==t){const s=n===null?NaN:parseFloat(n);this.sceneRenderer.setLensFlareArtifactIntensity(Number.isFinite(s)?s:0)}if(e==="lens-flare-brightness"&&n!==t){const s=n===null?NaN:parseFloat(n);this.sceneRenderer.setDazzleIntensity(Number.isFinite(s)?s:1)}}setCompassForced(e){this.sceneRenderer.setCompassForced(e)}setIndoorLook(e,t){this.sceneRenderer.setIndoorLook(e,t)}setWeather(e){this.sceneRenderer.setWeather(e),this.weatherAudio.setAmbient(e.precipitationType,e.precipitationIntensity,e.windSpeed)}pickDecorAt(e,t){return this.sceneRenderer.pickDecorAt(e,t)}resumeWeatherAudio(){this.weatherAudio.resume()}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return this.ufoElement.sightingData}set sightingData(e){this.ufoElement.sightingData=e,this.lastTimeMs=0,this.updateAstronomy(0)}get currentTerrainAttribution(){return this.sceneRenderer.currentTerrainAttribution}resizeToStage(){const e=this.frameElement.getBoundingClientRect(),t=Math.max(1,Math.round(e.width)),n=Math.max(1,Math.round(e.height));this.sceneCanvas.width=t,this.sceneCanvas.height=n,this.sceneRenderer.resize(t,n)}async loadStars(){const e=this.getAttribute("star-catalog-src")??dy;this.starCatalog=await ty(e),this.updateAstronomy(this.lastTimeMs)}updateAstronomy(e){const t=this.ufoElement.sighting;this.setWeather(Ho(t,e)),this.sceneRenderer.setDecor(t.decor);const n=Go(t,e);this.sceneRenderer.setObserverPose(n??Bo),this.sceneRenderer.updateDecorAnchoring(Go(t,0),n),this.sceneRenderer.updateDecorLitState(e),this.sceneRenderer.setTerrainOrigin(n?.lat,n?.lng);const s=n?.lat??Bo.lat,r=n?.lng??Bo.lng,a=n2(t.event.time??{},r,t.event.utcOffsetHours);if(!a){this.sceneRenderer.setAstronomy(uy);return}const o=new Date(a.getTime()+e),l={lat:s,lng:r,elevationM:n?.elevationM??0},c={...wo("Sun",o,l),magnitude:To("Sun",o)},h={...wo("Moon",o,l),phase:Q_(o),magnitude:To("Moon",o)},u=Z_.map(d=>({body:d,position:wo(d,o,l),magnitude:To(d,o)}));this.sceneRenderer.setAstronomy({sun:c,moon:h,planets:u,stars:this.starCatalog?{catalog:this.starCatalog,date:o,observer:l}:void 0})}updateUfoOcclusion(e){const t=this.ufoElement.sighting.timeline,n=this.ufoElement.canvasElement,s=new Set;for(const r of t.sourceIds){const a=t.getInterpolatedShapeAt(e,r);if(!a)continue;const o=(a.bounds.x+a.bounds.width/2)/n.width*2-1,l=-((a.bounds.y+a.bounds.height/2)/n.height*2-1);this.sceneRenderer.isScreenPointOccluded(o,l,r,a.physical?.distanceM)&&s.add(r)}this.ufoElement.setOccludedSourceIds(s)}}const hc="rr0-scene";function py(){Ra(),customElements.get(hc)||customElements.define(hc,fy)}class my{constructor(e,t){this.timeline=e,this.clock=t}currentPointer=null;shapePrototype=null;sourceId="";start(e,t){this.sourceId=e,this.shapePrototype=t,this.currentPointer=null,this.clock.start(n=>this.recordSample(n))}onPointerMove(e,t){this.currentPointer={x:e,y:t}}stop(){this.clock.stop()}recordSample(e){if(!this.currentPointer||!this.shapePrototype)return;const t=Kd(this.shapePrototype,this.currentPointer.x,this.currentPointer.y);this.timeline.addKeyframe(e,[{sourceId:this.sourceId,shape:t}])}}class gy{constructor(e){this.intervalMs=e}rafId=null;startTime=0;lastTick=0;start(e){this.startTime=performance.now(),this.lastTick=0;const t=()=>{const n=performance.now()-this.startTime;n-this.lastTick>=this.intervalMs&&(this.lastTick=n,e(n)),this.rafId=requestAnimationFrame(t)};this.rafId=requestAnimationFrame(t)}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}}const S0={oval:"Oval",polygon:"Polygon",addVertex:"Add vertex",deleteVertex:"Delete vertex",notAPolygon:"Select a single polygon shape",tooFewVertices:"A shape needs at least 3 points",color:"Color",transparency:"Transparency",halo:"Halo",shape:"Shape",shapeTitle:"Name",objectSize:"Real size",objectDistance:"Distance",apparentSize:"i.e. {deg}° — {moons}× the Moon",utcOffset:"Time zone",utcOffsetPlaceholder:"from longitude",objectSizePlaceholder:"reported width",objectDistancePlaceholder:"reported distance",addShape:"Add shape",deleteShape:"Delete shape",play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",group:"Group",ungroup:"Ungroup",bringToFront:"Bring to front",sendToBack:"Send to back",contextMenuDelete:"Delete",confirmDeleteShape:"Delete {name}? This can't be undone.",confirmDeleteShapes:"Delete {count} shapes? This can't be undone.",onlyOneShape:"There is only one shape",alreadyAtFront:"This shape is already at the front",alreadyAtBack:"This shape is already at the back",needTwoShapesToGroup:"Select at least two shapes to group them",notGrouped:"This shape isn't part of a group",multipleShapesSelected:"Multiple shapes selected — select just one to edit this",cloudBase:"Cloud base",elevation:"Altitude",samplingRate:"Sampling rate",duration:"Duration",durationPlaceholder:"observation length",durationImprecise:"These dates don't precisely enough determine a duration — enter it manually.",export:"Export JSON",importFile:"Load JSON file",importUrl:"Or load from URL",importUrlPlaceholder:"https://…/sighting.json",importButton:"Load",importError:"Couldn't load that recording — check the file or URL and try again.",record:"Record",stop:"Stop",latitude:"Latitude",longitude:"Longitude",heading:"Heading",headingPlaceholder:"unknown",pitch:"Tilt",observationTime:"Observation start",observationEndTime:"Observation end",edtfInvalid:"Not a valid EDTF date/time (e.g. 1965-07-01T05:00, 2025-06?, 2025~).",edtfPlaceholder:"YYYY-MM-DDThh:mm[?~%] or hh:mm",observationTimeHint:"EDTF — e.g. 1965-07-01T05:00, 2025-06? (uncertain), 2025~ (approximate), or just 05:00 if the date isn't known",observationEndTimeHint:"EDTF — e.g. 1965-07-01T05:10, 2025-06? (uncertain), 2025~ (approximate), or just 05:10 if the date isn't known",presetsGroupLabel:"UFO shape",witnessId:"Witness ID",witnessDirName:"Witness dir name",witnessTitle:"Witness title",witnessLastName:"Witness last name",witnessFirstNames:"Witness first names",caseId:"Case ID",description:"Description",tags:"Tags",tagsPlaceholder:"comma-separated",weather:"Weather",shapeGroup:"Shape",temporalGroup:"Temporal",locationGroup:"Location",observationGroup:"Observation",witnessGroup:"Witness",circumstancesGroup:"Circumstances",cloudCover:"Cloud cover",cloudDarkness:"Cloud darkness",precipitationType:"Precipitation",precipitationNone:"None",precipitationRain:"Rain",precipitationSnow:"Snow",precipitationHail:"Hail",precipitationIntensity:"Intensity",windDirection:"Wind direction",windSpeed:"Wind speed",storm:"Storm",lensFlareBrightness:"Light intensity",cameraDevice:"Camera/video device",decor:"Decor",decorBuilding:"Building",decorTree:"Tree",decorStreetlight:"Streetlight",decorVehicle:"Vehicle",decorWitness:"Other witness",addDecor:"Add",deleteDecor:"Delete decor",decorEast:"Distance east",decorNorth:"Distance north",decorHeading:"Heading",decorLit:"Lit",decorTitle:"Name",decorSightingUrl:"Witness's own recording URL",viewTestimony:"View testimony",noWitnessRecording:"No recording URL set for this witness",masks:"Masks",addWitness:"Add witness",decorFloors:"Floors",decorOccupiedFloor:"Occupied floor",decorWitnessSide:"Witness location",decorWitnessSideNone:"Not present",decorWindows:"Windows",decorSideFront:"Front",decorSideBehind:"Behind",decorSideLeft:"Left",decorSideRight:"Right",decorSideFrontLeft:"Front-left",decorSideFrontRight:"Front-right",decorSideBehindLeft:"Behind-left",decorSideBehindRight:"Behind-right"},vy=Object.freeze(Object.defineProperty({__proto__:null,ufoRecorderMessages_en:S0},Symbol.toStringTag,{value:"Module"}));Ra();py();const ls={width:48,height:28},Wd=.2,Xd=60,Yd=new Set(["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"]),ni=4,na="ufo-1",qd=["oval","polygon"],xy={presetId:"oval",color:"#39ff14",transparency:0,haloScale:1.5};function _y(i){return i.kind==="oval"?"oval":"polygon"}class Sy extends HTMLElement{shadow;sceneElement;ufoElement;recordButton;samplingRateInput;presetButtons;presetsGroup;colorInput;transparencyInput;haloScaleInput;sourceSelect;shapeTitleInput;utcOffsetInput;objectSizeInput;objectDistanceInput;apparentSizeOutput;addShapeButton;deleteShapeButton;contextMenu;contextGroupButton;contextUngroupButton;contextBringToFrontButton;contextSendToBackButton;contextDeleteButton;contextAddVertexButton;contextDeleteVertexButton;decorContextMenu;contextViewTestimonyButton;contextMasksSubmenu;labelContextMasks;playPauseButton;seekInput;timeStartLabel;timeEndLabel;loopButton;durationInput;exportButton;importFileInput;importUrlInput;importUrlButton;latInput;lngInput;headingInput;pitchInput;elevationInput;obsTimeInput;obsEndTimeInput;witnessIdInput;witnessDirNameInput;witnessTitleInput;witnessLastNameInput;witnessFirstNamesInput;caseIdInput;descriptionInput;tagsInput;cloudCoverInput;cloudDarknessInput;cloudBaseInput;precipitationTypeSelect;precipitationIntensityInput;windDirectionInput;windSpeedInput;stormInput;lensFlareBrightnessInput;cameraDeviceInput;labelColor;labelTransparency;labelHalo;labelShape;labelShapeTitle;labelUtcOffset;labelObjectSize;labelObjectDistance;labelSamplingRate;labelDuration;labelLatitude;labelLongitude;labelHeading;labelPitch;labelElevation;labelObservationTime;labelObservationEndTime;labelWitnessId;labelWitnessDirName;labelWitnessTitle;labelWitnessLastName;labelWitnessFirstNames;labelCaseId;labelDescription;labelTags;labelWeather;labelImportFile;labelImportUrl;labelShapeGroup;labelTemporalGroup;labelLocationGroup;labelObservationGroup;labelWitnessGroup;labelCircumstancesGroup;labelCloudCover;labelCloudDarkness;labelCloudBase;labelPrecipitationType;labelPrecipitationIntensity;labelWindDirection;labelWindSpeed;labelStorm;labelLensFlareBrightness;labelCameraDevice;optionPrecipitationNone;optionPrecipitationRain;optionPrecipitationSnow;optionPrecipitationHail;decorKindSelect;addDecorWitnessButton;addDecorBuildingButton;deleteDecorButton;decorSelect;decorTitleInput;decorEastInput;decorNorthInput;decorHeadingInput;decorLitInput;decorSightingUrlInput;decorFloorsInput;decorOccupiedFloorInput;decorWitnessSideSelect;decorWindowInputs;labelDecorSide;optionWitnessSide;labelDecor;labelDecorFieldset;labelDecorTitle;labelDecorEast;labelDecorNorth;labelDecorHeading;labelDecorLit;labelDecorSightingUrl;labelDecorFloors;labelDecorOccupiedFloor;labelDecorWitnessSide;labelDecorWindows;optionWitnessSideNone;optionDecorBuilding;optionDecorTree;optionDecorStreetlight;optionDecorVehicle;optionDecorWitness;recorder;isRecording=!1;messages=S0;currentAppearance={...xy};currentSourceId=na;selectedSourceIds=new Set([na]);currentDecorId;contextMenuDecorId;contextMenuPoint;dragState;cameraDragState;hoverCursor;indoorLookYawDeg=0;indoorLookPitchDeg=0;lastInhabitedKey;handleKeyDown=e=>{if(e.key==="Escape"&&(this.isRecording&&this.toggleRecording(),this.contextMenu.hidden||this.hideContextMenu(),this.decorContextMenu.hidden||this.hideDecorContextMenu()),Yd.has(e.key)||e.key==="Delete"||e.key==="Backspace"){const t=e.composedPath()[0];if(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement)return;Yd.has(e.key)?this.moveOrResizeSelectedShapes(e):this.deleteShape()}};handleDragPointerMove=e=>this.onDragPointerMove(e);handleDragPointerUp=()=>this.endDrag();constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${R0}</style>${I0}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.sceneElement=document.createElement(hc),this.sceneElement.setAttribute("show-compass",""),this.ufoElement=this.sceneElement.ufoElement,this.ufoElement.enableClickToPlay=!1,this.ufoElement.showToolbar=!1,this.shadow.getElementById("ufo-slot").replaceWith(this.sceneElement),this.recordButton=this.shadow.getElementById("record"),this.samplingRateInput=this.shadow.getElementById("samplingRate"),this.presetsGroup=this.shadow.getElementById("presets-group"),this.presetButtons={oval:this.shadow.getElementById("preset-oval"),polygon:this.shadow.getElementById("preset-polygon")},this.colorInput=this.shadow.getElementById("color"),this.transparencyInput=this.shadow.getElementById("transparency"),this.haloScaleInput=this.shadow.getElementById("haloScale"),this.sourceSelect=this.shadow.getElementById("source"),this.shapeTitleInput=this.shadow.getElementById("shapeTitle"),this.utcOffsetInput=this.shadow.getElementById("utcOffsetHours"),this.objectSizeInput=this.shadow.getElementById("objectSize"),this.objectDistanceInput=this.shadow.getElementById("objectDistance"),this.apparentSizeOutput=this.shadow.getElementById("apparent-size"),this.addShapeButton=this.shadow.getElementById("add-shape"),this.deleteShapeButton=this.shadow.getElementById("delete-shape"),this.contextMenu=this.shadow.getElementById("context-menu"),this.contextGroupButton=this.shadow.getElementById("context-group"),this.contextUngroupButton=this.shadow.getElementById("context-ungroup"),this.contextBringToFrontButton=this.shadow.getElementById("context-bring-to-front"),this.contextSendToBackButton=this.shadow.getElementById("context-send-to-back"),this.contextDeleteButton=this.shadow.getElementById("context-delete"),this.contextAddVertexButton=this.shadow.getElementById("context-add-vertex"),this.contextDeleteVertexButton=this.shadow.getElementById("context-delete-vertex"),this.decorContextMenu=this.shadow.getElementById("decor-context-menu"),this.contextViewTestimonyButton=this.shadow.getElementById("context-view-testimony"),this.contextMasksSubmenu=this.shadow.getElementById("context-masks-submenu"),this.labelContextMasks=this.shadow.getElementById("label-context-masks"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.loopButton=this.shadow.getElementById("loop"),this.durationInput=this.shadow.getElementById("durationSeconds"),this.exportButton=this.shadow.getElementById("export"),this.importFileInput=this.shadow.getElementById("import-file"),this.importUrlInput=this.shadow.getElementById("import-url"),this.importUrlButton=this.shadow.getElementById("import-url-button"),this.latInput=this.shadow.getElementById("lat"),this.lngInput=this.shadow.getElementById("lng"),this.headingInput=this.shadow.getElementById("heading"),this.pitchInput=this.shadow.getElementById("pitch"),this.elevationInput=this.shadow.getElementById("elevation"),this.obsTimeInput=this.shadow.getElementById("obs-time"),this.obsEndTimeInput=this.shadow.getElementById("obs-end-time"),this.witnessIdInput=this.shadow.getElementById("witnessId"),this.witnessDirNameInput=this.shadow.getElementById("witnessDirName"),this.witnessTitleInput=this.shadow.getElementById("witnessTitle"),this.witnessLastNameInput=this.shadow.getElementById("witnessLastName"),this.witnessFirstNamesInput=this.shadow.getElementById("witnessFirstNames"),this.caseIdInput=this.shadow.getElementById("caseId"),this.descriptionInput=this.shadow.getElementById("description"),this.tagsInput=this.shadow.getElementById("tags"),this.cloudCoverInput=this.shadow.getElementById("cloudCover"),this.cloudDarknessInput=this.shadow.getElementById("cloudDarkness"),this.cloudBaseInput=this.shadow.getElementById("cloudBase"),this.precipitationTypeSelect=this.shadow.getElementById("precipitationType"),this.precipitationIntensityInput=this.shadow.getElementById("precipitationIntensity"),this.windDirectionInput=this.shadow.getElementById("windDirection"),this.windSpeedInput=this.shadow.getElementById("windSpeed"),this.stormInput=this.shadow.getElementById("storm"),this.lensFlareBrightnessInput=this.shadow.getElementById("lensFlareBrightness"),this.cameraDeviceInput=this.shadow.getElementById("cameraDevice"),this.labelColor=this.shadow.getElementById("label-color"),this.labelTransparency=this.shadow.getElementById("label-transparency"),this.labelHalo=this.shadow.getElementById("label-halo"),this.labelShape=this.shadow.getElementById("label-shape"),this.labelShapeTitle=this.shadow.getElementById("label-shape-title"),this.labelUtcOffset=this.shadow.getElementById("label-utc-offset"),this.labelObjectSize=this.shadow.getElementById("label-object-size"),this.labelObjectDistance=this.shadow.getElementById("label-object-distance"),this.labelSamplingRate=this.shadow.getElementById("label-sampling-rate"),this.labelDuration=this.shadow.getElementById("label-duration"),this.labelLatitude=this.shadow.getElementById("label-lat"),this.labelLongitude=this.shadow.getElementById("label-lng"),this.labelHeading=this.shadow.getElementById("label-heading"),this.labelPitch=this.shadow.getElementById("label-pitch"),this.labelElevation=this.shadow.getElementById("label-elevation"),this.labelObservationTime=this.shadow.getElementById("label-observation-time"),this.labelObservationEndTime=this.shadow.getElementById("label-observation-end-time"),this.labelWitnessId=this.shadow.getElementById("label-witness-id"),this.labelWitnessDirName=this.shadow.getElementById("label-witness-dir-name"),this.labelWitnessTitle=this.shadow.getElementById("label-witness-title"),this.labelWitnessLastName=this.shadow.getElementById("label-witness-last-name"),this.labelWitnessFirstNames=this.shadow.getElementById("label-witness-first-names"),this.labelCaseId=this.shadow.getElementById("label-case-id"),this.labelDescription=this.shadow.getElementById("label-description"),this.labelTags=this.shadow.getElementById("label-tags"),this.labelWeather=this.shadow.getElementById("label-weather"),this.labelImportFile=this.shadow.getElementById("label-import-file"),this.labelImportUrl=this.shadow.getElementById("label-import-url"),this.labelShapeGroup=this.shadow.getElementById("label-shape-group"),this.labelTemporalGroup=this.shadow.getElementById("label-temporal-group"),this.labelLocationGroup=this.shadow.getElementById("label-location-group"),this.labelObservationGroup=this.shadow.getElementById("label-observation-group"),this.labelWitnessGroup=this.shadow.getElementById("label-witness-group"),this.labelCircumstancesGroup=this.shadow.getElementById("label-circumstances-group"),this.labelCloudCover=this.shadow.getElementById("label-cloud-cover"),this.labelCloudDarkness=this.shadow.getElementById("label-cloud-darkness"),this.labelCloudBase=this.shadow.getElementById("label-cloud-base"),this.labelPrecipitationType=this.shadow.getElementById("label-precipitation-type"),this.labelPrecipitationIntensity=this.shadow.getElementById("label-precipitation-intensity"),this.labelWindDirection=this.shadow.getElementById("label-wind-direction"),this.labelWindSpeed=this.shadow.getElementById("label-wind-speed"),this.labelStorm=this.shadow.getElementById("label-storm"),this.labelLensFlareBrightness=this.shadow.getElementById("label-lens-flare-brightness"),this.labelCameraDevice=this.shadow.getElementById("label-camera-device"),this.optionPrecipitationNone=this.shadow.getElementById("option-precipitation-none"),this.optionPrecipitationRain=this.shadow.getElementById("option-precipitation-rain"),this.optionPrecipitationSnow=this.shadow.getElementById("option-precipitation-snow"),this.optionPrecipitationHail=this.shadow.getElementById("option-precipitation-hail"),this.decorKindSelect=this.shadow.getElementById("decorKind"),this.addDecorWitnessButton=this.shadow.getElementById("add-decor-witness"),this.addDecorBuildingButton=this.shadow.getElementById("add-decor-building"),this.deleteDecorButton=this.shadow.getElementById("delete-decor"),this.decorSelect=this.shadow.getElementById("decor"),this.decorTitleInput=this.shadow.getElementById("decorTitle"),this.decorEastInput=this.shadow.getElementById("decorEast"),this.decorNorthInput=this.shadow.getElementById("decorNorth"),this.decorHeadingInput=this.shadow.getElementById("decorHeading"),this.decorLitInput=this.shadow.getElementById("decorLit"),this.decorSightingUrlInput=this.shadow.getElementById("decorSightingUrl"),this.decorFloorsInput=this.shadow.getElementById("decorFloors"),this.decorOccupiedFloorInput=this.shadow.getElementById("decorOccupiedFloor"),this.decorWitnessSideSelect=this.shadow.getElementById("decorWitnessSide"),this.decorWindowInputs={front:this.shadow.getElementById("decorWindowFront"),behind:this.shadow.getElementById("decorWindowBehind"),left:this.shadow.getElementById("decorWindowLeft"),right:this.shadow.getElementById("decorWindowRight"),"front-left":this.shadow.getElementById("decorWindowFrontLeft"),"front-right":this.shadow.getElementById("decorWindowFrontRight"),"behind-left":this.shadow.getElementById("decorWindowBehindLeft"),"behind-right":this.shadow.getElementById("decorWindowBehindRight")},this.labelDecorSide={front:this.shadow.getElementById("label-decor-window-front"),behind:this.shadow.getElementById("label-decor-window-behind"),left:this.shadow.getElementById("label-decor-window-left"),right:this.shadow.getElementById("label-decor-window-right"),"front-left":this.shadow.getElementById("label-decor-window-front-left"),"front-right":this.shadow.getElementById("label-decor-window-front-right"),"behind-left":this.shadow.getElementById("label-decor-window-behind-left"),"behind-right":this.shadow.getElementById("label-decor-window-behind-right")},this.optionWitnessSide={front:this.shadow.getElementById("option-witness-side-front"),behind:this.shadow.getElementById("option-witness-side-behind"),left:this.shadow.getElementById("option-witness-side-left"),right:this.shadow.getElementById("option-witness-side-right"),"front-left":this.shadow.getElementById("option-witness-side-front-left"),"front-right":this.shadow.getElementById("option-witness-side-front-right"),"behind-left":this.shadow.getElementById("option-witness-side-behind-left"),"behind-right":this.shadow.getElementById("option-witness-side-behind-right")},this.optionWitnessSideNone=this.shadow.getElementById("option-witness-side-none"),this.labelDecor=this.shadow.getElementById("label-decor"),this.labelDecorFieldset=this.shadow.getElementById("label-decor-fieldset"),this.labelDecorTitle=this.shadow.getElementById("label-decor-title"),this.labelDecorEast=this.shadow.getElementById("label-decor-east"),this.labelDecorNorth=this.shadow.getElementById("label-decor-north"),this.labelDecorHeading=this.shadow.getElementById("label-decor-heading"),this.labelDecorLit=this.shadow.getElementById("label-decor-lit"),this.labelDecorSightingUrl=this.shadow.getElementById("label-decor-sighting-url"),this.labelDecorFloors=this.shadow.getElementById("label-decor-floors"),this.labelDecorOccupiedFloor=this.shadow.getElementById("label-decor-occupied-floor"),this.labelDecorWitnessSide=this.shadow.getElementById("label-decor-witness-side"),this.labelDecorWindows=this.shadow.getElementById("label-decor-windows"),this.optionDecorBuilding=this.shadow.getElementById("option-decor-building"),this.optionDecorTree=this.shadow.getElementById("option-decor-tree"),this.optionDecorStreetlight=this.shadow.getElementById("option-decor-streetlight"),this.optionDecorVehicle=this.shadow.getElementById("option-decor-vehicle"),this.optionDecorWitness=this.shadow.getElementById("option-decor-witness"),this.ufoElement.canvasElement.addEventListener("pointerdown",t=>this.onPointerDown(t)),this.ufoElement.canvasElement.addEventListener("pointermove",t=>this.onPointerMove(t)),this.ufoElement.canvasElement.addEventListener("contextmenu",t=>this.onContextMenu(t)),this.contextGroupButton.addEventListener("click",()=>this.groupSelected()),this.contextUngroupButton.addEventListener("click",()=>this.ungroupSelected()),this.contextBringToFrontButton.addEventListener("click",()=>this.bringSelectedToFront()),this.contextSendToBackButton.addEventListener("click",()=>this.sendSelectedToBack()),this.contextDeleteButton.addEventListener("click",()=>{this.hideContextMenu(),this.deleteShape()}),this.contextAddVertexButton.addEventListener("click",()=>this.addVertexAtContextMenu()),this.contextDeleteVertexButton.addEventListener("click",()=>this.deleteVertexAtContextMenu()),this.contextViewTestimonyButton.addEventListener("click",()=>this.viewWitnessTestimony()),this.playPauseButton.addEventListener("click",()=>{this.ufoElement.togglePlayPause(),this.syncPlaybackControls()}),this.loopButton.addEventListener("click",()=>{this.ufoElement.toggleLoop(),this.syncPlaybackControls()}),this.seekInput.addEventListener("input",()=>this.ufoElement.currentTime=Number(this.seekInput.value)),this.recordButton.addEventListener("click",()=>this.toggleRecording()),this.addShapeButton.addEventListener("click",()=>this.addShape()),this.deleteShapeButton.addEventListener("click",()=>this.deleteShape()),this.exportButton.addEventListener("click",()=>this.exportJson()),this.importFileInput.addEventListener("change",()=>this.importFromFile()),this.importUrlButton.addEventListener("click",()=>this.importFromUrl()),this.durationInput.addEventListener("input",()=>{this.ufoElement.durationSeconds=this.durationInput.value===""?void 0:Number(this.durationInput.value),this.ufoElement.refresh(),this.updateDurationValidity()}),this.sourceSelect.addEventListener("change",()=>this.selectUnit(this.sourceSelect.value)),this.shapeTitleInput.addEventListener("input",()=>this.updateShapeTitle());for(const t of[this.objectSizeInput,this.objectDistanceInput])t.addEventListener("input",()=>this.updatePhysicalExtent());this.addDecorWitnessButton.addEventListener("click",()=>this.addDecor("witness")),this.addDecorBuildingButton.addEventListener("click",()=>this.addDecor()),this.deleteDecorButton.addEventListener("click",()=>this.deleteDecor()),this.decorSelect.addEventListener("change",()=>this.selectDecor(this.decorSelect.value));for(const t of[this.decorTitleInput,this.decorEastInput,this.decorNorthInput,this.decorHeadingInput,this.decorSightingUrlInput,this.decorFloorsInput,this.decorOccupiedFloorInput])t.addEventListener("input",()=>this.updateDecor());this.decorWitnessSideSelect.addEventListener("change",()=>this.updateDecor());for(const t of ss)this.decorWindowInputs[t].addEventListener("input",()=>this.updateDecorWindows());this.decorLitInput.addEventListener("input",()=>this.updateDecorLit()),this.ufoElement.addEventListener("timeupdate",()=>{this.onSelectionOrTimeChanged(),this.syncPlaybackControls(),this.dispatchEvent(new CustomEvent("sightingchange"))}),document.addEventListener("keydown",this.handleKeyDown);for(const t of qd)this.presetButtons[t].addEventListener("click",()=>this.setAppearance({presetId:t}));this.colorInput.addEventListener("input",()=>this.setAppearance({color:this.colorInput.value})),this.transparencyInput.addEventListener("input",()=>this.setAppearance({transparency:Number(this.transparencyInput.value)})),this.haloScaleInput.addEventListener("input",()=>this.setAppearance({haloScale:Number(this.haloScaleInput.value)}));for(const t of[this.latInput,this.lngInput,this.headingInput,this.pitchInput,this.elevationInput])t.addEventListener("input",()=>this.updateObserver());this.headingInput.addEventListener("focus",()=>this.sceneElement.setCompassForced(!0)),this.headingInput.addEventListener("blur",()=>this.sceneElement.setCompassForced(!1)),this.lensFlareBrightnessInput.addEventListener("input",()=>this.sceneElement.setAttribute("lens-flare-brightness",this.lensFlareBrightnessInput.value)),this.cameraDeviceInput.addEventListener("input",()=>this.sceneElement.setAttribute("lens-flare-intensity",this.cameraDeviceInput.value)),this.utcOffsetInput.addEventListener("input",()=>this.updateUtcOffset()),this.obsTimeInput.addEventListener("input",()=>this.updateObservationTime()),this.obsEndTimeInput.addEventListener("input",()=>this.updateObservationEndTime()),this.obsTimeInput.addEventListener("blur",()=>this.validateEdtfTimeInput(this.obsTimeInput)),this.obsEndTimeInput.addEventListener("blur",()=>this.validateEdtfTimeInput(this.obsEndTimeInput));for(const t of[this.witnessIdInput,this.witnessDirNameInput,this.witnessTitleInput,this.witnessLastNameInput,this.witnessFirstNamesInput,this.caseIdInput])t.addEventListener("input",()=>this.updateWitnessMetadata());this.descriptionInput.addEventListener("input",()=>this.updateDescription()),this.tagsInput.addEventListener("input",()=>this.updateTags());for(const t of[this.cloudCoverInput,this.cloudDarknessInput,this.cloudBaseInput,this.precipitationTypeSelect,this.precipitationIntensityInput,this.windDirectionInput,this.windSpeedInput,this.stormInput])t.addEventListener("input",()=>this.applyWeatherAtPlayhead());this.updatePresetButtons(),this.setRecordButtonLabel(!1),this.applyAppearanceAtPlayhead(),this.shapeTitleInput.value=this.shapeLabel(this.currentSourceId),this.updateShapeTitle(),this.currentDecorId=this.ufoElement.sighting.decor[0]?.id,this.refreshDecorList(),this.onSelectionOrTimeChanged(),this.syncDurationField(),this.syncPlaybackControls(),this.loadLocaleMessages()}static get observedAttributes(){return["src"]}connectedCallback(){const e=this.getAttribute("src");e&&this.importFromUrl(e)}attributeChangedCallback(e,t,n){e==="src"&&n&&n!==t&&this.isConnected&&this.importFromUrl(n)}disconnectedCallback(){document.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("click",this.handleOutsideContextMenuClick),this.endDrag()}get sightingData(){return this.ufoElement.sightingData}set sightingData(e){this.endDrag(),this.ufoElement.sightingData=e,this.currentSourceId=this.ufoElement.sighting.timeline.sourceIds[0]??na,this.selectedSourceIds=new Set([this.currentSourceId]),this.refreshSourceList(),this.currentDecorId=this.ufoElement.sighting.decor[0]?.id,this.refreshDecorList(),this.onSelectionOrTimeChanged(),this.syncDurationField(),this.syncObservationTimeFields(),this.syncObservationEndTimeFields(),this.syncWitnessMetadataFields()}exportJson(){const e=this.sightingData,t=`${e.witness?.id??"sighting"}-sighting.json`,n=new Blob([JSON.stringify(e,null,2)],{type:"application/json"}),s=URL.createObjectURL(n),r=document.createElement("a");r.href=s,r.download=t,r.click(),URL.revokeObjectURL(s)}async importFromFile(){const e=this.importFileInput.files?.[0];if(e)try{const t=await new Promise((n,s)=>{const r=new FileReader;r.onload=()=>n(r.result),r.onerror=()=>s(r.error),r.readAsText(e)});this.sightingData=JSON.parse(t)}catch{window.alert(this.messages.importError)}finally{this.importFileInput.value=""}}async importFromUrl(e=this.importUrlInput.value.trim()){if(e)try{const t=await fetch(e);if(!t.ok)throw new Error(`HTTP ${t.status}`);this.sightingData=await t.json()}catch{window.alert(this.messages.importError)}}numberOrUndefined(e){return e===""?void 0:Number(e)}stringOrUndefined(e){return e===""?void 0:e}wrapDegrees(e,t){if(e===void 0)return;const n=(e%360+360)%360;return String(n)!==t.value&&(t.value=String(n)),n}updateObserver(){if(this.ufoElement.playbackState==="playing")return;const e=this.numberOrUndefined(this.latInput.value),t=this.numberOrUndefined(this.lngInput.value),n=this.wrapDegrees(this.numberOrUndefined(this.headingInput.value),this.headingInput),s=this.numberOrUndefined(this.pitchInput.value)??0,r=this.numberOrUndefined(this.elevationInput.value)??0,a=this.ufoElement.sighting.event,o=this.ufoElement.sighting.witnessTrack,l=this.ufoElement.currentTime;a.place=e!==void 0&&t!==void 0?[{lat:e,lng:t}]:void 0,e===void 0&&t===void 0&&n===void 0&&s===0&&r===0?o.removeKeyframeAt(l):o.addKeyframe(l,{lat:e,lng:t,elevationM:r,headingDeg:n,pitchDeg:s,fovDeg:Xd}),this.ufoElement.refresh()}applyEdtfTimeInput(e,t){const n=e.value.trim();if(n==="")t(void 0);else{const s=Yc(n);if(!s)return;t(s)}e.setCustomValidity(""),e.classList.remove("invalid"),this.ufoElement.refresh(),this.syncDurationField()}validateEdtfTimeInput(e){const t=e.value.trim();t===""||Yc(t)||(e.setCustomValidity(this.messages.edtfInvalid),e.classList.add("invalid"))}updateUtcOffset(){this.ufoElement.sighting.event.utcOffsetHours=this.numberOrUndefined(this.utcOffsetInput.value),this.ufoElement.refresh()}updateObservationTime(){this.applyEdtfTimeInput(this.obsTimeInput,e=>{this.ufoElement.sighting.event.time=e})}updateObservationEndTime(){this.applyEdtfTimeInput(this.obsEndTimeInput,e=>{this.ufoElement.sighting.event.endTime=e})}updateWitnessMetadata(){const e=this.ufoElement.sighting,t=this.witnessFirstNamesInput.value.split(",").map(s=>s.trim()).filter(s=>s.length>0),n={id:this.stringOrUndefined(this.witnessIdInput.value),dirName:this.stringOrUndefined(this.witnessDirNameInput.value),title:this.stringOrUndefined(this.witnessTitleInput.value),lastName:this.stringOrUndefined(this.witnessLastNameInput.value),firstNames:t.length>0?t:void 0};e.witness=Object.values(n).some(s=>s!==void 0)?n:void 0,e.caseId=this.stringOrUndefined(this.caseIdInput.value),this.ufoElement.refresh()}updateDescription(){this.ufoElement.sighting.event.description=this.stringOrUndefined(this.descriptionInput.value),this.ufoElement.refresh()}updateTags(){const e=this.tagsInput.value.split(",").map(t=>t.trim()).filter(t=>t.length>0);this.ufoElement.sighting.event.tags=e.length>0?e:void 0,this.ufoElement.refresh()}applyWeatherAtPlayhead(){if(this.ufoElement.playbackState==="playing")return;this.sceneElement.resumeWeatherAudio();const e={cloudCover:Number(this.cloudCoverInput.value),cloudDarkness:Number(this.cloudDarknessInput.value),cloudBaseM:this.numberOrUndefined(this.cloudBaseInput.value),precipitationType:this.precipitationTypeSelect.value,precipitationIntensity:Number(this.precipitationIntensityInput.value),windDirectionDeg:this.wrapDegrees(this.numberOrUndefined(this.windDirectionInput.value),this.windDirectionInput)??0,windSpeed:Number(this.windSpeedInput.value),storm:this.stormInput.checked};this.ufoElement.sighting.weatherTrack.addKeyframe(this.ufoElement.currentTime,e),this.ufoElement.refresh()}syncDurationField(){if(this.shadow.activeElement!==this.durationInput){const e=dc(this.ufoElement.sighting.event);this.durationInput.value=e!==void 0?String(e/1e3):""}this.updateDurationValidity()}updateDurationValidity(){const e=this.durationInput.value==="";this.durationInput.classList.toggle("invalid",e),this.durationInput.setAttribute("aria-invalid",String(e));const t=K0(this.ufoElement.sighting.event);this.durationInput.title=t==="imprecise"?this.messages.durationImprecise:""}syncPlaybackControls(){const e=this.ufoElement.playbackState==="playing",t=this.ufoElement.seekableDuration>0;this.playPauseButton.textContent=e?"⏸":"▶",this.playPauseButton.disabled=!t;const n=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=n,this.playPauseButton.setAttribute("aria-label",n),this.loopButton.setAttribute("aria-pressed",String(this.ufoElement.autoReplayEnabled)),this.seekInput.max=String(this.ufoElement.seekableDuration),this.shadow.activeElement!==this.seekInput&&(this.seekInput.value=String(this.ufoElement.currentTime)),this.timeStartLabel.textContent=this.ufoElement.positionLabel,this.timeEndLabel.textContent=this.ufoElement.durationLabel}syncObservationTimeFields(){const e=this.ufoElement.sighting.event.time;this.obsTimeInput.value=e?qc(e):"",this.obsTimeInput.setCustomValidity(""),this.obsTimeInput.classList.remove("invalid")}syncObservationEndTimeFields(){const e=this.ufoElement.sighting.event.endTime;this.obsEndTimeInput.value=e?qc(e):"",this.obsEndTimeInput.setCustomValidity(""),this.obsEndTimeInput.classList.remove("invalid")}syncWitnessMetadataFields(){const e=this.ufoElement.sighting;this.witnessIdInput.value=e.witness?.id??"",this.witnessDirNameInput.value=e.witness?.dirName??"",this.witnessTitleInput.value=e.witness?.title??"",this.witnessLastNameInput.value=e.witness?.lastName??"",this.witnessFirstNamesInput.value=e.witness?.firstNames?.join(", ")??"",this.caseIdInput.value=e.caseId??"",this.descriptionInput.value=e.event.description??"",this.tagsInput.value=e.event.tags?.join(", ")??""}syncWeatherFromTimeline(){if(this.ufoElement.playbackState==="playing")return;const e=Ho(this.ufoElement.sighting,this.ufoElement.currentTime),t=this.shadow.activeElement;t!==this.cloudCoverInput&&(this.cloudCoverInput.value=String(e.cloudCover)),t!==this.cloudDarknessInput&&(this.cloudDarknessInput.value=String(e.cloudDarkness)),t!==this.cloudBaseInput&&(this.cloudBaseInput.value=e.cloudBaseM===void 0?"":String(e.cloudBaseM)),t!==this.precipitationTypeSelect&&(this.precipitationTypeSelect.value=e.precipitationType),t!==this.precipitationIntensityInput&&(this.precipitationIntensityInput.value=String(e.precipitationIntensity)),t!==this.windDirectionInput&&(this.windDirectionInput.value=String(e.windDirectionDeg)),t!==this.windSpeedInput&&(this.windSpeedInput.value=String(e.windSpeed)),t!==this.stormInput&&(this.stormInput.checked=e.storm)}syncObserverFromTimeline(){if(this.ufoElement.playbackState==="playing")return;const e=this.ufoElement.sighting,t=e.witnessTrack.getInterpolatedPoseAt(this.ufoElement.currentTime),n=e.event.place?.[0],s=this.shadow.activeElement;s!==this.latInput&&(this.latInput.value=t?.lat!==void 0?String(t.lat):n?String(n.lat):""),s!==this.lngInput&&(this.lngInput.value=t?.lng!==void 0?String(t.lng):n?String(n.lng):""),s!==this.headingInput&&(this.headingInput.value=t?.headingDeg!==void 0?String(t.headingDeg):""),s!==this.pitchInput&&(this.pitchInput.value=String(t?.pitchDeg??0)),s!==this.elevationInput&&(this.elevationInput.value=String(t?.elevationM??0))}get appearance(){return{...this.currentAppearance}}set appearance(e){this.setAppearance(e)}setAppearance(e){const t="presetId"in e;this.currentAppearance={...this.currentAppearance,...e},this.updatePresetButtons(),!(this.isRecording||this.ufoElement.playbackState==="playing")&&this.applyAppearanceAtPlayhead(void 0,t)}applyAppearanceAtPlayhead(e,t=!0){const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=n.getInterpolatedShapeAt(s,this.currentSourceId),a=this.buildAppearanceShape(e??r?.bounds??this.defaultBounds(),r,t);n.addKeyframe(s,[{sourceId:this.currentSourceId,shape:a}]),this.ufoElement.refresh()}buildAppearanceShape(e,t,n=!0){if(t&&!n)return{...t,color:this.currentAppearance.color,transparency:this.currentAppearance.transparency,haloScale:this.currentAppearance.haloScale};const s=Wc(e,this.currentAppearance);return t?{...s,angle:t.angle,title:t.title,selected:t.selected}:s}onSelectionOrTimeChanged(){this.syncAppearanceFromTimeline(),this.syncObserverFromTimeline(),this.syncWeatherFromTimeline(),this.syncDecorLitFromTimeline(),this.syncIndoorLookReset(),this.updateAppearanceFieldsDisabledState(),this.updateShapeTitleValidity(),this.ufoElement.selectedSourceIds=this.selectedSourceIds;const e=this.ufoElement.sighting.timeline.sourceIds,t=e.filter(n=>this.selectedSourceIds.has(n)).length;this.deleteShapeButton.disabled=this.isRecording||this.ufoElement.playbackState==="playing"||t===0||e.length-t<1}updateAppearanceFieldsDisabledState(){const e=this.selectedSourceIds.size>1,t=e?this.messages.multipleShapesSelected:"";for(const n of[this.shapeTitleInput,this.colorInput,this.transparencyInput,this.haloScaleInput,this.objectSizeInput,this.objectDistanceInput,this.sourceSelect,...Object.values(this.presetButtons)])n.disabled=e,n.title=t}syncAppearanceFromTimeline(){if(this.ufoElement.playbackState==="playing"||this.selectedSourceIds.size>1)return;const e=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,this.currentSourceId);e&&(this.currentAppearance={presetId:_y(e),color:e.color,transparency:e.transparency,haloScale:e.haloScale},this.updatePresetButtons(),this.colorInput.value=this.currentAppearance.color,this.transparencyInput.value=String(this.currentAppearance.transparency),this.haloScaleInput.value=String(this.currentAppearance.haloScale),this.shadow.activeElement!==this.shapeTitleInput&&(this.shapeTitleInput.value=e.title??""),this.shadow.activeElement!==this.objectSizeInput&&(this.objectSizeInput.value=e.physical?String(Number(e.physical.sizeM.toFixed(2))):""),this.shadow.activeElement!==this.objectDistanceInput&&(this.objectDistanceInput.value=e.physical?String(Number(e.physical.distanceM.toFixed(2))):""),this.refreshApparentSize())}updateShapeTitle(){const e=this.ufoElement.sighting.timeline,t=this.ufoElement.currentTime,n=e.getInterpolatedShapeAt(t,this.currentSourceId);n&&(e.addKeyframe(t,[{sourceId:this.currentSourceId,shape:{...n,title:this.stringOrUndefined(this.shapeTitleInput.value)}}]),this.ufoElement.refresh(),this.refreshSourceList(),this.updateShapeTitleValidity())}updatePhysicalExtent(){const e=this.ufoElement.sighting.timeline,t=this.ufoElement.currentTime,n=e.getInterpolatedShapeAt(t,this.currentSourceId);if(!n)return;const s=this.numberOrUndefined(this.objectSizeInput.value),r=this.numberOrUndefined(this.objectDistanceInput.value);if(s===void 0&&r===void 0){e.addKeyframe(t,[{sourceId:this.currentSourceId,shape:{...n,physical:void 0}}]),this.ufoElement.refresh(),this.refreshApparentSize();return}if(s===void 0||r===void 0||s<=0||r<=0){this.refreshApparentSize();return}const a={sizeM:s,distanceM:r},o=this.ufoElement.canvasElement,l=ps.widthPx(a,o.height,this.currentFovDeg()),c=n.bounds.height*(n.bounds.width===0?1:l/n.bounds.width),h={x:n.bounds.x+(n.bounds.width-l)/2,y:n.bounds.y+(n.bounds.height-c)/2,width:l,height:c},u=n.kind==="oval"?{...n,bounds:h,physical:a}:{...n,bounds:h,physical:a,points:this.scalePoints(n,h)};e.addKeyframe(t,[{sourceId:this.currentSourceId,shape:u}]),this.ufoElement.refresh(),this.refreshApparentSize()}scalePoints(e,t){const n=e.bounds.width===0?1:t.width/e.bounds.width,s=e.bounds.height===0?1:t.height/e.bounds.height;return e.points.map(r=>({x:r.x*n,y:r.y*s}))}currentFovDeg(){return Go(this.ufoElement.sighting,this.ufoElement.currentTime)?.fovDeg??Xd}refreshApparentSize(){const e=this.selectedSourceIds.size===1?this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,this.currentSourceId):void 0;if(!e){this.apparentSizeOutput.textContent="";return}const t=this.ufoElement.canvasElement,n=ps.pxToDeg(e.bounds.width,t.height,this.currentFovDeg()),s=ps.inMoons(n);this.apparentSizeOutput.textContent=this.messages.apparentSize.replace("{deg}",n.toLocaleString(void 0,{maximumFractionDigits:n<1?2:1})).replace("{moons}",s.toLocaleString(void 0,{maximumFractionDigits:s<10?1:0}))}updateShapeTitleValidity(){const e=this.selectedSourceIds.size===1&&this.shapeTitleInput.value==="";this.shapeTitleInput.classList.toggle("invalid",e),this.shapeTitleInput.setAttribute("aria-invalid",String(e))}shapeLabel(e){const t=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,e);if(t?.title)return t.title;const n=/^ufo-(\d+)$/.exec(e);return n?`${this.messages.shape} ${n[1]}`:e}addShape(){if(this.isRecording)return;const e=this.ufoElement.sighting.timeline,t=new Set([...e.sourceIds,this.currentSourceId]);let n=t.size+1;for(;t.has(`ufo-${n}`);)n++;this.currentSourceId=`ufo-${n}`,this.selectedSourceIds=new Set([this.currentSourceId]),this.applyAppearanceAtPlayhead(this.offsetDefaultBounds(e.sourceIds.length)),this.shapeTitleInput.value=this.shapeLabel(this.currentSourceId),this.updateShapeTitle()}deleteShape(){if(this.isRecording||this.ufoElement.playbackState==="playing")return;const e=this.ufoElement.sighting.timeline,t=e.sourceIds.filter(s=>this.selectedSourceIds.has(s));if(!(t.length===0||e.sourceIds.length-t.length<1||!(t.length===1?window.confirm(this.messages.confirmDeleteShape.replace("{name}",this.shapeLabel(t[0]))):window.confirm(this.messages.confirmDeleteShapes.replace("{count}",String(t.length)))))){for(const s of t)e.removeSource(s);this.currentSourceId=e.sourceIds[0],this.selectedSourceIds=new Set([this.currentSourceId]),this.refreshSourceList(),this.ufoElement.refresh()}}offsetDefaultBounds(e){const t=this.defaultBounds(),n=24;return{...t,x:t.x+e*n,y:t.y+e*n}}refreshSourceList(){const e=this.ufoElement.sighting.timeline,t=[...new Set([...e.sourceIds,this.currentSourceId])];this.sourceSelect.innerHTML="";for(const n of t){const s=document.createElement("option");s.value=n,s.textContent=this.shapeLabel(n),this.sourceSelect.appendChild(s)}this.sourceSelect.value=this.currentSourceId}decorLabel(e){if(e.title)return e.title;const n=this.ufoElement.sighting.decor.filter(r=>r.kind===e.kind).indexOf(e)+1;return`${this.decorKindSelect.querySelector(`option[value="${e.kind}"]`)?.textContent??e.kind} ${n}`}addDecor(e=this.decorKindSelect.value){const t=this.ufoElement.sighting,s={id:`decor-${t.decor.length+1}`,kind:e,title:this.nextDecorLabel(e),eastM:8*t.decor.length,northM:15,headingDeg:0,lit:!1,...e==="building"?{floors:ha}:{},windows:G2(e)};t.decor=[...t.decor,s],this.currentDecorId=s.id,this.refreshDecorList(),this.ufoElement.refresh()}nextDecorLabel(e){const t=this.ufoElement.sighting.decor.filter(s=>s.kind===e).length;return`${this.decorKindSelect.querySelector(`option[value="${e}"]`)?.textContent??e} ${t+1}`}deleteDecor(){if(this.currentDecorId===void 0)return;const e=this.ufoElement.sighting;e.decor=e.decor.filter(t=>t.id!==this.currentDecorId),this.currentDecorId=e.decor[0]?.id,this.refreshDecorList(),this.ufoElement.refresh()}selectDecor(e){this.currentDecorId=e,this.decorSelect.value=e,this.syncDecorFields()}updateDecor(){if(this.currentDecorId===void 0)return;const e=this.ufoElement.sighting,t=this.wrapDegrees(Number(this.decorHeadingInput.value),this.decorHeadingInput)??0,n=this.decorWitnessSideSelect.value;e.decor=e.decor.map(s=>{if(s.id!==this.currentDecorId)return s;const r=Zs(s.kind)&&n!==""?n:void 0;return{...s,title:this.stringOrUndefined(this.decorTitleInput.value),eastM:Number(this.decorEastInput.value),northM:Number(this.decorNorthInput.value),headingDeg:t,sightingUrl:this.stringOrUndefined(this.decorSightingUrlInput.value),witnessSide:r,floors:s.kind==="building"?Number(this.decorFloorsInput.value):void 0,occupiedFloor:s.kind==="building"?Number(this.decorOccupiedFloorInput.value):void 0}}),this.decorSelect.options[this.decorSelect.selectedIndex].textContent=this.decorLabel(e.decor.find(s=>s.id===this.currentDecorId)),this.syncDecorVisibility(),this.updateDecorTitleValidity(),this.ufoElement.refresh()}updateDecorTitleValidity(){const e=this.currentDecorId!==void 0&&this.decorTitleInput.value==="";this.decorTitleInput.classList.toggle("invalid",e),this.decorTitleInput.setAttribute("aria-invalid",String(e))}updateDecorWindows(){if(this.currentDecorId===void 0)return;const e=this.ufoElement.sighting;e.decor=e.decor.map(t=>{if(t.id!==this.currentDecorId)return t;const n={};for(const s of ss){const r=this.decorWindowInputs[s];if(r.value==="")continue;const a=sc(t.kind,s)?0:ic,o=Math.max(a,Math.min(100,Number(r.value)));n[s]=o,String(o)!==r.value&&(r.value=String(o))}return{...t,windows:n}}),this.ufoElement.refresh()}updateDecorLit(){if(this.currentDecorId===void 0||this.ufoElement.playbackState==="playing")return;const e=this.ufoElement.sighting,t=this.ufoElement.currentTime,n=this.decorLitInput.checked;e.decor=e.decor.map(s=>{if(s.id!==this.currentDecorId)return s;const r=[...(s.litKeyframes??[]).filter(a=>a.t!==t),{t,lit:n}].sort((a,o)=>a.t-o.t);return{...s,litKeyframes:r}}),this.ufoElement.refresh()}refreshDecorList(){const e=this.ufoElement.sighting.decor;this.decorSelect.innerHTML="";for(const t of e){const n=document.createElement("option");n.value=t.id,n.textContent=this.decorLabel(t),this.decorSelect.appendChild(n)}this.currentDecorId!==void 0&&(this.decorSelect.value=this.currentDecorId),this.syncDecorFields()}syncDecorFields(){const e=this.ufoElement.sighting.decor.find(n=>n.id===this.currentDecorId),t=e!==void 0;this.deleteDecorButton.disabled=!t;for(const n of[this.decorSelect,this.decorTitleInput,this.decorEastInput,this.decorNorthInput,this.decorHeadingInput,this.decorLitInput,this.decorSightingUrlInput,this.decorFloorsInput,this.decorOccupiedFloorInput,this.decorWitnessSideSelect])n.disabled=!t;this.decorTitleInput.value=e?.title??"",this.decorEastInput.value=String(e?.eastM??0),this.decorNorthInput.value=String(e?.northM??0),this.decorHeadingInput.value=String(e?.headingDeg??0),this.decorLitInput.checked=e?rc(e,this.ufoElement.currentTime):!1,this.decorSightingUrlInput.value=e?.sightingUrl??"",this.decorFloorsInput.value=String(e?.floors??ha),this.decorOccupiedFloorInput.value=String(e?.occupiedFloor??0),this.decorWitnessSideSelect.value=e?.witnessSide??"";for(const n of ss){const s=e?.windows?.[n];this.decorWindowInputs[n].value=s===void 0?"":String(s)}this.syncDecorVisibility(),this.updateDecorTitleValidity()}setRowVisible(e,t){const n=e.closest("label")??e;n.hidden=!t}syncDecorVisibility(){const e=this.ufoElement.sighting.decor.find(c=>c.id===this.currentDecorId),t=e!==void 0,n=e?.kind;this.setRowVisible(this.decorSelect,t),this.setRowVisible(this.deleteDecorButton,t),this.setRowVisible(this.decorTitleInput,t),this.setRowVisible(this.decorEastInput,t),this.setRowVisible(this.decorNorthInput,t),this.setRowVisible(this.decorHeadingInput,t),this.setRowVisible(this.decorLitInput,t);const s=t&&n!==void 0&&l0(n);this.setRowVisible(this.labelDecorWindows,s);const r=n!==void 0?Nc(n):[];for(const c of ss)this.decorWindowInputs[c].disabled=!t,this.decorWindowInputs[c].min=String(n!==void 0&&sc(n,c)?0:ic),this.setRowVisible(this.decorWindowInputs[c],s&&r.includes(c));const a=t&&n!==void 0&&Zs(n);this.setRowVisible(this.decorWitnessSideSelect,a);const o=n!==void 0?z2(n):[];for(const c of ss)this.optionWitnessSide[c].hidden=!o.includes(c);const l=t&&n==="building";this.setRowVisible(this.decorFloorsInput,l),this.setRowVisible(this.decorOccupiedFloorInput,l),this.decorOccupiedFloorInput.max=String(e?.floors??ha)}syncDecorLitFromTimeline(){if(this.ufoElement.playbackState==="playing"||this.shadow.activeElement===this.decorLitInput)return;const e=this.ufoElement.sighting.decor.find(t=>t.id===this.currentDecorId);e&&(this.decorLitInput.checked=rc(e,this.ufoElement.currentTime))}updatePresetButtons(){for(const e of qd)this.presetButtons[e]?.setAttribute("aria-pressed",String(e===this.currentAppearance.presetId))}get samplingRate(){return Number(this.samplingRateInput.value)}buildPrototype(e=this.defaultBounds()){return Wc(e,this.currentAppearance)}defaultBounds(){const e=this.ufoElement.canvasElement;return{x:e.width/2-ls.width/2,y:e.height/2-ls.height/2,width:ls.width,height:ls.height}}toggleRecording(){this.endDrag();const e=this.ufoElement.canvasElement;this.isRecording?(this.recorder?.stop(),this.isRecording=!1,this.setRecordButtonLabel(!1),this.setCanvasCursor(void 0),e.style.touchAction="",this.sourceSelect.disabled=!1,this.addShapeButton.disabled=!1,this.ufoElement.refresh(),this.refreshSourceList()):(this.recorder=new my(this.ufoElement.sighting.timeline,new gy(this.samplingRate)),this.recorder.start(this.currentSourceId,this.buildPrototype()),this.isRecording=!0,this.setRecordButtonLabel(!0),this.setCanvasCursor("record"),e.style.touchAction="none",this.sourceSelect.disabled=!0,this.addShapeButton.disabled=!0,this.deleteShapeButton.disabled=!0)}setRecordButtonLabel(e){this.recordButton.textContent=e?"⏹":"⏺";const t=e?this.messages.stop:this.messages.record;this.recordButton.title=t,this.recordButton.setAttribute("aria-label",t)}async loadLocaleMessages(){const e=Ia(navigator.languages,jd);e!=="en"&&this.applyMessages(await ff(e))}applyMessages(e){this.currentSourceId===na&&this.shapeTitleInput.value===`${this.messages.shape} 1`?(this.messages=e,this.shapeTitleInput.value=`${this.messages.shape} 1`,this.updateShapeTitle()):this.messages=e,this.presetButtons.oval.textContent=e.oval,this.presetButtons.polygon.textContent=e.polygon,this.contextAddVertexButton.textContent=e.addVertex,this.contextDeleteVertexButton.textContent=e.deleteVertex,this.labelColor.textContent=e.color,this.labelTransparency.textContent=e.transparency,this.labelHalo.textContent=e.halo,this.labelShape.textContent=e.shape,this.labelShapeTitle.textContent=e.shapeTitle,this.labelUtcOffset.textContent=e.utcOffset,this.utcOffsetInput.placeholder=e.utcOffsetPlaceholder,this.labelObjectSize.textContent=e.objectSize,this.labelObjectDistance.textContent=e.objectDistance,this.objectSizeInput.placeholder=e.objectSizePlaceholder,this.objectDistanceInput.placeholder=e.objectDistancePlaceholder,this.refreshApparentSize(),this.labelSamplingRate.textContent=e.samplingRate,this.labelDuration.textContent=e.duration,this.durationInput.placeholder=e.durationPlaceholder,this.addShapeButton.title=e.addShape,this.addShapeButton.setAttribute("aria-label",e.addShape),this.deleteShapeButton.title=e.deleteShape,this.deleteShapeButton.setAttribute("aria-label",e.deleteShape),this.contextGroupButton.textContent=e.group,this.contextUngroupButton.textContent=e.ungroup,this.contextBringToFrontButton.textContent=e.bringToFront,this.contextSendToBackButton.textContent=e.sendToBack,this.contextDeleteButton.textContent=e.contextMenuDelete,this.exportButton.textContent=e.export,this.labelImportFile.textContent=e.importFile,this.labelImportUrl.textContent=e.importUrl,this.importUrlInput.placeholder=e.importUrlPlaceholder,this.importUrlButton.textContent=e.importButton,this.labelLatitude.textContent=e.latitude,this.labelLongitude.textContent=e.longitude,this.labelHeading.textContent=e.heading,this.headingInput.placeholder=e.headingPlaceholder,this.labelPitch.textContent=e.pitch,this.labelElevation.textContent=e.elevation,this.labelObservationTime.textContent=e.observationTime,this.labelObservationEndTime.textContent=e.observationEndTime,this.obsTimeInput.placeholder=e.edtfPlaceholder,this.obsTimeInput.title=e.observationTimeHint,this.obsEndTimeInput.placeholder=e.edtfPlaceholder,this.obsEndTimeInput.title=e.observationEndTimeHint,this.presetsGroup.setAttribute("aria-label",e.presetsGroupLabel),this.labelDecor.textContent=e.decor,this.labelDecorFieldset.textContent=e.decor,this.optionDecorBuilding.textContent=e.decorBuilding,this.optionDecorTree.textContent=e.decorTree,this.optionDecorStreetlight.textContent=e.decorStreetlight,this.optionDecorVehicle.textContent=e.decorVehicle,this.optionDecorWitness.textContent=e.decorWitness,this.deleteDecorButton.title=e.deleteDecor,this.deleteDecorButton.setAttribute("aria-label",e.deleteDecor),this.labelDecorTitle.textContent=e.decorTitle,this.labelDecorEast.textContent=e.decorEast,this.labelDecorNorth.textContent=e.decorNorth,this.labelDecorHeading.textContent=e.decorHeading,this.labelDecorLit.textContent=e.decorLit,this.labelDecorSightingUrl.textContent=e.decorSightingUrl,this.contextViewTestimonyButton.textContent=e.viewTestimony,this.labelContextMasks.textContent=`${e.masks} ▸`,this.addDecorWitnessButton.textContent=e.addWitness,this.addDecorBuildingButton.title=e.addDecor,this.addDecorBuildingButton.setAttribute("aria-label",e.addDecor),this.labelDecorFloors.textContent=e.decorFloors,this.labelDecorOccupiedFloor.textContent=e.decorOccupiedFloor,this.labelDecorWitnessSide.textContent=e.decorWitnessSide,this.labelDecorWindows.textContent=e.decorWindows,this.optionWitnessSideNone.textContent=e.decorWitnessSideNone;const t={front:e.decorSideFront,behind:e.decorSideBehind,left:e.decorSideLeft,right:e.decorSideRight,"front-left":e.decorSideFrontLeft,"front-right":e.decorSideFrontRight,"behind-left":e.decorSideBehindLeft,"behind-right":e.decorSideBehindRight};for(const n of ss)this.labelDecorSide[n].textContent=t[n],this.optionWitnessSide[n].textContent=t[n];this.refreshDecorList(),this.labelWitnessId.textContent=e.witnessId,this.labelWitnessDirName.textContent=e.witnessDirName,this.labelWitnessTitle.textContent=e.witnessTitle,this.labelWitnessLastName.textContent=e.witnessLastName,this.labelWitnessFirstNames.textContent=e.witnessFirstNames,this.witnessFirstNamesInput.placeholder=e.tagsPlaceholder,this.labelCaseId.textContent=e.caseId,this.labelDescription.textContent=e.description,this.labelTags.textContent=e.tags,this.tagsInput.placeholder=e.tagsPlaceholder,this.labelWeather.textContent=e.weather,this.labelShapeGroup.textContent=e.shapeGroup,this.labelTemporalGroup.textContent=e.temporalGroup,this.labelLocationGroup.textContent=e.locationGroup,this.labelObservationGroup.textContent=e.observationGroup,this.labelWitnessGroup.textContent=e.witnessGroup,this.labelCircumstancesGroup.textContent=e.circumstancesGroup,this.labelCloudCover.textContent=e.cloudCover,this.labelCloudDarkness.textContent=e.cloudDarkness,this.labelCloudBase.textContent=e.cloudBase,this.labelPrecipitationType.textContent=e.precipitationType,this.optionPrecipitationNone.textContent=e.precipitationNone,this.optionPrecipitationRain.textContent=e.precipitationRain,this.optionPrecipitationSnow.textContent=e.precipitationSnow,this.optionPrecipitationHail.textContent=e.precipitationHail,this.labelPrecipitationIntensity.textContent=e.precipitationIntensity,this.labelWindDirection.textContent=e.windDirection,this.labelWindSpeed.textContent=e.windSpeed,this.labelStorm.textContent=e.storm,this.labelLensFlareBrightness.textContent=e.lensFlareBrightness,this.labelCameraDevice.textContent=e.cameraDevice,this.loopButton.title=e.autoReplay,this.loopButton.setAttribute("aria-label",e.autoReplay),this.setRecordButtonLabel(this.isRecording),this.syncPlaybackControls()}onPointerDown(e){if(this.isRecording){this.onPointerMove(e);return}const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=this.ufoElement.playbackState==="playing";if(this.selectedSourceIds.size>1){const l=new za(this.selectedMembers()),c=Ke.hitTestHandle({bounds:l.bounds(),angle:0},t);if(c==="rotate"){if(r)return;this.dragState={kind:"group-rotate",group:l,startPointer:t},this.startDragListening();return}if(c){if(r)return;this.dragState={kind:"group-resize",group:l,handle:c},this.startDragListening();return}}else{const l=n.getInterpolatedShapeAt(s,this.currentSourceId),c=l?.kind==="polygon"?Ke.hitTestVertex(l,t):void 0;if(l?.kind==="polygon"&&c!==void 0){if(r)return;this.dragState={kind:"vertex",sourceId:this.currentSourceId,original:l,vertexIndex:c},this.startDragListening();return}const h=l&&Ke.hitTestHandle(l,t);if(l&&h){if(r)return;this.beginDrag(h==="rotate"?"rotate":"resize",this.currentSourceId,l,t,h);return}}const a=n.hitTest(s,t.x,t.y);if(!a){const l=this.pickDecorAt(e);if(l!==void 0){this.selectDecor(l);return}r||this.beginCameraDrag(t);return}if(e.shiftKey?this.toggleUnitSelection(a.sourceId):this.selectedSourceIds.has(a.sourceId)||this.selectUnit(a.sourceId),r)return;const o=[...this.selectedSourceIds].map(l=>({sourceId:l,original:l===a.sourceId?a.shape:n.getInterpolatedShapeAt(s,l)??a.shape}));this.dragState={kind:"move",sources:o,startPointer:t},this.startDragListening()}selectedMembers(){const e=this.ufoElement.sighting.timeline,t=this.ufoElement.currentTime;return[...this.selectedSourceIds].map(n=>({sourceId:n,shape:e.getInterpolatedShapeAt(t,n)})).filter(n=>!!n.shape)}selectUnit(e){const t=this.ufoElement.sighting.timeline.groupMembers(e)??[e];t.length===this.selectedSourceIds.size&&t.every(n=>this.selectedSourceIds.has(n))&&e===this.currentSourceId||(this.selectedSourceIds=new Set(t),this.currentSourceId=e,this.refreshSourceList(),this.onSelectionOrTimeChanged())}toggleUnitSelection(e){const t=this.ufoElement.sighting.timeline,n=t.groupMembers(e)??[e];if(n.every(r=>this.selectedSourceIds.has(r))){if(n.length>=this.selectedSourceIds.size)return;for(const r of n)this.selectedSourceIds.delete(r);if(!this.selectedSourceIds.has(this.currentSourceId)){const r=t.sourceIds;this.currentSourceId=[...this.selectedSourceIds].sort((a,o)=>r.indexOf(a)-r.indexOf(o)).pop()}}else{for(const r of n)this.selectedSourceIds.add(r);this.currentSourceId=e}this.refreshSourceList(),this.onSelectionOrTimeChanged()}onContextMenu(e){if(e.preventDefault(),this.isRecording||this.ufoElement.playbackState==="playing")return;this.hideContextMenu(),this.hideDecorContextMenu();const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=this.selectedSourceIds.size===1?n.getInterpolatedShapeAt(s,this.currentSourceId):void 0,o=r?.kind==="polygon"&&Ke.hitTestVertex(r,t)!==void 0?{sourceId:this.currentSourceId}:n.hitTest(s,t.x,t.y);if(o){this.selectedSourceIds.has(o.sourceId)||this.selectUnit(o.sourceId),this.currentSourceId=o.sourceId,this.contextMenuPoint=t,this.showContextMenu(e.clientX,e.clientY);return}const l=this.pickDecorAt(e),c=l?this.ufoElement.sighting.decor.find(h=>h.id===l):void 0;c&&this.showDecorContextMenu(e.clientX,e.clientY,c)}pickDecorAt(e){const n=this.ufoElement.canvasElement.getBoundingClientRect();if(n.width===0||n.height===0)return;const s=(e.clientX-n.left)/n.width*2-1,r=-((e.clientY-n.top)/n.height*2-1);return this.sceneElement.pickDecorAt(s,r)}showContextMenu(e,t){this.contextMenu.style.left=`${e}px`,this.contextMenu.style.top=`${t}px`,this.contextMenu.hidden=!1;const n=this.ufoElement.sighting.timeline.sourceIds,s=n.filter(p=>this.selectedSourceIds.has(p)),r=n.length<=s.length,a=s.length>0&&n.slice(n.length-s.length).every(p=>this.selectedSourceIds.has(p)),o=s.length>0&&n.slice(0,s.length).every(p=>this.selectedSourceIds.has(p));this.contextBringToFrontButton.disabled=r||a,this.contextSendToBackButton.disabled=r||o,this.contextBringToFrontButton.title=r?this.messages.onlyOneShape:a?this.messages.alreadyAtFront:"",this.contextSendToBackButton.title=r?this.messages.onlyOneShape:o?this.messages.alreadyAtBack:"",this.contextGroupButton.disabled=this.selectedSourceIds.size<2,this.contextGroupButton.title=this.contextGroupButton.disabled?this.messages.needTwoShapesToGroup:"";const l=this.ufoElement.sighting.timeline.groupMembers(this.currentSourceId)!==void 0;this.contextUngroupButton.disabled=!l,this.contextUngroupButton.title=l?"":this.messages.notGrouped,this.contextDeleteButton.disabled=this.deleteShapeButton.disabled,this.contextDeleteButton.title=this.contextDeleteButton.disabled?this.messages.onlyOneShape:"";const c=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,this.currentSourceId),h=this.selectedSourceIds.size===1&&c?.kind==="polygon";this.contextAddVertexButton.disabled=!h,this.contextAddVertexButton.title=h?"":this.messages.notAPolygon;const u=h&&this.contextMenuPoint?Ke.hitTestVertex(c,this.contextMenuPoint):void 0,d=h&&c.points.length<=Qd;this.contextDeleteVertexButton.disabled=!h||u===void 0||d,this.contextDeleteVertexButton.title=h?d?this.messages.tooFewVertices:"":this.messages.notAPolygon,document.addEventListener("click",this.handleOutsideContextMenuClick)}hideContextMenu(){this.contextMenu.hidden=!0,this.contextMenuPoint=void 0,document.removeEventListener("click",this.handleOutsideContextMenuClick)}handleOutsideContextMenuClick=e=>{e.composedPath().includes(this.contextMenu)||e.composedPath().includes(this.decorContextMenu)||(this.hideContextMenu(),this.hideDecorContextMenu())};showDecorContextMenu(e,t,n){this.contextMenuDecorId=n.id,this.decorContextMenu.style.left=`${e}px`,this.decorContextMenu.style.top=`${t}px`,this.decorContextMenu.hidden=!1,this.contextViewTestimonyButton.disabled=!n.sightingUrl,this.contextViewTestimonyButton.title=n.sightingUrl?"":this.messages.noWitnessRecording,this.refreshContextMasksSubmenu(n),document.addEventListener("click",this.handleOutsideContextMenuClick)}hideDecorContextMenu(){this.decorContextMenu.hidden=!0,this.contextMenuDecorId=void 0,document.removeEventListener("click",this.handleOutsideContextMenuClick)}refreshContextMasksSubmenu(e){this.contextMasksSubmenu.innerHTML="";for(const t of this.ufoElement.sighting.timeline.sourceIds){const n=document.createElement("label"),s=document.createElement("input");s.type="checkbox",s.checked=e.occludesSourceIds?.includes(t)??!1,s.addEventListener("change",()=>this.updateDecorOccludesShape(e.id,t,s.checked)),n.appendChild(s),n.appendChild(document.createTextNode(this.shapeLabel(t))),this.contextMasksSubmenu.appendChild(n)}}updateDecorOccludesShape(e,t,n){const s=this.ufoElement.sighting;s.decor=s.decor.map(r=>{if(r.id!==e)return r;const a=n?[...new Set([...r.occludesSourceIds??[],t])]:(r.occludesSourceIds??[]).filter(o=>o!==t);return{...r,occludesSourceIds:a.length>0?a:void 0}}),this.ufoElement.refresh()}viewWitnessTestimony(){const e=this.ufoElement.sighting.decor.find(t=>t.id===this.contextMenuDecorId);this.hideDecorContextMenu(),e?.sightingUrl&&this.importFromUrl(e.sightingUrl)}addVertexAtContextMenu(){const e=this.contextMenuPoint;if(this.hideContextMenu(),!e)return;const t=this.ufoElement.currentTime,n=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(t,this.currentSourceId);if(n?.kind!=="polygon")return;const s=Ke.insertVertexNear(n,e);this.ufoElement.sighting.timeline.addKeyframe(t,[{sourceId:this.currentSourceId,shape:s}]),this.ufoElement.refresh()}deleteVertexAtContextMenu(){const e=this.contextMenuPoint;if(this.hideContextMenu(),!e)return;const t=this.ufoElement.currentTime,n=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(t,this.currentSourceId);if(n?.kind!=="polygon")return;const s=Ke.hitTestVertex(n,e);if(s===void 0)return;const r=Ke.deleteVertex(n,s);this.ufoElement.sighting.timeline.addKeyframe(t,[{sourceId:this.currentSourceId,shape:r}]),this.ufoElement.refresh()}bringSelectedToFront(){const e=this.ufoElement.sighting.timeline;for(const t of e.sourceIds.filter(n=>this.selectedSourceIds.has(n)))e.bringToFront(t);this.hideContextMenu(),this.ufoElement.refresh()}sendSelectedToBack(){const e=this.ufoElement.sighting.timeline;for(const t of e.sourceIds.filter(n=>this.selectedSourceIds.has(n)).reverse())e.sendToBack(t);this.hideContextMenu(),this.ufoElement.refresh()}groupSelected(){this.selectedSourceIds.size<2||(this.ufoElement.sighting.timeline.group([...this.selectedSourceIds]),this.hideContextMenu(),this.ufoElement.refresh())}ungroupSelected(){this.ufoElement.sighting.timeline.ungroup(this.currentSourceId),this.selectedSourceIds=new Set([this.currentSourceId]),this.hideContextMenu(),this.ufoElement.refresh()}beginDrag(e,t,n,s,r){this.dragState={kind:e,sourceId:t,original:n,handle:r,startPointer:s},this.startDragListening()}beginCameraDrag(e){const t=this.isWitnessInsideDecor();this.cameraDragState={startPointer:e,startHeadingDeg:t?this.indoorLookYawDeg:this.numberOrUndefined(this.headingInput.value)??0,startPitchDeg:t?this.indoorLookPitchDeg:this.numberOrUndefined(this.pitchInput.value)??0,insideDecor:t},t||this.sceneElement.setCompassForced(!0),this.setCanvasCursor("panning"),this.startDragListening()}isWitnessInsideDecor(){return this.ufoElement.sighting.decor.some(e=>e.witnessSide!==void 0&&Zs(e.kind))}syncIndoorLookReset(){const e=this.ufoElement.sighting.decor.find(n=>n.witnessSide!==void 0&&Zs(n.kind)),t=e?`${e.id}:${e.witnessSide}`:void 0;t!==this.lastInhabitedKey&&(this.lastInhabitedKey=t,this.indoorLookYawDeg=0,this.indoorLookPitchDeg=0,this.sceneElement.setIndoorLook(0,0))}startDragListening(){document.addEventListener("pointermove",this.handleDragPointerMove),document.addEventListener("pointerup",this.handleDragPointerUp)}onDragPointerMove(e){if(this.cameraDragState){this.onCameraDragPointerMove(e);return}if(!this.dragState)return;const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.currentTime;if(this.dragState.kind==="move"){const{sources:s,startPointer:r}=this.dragState,a=t.x-r.x,o=t.y-r.y,l=s.map(({sourceId:c,original:h})=>({sourceId:c,shape:{...h,bounds:{...h.bounds,x:h.bounds.x+a,y:h.bounds.y+o}}}));this.ufoElement.sighting.timeline.addKeyframe(n,l)}else if(this.dragState.kind==="group-resize"){const{group:s,handle:r}=this.dragState;this.ufoElement.sighting.timeline.addKeyframe(n,s.resize(r,t))}else if(this.dragState.kind==="group-rotate"){const{group:s,startPointer:r}=this.dragState;this.ufoElement.sighting.timeline.addKeyframe(n,s.rotate(t,r))}else if(this.dragState.kind==="vertex"){const{sourceId:s,original:r,vertexIndex:a}=this.dragState,o=Ke.moveVertex(r,a,t);this.ufoElement.sighting.timeline.addKeyframe(n,[{sourceId:s,shape:o}])}else{const{kind:s,sourceId:r,original:a,handle:o}=this.dragState,l=s==="resize"?Ke.resizeShape(a,o,t):Ke.rotateShape(a,t);this.ufoElement.sighting.timeline.addKeyframe(n,[{sourceId:r,shape:l}])}this.ufoElement.refresh()}onCameraDragPointerMove(e){if(!this.cameraDragState)return;const t=this.canvasPointFromEvent(e);if(!t)return;const{startPointer:n,startHeadingDeg:s,startPitchDeg:r,insideDecor:a}=this.cameraDragState,o=s+(t.x-n.x)*Wd,l=Math.max(-90,Math.min(90,r-(t.y-n.y)*Wd));if(a){this.indoorLookYawDeg=o,this.indoorLookPitchDeg=l,this.sceneElement.setIndoorLook(o,l),this.ufoElement.refresh();return}this.headingInput.value=String(o),this.pitchInput.value=String(l),this.updateObserver()}endDrag(){!this.dragState&&!this.cameraDragState||(this.cameraDragState&&!this.cameraDragState.insideDecor&&this.sceneElement.setCompassForced(!1),this.dragState=void 0,this.cameraDragState=void 0,this.setCanvasCursor(this.hoverCursor),document.removeEventListener("pointermove",this.handleDragPointerMove),document.removeEventListener("pointerup",this.handleDragPointerUp))}nudgeBounds(e,t){const n={...e};if(t.shiftKey){switch(t.key){case"ArrowLeft":n.width=Math.max(ai,n.width-ni);break;case"ArrowRight":n.width+=ni;break;case"ArrowUp":n.height=Math.max(ai,n.height-ni);break;case"ArrowDown":n.height+=ni;break}n.x-=(n.width-e.width)/2,n.y-=(n.height-e.height)/2}else switch(t.key){case"ArrowLeft":n.x-=ni;break;case"ArrowRight":n.x+=ni;break;case"ArrowUp":n.y-=ni;break;case"ArrowDown":n.y+=ni;break}return n}moveOrResizeSelectedShapes(e){if(this.isRecording||this.ufoElement.playbackState==="playing")return;const t=this.ufoElement.sighting.timeline,n=this.ufoElement.currentTime,s=this.selectedMembers();if(s.length!==0){if(e.preventDefault(),s.length===1){const{sourceId:r,shape:a}=s[0];t.addKeyframe(n,[{sourceId:r,shape:{...a,bounds:this.nudgeBounds(a.bounds,e)}}])}else{const r=new za(s),a=this.nudgeBounds(r.bounds(),e);t.addKeyframe(n,r.scaleTo(a))}this.ufoElement.refresh()}}updateHoverCursor(e){this.isRecording||this.dragState||this.cameraDragState||(this.hoverCursor=this.hoverCursorAt(e),this.setCanvasCursor(this.hoverCursor))}hoverCursorAt(e){const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=this.ufoElement.playbackState!=="playing";if(r)if(this.selectedSourceIds.size>1){const a=Ke.hitTestHandle({bounds:new za(this.selectedMembers()).bounds(),angle:0},t);if(a)return this.cursorForHandle(a,0)}else{const a=n.getInterpolatedShapeAt(s,this.currentSourceId);if(a?.kind==="polygon"&&Ke.hitTestVertex(a,t)!==void 0)return"vertex";const o=a&&Ke.hitTestHandle(a,t);if(a&&o)return this.cursorForHandle(o,a.angle)}return n.hitTest(s,t.x,t.y)?r?"move":"select":this.pickDecorAt(e)!==void 0?"select":r?"pan":void 0}cursorForHandle(e,t){return e==="rotate"?"rotate":`resize-${Ke.resizeAxisFor(e,t)}`}setCanvasCursor(e){const t=this.ufoElement.canvasElement;e?t.dataset.cursor=e:delete t.dataset.cursor}onPointerMove(e){if(!this.isRecording){this.updateHoverCursor(e);return}const t=this.ufoElement.canvasElement,n=t.getBoundingClientRect(),s=e.clientX-n.left,r=e.clientY-n.top;this.recorder?.onPointerMove(s,r),this.ufoElement.renderer.clear(t.width,t.height),this.ufoElement.renderer.paintShape(Kd(this.buildPrototype({x:0,y:0,width:ls.width,height:ls.height}),s,r))}canvasPointFromEvent(e){const t=this.ufoElement.canvasElement,n=t.getBoundingClientRect();if(!(n.width===0||n.height===0))return{x:(e.clientX-n.left)/n.width*t.width,y:(e.clientY-n.top)/n.height*t.height}}}const $d="rr0-ufo-recorder";function yy(){Ra(),customElements.get($d)||customElements.define($d,Sy)}yy();
5592
+ `;let ta;function JS(){if(ta)return ta;const i=128,e=document.createElement("canvas");e.width=i,e.height=i;const t=e.getContext("2d"),n=t.createRadialGradient(i/2,i/2,0,i/2,i/2,i/2);return n.addColorStop(0,"rgba(255,255,255,1)"),n.addColorStop(.4,"rgba(255,255,255,0.35)"),n.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=n,t.fillRect(0,0,i,i),ta=new Wn(e),ta}function QS(i){const t=document.createElement("canvas");t.width=128,t.height=128;const n=t.getContext("2d"),s=128/2-2,r=128/2,a=128/2,o="#2e2b26",l="#f4f1e2";n.fillStyle=o,n.beginPath(),n.arc(r,a,s,0,Math.PI*2),n.fill();const c=_0(i.illuminatedFraction,0,1),h=i.phaseFraction<.5,u=s*Math.abs(1-2*c),d=c<.5;return n.save(),n.beginPath(),n.arc(r,a,s,0,Math.PI*2),n.clip(),n.fillStyle=l,n.beginPath(),h?(n.arc(r,a,s,-Math.PI/2,Math.PI/2,!1),n.ellipse(r,a,u,s,0,Math.PI/2,-Math.PI/2,d)):(n.arc(r,a,s,Math.PI/2,-Math.PI/2,!1),n.ellipse(r,a,u,s,0,-Math.PI/2,Math.PI/2,d)),n.fill(),n.restore(),new Wn(t)}function jS(i){const e=document.createElement("canvas");e.width=128,e.height=128;const t=e.getContext("2d");return t.fillStyle="rgba(0, 0, 0, 0.55)",t.beginPath(),t.arc(64,64,60,0,Math.PI*2),t.fill(),t.fillStyle="#ffffff",t.font="bold 52px sans-serif",t.textAlign="center",t.textBaseline="middle",t.fillText(i,64,68),new Wn(e)}function Oo(i){let e=i;return function(){e|=0,e=e+1831565813|0;let n=Math.imul(e^e>>>15,1|e);return n=n+Math.imul(n^n>>>7,61|n)^n,((n^n>>>14)>>>0)/4294967296}}const Vd=new Map;function ey(i){const e=i.byteLength/(4*Float32Array.BYTES_PER_ELEMENT),t=e,n=t*Float32Array.BYTES_PER_ELEMENT;return{count:e,ra:new Float32Array(i,0*n,t),dec:new Float32Array(i,1*n,t),mag:new Float32Array(i,2*n,t),ci:new Float32Array(i,3*n,t)}}function ty(i){let e=Vd.get(i);return e||(e=fetch(i).then(t=>t.arrayBuffer()).then(ey),Vd.set(i,e)),e}const ny=new URL(""+new URL("rain-BvWZrB9h.ogg",import.meta.url).href,import.meta.url).href,iy=new URL(""+new URL("wind-BjklexY7.ogg",import.meta.url).href,import.meta.url).href,sy=new URL(""+new URL("thunder-DA48qs_v.wav",import.meta.url).href,import.meta.url).href,ry=20,ay=1;class oy{context;buffers=new Map;ambientSource;ambientGain;ambientKey="none";ambientToken=0;ambientVolume=0;windSource;windGain;windActive=!1;windToken=0;windVolume=0;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(e){console.warn("WeatherAudio: Web Audio unavailable, weather sounds disabled:",e);return}}this.context.state==="suspended"&&this.context.resume()}setAmbient(e,t,n){if(!this.context)return;const s=e==="rain"||e==="hail"?e:"none";if(this.ambientVolume=s==="hail"?Math.min(1,t*1.3+.2):s==="rain"?t:0,s!==this.ambientKey){this.ambientKey=s;const a=++this.ambientToken;this.stopSource(this.ambientSource,this.ambientGain),this.ambientSource=void 0,this.ambientGain=void 0,s!=="none"&&this.startLoop(ny,this.ambientVolume).then(o=>this.applyIfCurrent(o,a,()=>this.ambientToken,l=>{this.ambientSource=l?.source,this.ambientGain=l?.gain,l&&(l.gain.gain.value=this.ambientVolume)}))}else this.ambientGain&&(this.ambientGain.gain.value=this.ambientVolume);this.windVolume=Math.min(n/ry,1);const r=n>ay;if(r!==this.windActive){this.windActive=r;const a=++this.windToken;this.stopSource(this.windSource,this.windGain),this.windSource=void 0,this.windGain=void 0,r&&this.startLoop(iy,this.windVolume).then(o=>this.applyIfCurrent(o,a,()=>this.windToken,l=>{this.windSource=l?.source,this.windGain=l?.gain,l&&(l.gain.gain.value=this.windVolume)}))}else this.windGain&&(this.windGain.gain.value=this.windVolume)}playThunder(){this.context&&this.playOneShot(sy,.9)}dispose(){this.ambientToken++,this.windToken++,this.stopSource(this.ambientSource,this.ambientGain),this.stopSource(this.windSource,this.windGain),this.ambientSource=void 0,this.ambientGain=void 0,this.windSource=void 0,this.windGain=void 0,this.context?.close(),this.context=void 0,this.buffers.clear()}applyIfCurrent(e,t,n,s){if(t!==n()){e?.source.stop();return}s(e)}async startLoop(e,t){const n=this.context;if(n)try{const s=await this.getBuffer(e),r=n.createBufferSource();r.buffer=s,r.loop=!0;const a=n.createGain();return a.gain.value=t,r.connect(a).connect(n.destination),r.start(),{source:r,gain:a}}catch(s){console.warn("Weather ambient sound failed to load, staying silent:",s);return}}async playOneShot(e,t){const n=this.context;if(n)try{const s=await this.getBuffer(e),r=n.createBufferSource();r.buffer=s;const a=n.createGain();a.gain.value=t,r.connect(a).connect(n.destination),r.start()}catch(s){console.warn("Thunder sound failed to load:",s)}}async getBuffer(e){const t=this.context;if(!t)throw new Error("WeatherAudio: AudioContext not ready — resume() must be called first");let n=this.buffers.get(e);return n||(n=fetch(e).then(s=>{if(!s.ok)throw new Error(`Audio fetch failed (${s.status}): ${e}`);return s.arrayBuffer()}).then(s=>t.decodeAudioData(s)),this.buffers.set(e,n)),n}stopSource(e,t){try{e?.stop()}catch{}e?.disconnect(),t?.disconnect()}}Ra();const ly={sun:{en:"Sun",fr:"Soleil"},moon:{en:"Moon",fr:"Lune"},Venus:{en:"Venus",fr:"Vénus"},Mars:{en:"Mars",fr:"Mars"},Jupiter:{en:"Jupiter",fr:"Jupiter"},Saturn:{en:"Saturn",fr:"Saturne"}},cy=["en","fr"],hy={building:{en:"Building",fr:"Bâtiment"},tree:{en:"Tree",fr:"Arbre"},streetlight:{en:"Streetlight",fr:"Lampadaire"},vehicle:{en:"Vehicle",fr:"Véhicule"},witness:{en:"Witness",fr:"Témoin"}},dy=new URL(""+new URL("stars-mag7.5-DDr9QasD.bin",import.meta.url).href,import.meta.url).href,uy={sun:{altitudeDeg:-3,azimuthDeg:180,magnitude:-26.7},moon:{altitudeDeg:-90,azimuthDeg:0,phase:{phaseFraction:0,illuminatedFraction:0},magnitude:-12.7},planets:[]},Bo={lat:0,lng:0,elevationM:0,headingDeg:void 0,pitchDeg:0,fovDeg:60};class fy extends HTMLElement{static get observedAttributes(){return["src","star-catalog-src","show-compass","lens-flare-intensity","lens-flare-brightness"]}shadow;stageElement;frameElement;sceneCanvas;ufoElement;sceneRenderer;hoverTooltip;resizeObserver;lastTimeMs=0;starCatalog;weatherAudio=new oy;thunderTimeoutId;handleFullscreenChange=()=>this.resizeToStage();handlePointerMove=e=>{this.sceneRenderer.setCompassHovered(!0);const t=this.ufoElement.canvasElement,n=t.getBoundingClientRect();if(n.width===0||n.height===0)return;const s=(e.clientX-n.left)/n.width*t.width,r=(e.clientY-n.top)/n.height*t.height;if(this.ufoElement.hasVisibleShapeAt(s,r)){this.hoverTooltip.hidden=!0;return}const a=(e.clientX-n.left)/n.width*2-1,o=-((e.clientY-n.top)/n.height*2-1),l=Ia(navigator.languages,cy),c=this.sceneRenderer.pickBodyAt(a,o);if(c){this.showHoverTooltip(e,ly[c]?.[l]??c);return}const h=this.sceneRenderer.pickDecorAt(a,o),u=h?this.ufoElement.sighting.decor.find(d=>d.id===h):void 0;if(u){this.showHoverTooltip(e,u.title||hy[u.kind][l]);return}this.hoverTooltip.hidden=!0};showHoverTooltip(e,t){this.hoverTooltip.textContent=t,this.hoverTooltip.hidden=!1;const n=this.stageElement.getBoundingClientRect();this.hoverTooltip.style.left=`${e.clientX-n.left+12}px`,this.hoverTooltip.style.top=`${e.clientY-n.top+12}px`}handlePointerLeave=()=>{this.hoverTooltip.hidden=!0,this.sceneRenderer.setCompassHovered(!1)};handleLightningFlash=()=>{clearTimeout(this.thunderTimeoutId);const e=(.5+Math.random()*3.5)*1e3;this.thunderTimeoutId=window.setTimeout(()=>this.weatherAudio.playThunder(),e)};handleFirstInteraction=()=>{this.weatherAudio.resume(),this.setWeather(Ho(this.ufoElement.sighting,this.lastTimeMs))};handleTimeUpdate=e=>{this.lastTimeMs=e.detail.time,this.updateAstronomy(this.lastTimeMs),this.updateUfoOcclusion(this.lastTimeMs)};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${vf}</style>${gf}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.frameElement=this.shadow.getElementById("frame"),this.sceneCanvas=this.shadow.getElementById("scene-canvas"),this.sceneRenderer=new HS(this.sceneCanvas,void 0,this.handleLightningFlash),this.hoverTooltip=this.shadow.getElementById("hover-tooltip"),this.ufoElement=document.createElement(Wo),this.ufoElement.classList.add("ufo-overlay"),this.ufoElement.style.setProperty("--ufo-canvas-background","transparent"),this.ufoElement.style.setProperty("--ufo-canvas-border","none"),this.ufoElement.fullscreenTarget=this.stageElement,this.shadow.getElementById("ufo-slot").replaceWith(this.ufoElement),this.ufoElement.addEventListener("timeupdate",this.handleTimeUpdate),this.ufoElement.canvasElement.addEventListener("pointermove",this.handlePointerMove),this.ufoElement.canvasElement.addEventListener("pointerleave",this.handlePointerLeave),this.ufoElement.canvasElement.addEventListener("pointerdown",this.handleFirstInteraction,{once:!0})}connectedCallback(){this.resizeToStage(),this.updateAstronomy(this.lastTimeMs),this.loadStars(),this.resizeObserver=new ResizeObserver(()=>this.resizeToStage()),this.resizeObserver.observe(this.frameElement),document.addEventListener("fullscreenchange",this.handleFullscreenChange);const e=this.getAttribute("src");e&&this.loadFromSrc(e)}disconnectedCallback(){this.resizeObserver?.disconnect(),document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.sceneRenderer.stopTwinkle(),clearTimeout(this.thunderTimeoutId),this.weatherAudio.dispose()}attributeChangedCallback(e,t,n){if(e==="src"&&n&&n!==t&&this.isConnected&&this.loadFromSrc(n),e==="star-catalog-src"&&n!==t&&this.isConnected&&this.loadStars(),e==="show-compass"&&n!==t&&this.sceneRenderer.setShowCompass(this.hasAttribute("show-compass")),e==="lens-flare-intensity"&&n!==t){const s=n===null?NaN:parseFloat(n);this.sceneRenderer.setLensFlareArtifactIntensity(Number.isFinite(s)?s:0)}if(e==="lens-flare-brightness"&&n!==t){const s=n===null?NaN:parseFloat(n);this.sceneRenderer.setDazzleIntensity(Number.isFinite(s)?s:1)}}setCompassForced(e){this.sceneRenderer.setCompassForced(e)}setIndoorLook(e,t){this.sceneRenderer.setIndoorLook(e,t)}setWeather(e){this.sceneRenderer.setWeather(e),this.weatherAudio.setAmbient(e.precipitationType,e.precipitationIntensity,e.windSpeed)}pickDecorAt(e,t){return this.sceneRenderer.pickDecorAt(e,t)}resumeWeatherAudio(){this.weatherAudio.resume()}async loadFromSrc(e){const t=await fetch(e);this.sightingData=await t.json()}get sightingData(){return this.ufoElement.sightingData}set sightingData(e){this.ufoElement.sightingData=e,this.lastTimeMs=0,this.updateAstronomy(0)}get currentTerrainAttribution(){return this.sceneRenderer.currentTerrainAttribution}resizeToStage(){const e=this.frameElement.getBoundingClientRect(),t=Math.max(1,Math.round(e.width)),n=Math.max(1,Math.round(e.height));this.sceneCanvas.width=t,this.sceneCanvas.height=n,this.sceneRenderer.resize(t,n)}async loadStars(){const e=this.getAttribute("star-catalog-src")??dy;this.starCatalog=await ty(e),this.updateAstronomy(this.lastTimeMs)}updateAstronomy(e){const t=this.ufoElement.sighting;this.setWeather(Ho(t,e)),this.sceneRenderer.setDecor(t.decor);const n=Go(t,e);this.sceneRenderer.setObserverPose(n??Bo),this.sceneRenderer.updateDecorAnchoring(Go(t,0),n),this.sceneRenderer.updateDecorLitState(e),this.sceneRenderer.setTerrainOrigin(n?.lat,n?.lng);const s=n?.lat??Bo.lat,r=n?.lng??Bo.lng,a=n2(t.event.time??{},r,t.event.utcOffsetHours);if(!a){this.sceneRenderer.setAstronomy(uy);return}const o=new Date(a.getTime()+e),l={lat:s,lng:r,elevationM:n?.elevationM??0},c={...wo("Sun",o,l),magnitude:To("Sun",o)},h={...wo("Moon",o,l),phase:Q_(o),magnitude:To("Moon",o)},u=Z_.map(d=>({body:d,position:wo(d,o,l),magnitude:To(d,o)}));this.sceneRenderer.setAstronomy({sun:c,moon:h,planets:u,stars:this.starCatalog?{catalog:this.starCatalog,date:o,observer:l}:void 0})}updateUfoOcclusion(e){const t=this.ufoElement.sighting.timeline,n=this.ufoElement.canvasElement,s=new Set;for(const r of t.sourceIds){const a=t.getInterpolatedShapeAt(e,r);if(!a)continue;const o=(a.bounds.x+a.bounds.width/2)/n.width*2-1,l=-((a.bounds.y+a.bounds.height/2)/n.height*2-1),c={declared:a.behindCloud,distanceM:a.physical?.distanceM};this.sceneRenderer.isScreenPointOccluded(o,l,r,c)&&s.add(r)}this.ufoElement.setOccludedSourceIds(s)}}const hc="rr0-scene";function py(){Ra(),customElements.get(hc)||customElements.define(hc,fy)}class my{constructor(e,t){this.timeline=e,this.clock=t}currentPointer=null;shapePrototype=null;sourceId="";start(e,t){this.sourceId=e,this.shapePrototype=t,this.currentPointer=null,this.clock.start(n=>this.recordSample(n))}onPointerMove(e,t){this.currentPointer={x:e,y:t}}stop(){this.clock.stop()}recordSample(e){if(!this.currentPointer||!this.shapePrototype)return;const t=Kd(this.shapePrototype,this.currentPointer.x,this.currentPointer.y);this.timeline.addKeyframe(e,[{sourceId:this.sourceId,shape:t}])}}class gy{constructor(e){this.intervalMs=e}rafId=null;startTime=0;lastTick=0;start(e){this.startTime=performance.now(),this.lastTick=0;const t=()=>{const n=performance.now()-this.startTime;n-this.lastTick>=this.intervalMs&&(this.lastTick=n,e(n)),this.rafId=requestAnimationFrame(t)};this.rafId=requestAnimationFrame(t)}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}}const S0={oval:"Oval",polygon:"Polygon",addVertex:"Add vertex",deleteVertex:"Delete vertex",notAPolygon:"Select a single polygon shape",tooFewVertices:"A shape needs at least 3 points",color:"Color",transparency:"Transparency",halo:"Halo",shape:"Shape",shapeTitle:"Name",objectSize:"Real size",objectDistance:"Distance",apparentSize:"i.e. {deg}° — {moons}× the Moon",utcOffset:"Time zone",utcOffsetPlaceholder:"from longitude",objectSizePlaceholder:"reported width",objectDistancePlaceholder:"reported distance",addShape:"Add shape",deleteShape:"Delete shape",play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",group:"Group",ungroup:"Ungroup",bringToFront:"Bring to front",sendToBack:"Send to back",contextMenuDelete:"Delete",confirmDeleteShape:"Delete {name}? This can't be undone.",confirmDeleteShapes:"Delete {count} shapes? This can't be undone.",onlyOneShape:"There is only one shape",alreadyAtFront:"This shape is already at the front",alreadyAtBack:"This shape is already at the back",needTwoShapesToGroup:"Select at least two shapes to group them",notGrouped:"This shape isn't part of a group",multipleShapesSelected:"Multiple shapes selected — select just one to edit this",cloudBase:"Cloud base",elevation:"Altitude",samplingRate:"Sampling rate",duration:"Duration",durationPlaceholder:"observation length",durationImprecise:"These dates don't precisely enough determine a duration — enter it manually.",export:"Export JSON",importFile:"Load JSON file",importUrl:"Or load from URL",importUrlPlaceholder:"https://…/sighting.json",importButton:"Load",importError:"Couldn't load that recording — check the file or URL and try again.",record:"Record",stop:"Stop",latitude:"Latitude",longitude:"Longitude",heading:"Heading",headingPlaceholder:"unknown",pitch:"Tilt",observationTime:"Observation start",observationEndTime:"Observation end",edtfInvalid:"Not a valid EDTF date/time (e.g. 1965-07-01T05:00, 2025-06?, 2025~).",edtfPlaceholder:"YYYY-MM-DDThh:mm[?~%] or hh:mm",observationTimeHint:"EDTF — e.g. 1965-07-01T05:00, 2025-06? (uncertain), 2025~ (approximate), or just 05:00 if the date isn't known",observationEndTimeHint:"EDTF — e.g. 1965-07-01T05:10, 2025-06? (uncertain), 2025~ (approximate), or just 05:10 if the date isn't known",presetsGroupLabel:"UFO shape",witnessId:"Witness ID",witnessDirName:"Witness dir name",witnessTitle:"Witness title",witnessLastName:"Witness last name",witnessFirstNames:"Witness first names",caseId:"Case ID",description:"Description",tags:"Tags",tagsPlaceholder:"comma-separated",weather:"Weather",shapeGroup:"Shape",temporalGroup:"Temporal",locationGroup:"Location",observationGroup:"Observation",witnessGroup:"Witness",circumstancesGroup:"Circumstances",cloudCover:"Cloud cover",cloudDarkness:"Cloud darkness",precipitationType:"Precipitation",precipitationNone:"None",precipitationRain:"Rain",precipitationSnow:"Snow",precipitationHail:"Hail",precipitationIntensity:"Intensity",windDirection:"Wind direction",windSpeed:"Wind speed",storm:"Storm",lensFlareBrightness:"Light intensity",cameraDevice:"Camera/video device",decor:"Decor",decorBuilding:"Building",decorTree:"Tree",decorStreetlight:"Streetlight",decorVehicle:"Vehicle",decorWitness:"Other witness",addDecor:"Add",deleteDecor:"Delete decor",decorEast:"Distance east",decorNorth:"Distance north",decorHeading:"Heading",decorLit:"Lit",decorTitle:"Name",decorSightingUrl:"Witness's own recording URL",viewTestimony:"View testimony",noWitnessRecording:"No recording URL set for this witness",masks:"Masks",addWitness:"Add witness",decorFloors:"Floors",decorOccupiedFloor:"Occupied floor",decorWitnessSide:"Witness location",decorWitnessSideNone:"Not present",decorWindows:"Windows",decorSideFront:"Front",decorSideBehind:"Behind",decorSideLeft:"Left",decorSideRight:"Right",decorSideFrontLeft:"Front-left",decorSideFrontRight:"Front-right",decorSideBehindLeft:"Behind-left",decorSideBehindRight:"Behind-right"},vy=Object.freeze(Object.defineProperty({__proto__:null,ufoRecorderMessages_en:S0},Symbol.toStringTag,{value:"Module"}));Ra();py();const ls={width:48,height:28},Wd=.2,Xd=60,Yd=new Set(["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"]),ni=4,na="ufo-1",qd=["oval","polygon"],xy={presetId:"oval",color:"#39ff14",transparency:0,haloScale:1.5};function _y(i){return i.kind==="oval"?"oval":"polygon"}class Sy extends HTMLElement{shadow;sceneElement;ufoElement;recordButton;samplingRateInput;presetButtons;presetsGroup;colorInput;transparencyInput;haloScaleInput;sourceSelect;shapeTitleInput;utcOffsetInput;objectSizeInput;objectDistanceInput;apparentSizeOutput;addShapeButton;deleteShapeButton;contextMenu;contextGroupButton;contextUngroupButton;contextBringToFrontButton;contextSendToBackButton;contextDeleteButton;contextAddVertexButton;contextDeleteVertexButton;decorContextMenu;contextViewTestimonyButton;contextMasksSubmenu;labelContextMasks;playPauseButton;seekInput;timeStartLabel;timeEndLabel;loopButton;durationInput;exportButton;importFileInput;importUrlInput;importUrlButton;latInput;lngInput;headingInput;pitchInput;elevationInput;obsTimeInput;obsEndTimeInput;witnessIdInput;witnessDirNameInput;witnessTitleInput;witnessLastNameInput;witnessFirstNamesInput;caseIdInput;descriptionInput;tagsInput;cloudCoverInput;cloudDarknessInput;cloudBaseInput;precipitationTypeSelect;precipitationIntensityInput;windDirectionInput;windSpeedInput;stormInput;lensFlareBrightnessInput;cameraDeviceInput;labelColor;labelTransparency;labelHalo;labelShape;labelShapeTitle;labelUtcOffset;labelObjectSize;labelObjectDistance;labelSamplingRate;labelDuration;labelLatitude;labelLongitude;labelHeading;labelPitch;labelElevation;labelObservationTime;labelObservationEndTime;labelWitnessId;labelWitnessDirName;labelWitnessTitle;labelWitnessLastName;labelWitnessFirstNames;labelCaseId;labelDescription;labelTags;labelWeather;labelImportFile;labelImportUrl;labelShapeGroup;labelTemporalGroup;labelLocationGroup;labelObservationGroup;labelWitnessGroup;labelCircumstancesGroup;labelCloudCover;labelCloudDarkness;labelCloudBase;labelPrecipitationType;labelPrecipitationIntensity;labelWindDirection;labelWindSpeed;labelStorm;labelLensFlareBrightness;labelCameraDevice;optionPrecipitationNone;optionPrecipitationRain;optionPrecipitationSnow;optionPrecipitationHail;decorKindSelect;addDecorWitnessButton;addDecorBuildingButton;deleteDecorButton;decorSelect;decorTitleInput;decorEastInput;decorNorthInput;decorHeadingInput;decorLitInput;decorSightingUrlInput;decorFloorsInput;decorOccupiedFloorInput;decorWitnessSideSelect;decorWindowInputs;labelDecorSide;optionWitnessSide;labelDecor;labelDecorFieldset;labelDecorTitle;labelDecorEast;labelDecorNorth;labelDecorHeading;labelDecorLit;labelDecorSightingUrl;labelDecorFloors;labelDecorOccupiedFloor;labelDecorWitnessSide;labelDecorWindows;optionWitnessSideNone;optionDecorBuilding;optionDecorTree;optionDecorStreetlight;optionDecorVehicle;optionDecorWitness;recorder;isRecording=!1;messages=S0;currentAppearance={...xy};currentSourceId=na;selectedSourceIds=new Set([na]);currentDecorId;contextMenuDecorId;contextMenuPoint;dragState;cameraDragState;hoverCursor;indoorLookYawDeg=0;indoorLookPitchDeg=0;lastInhabitedKey;handleKeyDown=e=>{if(e.key==="Escape"&&(this.isRecording&&this.toggleRecording(),this.contextMenu.hidden||this.hideContextMenu(),this.decorContextMenu.hidden||this.hideDecorContextMenu()),Yd.has(e.key)||e.key==="Delete"||e.key==="Backspace"){const t=e.composedPath()[0];if(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement)return;Yd.has(e.key)?this.moveOrResizeSelectedShapes(e):this.deleteShape()}};handleDragPointerMove=e=>this.onDragPointerMove(e);handleDragPointerUp=()=>this.endDrag();constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${R0}</style>${I0}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.sceneElement=document.createElement(hc),this.sceneElement.setAttribute("show-compass",""),this.ufoElement=this.sceneElement.ufoElement,this.ufoElement.enableClickToPlay=!1,this.ufoElement.showToolbar=!1,this.shadow.getElementById("ufo-slot").replaceWith(this.sceneElement),this.recordButton=this.shadow.getElementById("record"),this.samplingRateInput=this.shadow.getElementById("samplingRate"),this.presetsGroup=this.shadow.getElementById("presets-group"),this.presetButtons={oval:this.shadow.getElementById("preset-oval"),polygon:this.shadow.getElementById("preset-polygon")},this.colorInput=this.shadow.getElementById("color"),this.transparencyInput=this.shadow.getElementById("transparency"),this.haloScaleInput=this.shadow.getElementById("haloScale"),this.sourceSelect=this.shadow.getElementById("source"),this.shapeTitleInput=this.shadow.getElementById("shapeTitle"),this.utcOffsetInput=this.shadow.getElementById("utcOffsetHours"),this.objectSizeInput=this.shadow.getElementById("objectSize"),this.objectDistanceInput=this.shadow.getElementById("objectDistance"),this.apparentSizeOutput=this.shadow.getElementById("apparent-size"),this.addShapeButton=this.shadow.getElementById("add-shape"),this.deleteShapeButton=this.shadow.getElementById("delete-shape"),this.contextMenu=this.shadow.getElementById("context-menu"),this.contextGroupButton=this.shadow.getElementById("context-group"),this.contextUngroupButton=this.shadow.getElementById("context-ungroup"),this.contextBringToFrontButton=this.shadow.getElementById("context-bring-to-front"),this.contextSendToBackButton=this.shadow.getElementById("context-send-to-back"),this.contextDeleteButton=this.shadow.getElementById("context-delete"),this.contextAddVertexButton=this.shadow.getElementById("context-add-vertex"),this.contextDeleteVertexButton=this.shadow.getElementById("context-delete-vertex"),this.decorContextMenu=this.shadow.getElementById("decor-context-menu"),this.contextViewTestimonyButton=this.shadow.getElementById("context-view-testimony"),this.contextMasksSubmenu=this.shadow.getElementById("context-masks-submenu"),this.labelContextMasks=this.shadow.getElementById("label-context-masks"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.seekInput=this.shadow.getElementById("seek"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end"),this.loopButton=this.shadow.getElementById("loop"),this.durationInput=this.shadow.getElementById("durationSeconds"),this.exportButton=this.shadow.getElementById("export"),this.importFileInput=this.shadow.getElementById("import-file"),this.importUrlInput=this.shadow.getElementById("import-url"),this.importUrlButton=this.shadow.getElementById("import-url-button"),this.latInput=this.shadow.getElementById("lat"),this.lngInput=this.shadow.getElementById("lng"),this.headingInput=this.shadow.getElementById("heading"),this.pitchInput=this.shadow.getElementById("pitch"),this.elevationInput=this.shadow.getElementById("elevation"),this.obsTimeInput=this.shadow.getElementById("obs-time"),this.obsEndTimeInput=this.shadow.getElementById("obs-end-time"),this.witnessIdInput=this.shadow.getElementById("witnessId"),this.witnessDirNameInput=this.shadow.getElementById("witnessDirName"),this.witnessTitleInput=this.shadow.getElementById("witnessTitle"),this.witnessLastNameInput=this.shadow.getElementById("witnessLastName"),this.witnessFirstNamesInput=this.shadow.getElementById("witnessFirstNames"),this.caseIdInput=this.shadow.getElementById("caseId"),this.descriptionInput=this.shadow.getElementById("description"),this.tagsInput=this.shadow.getElementById("tags"),this.cloudCoverInput=this.shadow.getElementById("cloudCover"),this.cloudDarknessInput=this.shadow.getElementById("cloudDarkness"),this.cloudBaseInput=this.shadow.getElementById("cloudBase"),this.precipitationTypeSelect=this.shadow.getElementById("precipitationType"),this.precipitationIntensityInput=this.shadow.getElementById("precipitationIntensity"),this.windDirectionInput=this.shadow.getElementById("windDirection"),this.windSpeedInput=this.shadow.getElementById("windSpeed"),this.stormInput=this.shadow.getElementById("storm"),this.lensFlareBrightnessInput=this.shadow.getElementById("lensFlareBrightness"),this.cameraDeviceInput=this.shadow.getElementById("cameraDevice"),this.labelColor=this.shadow.getElementById("label-color"),this.labelTransparency=this.shadow.getElementById("label-transparency"),this.labelHalo=this.shadow.getElementById("label-halo"),this.labelShape=this.shadow.getElementById("label-shape"),this.labelShapeTitle=this.shadow.getElementById("label-shape-title"),this.labelUtcOffset=this.shadow.getElementById("label-utc-offset"),this.labelObjectSize=this.shadow.getElementById("label-object-size"),this.labelObjectDistance=this.shadow.getElementById("label-object-distance"),this.labelSamplingRate=this.shadow.getElementById("label-sampling-rate"),this.labelDuration=this.shadow.getElementById("label-duration"),this.labelLatitude=this.shadow.getElementById("label-lat"),this.labelLongitude=this.shadow.getElementById("label-lng"),this.labelHeading=this.shadow.getElementById("label-heading"),this.labelPitch=this.shadow.getElementById("label-pitch"),this.labelElevation=this.shadow.getElementById("label-elevation"),this.labelObservationTime=this.shadow.getElementById("label-observation-time"),this.labelObservationEndTime=this.shadow.getElementById("label-observation-end-time"),this.labelWitnessId=this.shadow.getElementById("label-witness-id"),this.labelWitnessDirName=this.shadow.getElementById("label-witness-dir-name"),this.labelWitnessTitle=this.shadow.getElementById("label-witness-title"),this.labelWitnessLastName=this.shadow.getElementById("label-witness-last-name"),this.labelWitnessFirstNames=this.shadow.getElementById("label-witness-first-names"),this.labelCaseId=this.shadow.getElementById("label-case-id"),this.labelDescription=this.shadow.getElementById("label-description"),this.labelTags=this.shadow.getElementById("label-tags"),this.labelWeather=this.shadow.getElementById("label-weather"),this.labelImportFile=this.shadow.getElementById("label-import-file"),this.labelImportUrl=this.shadow.getElementById("label-import-url"),this.labelShapeGroup=this.shadow.getElementById("label-shape-group"),this.labelTemporalGroup=this.shadow.getElementById("label-temporal-group"),this.labelLocationGroup=this.shadow.getElementById("label-location-group"),this.labelObservationGroup=this.shadow.getElementById("label-observation-group"),this.labelWitnessGroup=this.shadow.getElementById("label-witness-group"),this.labelCircumstancesGroup=this.shadow.getElementById("label-circumstances-group"),this.labelCloudCover=this.shadow.getElementById("label-cloud-cover"),this.labelCloudDarkness=this.shadow.getElementById("label-cloud-darkness"),this.labelCloudBase=this.shadow.getElementById("label-cloud-base"),this.labelPrecipitationType=this.shadow.getElementById("label-precipitation-type"),this.labelPrecipitationIntensity=this.shadow.getElementById("label-precipitation-intensity"),this.labelWindDirection=this.shadow.getElementById("label-wind-direction"),this.labelWindSpeed=this.shadow.getElementById("label-wind-speed"),this.labelStorm=this.shadow.getElementById("label-storm"),this.labelLensFlareBrightness=this.shadow.getElementById("label-lens-flare-brightness"),this.labelCameraDevice=this.shadow.getElementById("label-camera-device"),this.optionPrecipitationNone=this.shadow.getElementById("option-precipitation-none"),this.optionPrecipitationRain=this.shadow.getElementById("option-precipitation-rain"),this.optionPrecipitationSnow=this.shadow.getElementById("option-precipitation-snow"),this.optionPrecipitationHail=this.shadow.getElementById("option-precipitation-hail"),this.decorKindSelect=this.shadow.getElementById("decorKind"),this.addDecorWitnessButton=this.shadow.getElementById("add-decor-witness"),this.addDecorBuildingButton=this.shadow.getElementById("add-decor-building"),this.deleteDecorButton=this.shadow.getElementById("delete-decor"),this.decorSelect=this.shadow.getElementById("decor"),this.decorTitleInput=this.shadow.getElementById("decorTitle"),this.decorEastInput=this.shadow.getElementById("decorEast"),this.decorNorthInput=this.shadow.getElementById("decorNorth"),this.decorHeadingInput=this.shadow.getElementById("decorHeading"),this.decorLitInput=this.shadow.getElementById("decorLit"),this.decorSightingUrlInput=this.shadow.getElementById("decorSightingUrl"),this.decorFloorsInput=this.shadow.getElementById("decorFloors"),this.decorOccupiedFloorInput=this.shadow.getElementById("decorOccupiedFloor"),this.decorWitnessSideSelect=this.shadow.getElementById("decorWitnessSide"),this.decorWindowInputs={front:this.shadow.getElementById("decorWindowFront"),behind:this.shadow.getElementById("decorWindowBehind"),left:this.shadow.getElementById("decorWindowLeft"),right:this.shadow.getElementById("decorWindowRight"),"front-left":this.shadow.getElementById("decorWindowFrontLeft"),"front-right":this.shadow.getElementById("decorWindowFrontRight"),"behind-left":this.shadow.getElementById("decorWindowBehindLeft"),"behind-right":this.shadow.getElementById("decorWindowBehindRight")},this.labelDecorSide={front:this.shadow.getElementById("label-decor-window-front"),behind:this.shadow.getElementById("label-decor-window-behind"),left:this.shadow.getElementById("label-decor-window-left"),right:this.shadow.getElementById("label-decor-window-right"),"front-left":this.shadow.getElementById("label-decor-window-front-left"),"front-right":this.shadow.getElementById("label-decor-window-front-right"),"behind-left":this.shadow.getElementById("label-decor-window-behind-left"),"behind-right":this.shadow.getElementById("label-decor-window-behind-right")},this.optionWitnessSide={front:this.shadow.getElementById("option-witness-side-front"),behind:this.shadow.getElementById("option-witness-side-behind"),left:this.shadow.getElementById("option-witness-side-left"),right:this.shadow.getElementById("option-witness-side-right"),"front-left":this.shadow.getElementById("option-witness-side-front-left"),"front-right":this.shadow.getElementById("option-witness-side-front-right"),"behind-left":this.shadow.getElementById("option-witness-side-behind-left"),"behind-right":this.shadow.getElementById("option-witness-side-behind-right")},this.optionWitnessSideNone=this.shadow.getElementById("option-witness-side-none"),this.labelDecor=this.shadow.getElementById("label-decor"),this.labelDecorFieldset=this.shadow.getElementById("label-decor-fieldset"),this.labelDecorTitle=this.shadow.getElementById("label-decor-title"),this.labelDecorEast=this.shadow.getElementById("label-decor-east"),this.labelDecorNorth=this.shadow.getElementById("label-decor-north"),this.labelDecorHeading=this.shadow.getElementById("label-decor-heading"),this.labelDecorLit=this.shadow.getElementById("label-decor-lit"),this.labelDecorSightingUrl=this.shadow.getElementById("label-decor-sighting-url"),this.labelDecorFloors=this.shadow.getElementById("label-decor-floors"),this.labelDecorOccupiedFloor=this.shadow.getElementById("label-decor-occupied-floor"),this.labelDecorWitnessSide=this.shadow.getElementById("label-decor-witness-side"),this.labelDecorWindows=this.shadow.getElementById("label-decor-windows"),this.optionDecorBuilding=this.shadow.getElementById("option-decor-building"),this.optionDecorTree=this.shadow.getElementById("option-decor-tree"),this.optionDecorStreetlight=this.shadow.getElementById("option-decor-streetlight"),this.optionDecorVehicle=this.shadow.getElementById("option-decor-vehicle"),this.optionDecorWitness=this.shadow.getElementById("option-decor-witness"),this.ufoElement.canvasElement.addEventListener("pointerdown",t=>this.onPointerDown(t)),this.ufoElement.canvasElement.addEventListener("pointermove",t=>this.onPointerMove(t)),this.ufoElement.canvasElement.addEventListener("contextmenu",t=>this.onContextMenu(t)),this.contextGroupButton.addEventListener("click",()=>this.groupSelected()),this.contextUngroupButton.addEventListener("click",()=>this.ungroupSelected()),this.contextBringToFrontButton.addEventListener("click",()=>this.bringSelectedToFront()),this.contextSendToBackButton.addEventListener("click",()=>this.sendSelectedToBack()),this.contextDeleteButton.addEventListener("click",()=>{this.hideContextMenu(),this.deleteShape()}),this.contextAddVertexButton.addEventListener("click",()=>this.addVertexAtContextMenu()),this.contextDeleteVertexButton.addEventListener("click",()=>this.deleteVertexAtContextMenu()),this.contextViewTestimonyButton.addEventListener("click",()=>this.viewWitnessTestimony()),this.playPauseButton.addEventListener("click",()=>{this.ufoElement.togglePlayPause(),this.syncPlaybackControls()}),this.loopButton.addEventListener("click",()=>{this.ufoElement.toggleLoop(),this.syncPlaybackControls()}),this.seekInput.addEventListener("input",()=>this.ufoElement.currentTime=Number(this.seekInput.value)),this.recordButton.addEventListener("click",()=>this.toggleRecording()),this.addShapeButton.addEventListener("click",()=>this.addShape()),this.deleteShapeButton.addEventListener("click",()=>this.deleteShape()),this.exportButton.addEventListener("click",()=>this.exportJson()),this.importFileInput.addEventListener("change",()=>this.importFromFile()),this.importUrlButton.addEventListener("click",()=>this.importFromUrl()),this.durationInput.addEventListener("input",()=>{this.ufoElement.durationSeconds=this.durationInput.value===""?void 0:Number(this.durationInput.value),this.ufoElement.refresh(),this.updateDurationValidity()}),this.sourceSelect.addEventListener("change",()=>this.selectUnit(this.sourceSelect.value)),this.shapeTitleInput.addEventListener("input",()=>this.updateShapeTitle());for(const t of[this.objectSizeInput,this.objectDistanceInput])t.addEventListener("input",()=>this.updatePhysicalExtent());this.addDecorWitnessButton.addEventListener("click",()=>this.addDecor("witness")),this.addDecorBuildingButton.addEventListener("click",()=>this.addDecor()),this.deleteDecorButton.addEventListener("click",()=>this.deleteDecor()),this.decorSelect.addEventListener("change",()=>this.selectDecor(this.decorSelect.value));for(const t of[this.decorTitleInput,this.decorEastInput,this.decorNorthInput,this.decorHeadingInput,this.decorSightingUrlInput,this.decorFloorsInput,this.decorOccupiedFloorInput])t.addEventListener("input",()=>this.updateDecor());this.decorWitnessSideSelect.addEventListener("change",()=>this.updateDecor());for(const t of ss)this.decorWindowInputs[t].addEventListener("input",()=>this.updateDecorWindows());this.decorLitInput.addEventListener("input",()=>this.updateDecorLit()),this.ufoElement.addEventListener("timeupdate",()=>{this.onSelectionOrTimeChanged(),this.syncPlaybackControls(),this.dispatchEvent(new CustomEvent("sightingchange"))}),document.addEventListener("keydown",this.handleKeyDown);for(const t of qd)this.presetButtons[t].addEventListener("click",()=>this.setAppearance({presetId:t}));this.colorInput.addEventListener("input",()=>this.setAppearance({color:this.colorInput.value})),this.transparencyInput.addEventListener("input",()=>this.setAppearance({transparency:Number(this.transparencyInput.value)})),this.haloScaleInput.addEventListener("input",()=>this.setAppearance({haloScale:Number(this.haloScaleInput.value)}));for(const t of[this.latInput,this.lngInput,this.headingInput,this.pitchInput,this.elevationInput])t.addEventListener("input",()=>this.updateObserver());this.headingInput.addEventListener("focus",()=>this.sceneElement.setCompassForced(!0)),this.headingInput.addEventListener("blur",()=>this.sceneElement.setCompassForced(!1)),this.lensFlareBrightnessInput.addEventListener("input",()=>this.sceneElement.setAttribute("lens-flare-brightness",this.lensFlareBrightnessInput.value)),this.cameraDeviceInput.addEventListener("input",()=>this.sceneElement.setAttribute("lens-flare-intensity",this.cameraDeviceInput.value)),this.utcOffsetInput.addEventListener("input",()=>this.updateUtcOffset()),this.obsTimeInput.addEventListener("input",()=>this.updateObservationTime()),this.obsEndTimeInput.addEventListener("input",()=>this.updateObservationEndTime()),this.obsTimeInput.addEventListener("blur",()=>this.validateEdtfTimeInput(this.obsTimeInput)),this.obsEndTimeInput.addEventListener("blur",()=>this.validateEdtfTimeInput(this.obsEndTimeInput));for(const t of[this.witnessIdInput,this.witnessDirNameInput,this.witnessTitleInput,this.witnessLastNameInput,this.witnessFirstNamesInput,this.caseIdInput])t.addEventListener("input",()=>this.updateWitnessMetadata());this.descriptionInput.addEventListener("input",()=>this.updateDescription()),this.tagsInput.addEventListener("input",()=>this.updateTags());for(const t of[this.cloudCoverInput,this.cloudDarknessInput,this.cloudBaseInput,this.precipitationTypeSelect,this.precipitationIntensityInput,this.windDirectionInput,this.windSpeedInput,this.stormInput])t.addEventListener("input",()=>this.applyWeatherAtPlayhead());this.updatePresetButtons(),this.setRecordButtonLabel(!1),this.applyAppearanceAtPlayhead(),this.shapeTitleInput.value=this.shapeLabel(this.currentSourceId),this.updateShapeTitle(),this.currentDecorId=this.ufoElement.sighting.decor[0]?.id,this.refreshDecorList(),this.onSelectionOrTimeChanged(),this.syncDurationField(),this.syncPlaybackControls(),this.loadLocaleMessages()}static get observedAttributes(){return["src"]}connectedCallback(){const e=this.getAttribute("src");e&&this.importFromUrl(e)}attributeChangedCallback(e,t,n){e==="src"&&n&&n!==t&&this.isConnected&&this.importFromUrl(n)}disconnectedCallback(){document.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("click",this.handleOutsideContextMenuClick),this.endDrag()}get sightingData(){return this.ufoElement.sightingData}set sightingData(e){this.endDrag(),this.ufoElement.sightingData=e,this.currentSourceId=this.ufoElement.sighting.timeline.sourceIds[0]??na,this.selectedSourceIds=new Set([this.currentSourceId]),this.refreshSourceList(),this.currentDecorId=this.ufoElement.sighting.decor[0]?.id,this.refreshDecorList(),this.onSelectionOrTimeChanged(),this.syncDurationField(),this.syncObservationTimeFields(),this.syncObservationEndTimeFields(),this.syncWitnessMetadataFields()}exportJson(){const e=this.sightingData,t=`${e.witness?.id??"sighting"}-sighting.json`,n=new Blob([JSON.stringify(e,null,2)],{type:"application/json"}),s=URL.createObjectURL(n),r=document.createElement("a");r.href=s,r.download=t,r.click(),URL.revokeObjectURL(s)}async importFromFile(){const e=this.importFileInput.files?.[0];if(e)try{const t=await new Promise((n,s)=>{const r=new FileReader;r.onload=()=>n(r.result),r.onerror=()=>s(r.error),r.readAsText(e)});this.sightingData=JSON.parse(t)}catch{window.alert(this.messages.importError)}finally{this.importFileInput.value=""}}async importFromUrl(e=this.importUrlInput.value.trim()){if(e)try{const t=await fetch(e);if(!t.ok)throw new Error(`HTTP ${t.status}`);this.sightingData=await t.json()}catch{window.alert(this.messages.importError)}}numberOrUndefined(e){return e===""?void 0:Number(e)}stringOrUndefined(e){return e===""?void 0:e}wrapDegrees(e,t){if(e===void 0)return;const n=(e%360+360)%360;return String(n)!==t.value&&(t.value=String(n)),n}updateObserver(){if(this.ufoElement.playbackState==="playing")return;const e=this.numberOrUndefined(this.latInput.value),t=this.numberOrUndefined(this.lngInput.value),n=this.wrapDegrees(this.numberOrUndefined(this.headingInput.value),this.headingInput),s=this.numberOrUndefined(this.pitchInput.value)??0,r=this.numberOrUndefined(this.elevationInput.value)??0,a=this.ufoElement.sighting.event,o=this.ufoElement.sighting.witnessTrack,l=this.ufoElement.currentTime;a.place=e!==void 0&&t!==void 0?[{lat:e,lng:t}]:void 0,e===void 0&&t===void 0&&n===void 0&&s===0&&r===0?o.removeKeyframeAt(l):o.addKeyframe(l,{lat:e,lng:t,elevationM:r,headingDeg:n,pitchDeg:s,fovDeg:Xd}),this.ufoElement.refresh()}applyEdtfTimeInput(e,t){const n=e.value.trim();if(n==="")t(void 0);else{const s=Yc(n);if(!s)return;t(s)}e.setCustomValidity(""),e.classList.remove("invalid"),this.ufoElement.refresh(),this.syncDurationField()}validateEdtfTimeInput(e){const t=e.value.trim();t===""||Yc(t)||(e.setCustomValidity(this.messages.edtfInvalid),e.classList.add("invalid"))}updateUtcOffset(){this.ufoElement.sighting.event.utcOffsetHours=this.numberOrUndefined(this.utcOffsetInput.value),this.ufoElement.refresh()}updateObservationTime(){this.applyEdtfTimeInput(this.obsTimeInput,e=>{this.ufoElement.sighting.event.time=e})}updateObservationEndTime(){this.applyEdtfTimeInput(this.obsEndTimeInput,e=>{this.ufoElement.sighting.event.endTime=e})}updateWitnessMetadata(){const e=this.ufoElement.sighting,t=this.witnessFirstNamesInput.value.split(",").map(s=>s.trim()).filter(s=>s.length>0),n={id:this.stringOrUndefined(this.witnessIdInput.value),dirName:this.stringOrUndefined(this.witnessDirNameInput.value),title:this.stringOrUndefined(this.witnessTitleInput.value),lastName:this.stringOrUndefined(this.witnessLastNameInput.value),firstNames:t.length>0?t:void 0};e.witness=Object.values(n).some(s=>s!==void 0)?n:void 0,e.caseId=this.stringOrUndefined(this.caseIdInput.value),this.ufoElement.refresh()}updateDescription(){this.ufoElement.sighting.event.description=this.stringOrUndefined(this.descriptionInput.value),this.ufoElement.refresh()}updateTags(){const e=this.tagsInput.value.split(",").map(t=>t.trim()).filter(t=>t.length>0);this.ufoElement.sighting.event.tags=e.length>0?e:void 0,this.ufoElement.refresh()}applyWeatherAtPlayhead(){if(this.ufoElement.playbackState==="playing")return;this.sceneElement.resumeWeatherAudio();const e={cloudCover:Number(this.cloudCoverInput.value),cloudDarkness:Number(this.cloudDarknessInput.value),cloudBaseM:this.numberOrUndefined(this.cloudBaseInput.value),precipitationType:this.precipitationTypeSelect.value,precipitationIntensity:Number(this.precipitationIntensityInput.value),windDirectionDeg:this.wrapDegrees(this.numberOrUndefined(this.windDirectionInput.value),this.windDirectionInput)??0,windSpeed:Number(this.windSpeedInput.value),storm:this.stormInput.checked};this.ufoElement.sighting.weatherTrack.addKeyframe(this.ufoElement.currentTime,e),this.ufoElement.refresh()}syncDurationField(){if(this.shadow.activeElement!==this.durationInput){const e=dc(this.ufoElement.sighting.event);this.durationInput.value=e!==void 0?String(e/1e3):""}this.updateDurationValidity()}updateDurationValidity(){const e=this.durationInput.value==="";this.durationInput.classList.toggle("invalid",e),this.durationInput.setAttribute("aria-invalid",String(e));const t=K0(this.ufoElement.sighting.event);this.durationInput.title=t==="imprecise"?this.messages.durationImprecise:""}syncPlaybackControls(){const e=this.ufoElement.playbackState==="playing",t=this.ufoElement.seekableDuration>0;this.playPauseButton.textContent=e?"⏸":"▶",this.playPauseButton.disabled=!t;const n=t?e?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=n,this.playPauseButton.setAttribute("aria-label",n),this.loopButton.setAttribute("aria-pressed",String(this.ufoElement.autoReplayEnabled)),this.seekInput.max=String(this.ufoElement.seekableDuration),this.shadow.activeElement!==this.seekInput&&(this.seekInput.value=String(this.ufoElement.currentTime)),this.timeStartLabel.textContent=this.ufoElement.positionLabel,this.timeEndLabel.textContent=this.ufoElement.durationLabel}syncObservationTimeFields(){const e=this.ufoElement.sighting.event.time;this.obsTimeInput.value=e?qc(e):"",this.obsTimeInput.setCustomValidity(""),this.obsTimeInput.classList.remove("invalid")}syncObservationEndTimeFields(){const e=this.ufoElement.sighting.event.endTime;this.obsEndTimeInput.value=e?qc(e):"",this.obsEndTimeInput.setCustomValidity(""),this.obsEndTimeInput.classList.remove("invalid")}syncWitnessMetadataFields(){const e=this.ufoElement.sighting;this.witnessIdInput.value=e.witness?.id??"",this.witnessDirNameInput.value=e.witness?.dirName??"",this.witnessTitleInput.value=e.witness?.title??"",this.witnessLastNameInput.value=e.witness?.lastName??"",this.witnessFirstNamesInput.value=e.witness?.firstNames?.join(", ")??"",this.caseIdInput.value=e.caseId??"",this.descriptionInput.value=e.event.description??"",this.tagsInput.value=e.event.tags?.join(", ")??""}syncWeatherFromTimeline(){if(this.ufoElement.playbackState==="playing")return;const e=Ho(this.ufoElement.sighting,this.ufoElement.currentTime),t=this.shadow.activeElement;t!==this.cloudCoverInput&&(this.cloudCoverInput.value=String(e.cloudCover)),t!==this.cloudDarknessInput&&(this.cloudDarknessInput.value=String(e.cloudDarkness)),t!==this.cloudBaseInput&&(this.cloudBaseInput.value=e.cloudBaseM===void 0?"":String(e.cloudBaseM)),t!==this.precipitationTypeSelect&&(this.precipitationTypeSelect.value=e.precipitationType),t!==this.precipitationIntensityInput&&(this.precipitationIntensityInput.value=String(e.precipitationIntensity)),t!==this.windDirectionInput&&(this.windDirectionInput.value=String(e.windDirectionDeg)),t!==this.windSpeedInput&&(this.windSpeedInput.value=String(e.windSpeed)),t!==this.stormInput&&(this.stormInput.checked=e.storm)}syncObserverFromTimeline(){if(this.ufoElement.playbackState==="playing")return;const e=this.ufoElement.sighting,t=e.witnessTrack.getInterpolatedPoseAt(this.ufoElement.currentTime),n=e.event.place?.[0],s=this.shadow.activeElement;s!==this.latInput&&(this.latInput.value=t?.lat!==void 0?String(t.lat):n?String(n.lat):""),s!==this.lngInput&&(this.lngInput.value=t?.lng!==void 0?String(t.lng):n?String(n.lng):""),s!==this.headingInput&&(this.headingInput.value=t?.headingDeg!==void 0?String(t.headingDeg):""),s!==this.pitchInput&&(this.pitchInput.value=String(t?.pitchDeg??0)),s!==this.elevationInput&&(this.elevationInput.value=String(t?.elevationM??0))}get appearance(){return{...this.currentAppearance}}set appearance(e){this.setAppearance(e)}setAppearance(e){const t="presetId"in e;this.currentAppearance={...this.currentAppearance,...e},this.updatePresetButtons(),!(this.isRecording||this.ufoElement.playbackState==="playing")&&this.applyAppearanceAtPlayhead(void 0,t)}applyAppearanceAtPlayhead(e,t=!0){const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=n.getInterpolatedShapeAt(s,this.currentSourceId),a=this.buildAppearanceShape(e??r?.bounds??this.defaultBounds(),r,t);n.addKeyframe(s,[{sourceId:this.currentSourceId,shape:a}]),this.ufoElement.refresh()}buildAppearanceShape(e,t,n=!0){if(t&&!n)return{...t,color:this.currentAppearance.color,transparency:this.currentAppearance.transparency,haloScale:this.currentAppearance.haloScale};const s=Wc(e,this.currentAppearance);return t?{...s,angle:t.angle,title:t.title,selected:t.selected}:s}onSelectionOrTimeChanged(){this.syncAppearanceFromTimeline(),this.syncObserverFromTimeline(),this.syncWeatherFromTimeline(),this.syncDecorLitFromTimeline(),this.syncIndoorLookReset(),this.updateAppearanceFieldsDisabledState(),this.updateShapeTitleValidity(),this.ufoElement.selectedSourceIds=this.selectedSourceIds;const e=this.ufoElement.sighting.timeline.sourceIds,t=e.filter(n=>this.selectedSourceIds.has(n)).length;this.deleteShapeButton.disabled=this.isRecording||this.ufoElement.playbackState==="playing"||t===0||e.length-t<1}updateAppearanceFieldsDisabledState(){const e=this.selectedSourceIds.size>1,t=e?this.messages.multipleShapesSelected:"";for(const n of[this.shapeTitleInput,this.colorInput,this.transparencyInput,this.haloScaleInput,this.objectSizeInput,this.objectDistanceInput,this.sourceSelect,...Object.values(this.presetButtons)])n.disabled=e,n.title=t}syncAppearanceFromTimeline(){if(this.ufoElement.playbackState==="playing"||this.selectedSourceIds.size>1)return;const e=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,this.currentSourceId);e&&(this.currentAppearance={presetId:_y(e),color:e.color,transparency:e.transparency,haloScale:e.haloScale},this.updatePresetButtons(),this.colorInput.value=this.currentAppearance.color,this.transparencyInput.value=String(this.currentAppearance.transparency),this.haloScaleInput.value=String(this.currentAppearance.haloScale),this.shadow.activeElement!==this.shapeTitleInput&&(this.shapeTitleInput.value=e.title??""),this.shadow.activeElement!==this.objectSizeInput&&(this.objectSizeInput.value=e.physical?String(Number(e.physical.sizeM.toFixed(2))):""),this.shadow.activeElement!==this.objectDistanceInput&&(this.objectDistanceInput.value=e.physical?String(Number(e.physical.distanceM.toFixed(2))):""),this.refreshApparentSize())}updateShapeTitle(){const e=this.ufoElement.sighting.timeline,t=this.ufoElement.currentTime,n=e.getInterpolatedShapeAt(t,this.currentSourceId);n&&(e.addKeyframe(t,[{sourceId:this.currentSourceId,shape:{...n,title:this.stringOrUndefined(this.shapeTitleInput.value)}}]),this.ufoElement.refresh(),this.refreshSourceList(),this.updateShapeTitleValidity())}updatePhysicalExtent(){const e=this.ufoElement.sighting.timeline,t=this.ufoElement.currentTime,n=e.getInterpolatedShapeAt(t,this.currentSourceId);if(!n)return;const s=this.numberOrUndefined(this.objectSizeInput.value),r=this.numberOrUndefined(this.objectDistanceInput.value);if(s===void 0&&r===void 0){e.addKeyframe(t,[{sourceId:this.currentSourceId,shape:{...n,physical:void 0}}]),this.ufoElement.refresh(),this.refreshApparentSize();return}if(s===void 0||r===void 0||s<=0||r<=0){this.refreshApparentSize();return}const a={sizeM:s,distanceM:r},o=this.ufoElement.canvasElement,l=ps.widthPx(a,o.height,this.currentFovDeg()),c=n.bounds.height*(n.bounds.width===0?1:l/n.bounds.width),h={x:n.bounds.x+(n.bounds.width-l)/2,y:n.bounds.y+(n.bounds.height-c)/2,width:l,height:c},u=n.kind==="oval"?{...n,bounds:h,physical:a}:{...n,bounds:h,physical:a,points:this.scalePoints(n,h)};e.addKeyframe(t,[{sourceId:this.currentSourceId,shape:u}]),this.ufoElement.refresh(),this.refreshApparentSize()}scalePoints(e,t){const n=e.bounds.width===0?1:t.width/e.bounds.width,s=e.bounds.height===0?1:t.height/e.bounds.height;return e.points.map(r=>({x:r.x*n,y:r.y*s}))}currentFovDeg(){return Go(this.ufoElement.sighting,this.ufoElement.currentTime)?.fovDeg??Xd}refreshApparentSize(){const e=this.selectedSourceIds.size===1?this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,this.currentSourceId):void 0;if(!e){this.apparentSizeOutput.textContent="";return}const t=this.ufoElement.canvasElement,n=ps.pxToDeg(e.bounds.width,t.height,this.currentFovDeg()),s=ps.inMoons(n);this.apparentSizeOutput.textContent=this.messages.apparentSize.replace("{deg}",n.toLocaleString(void 0,{maximumFractionDigits:n<1?2:1})).replace("{moons}",s.toLocaleString(void 0,{maximumFractionDigits:s<10?1:0}))}updateShapeTitleValidity(){const e=this.selectedSourceIds.size===1&&this.shapeTitleInput.value==="";this.shapeTitleInput.classList.toggle("invalid",e),this.shapeTitleInput.setAttribute("aria-invalid",String(e))}shapeLabel(e){const t=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,e);if(t?.title)return t.title;const n=/^ufo-(\d+)$/.exec(e);return n?`${this.messages.shape} ${n[1]}`:e}addShape(){if(this.isRecording)return;const e=this.ufoElement.sighting.timeline,t=new Set([...e.sourceIds,this.currentSourceId]);let n=t.size+1;for(;t.has(`ufo-${n}`);)n++;this.currentSourceId=`ufo-${n}`,this.selectedSourceIds=new Set([this.currentSourceId]),this.applyAppearanceAtPlayhead(this.offsetDefaultBounds(e.sourceIds.length)),this.shapeTitleInput.value=this.shapeLabel(this.currentSourceId),this.updateShapeTitle()}deleteShape(){if(this.isRecording||this.ufoElement.playbackState==="playing")return;const e=this.ufoElement.sighting.timeline,t=e.sourceIds.filter(s=>this.selectedSourceIds.has(s));if(!(t.length===0||e.sourceIds.length-t.length<1||!(t.length===1?window.confirm(this.messages.confirmDeleteShape.replace("{name}",this.shapeLabel(t[0]))):window.confirm(this.messages.confirmDeleteShapes.replace("{count}",String(t.length)))))){for(const s of t)e.removeSource(s);this.currentSourceId=e.sourceIds[0],this.selectedSourceIds=new Set([this.currentSourceId]),this.refreshSourceList(),this.ufoElement.refresh()}}offsetDefaultBounds(e){const t=this.defaultBounds(),n=24;return{...t,x:t.x+e*n,y:t.y+e*n}}refreshSourceList(){const e=this.ufoElement.sighting.timeline,t=[...new Set([...e.sourceIds,this.currentSourceId])];this.sourceSelect.innerHTML="";for(const n of t){const s=document.createElement("option");s.value=n,s.textContent=this.shapeLabel(n),this.sourceSelect.appendChild(s)}this.sourceSelect.value=this.currentSourceId}decorLabel(e){if(e.title)return e.title;const n=this.ufoElement.sighting.decor.filter(r=>r.kind===e.kind).indexOf(e)+1;return`${this.decorKindSelect.querySelector(`option[value="${e.kind}"]`)?.textContent??e.kind} ${n}`}addDecor(e=this.decorKindSelect.value){const t=this.ufoElement.sighting,s={id:`decor-${t.decor.length+1}`,kind:e,title:this.nextDecorLabel(e),eastM:8*t.decor.length,northM:15,headingDeg:0,lit:!1,...e==="building"?{floors:ha}:{},windows:G2(e)};t.decor=[...t.decor,s],this.currentDecorId=s.id,this.refreshDecorList(),this.ufoElement.refresh()}nextDecorLabel(e){const t=this.ufoElement.sighting.decor.filter(s=>s.kind===e).length;return`${this.decorKindSelect.querySelector(`option[value="${e}"]`)?.textContent??e} ${t+1}`}deleteDecor(){if(this.currentDecorId===void 0)return;const e=this.ufoElement.sighting;e.decor=e.decor.filter(t=>t.id!==this.currentDecorId),this.currentDecorId=e.decor[0]?.id,this.refreshDecorList(),this.ufoElement.refresh()}selectDecor(e){this.currentDecorId=e,this.decorSelect.value=e,this.syncDecorFields()}updateDecor(){if(this.currentDecorId===void 0)return;const e=this.ufoElement.sighting,t=this.wrapDegrees(Number(this.decorHeadingInput.value),this.decorHeadingInput)??0,n=this.decorWitnessSideSelect.value;e.decor=e.decor.map(s=>{if(s.id!==this.currentDecorId)return s;const r=Zs(s.kind)&&n!==""?n:void 0;return{...s,title:this.stringOrUndefined(this.decorTitleInput.value),eastM:Number(this.decorEastInput.value),northM:Number(this.decorNorthInput.value),headingDeg:t,sightingUrl:this.stringOrUndefined(this.decorSightingUrlInput.value),witnessSide:r,floors:s.kind==="building"?Number(this.decorFloorsInput.value):void 0,occupiedFloor:s.kind==="building"?Number(this.decorOccupiedFloorInput.value):void 0}}),this.decorSelect.options[this.decorSelect.selectedIndex].textContent=this.decorLabel(e.decor.find(s=>s.id===this.currentDecorId)),this.syncDecorVisibility(),this.updateDecorTitleValidity(),this.ufoElement.refresh()}updateDecorTitleValidity(){const e=this.currentDecorId!==void 0&&this.decorTitleInput.value==="";this.decorTitleInput.classList.toggle("invalid",e),this.decorTitleInput.setAttribute("aria-invalid",String(e))}updateDecorWindows(){if(this.currentDecorId===void 0)return;const e=this.ufoElement.sighting;e.decor=e.decor.map(t=>{if(t.id!==this.currentDecorId)return t;const n={};for(const s of ss){const r=this.decorWindowInputs[s];if(r.value==="")continue;const a=sc(t.kind,s)?0:ic,o=Math.max(a,Math.min(100,Number(r.value)));n[s]=o,String(o)!==r.value&&(r.value=String(o))}return{...t,windows:n}}),this.ufoElement.refresh()}updateDecorLit(){if(this.currentDecorId===void 0||this.ufoElement.playbackState==="playing")return;const e=this.ufoElement.sighting,t=this.ufoElement.currentTime,n=this.decorLitInput.checked;e.decor=e.decor.map(s=>{if(s.id!==this.currentDecorId)return s;const r=[...(s.litKeyframes??[]).filter(a=>a.t!==t),{t,lit:n}].sort((a,o)=>a.t-o.t);return{...s,litKeyframes:r}}),this.ufoElement.refresh()}refreshDecorList(){const e=this.ufoElement.sighting.decor;this.decorSelect.innerHTML="";for(const t of e){const n=document.createElement("option");n.value=t.id,n.textContent=this.decorLabel(t),this.decorSelect.appendChild(n)}this.currentDecorId!==void 0&&(this.decorSelect.value=this.currentDecorId),this.syncDecorFields()}syncDecorFields(){const e=this.ufoElement.sighting.decor.find(n=>n.id===this.currentDecorId),t=e!==void 0;this.deleteDecorButton.disabled=!t;for(const n of[this.decorSelect,this.decorTitleInput,this.decorEastInput,this.decorNorthInput,this.decorHeadingInput,this.decorLitInput,this.decorSightingUrlInput,this.decorFloorsInput,this.decorOccupiedFloorInput,this.decorWitnessSideSelect])n.disabled=!t;this.decorTitleInput.value=e?.title??"",this.decorEastInput.value=String(e?.eastM??0),this.decorNorthInput.value=String(e?.northM??0),this.decorHeadingInput.value=String(e?.headingDeg??0),this.decorLitInput.checked=e?rc(e,this.ufoElement.currentTime):!1,this.decorSightingUrlInput.value=e?.sightingUrl??"",this.decorFloorsInput.value=String(e?.floors??ha),this.decorOccupiedFloorInput.value=String(e?.occupiedFloor??0),this.decorWitnessSideSelect.value=e?.witnessSide??"";for(const n of ss){const s=e?.windows?.[n];this.decorWindowInputs[n].value=s===void 0?"":String(s)}this.syncDecorVisibility(),this.updateDecorTitleValidity()}setRowVisible(e,t){const n=e.closest("label")??e;n.hidden=!t}syncDecorVisibility(){const e=this.ufoElement.sighting.decor.find(c=>c.id===this.currentDecorId),t=e!==void 0,n=e?.kind;this.setRowVisible(this.decorSelect,t),this.setRowVisible(this.deleteDecorButton,t),this.setRowVisible(this.decorTitleInput,t),this.setRowVisible(this.decorEastInput,t),this.setRowVisible(this.decorNorthInput,t),this.setRowVisible(this.decorHeadingInput,t),this.setRowVisible(this.decorLitInput,t);const s=t&&n!==void 0&&l0(n);this.setRowVisible(this.labelDecorWindows,s);const r=n!==void 0?Nc(n):[];for(const c of ss)this.decorWindowInputs[c].disabled=!t,this.decorWindowInputs[c].min=String(n!==void 0&&sc(n,c)?0:ic),this.setRowVisible(this.decorWindowInputs[c],s&&r.includes(c));const a=t&&n!==void 0&&Zs(n);this.setRowVisible(this.decorWitnessSideSelect,a);const o=n!==void 0?z2(n):[];for(const c of ss)this.optionWitnessSide[c].hidden=!o.includes(c);const l=t&&n==="building";this.setRowVisible(this.decorFloorsInput,l),this.setRowVisible(this.decorOccupiedFloorInput,l),this.decorOccupiedFloorInput.max=String(e?.floors??ha)}syncDecorLitFromTimeline(){if(this.ufoElement.playbackState==="playing"||this.shadow.activeElement===this.decorLitInput)return;const e=this.ufoElement.sighting.decor.find(t=>t.id===this.currentDecorId);e&&(this.decorLitInput.checked=rc(e,this.ufoElement.currentTime))}updatePresetButtons(){for(const e of qd)this.presetButtons[e]?.setAttribute("aria-pressed",String(e===this.currentAppearance.presetId))}get samplingRate(){return Number(this.samplingRateInput.value)}buildPrototype(e=this.defaultBounds()){return Wc(e,this.currentAppearance)}defaultBounds(){const e=this.ufoElement.canvasElement;return{x:e.width/2-ls.width/2,y:e.height/2-ls.height/2,width:ls.width,height:ls.height}}toggleRecording(){this.endDrag();const e=this.ufoElement.canvasElement;this.isRecording?(this.recorder?.stop(),this.isRecording=!1,this.setRecordButtonLabel(!1),this.setCanvasCursor(void 0),e.style.touchAction="",this.sourceSelect.disabled=!1,this.addShapeButton.disabled=!1,this.ufoElement.refresh(),this.refreshSourceList()):(this.recorder=new my(this.ufoElement.sighting.timeline,new gy(this.samplingRate)),this.recorder.start(this.currentSourceId,this.buildPrototype()),this.isRecording=!0,this.setRecordButtonLabel(!0),this.setCanvasCursor("record"),e.style.touchAction="none",this.sourceSelect.disabled=!0,this.addShapeButton.disabled=!0,this.deleteShapeButton.disabled=!0)}setRecordButtonLabel(e){this.recordButton.textContent=e?"⏹":"⏺";const t=e?this.messages.stop:this.messages.record;this.recordButton.title=t,this.recordButton.setAttribute("aria-label",t)}async loadLocaleMessages(){const e=Ia(navigator.languages,jd);e!=="en"&&this.applyMessages(await ff(e))}applyMessages(e){this.currentSourceId===na&&this.shapeTitleInput.value===`${this.messages.shape} 1`?(this.messages=e,this.shapeTitleInput.value=`${this.messages.shape} 1`,this.updateShapeTitle()):this.messages=e,this.presetButtons.oval.textContent=e.oval,this.presetButtons.polygon.textContent=e.polygon,this.contextAddVertexButton.textContent=e.addVertex,this.contextDeleteVertexButton.textContent=e.deleteVertex,this.labelColor.textContent=e.color,this.labelTransparency.textContent=e.transparency,this.labelHalo.textContent=e.halo,this.labelShape.textContent=e.shape,this.labelShapeTitle.textContent=e.shapeTitle,this.labelUtcOffset.textContent=e.utcOffset,this.utcOffsetInput.placeholder=e.utcOffsetPlaceholder,this.labelObjectSize.textContent=e.objectSize,this.labelObjectDistance.textContent=e.objectDistance,this.objectSizeInput.placeholder=e.objectSizePlaceholder,this.objectDistanceInput.placeholder=e.objectDistancePlaceholder,this.refreshApparentSize(),this.labelSamplingRate.textContent=e.samplingRate,this.labelDuration.textContent=e.duration,this.durationInput.placeholder=e.durationPlaceholder,this.addShapeButton.title=e.addShape,this.addShapeButton.setAttribute("aria-label",e.addShape),this.deleteShapeButton.title=e.deleteShape,this.deleteShapeButton.setAttribute("aria-label",e.deleteShape),this.contextGroupButton.textContent=e.group,this.contextUngroupButton.textContent=e.ungroup,this.contextBringToFrontButton.textContent=e.bringToFront,this.contextSendToBackButton.textContent=e.sendToBack,this.contextDeleteButton.textContent=e.contextMenuDelete,this.exportButton.textContent=e.export,this.labelImportFile.textContent=e.importFile,this.labelImportUrl.textContent=e.importUrl,this.importUrlInput.placeholder=e.importUrlPlaceholder,this.importUrlButton.textContent=e.importButton,this.labelLatitude.textContent=e.latitude,this.labelLongitude.textContent=e.longitude,this.labelHeading.textContent=e.heading,this.headingInput.placeholder=e.headingPlaceholder,this.labelPitch.textContent=e.pitch,this.labelElevation.textContent=e.elevation,this.labelObservationTime.textContent=e.observationTime,this.labelObservationEndTime.textContent=e.observationEndTime,this.obsTimeInput.placeholder=e.edtfPlaceholder,this.obsTimeInput.title=e.observationTimeHint,this.obsEndTimeInput.placeholder=e.edtfPlaceholder,this.obsEndTimeInput.title=e.observationEndTimeHint,this.presetsGroup.setAttribute("aria-label",e.presetsGroupLabel),this.labelDecor.textContent=e.decor,this.labelDecorFieldset.textContent=e.decor,this.optionDecorBuilding.textContent=e.decorBuilding,this.optionDecorTree.textContent=e.decorTree,this.optionDecorStreetlight.textContent=e.decorStreetlight,this.optionDecorVehicle.textContent=e.decorVehicle,this.optionDecorWitness.textContent=e.decorWitness,this.deleteDecorButton.title=e.deleteDecor,this.deleteDecorButton.setAttribute("aria-label",e.deleteDecor),this.labelDecorTitle.textContent=e.decorTitle,this.labelDecorEast.textContent=e.decorEast,this.labelDecorNorth.textContent=e.decorNorth,this.labelDecorHeading.textContent=e.decorHeading,this.labelDecorLit.textContent=e.decorLit,this.labelDecorSightingUrl.textContent=e.decorSightingUrl,this.contextViewTestimonyButton.textContent=e.viewTestimony,this.labelContextMasks.textContent=`${e.masks} ▸`,this.addDecorWitnessButton.textContent=e.addWitness,this.addDecorBuildingButton.title=e.addDecor,this.addDecorBuildingButton.setAttribute("aria-label",e.addDecor),this.labelDecorFloors.textContent=e.decorFloors,this.labelDecorOccupiedFloor.textContent=e.decorOccupiedFloor,this.labelDecorWitnessSide.textContent=e.decorWitnessSide,this.labelDecorWindows.textContent=e.decorWindows,this.optionWitnessSideNone.textContent=e.decorWitnessSideNone;const t={front:e.decorSideFront,behind:e.decorSideBehind,left:e.decorSideLeft,right:e.decorSideRight,"front-left":e.decorSideFrontLeft,"front-right":e.decorSideFrontRight,"behind-left":e.decorSideBehindLeft,"behind-right":e.decorSideBehindRight};for(const n of ss)this.labelDecorSide[n].textContent=t[n],this.optionWitnessSide[n].textContent=t[n];this.refreshDecorList(),this.labelWitnessId.textContent=e.witnessId,this.labelWitnessDirName.textContent=e.witnessDirName,this.labelWitnessTitle.textContent=e.witnessTitle,this.labelWitnessLastName.textContent=e.witnessLastName,this.labelWitnessFirstNames.textContent=e.witnessFirstNames,this.witnessFirstNamesInput.placeholder=e.tagsPlaceholder,this.labelCaseId.textContent=e.caseId,this.labelDescription.textContent=e.description,this.labelTags.textContent=e.tags,this.tagsInput.placeholder=e.tagsPlaceholder,this.labelWeather.textContent=e.weather,this.labelShapeGroup.textContent=e.shapeGroup,this.labelTemporalGroup.textContent=e.temporalGroup,this.labelLocationGroup.textContent=e.locationGroup,this.labelObservationGroup.textContent=e.observationGroup,this.labelWitnessGroup.textContent=e.witnessGroup,this.labelCircumstancesGroup.textContent=e.circumstancesGroup,this.labelCloudCover.textContent=e.cloudCover,this.labelCloudDarkness.textContent=e.cloudDarkness,this.labelCloudBase.textContent=e.cloudBase,this.labelPrecipitationType.textContent=e.precipitationType,this.optionPrecipitationNone.textContent=e.precipitationNone,this.optionPrecipitationRain.textContent=e.precipitationRain,this.optionPrecipitationSnow.textContent=e.precipitationSnow,this.optionPrecipitationHail.textContent=e.precipitationHail,this.labelPrecipitationIntensity.textContent=e.precipitationIntensity,this.labelWindDirection.textContent=e.windDirection,this.labelWindSpeed.textContent=e.windSpeed,this.labelStorm.textContent=e.storm,this.labelLensFlareBrightness.textContent=e.lensFlareBrightness,this.labelCameraDevice.textContent=e.cameraDevice,this.loopButton.title=e.autoReplay,this.loopButton.setAttribute("aria-label",e.autoReplay),this.setRecordButtonLabel(this.isRecording),this.syncPlaybackControls()}onPointerDown(e){if(this.isRecording){this.onPointerMove(e);return}const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=this.ufoElement.playbackState==="playing";if(this.selectedSourceIds.size>1){const l=new za(this.selectedMembers()),c=Ke.hitTestHandle({bounds:l.bounds(),angle:0},t);if(c==="rotate"){if(r)return;this.dragState={kind:"group-rotate",group:l,startPointer:t},this.startDragListening();return}if(c){if(r)return;this.dragState={kind:"group-resize",group:l,handle:c},this.startDragListening();return}}else{const l=n.getInterpolatedShapeAt(s,this.currentSourceId),c=l?.kind==="polygon"?Ke.hitTestVertex(l,t):void 0;if(l?.kind==="polygon"&&c!==void 0){if(r)return;this.dragState={kind:"vertex",sourceId:this.currentSourceId,original:l,vertexIndex:c},this.startDragListening();return}const h=l&&Ke.hitTestHandle(l,t);if(l&&h){if(r)return;this.beginDrag(h==="rotate"?"rotate":"resize",this.currentSourceId,l,t,h);return}}const a=n.hitTest(s,t.x,t.y);if(!a){const l=this.pickDecorAt(e);if(l!==void 0){this.selectDecor(l);return}r||this.beginCameraDrag(t);return}if(e.shiftKey?this.toggleUnitSelection(a.sourceId):this.selectedSourceIds.has(a.sourceId)||this.selectUnit(a.sourceId),r)return;const o=[...this.selectedSourceIds].map(l=>({sourceId:l,original:l===a.sourceId?a.shape:n.getInterpolatedShapeAt(s,l)??a.shape}));this.dragState={kind:"move",sources:o,startPointer:t},this.startDragListening()}selectedMembers(){const e=this.ufoElement.sighting.timeline,t=this.ufoElement.currentTime;return[...this.selectedSourceIds].map(n=>({sourceId:n,shape:e.getInterpolatedShapeAt(t,n)})).filter(n=>!!n.shape)}selectUnit(e){const t=this.ufoElement.sighting.timeline.groupMembers(e)??[e];t.length===this.selectedSourceIds.size&&t.every(n=>this.selectedSourceIds.has(n))&&e===this.currentSourceId||(this.selectedSourceIds=new Set(t),this.currentSourceId=e,this.refreshSourceList(),this.onSelectionOrTimeChanged())}toggleUnitSelection(e){const t=this.ufoElement.sighting.timeline,n=t.groupMembers(e)??[e];if(n.every(r=>this.selectedSourceIds.has(r))){if(n.length>=this.selectedSourceIds.size)return;for(const r of n)this.selectedSourceIds.delete(r);if(!this.selectedSourceIds.has(this.currentSourceId)){const r=t.sourceIds;this.currentSourceId=[...this.selectedSourceIds].sort((a,o)=>r.indexOf(a)-r.indexOf(o)).pop()}}else{for(const r of n)this.selectedSourceIds.add(r);this.currentSourceId=e}this.refreshSourceList(),this.onSelectionOrTimeChanged()}onContextMenu(e){if(e.preventDefault(),this.isRecording||this.ufoElement.playbackState==="playing")return;this.hideContextMenu(),this.hideDecorContextMenu();const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=this.selectedSourceIds.size===1?n.getInterpolatedShapeAt(s,this.currentSourceId):void 0,o=r?.kind==="polygon"&&Ke.hitTestVertex(r,t)!==void 0?{sourceId:this.currentSourceId}:n.hitTest(s,t.x,t.y);if(o){this.selectedSourceIds.has(o.sourceId)||this.selectUnit(o.sourceId),this.currentSourceId=o.sourceId,this.contextMenuPoint=t,this.showContextMenu(e.clientX,e.clientY);return}const l=this.pickDecorAt(e),c=l?this.ufoElement.sighting.decor.find(h=>h.id===l):void 0;c&&this.showDecorContextMenu(e.clientX,e.clientY,c)}pickDecorAt(e){const n=this.ufoElement.canvasElement.getBoundingClientRect();if(n.width===0||n.height===0)return;const s=(e.clientX-n.left)/n.width*2-1,r=-((e.clientY-n.top)/n.height*2-1);return this.sceneElement.pickDecorAt(s,r)}showContextMenu(e,t){this.contextMenu.style.left=`${e}px`,this.contextMenu.style.top=`${t}px`,this.contextMenu.hidden=!1;const n=this.ufoElement.sighting.timeline.sourceIds,s=n.filter(p=>this.selectedSourceIds.has(p)),r=n.length<=s.length,a=s.length>0&&n.slice(n.length-s.length).every(p=>this.selectedSourceIds.has(p)),o=s.length>0&&n.slice(0,s.length).every(p=>this.selectedSourceIds.has(p));this.contextBringToFrontButton.disabled=r||a,this.contextSendToBackButton.disabled=r||o,this.contextBringToFrontButton.title=r?this.messages.onlyOneShape:a?this.messages.alreadyAtFront:"",this.contextSendToBackButton.title=r?this.messages.onlyOneShape:o?this.messages.alreadyAtBack:"",this.contextGroupButton.disabled=this.selectedSourceIds.size<2,this.contextGroupButton.title=this.contextGroupButton.disabled?this.messages.needTwoShapesToGroup:"";const l=this.ufoElement.sighting.timeline.groupMembers(this.currentSourceId)!==void 0;this.contextUngroupButton.disabled=!l,this.contextUngroupButton.title=l?"":this.messages.notGrouped,this.contextDeleteButton.disabled=this.deleteShapeButton.disabled,this.contextDeleteButton.title=this.contextDeleteButton.disabled?this.messages.onlyOneShape:"";const c=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(this.ufoElement.currentTime,this.currentSourceId),h=this.selectedSourceIds.size===1&&c?.kind==="polygon";this.contextAddVertexButton.disabled=!h,this.contextAddVertexButton.title=h?"":this.messages.notAPolygon;const u=h&&this.contextMenuPoint?Ke.hitTestVertex(c,this.contextMenuPoint):void 0,d=h&&c.points.length<=Qd;this.contextDeleteVertexButton.disabled=!h||u===void 0||d,this.contextDeleteVertexButton.title=h?d?this.messages.tooFewVertices:"":this.messages.notAPolygon,document.addEventListener("click",this.handleOutsideContextMenuClick)}hideContextMenu(){this.contextMenu.hidden=!0,this.contextMenuPoint=void 0,document.removeEventListener("click",this.handleOutsideContextMenuClick)}handleOutsideContextMenuClick=e=>{e.composedPath().includes(this.contextMenu)||e.composedPath().includes(this.decorContextMenu)||(this.hideContextMenu(),this.hideDecorContextMenu())};showDecorContextMenu(e,t,n){this.contextMenuDecorId=n.id,this.decorContextMenu.style.left=`${e}px`,this.decorContextMenu.style.top=`${t}px`,this.decorContextMenu.hidden=!1,this.contextViewTestimonyButton.disabled=!n.sightingUrl,this.contextViewTestimonyButton.title=n.sightingUrl?"":this.messages.noWitnessRecording,this.refreshContextMasksSubmenu(n),document.addEventListener("click",this.handleOutsideContextMenuClick)}hideDecorContextMenu(){this.decorContextMenu.hidden=!0,this.contextMenuDecorId=void 0,document.removeEventListener("click",this.handleOutsideContextMenuClick)}refreshContextMasksSubmenu(e){this.contextMasksSubmenu.innerHTML="";for(const t of this.ufoElement.sighting.timeline.sourceIds){const n=document.createElement("label"),s=document.createElement("input");s.type="checkbox",s.checked=e.occludesSourceIds?.includes(t)??!1,s.addEventListener("change",()=>this.updateDecorOccludesShape(e.id,t,s.checked)),n.appendChild(s),n.appendChild(document.createTextNode(this.shapeLabel(t))),this.contextMasksSubmenu.appendChild(n)}}updateDecorOccludesShape(e,t,n){const s=this.ufoElement.sighting;s.decor=s.decor.map(r=>{if(r.id!==e)return r;const a=n?[...new Set([...r.occludesSourceIds??[],t])]:(r.occludesSourceIds??[]).filter(o=>o!==t);return{...r,occludesSourceIds:a.length>0?a:void 0}}),this.ufoElement.refresh()}viewWitnessTestimony(){const e=this.ufoElement.sighting.decor.find(t=>t.id===this.contextMenuDecorId);this.hideDecorContextMenu(),e?.sightingUrl&&this.importFromUrl(e.sightingUrl)}addVertexAtContextMenu(){const e=this.contextMenuPoint;if(this.hideContextMenu(),!e)return;const t=this.ufoElement.currentTime,n=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(t,this.currentSourceId);if(n?.kind!=="polygon")return;const s=Ke.insertVertexNear(n,e);this.ufoElement.sighting.timeline.addKeyframe(t,[{sourceId:this.currentSourceId,shape:s}]),this.ufoElement.refresh()}deleteVertexAtContextMenu(){const e=this.contextMenuPoint;if(this.hideContextMenu(),!e)return;const t=this.ufoElement.currentTime,n=this.ufoElement.sighting.timeline.getInterpolatedShapeAt(t,this.currentSourceId);if(n?.kind!=="polygon")return;const s=Ke.hitTestVertex(n,e);if(s===void 0)return;const r=Ke.deleteVertex(n,s);this.ufoElement.sighting.timeline.addKeyframe(t,[{sourceId:this.currentSourceId,shape:r}]),this.ufoElement.refresh()}bringSelectedToFront(){const e=this.ufoElement.sighting.timeline;for(const t of e.sourceIds.filter(n=>this.selectedSourceIds.has(n)))e.bringToFront(t);this.hideContextMenu(),this.ufoElement.refresh()}sendSelectedToBack(){const e=this.ufoElement.sighting.timeline;for(const t of e.sourceIds.filter(n=>this.selectedSourceIds.has(n)).reverse())e.sendToBack(t);this.hideContextMenu(),this.ufoElement.refresh()}groupSelected(){this.selectedSourceIds.size<2||(this.ufoElement.sighting.timeline.group([...this.selectedSourceIds]),this.hideContextMenu(),this.ufoElement.refresh())}ungroupSelected(){this.ufoElement.sighting.timeline.ungroup(this.currentSourceId),this.selectedSourceIds=new Set([this.currentSourceId]),this.hideContextMenu(),this.ufoElement.refresh()}beginDrag(e,t,n,s,r){this.dragState={kind:e,sourceId:t,original:n,handle:r,startPointer:s},this.startDragListening()}beginCameraDrag(e){const t=this.isWitnessInsideDecor();this.cameraDragState={startPointer:e,startHeadingDeg:t?this.indoorLookYawDeg:this.numberOrUndefined(this.headingInput.value)??0,startPitchDeg:t?this.indoorLookPitchDeg:this.numberOrUndefined(this.pitchInput.value)??0,insideDecor:t},t||this.sceneElement.setCompassForced(!0),this.setCanvasCursor("panning"),this.startDragListening()}isWitnessInsideDecor(){return this.ufoElement.sighting.decor.some(e=>e.witnessSide!==void 0&&Zs(e.kind))}syncIndoorLookReset(){const e=this.ufoElement.sighting.decor.find(n=>n.witnessSide!==void 0&&Zs(n.kind)),t=e?`${e.id}:${e.witnessSide}`:void 0;t!==this.lastInhabitedKey&&(this.lastInhabitedKey=t,this.indoorLookYawDeg=0,this.indoorLookPitchDeg=0,this.sceneElement.setIndoorLook(0,0))}startDragListening(){document.addEventListener("pointermove",this.handleDragPointerMove),document.addEventListener("pointerup",this.handleDragPointerUp)}onDragPointerMove(e){if(this.cameraDragState){this.onCameraDragPointerMove(e);return}if(!this.dragState)return;const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.currentTime;if(this.dragState.kind==="move"){const{sources:s,startPointer:r}=this.dragState,a=t.x-r.x,o=t.y-r.y,l=s.map(({sourceId:c,original:h})=>({sourceId:c,shape:{...h,bounds:{...h.bounds,x:h.bounds.x+a,y:h.bounds.y+o}}}));this.ufoElement.sighting.timeline.addKeyframe(n,l)}else if(this.dragState.kind==="group-resize"){const{group:s,handle:r}=this.dragState;this.ufoElement.sighting.timeline.addKeyframe(n,s.resize(r,t))}else if(this.dragState.kind==="group-rotate"){const{group:s,startPointer:r}=this.dragState;this.ufoElement.sighting.timeline.addKeyframe(n,s.rotate(t,r))}else if(this.dragState.kind==="vertex"){const{sourceId:s,original:r,vertexIndex:a}=this.dragState,o=Ke.moveVertex(r,a,t);this.ufoElement.sighting.timeline.addKeyframe(n,[{sourceId:s,shape:o}])}else{const{kind:s,sourceId:r,original:a,handle:o}=this.dragState,l=s==="resize"?Ke.resizeShape(a,o,t):Ke.rotateShape(a,t);this.ufoElement.sighting.timeline.addKeyframe(n,[{sourceId:r,shape:l}])}this.ufoElement.refresh()}onCameraDragPointerMove(e){if(!this.cameraDragState)return;const t=this.canvasPointFromEvent(e);if(!t)return;const{startPointer:n,startHeadingDeg:s,startPitchDeg:r,insideDecor:a}=this.cameraDragState,o=s+(t.x-n.x)*Wd,l=Math.max(-90,Math.min(90,r-(t.y-n.y)*Wd));if(a){this.indoorLookYawDeg=o,this.indoorLookPitchDeg=l,this.sceneElement.setIndoorLook(o,l),this.ufoElement.refresh();return}this.headingInput.value=String(o),this.pitchInput.value=String(l),this.updateObserver()}endDrag(){!this.dragState&&!this.cameraDragState||(this.cameraDragState&&!this.cameraDragState.insideDecor&&this.sceneElement.setCompassForced(!1),this.dragState=void 0,this.cameraDragState=void 0,this.setCanvasCursor(this.hoverCursor),document.removeEventListener("pointermove",this.handleDragPointerMove),document.removeEventListener("pointerup",this.handleDragPointerUp))}nudgeBounds(e,t){const n={...e};if(t.shiftKey){switch(t.key){case"ArrowLeft":n.width=Math.max(ai,n.width-ni);break;case"ArrowRight":n.width+=ni;break;case"ArrowUp":n.height=Math.max(ai,n.height-ni);break;case"ArrowDown":n.height+=ni;break}n.x-=(n.width-e.width)/2,n.y-=(n.height-e.height)/2}else switch(t.key){case"ArrowLeft":n.x-=ni;break;case"ArrowRight":n.x+=ni;break;case"ArrowUp":n.y-=ni;break;case"ArrowDown":n.y+=ni;break}return n}moveOrResizeSelectedShapes(e){if(this.isRecording||this.ufoElement.playbackState==="playing")return;const t=this.ufoElement.sighting.timeline,n=this.ufoElement.currentTime,s=this.selectedMembers();if(s.length!==0){if(e.preventDefault(),s.length===1){const{sourceId:r,shape:a}=s[0];t.addKeyframe(n,[{sourceId:r,shape:{...a,bounds:this.nudgeBounds(a.bounds,e)}}])}else{const r=new za(s),a=this.nudgeBounds(r.bounds(),e);t.addKeyframe(n,r.scaleTo(a))}this.ufoElement.refresh()}}updateHoverCursor(e){this.isRecording||this.dragState||this.cameraDragState||(this.hoverCursor=this.hoverCursorAt(e),this.setCanvasCursor(this.hoverCursor))}hoverCursorAt(e){const t=this.canvasPointFromEvent(e);if(!t)return;const n=this.ufoElement.sighting.timeline,s=this.ufoElement.currentTime,r=this.ufoElement.playbackState!=="playing";if(r)if(this.selectedSourceIds.size>1){const a=Ke.hitTestHandle({bounds:new za(this.selectedMembers()).bounds(),angle:0},t);if(a)return this.cursorForHandle(a,0)}else{const a=n.getInterpolatedShapeAt(s,this.currentSourceId);if(a?.kind==="polygon"&&Ke.hitTestVertex(a,t)!==void 0)return"vertex";const o=a&&Ke.hitTestHandle(a,t);if(a&&o)return this.cursorForHandle(o,a.angle)}return n.hitTest(s,t.x,t.y)?r?"move":"select":this.pickDecorAt(e)!==void 0?"select":r?"pan":void 0}cursorForHandle(e,t){return e==="rotate"?"rotate":`resize-${Ke.resizeAxisFor(e,t)}`}setCanvasCursor(e){const t=this.ufoElement.canvasElement;e?t.dataset.cursor=e:delete t.dataset.cursor}onPointerMove(e){if(!this.isRecording){this.updateHoverCursor(e);return}const t=this.ufoElement.canvasElement,n=t.getBoundingClientRect(),s=e.clientX-n.left,r=e.clientY-n.top;this.recorder?.onPointerMove(s,r),this.ufoElement.renderer.clear(t.width,t.height),this.ufoElement.renderer.paintShape(Kd(this.buildPrototype({x:0,y:0,width:ls.width,height:ls.height}),s,r))}canvasPointFromEvent(e){const t=this.ufoElement.canvasElement,n=t.getBoundingClientRect();if(!(n.width===0||n.height===0))return{x:(e.clientX-n.left)/n.width*t.width,y:(e.clientY-n.top)/n.height*t.height}}}const $d="rr0-ufo-recorder";function yy(){Ra(),customElements.get($d)||customElements.define($d,Sy)}yy();