@rr0/ufoathome 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -221,21 +221,24 @@ block) — likely means unrelated recordings got listed together by mistake.
|
|
|
221
221
|
| `witnessUrls` | property (get/set) | The manifest as a plain array of URLs, for programmatic use instead of `src` |
|
|
222
222
|
| `loadFromSrc(url)` | method (async) | What the `src` attribute triggers internally; can be called directly too |
|
|
223
223
|
|
|
224
|
-
A toolbar row sits above the scene
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
224
|
+
A toolbar row sits above the scene: a "Testimony by <witness>" sentence on the left, and a round "?" info
|
|
225
|
+
button on the right. The witness portion is plain text for a single witness (a one-option `<select>` would be
|
|
226
|
+
pointless); once there's more than one, it becomes the live `<select>` instead — but the sentence itself, and the
|
|
227
|
+
info button, stay visible either way. The first witness loads automatically once the list is known; switching the
|
|
228
|
+
selector loads that witness's already-fetched recording into the nested `<rr0-scene>` (no re-fetch). Setting
|
|
229
|
+
`witnessUrls` again (e.g. a manifest refresh) keeps the current selection if that witness is still present, instead
|
|
230
|
+
of resetting back to the first.
|
|
231
|
+
|
|
232
|
+
Clicking "?" opens a panel, anchored to the button as a floating overlay (it never shifts the canvas below it).
|
|
233
|
+
Its main content is the currently-selected witness's observation metadata (date, location, case id — whichever are
|
|
234
|
+
actually present in that witness's own `sighting.json`; the witness's own name isn't repeated here, since it's
|
|
235
|
+
already in the toolbar's testimony line). Below that, a smaller footer row holds the app's own name/version on the
|
|
236
|
+
left, linking to [ufoathome.org](https://ufoathome.org), and a "Credits" link on the right that reveals third-party
|
|
237
|
+
credits on click (the live terrain imagery attribution, once a real relief patch has resolved, plus the bundled
|
|
238
|
+
thunder sound's own required attribution — see [`CREDITS.md`](CREDITS.md)).
|
|
239
|
+
|
|
240
|
+
All of this component's own labels (Testimony by, About, Close, Observation/Date/Location/Case, Credits) are
|
|
241
|
+
translated (English/French) the same way as `<rr0-ufo>`'s own labels.
|
|
239
242
|
|
|
240
243
|
## Data format
|
|
241
244
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={testimonyBy:"Témoignage de",about:"À propos",close:"Fermer",observation:"Observation",date:"Date",location:"Lieu",case:"Dossier",credits:"Crédits"};export{e as eyewitnessMessages_fr};
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
const Ku=`
|
|
2
2
|
<div class="toolbar" id="toolbar" hidden>
|
|
3
|
-
<
|
|
3
|
+
<span id="testimony" class="testimony">
|
|
4
|
+
<span id="testimony-prefix">Testimony by</span>
|
|
5
|
+
<span id="witness-text"></span><select id="witness" hidden></select>
|
|
6
|
+
</span>
|
|
4
7
|
<button id="info-button" class="info-btn" type="button" title="About" aria-label="About" aria-expanded="false">?</button>
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<ul id="info-credits-list" class="info-ul"></ul>
|
|
16
|
-
</
|
|
8
|
+
<div id="info-panel" class="info-panel" hidden>
|
|
9
|
+
<button id="info-close" class="info-close" type="button" aria-label="Close">×</button>
|
|
10
|
+
<section>
|
|
11
|
+
<h3 id="info-observation-heading">Observation</h3>
|
|
12
|
+
<dl id="info-observation-list" class="info-dl"></dl>
|
|
13
|
+
</section>
|
|
14
|
+
<div class="info-footer">
|
|
15
|
+
<a id="info-app-link" href="https://ufoathome.org" target="_blank" rel="noopener"></a>
|
|
16
|
+
<button id="info-credits-toggle" class="info-credits-toggle" type="button" aria-expanded="false">Credits</button>
|
|
17
|
+
</div>
|
|
18
|
+
<ul id="info-credits-list" class="info-ul" hidden></ul>
|
|
19
|
+
</div>
|
|
17
20
|
</div>
|
|
18
21
|
<div id="ufo-slot"></div>
|
|
19
22
|
`,Ju=`
|
|
@@ -22,16 +25,28 @@ const Ku=`
|
|
|
22
25
|
font-family: sans-serif;
|
|
23
26
|
}
|
|
24
27
|
/* Normal document flow, above the canvas — unlike the video player's own toolbar (ufoTemplate.ts),
|
|
25
|
-
this
|
|
26
|
-
once there's something to show, it just stays visible like any other page content.
|
|
28
|
+
this row itself never overlays the scene, so it needs none of that toolbar's hover/auto-hide
|
|
29
|
+
dance: once there's something to show, it just stays visible like any other page content. Its
|
|
30
|
+
own info panel (below) is the one thing that overlays — anchored to this row via
|
|
31
|
+
position:relative — so opening it never shifts the canvas or the rest of the page. */
|
|
27
32
|
.toolbar {
|
|
33
|
+
position: relative;
|
|
28
34
|
display: flex;
|
|
29
35
|
align-items: center;
|
|
30
|
-
justify-content: space-between;
|
|
31
36
|
gap: 0.5em;
|
|
32
37
|
margin-bottom: 0.5em;
|
|
33
38
|
}
|
|
39
|
+
.testimony {
|
|
40
|
+
min-width: 0;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
}
|
|
45
|
+
.testimony select {
|
|
46
|
+
max-width: 12em;
|
|
47
|
+
}
|
|
34
48
|
.info-btn {
|
|
49
|
+
margin-left: auto;
|
|
35
50
|
flex-shrink: 0;
|
|
36
51
|
width: 1.6em;
|
|
37
52
|
height: 1.6em;
|
|
@@ -45,8 +60,11 @@ const Ku=`
|
|
|
45
60
|
padding: 0;
|
|
46
61
|
}
|
|
47
62
|
.info-panel {
|
|
48
|
-
position:
|
|
49
|
-
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 100%;
|
|
65
|
+
right: 0;
|
|
66
|
+
z-index: 2;
|
|
67
|
+
margin-top: 0.3em;
|
|
50
68
|
padding: 0.6em 0.8em;
|
|
51
69
|
border: 1px solid #ccc;
|
|
52
70
|
border-radius: 4px;
|
|
@@ -54,6 +72,7 @@ const Ku=`
|
|
|
54
72
|
color: #222;
|
|
55
73
|
max-width: 28em;
|
|
56
74
|
font-size: 0.9em;
|
|
75
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
|
57
76
|
}
|
|
58
77
|
.info-close {
|
|
59
78
|
position: absolute;
|
|
@@ -69,12 +88,8 @@ const Ku=`
|
|
|
69
88
|
line-height: 1;
|
|
70
89
|
padding: 0;
|
|
71
90
|
}
|
|
72
|
-
.info-app {
|
|
73
|
-
margin: 0 1.6em 0.5em 0;
|
|
74
|
-
font-weight: bold;
|
|
75
|
-
}
|
|
76
91
|
.info-panel h3 {
|
|
77
|
-
margin: 0
|
|
92
|
+
margin: 0 0 0.2em;
|
|
78
93
|
font-size: 0.95em;
|
|
79
94
|
}
|
|
80
95
|
.info-dl {
|
|
@@ -89,8 +104,33 @@ const Ku=`
|
|
|
89
104
|
.info-dl dd {
|
|
90
105
|
margin: 0;
|
|
91
106
|
}
|
|
107
|
+
/* The smaller, secondary row below the observation details — app identity on the left, the
|
|
108
|
+
credits reveal on the right, matching the reduced visual weight of "fine print" rather than
|
|
109
|
+
competing with the sighting's own metadata for attention. */
|
|
110
|
+
.info-footer {
|
|
111
|
+
display: flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
justify-content: space-between;
|
|
114
|
+
gap: 0.8em;
|
|
115
|
+
margin-top: 0.6em;
|
|
116
|
+
padding-top: 0.4em;
|
|
117
|
+
border-top: 1px solid #eee;
|
|
118
|
+
font-size: 0.8em;
|
|
119
|
+
}
|
|
120
|
+
#info-app-link {
|
|
121
|
+
color: #555;
|
|
122
|
+
}
|
|
123
|
+
.info-credits-toggle {
|
|
124
|
+
border: none;
|
|
125
|
+
background: none;
|
|
126
|
+
padding: 0;
|
|
127
|
+
color: #06c;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
font-size: 1em;
|
|
130
|
+
text-decoration: underline;
|
|
131
|
+
}
|
|
92
132
|
.info-ul {
|
|
93
|
-
margin: 0;
|
|
133
|
+
margin: 0.4em 0 0;
|
|
94
134
|
padding-left: 1.2em;
|
|
95
135
|
}
|
|
96
136
|
`,Qu=`
|
|
@@ -300,7 +340,7 @@ input[type=range] {
|
|
|
300
340
|
min-width: 3em;
|
|
301
341
|
text-align: center;
|
|
302
342
|
}
|
|
303
|
-
`;function n0(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 Yt(i,e,t){return i+(e-i)*t}function Fl(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 i0(i){return`#${i.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function s0(i,e,t){const n=Fl(i),s=Fl(e);return!n||!s?t<1?i:e:i0([Yt(n[0],s[0],t),Yt(n[1],s[1],t),Yt(n[2],s[2],t)])}function r0(i,e,t){const n={x:Yt(i.bounds.x,e.bounds.x,t),y:Yt(i.bounds.y,e.bounds.y,t),width:Yt(i.bounds.width,e.bounds.width,t),height:Yt(i.bounds.height,e.bounds.height,t)},s=Yt(i.angle,e.angle,t),r=Yt(i.transparency,e.transparency,t),a=Yt(i.haloScale,e.haloScale,t),o=s0(i.color,e.color,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,points:i.points.map((l,c)=>({x:Yt(l.x,e.points[c].x,t),y:Yt(l.y,e.points[c].y,t)}))}:{...t<1?i:e,bounds:n,angle:s,transparency:r,haloScale:a,color:o}}class Vr{keyframes=[];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]})}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?r0(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){const s=this.sourceIds;for(let r=s.length-1;r>=0;r--){const a=this.getInterpolatedShapeAt(e,s[r]);if(a&&n0(a,t,n))return{sourceId:s[r],shape:a}}}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){const e=new Set;for(const t of this.keyframes)for(const n of t.shapes)e.add(n.sourceId);return[...e]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(e){const t=new Vr;for(const n of e.keyframes)t.addKeyframe(n.t,n.shapes);return t}}function a0(i,e,t){return Math.max(e,Math.min(t,i))}function o0(i,e,t){const n=((e-i)%360+540)%360-180;return((i+n*t)%360+360)%360}function es(i,e,t){return i+(e-i)*t}function l0(i,e,t){return{lat:i.lat===void 0||e.lat===void 0?void 0:es(i.lat,e.lat,t),lng:i.lng===void 0||e.lng===void 0?void 0:es(i.lng,e.lng,t),elevationM:es(i.elevationM,e.elevationM,t),headingDeg:i.headingDeg===void 0||e.headingDeg===void 0?void 0:o0(i.headingDeg,e.headingDeg,t),pitchDeg:es(i.pitchDeg,e.pitchDeg,t),fovDeg:es(i.fovDeg,e.fovDeg,t)}}class Ms{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?l0(n.pose,s.pose,a0((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 Ms;for(const n of e.keyframes)t.addKeyframe(n.t,n.pose);return t}}function Xa(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 c0(i){if(i.durationSeconds!==void 0)return i.durationSeconds*1e3;const e=i.time?Xa(i.time):void 0,t=i.endTime?Xa(i.endTime):void 0;return e!==void 0&&t!==void 0?t-e:void 0}class Wr{constructor(e,t,n,s,r,a,o){this.event=e,this.timeline=t,this.observerTrack=n,this.witnessId=s,this.witnessName=r,this.caseId=a,this.weather=o}static create(e,t,n){return new Wr({eventType:"sighting",time:e,place:t},new Vr,new Ms,n)}}const h0=0,u0=0,d0=60;function f0(i,e){const t=i.observerTrack.getInterpolatedPoseAt(e);if(t)return t;const n=i.event.place?.[0];if(n)return{lat:n.lat,lng:n.lng,elevationM:h0,headingDeg:void 0,pitchDeg:u0,fovDeg:d0}}class p0{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 m0=["nw","n","ne","e","se","s","sw","w"],g0=24;function _0(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 v0(i){return{x:i.x+i.width/2,y:i.y+i.height/2}}function x0(i){const{x:e,y:t,width:n,height:s}=i.bounds,r={nw:{x:e,y:t},n:{x:e+n/2,y:t},ne:{x:e+n,y:t},e:{x:e+n,y:t+s/2},se:{x:e+n,y:t+s},s:{x:e+n/2,y:t+s},sw:{x:e,y:t+s},w:{x:e,y:t+s/2},rotate:{x:e+n/2,y:t-g0}},a=v0(i.bounds),o={};for(const l of Object.keys(r))o[l]=_0(r[l],a,i.angle);return o}const qa=6,Cs=qa/2,M0=20;class S0{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=M0*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){const t=x0(e);this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(t.nw.x,t.nw.y),this.ctx.lineTo(t.ne.x,t.ne.y),this.ctx.lineTo(t.se.x,t.se.y),this.ctx.lineTo(t.sw.x,t.sw.y),this.ctx.closePath(),this.ctx.stroke(),this.ctx.fillStyle="lightgray";for(const n of m0){const s=t[n];this.ctx.fillRect(s.x-Cs,s.y-Cs,qa,qa)}this.ctx.beginPath(),this.ctx.moveTo(t.n.x,t.n.y),this.ctx.lineTo(t.rotate.x,t.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(t.rotate.x,t.rotate.y,Cs+1,Cs+1,0,0,2*Math.PI),this.ctx.fill()}}function y0(i){return{version:1,time:i.event.time,endTime:i.event.endTime,durationSeconds:i.event.durationSeconds,place:i.event.place,witnessId:i.witnessId,witnessName:i.witnessName,caseId:i.caseId,timeline:i.timeline.toJSON(),observerTrack:i.observerTrack.toJSON(),weather:i.weather}}function E0(i){return new Wr({eventType:"sighting",time:i.time,endTime:i.endTime,durationSeconds:i.durationSeconds,place:i.place},Vr.fromJSON(i.timeline),i.observerTrack?Ms.fromJSON(i.observerTrack):new Ms,i.witnessId,i.witnessName,i.caseId,i.weather)}function ll(i,e){for(const t of i){const n=t.toLowerCase().split("-")[0];if(e.includes(n))return n}return"en"}const b0="modulepreload",T0=function(i,e){return new URL(i,e).href},Ol={},Rr=function(e,t,n){let s=Promise.resolve();if(t&&t.length>0){let a=function(h){return Promise.all(h.map(d=>Promise.resolve(d).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};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=T0(h,n),h in Ol)return;Ol[h]=!0;const d=h.endsWith(".css"),u=d?'[rel="stylesheet"]':"";if(!!n)for(let S=o.length-1;S>=0;S--){const m=o[S];if(m.href===h&&(!d||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${h}"]${u}`))return;const _=document.createElement("link");if(_.rel=d?"stylesheet":b0,d||(_.as="script"),_.crossOrigin="",_.href=h,c&&_.setAttribute("nonce",c),document.head.appendChild(_),d)return new Promise((S,m)=>{_.addEventListener("load",S),_.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)})},wh=["en","fr"],A0={en:()=>Rr(()=>Promise.resolve().then(()=>P0),void 0,import.meta.url).then(i=>i.ufoMessages_en),fr:()=>Rr(()=>import("./UfoMessages_fr-COCxf8Q_.js"),[],import.meta.url).then(i=>i.ufoMessages_fr)};function w0(i){return A0[i]()}const R0={en:()=>Rr(()=>Promise.resolve().then(()=>Hx),void 0,import.meta.url).then(i=>i.eyewitnessMessages_en),fr:()=>Rr(()=>import("./EyewitnessMessages_fr-DrCqtB3O.js"),[],import.meta.url).then(i=>i.eyewitnessMessages_fr)};function C0(i){return R0[i]()}const Rh={play:"Play",pause:"Pause",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen"},P0=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Rh},Symbol.toStringTag,{value:"Module"}));class I0 extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=Wr.create();player;loopEnabled=!0;highlightedSourceId;enableClickToPlay=!0;fullscreenTarget;messages=Rh;realDurationMs;realStartMs;handleFullscreenChange=()=>this.updateFullscreenButton();constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${t0}</style>${e0}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new S0(this.canvas.getContext("2d")),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()}),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 y0(this.currentSighting)}set sightingData(e){this.currentSighting=E0(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}get playbackState(){return this.player.playbackState}get selectedSourceId(){return this.highlightedSourceId}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh()}set selectedSourceId(e){e!==this.highlightedSourceId&&(this.highlightedSourceId=e,this.refresh())}refresh(){this.seekInput.max=String(this.player.seekableDuration),this.player.seek(this.player.time)}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const n=this.playbackState!=="playing";for(const[s,r]of t){const a=n&&s===this.highlightedSourceId;this.canvasRenderer.paintShape(a?{...r,selected:!0}:r)}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 p0(this.currentSighting.timeline,(t,n)=>this.onFrame(t,n));return e.loop=this.loopEnabled,e}togglePlayPause(){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?"⏸":"▶",this.playPauseButton.title=e?this.messages.pause:this.messages.play,this.playPauseButton.setAttribute("aria-label",e?this.messages.pause:this.messages.play),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=ll(navigator.languages,wh);e!=="en"&&this.applyMessages(await w0(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=c0(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?Xa(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=this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time)}formatPosition(e){if(this.realDurationMs===void 0)return Ps(e);const t=this.currentSighting.timeline.duration,n=t>0&&e<=t?e/t*this.realDurationMs:e;return this.realStartMs!==void 0?zl(Bl(this.realStartMs+n)):Ps(n)}formatEndOfTimeline(){return this.realDurationMs===void 0?Ps(this.currentSighting.timeline.duration):this.realStartMs!==void 0?zl(Bl(this.realStartMs+this.realDurationMs)):Ps(this.realDurationMs)}}function Bl(i){const e=new Date(i);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function zl(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 Ps(i){const e=Math.round(i/1e3),t=Math.floor(e/60),n=e%60;return`${t}:${String(n).padStart(2,"0")}`}const Ya="rr0-ufo";function Ch(){customElements.get(Ya)||customElements.define(Ya,I0)}/**
|
|
343
|
+
`;function n0(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 Yt(i,e,t){return i+(e-i)*t}function Fl(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 i0(i){return`#${i.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`}function s0(i,e,t){const n=Fl(i),s=Fl(e);return!n||!s?t<1?i:e:i0([Yt(n[0],s[0],t),Yt(n[1],s[1],t),Yt(n[2],s[2],t)])}function r0(i,e,t){const n={x:Yt(i.bounds.x,e.bounds.x,t),y:Yt(i.bounds.y,e.bounds.y,t),width:Yt(i.bounds.width,e.bounds.width,t),height:Yt(i.bounds.height,e.bounds.height,t)},s=Yt(i.angle,e.angle,t),r=Yt(i.transparency,e.transparency,t),a=Yt(i.haloScale,e.haloScale,t),o=s0(i.color,e.color,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,points:i.points.map((l,c)=>({x:Yt(l.x,e.points[c].x,t),y:Yt(l.y,e.points[c].y,t)}))}:{...t<1?i:e,bounds:n,angle:s,transparency:r,haloScale:a,color:o}}class Vr{keyframes=[];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]})}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?r0(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){const s=this.sourceIds;for(let r=s.length-1;r>=0;r--){const a=this.getInterpolatedShapeAt(e,s[r]);if(a&&n0(a,t,n))return{sourceId:s[r],shape:a}}}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){const e=new Set;for(const t of this.keyframes)for(const n of t.shapes)e.add(n.sourceId);return[...e]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(e){const t=new Vr;for(const n of e.keyframes)t.addKeyframe(n.t,n.shapes);return t}}function a0(i,e,t){return Math.max(e,Math.min(t,i))}function o0(i,e,t){const n=((e-i)%360+540)%360-180;return((i+n*t)%360+360)%360}function es(i,e,t){return i+(e-i)*t}function l0(i,e,t){return{lat:i.lat===void 0||e.lat===void 0?void 0:es(i.lat,e.lat,t),lng:i.lng===void 0||e.lng===void 0?void 0:es(i.lng,e.lng,t),elevationM:es(i.elevationM,e.elevationM,t),headingDeg:i.headingDeg===void 0||e.headingDeg===void 0?void 0:o0(i.headingDeg,e.headingDeg,t),pitchDeg:es(i.pitchDeg,e.pitchDeg,t),fovDeg:es(i.fovDeg,e.fovDeg,t)}}class Ms{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?l0(n.pose,s.pose,a0((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 Ms;for(const n of e.keyframes)t.addKeyframe(n.t,n.pose);return t}}function Xa(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 c0(i){if(i.durationSeconds!==void 0)return i.durationSeconds*1e3;const e=i.time?Xa(i.time):void 0,t=i.endTime?Xa(i.endTime):void 0;return e!==void 0&&t!==void 0?t-e:void 0}class Wr{constructor(e,t,n,s,r,a,o){this.event=e,this.timeline=t,this.observerTrack=n,this.witnessId=s,this.witnessName=r,this.caseId=a,this.weather=o}static create(e,t,n){return new Wr({eventType:"sighting",time:e,place:t},new Vr,new Ms,n)}}const h0=0,u0=0,d0=60;function f0(i,e){const t=i.observerTrack.getInterpolatedPoseAt(e);if(t)return t;const n=i.event.place?.[0];if(n)return{lat:n.lat,lng:n.lng,elevationM:h0,headingDeg:void 0,pitchDeg:u0,fovDeg:d0}}class p0{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 m0=["nw","n","ne","e","se","s","sw","w"],g0=24;function _0(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 v0(i){return{x:i.x+i.width/2,y:i.y+i.height/2}}function x0(i){const{x:e,y:t,width:n,height:s}=i.bounds,r={nw:{x:e,y:t},n:{x:e+n/2,y:t},ne:{x:e+n,y:t},e:{x:e+n,y:t+s/2},se:{x:e+n,y:t+s},s:{x:e+n/2,y:t+s},sw:{x:e,y:t+s},w:{x:e,y:t+s/2},rotate:{x:e+n/2,y:t-g0}},a=v0(i.bounds),o={};for(const l of Object.keys(r))o[l]=_0(r[l],a,i.angle);return o}const qa=6,Cs=qa/2,M0=20;class S0{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=M0*e.haloScale,this.paintBase(e),this.ctx.restore()}paintSelectionHandles(e){const t=x0(e);this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(t.nw.x,t.nw.y),this.ctx.lineTo(t.ne.x,t.ne.y),this.ctx.lineTo(t.se.x,t.se.y),this.ctx.lineTo(t.sw.x,t.sw.y),this.ctx.closePath(),this.ctx.stroke(),this.ctx.fillStyle="lightgray";for(const n of m0){const s=t[n];this.ctx.fillRect(s.x-Cs,s.y-Cs,qa,qa)}this.ctx.beginPath(),this.ctx.moveTo(t.n.x,t.n.y),this.ctx.lineTo(t.rotate.x,t.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(t.rotate.x,t.rotate.y,Cs+1,Cs+1,0,0,2*Math.PI),this.ctx.fill()}}function y0(i){return{version:1,time:i.event.time,endTime:i.event.endTime,durationSeconds:i.event.durationSeconds,place:i.event.place,witnessId:i.witnessId,witnessName:i.witnessName,caseId:i.caseId,timeline:i.timeline.toJSON(),observerTrack:i.observerTrack.toJSON(),weather:i.weather}}function E0(i){return new Wr({eventType:"sighting",time:i.time,endTime:i.endTime,durationSeconds:i.durationSeconds,place:i.place},Vr.fromJSON(i.timeline),i.observerTrack?Ms.fromJSON(i.observerTrack):new Ms,i.witnessId,i.witnessName,i.caseId,i.weather)}function ll(i,e){for(const t of i){const n=t.toLowerCase().split("-")[0];if(e.includes(n))return n}return"en"}const b0="modulepreload",T0=function(i,e){return new URL(i,e).href},Ol={},Rr=function(e,t,n){let s=Promise.resolve();if(t&&t.length>0){let a=function(h){return Promise.all(h.map(d=>Promise.resolve(d).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};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=T0(h,n),h in Ol)return;Ol[h]=!0;const d=h.endsWith(".css"),u=d?'[rel="stylesheet"]':"";if(!!n)for(let S=o.length-1;S>=0;S--){const m=o[S];if(m.href===h&&(!d||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${h}"]${u}`))return;const _=document.createElement("link");if(_.rel=d?"stylesheet":b0,d||(_.as="script"),_.crossOrigin="",_.href=h,c&&_.setAttribute("nonce",c),document.head.appendChild(_),d)return new Promise((S,m)=>{_.addEventListener("load",S),_.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)})},wh=["en","fr"],A0={en:()=>Rr(()=>Promise.resolve().then(()=>P0),void 0,import.meta.url).then(i=>i.ufoMessages_en),fr:()=>Rr(()=>import("./UfoMessages_fr-COCxf8Q_.js"),[],import.meta.url).then(i=>i.ufoMessages_fr)};function w0(i){return A0[i]()}const R0={en:()=>Rr(()=>Promise.resolve().then(()=>Hx),void 0,import.meta.url).then(i=>i.eyewitnessMessages_en),fr:()=>Rr(()=>import("./EyewitnessMessages_fr-DRWDa8m_.js"),[],import.meta.url).then(i=>i.eyewitnessMessages_fr)};function C0(i){return R0[i]()}const Rh={play:"Play",pause:"Pause",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen"},P0=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Rh},Symbol.toStringTag,{value:"Module"}));class I0 extends HTMLElement{static get observedAttributes(){return["src"]}shadow;stageElement;canvas;canvasRenderer;toolbar;playPauseButton;loopButton;fullscreenButton;seekInput;timeStartLabel;timeEndLabel;currentSighting=Wr.create();player;loopEnabled=!0;highlightedSourceId;enableClickToPlay=!0;fullscreenTarget;messages=Rh;realDurationMs;realStartMs;handleFullscreenChange=()=>this.updateFullscreenButton();constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${t0}</style>${e0}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new S0(this.canvas.getContext("2d")),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()}),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 y0(this.currentSighting)}set sightingData(e){this.currentSighting=E0(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}get playbackState(){return this.player.playbackState}get selectedSourceId(){return this.highlightedSourceId}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(e){this.currentSighting.event.durationSeconds=e,this.updateTimeLabels(),this.refresh()}set selectedSourceId(e){e!==this.highlightedSourceId&&(this.highlightedSourceId=e,this.refresh())}refresh(){this.seekInput.max=String(this.player.seekableDuration),this.player.seek(this.player.time)}onFrame(e,t){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const n=this.playbackState!=="playing";for(const[s,r]of t){const a=n&&s===this.highlightedSourceId;this.canvasRenderer.paintShape(a?{...r,selected:!0}:r)}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 p0(this.currentSighting.timeline,(t,n)=>this.onFrame(t,n));return e.loop=this.loopEnabled,e}togglePlayPause(){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?"⏸":"▶",this.playPauseButton.title=e?this.messages.pause:this.messages.play,this.playPauseButton.setAttribute("aria-label",e?this.messages.pause:this.messages.play),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=ll(navigator.languages,wh);e!=="en"&&this.applyMessages(await w0(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=c0(e);this.realDurationMs=t!==void 0&&t>0?t:void 0,this.realStartMs=e.time?Xa(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=this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time)}formatPosition(e){if(this.realDurationMs===void 0)return Ps(e);const t=this.currentSighting.timeline.duration,n=t>0&&e<=t?e/t*this.realDurationMs:e;return this.realStartMs!==void 0?zl(Bl(this.realStartMs+n)):Ps(n)}formatEndOfTimeline(){return this.realDurationMs===void 0?Ps(this.currentSighting.timeline.duration):this.realStartMs!==void 0?zl(Bl(this.realStartMs+this.realDurationMs)):Ps(this.realDurationMs)}}function Bl(i){const e=new Date(i);return{hour:e.getUTCHours(),minute:e.getUTCMinutes(),second:e.getUTCSeconds()}}function zl(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 Ps(i){const e=Math.round(i/1e3),t=Math.floor(e/60),n=e%60;return`${t}:${String(n).padStart(2,"0")}`}const Ya="rr0-ufo";function Ch(){customElements.get(Ya)||customElements.define(Ya,I0)}/**
|
|
304
344
|
* @license
|
|
305
345
|
* Copyright 2010-2026 Three.js Authors
|
|
306
346
|
* SPDX-License-Identifier: MIT
|
|
@@ -4714,4 +4754,4 @@ void main() {
|
|
|
4714
4754
|
float blur = max(vNearBlur, vHaze);
|
|
4715
4755
|
gl_FragColor = vec4(color, tex.a * uOpacity * (1.0 - blur * 0.5));
|
|
4716
4756
|
}
|
|
4717
|
-
`;let xr;function wx(){if(xr)return xr;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),xr=new Zn(e),xr}function Rx(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=Gu(i.illuminatedFraction,0,1),h=i.phaseFraction<.5,d=s*Math.abs(1-2*c),u=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,d,s,0,Math.PI/2,-Math.PI/2,u)):(n.arc(r,a,s,Math.PI/2,-Math.PI/2,!1),n.ellipse(r,a,d,s,0,-Math.PI/2,Math.PI/2,u)),n.fill(),n.restore(),new Zn(t)}function Cx(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 Zn(e)}function Va(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 Th=new Map;function Px(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 Ix(i){let e=Th.get(i);return e||(e=fetch(i).then(t=>t.arrayBuffer()).then(Px),Th.set(i,e)),e}const Dx=new URL(""+new URL("rain-BvWZrB9h.ogg",import.meta.url).href,import.meta.url).href,Lx=new URL(""+new URL("wind-BjklexY7.ogg",import.meta.url).href,import.meta.url).href,Ux=new URL(""+new URL("thunder-DA48qs_v.wav",import.meta.url).href,import.meta.url).href;class Nx{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(Dx,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=n;const r=n>.03;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(Lx,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(Ux,.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()}}Ch();const Fx={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"}},Ox=["en","fr"],Bx=new URL(""+new URL("stars-mag7.5-DDr9QasD.bin",import.meta.url).href,import.meta.url).href,zx={sun:{altitudeDeg:-3,azimuthDeg:180,magnitude:-26.7},moon:{altitudeDeg:-90,azimuthDeg:0,phase:{phaseFraction:0,illuminatedFraction:0},magnitude:-12.7},planets:[]},Wa={lat:0,lng:0,elevationM:0,headingDeg:void 0,pitchDeg:0,fovDeg:60};class Gx extends HTMLElement{static get observedAttributes(){return["src","star-catalog-src","show-compass"]}shadow;stageElement;frameElement;sceneCanvas;ufoElement;sceneRenderer;bodyTooltip;resizeObserver;lastTimeMs=0;starCatalog;weatherAudio=new Nx;thunderTimeoutId;handleFullscreenChange=()=>this.resizeToStage();handlePointerMove=e=>{this.sceneRenderer.setCompassHovered(!0);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),a=this.sceneRenderer.pickBodyAt(s,r);if(!a){this.bodyTooltip.hidden=!0;return}const o=ll(navigator.languages,Ox);this.bodyTooltip.textContent=Fx[a]?.[o]??a,this.bodyTooltip.hidden=!1;const l=this.stageElement.getBoundingClientRect();this.bodyTooltip.style.left=`${e.clientX-l.left+12}px`,this.bodyTooltip.style.top=`${e.clientY-l.top+12}px`};handlePointerLeave=()=>{this.bodyTooltip.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(this.ufoElement.sighting.weather)};handleTimeUpdate=e=>{this.lastTimeMs=e.detail.time,this.updateAstronomy(this.lastTimeMs)};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${ju}</style>${Qu}`,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 vx(this.sceneCanvas,void 0,this.handleLightningFlash),this.bodyTooltip=this.shadow.getElementById("body-tooltip"),this.ufoElement=document.createElement(Ya),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){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"))}setCompassForced(e){this.sceneRenderer.setCompassForced(e)}setWeather(e){const t=e??zu;this.sceneRenderer.setWeather(t),this.weatherAudio.setAmbient(t.precipitationType,t.precipitationIntensity,t.windSpeed)}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),this.setWeather(this.ufoElement.sighting.weather)}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")??Bx;this.starCatalog=await Ix(e),this.updateAstronomy(this.lastTimeMs)}updateAstronomy(e){const t=this.ufoElement.sighting,n=f0(t,e);this.sceneRenderer.setObserverPose(n??Wa),this.sceneRenderer.setTerrainOrigin(n?.lat,n?.lng);const s=n?.lat??Wa.lat,r=n?.lng??Wa.lng,a=Lu(t.event.time??{},r);if(!a){this.sceneRenderer.setAstronomy(zx);return}const o=new Date(a.getTime()+e),l={lat:s,lng:r,elevationM:n?.elevationM??0},c={...Oa("Sun",o,l),magnitude:Ba("Sun",o)},h={...Oa("Moon",o,l),phase:cv(o),magnitude:Ba("Moon",o)},d=ov.map(u=>({body:u,position:Oa(u,o,l),magnitude:Ba(u,o)}));this.sceneRenderer.setAstronomy({sun:c,moon:h,planets:d,stars:this.starCatalog?{catalog:this.starCatalog,date:o,observer:l}:void 0})}}const ol="rr0-scene";function Hu(){Ch(),customElements.get(ol)||customElements.define(ol,Gx)}const ku={witness:"Witness",about:"About",close:"Close",observation:"Observation",date:"Date",location:"Location",case:"Case",credits:"Credits"},Hx=Object.freeze(Object.defineProperty({__proto__:null,eyewitnessMessages_en:ku},Symbol.toStringTag,{value:"Module"}));Hu();const kx="“Thunder” by Jerimee",Vx="https://creativecommons.org/licenses/by/3.0/",Wx="CC BY 3.0",Xx="https://ufoathome.org";class qx extends HTMLElement{static get observedAttributes(){return["src"]}shadow;sceneElement;toolbarElement;witnessPicker;witnessSelect;labelWitness;infoButton;infoPanel;infoAppLink;infoObservationHeading;infoObservationList;infoCreditsHeading;infoCreditsList;infoCloseButton;entries=[];currentSrc;infoOpen=!1;language="en";messages=ku;constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Ju}</style>${Ku}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.sceneElement=document.createElement(ol),this.shadow.getElementById("ufo-slot").replaceWith(this.sceneElement),this.toolbarElement=this.shadow.getElementById("toolbar"),this.witnessPicker=this.shadow.getElementById("witness-picker"),this.witnessSelect=this.shadow.getElementById("witness"),this.labelWitness=this.shadow.getElementById("label-witness"),this.infoButton=this.shadow.getElementById("info-button"),this.infoPanel=this.shadow.getElementById("info-panel"),this.infoAppLink=this.shadow.getElementById("info-app-link"),this.infoObservationHeading=this.shadow.getElementById("info-observation-heading"),this.infoObservationList=this.shadow.getElementById("info-observation-list"),this.infoCreditsHeading=this.shadow.getElementById("info-credits-heading"),this.infoCreditsList=this.shadow.getElementById("info-credits-list"),this.infoCloseButton=this.shadow.getElementById("info-close"),this.witnessSelect.addEventListener("change",()=>this.selectWitness(this.witnessSelect.value)),this.infoButton.addEventListener("click",()=>this.toggleInfoPanel()),this.infoCloseButton.addEventListener("click",()=>this.toggleInfoPanel()),this.loadLocaleMessages()}async loadLocaleMessages(){this.language=ll(navigator.languages,wh),this.language!=="en"&&(this.messages=await C0(this.language),this.labelWitness.textContent=this.messages.witness,this.infoButton.title=this.messages.about,this.infoButton.setAttribute("aria-label",this.messages.about),this.infoCloseButton.setAttribute("aria-label",this.messages.close),this.infoObservationHeading.textContent=this.messages.observation,this.infoCreditsHeading.textContent=this.messages.credits,this.infoOpen&&this.populateInfoPanel())}connectedCallback(){const e=this.getAttribute("src");e&&this.loadFromSrc(e)}attributeChangedCallback(e,t,n){e==="src"&&n&&n!==t&&this.isConnected&&this.loadFromSrc(n)}async loadFromSrc(e){const n=await(await fetch(e)).json();Array.isArray(n)?await this.loadWitnessUrls(n):this.setEntries([{src:e,sighting:n}])}get witnessUrls(){return this.entries.map(e=>e.src)}set witnessUrls(e){this.loadWitnessUrls(e)}async loadWitnessUrls(e){const t=await Promise.all(e.map(async n=>{const s=await fetch(n);return{src:n,sighting:await s.json()}}));this.setEntries(t)}setEntries(e){this.entries=e,this.warnOnMismatchedCaseIds(e),this.toolbarElement.hidden=e.length===0;const t=e.length>1;this.witnessPicker.hidden=!t,this.witnessSelect.innerHTML="";for(const s of e){const r=document.createElement("option");r.value=s.src,r.textContent=s.sighting.witnessName??s.sighting.witnessId??s.src,this.witnessSelect.appendChild(r)}const n=e.find(s=>s.src===this.currentSrc)??e[0];n&&this.selectWitness(n.src)}warnOnMismatchedCaseIds(e){const t=new Set(e.map(n=>n.sighting.caseId).filter(n=>n!==void 0));t.size>1&&console.warn(`<rr0-eyewitness>: witnesses declare different case ids (${[...t].join(", ")}) — they may not belong to the same case.`)}selectWitness(e){const t=this.entries.find(n=>n.src===e);t&&(this.currentSrc=e,this.witnessSelect.value=e,this.sceneElement.sightingData=t.sighting,this.infoOpen&&this.populateInfoPanel())}toggleInfoPanel(){this.infoOpen=!this.infoOpen,this.infoPanel.hidden=!this.infoOpen,this.infoButton.setAttribute("aria-expanded",String(this.infoOpen)),this.infoOpen&&this.populateInfoPanel()}populateInfoPanel(){this.infoAppLink.href=Xx,this.infoAppLink.textContent="UFO@home v0.6.0";const e=this.entries.find(r=>r.src===this.currentSrc);if(this.infoObservationList.innerHTML="",e){const r=e.sighting.place?.[0],a=Lu(e.sighting.time??{},r?.lng??0);a&&this.appendInfoRow(this.infoObservationList,this.messages.date,new Intl.DateTimeFormat(this.language==="fr"?"fr-FR":"en-US",{dateStyle:"long",timeStyle:"short"}).format(a)),r&&this.appendInfoRow(this.infoObservationList,this.messages.location,`${r.lat.toFixed(4)}, ${r.lng.toFixed(4)}`),e.sighting.witnessName&&this.appendInfoRow(this.infoObservationList,this.messages.witness,e.sighting.witnessName),e.sighting.caseId&&this.appendInfoRow(this.infoObservationList,this.messages.case,e.sighting.caseId)}this.infoCreditsList.innerHTML="";const t=this.sceneElement.currentTerrainAttribution;if(t){const r=document.createElement("li");r.textContent=t,this.infoCreditsList.appendChild(r)}const n=document.createElement("li");n.textContent=`${kx} (`;const s=document.createElement("a");s.href=Vx,s.target="_blank",s.rel="noopener",s.textContent=Wx,n.appendChild(s),n.appendChild(document.createTextNode(")")),this.infoCreditsList.appendChild(n)}appendInfoRow(e,t,n){const s=document.createElement("dt");s.textContent=t;const r=document.createElement("dd");r.textContent=n,e.appendChild(s),e.appendChild(r)}}const Ah="rr0-eyewitness";function Yx(){Hu(),customElements.get(Ah)||customElements.define(Ah,qx)}Yx();
|
|
4757
|
+
`;let xr;function wx(){if(xr)return xr;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),xr=new Zn(e),xr}function Rx(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=Gu(i.illuminatedFraction,0,1),h=i.phaseFraction<.5,d=s*Math.abs(1-2*c),u=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,d,s,0,Math.PI/2,-Math.PI/2,u)):(n.arc(r,a,s,Math.PI/2,-Math.PI/2,!1),n.ellipse(r,a,d,s,0,-Math.PI/2,Math.PI/2,u)),n.fill(),n.restore(),new Zn(t)}function Cx(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 Zn(e)}function Va(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 Th=new Map;function Px(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 Ix(i){let e=Th.get(i);return e||(e=fetch(i).then(t=>t.arrayBuffer()).then(Px),Th.set(i,e)),e}const Dx=new URL(""+new URL("rain-BvWZrB9h.ogg",import.meta.url).href,import.meta.url).href,Lx=new URL(""+new URL("wind-BjklexY7.ogg",import.meta.url).href,import.meta.url).href,Ux=new URL(""+new URL("thunder-DA48qs_v.wav",import.meta.url).href,import.meta.url).href;class Nx{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(Dx,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=n;const r=n>.03;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(Lx,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(Ux,.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()}}Ch();const Fx={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"}},Ox=["en","fr"],Bx=new URL(""+new URL("stars-mag7.5-DDr9QasD.bin",import.meta.url).href,import.meta.url).href,zx={sun:{altitudeDeg:-3,azimuthDeg:180,magnitude:-26.7},moon:{altitudeDeg:-90,azimuthDeg:0,phase:{phaseFraction:0,illuminatedFraction:0},magnitude:-12.7},planets:[]},Wa={lat:0,lng:0,elevationM:0,headingDeg:void 0,pitchDeg:0,fovDeg:60};class Gx extends HTMLElement{static get observedAttributes(){return["src","star-catalog-src","show-compass"]}shadow;stageElement;frameElement;sceneCanvas;ufoElement;sceneRenderer;bodyTooltip;resizeObserver;lastTimeMs=0;starCatalog;weatherAudio=new Nx;thunderTimeoutId;handleFullscreenChange=()=>this.resizeToStage();handlePointerMove=e=>{this.sceneRenderer.setCompassHovered(!0);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),a=this.sceneRenderer.pickBodyAt(s,r);if(!a){this.bodyTooltip.hidden=!0;return}const o=ll(navigator.languages,Ox);this.bodyTooltip.textContent=Fx[a]?.[o]??a,this.bodyTooltip.hidden=!1;const l=this.stageElement.getBoundingClientRect();this.bodyTooltip.style.left=`${e.clientX-l.left+12}px`,this.bodyTooltip.style.top=`${e.clientY-l.top+12}px`};handlePointerLeave=()=>{this.bodyTooltip.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(this.ufoElement.sighting.weather)};handleTimeUpdate=e=>{this.lastTimeMs=e.detail.time,this.updateAstronomy(this.lastTimeMs)};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${ju}</style>${Qu}`,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 vx(this.sceneCanvas,void 0,this.handleLightningFlash),this.bodyTooltip=this.shadow.getElementById("body-tooltip"),this.ufoElement=document.createElement(Ya),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){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"))}setCompassForced(e){this.sceneRenderer.setCompassForced(e)}setWeather(e){const t=e??zu;this.sceneRenderer.setWeather(t),this.weatherAudio.setAmbient(t.precipitationType,t.precipitationIntensity,t.windSpeed)}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),this.setWeather(this.ufoElement.sighting.weather)}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")??Bx;this.starCatalog=await Ix(e),this.updateAstronomy(this.lastTimeMs)}updateAstronomy(e){const t=this.ufoElement.sighting,n=f0(t,e);this.sceneRenderer.setObserverPose(n??Wa),this.sceneRenderer.setTerrainOrigin(n?.lat,n?.lng);const s=n?.lat??Wa.lat,r=n?.lng??Wa.lng,a=Lu(t.event.time??{},r);if(!a){this.sceneRenderer.setAstronomy(zx);return}const o=new Date(a.getTime()+e),l={lat:s,lng:r,elevationM:n?.elevationM??0},c={...Oa("Sun",o,l),magnitude:Ba("Sun",o)},h={...Oa("Moon",o,l),phase:cv(o),magnitude:Ba("Moon",o)},d=ov.map(u=>({body:u,position:Oa(u,o,l),magnitude:Ba(u,o)}));this.sceneRenderer.setAstronomy({sun:c,moon:h,planets:d,stars:this.starCatalog?{catalog:this.starCatalog,date:o,observer:l}:void 0})}}const ol="rr0-scene";function Hu(){Ch(),customElements.get(ol)||customElements.define(ol,Gx)}const ku={testimonyBy:"Testimony by",about:"About",close:"Close",observation:"Observation",date:"Date",location:"Location",case:"Case",credits:"Credits"},Hx=Object.freeze(Object.defineProperty({__proto__:null,eyewitnessMessages_en:ku},Symbol.toStringTag,{value:"Module"}));Hu();const kx="“Thunder” by Jerimee",Vx="https://creativecommons.org/licenses/by/3.0/",Wx="CC BY 3.0",Xx="https://ufoathome.org";class qx extends HTMLElement{static get observedAttributes(){return["src"]}shadow;sceneElement;toolbarElement;testimonyPrefix;witnessText;witnessSelect;infoButton;infoPanel;infoAppLink;infoObservationHeading;infoObservationList;infoCreditsToggle;infoCreditsList;infoCloseButton;entries=[];currentSrc;infoOpen=!1;creditsOpen=!1;language="en";messages=ku;constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const e=document.createElement("template");e.innerHTML=`<style>${Ju}</style>${Ku}`,this.shadow.appendChild(e.content.cloneNode(!0)),this.sceneElement=document.createElement(ol),this.shadow.getElementById("ufo-slot").replaceWith(this.sceneElement),this.toolbarElement=this.shadow.getElementById("toolbar"),this.testimonyPrefix=this.shadow.getElementById("testimony-prefix"),this.witnessText=this.shadow.getElementById("witness-text"),this.witnessSelect=this.shadow.getElementById("witness"),this.infoButton=this.shadow.getElementById("info-button"),this.infoPanel=this.shadow.getElementById("info-panel"),this.infoAppLink=this.shadow.getElementById("info-app-link"),this.infoObservationHeading=this.shadow.getElementById("info-observation-heading"),this.infoObservationList=this.shadow.getElementById("info-observation-list"),this.infoCreditsToggle=this.shadow.getElementById("info-credits-toggle"),this.infoCreditsList=this.shadow.getElementById("info-credits-list"),this.infoCloseButton=this.shadow.getElementById("info-close"),this.witnessSelect.addEventListener("change",()=>this.selectWitness(this.witnessSelect.value)),this.infoButton.addEventListener("click",()=>this.toggleInfoPanel()),this.infoCloseButton.addEventListener("click",()=>this.toggleInfoPanel()),this.infoCreditsToggle.addEventListener("click",()=>this.toggleCredits()),this.loadLocaleMessages()}async loadLocaleMessages(){this.language=ll(navigator.languages,wh),this.language!=="en"&&(this.messages=await C0(this.language),this.testimonyPrefix.textContent=this.messages.testimonyBy,this.infoButton.title=this.messages.about,this.infoButton.setAttribute("aria-label",this.messages.about),this.infoCloseButton.setAttribute("aria-label",this.messages.close),this.infoObservationHeading.textContent=this.messages.observation,this.infoCreditsToggle.textContent=this.messages.credits,this.infoOpen&&this.populateInfoPanel(),this.updateTestimonyLine())}connectedCallback(){const e=this.getAttribute("src");e&&this.loadFromSrc(e)}attributeChangedCallback(e,t,n){e==="src"&&n&&n!==t&&this.isConnected&&this.loadFromSrc(n)}async loadFromSrc(e){const n=await(await fetch(e)).json();Array.isArray(n)?await this.loadWitnessUrls(n):this.setEntries([{src:e,sighting:n}])}get witnessUrls(){return this.entries.map(e=>e.src)}set witnessUrls(e){this.loadWitnessUrls(e)}async loadWitnessUrls(e){const t=await Promise.all(e.map(async n=>{const s=await fetch(n);return{src:n,sighting:await s.json()}}));this.setEntries(t)}setEntries(e){this.entries=e,this.warnOnMismatchedCaseIds(e),this.toolbarElement.hidden=e.length===0;const t=e.length>1;this.witnessSelect.hidden=!t,this.witnessText.hidden=t,this.witnessSelect.innerHTML="";for(const s of e){const r=document.createElement("option");r.value=s.src,r.textContent=s.sighting.witnessName??s.sighting.witnessId??s.src,this.witnessSelect.appendChild(r)}const n=e.find(s=>s.src===this.currentSrc)??e[0];n?this.selectWitness(n.src):this.updateTestimonyLine()}warnOnMismatchedCaseIds(e){const t=new Set(e.map(n=>n.sighting.caseId).filter(n=>n!==void 0));t.size>1&&console.warn(`<rr0-eyewitness>: witnesses declare different case ids (${[...t].join(", ")}) — they may not belong to the same case.`)}selectWitness(e){const t=this.entries.find(n=>n.src===e);t&&(this.currentSrc=e,this.witnessSelect.value=e,this.sceneElement.sightingData=t.sighting,this.updateTestimonyLine(),this.infoOpen&&this.populateInfoPanel())}updateTestimonyLine(){const e=this.entries.find(t=>t.src===this.currentSrc);e&&(this.witnessText.textContent=e.sighting.witnessName??e.sighting.witnessId??e.src)}formatDate(e){const t=e.place?.[0],n=Lu(e.time??{},t?.lng??0);if(n)return new Intl.DateTimeFormat(this.language==="fr"?"fr-FR":"en-US",{dateStyle:"long",timeStyle:"short"}).format(n)}formatLocation(e){const t=e.place?.[0];if(t)return`${t.lat.toFixed(4)}, ${t.lng.toFixed(4)}`}toggleInfoPanel(){this.infoOpen=!this.infoOpen,this.infoPanel.hidden=!this.infoOpen,this.infoButton.setAttribute("aria-expanded",String(this.infoOpen)),this.infoOpen?this.populateInfoPanel():(this.creditsOpen=!1,this.infoCreditsList.hidden=!0,this.infoCreditsToggle.setAttribute("aria-expanded","false"))}toggleCredits(){this.creditsOpen=!this.creditsOpen,this.infoCreditsList.hidden=!this.creditsOpen,this.infoCreditsToggle.setAttribute("aria-expanded",String(this.creditsOpen))}populateInfoPanel(){const e=this.entries.find(r=>r.src===this.currentSrc);if(this.infoObservationList.innerHTML="",e){const r=this.formatDate(e.sighting);r&&this.appendInfoRow(this.infoObservationList,this.messages.date,r);const a=this.formatLocation(e.sighting);a&&this.appendInfoRow(this.infoObservationList,this.messages.location,a),e.sighting.caseId&&this.appendInfoRow(this.infoObservationList,this.messages.case,e.sighting.caseId)}this.infoAppLink.href=Xx,this.infoAppLink.textContent="UFO@home v0.7.0",this.infoCreditsList.innerHTML="";const t=this.sceneElement.currentTerrainAttribution;if(t){const r=document.createElement("li");r.textContent=t,this.infoCreditsList.appendChild(r)}const n=document.createElement("li");n.textContent=`${kx} (`;const s=document.createElement("a");s.href=Vx,s.target="_blank",s.rel="noopener",s.textContent=Wx,n.appendChild(s),n.appendChild(document.createTextNode(")")),this.infoCreditsList.appendChild(n)}appendInfoRow(e,t,n){const s=document.createElement("dt");s.textContent=t;const r=document.createElement("dd");r.textContent=n,e.appendChild(s),e.appendChild(r)}}const Ah="rr0-eyewitness";function Yx(){Hu(),customElements.get(Ah)||customElements.define(Ah,qx)}Yx();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rr0/ufoathome",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"description": "UFO@home — record and replay a UFO sighting's shape, movement and appearance",
|
|
6
6
|
"author": "Jérôme Beau <rr0@rr0.org> (https://rr0.org)",
|
|
7
7
|
"license": "MIT",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e={witness:"Témoin",about:"À propos",close:"Fermer",observation:"Observation",date:"Date",location:"Lieu",case:"Dossier",credits:"Crédits"};export{e as eyewitnessMessages_fr};
|